Přejít k navigační liště

Zdroják » Webdesign » Proč jsme zvolili atomické CSS

Proč jsme zvolili atomické CSS

Články Webdesign

Při tvorbě nové verze Fakturoidu jsme zvolili atomická CSS. Dost možná máte proti našemu zápisu class=“flex mb-3 p-3 b b-secondary bg-secondary-lighter br-2 bs-1″ výhrady. Nevadí. My našemu přístupu věříme a pokusíme se ho obhájit. Pojďme na to.

Nálepky:

Text vyšel původně na blogu Fakturoidu.

Původní verze naší aplikace (Fakturoid) spatřila světlo světa v roce 2009, kdy metodologie pro organizaci CSS prakticky neexistovaly. Postupné rozšiřování funkčnosti a interakcí znamenalo psaní CSS, které dříve či později muselo vyústit v neudržitelný stav a technický dluh.

Příprava nové verze započala dva roky zpět, a byla ideální dobou zamyslet se (nejen) nad CSS architekturou.

✍️ Prvotní požadavky

  • Malá velikost výsledného CSS a minimální navyšování při rozšiřování funkčnosti. Objemné CSS stojí uživatele čas i peníze.
  • Sdílení vybraných CSS pro desktop aplikaci a mobilní web, resp. hybridní mobilní aplikaci. Prvotní úvahy nepočítaly s finálním responzivním designem, ve hře byly i separátní mobilní verze.
  • Rychlé prototypování nových obrazovek i menších zásahů do UI. Znáte to, i drobné vylepšení microcopy vám může zamíchat s rozhraním.
  • Nízká specificita selektorů.
  • Technikálie: SASS, generovaná dokumentace, autoprefixování vlastností, apod.

Monolitické frameworky typu Bootstrap 4 (tehdy vyšla první alfaverze, vzpomínáte si ještě?) nebo Foundation jsme brzo zamítli. Ekosystém okolo Bootstrapu byl lákavý, ale s průběžnými návrhy nového rozhraní jsme si jen potvrdili, že bychom využili jen část a valná většina rozhraní by skončila v „custom“ kódu.

⚛️ Atomické, ale jak moc?

Na jaře 2016 vydal Adam Morse (autor knihovny Tachyons) článek CSS and Scalability o úskalích sémantických CSS (nejen BEMu). Najednou bylo mnohem jasnější, že nechceme psát dokolečka nové sémantické komponenty nebo se rozhodovat, zda nová část rozhraní bude novou komponentou, či modifikací stávající. Odpadnou otázky: Jak vlastně pojmenovat Blok, Element, Modifikátor? Jak škálovatelné bude CSS po pár letech vývoje?

Nevíte, co je to BEM? Pak je načase přečíst si náš článek BEM: Pojmenovávací konvence pro třídy v CSS.

Najednou Atomické CSS (chcete-li Funkcionální nebo Utility) začalo pro naše potřeby dávat smysl. Otázkou bylo, v jakém rozsahu.

V té době jsme začali navrhovat první obrazovky nové verze (jen pro informaci, aplikace jich má bez admin sekcí kolem 150) a krystalizoval nám vizuální jazyk rozhraní. Souběžně s tím jsme se pustili do vlastní atomické knihovny, která doplňovala formulářové minikomponenty. Věděli jsme, že klasickým component-first přístupem chceme obsloužit hlavně nejmenší formulářové prvky – buttoninput typy, selecty, ale i custom přepínače (přestylované nativní checkboxy nebo radio buttony). Tehdejší atomické knihovny nebyly kompletní (TachyonsBasscss), případně byly striktně atomické a nelíbila se nám syntaxe (Atomic CSS). Nezbývalo nám, než si připravit vlastní.

Náš přístup k Atomickému CSS

Atomický přístup (zjednodušeně řečeno) znamená nahrazení „sématických“ tříd sadou tříd, které reprezentují samotné vlastnosti. Utilita zpravidla nese jedinou CSS vlastnost (tím je interně oddělujeme od Helper tříd jako .clearfix). Flexbox container se tak nastaví pomocí .flex, vlastnost display: block; reprezentuje třída .d-b, apod.

