Styl, konwencje i metodologie CSS – część 2. Sass, BEM, rscss, SMACSS, WTF?

Poziom zaawansowany

Ciąg dalszy tematyki poruszanej w części I. Dziś przyjrzymy się kolejnym metodologiom pisania CSS, takim jak BEM, rscss, SMACSS. Należy dodać, że metodologie możemy łączyć (np. OOCSS + BEM). O ile oczywiście ma to dla nas sens. Do rzeczy.

Metodologie CSS: SMACSS

Zaczniemy od SMACSS, choć od razu powiem, że nie pracowałem z tym przy realnym projekcie. Nie chodzi o to, że SMACSS jest kiepski. Ta metodologia po prostu mi nie odpowiada; z pewnością jest nie bez zalet, jednak niektóre jej detale dla mnie są irytujące. ALE może oczywiście odpowiadać innym.

SMACSS (Scalable and Modular Architecture for CSS), którego autorem jest Jonathan Snook, to style guide dla kodu CSS, które organizuje kod CSS według kategorii (Base, Layout, Module, State, Theme). Przyjrzyjmy się im bliżej.

Base

Style bazowe, ogólne. Ta grupa stylów odpowiada za wartości domyślne; domyślny wygląd elementów strony (marginesy, inputy, fonty, przykład: normalize.scss).

Layout

Style layoutu definiują wygląd i rozmieszczenie typowych dla stron i aplikacji WWW elementów, takich jak header, footer, sidebar. W SMACSS (i nie tylko) przyjęło się używać dla nich prefixu „l-„.

Module

Moduły w SMACSS to niezależne, re-używalne bloki kodu (UI), takie jak menu, search box, listy pozycji, etc. Można powiedzieć że są czymś w rodzaju bloków w BEM. Moduły zwykle znajdują się w kontenerach, wewnątrz elementów layoutu.

Założenie jest takie, że każdy moduł powinien być niezależny od pozostałych modułów. Powinien on też działać tak samo w każdym kontenerze, w jakim go umieścimy. Nie należy zatem tworzyć stylów uzależnionych od kontenera nadrzędnego.

Przykładem może być moduł menu:

...

.menu {
  background: #00a;
}

.menu h1 {
  font-size: 20px;
}

.menu .menu-item {
  border: 1px solid #000;
}
...

State

Style stanu po prostu określają wygląd stanu, w którym element może się znajdować, zależnie od sytuacji (hidden, expanded, etc). Zwijamy lub rozwijamy listę, najeżdżamy kursorem na element, i inne takie sytuacje.

Taka sytuacja

Taka sytuacja

Przykład:

<div class="menu is-expanded">
  ...
</div>

Zaleca się takie stany logiczne definiować z prefixem „is-„, np. is-active.

Theme

Themes zwykle znajdziemy w większych projektach, gdzie występują warianty (np. daily, nightly, sexy, sunny, etc), które określają różny wygląd (look and feel) dla elementów modułów i layoutów projektu.

SMACSS – przykład 1:

// Layouts
.l-default {}

// Modules 
.m-accordion {}

// States, mostly with prefix like &quot;is-&quot;
.is-active {}
.is-hidden {}

SMACSS – przykład 2 – mediaqueries:

.m-navigation {
  &:after {
    @media screen and (min-width: 767px) {
      content: '';
      display: inline-block;
      width: 100%;
    }
  }

  li {
    display: block;

    @media screen and (min-width: 767px) {
      float: left;
    }
  }
}

Zobacz także

Ten autor stworzył własny style guide na bazie SMACSS i OOCSS:

https://github.com/timhartmann/Scss-Styleguide

