Kobieta w ciąży używa laptop w biurze

Metodyka BEM w CSS – jak nazywać klasy i porządkować style

12 min. czytania

Metodyka BEM w CSS to jedna z najskuteczniejszych dróg do zapanowania nad rozrastającymi się arkuszami stylów i wprowadzenia w projekcie jasnych, wspólnych zasad nazewnictwa klas. BEM nie jest ani frameworkiem, ani preprocesorem, lecz pragmatyczną metodyką opisu komponentów interfejsu za pomocą przejrzystych klas CSS, opartych na trzech pojęciach: blok, element i modyfikator.

To rozbudowany, praktyczny przewodnik po BEM z naciskiem na to, jak nazywać klasy, jak porządkować style i jak łączyć BEM z dostępnością.

Czym jest BEM i po co ci ta metodyka?

BEM to skrót od Block, Element, Modifier (blok, element, modyfikator). To metodyka nazewnictwa klas CSS – korzystasz z niej w zwykłym HTML i CSS (lub Sass/LESS/PostCSS), a nie osobna technologia.

Główne cele BEM to:

  • uporządkowanie i ustandaryzowanie nazw klas,
  • poprawa modularności i wielokrotnego użycia komponentów,
  • łatwiejsze skalowanie projektów i praca w większych zespołach,
  • ograniczenie konfliktów stylów dzięki jednoznacznym nazwom.

Zamiast przypadkowych klas typu .left, .box2, .menuNew, BEM zachęca do myślenia w kategoriach komponentów (bloków) i ich części (elementów) z jasno opisanymi wariantami (modyfikatorami).

Podstawowe pojęcia – blok, element, modyfikator

Blok

Blok to samodzielny komponent interfejsu, który ma sens bez kontekstu innych elementów. Jest niezależny – możesz przenieść go w inne miejsce lub do innego projektu i nadal będzie działał.

Przykłady bloków:

  • .menu,
  • .site-header,
  • .article-card,
  • .button,
  • .form.

Nazwa bloku powinna opisywać jego funkcję, a nie wygląd (np. .button, a nie .blue-box).

Element

Element to część bloku, która nie ma sensu bez tego bloku.

Konwencja nazwy elementu wygląda następująco:

.block__element

Między nazwą bloku a elementu stosujemy podwójne podkreślenie __.

Przykłady elementów z opisem roli:

  • .menu__item – pojedynczy element listy w bloku .menu;
  • .menu__link – link w menu;
  • .article-card__title – tytuł w karcie artykułu;
  • .article-card__meta – metadane wpisu (autor, data);
  • .form__field – pole formularza;
  • .form__error – komunikat błędu.

Nazwa elementu zawsze zaczyna się nazwą bloku, dzięki czemu od razu wiadomo, do czego należy.

Modyfikator

Modyfikator opisuje wariant wyglądu lub zachowania bloku albo elementu.

Konwencja nazwy modyfikatora wygląda tak:

.block--modifier
.block__element--modifier

Modyfikator oddzielamy od nazwy bloku/elementu podwójnym myślnikiem --.

Przykłady modyfikatorów:

  • .button--primary, .button--secondary,
  • .button--disabled,
  • .menu__item--active,
  • .article-card--featured.

Modyfikator nie istnieje samodzielnie – zawsze jest „nadbudową” nad istniejącą klasą bloku lub elementu. W HTML stosuje się go obok klasy bazowej, np.:

<button class="button button--primary">Kup teraz</button>

Dzięki temu klasa .button zawiera podstawowy wygląd, a .button--primary nadpisuje tylko to, co trzeba.

Konwencja nazewnictwa klas w BEM

Metodyka BEM nie narzuca jednego „oficjalnego” sposobu zapisu nazw, ale praktyka wypracowała spójne schematy. Najczęściej stosowane wzorce są następujące:

  • blok.block;
  • blok + element.block__element;
  • blok + modyfikator.block--modifier;
  • element + modyfikator.block__element--modifier;
  • pełny przykład.nazwa-bloku__nazwa-elementu--nazwa-modyfikatora.

Separator słów w nazwach

Wieloczłonowe nazwy klas zapisuje się zwykle małymi literami z myślnikami (kebab-case), np. .site-header, .main-nav, .article-card__meta-info. Metodyka BEM nie narzuca konkretnego stylu dla wielowyrazowych nazw – ważna jest konsekwencja i czytelne rozróżnienie bloku, elementu i modyfikatora. Praktycznie najczęściej wybiera się kebab-case, rzadziej camelCase w CSS.

Ogólne zasady pisania CSS z BEM

