Nazewnictwo klas w CSS: kebab-case, BEM i inne konwencje

Większość nazw klas CSS pisze się małymi literami w kebab-case (.site-header, .price-tag), bo tak samo CSS nazywa własne elementy: background-color, font-size, flex-direction. BEM dokłada do tego stałą strukturę, block__element--modifier, więc klasa taka jak card__title--large mówi, do którego komponentu należy, którą jego częścią jest i jaki wariant pokazuje.
Poniżej: co w CSS rozróżnia wielkość liter, a co nie, dlaczego kebab-case stał się domyślny, jak naprawdę brzmią reguły nazewnictwa BEM i kiedy inne podejście sprawia, że BEM jest zbędny.
Co w CSS rozróżnia wielkość liter, a co nie
Własna składnia CSS nie rozróżnia wielkości liter w zakresie ASCII, więc COLOR: RED działa dokładnie tak samo jak color: red. Trzy rzeczy działają inaczej:
- Nazwy klas rozróżniają wielkość liter.
.Cardnie pasuje doclass="card". Jedynym wyjątkiem jest strona w trybie quirks, w którym przeglądarki dopasowują nazwy klas bez rozróżniania wielkości liter, co jest kolejnym powodem, by każdą stronę zaczynać od<!DOCTYPE html>. - Właściwości niestandardowe rozróżniają wielkość liter.
--brand-colori--Brand-Colorto dwie różne zmienne. - Nazwy elementów i atrybutów HTML jej nie rozróżniają. W dokumencie HTML
DIVidivwybierają te same elementy. W XML byłyby różne.
Najprostszy sposób, żeby nigdy więcej o tym nie myśleć, to pisać wszystkie nazwy klas i właściwości niestandardowych małymi literami. O tym, jak z wielkością liter radzą sobie inne języki i systemy plików, przeczytasz w naszym porównaniu wrażliwości na wielkość liter.
Dlaczego kebab-case wygrał w nazwach klas CSS
Nic w CSS nie narzuca kebab-case nazwom klas. .cardTitle i .card_title są poprawne. Myślniki mają sens, bo wszystko wokół nazwy klasy już ich używa: nazwy właściwości, wartości (jak space-between) i atrybuty HTML, takie jak aria-label i data-user-id. Klasa w camelCase w arkuszu pełnym myślników wygląda jak literówka.
JavaScript to miejsce, w którym kebab-case staje się kłopotliwy, bo myślniki nie są dozwolone w identyfikatorach. Przeglądarka radzi sobie z własnymi przypadkami: data-user-id jest odczytywane jako element.dataset.userId, a background-color staje się element.style.backgroundColor. Nazwy klas zwykle pozostają ciągami znaków (classList.add("is-open")), więc im to nie przeszkadza.
Wyjątkiem są moduły CSS (CSS Modules). Moduł CSS eksportuje nazwy klas do JavaScriptu jako obiekt, więc .card-title trzeba zapisać jako styles["card-title"], a .cardTitle staje się czystszym styles.cardTitle. To jedyna konfiguracja, w której przestawilibyśmy nazwy klas na camelCase.
Nazewnictwo BEM: blok, element, modyfikator
BEM (Block, Element, Modifier) wywodzi się z Yandexa. Strona z historią metodologii wiąże początki prac z rokiem 2005, za początek BEM uznaje wydanie frameworku Lego 2.0 przez Yandex w marcu 2009 roku, a przejście na open source datuje na rok 2010. Reguły nazewnictwa są krótkie:
- Blok (block): samodzielny komponent, który ma sens sam w sobie, taki jak
.card,.search-formczy.menu. - Element: część bloku, która nie ma znaczenia poza nim; nazwa to blok, dwa podkreślniki, element:
.card__title,.menu__item. - Modyfikator (modifier): flaga zmieniająca wygląd, zachowanie lub stan; nazwa to blok lub element, dwa myślniki, modyfikator:
.card--featured,.card__button--disabled. Modyfikator może też być parą klucz i wartość:.form--theme-xmas.
Wyrazy wewnątrz każdej części oddziela się pojedynczymi myślnikami, jak w .search-form__submit-button. Oto cały komponent:
<article class="card card--featured">
<h2 class="card__title">Spring sale</h2>
<p class="card__body">Everything 20% off this week.</p>
<a class="card__button card__button--disabled" href="/sale">Shop now</a>
</article>.card { border: 1px solid #ddd; padding: 1rem; }
.card--featured { border-color: gold; }
.card__title { font-size: 1.25rem; }
.card__button--disabled { opacity: 0.5; pointer-events: none; }Klasyczny BEM a styl z dwoma myślnikami
Jeśli zajrzysz na oficjalną stronę metodologii, bem.info, modyfikatory wyglądają inaczej. Klasyczny schemat oddziela modyfikatory pojedynczym podkreślnikiem: menu_hidden, menu__item_visible, menu__item_type_radio. Wersja z -- jest tam wymieniona jako alternatywa, „Two Dashes style” (styl dwóch myślników), i to ją opisuje getbem.com. Obie są poprawnym BEM; tylko nie mieszaj ich w jednej bazie kodu. bem.info zwraca też uwagę na drobny haczyk związany z myślnikami: podwójny myślnik w komentarzu HTML może nie przejść walidacji.
Reguły BEM, w których łatwo się pomylić
- Żadnych elementów elementów. Tytuł wewnątrz nagłówka karty to nadal
card__title, a niecard__header__title. W HTML elementy możesz zagnieżdżać tak głęboko, jak chcesz; nazwy pozostają płaskie. Uzasadnienie bem.info: przy nazwach łańcuchowych nie można przenieść, dodać ani usunąć elementu bez zmiany istniejącego kodu. - Modyfikatory nigdy nie występują samodzielnie. Pisz
class="button button--primary", a nieclass="button--primary". Modyfikator tylko uzupełnia klasę bazową. - Używaj selektorów z jedną klasą.
.card__title, a nie.card .card__titleanih2.card__title. Przyjętym wyjątkiem jest modyfikator bloku, który zmienia jego elementy:.card--featured .card__title. - Nazywaj modyfikatory według celu. To nasza opinia, a nie reguła BEM:
--featuredalbo--errorprzetrwają redesign, a--yellowstaje się kłamstwem w dniu, w którym zmienia się kolor marki. - Oddziel punkty zaczepienia dla JavaScriptu od klas stylów. To również nasza opinia: wybieraj elementy w skryptach atrybutem data, takim jak
data-action="close", a nie klasą.card__button, żeby zmiana nazwy klasy ze względów stylistycznych nie mogła zepsuć działania.
Późniejsza zmiana nazwy bloku to w większości wyszukiwanie i zamiana. Wklej szablon do naszego narzędzia Znajdź i zamień, włącz tryb regex i zamień card(__|--) na product-card$1: każda klasa elementu i modyfikatora przejdzie na nową nazwę bloku, a liczba dopasowań powie Ci, ile ich zmieniono. Samą klasę card zmień osobno, żeby nie złapać niepowiązanych wyrazów.
Pisanie BEM w Sass i w natywnym zagnieżdżaniu CSS
Selektor rodzica w Sass potrafi dodać sufiks do zewnętrznego selektora, a jego dokumentacja używa BEM jako przykładu. Natywne zagnieżdżanie CSS tego nie potrafi i MDN zwraca na to wprost uwagę: zagnieżdżone &__title jest odczytywane jako selektor typu, więc nie wyprodukuje .card__title.
// Sass: works
.card {
&__title { font-size: 1.25rem; } // compiles to .card__title
&--featured { border-color: gold; } // compiles to .card--featured
}/* Native CSS nesting: write the full class names */
.card {
border: 1px solid #ddd;
&.card--featured { border-color: gold; } /* .card.card--featured */
}
.card__title { font-size: 1.25rem; }Poza BEM: kiedy go nie potrzebujesz
BEM istnieje po to, by nazwy klas nie kolidowały i by struktura była widoczna w globalnym arkuszu stylów. Jeśli coś innego rozwiązuje problem kolizji, jego długie nazwy dają mniej:
- Moduły CSS (CSS Modules) domyślnie ograniczają nazwy klas do zakresu lokalnego, więc zwykłe
.titlew jednym komponencie nie zderzy się z.titlew innym. - CSS oparty na klasach użytkowych (utility-first), jak Tailwind, w dużej mierze zastępuje własne nazwy klas gotowymi klasami użytkowymi, takimi jak
font-bold, więc nazewnictwo przenosi się do Twoich komponentów. - Małe witryny z garstką komponentów często dobrze radzą sobie ze zwykłym kebab-case i prefiksem dla każdego komponentu.
Nawet wtedy kebab-case pozostaje bezpiecznym domyślnym wyborem dla każdej nazwy klasy, którą piszesz.
Zasady praktyczne
- Małe litery w kebab-case dla nazw klas i właściwości niestandardowych, zawsze.
- BEM, gdy wiele osób dzieli jeden globalny arkusz stylów:
block__element--modifier, płaskie nazwy elementów, modyfikatory obok klasy bazowej. - Nazwy klas w camelCase tylko wtedy, gdy narzędzie takie jak moduły CSS udostępnia je JavaScriptowi.
Zamieniasz nazwę komponentu na klasę? Wpisz „Product Card” do naszego konwertera wielkości liter, a przycisk kebab-case da Ci product-card. Konwencje dla wszystkich pozostałych warstw projektu, od zmiennych środowiskowych po adresy URL, znajdziesz w naszej ściągawce ze stylami nazewnictwa dla programistów.


