Rozwiązany za pomocą :has(): Odstępy pionowe w długim tekście

Rozwiązany za pomocą :has(): Odstępy pionowe w długim tekście

Jeśli kiedykolwiek pracowałeś w witrynach z dużą ilością długi tekst — zwłaszcza witryny CMS, w których ludzie mogą wprowadzać tabele tekstu w edytorze WYSIWYG — prawdopodobnie musiałeś napisać CSS, aby zarządzać pionowymi odstępami między różnymi elementami typograficznymi, takimi jak nagłówki, akapity, listy i tak dalej.

Zaskakująco trudno jest to naprawić. I to jest jeden z powodów, dla których istnieją rzeczy takie jak wtyczka Tailwind Typography i Prose Stack Overflow — chociaż obsługują one znacznie więcej niż tylko pionowe odstępy.

Firefox obsługuje :has() za layout.css.has-selector.enabled flaga in about:config w momencie pisania.

Co sprawia, że ​​typograficzne odstępy pionowe są skomplikowane?

Z pewnością powinno to być tak proste, jak powiedzenie, że każdy element — p, h2, ul, itp. — ma pewien margines górny i/lub dolny… prawda? Niestety tak nie jest. Rozważ to pożądane zachowanie:

  • Pierwszy i ostatni element w bloku długiego tekstu nie powinien mieć dodatkowego miejsca powyżej ani poniżej (odpowiednio). Dzieje się tak, aby inne, nietypograficzne elementy były nadal umieszczane w przewidywalny sposób wokół długiej treści.
  • Sekcje w dłuższej treści powinny mieć ładny, duży odstęp między nimi. „Sekcja” będąca nagłówkiem i całą następującą po nim treścią, która należy do tego nagłówka. W praktyce oznacza to posiadanie ładnej, dużej przestrzeni przed nagłówkiem… ale nie jeżeli pozycja ta jest bezpośrednio poprzedzona inną pozycją!
Przykład nagłówka 3 po akapicie i innego nagłówka po nagłówku 2.
Chcemy mieć więcej miejsca nad nagłówkiem 3, gdy następuje on po elemencie typograficznym, takim jak akapit, ale mniej miejsca, gdy następuje bezpośrednio po innym nagłówku.

Nie musisz szukać dalej niż tutaj, w CSS-Tricks, aby zobaczyć, gdzie to może się przydać. Oto kilka zrzutów ekranu odstępów, które wyciągnąłem z innego artykułu.

Element nagłówka 2 bezpośrednio nad nagłówkiem 3.
Pionowy odstęp między nagłówkiem 2 a nagłówkiem 3
Element nagłówka 3 bezpośrednio następujący po elemencie akapitu.
Pionowa przestrzeń między nagłówkiem 3 a akapitem

Tradycyjne rozwiązanie

Typowe rozwiązanie, które widziałem, polega na umieszczeniu dowolnej długiej treści w opakowaniu div (lub znacznik semantyczny, jeśli ma to zastosowanie). Moje ulubione imię do klasy to .rich-text, którego chyba używam jako pozostałość po starszych wersjach CMS Pliszka, który dodałby tę klasę automatycznie podczas renderowania treści WYSIWYG. Typografia tylnego wiatru używa a .prose klasa (plus kilka klas modyfikatorów).

Następnie dodajemy CSS, aby zaznaczyć wszystkie elementy typograficzne w tym opakowaniu i dodać pionowe marginesy. Zwracając oczywiście uwagę na wspomniane powyżej specjalne zachowanie związane z ułożonymi w stos nagłówkami i pierwszym/ostatnim elementem.

Tradycyjne rozwiązanie brzmi rozsądnie… w czym problem? Myślę, że jest kilka…

Sztywna struktura

Konieczność dodania klasy opakowania, takiej jak .rich-text we wszystkich właściwych miejscach oznacza upieczenie w określonej strukturze do kodu HTML. Czasami jest to konieczne, ale wydaje się, że nie powinno tak być w tym konkretnym przypadku. Łatwo jest też zapomnieć o zrobieniu tego wszędzie tam, gdzie jest to potrzebne, zwłaszcza jeśli musisz użyć go do połączenia CMS i treści zakodowanych na stałe.

Struktura HTML staje się jeszcze bardziej sztywna, gdy chcesz mieć możliwość przycięcia górnego i dolnego marginesu odpowiednio pierwszego i ostatniego elementu, ponieważ muszą one być bezpośrednimi dziećmi elementu opakowania, np. .rich-text > *:first-child, Że > jest ważne — w końcu nie chcemy przypadkowo wybrać pierwszego elementu listy w każdym z nich ul or ol z tym wybierakiem.

Właściwości marginesu mieszania

W przed-:has() świecie, nie mieliśmy możliwości wybrania elementu na podstawie tego, co następuje po nim. Dlatego tradycyjne podejście do rozmieszczania elementów typograficznych polega na połączeniu obu margin-top i margin-bottom:

  1. Zaczynamy od ustawienia domyślnych odstępów dla elementów z margin-bottom.
  2. Następnie rozmieszczamy nasze „sekcje” za pomocą margin-top — czyli bardzo duża przestrzeń nad każdym nagłówkiem
  3. Następnie nadpisujemy te duże margin-tops, gdy bezpośrednio po nagłówku następuje inny nagłówek za pomocą sąsiedniego selektora rodzeństwa (np h2 + h3).