Materiały o BEM dość zgodnie wskazują kilka kluczowych zasad, które pomagają utrzymać kod w ryzach. Trzymanie się ich upraszcza nadpisywanie stylów i zmniejsza specyficzność selektorów.

Styluj tylko po klasach

W BEM nie stylujemy po ID ani po nazwach elementów HTML (np. nav, footer, h1). Zamiast selektorów ogólnych, pisz:

nav ul li a { … }
#main-menu li a { … }

Używaj klas BEM wprost:

.menu { … }
.menu__item { … }
.menu__link { … }

Zasada „tylko klasy” zmniejsza specyficzność selektorów i ułatwia nadpisywanie stylów bez uciekania się do !important.

Unikaj selektorów potomnych dla bloków i elementów

W kontekście BEM nie stosujemy selektorów potomnych dla bloków i elementów, by nie wprowadzać zbędnej złożoności.

Przykłady do unikania:

.menu li a { … } /* źle w kontekście BEM */
.menu__item a { … } /* też niewskazane */
.menu .menu__link { … } /* niepotrzebna złożoność */

Zamiast tego styluj bezpośrednio po klasie:

.menu__link { … }

Wyjątek bywa stosowany dla modyfikatorów bloków jako specjalnych wariantów:

.menu--vertical .menu__item {
display: block;
}

Jedna klasa BEM na element + modyfikatory

Każdy element HTML powinien mieć jedną „klasę bazową” BEM, a do tego ewentualne modyfikatory. Oto poprawne i błędne przykłady:

<!-- OK: blok + modyfikator -->
<button class="button button--primary">Kup teraz</button>

<!-- OK: element + modyfikator -->
<li class="menu__item menu__item--active">
<a class="menu__link" href="#">Strona główna</a>
</li>

<!-- Źle: dwa różne bloki na jednym elemencie -->
<div class="card form">…</div>

Taki model wymusza „czyste” komponenty – jeden element = jeden komponent BEM, bez przypadkowego mieszania kontekstów.

BEM a klasy do JavaScriptu

W wielu projektach stosuje się oddzielne klasy techniczne do JS (np. js-modal, js-toggle), aby nie uzależniać logiki od klas prezentacyjnych. To naturalne rozszerzenie praktyki BEM i dbałość o rozdzielenie warstw.

Praktyczne przykłady nazewnictwa i stylowania w BEM

Proste menu nawigacyjne

HTML:

<nav class="menu" aria-label="Główne menu">
<ul class="menu__list">
<li class="menu__item menu__item--active">
<a class="menu__link" href="/">Strona główna</a>
</li>
<li class="menu__item">
<a class="menu__link" href="/oferta">Oferta</a>
</li>
<li class="menu__item">
<a class="menu__link" href="/kontakt">Kontakt</a>
</li>
</ul>
</nav>

CSS:

.menu {
display: flex;
}
.menu__list {
list-style: none;
display: flex;
gap: 1rem;
margin: 0;
padding: 0;
}
.menu__item {
/* styl bazowy elementu listy */
}
.menu__item--active .menu__link {
font-weight: 700;
}
.menu__link {
text-decoration: none;
color: #222;
}

Zwróć uwagę na przypisanie ról klasom:

  • .menu – blok,
  • .menu__list, .menu__item, .menu__link – elementy,
  • .menu__item--active – modyfikator elementu (stan aktywny).

Karta artykułu

HTML:

<article class="article-card">
<a class="article-card__image-link" href="/artykul">
<img class="article-card__image" src="…" alt="Opis grafiki">
</a>
<div class="article-card__content">
<h2 class="article-card__title">
<a class="article-card__title-link" href="/artykul">
Tytuł artykułu
</a>
</h2>
<p class="article-card__excerpt">
Krótkie zajawienie treści artykułu…
</p>
<p class="article-card__meta">
<span class="article-card__author">Jan Kowalski</span>
<time class="article-card__date" datetime="2026-06-01">
1 czerwca 2026
</time>
</p>
</div>
</article>

CSS:

.article-card {
display: flex;
gap: 1rem;
}
.article-card__image-link {
flex-shrink: 0;
}
.article-card__image {
display: block;
max-width: 100%;
height: auto;
}
.article-card__content {
flex: 1;
}
.article-card__title {
font-size: 1.25rem;
margin: 0 0 .5rem;
}
.article-card__title-link {
color: inherit;
text-decoration: none;
}
.article-card__excerpt {
margin: 0 0 .5rem;
}
.article-card__meta {
font-size: .875rem;
color: #666;
}

