Un método reproducible para bugs de layout
La secuencia de preguntas que lleva a la causa sin probar cosas al azar: primero la partición cascada o modelo, luego las cuatro preguntas del layout, y la bisección cuando el árbol no llega.
La diferencia entre quien resuelve un bug de layout en tres minutos y quien tarda una hora casi nunca es cuánto CSS sabe. Es que uno sigue una secuencia y el otro prueba cosas. Probar cosas funciona a veces, no enseña nada cuando funciona, y falla por completo cuando la causa está a cuatro niveles de distancia del síntoma. Esta lección es la secuencia: seis preguntas ordenadas por cuánto reducen el espacio de búsqueda, con una regla que hay que respetar para que sirvan de algo.
- Formular un bug de layout como una diferencia medible antes de investigarlo.
- Aplicar la partición entre problema de cascada y problema de modelo.
- Recorrer las cuatro preguntas del layout en orden y saber qué descarta cada una.
- Usar la bisección cuando ninguna pregunta da respuesta.
Antes de investigar nada
La regla previa
Antes de la primera pregunta hay una regla y es la que hace que todo lo demás funcione:
No cambies nada hasta haber localizado la causa.
Cambiar un valor para ver qué pasa parece barato y no lo es. Destruye el estado que estabas observando, añade una variable nueva, y —esto es lo peor— si acierta por casualidad te quedas sin saber por qué, con lo que el mismo bug volverá en otro sitio y no lo reconocerás. La única excepción legítima es la bisección, que es un cambio sistemático y no una prueba.
Paso cero: formula la diferencia en números
Un bug que no puedes enunciar no lo puedes buscar. “Se ve mal” no es un enunciado. El enunciado válido tiene esta forma:
El elemento X mide 320 píxeles de ancho y debería medir 480. / El elemento X está a 40 píxeles del borde superior y debería estar a 0. / El elemento X desborda su contenedor por 18 píxeles a la derecha.
Formularlo así obliga a hacer dos cosas que la gente se salta: identificar exactamente qué elemento —muy a menudo el elemento culpable no es el que se ve mal, sino su padre— y obtener el número real en el panel de computados. Con el número en la mano, el resto del método es mecánico.
La partición: cascada o modelo
Es la pregunta que parte el problema en dos mitades que se investigan de formas distintas, y es la que nunca se puede saltar.
¿El valor computado es el que escribiste?
Si no lo es, el problema está en que tu declaración no llega: es un problema de cascada, y la investigación transcurre entera en el panel de estilos.
Si sí lo es y aun así el resultado está mal, tu declaración llega y significa algo distinto de lo que creías: es un problema de modelo, y la investigación transcurre en el layout.
flowchart TB
A[Formula la diferencia en numeros] --> B{El valor computado es el que escribiste}
B -->|No| C{La regla aparece en el panel de estilos}
C -->|No| D[El selector no coincide o la hoja no cargo]
C -->|Si| E{La declaracion aparece tachada}
E -->|Si| F[Gana otra regla, mira capa especificidad y orden]
E -->|No| G[Valor invalido o propiedad que no aplica a este elemento]
B -->|Si| H{Que contexto de formato manda sobre esta caja}
H --> I{El tamano lo impone el contenido o el contenedor}
I -->|El contenido| J[Busca el hijo con el minimo que no cede]
I -->|El contenedor| K{Cual es el bloque contenedor real}
K --> L[Revisa transform filter contain will-change y position en los ancestros]
D --> M[Causa localizada]
F --> M
G --> M
J --> M
L --> M
style A fill:#89b4fa,color:#11111b
style D fill:#f38ba8,color:#11111b
style F fill:#f38ba8,color:#11111b
style G fill:#f38ba8,color:#11111b
style J fill:#f9e2af,color:#11111b
style L fill:#f9e2af,color:#11111b
style M fill:#a6e3a1,color:#11111bLas dos ramas
Rama de cascada: tres preguntas
¿Aparece la regla en el panel de estilos? Si no aparece, el selector no coincide con este elemento. Causas por frecuencia: te has equivocado de elemento y estás mirando el padre o el hijo; el selector tiene una errata; el elemento no tiene la clase que crees porque la plantilla la construye dinámicamente; la hoja no ha cargado. La comprobación de un segundo es escribir el selector en la consola con document.querySelectorAll y ver si devuelve el elemento.
¿Aparece tachada? Entonces otra regla gana. Todo lo que está por encima en el panel es la lista de sospechosos ordenada por victoria. Los cuatro motivos posibles, en orden de frecuencia real en 2026: la capa —una regla de una capa posterior gana pese a tener selector más simple, o el ganador está fuera de todas las capas y por eso gana a todas—; la especificidad; el orden, cuando hay empate; y !important.
¿Aparece sin tachar pero el computado es otro? Entonces la declaración es inválida o inaplicable. Inválida: errata en el valor, unidad incorrecta, o un var() que no resuelve. Inaplicable: la propiedad no hace nada en ese elemento, como gap en un bloque normal o width en un elemento en línea. Firefox marca este segundo caso explícitamente con el motivo.
Rama de modelo: cuatro preguntas
Esta rama es la que la gente evita porque exige entender el layout, y es donde están los bugs que duran horas. Las preguntas van de la que más descarta a la que menos.
Primera: ¿qué contexto de formato manda sobre esta caja? La misma declaración significa cosas distintas según quién sea el padre. width: 100% en un hijo de un contenedor flexible compite con flex-basis. height: 100% en flujo normal no hace nada si el padre no tiene altura definida. align-self no existe fuera de flex y grid. margin: auto centra en flujo normal solo en el eje horizontal y en flex y grid en los dos. Selecciona el padre y mira su display computado. Si esa respuesta no coincide con lo que suponías, el bug suele acabar aquí.
Segunda: ¿el tamaño lo impone el contenido o el contenedor? Es la distinción entre dimensionado intrínseco y extrínseco. Si lo impone el contenido, el culpable es un hijo, y casi siempre es uno de estos cuatro: un elemento con min-width: auto que no cede, una imagen sin max-width, una palabra o cadena larga sin punto de corte, o una tabla. Si lo impone el contenedor, el culpable está arriba. El overlay de Flexbox o de cuadrícula responde esta pregunta visualmente en un segundo, como se vio en la lección de overlays.
Tercera: ¿cuál es el bloque contenedor real? Solo aplica a elementos posicionados, y es la causa del bug de posicionamiento más frecuente que existe. Un position: absolute se posiciona respecto al antepasado posicionado más cercano; un position: fixed respecto a la ventana salvo que algún antepasado establezca un bloque contenedor. Y establecen bloque contenedor para posicionados fijos, entre otros: transform con cualquier valor distinto de none, filter, backdrop-filter, perspective, contain con layout o paint, content-visibility distinto de visible y will-change de cualquiera de esas propiedades. La comprobación es subir por el árbol mirando los computados de esas propiedades en cada antepasado. Es tedioso y es donde está la respuesta.
Cuarta: ¿quién recorta y quién desborda? Para bugs donde algo desaparece o aparece una barra de scroll de la nada. Sube por el árbol buscando overflow distinto de visible, clip-path, contain: paint o content-visibility. Y para el desbordamiento horizontal misterioso, la comprobación más rápida es una regla temporal que dibuje el contorno de todo:
* { outline: 1px solid oklch(0.7 0.2 20); }
El elemento que sobresale se ve inmediatamente, y su contorno indica exactamente cuánto.
Cuando el árbol no llega: bisección
Si has recorrido las seis preguntas y no hay respuesta, quedan dos técnicas mecánicas que no requieren entender nada y siempre terminan.
Bisección por declaración. Desactiva la mitad de las declaraciones del elemento sospechoso con las casillas del panel. Si el problema desaparece, está en esa mitad; si no, en la otra. Repite. Con treinta declaraciones son cinco pasos.
Bisección por subárbol. Borra la mitad de los hijos del contenedor en el árbol del DOM. Igual: el problema está en la mitad que lo reproduce. Sirve para encontrar qué elemento concreto de una lista de doscientos causa el desbordamiento. El DOM se recupera recargando, así que no hay riesgo.
Reducción a caso mínimo. La versión definitiva: copia el marcado y los estilos a un documento vacío y ve quitando hasta que el bug desaparezca. Lleva más tiempo y tiene dos ventajas que compensan de sobra en un bug difícil: casi siempre encuentras la causa antes de terminar, y si no la encuentras, tienes un caso reproducible que puedes enseñar a otra persona o adjuntar a un informe de bug del navegador.
Antes de concluir que el motor tiene un fallo, el caso mínimo es obligatorio. En la inmensa mayoría de las ocasiones, el proceso de reducirlo revela que la causa era una declaración propia que no habías considerado. En las pocas ocasiones en que de verdad es un bug del navegador, sin caso mínimo nadie va a poder confirmarlo.
Lo que de verdad distingue a un método de la prueba y error no es la tasa de acierto, porque probar cosas también acierta. Es lo que ocurre cuando no aciertas. Al probar valores al azar, un intento fallido no te deja nada: no has descartado nada, no sabes dónde no está el problema, y el siguiente intento parte del mismo punto que el anterior. Al seguir una secuencia, cada pregunta respondida elimina permanentemente una mitad del espacio de búsqueda, y eso es cierto tanto si la respuesta te acerca a la causa como si no. Después de responder “el computado sí es el que escribí” sabes con certeza que no hay que volver a mirar el panel de estilos, y eso vale aunque tardes veinte minutos más en la otra rama. Esa propiedad —que el progreso sea monótono— es la que convierte un bug difícil en un problema acotado en lugar de en una sesión indefinida de frustración. Y tiene un segundo efecto que se nota a lo largo de los años: como cada pregunta te obliga a mirar algo concreto del modelo —el contexto de formato del padre, el bloque contenedor real, de dónde viene el tamaño— depurar con método te enseña CSS y depurar al azar no te enseña nada. La gente que lleva diez años probando valores hasta que algo funciona sigue teniendo los mismos huecos que tenía el primer año, porque nunca ha tenido que nombrar la causa de nada. La secuencia es lenta las primeras veces, se automatiza en unas semanas, y a partir de ahí es lo que hace que un bug de layout deje de dar miedo.