Příklad

Takto vypadá jedna z našich pomocných hlášek:

V tradiční B.E.M. metodologii by mohla být zapsaná takto:

<div class="helpbox helpbox--highlighted">
  <div class="helpbox__image-column">
    <img class="helpbox__image" src="robot-tip.png" alt"">
  </div>
  <div class="helpbox__feature-column">
    <p>
      Umím automaticky označovat faktury jako zaplacené podle plateb, které dorazí na váš účet.
    </p>
    <p class="helpbox__upsell">
      <a href="/blank/subscription">Přejděte na placený tarif</a> a budete moci využít párování plateb.
    </p>
  </div>
</div>

a s robotí atomickou knihovnou…

<div class="flex mb-3 p-3 b b-secondary bg-secondary-lighter br-2 bs-1">
  <div class="wf-80 md-wf-100 flex-none self-center">
    <img src="robot-tip.png" alt="">
  </div>
  <div class="md-pl-3 small">
    <p>
      Umím automaticky označovat faktury jako zaplacené podle plateb, které dorazí na váš účet.
    </p>
    <p class="mb-0">
      <a href="/blank/subscription">Přejděte na placený tarif</a> a budete moci využít párování plateb.
    </p>
  </div>
</div>

Cože? 😧 To můžu rovnou psát inline styly! 😤

Na první pohled se může zdát, že zápis přes style by vyšel nastejno. Ale pozor, inline styly:

  • mají vysokou specificitu,
  • nepodporují podmíněná pravidla jako @media@supports@keyframe@font-face, apod.,
  • nezapíšete s nimi pseudotřídy (:) a pseudoelementy (::),
  • nekešují se,
  • jsou zbytečně „ukecané“ (rozhodně více než zápisy většiny atomických knihoven),
  • s inline styly lehce narušíte konzistenci. Atomická CSS vás jednoduše omezí barvami pro text nebo škálou pro odsazení.

🤔 Hmm, ale takhle mi nabobtná přenášený HTML kód

Ano, ale zanedbatelně. Snapshot obrazovky s výpisem faktur má 187 kB, GZIPovaná 17 kB. Pokud odstraníme CSS třídy, snížíme velikost na 143 kB (GZIP 14 kB). Výsledný rozdíl = 3 kB. To není tak strašné, ne? A přidáme-li klasické component-first třídy, které budou mít kompresní poměr jistě horší, rozdíl se ještě sníží.

Proč ne osvědčené metodologie jako BEM?

BEM metodologie vnesla do CSS řád, skvěle řeší namespacing a kód je v šabloně bezpochyby čitelnější. Problém nastává u komplexnějších bloků na složitém projektu. Jednotlivé komponenty samy o sobě většinou dávají smysl, ale poskládané v různých kombinacích, na různých obrazovkách nebo s odlišným copy obvykle jako celek netvoří ideální výsledek.

V rodícím se designu jsme samozřejmě nacházeli opakující se návrhové vzory, ale příliš často se lišily, byť jen drobnostmi. Kombinace komponent vyžadovala často jiná odsazení, oddělující prvky nebo potřebu komponentu podbarvit. Přidejte k tomu variace na jednotlivých breakpointech a na složitějším projektu, psaném v čistém BEM zápisu, dříve nebo později skončíte v CSS pekle. Nakonec tvoříte další komponenty nebo jejich modifikace, které zřídkakdy znovu použijete. Zkrátka stav, kterému se zvlášť na „živém“ projektu, jako je Fakturoid, chcete vyhnout. A to jsme ani nenakousli problematiku pojmenování tříd. Ale to je na jiné povídání. Zpět k naší CSS architektuře…

🤖 Pod pokličkou robotí aplikace

Jak bylo zmíněno, nepoužíváme striktně pouze atomická CSS. Odhadem utility pokrývají zhruba 95 % kódu v šablonách (Erb Views). O architektuře CSS asi napoví adresářová struktura:

  • /vendor (normalize.css)
  • /base (resetování, výchozí typografie, tabulky, apod.)
  • /mixins (SASSové mixiny)
  • /layout (styly pro layouty obrazovek)
  • /forms (formulářové prvky psané čistě BEM zápisem)
  • /components (vše ostatní, co nepokryjeme utilitami)
  • /utility (sada utilit pro většinu základních CSS vlastností)
  • _variables.scss (SASS proměnné)
  • style.scss (hlavní soubor pro import SCSS souborů)

