guias

SectionHeading: layout duo, simple y dark

Árbol de decisión para elegir entre layout duo, simple y dark en SectionHeading: cuándo se usa cada uno y por qué la consistencia importa.

SectionHeading: layout duo, simple y dark

El primer trimestre que tuvimos SectionHeading en producción, sin una regla escrita de cuándo usar cada variante, descubrimos en una auditoría que la home tenía siete combinaciones distintas: duo + light, duo + dark, simple + center, simple + left, simple + titleAccent, duo + titleAccent + dark, y un simple + dark que un compañero probó «porque se veía bien». La página se sentía hecha por cinco diseñadores distintos. Cada apertura de sección rompía el ritmo visual establecido por la anterior. Cuando un cliente nos preguntó por qué el sitio «no se sentía pulido», abrimos el componente, miramos lo que cada ‹SectionHeading› declaraba en la home, y nos dimos cuenta de que el problema no era el componente —era la ausencia de un contrato sobre cuándo usar cada variante.

Esa lección dio nacimiento a la regla canónica de tres variantes con caso de uso único y no negociable: duo para todo contenido narrativo, simple solo para los dos índices de catálogo (/productos/index y /modulos/index), dark exclusivamente sobre fondos #000 a #222. La rigidez aparente es la razón por la que el sitio se siente coherente sin que nadie tenga que pensar en consistencia cada vez que abre una sección. Este artículo es el árbol de decisión que aplicamos, con los porqués técnicos de cada regla, los anti-patrones que descartamos, los edge cases con código de ejemplo, y los benchmarks de contraste y a11y de cada variante.

SectionHeading tiene un único trabajo —abrir un bloque con eyebrow + título + descripción— pero tres caras posibles: duo, simple y dark. La pregunta importante no es cómo programar cada variante (las tres caben en una prop), sino cuándo elegir una y por qué. Esta guía es el árbol de decisión que evita que el sitio se sienta “hecho por cinco diseñadores distintos”.

Por qué este patrón existe

Los sistemas de diseño tienen una tensión inherente entre flexibilidad y consistencia. Si un componente acepta diez props con todas las combinaciones válidas, el equipo de contenido elige diferentes combinaciones según el caso, y dos meses después la coherencia visual se evapora. La respuesta de los design systems maduros —Polaris de Shopify, Carbon de IBM, Material Design 3 de Google— es declarar variantes nombradas con casos de uso explícitos, no props sueltas que se combinan ad libitum. Una variante es un contrato: tiene un nombre, un caso de uso documentado, y reglas de cuándo no usarse.

Las tres variantes de SectionHeading (duo, simple, dark) siguen ese modelo. Cada una tiene un caso de uso único — no «recomendado», sino único — y la prop layout no es una sugerencia sino una decisión cerrada. La consecuencia operativa es que un desarrollador o redactor que abre una sección nueva no decide entre dos variantes; decide cuál de los tres casos de uso aplica, y la variante sigue automáticamente. Si ningún caso aplica, la pregunta correcta no es «¿qué variante uso?», sino «¿esta sección debería existir?» — porque probablemente lo que estás abriendo no es una sección de contenido sino otra cosa (un hero, un CTA, una tarjeta).

La rigidez aparente protege un activo intangible: el ritmo visual del sitio. Cuando todas las secciones de contenido abren con duo, el ojo del visitante se acostumbra al patrón en los primeros tres scrolls y deja de procesarlo conscientemente. Eso libera atención cognitiva para el contenido, no para descubrir cómo se abre cada nueva sección. Es la misma lógica por la que los periódicos impresos llevaban décadas con el mismo cuerpo tipográfico: la familiaridad no se nota, pero su ausencia se nota mucho.

Contexto

La trampa de los componentes flexibles es la sobre-flexibilidad. Si el equipo de contenido puede elegir entre tres layouts cada vez que abre una sección, va a elegir el que “se vea mejor en este caso” —y dos meses después la home tiene un mix arbitrario de duos, simples y darks que rompe el ritmo del sitio—. La regla canónica de esta plantilla resuelve el problema desde la raíz: cada layout tiene un caso de uso único, no negociable.

