Resolvido com :has(): espaçamento vertical em texto longo

Resolvido com :has(): espaçamento vertical em texto longo

Se você já trabalhou em sites com muitos texto longo — especialmente sites CMS onde as pessoas podem inserir screeds de texto em um editor WYSIWYG — você provavelmente teve que escrever CSS para gerenciar o espaçamento vertical entre diferentes elementos tipográficos, como cabeçalhos, parágrafos, listas e assim por diante.

É surpreendentemente complicado fazer isso direito. E é uma das razões pelas quais coisas como o plug-in Tailwind Typography e o Stack Overflow's Prose existem - embora eles lidem com muito mais do que apenas o espaçamento vertical.

Suporte do Firefox :has() atrás do layout.css.has-selector.enabled bandeira in about:config no momento da escrita.

O que torna o espaçamento vertical tipográfico complicado?

Certamente deve ser tão simples quanto dizer que cada elemento - p, h2, ul, etc. — tem alguma margem superior e/ou inferior... certo? Infelizmente, este não é o caso. Considere este comportamento desejado:

  • O primeiro e o último elemento em um bloco de texto longo não devem ter nenhum espaço extra acima ou abaixo (respectivamente). Isso ocorre para que outros elementos não tipográficos ainda sejam colocados previsivelmente em torno do conteúdo de formato longo.
  • As seções dentro do conteúdo de formato longo devem ter um bom espaço entre elas. Uma “seção” sendo um título e todo o conteúdo seguinte que pertence a esse título. Na prática, isso significa ter um grande espaço antes de um título… não se esse cabeçalho for imediatamente precedido por outro cabeçalho!
Exemplo de um Título 3 seguindo um parágrafo e outro seguindo um Título 2.
Queremos mais espaço acima do Título 3 quando segue um elemento tipográfico, como um parágrafo, mas menos espaço quando segue imediatamente outro título.

Você não precisa procurar mais do que aqui no CSS-Tricks para ver onde isso pode ser útil. Aqui estão algumas capturas de tela de espaçamento que tirei de outro artigo.

Um elemento Título 2 diretamente acima de um Título 3.
O espaçamento vertical entre o Título 2 e o Título 3
Um elemento de Título 3 diretamente após um elemento de parágrafo.
O espaço vertical entre o Título 3 e um parágrafo

A solução tradicional

A solução típica que vi envolve colocar qualquer conteúdo de formato longo em um embrulho div (ou uma tag semântica, se apropriado). O nome da minha turma preferida foi .rich-text, que acho que uso como ressaca de versões anteriores do Wagtail CMS, que adicionaria essa classe automaticamente ao renderizar o conteúdo WYSIWYG. Tipografia do vento de cauda utiliza um .prose classe (mais algumas classes modificadoras).

Em seguida, adicionamos CSS para selecionar todos os elementos tipográficos nesse wrapper e adicionamos margens verticais. Observando, é claro, o comportamento especial mencionado acima em relação aos cabeçalhos empilhados e ao primeiro/último elemento.

A solução tradicional parece razoável... qual é o problema? Acho que tem alguns…

Estrutura rígida

Ter que adicionar uma classe wrapper como .rich-text em todos os lugares certos significa criar uma estrutura específica para o seu código HTML. Isso às vezes é necessário, mas parece que não deveria ser neste caso específico. Também pode ser fácil esquecer de fazer isso em todos os lugares que você precisa, especialmente se precisar usá-lo para uma mistura de CMS e conteúdo codificado.

A estrutura HTML fica ainda mais rígida quando você deseja cortar as margens superior e inferior do primeiro e do último elementos, respectivamente, porque eles precisam ser filhos imediatos do elemento wrapper, por exemplo, .rich-text > *:first-child. que > é importante — afinal, não queremos selecionar acidentalmente o primeiro item da lista em cada ul or ol com este seletor.

Misturando propriedades de margem

No pré:has() mundo, não tivemos uma maneira de selecionar um elemento com base no que o segue. Portanto, a abordagem tradicional para espaçar elementos tipográficos envolve o uso de uma mistura de ambos margin-top e margin-bottom:

  1. Começamos definindo nosso espaçamento padrão para elementos com margin-bottom.
  2. Em seguida, espaçamos nossas “seções” usando margin-top — ou seja, espaço muito grande acima de cada título
  3. Em seguida, substituímos esses grandes margin-tops quando um título é seguido imediatamente por outro título usando o seletor irmão adjacente (por exemplo h2 + h3).

Agora, eu não sei sobre você, mas sempre achei melhor usar uma única direção de margem ao espaçar as coisas, geralmente favorecendo margin-bottom (isso está assumindo o APF gap propriedade não é viável, o que não é neste caso). Se isso é importante ou mesmo verdade, vou deixar você decidir. Mas, pessoalmente, prefiro definir margin-bottom para espaçamento de conteúdo de formato longo.