Z kolei autor SMACSS’a prowadzi stronę (https://smacss.com), gdzie możemy kupić lub przeczytać za darmo on-line, książkę poświęconą tej metodologii:

https://smacss.com/book


Chodźmy poznać kolejne metodologie. Nie jest też niespodzianką, że znajdziemy podobieństwa między nimi.

Metodologie CSS: rscss

Kolejnym podejściem jest to prezentowane przez rscss. W zasadzie jest to całkiem prosta metodologia, mniejsza w porównaniu do innych. Myślę że szybko można zacząć jej używać w praktyce. Godna polecenia. A jak nie wiesz od jakiej metodyki zacząć swoją przygodę i eksperymenty, śmiało można zacząć właśnie od rscss.

RSCSS – Reasonable System for CSS Stylesheet Structure to CSS style guide z prostymi założeniami. W rscss każdy „building block” jest komponentem. Obok komponentów rozróżniamy również: elementy, warianty oraz helpery.

Component – pojedynczy „kawałek” UI, re-używalny bez problemu w wielu miejscach aplikacji. Zawiera elementy.

Element – tagi HTML wewnątrz komponentów.

Variants – wariacje domyślnego wyglądu (look and feel) elementu.

Helpers – utils, narzędzia ogólnego przeznaczenia.

Tworząc nieduży front-end z Bootstrap, rscss oraz Slim templates (http://slim-lang.com), nie podobał mi się prefix zalecany dla helperów „_”. Dziwnie wyglądał kod np.:


.clearfix
._divided10

Obecnie, niezależnie od metodologii, moje helpery mają prefix h-, np.:

.h-divided10

Prosty przykład:

// ----------------------------------------------------------------
// Search form component.
// ----------------------------------------------------------------

.search-box {
  > .label {
    font-weight: 500;
    color: $color-base-blue;
  }

  > .field {
    // ...
  }

  // > input ...

  > .button {
    // ...
  }

  // Variants.
  &.-basic {
    color: $color-base-red;
  }

  &.-extended {
    color: $color-base-blue;
  }

  &.-hidden {
    display: none;
  }

}

Zachęcam do przeczytania:

http://rscss.io

https://github.com/rishabhp/rscss

Wydawnictwo Strefa Kursów

Metodologie CSS: BEM

Wreszcie pora na BEM. Skrót pochodzi od Block Element Modifier. Metodologia stworzona przez developerów z Yandex. Jest to w zasadzie dość proste podejście do tworzenia modularnego, re-używalnego kodu CSS.

BEM uwalnia nas od kaskadowości – aplikujemy te style, które są faktycznie potrzebne; bloki stałe nie zależą od innych elementów na stronie. W ten sposób o wiele łatwiej nie tylko utrzymywać i modyfikować kod, ale również przenosić (bloki) w inne miejsca, a nawet do innego projektu.

W założeniach BEM ma być:

– prosty – podstawą jest używanie BEM’owskiej konwencji nazewniczej

– modularny – niezależne bloki i selektory CSS

– elastyczny – mamy spore pole manewru, aby uczynić BEM wygodnym – tak jak lubimy

Warto również dodać, że BEM powstał dla projektów, które muszą być wypuszczone szybko, ale utrzymywane w długiej (long-term) perspektywie.

W BEM nasz kod widzimy jako:

– bloki – na przykład prosty, stylizowany przycisk, menu lub rozbudowany formularz – myślimy o bloku jako o komponencie re-używalnym w naszym projekcie (przykłady: header, container, menu, checkbox, input)

– elementy – poszczególne elementy, np. przycisk składa się z odnośnika, z tekstem oraz obrazkiem, formularz posiada elementy typu input, itd… (przykłady: menu item, list item, checkbox caption, header title)

– modyfikatory – to po prostu dodatkowe klasy, które modyfikują wygląd bloku lub elementu zależnie od stanu, np. aktywna pozycja menu po najechaniu kursorem (przykłady: disabled, highlighted, checked, fixed, size big, color yellow)

Metodologia BEM daje prostą i zrozumiałą strukturę. Style tworzymy w oparciu o konwencje nazw.

BEM – konwencja nazewnicza

W BEM istnieje pewna konwencja nazewnictwa klas CSS:

.block – pierwsze słowo w nazwie oznacza, że klasa dotyczy danego bloku

__element – dwa podkreślenia oznaczają, że dana klasa dotyczy elementu

–modifier – dwa myślniki definiuje modyfikator

Przykład:

// Blok
.block {}

// Element powiązany z blokiem
.block__element {}

// Modyfikator bloku
.block--modifier {}

// Modyfikator elementu
.block__element--modifier {}

To wszystko w kwestii podstawowej konwencji nazw. Naszym zadaniem jest po prostu zastanowić się, jak zorganizować style, aby elegancko i wygodnie odwzorować wymagania naszego projektu.

Można również powiedzieć, że BEM skłania nas do pomyślenia dwa razy, zanim skomplikujemy rzeczy bez potrzeby. Przykłady poniżej.

Blok reprezentuje obiekt, np.:
– osoba
– menu
– formularz logowania
– search box

itp.

Element to część bloku, która wykonuje określoną funkcję w jego kontekście, np.:
– głowa (osoby)
– pozycja menu
– przycisk lub label (formularza logowania)
– pole input (formularza szukania)

itp.

Modyfikator określa sposób reprezentacji wariacji bloku lub elementu, np.:
– wysoka / niska osoba
– active – podświetlona pozycja menu
– expanded / collapsed input – nieużywany input formularza szukania jest „krótki” lub „schowany”, jednak rozszerza się znacząco, gdy chcemy coś wyszukać

itp.

Przykłady:

// Menu
.menu {}
.menu__item {}
.menu__item--is-active {}

// Osoba
.person {}
.person__head {}
.person__hand {}
.person--female {}
.person__hand--left {}

W ten sposób wiemy, że głowa lub ręka odnosi się do kontekstu osoby. Po dekapitacji moglibyśmy mieć osobne klasy, np.:

.head {}
.left-hand {}

Tyle tylko, że głowa może należeć do osoby, ale również do psa, kota, małpy, itd. I w każdym przypadku wyglądać będzie inaczej 🙂

W Sass, cechy tego pre-procesora wspomagają znacząco pisanie kodu zgodnego z BEM.

Przykład – mixin:

@mixin button {
  padding: 0.6em 0.7em;
  // style ...
}

.button {
  @include button;
  background-color: $color-red;
}

.button--secondary {
  @include button;
  background-color: $color-green;
}

BEM – dobre praktyki

W pośpiechu możemy szybko zacząć pisać dziwaczne, bądź niepotrzebnie skomplikowane style. Dla BEM jest garść zasad, zdefiniowanych przez używających go programistów, tak aby unikać pułapek.

A. Stosujmy unikalne nazwy dla bloków (klas CSS).

B. Nie stosujmy selektorów ID, ani selektorów tagów HTML i/lub opartych o kaskadowość (selektory potomka)

Czyż nie lepiej używać selektora w stylu

.score–in-progress

zamiast

#scoreboard > li > b + span

C. Prefixed namespace. Czy stosować jeszcze jakieś prefixy? Jak najbardziej można.

Np. dla modyfikatorów stosujemy ‚is-‚ lub ‚has-‚:

.is-active {}
.is-hover {}
.is-dragged {}

Dla selektorów do pracy z JavaScript, możemy dodać prefix ‚js-‚:

.js-sortable {}
.js-is-active {}
.js-drag-and-drop {}

Dla helperów ‚h-‚, dla layoutów ‚l-‚, itp.

bem-types-prefixes

D. Don’t chain BEM elements – czyli jeśli np. nasz klasa wygląda tak:

.form__row__input

coś nie gra – lepiej powiązać „elementy-wnuki” bezpośrednio z blokiem, lub stworzyć dla nich po prostu osobny blok.

W BEM tworzymy własną, wirtualną przestrzeń nazw (namespace) dla określonych elementów, a dzięki temu unikamy konfliktów (np.: .nav__item {} oraz .menu__item {}, a nie .item {}).

Ale…

E. Uwaga na nesting (zagnieżdżanie). Chodzi o rozsądne zagnieżdżanie stylów, przede wszystkim – NIE za głębokie.

Przykład – źle:

.person {}
.person__head {}
.person__head__face {}
.person__head__face__eye {}
.person__head__face__eye__pupil {}

Przykład – dobrze:

.person {}
.person__head {}
.person__face {}
.person__eye {}
.person__pupil {}

Jak by nie patrzeć, lepiej gdy podwójne podkreślenie pojawia się w nazwie selektora jeden raz. A zatem piszemy

Block__Element–Modifier

A NIE

Block__Element__Element–Modifier

Np. jeśli mamy formularz ‚search-form’, a w nim div ‚search-form__content’, w którym są inputy, klasy dla inputów możemy zapisać w dwojaki sposób.

NIE piszemy

search-form__content__input

ALE

search-form__input

lub

search-form__content-input

Obie wersje są poprawne – rekomendowane przez dokumentację BEM.

Przykład – tego unikamy:

<div class="c-card">
  <div class="c-card__header">
    <!-- Grandchild -->
    <h2 class="c-card__header__title">Title text here</h2>
  </div>

  <div class="c-card__body">
    <img class="c-card__body__img" src="pic.png" alt="description">

  ...
</div>

Zobaczmy teraz ten sam kod, z poprawionymi selektorami.

Przykład – tak robimy:

<div class="c-card">
  <div class="c-card__header">
    <h2 class="c-card__title">Title text here</h2>
  </div>

  <div class="c-card__body">
    <img class="c-card__img" src="pic.png" alt="description">
  ...
</div>

Lepiej? No ba.

Podobnie:

<div class="block">
  <div class="block__elem1">
    <div class="block__elem2">
      <div class="block__elem3"></div>
    </div>
  </div>
</div>
.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}

I tak dalej. Dlaczego? Budujmy nasze komponenty w bystry sposób. BEM (czy inna konwencja) ma pomagać nam, a nie nas ograniczać, lub komplikować proste rzeczy.

Tip: dla wieloczłonowych nazw składowych selektora (bloków, elementów lub modyfikatorów), używamy po prostu myślnika, np.:

block-name__my-element-name–foo-bar-modifier-name

Schemat nazewnictwa zalecany, choć nie jedyny: https://en.bem.info/methodology/naming-convention/#alternative-naming-schemes (BEM jest rozwijany już jakiś czas ;-))

Więcej o dobrych praktykach i pułapkach do poczytania tutaj:

https://www.sitepoint.com/working-bem-scale-advice-top-developers

https://www.smashingmagazine.com/2016/06/battling-bem-extended-edition-common-problems-and-how-to-avoid-them

Słowo o miksach

Nie zawsze musimy tworzyć selektory związane z blokiem. Jest to wysoce zalecane, jeśli myślimy, że nasz blok będzie re-używalny. Ale np. zakładając, że miksujemy style menu item z buttonem, a klasy ‚button’ i ‚active’ wszystkich naszych przycisków będą definiowane przez ten sam kod CSS (i to wystarczy), wtedy możemy stworzyć osobne klasy, więc napiszemy:

<li class="menu__item button active">

zamiast np.:

<li class="menu__item menu__button menu__button--active">

Jednak częściej zdarzy się, że elementy naszego projektu będą się zbyt różnić od siebie, i wtedy oczywiście trzymamy się zalecanej konwencji BEM.

A co z Bootstrap?

Framework Bootstrap nie stosuje konwencji BEM, więc możemy się obawiać paskudnego kodu, gdy pomieszamy style Bootstrapa oraz nasze – BEM-owe. Myślę że to żaden problem. Stosujemy zupełnie normalnie klasy zdefiniowane w Bootstrap, a nasze komponenty piszemy używając konwencji BEM. Gołym okiem widać, które są z Bootstrapa, a które nasze. Nie powinno być problemów.

BEM – dodatkowe zasoby

http://getbem.com

https://en.bem.info/methodology/quick-start

https://robots.thoughtbot.com/keeping-the-frontend-modular-with-bem

https://evilmartians.com/chronicles/bootstrap-an-intervention

http://mono.company/journal/frontend/learning-to-love-bem

https://mono.company/journal/frontend/overthinking-bem

https://m.alphasights.com/bem-i-finally-understand-b0c74815d5b0

BEM w praktyce – znakomite artykuły (zwłaszcza cz. 1 i 2):

https://zellwk.com/blog/css-architecture-1

https://zellwk.com/blog/css-architecture-2

https://zellwk.com/blog/css-architecture-3

Inne metodologie CSS

Poznaliśmy popularne metodologie CSS, ale z pewnością nie jedyne. Jest wiele innych, niektóre już nie rozwijane. Możemy również spodziewać się pojawienia zupełnie nowych.

Obok OOCSS czy BEMa, znajdziemy takie jak:

Atomic – https://www.smashingmagazine.com/2013/10/challenging-css-best-practices-atomic-approach

MaintainableCSS – https://maintainablecss.com/chapters/reuse

ecss – http://ecss.io

Ale można jeszcze inaczej.

Scoped CSS

Chyba po raz pierwszy użyłem scoped CSS w praktyce przy okazji (wspaniałego) frameworku Vue.js, gdzie scoped CSS świetnie sprawdza się przy stylowaniu komponentów:

https://vue-loader.vuejs.org/en/features/scoped-css.html

Potrzebne style będą zastosowane do bieżącego komponentu, a nie do całego dokumentu. Prosto i przyjemnie. Pisząc CSSy z użyciem BEM czy innej metodologii, również definiujemy scopes (zakresy), w sposób odpowiedni dla danej konwencji.

Zobacz także:

https://www.w3schools.com/TAgs/att_style_scoped.asp

https://davidwalsh.name/scoped-css

A może po prostu CSS modules?

Zyskują one na popularności. Mogą być dobrym rozwiązaniem dla kogoś, kto pracuje z innymi technologiami i/lub nie chce adaptować tych wszystkich Sass’ów, BEM’ów, itd.

Moduły CSS dostępne są np. w css-loaderze dla Webpacka (ktokolwiek jeszcze nie korzystał z webpacka, niech spróbuje ;-)). Style (jako moduły) mogą elegancko zostać załadowane do projektu z konwencji modułów ES6, np:

import styles from './LoginForm.css';

Style z modułu stają się dostępne, a można ich użyć w taki sposób:

  &lt;div class=&quot;${styles.root}&quot;&gt;

Zasoby

https://css-tricks.com/css-modules-part-1-need

https://www.universalmind.com/blog/css-modules-solving-the-challenges-of-css-at-scale

https://www.triplet.fi/blog/practical-guide-to-react-and-css-modules

https://glenmaddern.com/articles/css-modules

https://nafrontendzie.pl/css-modules-kolejny-sposob-style-react

https://www.sitepoint.com/understanding-css-modules-methodology

https://x-team.com/blog/css-modules-a-new-way-to-css

https://x-team.com/blog/css-modules-rethinking-the-past

https://github.com/css-modules/css-modules

Idąc dalej, możemy dojść do CSS styled components (zobacz także: React).

I jeszcze więcej zasobów…

Metodologie CSS – do poczytania

http://helion.pl/view/8448w/sasspp.htm

https://www.leemunroe.com/css-sass-scss-bem-less

Scss-Styleguide with BEM, OOCSS & SMACSS

https://medium.com/peergrade-io/structuring-css-in-large-projects-37f1695f5ec8

https://sass-guidelin.es

http://helion.pl/view/8448w/iojak.htm

Metodologie CSS – podsumowanie

Kaskadowe arkusze stylów są problematyczne, ponieważ są… kaskadowe. Daje się to we znaki prędzej czy później, w miarę rozwoju projektu. Metodologie CSS pozwalają organizować style w lepszy, spójny sposób, rozwiązując typowe bolączki CSS.

Nikt niczego nikomu nie powinien narzucać. Potencjalnych rozwiązań jest cała gama, wiec każdy może znaleźć coś odpowiedniego dla siebie. Podejścia do pisania CSS powinny skupiać się na spójnej strukturze plików i folderów, a także konwencji nazw. Ponadto powinno się dążyć do opracowywania CSS (oraz JS) w sposób modularny, tak aby można było utrzymywać, importować i używać moduły zależnie od potrzeb.

Tak wygląda też sprawa JavaScript i AMD. W najświeższym wydaniu korzystamy po prostu ze znakomitego podejścia, oferowanego przez ES6 modules. Nie jest jeszcze wspierane w przeglądarkach bezpośrednio, ale żaden problem – zaprzęgamy do pracy Babel (transpiler) oraz webpack (module bundler) i gotowe.

Więc naprzód! Ku przyszłości 🙂

Możesz skomentować leave a response, lub podać trackback z własnej strony.