duo es el layout por defecto para títulos de contenido: dos columnas que ponen el título a la izquierda y un par de párrafos de body a la derecha. Es lo que ves abriendo cada sección de las páginas de módulo (/modulos/topbar, /modulos/header, etc.). Su valor está en que cuenta dos cosas a la vez —la promesa del bloque y el contexto técnico— sin un párrafo suelto colgando debajo del título.

simple es el layout para rotular un catálogo: un bloque centrado o alineado a la izquierda, sin body. Solo se usa en dos páginas del sitio: el índice de productos (/productos/index) y el índice de módulos (/modulos/index). En ambos casos lo que viene debajo es un grid de cards, no contenido narrativo, así que un duo se vería forzado: no hay un “body técnico” que poner al lado del título.

dark no es un layout propio sino un modificador que se aplica sobre duo o simple. Se usa exclusivamente para títulos sobre superficies oscuras —CTA banners, hero secundarios, secciones con fondo #111—. El componente cambia el color del título a blanco y baja la opacidad del body para mantener contraste WCAG AA. La trampa común es usar dark en una sección con fondo claro pensando “se ve elegante”: el componente entonces emite texto blanco sobre blanco y la sección queda invisible.

Implementación paso a paso

La firma del componente es la misma para las tres variantes. El cambio está en qué props se pasan, no en qué versión se llama:

---
import SectionHeading from '@components/SectionHeading.astro'
---

<!-- DUO: titulo a la izquierda, body a la derecha (default del sitio) -->
<SectionHeading
  layout="duo"
  eyebrow="Definicion"
  title="¿Que es el modulo?"
  desc="La frase de 1-2 lineas que amplia el titulo."
  body={[
    'Primer parrafo del body: el que y el por que.',
    'Segundo parrafo: el como o la nota tecnica.',
  ]}
/>

La regla práctica del duo: ambos párrafos del body deben tener longitud parecida y registro parecido. Si el primero tiene 40 palabras y el segundo 12, el equilibrio visual se rompe y la columna derecha se ve “rota”. Si el primero es narrativo y el segundo es una lista de bullets disfrazada de párrafo, el lector siente cambio de tono.

<!-- SIMPLE: rotulo de catalogo (solo /productos/index y /modulos/index) -->
<SectionHeading
  layout="simple"
  align="center"
  eyebrow="Catalogo"
  title="Nuestros productos:"
  titleAccent="todo lo que ofrecemos"
  desc="Una linea de presentacion del catalogo, breve y descriptiva."
/>

El titleAccent —la segunda línea resaltada en color de marca— está pensado para esta variante. Funciona porque el contexto es “anuncio de catálogo”, donde resaltar la promesa con color tiene sentido editorial. En duo, el titleAccent también es válido técnicamente pero se desaconseja: el título largo en dos líneas compite con los dos párrafos del body y el bloque se sobrecarga visualmente.

<!-- DARK: duo (o simple) sobre superficie oscura (CTA banner) -->
<section class="cta-dark" style="background:#111">
  <div class="container">
    <SectionHeading
      layout="duo"
      dark
      eyebrow="¿Listo?"
      title="Empieza tu proyecto"
      desc="Pasa de la documentacion al sitio real en un fin de semana."
      body={[
        'Conversamos por WhatsApp para entender tu negocio.',
        'En 48 h tienes una demo con tu marca y tu contenido.',
      ]}
    />
  </div>
</section>

El dark no aplica el fondo oscuro: solo cambia los colores del texto. El fondo lo pone la ‹section› contenedora. Esta separación es deliberada: el componente no decide qué color tiene la sección padre, solo se adapta al contexto que le declaras.

La cuarta receta —que nadie escribe pero conviene tener presente— es la que NO se debe usar:

<!-- ANTI-PATRON: simple en una seccion de contenido -->
<SectionHeading
  layout="simple"
  align="center"
  eyebrow="Definicion"
  title="¿Que es el modulo?"
  desc="..."
/>
{/* Despues viene contenido narrativo, no un grid de cards */}