Responsivní design používá 3 breakpointy, pro které jsou připraveny utility. Třídy na breakpointech jsou prefixované na začátku názvu .md-*.lg-*. Mohlo by se zdát, že 3 breakpointy jsou málo, ale zatím si vystačíme. Podle potřeby píšeme pro komponenty tweakpointy pro odlišné chování mimo hlavní „zlomy“.

⭐️ Zajímavosti

  • BEM komponenty mícháme (s čistým svědomím) s utilitami. BEM komponenta má třeba jen definovaný obrázek na pozadí a v HTML je doplněná o ostatní vlastnosti utilitami. Díky tomu je komponenta lépe znovupoužitelná.
  • Utility mají nejnižší možnou specificitu, importují se na konec hlavního souboru a slouží jako modifikační třídy.
  • Škála pro spacingové utility (marginpadding) má 5 stupňů. Každé odsazení v aplikaci může nabývat pouze jednu z těchto hodnot.
  • Barevnost je definována přes 5 „prioritních“ proměnných ($primary$secondary, …), z nichž každá má navíc odstupňované odstíny $primary-light$primary-lighter$primary-dark$primary-darker. Vedle toho máme 5 stupňů na škále od bílé do černé. Maximálně počet použitelných barev v rozhraní aplikace je 30 (pokud nepočítáme barvy štítků a barvy vzhledů faktur).
  • Nejběžnější barevné kombinace (pozadí vs. popředí textu) mají dostatečný kontrast barev (AAA nebo AA). Doporučujeme nástroj Contrast Grid.

🏁 Půl roku po spuštění nového Fakturoidu

Fakturoid je (opravdu) živý organismus. Denně dostává menší či větší vylepšení nebo opravy. Mimo jiné i díky tomu, že kódování a prototypování přímo v kódu se znatelně zrychlilo. Jeho frontend tvůrci nemusí přepínat neustále kontext mezi CSS a HTML.


Ukázka prototypování pomocí atomické knihovny Tailwind

Po ustálení knihovny píšeme nové CSS opravdu výjimečně. Zároveň odpadla nutnost přemýšlet nad pojmenováním tříd.

There are only two hard things in Computer Science: cache invalidation and naming things.

— Phil Karlton

Nepamatujeme, že bychom řešili jediný specificity problém.

Two CSS properties walk into a bar.

A barstool in a completely different bar falls over.

— Thomas Fuchs 😽 (@thomasfuchs) 28. července 2014

Ve srovnání s původní (neresponzivní) verzí jsme snížili velikost CSS na méně než polovinu (44 kB → 19 kB GZIP). A očekáváme, že s rozšiřováním funkčnosti by neměla narůstat.

Robot generuje styleguide přes Hologram. Díky převaze utilit by asi bylo lepší ji nazývat dokumentační knihovnou. Všechny utility mají ukázky, dokumentace modifikací a definovaných breakpointů.

Opakované části většího kódu řešíme pomocí partial šablon. Na úrovni složitějších formulářových komponent (např. vícepolohové přepínače) nám zase pomáhají railsové helpery, které dokáží nagenerovat prvek s definovanou sadou utilit i potřebnými HTML atributy. Obojí šetří čas a přispívá ke konzistenci. Přesto se objevuje pár částí, které se v kódu opakují často a nejspíš si časem zaslouží vytáhnout do komponent.

Nakonec je fér zmínit i nevýhodu v podobě horší skenovatelnosti šablon – prostě se v nic teď o trochu hůř orientuje. To je ale daň, se kterou jsme počítali.

Komentáře

Subscribe
Upozornit na
guest
30 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Mlocik97

Je to strašne hlúpe takto písať CSS.

Martin Hassman

Je možném rozepsat proč? Je totiž strašně hloupé psát komentáře jen o svých pocitech, ze kterých si ostatní nic moc nevezmou. Jaké jsou hlavní důvody toho, proč považujete tento způsob za hloupý?