Margens em colapso

Por causa de margens em colapso, essa mistura de margens superior e inferior não é um grande problema em si. Somente a maior das duas margens empilhadas entrará em vigor, não a soma de ambas as margens. Mas... bem... eu realmente não gosto de margens em colapso.

O colapso das margens é mais uma coisa a ser observada. Pode ser confuso para desenvolvedores juniores que não estão familiarizados com essa peculiaridade do CSS. O espaçamento será totalmente alterado (ou seja, parará de ser recolhido) se você alterar o wrapper para um flex layout com flex-direction: column por exemplo, algo que não aconteceria se você definisse suas margens verticais em uma única direção.

Eu sei mais ou menos como as margens colapsadas funcionam e sei que elas existem por design. Também sei que eles tornaram minha vida mais fácil de vez em quando. Mas eles também tornaram isso mais difícil outras vezes. Eu só acho que eles são meio estranhos e geralmente prefiro evitar confiar neles.

A :has() solução

E aqui está minha tentativa de resolver esses problemas com :has().

Para recapitular as melhorias que isso pretende fazer:

  • Nenhuma classe wrapper é necessária.
  • Estamos trabalhando com um direção de margem consistente.
  • Margens em colapso são evitadas (o que pode ou não ser uma melhoria, dependendo da sua posição).
  • Não há estilos de configuração e, em seguida, substituindo-os imediatamente.

Notas e advertências sobre o :has() solução

  • Verifique sempre o suporte do navegador. No momento da escrita, O Firefox suporta apenas :has() atrás de uma bandeira experimental.
  • Minha solução não inclui todos os elementos tipográficos possíveis. Por exemplo, não há <blockquote> na minha demonstração. A lista de seletores é fácil de estender.
  • Minha solução também não lida com elementos não tipográficos que podem estar presentes em seus blocos de texto longos específicos, por exemplo <img>. Isso ocorre porque, nos sites em que trabalho, tendemos a bloquear o WYSIWYG o máximo possível para os nós de texto principais, como cabeçalhos, parágrafos e listas. Qualquer outra coisa — por exemplo, citações, imagens, tabelas, etc. — é um bloco de componentes CMS separado, e esses próprios blocos são espaçados uns dos outros quando renderizados em uma página. Mas, novamente, a lista de seletores pode ser estendida.
  • Eu só incluí h1 por uma questão de completude. Eu normalmente não permitiria que um usuário do CMS adicionasse um h1 via WYSIWYG, pois o título da página seria inserido no modelo de página em algum lugar, em vez de inserido no editor de página do CMS.
  • Não estou atendendo a um cabeçalho seguido imediatamente pelo mesmo cabeçalho de nível (h2 + h2). Isso significaria que o primeiro título não seria "proprietário" de nenhum conteúdo, o que parece ser um uso indevido de títulos (e, corrija-me se eu estiver errado, mas poder violar WCAG 1.3.1 Informações e Relacionamentos). Também não estou atendendo a níveis de título ignorados, que são inválidos.
  • Não estou de forma alguma criticando as abordagens existentes que mencionei. Se e quando eu construir outro site Tailwind, usarei o excelente plug-in Typography, sem dúvida!
  • Eu não sou um designer. Eu criei esses valores de espaçamento observando-os. Você provavelmente poderia (e deveria) usar valores melhores.

Especificidade e estrutura do projeto

Eu ia escrever uma grande coisa aqui sobre como o método tradicional e o novo :has() maneira de fazer isso pode se encaixar no ITCSS metodologia… Mas agora que temos :where() (o seletor de especificidade zero), você pode escolher seu nível preferido de especificidade para qualquer seletor agora.

Dito isso, o fato de não estarmos mais lidando com um invólucro — .prose, .rich-text, etc. - para mim, parece que isso deveria estar na camada de "elementos", ou seja, antes de você começar a lidar com a especificidade do nível de classe. eu usei :where() em meus exemplos para manter a especificidade consistente. Todos os seletores em ambos os meus exemplos têm um pontuação de especificidade of 0,0,1 (exceto para a redefinição básica).

Resumindo

Então aí está, uma solução de ponta para um problema muito chato! Essa nova abordagem ainda não é o que eu chamaria de CSS “simples” — como disse no início, é um tópico mais complexo do que pode parecer à primeira vista. Mas, além de ter alguns seletores ligeiramente complexos, acho que a nova abordagem faz mais sentido no geral, e a estrutura HTML menos rígida parece muito atraente.

Se você acabar usando isso, ou algo parecido, adoraria saber como funciona para você. E se você puder pensar em maneiras de melhorá-lo, adoraria ouvi-las também!

Carimbo de hora:

Mais de Truques CSS