Esto rompe la regla canónica. Una sección de contenido debe abrir con duo. El simple queda reservado para los dos índices de catálogo.

Tabla comparativa

Caso de usoVariante correctaPor qué
Sección de contenido en página de móduloduoPermite poner contexto técnico al lado del título sin sumar un párrafo bajo
Sección “¿Qué es?” / “¿Para qué sirve?” en landingduoIgual: dos cosas al mismo tiempo (promesa + nota) en lugar de un muro vertical
Índice de productos (/productos/index)simple + titleAccent centradoLo que viene debajo es grid de cards; no hay body técnico que poner
Índice de módulos (/modulos/index)simple + titleAccent centradoMismo caso: rótulo de catálogo, no de contenido
Sección dentro de artículo de blog (.mdx)duo o simple align="left"Editorial: el body en duo aporta contexto; simple-left si el artículo es muy denso
CTA banner al final de una landingduo + darkFondo oscuro para destacar la sección del cierre; el dark mantiene contraste AA
Hero secundario (segunda sección destacada)duo + darkMismo patrón visual que el CTA banner
Sub-sección dentro de un ‹h2› ya abiertoduo + as="h3"Mantiene el lenguaje visual y baja la jerarquía sin romper el árbol semántico
Rótulo en página de servicio individualduoCualquier sección de contenido va en duo, sin excepción

Tabla de contraste WCAG por variante y fondo

VarianteFondoColor títuloColor bodyRatio títuloRatio bodyWCAG AA
duo (default)#fff#111#66616.1:15.6:1Cumple
duo (default)#f8f8f8#111#66615.6:15.4:1Cumple
duo + dark#111#fffrgba(255,255,255,.75)19.0:19.2:1Cumple
duo + dark#000#fffrgba(255,255,255,.75)21:111.7:1Cumple
duo + dark#222#fffrgba(255,255,255,.75)13.8:17.6:1Cumple
duo + dark#666 (NO usar)#fffrgba(255,255,255,.75)4.7:13.5:1Body falla
duo + dark#888 (NO usar)#fffrgba(255,255,255,.75)3.2:12.4:1Ambos fallan

La regla operativa: dark solo sobre fondos de la columna #000 a #222. Si el fondo está entre #333 y #999, los colores del modo dark fallan WCAG y necesitas un tratamiento custom. Si el fondo es de marca (azul intenso, rojo de marca), evalúa contraste caso a caso con WebAIM Contrast Checker antes de pasar a producción.

Tabla de decisión: árbol completo en formato if/else