Mlocik97

5 dôvodov:
– jde proti tomu na čo bol CSS určený a jak mal byť používaný…
– neprehladný, zaplevelený, nechutný HTML kód.
– ťažké úpravy (trebárs CSS Grid Areas v HTML classoch? asi ne no…)
– opakovanie
– omedzujúca funkcionalita… stejne CSS budete musieť otvárať, kedže už vidím jak by ste 255^3 farieb nacpali do classov a cez 20 tisíc^2 rozmerov veľkostí v rôznych jednotkách.

Vít Heřman
  • CSS byl určen pro separaci stylů od HTML. Pro webové aplikace často irelevantní důvod. Přineslo by to tedy něco, co nepotřebuji v neprospěch toho, co potřebuji.
  • opticky ano, ale praktický přínos je jasný. Na jednom místě je to, co spolu souvisí „neoddělitelně“
  • ???
  • prašť jako uhoď. Buď se opakují třídy v HTML nebo pravidla v CSS. Nebo se nějaká opakování zapouzdří do abstrakcí, jejichž množství ale také k přehlednosti a udržovatelnosti nepřispějí
  • správně omezená funkcionalita je benefit, který vede k přehlednosti a udržovatelnosti. takže v daném kontextu je to spíš dobrá volba

Vaše argumenty nijak nereflektují kontext a potřeby, které autor řešil, ač je v článku popsal. Moudrost od hlouposti lze rozeznat spíše hodnocením souladu volby s vytknutými cíli a potřebami. Nikoli podle volby samotné

Martin Hassman

👍👍 Děkuji za rozepsání! (Poděkování neznamená souhlas.)

Martin Hassman

Já si zatím pro sebe brainstormuji tyhle tři zápisy:

<p><font color="red">Text</font></p>
<p style="color:red">Text</p>
<p class=".color-red">Text</p>

Známe dobře z historie, že ano?

Všechny tři mají stejnou linku, která jasně odrazuje. Těžko si můžeme pomoct, abychom i v tom posledním neviděli ten první, středověký. Na druhou stranu je v každém kroku pokrok. Každý z těch kroků = nová generace. S lepšími možnostmi. Hledám pro sebe, jak moc mám ještě nenávidět ten poslední zápis. A hlavně kdy ano a kdy ne.

Většiny výtek, které napsal Mlocik, jsem si byl už vědom. Nezaskočily by mě, snad jim předejdu. Takže pro mě nemají smysl. Myslím, že nikdo snad nebude vytvářet třídy pro všechny barvy, ale bude mít předem danou paletu barev – třeba v designovém manuálu/styleguide, který si tým sepíše – pro tu třídy vytvoří a pak už je sází atd., že jo? Podobně další výtky.

Tady by asi pro příští text o atomickém CSS chtělo víc zdůraznit nějaká pravidla, která pomůžou se těm slepým uličkám vyvarovat, a která každý zřejmě nevidí. Možná něco, co ty Martine a další děláte podvědomě, ale do těch atomických návodů se to nedostane.

Vít Heřman

Myslím si, že hlavní přínos atomických CSS na Fakturoidu není ani tak o zápisu, ale o tom, že byl v podstatě na míru napsán takový malý a asi nepříliš obsáhlý DSL nad CSS, který narozdíl od sémantického přístupu nebude po stabilizaci vyžadovat téměř žádnou údržbu. Krom toho pokrývá jasně a přesně specifika projektu, reálně zjednodušuje stylování přesně takovým způsobem, který autor potřebuje. Toto nedokáže zajistit ani univerzální CSS framework. Třeba Bootstrap je vynikající a poměrně obstojně konfigurovatelný, ale vždy s vědomím, že bude lepší své potřeby přizpůsobovat Bootstrapu, než-li obráceně (chci-li ho opravdu využít).

Jednoduše byla použitá správná věc pro daný úkol. Obhajoba obstála :-)

Martin Hassman

Zda obhajoba obstojí, to ukáže čas. Ale já nesoudím Fakturoid, zamýšlím se čistě obecně nad správným použitím technologií.