Nie wiem jak wy, ale ja zawsze uważałem, że przy rozmieszczaniu elementów lepiej jest stosować kierunek pojedynczego marginesu, generalnie faworyzując margin-bottom (to przy założeniu, CSS gap własność nie jest wykonalne, co nie jest w tym przypadku). Niezależnie od tego, czy to wielka sprawa, czy nawet prawda, pozwolę ci zdecydować. Ale osobiście wolałbym ustawiać margin-bottom do rozmieszczania długich treści.

Zapadające się marginesy

Z powodu zapadające się marginesy, ta mieszanka górnego i dolnego marginesu sama w sobie nie stanowi dużego problemu. Obowiązuje tylko większy z dwóch nałożonych marginesów, a nie suma obu marginesów. Ale… cóż… nie lubię zapadających się marginesów.

Zapadające się marże to jeszcze jedna rzecz, o której należy pamiętać. Może to być mylące dla młodszych programistów, którzy nie są na bieżąco z tym dziwactwem CSS. Odstępy całkowicie się zmienią (tj. przestaną się zapadać), jeśli zmienisz opakowanie na a flex układ z flex-direction: column na przykład, co nie miałoby miejsca, gdybyś ustawił pionowe marginesy w jednym kierunku.

Mniej więcej wiem, jak działają zwijające się marginesy, i wiem, że są tam z założenia. Wiem też, że czasami ułatwiały mi życie. Ale utrudnili to także innym razem. Po prostu myślę, że są trochę dziwni i generalnie wolę unikać polegania na nich.

Połączenia :has() rozwiązanie

A oto moja próba rozwiązania tych problemów za pomocą :has().

Podsumowując ulepszenia, które ma to wprowadzić:

  • Nie jest wymagana żadna klasa opakowania.
  • Współpracujemy z spójny kierunek marginesu.
  • Unika się zapadających się marginesów (co może, ale nie musi być poprawą, w zależności od twojego stanowiska).
  • Nie ma ustawiania stylów, a następnie natychmiastowego nadpisywania ich.

Uwagi i zastrzeżenia dot :has() rozwiązanie

  • Zawsze sprawdzaj obsługę przeglądarki. W momencie pisania Firefox obsługuje tylko :has() za eksperymentalną flagą.
  • Moje rozwiązanie nie zawiera wszystkich możliwych elementów typograficznych. Na przykład nie ma <blockquote> w moim democie Listę selektorów można jednak łatwo rozszerzyć.
  • Moje rozwiązanie również nie obsługuje elementów innych niż typograficzne które mogą być obecne w twoich konkretnych długich blokach tekstowych, np <img>. To dlatego, że w przypadku witryn, nad którymi pracuję, zwykle ograniczamy WYSIWYG do głównych węzłów tekstowych, takich jak nagłówki, akapity i listy. Wszystko inne — np. cytaty, obrazy, tabele itp. — jest oddzielnym blokiem komponentu CMS, a same te bloki są oddalone od siebie podczas renderowania na stronie. Ale znowu listę selektorów można rozszerzyć.
  • tylko uwzględniłem h1 dla kompletności. Zwykle nie pozwalałbym użytkownikowi CMS dodawać pliku h1 przez WYSIWYG, ponieważ tytuł strony zostałby upieczony gdzieś w szablonie strony, a nie wpisany w edytorze stron CMS.
  • Nie uwzględniam nagłówka, po którym bezpośrednio następuje ten sam nagłówek poziomu (h2 + h2). Oznaczałoby to, że pierwszy nagłówek nie „posiadałby” żadnej treści, co wydaje się niewłaściwym użyciem nagłówków (i popraw mnie, jeśli się mylę, ale to może naruszać WCAG 1.3.1 Informacje i relacje). Nie uwzględniam również pominiętych poziomów nagłówków, które są nieprawidłowe.
  • W żaden sposób nie odrzucam istniejących podejść, o których wspomniałem. Jeśli i kiedy zbuduję kolejną witrynę Tailwind, użyję doskonałej wtyczki Typography, bez wątpienia!
  • Nie jestem projektantem. Wymyśliłem te wartości odstępów, obserwując je. Prawdopodobnie mógłbyś (i powinieneś) użyć lepszych wartości.

Specyfika i struktura projektu

Zamierzałem napisać tutaj całą wielką rzecz o tym, jak tradycyjna metoda i nowa :has() sposób zrobienia tego może pasować do ITCSS metodologia… Ale teraz, kiedy mamy :where() (selektor o zerowej specyficzności) możesz teraz prawie wybrać preferowany poziom specyficzności dla dowolnego selektora.

To powiedziawszy, fakt, że nie mamy już do czynienia z opakowaniem — .prose, .rich-textitp. — wydaje mi się, że powinno to żyć w warstwie „elementów”, czyli zanim zaczniesz zajmować się specyfiką klasową. używałem :where() w moich przykładach, aby zachować spójność specyficzności. Wszystkie selektory w obu moich przykładach mają a wynik specyficzności of 0,0,1 (z wyjątkiem resetu bez kości).

Zamykając

Więc masz to, najnowocześniejsze rozwiązanie bardzo nudnego problemu! To nowsze podejście wciąż nie jest tym, co nazwałbym „prostym” CSS — jak powiedziałem na początku, jest to bardziej złożony temat, niż mogłoby się wydawać na początku. Ale oprócz kilku nieco skomplikowanych selektorów, myślę, że nowe podejście ma ogólnie większy sens, a mniej sztywna struktura HTML wydaje się bardzo atrakcyjna.

Jeśli w końcu użyjesz tego lub czegoś podobnego, chciałbym wiedzieć, jak to działa dla ciebie. A jeśli możesz wymyślić sposoby na ulepszenie tego, też chciałbym to usłyszeć!

Znak czasu:

Więcej z Sztuczki CSS