Si alguna vez ha trabajado en sitios con mucho texto de formato largo, especialmente sitios CMS donde las personas pueden escribir texto en un editor WYSIWYG, probablemente haya tenido que escribir CSS para administrar el espacio vertical entre varios elementos tipográficos, como títulos, párrafos, listas, etc.
Es sorprendentemente difícil hacerlo bien. Y esa es una de las razones por las que existen cosas como el complemento Tailwind Typography y Stack Overflow’s Prose, aunque se ocupan de mucho más que solo el espaciado vertical.
Firefox es compatible :has() detrás layout.css.has-selector.enabled bandera en about:config en el momento de escribir.
¿Qué hace que el espaciado vertical tipográfico sea complicado?
Seguramente debería ser tan simple como decir que cada elemento – p, h2, uletc. – tiene cierta cantidad de margen superior y/o inferior… ¿verdad? Por desgracia, este no es el caso. Considere este comportamiento deseado:
- El primer y último elemento de un bloque de texto de formato largo no debe tener espacio adicional arriba o abajo (respectivamente). Esto es para que otros elementos no tipográficos se coloquen de manera predecible alrededor del contenido de formato largo.
- Las secciones dentro del contenido largo deben tener un espacio grande y agradable entre ellas. «Sección» es un encabezado y todo el contenido posterior que pertenece a ese encabezado. En la práctica, esto significa tener un espacio grande y agradable antes de un encabezado… pero no ¡Si este título es inmediatamente precedido por otro título!

No necesita buscar más allá de CSS-Tricks para ver dónde puede ser útil. Aquí hay algunas capturas de pantalla de intervalos que saqué de otro artículo.