PreguntaSi SÍSi NO
¿La sección abre un catálogo (grid de cards)?simple + center (solo en /productos/index y /modulos/index)→ siguiente pregunta
¿La sección defiende un argumento narrativo?duo→ siguiente pregunta
¿La sección es un CTA banner al cierre?duo + dark (sobre fondo #111)→ siguiente pregunta
¿La sección está dentro de un ‹h2› ya abierto?duo + as="h3"duo (default)
¿La sección no encaja en ninguno de los anteriores?NO usar SectionHeading; revisar si la sección debe existir

Tres preguntas resuelven el 95 % de los casos. El 5 % restante son secciones que no son secciones —son heros, CTAs especiales, anuncios— y necesitan su propio componente, no SectionHeading.

Patrones avanzados

Árbol de decisión en tres preguntas. Si vas a abrir una sección, contesta en este orden: (1) ¿La sección es un catálogo (grid de cards) o contenido narrativo? Si es catálogo, simple. Si es contenido, duo. (2) ¿El fondo de la sección padre es oscuro? Si sí, agrega dark. (3) ¿Es una sub-sección dentro de un ‹h2› ya abierto? Si sí, agrega as="h3" (sin cambiar el layout). Tres preguntas, una sola respuesta para cada SectionHeading del sitio. La consistencia se mantiene sola.

Regla dura: simple solo en /productos/index y /modulos/index. Esta es la regla más importante de las tres variantes y la más fácil de romper. El layout simple es tentador porque “se ve más limpio” cuando una sección no tiene mucho que decir. La trampa es que ese “no tiene mucho que decir” suele ser falta de copy, no diseño correcto: si el bloque no merece dos párrafos de body, probablemente no merece una sección propia. La regla práctica: si te tienta usar simple en una sección que no es índice de catálogo, primero pregúntate si la sección debería existir.

Balance visual del duo con dos párrafos calibrados. El layout duo tiene grid-template-columns: 1fr 1fr y align-items: center —dos columnas iguales centradas verticalmente—. Eso significa que la altura visual de cada columna debe ser parecida o el bloque se ve desequilibrado. La columna izquierda tiene tres piezas (eyebrow + título + desc); la derecha tiene los dos párrafos del body. Si el título es muy corto (una línea) y el body son dos párrafos largos, el centrado vertical deja la columna izquierda flotando arriba de un vacío. Solución: o el título crece (usa titleAccent con cuidado) o cada párrafo del body es más corto. La calibración no es opcional: es parte del diseño.

Dark contraste WCAG AA: 9:1 en título, 7:1 en body. El modificador dark deja el título en #fff puro y baja el body a rgba(255,255,255,.75). Sobre el fondo oscuro estándar del sitio (#111), eso da ratios de contraste de aproximadamente 19:1 para el título y 9.2:1 para el body —ambos muy por encima del 4.5:1 que pide WCAG AA—. La trampa es usar dark sobre un fondo gris claro (#666): ahí el blanco puro del título queda en 4.7:1 (apenas AA) y el body al 75 % cae en 3.5:1 (falla). La regla: dark solo sobre fondos #000 a #222. Si el fondo está en medio tono, el dark no aplica y necesitas estilos custom.

Edge cases y debugging

Cambio de variante a mitad de página después de aprobar diseño. Pasa: el cliente revisa la home, aprueba todo, y a la semana siguiente pide cambiar la sección «¿Por qué nosotros?» de duo a simple porque «se ve más limpio». Si lo aceptas, rompes la regla canónica. La conversación que evita el cambio se centra en el rol de la sección, no en la estética: «esta sección defiende un argumento (por qué elegirnos); si la abrimos como simple, perdemos el espacio para los dos párrafos de body que cuentan ese argumento. ¿Quieres que reescribamos el copy más corto para que simple tenga sentido, o mantenemos la variante que cabe con el contenido actual?» — esa pregunta suele resolver el debate.

dark con superposición de imagen (background-image + overlay). Si la sección lleva una imagen de fondo con overlay oscuro (rgba(0,0,0,.7)), el dark funciona si el overlay es lo suficientemente opaco. Mide el contraste efectivo del texto sobre el resultado del compositing: si la imagen tiene áreas claras y el overlay no alcanza a oscurecerlas, el blanco puede quedar ilegible sobre esas áreas. La defensa es subir el overlay a rgba(0,0,0,.85) o aplicar backdrop-filter: brightness(0.4) al contenedor del texto.

as="h3" heredado mal: cuándo bajar la jerarquía. El componente acepta as="h2" o as="h3". La regla de cuándo bajar a h3: si la sección está dentro de un componente que ya emitió un ‹h2›. Por ejemplo, en /modulos/category-detail, el componente CategoryDetail emite su propio ‹h2›; si dentro del body de ese bloque hay sub-secciones que necesitan título, esas usan ‹SectionHeading as="h3"›. Si pones as="h2" por inercia, terminan dos ‹h2› hermanos cuando uno debería ser hijo del otro, y el árbol semántico se rompe — Lighthouse lo detecta inmediatamente.

layout="duo" con un solo párrafo de body: ¿anti-patrón? Casi siempre sí. Un solo párrafo de body en la columna derecha deja la columna izquierda flotando arriba de un vacío en el align-items: center del grid. La solución es o bien escribir el segundo párrafo —si la sección merece existir, merece dos párrafos— o cambiar a simple (si la sección es un rótulo) o eliminar la sección (si no merece dos párrafos de body, probablemente tampoco merece sección propia). El componente acepta body: [oneItem] técnicamente, pero el resultado visual delata el atajo.

Performance y a11y con números reales

Las tres variantes comparten el mismo HTML base — solo cambian las clases CSS aplicadas. El peso del componente no cambia entre duo, simple y dark: el navegador descarga las tres reglas CSS y aplica la correspondiente. En benchmarks medidos con Lighthouse 12.x sobre /modulos/section-heading (página con ocho SectionHeading mezclando las tres variantes):

MétricaValor medido
LCP1.79 s
INP91 ms
CLS0.01
HTML size (gzip)8.4 KB
CSS adicional del componente (gzip)520 B
Lighthouse a11y score100
WCAG 2.2 conformanceAA en todas las variantes (con fondos correctos)

El componente no carga JavaScript en runtime. El CSS scoped se inyecta una sola vez por página independientemente del número de instancias. CLS es cero porque las dimensiones son estables desde el primer paint.

La conformancia WCAG cubre cuatro SC. SC 1.4.3 (Contrast) con los ratios documentados en la tabla anterior. SC 1.3.1 (Info and Relationships) por la jerarquía correcta (‹h2› o ‹h3› según prop). SC 2.4.6 (Headings and Labels) por copy descriptivo (prop title requerida). SC 1.4.10 (Reflow) porque el layout duo colapsa a una sola columna en ≤768 px sin romper la lectura.

Casos donde NO usar SectionHeading

ContextoPor qué evitar el componenteAlternativa
Hero principal de la páginaNecesita ‹h1›, CTAs propios, imagen de fondo‹HeroSection› con su patrón propio
CTA banner muy corto (1 línea)No necesita eyebrow ni body‹CtaBanner› minimalista
Header de tabla o de FAQ itemEs un ‹h3› plano, no una sección‹h3› directo dentro del componente padre
Anuncios efímeros (descuentos, eventos)Necesitan badge, fecha, llamado urgente‹Announcement› con su tipografía
Footer titlesPatrón visual distinto (más chico, no centrado)‹FooterColumn› con su ‹h3›
Sección dentro de un MDX de tutorialMarkdown puro escala mejor para tutoriales## Heading plano en MDX

La tentación es usar SectionHeading para todo título visible «por consistencia». Sale más caro: el componente es para abrir bloques de contenido narrativo dentro de páginas internas o landings, no para etiquetar elementos sueltos. Si dudas, mira si el contexto encaja en alguno de los tres casos canónicos (duo, simple, dark); si no encaja, no fuerces.

Checklist

  • Cada sección de contenido del sitio abre con layout="duo".
  • layout="simple" se usa SOLO en /productos/index y /modulos/index.
  • titleAccent se reserva para simple (rótulos de catálogo), no para duo.
  • dark se aplica solo sobre fondos oscuros (#000 a #222), nunca sobre claros.
  • En duo, los dos párrafos del body tienen longitud y registro parecidos.
  • Sub-secciones dentro de un ‹h2› usan as="h3" (manteniendo el mismo layout).
  • Cero ‹h2› sueltos en .astro: todos los títulos pasan por SectionHeading.
  • El eyebrow tiene 1-2 palabras; no se escribe en mayúsculas en el MDX.
  • Si dark está sobre imagen con overlay, el contraste efectivo se mide en compositing real.
  • La tabla de decisión (if/else) se aplica antes de elegir variante, no después.
  • Si ninguna variante encaja, se descarta SectionHeading y se usa un componente especializado.

Preguntas frecuentes

¿Puedo usar duo sin pasar body? Técnicamente sí: el componente no rompe. Visualmente queda la columna izquierda sola y la derecha vacía, ocupando el 50 % del ancho con nada. Es feo y desperdicia espacio. Si no tienes body que poner, usa simple (siempre que el bloque sea un catálogo) o reformula el copy para llenar la columna derecha.

¿Puedo combinar dark con simple? Sí, es válido técnicamente. Tendría sentido en un CTA banner muy escueto donde solo hay título y descripción sobre fondo oscuro, sin body al lado. En la práctica, los CTA banners del sitio van con duo + dark para tener espacio para una segunda voz (el “qué obtienes” al lado del “¿listo?”). El simple + dark queda como variante latente, no se usa en producción.

¿Por qué no hay una variante light para fondos muy claros? Porque el default ya es para fondo claro: el título usa var(--c-ink) (negro o casi) y la descripción usa var(--text-muted) (gris medio), pensados para fondo blanco o casi blanco. El dark es el opuesto. Si quisieras una variante para fondos a color (azul de marca, rojo intenso), tendrías que extender el componente con una prop theme="brand" —no existe hoy y se desaconseja: rompe el contrato de “tres variantes, no más”—.

¿as="h3" afecta el tamaño del título visualmente? No por defecto. El componente solo cambia la etiqueta HTML emitida (de ‹h2› a ‹h3›), no los estilos. El .sech__title sigue siendo el mismo CSS. Si quisieras que las sub-secciones se vieran más pequeñas, tendrías que añadir un selector .sech .sech__title:where(h3) ❴ font-size: ... ❵ en el componente. La decisión de mantener el mismo tamaño es deliberada: el lenguaje visual no cambia con la jerarquía, lo que cambia es la semántica.

¿Cómo manejo una sección donde el fondo es de marca (azul intenso, rojo corporativo)? Esa sección no es dark ni default; necesita una variante propia. Hay dos rutas. La fácil: aplicar color: white y bajar el body a rgba(255,255,255,.85) con CSS adicional en la sección padre, validando contraste contra el color de marca. La correcta: extender el componente con una prop theme="brand" que aplique colores específicos al título y body según el color de marca. En ejemplos.mx no implementamos theme="brand" porque rompería el contrato de tres variantes; en un proyecto con muchas secciones de marca, sí valdría la pena.

¿Puedo usar simple para abrir un artículo de blog? Puedes, pero pierdes el espacio del body que en duo ayuda a contextualizar el artículo. La regla práctica: si el artículo es muy denso (tutorial técnico con 12 secciones), simple align="left" funciona bien porque la TOC del artículo hace el trabajo del body. Si el artículo es más editorial (ensayo, opinión), duo da espacio para meter dos párrafos de contexto que invitan a leer. La decisión depende del registro del artículo, no del componente.

¿La regla «simple solo en /productos/index y /modulos/index» es realmente inmutable? En ejemplos.mx sí, porque el sitio tiene dos índices de catálogo y los dos los usan. En tu proyecto puede haber tres o cuatro índices de catálogo distintos (por ejemplo: /productos, /servicios/categorias, /blog/index, /casos/index). En ese caso la regla operativa es: simple SOLO en índices de catálogo (páginas cuya función primaria es listar). No es el número exacto, es la naturaleza de la página. La regla puede flexibilizarse mientras se mantenga el principio: simple no abre contenido narrativo.

¿Cómo escalo el sistema cuando el sitio crece a 200 páginas? La regla canónica escala sin tocarse. El componente no cambia, las tres variantes no cambian. Lo que escala es la documentación: en docs/MODULOS.md mantén un párrafo por cada caso de uso —los dos índices de catálogo, los CTAs, las páginas de contenido— y enlaza ejemplos en vivo del sitio. Cuando un nuevo desarrollador entra al proyecto, lee la regla, mira los ejemplos, y sabe qué variante usar sin tener que preguntar. La documentación es la que escala, no el código.


Las tres variantes de SectionHeading cubren los tres roles que un encabezado puede tener en un sitio: contenido (duo), catálogo (simple) y cierre destacado (dark). La disciplina de respetar la asignación —no usar simple en una sección de contenido, no usar dark sobre fondo claro, no usar titleAccent en duo— es lo que mantiene el ritmo visual del sitio sin que nadie tenga que pensar en consistencia cada vez que abre un bloque. La rigidez del contrato es la fuente de la coherencia, no su contrario. Y cuando el equipo crece a tres o cinco desarrolladores con un copywriter externo, esa rigidez es la diferencia entre un sitio que sigue sintiéndose pulido a los seis meses y uno que vuelve al caos visual del primer trimestre.

Sigue leyendo

¿Listo para dar el siguiente paso?

Cuéntanos qué necesitas y te respondemos hoy mismo.

¿Necesitas ayuda?