Jeśli potrzebujesz wariantu wyróżnionego, dodaj modyfikator:

<article class="article-card article-card--featured">…</article>

.article-card--featured {
border: 2px solid #0b74de;
background: #f2f7ff;
}

Formularz z błędami walidacji

HTML:

<form class="form" novalidate>
<div class="form__field form__field--error">
<label class="form__label" for="email">Adres e-mail</label>
<input class="form__input" type="email" id="email" name="email" aria-invalid="true" aria-describedby="email-error">
<p class="form__error" id="email-error">
Podaj poprawny adres e-mail.
</p>
</div>
<button class="button button--primary form__submit" type="submit">
Wyślij
</button>
</form>

CSS:

.form {
max-width: 400px;
}
.form__field {
margin-bottom: 1rem;
}
.form__label {
display: block;
margin-bottom: .25rem;
}
.form__input {
width: 100%;
padding: .5rem;
border: 1px solid #ccc;
}
.form__field--error .form__input {
border-color: #d93025;
}
.form__error {
margin-top: .25rem;
color: #d93025;
font-size: .875rem;
}
/* button jako osobny blok */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: .5rem 1rem;
border-radius: .25rem;
border: none;
cursor: pointer;
}
.button--primary {
background: #0b74de;
color: #fff;
}

W tym przykładzie role klas są następujące:

  • .form to blok formularza,
  • .form__field, .form__label, .form__input, .form__error, .form__submit to elementy,
  • .form__field--error to modyfikator elementu, który zmienia wygląd pola z błędem.

Organizacja plików CSS/SCSS w projektach z BEM

BEM naturalnie prowadzi do modułowego podziału arkuszy stylów – możesz przypisać osobny plik do każdego bloku, co ułatwia pracę w zespole i skalowanie projektu.

Typowy układ (np. w Sass) może wyglądać tak:

styles/
base/
_reset.scss
_variables.scss
_typography.scss
blocks/
_button.scss
_menu.scss
_article-card.scss
_form.scss
pages/
_home.scss
_article.scss
main.scss

W pliku main.scss importujesz moduły:

@use 'base/reset';
@use 'base/variables';
@use 'base/typography';

@use 'blocks/button';
@use 'blocks/menu';
@use 'blocks/article-card';
@use 'blocks/form';

@use 'pages/home';
@use 'pages/article';

Każdy plik w blocks/ zawiera kompletną definicję bloku – wszystkie jego elementy i modyfikatory. Przykład:

// _menu.scss
.menu { display: flex; }
.menu__list { … }
.menu__item { … }
.menu__item--active { … }
.menu__link { … }

Taka struktura przynosi konkretne korzyści:

  • ułatwia odnajdywanie stylów (szukasz bloku → otwierasz konkretny plik),
  • zachęca do ponownego użycia komponentów,
  • redukuje konflikty między blokami.

BEM a dostępność (a11y)

BEM organizuje klasy i kod CSS, a semantykę niesie HTML. Razem wspierają utrzymanie dostępnego interfejsu, zwłaszcza w większych projektach.

BEM nie zastępuje semantycznego HTML

Choć w BEM stylujemy po klasach (np. .menu, .menu__item, .menu__link), nadal należy używać semantycznych znaczników i atrybutów dostępności, np. <nav aria-label="Główne menu">, <button> zamiast <div> odgrywającego rolę przycisku. Semantyka pochodzi z HTML, a BEM porządkuje CSS.

Nazwy klas a zrozumiałość struktury

Nazwy w BEM są opisowe i związane z funkcją, a nie z kolorem czy położeniem. Na przykład .form__error jest czytelniejsze niż .text-red, a .menu__item--active jasno komunikuje bieżącą pozycję menu.

Ma to kilka zalet:

  • ułatwia utrzymanie spójnych stanów ARIA (np. aria-current="page" powiązane z .menu__item--active),
  • osobom testującym z czytnikami ekranu łatwiej wskazuje elementy do weryfikacji,
  • w dokumentacji komponentów (np. Storybook) klasy BEM pomagają jasno nazwać części komponentu.

Stany komponentów – BEM + atrybuty ARIA

Modyfikatory świetnie opisują stany interakcji i mogą iść w parze z ARIA.

Przykłady użycia w HTML:

<button class="button button--primary button--pressed" aria-pressed="true">
Włącz
</button>

<button class="button button--primary button--disabled" aria-disabled="true" disabled>
Niedostępny
</button>

Kluczem jest spójność – jeżeli masz modyfikator stanu w BEM, dodaj odpowiadający atrybut ARIA lub natywny (np. disabled, checked), aby zakomunikować stan technologiom asystującym.