La solución tradicional
Una solución típica que he visto consiste en poner cualquier contenido a largo plazo en un paquete div (o etiqueta semántica si corresponde). El nombre de mi clase era .rich-textque creo que estoy usando como resaca de versiones anteriores de Wagtail CMS que agregarán esta clase automáticamente al representar contenido WYSIWYG. Tipografía de viento en contra usa un .prose clase (más algunas clases de modificadores).
Luego, agregamos CSS para seleccionar todos los elementos tipográficos en ese envoltorio y agregamos márgenes verticales. Tenga en cuenta, por supuesto, el comportamiento especial mencionado anteriormente para los títulos ordenados y el primer/último elemento.
La solución tradicional suena razonable… ¿cuál es el problema? Creo que hay algunos…
Construcción sólida
Necesita agregar una clase contenedora como .rich-text en todos los lugares correctos significa ingresar una estructura específica en su código HTML. Esto a veces es necesario, pero parece que no debería serlo en este caso particular. También puede ser fácil olvidarse de hacer esto siempre que lo necesite, especialmente si necesita usarlo para una combinación de CMS y contenido codificado.
La estructura HTML se vuelve aún más rígida cuando desea poder recortar los márgenes superior e inferior del primer y último elemento respectivamente, porque deben ser elementos secundarios inmediatos del elemento contenedor, p. .rich-text > *:first-child.Eso > es importante; después de todo, no queremos seleccionar al azar el primer elemento de la lista en cada ul o ol con este seleccionador.
Propiedades de mezcla de campo
en pre-:has() mundo, no hemos tenido forma de seleccionar un elemento basado en lo que le sigue. Por lo tanto, el enfoque tradicional para la asignación de elementos tipográficos implica el uso de una combinación de los dos margin-top y margin-bottom:
- Comenzamos configurando nuestro espaciado predeterminado para elementos con
margin-bottom. - Luego asignamos nuestras «secciones» usando
margin-top– es decir. espacio extra grande encima de cada encabezado - Luego deshacemos esos grandes
margin-tops cuando un título es seguido inmediatamente por otro título utilizando el selector de hermanos adyacente (p. ej.h2 + h3).
Ahora, no sé ustedes, pero siempre me ha parecido mejor usar una dirección de campo al diseñar las cosas, generalmente prefiero margin-bottom (esto supone CSS gap la propiedad no es factible, que no lo es en este caso). Ya sea que eso sea un gran problema o incluso cierto, te dejaré decidir. Pero personalmente preferiría margin-bottom para distribuir contenido largo.
campos cada vez más reducidos
Debido a la reducción de los márgenes, esta combinación de márgenes superior e inferior en sí misma no es un gran problema. Solo tendrá efecto el mayor de los dos campos ordenados, no la suma de los dos campos. Pero… bueno… Realmente no me disgustan los márgenes reducidos.
Los campos colapsables son más algo que debes saber. Puede ser confuso para los desarrolladores junior que no están al día con esta peculiaridad de CSS. El espaciado cambiará por completo (es decir, dejará de encogerse) si cambia el envoltorio del flex diseño con flex-direction: column por ejemplo, que es algo que no sucedería si configura los márgenes verticales en una dirección.
Sé más o menos cómo funcionan los márgenes de reducción y sé que están ahí por diseño. También sé que a veces me han hecho la vida más fácil. Pero lo han hecho en otro momento más difícil. Simplemente creo que son bastante raros y, en general, prefieren evitar confiar en ellos.
Él :has() respuesta
Y aquí está mi intento de resolver estos problemas con :has().
Para resumir las mejoras que esto pretende hacer:
- No se requiere ninguna clase contenedora.
- Trabajamos con un dirección de margen consistente.
- Se evitan márgenes reducidos (lo que puede o no ser una mejora, dependiendo de su posición).
- Sin configurar estilos y luego cancelar inmediatamente.
Notas y advertencias sobre :has() respuesta
- Siempre verifique el soporte del navegador. En el momento de escribir, Firefox solo es compatible
:has()detrás de una bandera experimental. - Mi solución no incluye todos los elementos tipográficos posibles. por ejemplo, sin
<blockquote>en mi demostración. Sin embargo, la lista de opciones es bastante fácil de ampliar. - Mi solución tampoco maneja elementos no tipográficos. que pueden estar presentes en sus bloques de texto específicos de formato largo, p.
<img>Esto se debe a que en los sitios en los que trabajo, tendemos a bloquear WYSIWYG tanto como sea posible en los nodos de texto principal, como encabezados, párrafos y listas. Todo lo demás, por ejemplo, citas, imágenes, tablas, etc. – es un bloque de componente CMS separado, y estos bloques en sí mismos están separados cuando se representan en una página. Pero nuevamente la lista de selección se puede expandir. - acabo de encenderlo
h1por completitud. Normalmente no permitiría que un usuario de CMS agregueh1a través de WYSIWYG, ya que el título de la página se escribirá en algún lugar de la plantilla de la página en lugar de escribirse en el editor de la página CMS. - No me importa un título seguido inmediatamente por un título del mismo nivel (
h2 + h2). Esto significaría que el primer título no «poseería» ningún contenido, lo que parece un abuso de título (y, corríjame si me equivoco, pero podría, podría violado WCAG 1.3.1 Información y relaciones). Tampoco me importa que falten niveles de título que no sean válidos. - De ninguna manera estoy rompiendo los enfoques existentes que mencioné. Si y cuando construya otro sitio de Tailwind, ¡estaré usando el excelente complemento de tipografía, sin duda!
- No soy diseñador. Se me ocurrieron estos valores de distancia mirando alrededor. Probablemente podría (y debería) usar mejores valores.
Especificidad y estructura del proyecto.
Iba a escribir aquí una cosa muy grande sobre cómo funciona el método tradicional y el nuevo :has() la forma de hacerlo puede encajar ITCSS metodología… Pero ahora que tenemos :where() (el selector de especificidad cero) ahora puede elegir prácticamente su nivel de especificidad preferido para cada selector.
Sin embargo, el hecho de que ya no estemos lidiando con un caparazón – .prose, .rich-textetc. – me parece que esto debería vivir en la capa de «elementos», es decir. antes de entrar en los detalles del nivel de clase. :where() en mis ejemplos para mantener la especificidad consistente Todos los selectores en mis dos ejemplos tienen una puntuación de especificidad de 0,0,1 (excepto por el reinicio básico).
resumiendo
Así que ahí lo tienes, ¡una solución moderna para un problema muy aburrido! Este enfoque más nuevo todavía no es lo que yo llamaría CSS «simple» — como dije al principio, es un tema más complejo de lo que parece. Pero aparte de algunos selectores un poco complicados, creo que el nuevo enfoque hace más sentido en general y la estructura HTML menos rígida se ve muy atractiva.
Si terminas usando esto o algo similar, me encantaría saber cómo funciona para ti. Y si se te ocurren formas de mejorarlo, ¡me encantaría escucharlas también!