Tahle formulace se mi líbí:

na míru napsán takový malý a asi nepříliš obsáhlý DSL nad CSS

O to bych se při úvahách o atomickém CSS opíral.

Vít Heřman

To ale přesně vyhovuje definici DSL. Ono to omezení neznamená nutně na jeden projekt. Doménou může být klidně více (nejen!) vašich projektů. Důležité je, že to má limitovat přímé využití CSS. Koukal jsem na Fakturoid a je pravda, že .d-b jen mapuje na stejný výraz CSS. Ale celkově to nic nemění na tom, že jste si vytvořili vlastní výrazový aparát (jazyk) jako výrazně redukovanou podmnožinu CSS.

Koneckonců Bootstrap je také kombinace DSL a komponent. Jeho utility třídy nejsou ničím jiným, než DSL.

Neberte to ale dogmaticky. Spíš jsem toto označení použil proto, abych vynesl strukturální aspekt tohoto řešení a trochu potlačil povrchnější úvahy nad hezkostí/ošklivostí zápisu.

Mlocik97

Ja sa designu (teda ani CSS) moc nevenujem, pracujem najmä na web aplikáciách zväčša v Angulari, jinak Electron/Cordova aplikace, jinak jazyky JS (i node.js), Scala a Go. Zatial když som ptreboval psát CSS mi stačil objektový prístup s pravidlo „#kde .jak“ a zatím jak CSS znám (možno až tak moc jak Vy asi ne), tak mi to prijde ako celkom v pohode.

Vít Heřman

Ono to celkem v pohodě může být. Jde ale o další požadavky jako když třeba chcete maximalizovat re-using CSS tříd, potřebujete se v týmu orientovat v CSS třídách a CSS stylech, zabránit mrtvému CSS zejména na sdílených projektech, atd. Ad-hoc řešení, které dělal kolega pak je často peklo největší. Taky jsem hlavně programátor, byť s přesahem do kódování designu, ale důsledné OOCSS (a příbuzné metodiky) je zatím nejlepším vynálezem jak CSS dostat pod kontrolu. A ty atomické CSS ještě trochu posouvají hranice jinam a minimálně má smysl to prověřit.

Tomáš Votruba

Tenhle přístup se mi strašně líbí. Nemusím nic psát do css a přitom se mi web rodí pod rukama.
Jedu amatérsky Bootstrap už několik let a jsem rád, že se ty utils classy stávají populárnější.

Díky za shrnutí a vysvětlení proč a výhod a nevýhod!

lenoch

Nevím… mě to připadá jako by někdo začal propagovat spagetti kód a globální proměnné. Resp. nepochopil jsem důvody, proč to dělat. Jasně, pokud mám na všechno „sémantické“ šablony, např. alert_success, alert_danger, tak to může fungovat, ale jinak jsou jasné nevýhody: nekonzistence (každý to dělá jinak), nízká míra deskriptivnosti, obtížná změna designu, refaktoring musí změnit veškeré html, není schopné se přizpůsobit zobrazení v odlišných prostředích…

Jan

Tvrdit, že 3kB není mnoho v době omezených mobilních dat je nesmysl. Je to jen hra se statistikou, čtenář se podívá a řekne si, no opravdu 3kB nejsou takový rozdíl. Řekněme si kolikrát si můžeme takovou stránku bez „cache“ stáhnout při limitu 1GB (nezáleží na tom jaký je, vždy se to jen vynásobí nějakou pěknou konstantou). 1GB / 17kB ~ 60000 (bude to něco málo pod nebo přes, záleží zda počítáme GiB nebo GB). Pro tu menší verzi je to 1GB / 13kB ~ 80000 (viz nahoře). No a nyní si spočítejme o kolik takových stránek si můžu za měsíc prohlédnout více? No je to 17/13 ~ 1,308, což znamená, něco okolo 30% více stránek s menší verzí. Pro uživatele, který si ve 3. týdnu vyčerpal data to znamená, že by mu při nižší variantě mohli vydržet skoro celý další týden. A to už není zas tak zanedbatelné.

Mlocik97

existuje pojem service-worker a pojem PWA.

Vít Heřman