Jak nadawać dobre nazwy klas w BEM (praktyczne wytyczne)

W BEM liczy się wyraźne rozróżnienie blok/element/modyfikator, ale to jakość nazewnictwa przesądza o czytelności i skalowalności projektu.

Nazwy oparte na funkcji, nie wyglądzie

Warto, by nazwy klas były konkretne, zrozumiałe i jednoznaczne. Oto lepsze wybory:

  • .button, .button--primary zamiast .blue-btn,
  • .alert--error zamiast .alert--red,
  • .layout__sidebar zamiast .layout__left.

Wygląd może zmieniać się w czasie (kolor brandu, układ), natomiast funkcja pozostaje stała – i to ją warto zakodować w nazwie.

Bloki – rzeczowniki

Bloki nazywamy zwykle rzeczownikami lub rzeczownikami z przymiotnikami. Przykłady:

  • .site-header,
  • .main-nav,
  • .article-card,
  • .login-form,
  • .modal.

Nazwa powinna opisywać rolę komponentu na stronie i być na tyle ogólna, by blok dało się wykorzystać w więcej niż jednym miejscu.

Elementy – rola w obrębie bloku

Elementy nazywamy według ich funkcji w bloku. Przykłady grup elementów:

  • .article-card__title, .article-card__excerpt, .article-card__meta,
  • .menu__item, .menu__link, .menu__list,
  • .form__label, .form__input, .form__error.

Jeśli nazwy elementów zaczynają się nadmiernie rozrastać (np. .article-card__header-title-large), rozważ wydzielenie nowego bloku wewnętrznego zamiast dokładania kolejnych członów.

Modyfikatory – przymiotniki i stany

Modyfikatory opisują warianty wyglądu (np. .button--primary, .alert--warning) i stany (np. .menu__item--active, .accordion__item--expanded). Zadbaj, by nazwy dotyczyły znaczenia (np. --error) zamiast samego koloru (np. --red).

Dobry modyfikator zazwyczaj:

  • jest binarny (ma/nie ma danej cechy),
  • odnosi się do znaczenia, a nie do koloru czy położenia,
  • nie duplikuje bez sensu nazwy bloku.

Typowe problemy i antywzorce w BEM

Zbyt głębokie zagnieżdżenie elementów

Antywzorzec z nazewnictwem rosnącym kaskadowo:

<div class="card">
<div class="card__header">
<span class="card__header-title">Tytuł</span>
</div>
</div>

Duże zagnieżdżenia typu __header-title-text-icon utrudniają utrzymanie. Jeśli część interfejsu zaczyna żyć własnym życiem, rozważ wydzielenie nowego bloku:

<div class="card">
<header class="card-header">
<h2 class="card-header__title">Tytuł</h2>
</header>
</div>

card-header staje się osobnym blokiem – potencjalnie wielokrotnego użytku.

Mieszanie wielu bloków na jednym elemencie

Unikaj sytuacji typu <div class="card form">…</div>. Taki element próbuje być jednocześnie dwoma blokami, co sugeruje pomieszanie odpowiedzialności. Lepiej umieścić blok .form wewnątrz .card albo uczynić z tego wariant jednego bloku (np. .card--with-form).

Pół-BEM, pół-cokolwiek

W legacy CSS często widać miks konwencji. Refaktoryzację warto przeprowadzać stopniowo:

  1. Wybierz jedną konwencję BEM (np. kebab-case, __, --).
  2. Stopniowo przenoś komponenty do BEM: sekcja po sekcji, blok po bloku.
  3. Nie dodawaj nowych klas niezgodnych z BEM – inaczej nigdy nie uporządkujesz kodu.

BEM w połączeniu z innymi podejściami (utility-first, frameworki)

Wielu deweloperów łączy BEM z innymi podejściami. Najczęstsze kombinacje to:

  • klasy utility-first (np. .u-mb-1, .u-text-center),
  • frameworki komponentowe (Bootstrap, Foundation),
  • metodologie organizacji (ITCSS, SMACSS).

Praktyczne kompromisy sprawdzające się w dużych projektach:

  • komponenty złożone (karty, formularze, nawigacje) – pisz w BEM,
  • drobne odstępy i wyrównania – dopuszczaj klasy narzędziowe,
  • klasy frameworka traktuj jako „zewnętrzne bloki”, a wewnątrz – jeśli trzeba – dodaj własne elementy BEM.

Konsekwencja i jasna dokumentacja kiedy stosujemy BEM, a kiedy klasy narzędziowe, to klucz do utrzymania porządku w kodzie.