Acto I — Divitis crónica: del h1 saltarín al botón encadenado
Te han dicho que el DOM es una caja dentro de otra caja. Que el padre es un contenedor tontorrón y el hijo es donde va “lo importante”. Así salen sitios con divitis, encabezados saltando de h1 a h4 porque “se veía grande”, button dentro de a (un clásico del crimen) y listas de navegación hechas con div porque “los estilos de ul molestaban”. Después lloras: márgenes que colapsan, z-index que no obedece, eventos que no se disparan, CSS que se pisa y auditorías de accesibilidad llenas de rojo.
Esa visión utilitarista convierte la jerarquía en coreografía improvisada. El padre pone una clase, el hijo la sobreescribe, el nieto la revienta con !important. Rompes el layout al mover una tarjeta de una cuadrícula a otra porque el espaciado vive en los hijos. Y cuando llega negocio: “mismo componente en home y checkout”, cambias seis CSS, doblas el JavaScript y aún así se rompe en móvil.
No es mala suerte. Es que tratas la estructura como maquillaje. La jerarquía es arquitectura, no atrezzo.
Acto II — Jerarquía o anarquía: contexto arriba, obediencia abajo
La relación padre → hijo es tu cadena de mando. El padre define contextos (semántica, layout, tipografía, stacking, estados). El hijo obedece: ocupa, hereda y comunica su estado sin apropiarse del control del grupo. Si el padre manda, el sistema es predecible. Si mandan los hijos, tienes anarquía.
1) Semántica primero: el padre da sentido
El árbol correcto antes que el color correcto. Landmarks, encabezados, listas. El lector de pantalla y Google no adivinan tus intenciones.
HTML
<header>
<a class="skip-link" href="#content">Saltar al contenido</a>
<div class="brand">…</div>
<nav aria-label="Principal">
<ul>
<li><a href="/">Inicio</a></li>
<li><a href="/tienda">Tienda</a></li>
</ul>
</nav>
</header>
<main id="content" tabindex="-1">
<article>
<header>
<h1>Guía de compra</h1>
</header>
<h2>Portátiles</h2>
<p>…</p>
</article>
<aside aria-label="Promociones">…</aside>
</main>
<footer>…</footer>
Padres semánticos:
HTML
<nav>, <main>, <article>, <section>,
<ul>
<li style="list-style-type: none;">
<ul></ul>
</li>
</ul>
<ul>
Hijos correctos:
<li> dentro de <ul>, encabezados en orden (<h1> → <h2> → <h3>).
</ul>
Negocio: mejor SEO, accesibilidad certificable y menor coste de mantenimiento (un documento bien estructurado se estiliza solo).
2) Layout: el padre controla el espacio, el hijo ocupa
El spacing vive en el padre (con gap) y no con márgenes errantes en los hijos. El contenedor define el modelo de disposición; los hijos sólo declaran cómo encajan.
CSS
/ El padre define el sistema de espacio /
.grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.stack { display: grid; gap: .75rem; }
.cluster { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: center; }
/ Los hijos se limitan a propiedades de tamaño/crecimiento /
.card { min-block-size: 12rem; }
.btn { inline-size: max-content; }
Contenedores de verdad: gap mata la guerra de márgenes y sus colapsos.
Responsivo adulto: el padre maneja breakpoints o consultas de contenedor, no cada hijo con su media query.
CSS
/ El padre habilita el contexto de contenedor /
.product-grid { container-type: inline-size; }
/ Los hijos responden al ancho disponible del padre /
@container (min-width: 48rem) {
.product-card {
display: grid;
grid-template-columns: 160px 1fr;
gap: 1rem;
}
}
Negocio: cambias densidades y rejillas sin tocar componentes. Time-to-change en minutos, no sprints.
3) Herencia: logística tipográfica y cromática
Define tipografía y color arriba y deja que fluya. Menos líneas, menos bugs.
CSS
:root {
--fg: #111;
--bg: #fff;
--accent: #0a84ff;
}
html {
color: var(--fg);
background: var(--bg);
}
body {
font: 400 1rem/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
.article {
max-inline-size: 70ch;
}
.article h2 {
line-height: 1.2;
}
El padre (html/body/secciones) marca tonos y escala.
Los hijos no redefinen color por deporte.
Negocio: coherencia de marca + menor CSS = mejores métricas de rendimiento y lectura.
4) Estados en el padre; señal en los hijos
Un solo atributo en el bloque controla la UI completa. Nada de clases dispersas.
HTML
<div class="accordion" data-state="collapsed">
<button class="accordion__header" aria-expanded="false" aria-controls="p1">FAQ</button>
<div id="p1" class="accordion__panel" hidden>…</div>
</div>
CSS
.accordion[data-state="expanded"] .accordion__panel {
display: block;
}
.accordion[data-state="expanded"] .accordion__header {
border-bottom: 2px solid var(--accent);
}
Con :has() puedes derivar estado del hijo, sin JS
CSS
.accordion:has(.accordion__header[aria-expanded="true"]) .accordion__panel {
display: block;
}
Negocio: un estado, un origen. Menos condiciones en JS, menos errores.
5) Eventos: delegación en el padre
Clics y teclas se gestionan en el contenedor; los hijos sólo emiten intención.
JS
document.addEventListener('click', (e) => {
const btn = e.target.closest('.accordion__header');
if (!btn) return;
const acc = btn.closest('.accordion');
const expanded = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!expanded));
acc.dataset.state = expanded ? 'collapsed' : 'expanded';
acc.querySelector('.accordion__panel')?.toggleAttribute('hidden', expanded);
});
Un listener, infinitos hijos.
Funciona con contenido dinámico.
Negocio: menos suscripciones, menos fugas de memoria, mejor INP.
6) Aislamiento: stacking y contención
Muchos “bugs de `z-index`” son culpa del padre que no crea contexto.
CSS
/ El padre crea un stacking context controlado /
.modal {
position: fixed;
inset: 0;
isolation: isolate;
z-index: 1000;
}
/ El contenedor traza límites de layout y estilo /
.card {
contain: layout style paint;
}
isolation: isolate; = el hijo no compite con capas externas.
contain; reduce trabajo del motor y evita efectos colaterales.
Negocio: menos z-fights, pintura más barata, scroll fluido.
7) Prohibiciones tácticas: Porque la libertad sin doctrina es caos
Nada de elementos interactivos anidados ≶a> dentro de <button> o viceversa.
Nada de roles falsos: no simules:
- Nada de elementos interactivos anidados (<a> dentro de <button> o viceversa).
- Nada de roles falsos: no simules <ul> con <div> + ARIA si puedes usar HTML nativo.
- Nada de spacing en niños que afecta al grupo: usa `gap` en el padre.
- Nada de cascadas inventadas: si un hijo necesita estilos distintos por contexto, crea variantes del padre o usa consultas de contenedor.
Acto III — El manifiesto: el contenedor manda, la interfaz obedece
- El padre manda; el hijo obedece: El padre define semántica, layout, espaciado, contexto y estado. El hijo sólo declara cómo encaja.
- El espacio vive en el contenedor: gap y consultas de contenedor antes que márgenes y media queries sueltas.
- Hereda por defecto; excepciona por motivo: Tipografía y color arriba; excepciones donde la semántica lo justifique.
- Estados declarados, no dispersos: Un atributo en el padre (`data-`, `aria-`) gobierna la UI completa.
- Delegación de eventos o muerte: Un listener en el padre, hijos declarativos. Componentes dinámicos gratis.
- Aislamiento explícito: Crea stacking y contención donde corresponda; `z-index` es la última herramienta, no la primera.
- Semántica sobre estilo: HTML nativo, roles correctos, encabezados en orden. El SEO y la a11y no son “extras”.
- Variantes por contexto, no por capricho: Si cambia el entorno, define una variante del padre o un contenedor distinto.
- Prohibido anidar interactivos: Si algo se pulsa, es un control único con foco y semántica propia.
- Documenta la doctrina: Primeras líneas de tu CSS/README: quién manda, qué hereda, cómo se espacian los bloques.
Guía de batalla: Para mañana por la mañana
- Audita un flujo crítico (home → producto → checkout). Lista padres y roles: main, nav, ul, article… Corrige lo que no sea semántico.
- Mueve el espaciado de los hijos al contenedor con gap. Desactiva márgenes que colapsan.
- Introduce consultas de contenedor en una rejilla clave. Mide el diff de CSS eliminado.
- Centraliza estados con data- en bloques (accordion, tabs, drawer) y elimina clases sueltas.
- Aplica delegación en un componente repetido. Quita listeners de los hijos.
- Aísla capas problemáticas con isolation y contain. Documenta por qué y dónde.
- Escribe la ley en el README del repo: 10 reglas y 5 anti-patrones prohibidos.
Preguntas frecuentes
-
¿Qué es la “divitis” y por qué te rompe la web?
La divitis es el vicio de usar <div> para todo y convertir la jerarquía en caos. Terminas con encabezados fuera de orden, controles mal anidados (botones dentro de enlaces), listas falsas y un CSS que se pisa a sí mismo. La cura es semántica real (landmarks, listas, encabezados en orden), layout desde el contenedor con gap, estados declarados y herencia tipográfica arriba. La jerarquía es arquitectura, no maquillaje.
-
¿Quién manda en el DOM: el padre o los hijos?
Manda el padre. El contenedor define contexto (semántica, layout, tipografía, stacking y estados) y los hijos obedecen: ocupan espacio, heredan y comunican su estado sin apropiarse del grupo. Cuando cada hijo impone sus reglas aparecen “anarquía”,
!importanty regresiones al mover componentes. Si el espaciado, las rejillas y los estados viven arriba, el sistema se vuelve predecible y barato de mantener. El padre manda; la interfaz obedece. -
¿Puedo tener más de un H1? ¿Cómo ordeno los encabezados y landmarks?
HTML5 permite varios H1 por secciones, pero lo sensato es un H1 principal y jerarquía descendente (h2 → h3…). No saltes niveles por estética. Usa landmarks nativos —<header>, <main>, <nav>, <article>, <aside>, <footer>— y listas reales para navegación (<ul> + <li>). Así lectores de pantalla y buscadores entienden el documento sin adivinar.
-
¿Gap o márgenes para el espaciado del layout?
Para el espaciado del grupo, manda
gapen el contenedor.gapelimina guerras de márgenes y colapsos, y te deja cambiar densidad sin tocar cada hijo. Reserva los márgenes para excepciones locales. Patrón sano: el padre controla el espacio y el modelo de disposición; los hijos solo declaran tamaño y crecimiento. -
¿Cómo uso consultas de contenedor sin convertir el CSS en un sudoku?
Activa
container-typeen el padre y adapta los hijos al ancho del contenedor, no de la ventana. Obtienes componentes verdaderamente responsivos: misma tarjeta, distinta densidad y cero hacks globales. Beneficios: menos media queries dispersas, menos acoplamiento y un CSS más corto. -
¿Cómo gestiono estados de UI sin clases sueltas por todas partes?
Centraliza el estado en el bloque padre con un único origen (
data-*o ARIA) y deja que el CSS derive estilos. Incluso puedes reaccionar al estado del hijo con:has()y evitar JavaScript. Ventajas: un estado, un sitio; menos condiciones en JS, menos errores y una UI fácil de auditar. -
¿Qué es la delegación de eventos y por qué mejora el INP?
Delegar es escuchar arriba (contenedor o
document) y reaccionar conclosest()al objetivo real. Con un solo listener manejas infinitos hijos (también dinámicos), reduces suscripciones, evitas fugas de memoria y alivias el hilo principal. Mejor INP y menos wiring. -
¿Por qué mi z-index no obedece? (stacking, isolation y contain)
Te falta contexto de apilamiento. Crea uno donde corresponde (modales, overlays) con posicionamiento y
isolation: isolate;para que los hijos no compitan con capas externas. Añadecontainpara acotar layout/pintura y abaratar el render.z-indexes la última herramienta, no la primera. -
¿Qué está prohibido si quieres una interfaz que escale?
Prohibido anidar interactivos (<a> dentro de <button> o al revés), falsificar roles con <div> + ARIA existiendo HTML nativo, repartir espaciado en hijos que afecta al grupo (usa
gap) y provocar cascadas inventadas por contexto. Si cambia el entorno, define variantes del padre o un contenedor distinto. -
¿Cómo lo aplico mañana por la mañana sin romper nada?
Audita un flujo crítico (home → producto → checkout) y lista padres/roles; corrige lo no semántico. Mueve el espaciado al contenedor con
gap. Introduce consultas de contenedor en una rejilla clave y mide el CSS eliminado. Centraliza estados condata-*en bloques y elimina clases sueltas. Aplica delegación en un componente repetido y aísla capas conisolation/contain. Cierra documentando la doctrina en el README.
Pablo Rodríguez: el crítico implacable
Qué opinas de la relacion padre hijo en diseño web?
La mediocridad ha vendido la idea de que es una relación familiar, democrática. Una mentira. En el DOM, como en la guerra, no hay democracia. Hay una cadena de mando. Y si esa cadena se rompe, todo el sistema se va a la mierda.
En mi opinión, el padre es el arquitecto. Su trabajo no es «contener» a los hijos. Su trabajo es definir las leyes de la física del universo en el que van a vivir. El padre dicta el display: grid, el gap, el padding. El padre establece el @container y le dice al hijo: «este es tu espacio, estas son las reglas, adáptate o muere». El padre es el responsable del orden, del ritmo y del contexto. Toda la responsabilidad del layout recae sobre sus putos hombros.
Por su parte el hijo es un mero soldado. No tiene opinión sobre la estrategia general. Su única misión es cumplir su función dentro de los límites que le ha impuesto su sargento (el padre). Un hijo no usa margin para separarse de su hermano; para eso está el gap que ha dictado el padre. Un hijo no usa position: absolute para escapar de la formación; eso es desertar. La única responsabilidad del hijo es su contenido interno. Nada más.
La mayoría de las chapuzas que ves ahí fuera nacen de una inversión de roles: padres vagos que no imponen reglas y hijos rebeldes que intentan arreglar el caos con parches (margin negativos, transform, anidamientos absurdos). Eso es la anarquía. Y la anarquía, en CSS, siempre acaba en un infierno de especificidad y deuda técnica. Así que mi opinión no es una opinión. Es pura doctrina. La anarquía empieza donde acaba la responsabilidad del padre.

Por fin una explicación clara acerca de la relación entre elementos HTML y su estructura en el DOM. Gracias de parte de un recién llegado al mundo de la programación.
Gracias a ti por visitarnos!