To je teoretická úvaha, avšak zcela nevhodně aplikovaná. Předpokládáte, že uživatel využije celou svou mobilní kapacitu pro Fakturoid. Pro realistickou představu byste měl ale uvažovat realistický vzorek, třeba průměrný počet zobrazených stránek za měsíc nebo počítat s nějakým rozumným rozložením. Pak dojdete ke zcela opačnému závěru o té zanedbatelnosti :-)

Dor

Autor udělal tu chybu, že napsal „rozdíl = 3 kB“. Kdyby napsal „rozdíl = 2%“, tak by neinspiroval k zavádějícím počtům se spoustou neověřených předpokladů. Mám spoustu výhrad k a tomickým css, ale velikost to opravdu není.

BTW 60000 za měsíc? Tedy 2000 denně? To imho není úplně běžný use-case. Zato to 1 GB vcelku ano.

jirkakosek

Tak samozřejmě je takový způsob zápisu CSS v podstatě prasárna, která jde proti duchu CSS. Podobně to má ale třeba i onen zmiňovaný Bootstrap, a bohužel to dnes nejde dělat o moc lépe. Co v CSS chybí, je možnost říci, že nějaká třída má zdědit vlastnosti z několika dalších tříd najednou. Tj. aby bylo možné v HTML zapsat něco ve stylu:

<table class="tabulka-vypis">

A v CSS se pak řeklo, jak má taková tabulka vypadat odkazem na mnoho dalších tříd, za kterými je schováno konkrétní další vzhled a chování:

.tabulka-vypis { extends: .table, .table-dark, .table-striped, .table-responsive }

Bohužel čisté CSS tohle neumí a CSS preprocessory to umí jen v omezené míře.

Dor

Podobně to dělám v LESS a funguje to dle očekávání. Zatím jediný zádrhel, na který jsem přišel, je to, že to může být matoucí, když se to ladí v prohlížeči. Není z toho moc poznat, co je kde definované.

Mlocik97

a nenapadlo Vás podať návrh do W3C?

Vít Heřman

Můžete naznačit, v čem jsou podle Vás preprocesory v tomto omezené? Podle mne spolehlivě tu úroveň abstrakce zajistí.

Ale jinak jak píšete, lépe to moc nejde. A já bych jen dodal, že ani nemůže z principu jít. Buď redukujete duplicity, kladete důraz na čistou sémantiku a skončíte se složitou a obtížně udržovatelnou taxonomií nebo uděláte jednoduchou taxonomii, která má mnoho výhod, avšak může působit trochu jako prasárna.

jirkakosek

Můžete naznačit, v čem jsou podle Vás preprocesory v tomto omezené? Podle mne spolehlivě tu úroveň abstrakce zajistí.

Nejsmutnější je, že jsou vůbec potřeba. Tím, že to není nativní funkce v CSS, preprocesory musí u mnoha deklarací přidávat další selektory a výsledné CSS pak může dost nakynout.

Vít Heřman

Jj, o tom žádná, že by bylo nejlepší, kdyby tohle umělo nativní CSS. Mne jen zajímalo „to něco“ , co podle Vás nejde udělat ani s preprocesory (a bez ohledu na výsledné CSS). Jednoduše mám pocit, že mixiny tento problém řeší, a to zcela. A třeba se mýlím a jen něco nevidím :-)

Dor

Ještě mě napadá další nevýhoda těch preprocesorů, že kdybych chtěl udělat .trida_elementu {.trida_s_barvou}, tak kdybych chtěl mít více variant souborů s .trida_s_barvou a ty podle potřeby proměňovat, tak mi nezbývá než sestavovat to pro každou variantu zvlášť nebo to sestavovat dynamicky v reálném čase. Takže kdyby to CSS uměly nativně, byl by to velký důvod k oslavě.

ladislavsulc
Jan Pobořil

Tu špatnou čitelnost by řešilo pojmenování tříd bez zkratek – místo .d-b psát .display-block. Je to sice delší, ale bude rozdíl při kompresi podstatný?

Martin

Není možné se podělit o kód atomického css? :) Rád bych získal inspiraci a z minifkovaného souboru se to čte celkem blbě.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.