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

Zdroják » Webdesign » Tailwind není jediná cesta. Pět přístupů k CSS, které stojí za zvážení

Tailwind není jediná cesta. Pět přístupů k CSS, které stojí za zvážení

Zeptejte se dnes libovolného juniora, čím bude stylovat nový projekt, a s pravděpodobností hraničící s jistotou uslyšíte „Tailwind“. Zeptejte se jazykového modelu a dostanete Tailwind dokonce i tehdy, když jste o něm nic neřekli. Utility-first přístup se za posledních pět let stal tichým defaultem celého frontendu, a to natolik, že volba stylovacího nástroje přestala být rozhodnutím a stala se rituálem.

Nálepky:

Tenhle text není o tom, že by byl Tailwind špatný. Není. Je to promyšlený nástroj s vynikající dokumentací, který verzí 4 udělal poctivý krok dopředu: konfigurace se přesunula do CSS přes @theme, přibyl nativní Vite plugin a nový engine zkrátil inkrementální buildy na jednotky milisekund. Verze 4.3 z letošního května přidala třeba first-party stylování scrollbarů. Za tou popularitou stojí reálná práce.

Problém je jinde. Když se z jednoho nástroje stane default, přestaneme se ptát, jaký problém vlastně řešíme. A přitom se za poslední tři roky změnilo samotné CSS natolik, že řada původních důvodů pro sáhnutí po frameworku prostě zmizela. Cascade layers, nativní nesting, @scope, container queries, :has() nebo color-mix(), to všechno dnes běží ve všech hlavních enginech a většina z nich má status Baseline widely available.

Pojďme se tedy podívat na pět jiných cest. Ne jako na seznam náhrad jedna ku jedné, ale jako na pět odlišných odpovědí na otázku „jak zabránit tomu, aby se mi CSS rozpadlo pod rukama“.


Co Tailwind vlastně řeší

Než začneme hledat alternativy, stojí za to pojmenovat, co utility-first přístup dělá dobře. Jinak budeme porovnávat hrušky s traktory.

Tailwind řeší tři věci najednou. Za prvé pojmenování: nemusíte vymýšlet název pro obal, který jen odsazuje. Za druhé izolaci: p-4 nikdy nikomu nic nerozbije, protože nemá specificitu navíc ani globální dosah. A za třetí konzistenci: hodnoty spacingu, barev a fontů jsou předem dané, takže vám do projektu nepropluje sedm odstínů šedé.

Cenou je čitelnost šablon a to, co bych nazval rozpuštěním designu do markupu. Když se podíváte na <div class="flex items-center gap-3 rounded-lg border border-slate-200 px-4 py-3 shadow-sm hover:bg-slate-50">, nevíte, co ta komponenta je. Víte jen, jak vypadá. To je zásadní rozdíl a projeví se ve chvíli, kdy stejnou věc potřebujete na dvaceti místech a dvě z nich se mírně liší.

Typickou reakcí na tenhle problém bývá @apply, tedy pokus schovat sadu utilit zpátky pod jeden název. Jenže tím se vracíte přesně tam, odkud jste utekli: máte znovu pojmenované třídy, znovu vlastní stylesheet a navíc k tomu vrstvu nástroje, který to všechno musí přeložit. Autoři Tailwindu sami dlouhodobě doporučují @apply používat co nejméně a řešit opakování komponentami. To je rozumná rada, ale zároveň přiznání, že utility-first funguje dobře jen ve světě, kde máte komponentový framework. Na klasicky renderovaném webu se šablonami v Twigu nebo Blade se ta výhoda dost ztrácí.

Každý z následujících přístupů řeší stejnou trojici problémů (pojmenování, izolace, konzistence) jinými prostředky. V tom je celá pointa.


1. Moderní nativní CSS

Nejpodceňovanější varianta. Většina argumentů proti „obyčejnému CSS“ pochází z doby, kdy jsme neměli proměnné, nemohli jsme nic zapouzdřit a jediným způsobem, jak přebít cizí selektor, bylo přidat další třídu navrch. To už dávno neplatí.

Klíčová trojice je @layer, @scope a vlastní vlastnosti. Cascade layers vyřeší celý ten letitý cirkus se specificitou jedním deklarativním řádkem:

@layer reset, tokens, base, komponenty, utility;
Code language: CSS (css)

Od téhle chvíle přebije cokoliv z vrstvy utility cokoliv z vrstvy komponenty, bez ohledu na to, jak složitý selektor kdo použil. Žádné !important, žádné zdvojování tříd. Kdo si někdy pohrál s ITCSS a strávil odpoledne přemýšlením nad pořadím importů, ví, jak velká úleva to je.

Zapouzdření řeší @scope, které se stalo Baseline newly available na konci roku 2025:

@scope (.karta) to (.karta__obsah) {
  h3 { font-size: 1.125rem; margin-block: 0 0.5rem; }
  a  { color: var(--barva-odkaz); }
}
Code language: CSS (css)

Ta druhá část, takzvaná donut scope, je věc, kterou vám nedá žádný preprocesor: styly platí uvnitř .karta, ale zastaví se na hranici vnořeného obsahu. Pro komponenty, do kterých se vkládá cizí HTML (CMS, editor, markdown), je to přesně to, co jste vždycky chtěli a doteď obcházeli řetězci :not().

Zbytek pokryjí tokeny v proměnných a container queries. Poslední jmenované považuju za nejdůležitější změnu v layoutu za dekádu, protože ruší předpoklad, že komponenta ví, jak široké je okno:

.seznam { container-type: inline-size; }

@container (width > 40rem) {
  .polozka { grid-template-columns: 8rem 1fr; }
}
Code language: CSS (css)

Za zmínku stojí i to, co se stalo s barvami. light-dark() a color-mix() dohromady odstraňují potřebu generovat desítky odstínů dopředu. Světlý a tmavý motiv se dá zapsat na jednom řádku, hover stav se dopočítá:

:root { color-scheme: light dark; }

.tlacitko {
  --zaklad: light-dark(#2563eb, #60a5fa);
  background: var(--zaklad);
}
.tlacitko:hover {
  background: color-mix(in oklch, var(--zaklad) 85%, black);
}
Code language: CSS (css)

Ještě před dvěma lety to znamenalo buď Sass funkce, nebo tabulku barev vygenerovanou skriptem. Dneska je to devět řádků, které pochopí kdokoliv, kdo umí CSS.

Kdy to dává smysl: menší a středně velké projekty, statické weby, servisní aplikace, cokoliv bez těžkého JS frameworku. Také všude tam, kde chcete mít nulové závislosti a být si jistí, že projekt půjde sestavit i za pět let.

Kde to skřípe: velký tým bez sdílené konvence si i s vrstvami dokáže vyrobit chaos. Nativní CSS vám dá nástroje, ale žádnou disciplínu. A pořád nemáte mixiny ani smyčky, takže generování dvaceti variant utility tříd je otravné.


2. CSS Modules a scoped styly ve frameworcích

Pokud píšete komponentovou aplikaci, existuje řešení izolace, které je starší než Tailwind, funguje bez runtime a nikdo ho pořádně nenahradil. CSS Modules jsou v podstatě jednořádkový nápad: build vám přejmenuje třídy na unikátní hashe a vrátí mapu.

/* Karta.module.css */
.obal {
  display: grid;
  gap: var(--mezera-2);
  padding: var(--mezera-3);
  border-radius: 12px;
}
.obal:hover { background: var(--povrch-2); }
.nadpis { font-weight: 600; }
Code language: CSS (css)
import s from './Karta.module.css';

export function Karta({ nadpis, children }) {
  return (
    <article className={s.obal}>
      <h3 className={s.nadpis}>{nadpis}</h3>
      {children}
    </article>
  );
}
Code language: JavaScript (javascript)

Co za to dostanete: normální CSS soubor, který otevře i grafik, plnou podporu v editorech, žádný JavaScript v runtimu, funkční kolokaci se souborem komponenty a jistotu, že .obal v jedné komponentě nikdy nepotká .obal v jiné. Ve Vue je totéž <style scoped>, ve Svelte to funguje bez jakéhokoliv opt-inu.

Málo se ví, že CSS Modules umí i skládání. Direktiva composes vám dovolí dědit deklarace z jiné třídy, klidně napříč soubory, aniž byste v šabloně museli řetězit názvy tříd za sebou. Základní vzhled tlačítka tak napíšete jednou a varianty z něj vycházejí:

.tlacitko { composes: zaklad from './primitiva.module.css'; }
.tlacitkoNebezpecne { composes: tlacitko; background: var(--barva-chyba); }
Code language: CSS (css)

Zajímavý detail: přesně tenhle princip (kompilační zapouzdření místo runtime injekce) je dnes zpětně považován za jedno z nejlepších rozhodnutí v návrhu Vue. Zatímco CSS-in-JS knihovny se lámaly na server komponentách, scoped styly přežily beze změny, protože jejich výstupem je od začátku obyčejný stylesheet.

Kdy to dává smysl: React, Vue, Svelte, Astro, Next, tedy v podstatě jakákoliv komponentová aplikace, kde chcete psát CSS jako CSS.

Kde to skřípe: samo o sobě to neřeší konzistenci. Nic vám nezabrání napsat padding: 13px. To musíte dorovnat tokeny. A dynamické hodnoty závislé na props se řeší přes inline style a proměnné, což je funkční, ale trochu neelegantní.


3. Konvence: BEM, ITCSS, CUBE CSS

Metodika není knihovna. Nic neinstalujete, nic se nebuilduje. Kupujete si jen dohodu o tom, jak pojmenovávat a jak vrstvit.

BEM se dnes zmiňuje spíš s úsměškem, ale ten posměch není zasloužený. .karta__nadpis--zvyrazneny je ošklivé, o tom žádná. Zato okamžitě víte, kam ten selektor patří, co je jeho rodič a že jde o variantu. V projektu, kterým za pět let projde deset lidí, má tahle vlastnost hodnotu, kterou docení hlavně ten, kdo přijde po vás.

Zajímavější je dnes CUBE CSS od Andyho Bella, protože bere v úvahu, že kaskáda není bug. Zjednodušeně: nejdřív nastavíte globální chování (typografie, barvy, spacing přes :where(), tedy s nulovou specificitou), pak přidáte pár utilit na opakující se hodnoty, a teprve to, co zbyde a je skutečně unikátní, zabalíte do bloku s vlastním jménem.

/* globální vrstva, nulová specificita, snadno přebitelná */
:where(h1, h2, h3) { line-height: 1.2; text-wrap: balance; }
/* .tok je zdejší obdoba známé utility .flow */
:where(.tok > * + *) { margin-block-start: var(--tok-mezera, 1.5rem); }

/* utilita, jedna vlastnost, jeden účel */
.mezera-l { --tok-mezera: 3rem; }

/* blok, tedy skutečná komponenta */
.faktura { display: grid; grid-template-columns: 1fr auto; }
Code language: CSS (css)

Ten trik s :where() a děděnou proměnnou (--tok-mezera) je mimochodem elegantnější řešení vertikálního rytmu než cokoliv, co vám dá utility framework, protože nastavení se dědí do celého podstromu a přepíše se na jednom místě.

Kdy to dává smysl: dlouhověké projekty, redakční systémy, weby s obsahem od netechnických autorů, týmy, které potřebují sdílený jazyk víc než sdílený nástroj.

Kde to skřípe: konvenci musí někdo hlídat. Bez lintru a bez code review se rozpadne do tří měsíců. A první týden je na commitech vždycky vidět, kdo si dokumentaci přečetl a kdo ne.


4. Zero-runtime CSS-in-JS

CSS-in-JS prošlo tvrdou lekcí. Runtime knihovny typu styled-components a Emotion nabídly kolokaci a typové jistoty, ale platilo se za to JavaScriptem v bundlu a prací na hlavním vlákně při každém renderu. Když přišly React Server Components, ukázalo se, že celý model stojí na předpokladu, že styl vzniká během renderu v prohlížeči. Knihovna styled-components dnes běží v režimu údržby a na nových projektech ji prakticky nikdo nezakládá.

Přežila ale ta lepší část myšlenky. Nová generace nástrojů (vanilla-extract, Panda CSS, StyleX od Mety) píše styly v TypeScriptu, ale extrahuje je při buildu do obyčejného statického CSS. V prohlížeči pak neběží nic.

// karta.css.ts (vanilla-extract)
import { style } from '@vanilla-extract/css';
import { tokeny } from './tema.css';

export const obal = style({
  display: 'grid',
  gap: tokeny.mezera.stredni,
  padding: tokeny.mezera.velka,
  selectors: {
    '&:hover': { background: tokeny.barvy.povrch2 },
  },
});
Code language: JavaScript (javascript)

Hlavní devíza je typová kontrola. tokeny.mezera.stredni buď existuje, nebo vám neprojde build. Překlep v názvu tokenu nikdy nedoputuje do produkce, refaktoring přejmenováním funguje napříč projektem a autocomplete zná vaše barvy, ne barvy někoho jiného. Pro autory design systémů je to podstatně silnější záruka než konvence a lint.

Panda CSS jde jiným směrem a nabízí style props přímo v JSX, které se při buildu přeloží na atomické třídy. StyleX řeší extrémní škálu; Meta jím styluje facebook.com, takže deterministické slučování stylů má vyřešené do posledního detailu.

Kdy to dává smysl: design systémy a komponentové knihovny, velké týmy v TypeScriptu, projekty na App Routeru, kde chcete kolokaci bez runtime.

Kde to skřípe: platíte časem buildu a přivazujete si stylování k JS toolingu. Grafik, který umí CSS, do karta.css.ts nesáhne. A pokud potřebujete měnit motiv skutečně za běhu, budete stejně řešit proměnné v CSS.


5. Tokeny a hotové systémy: Open Props, Pico, klasické frameworky

Poslední přístup vychází z nepohodlné otázky: opravdu potřebujete psát vlastní design?

Open Props je knihovna, která nedodává ani jednu komponentu. Dodává jen sadu prověřených hodnot jako CSS proměnné, tedy škály odsazení, stíny, easing funkce, poloměry rohů a barevné palety v OKLCH:

@import "open-props/style";

.tlacitko {
  padding: var(--size-2) var(--size-4);
  border-radius: var(--radius-2);
  box-shadow: var(--shadow-2);
  transition: box-shadow var(--ease-3) 200ms;
}
Code language: CSS (css)

Dostanete tím konzistenci (třetí problém z úvodu) a přitom si necháváte plnou kontrolu nad selektory a strukturou. Je to prakticky Tailwind bez utility tříd: stejné rozhodnutí o hodnotách, jiné rozhodnutí o zápisu.

Na druhém konci škály stojí classless frameworky typu Pico.css, které nastylují sémantické HTML samy o sobě. Napíšete <button>, <table>, <article> a vypadá to slušně bez jediné třídy. Pro admin rozhraní, interní nástroje, dokumentaci nebo prototyp je to úspora několika dní práce.

A pak jsou tu klasické komponentové frameworky. Bootstrap se dneska nosí asi jako sako s náloketníky, jenže pokud stavíte vnitrofiremní aplikaci s dvaceti formuláři, modálními okny a datovými tabulkami, dostanete hotové, přístupné a odladěné komponenty, které v Tailwindu buď postavíte sami, nebo si k němu doinstalujete druhou knihovnu.

Kdy to dává smysl: interní nástroje, MVP, dokumentace, projekty bez dedikovaného designéra, situace, kdy je „dobré a hotové“ lepší než „přesně podle vizuálu a za tři týdny“.

Kde to skřípe: jakmile designér dodá vlastní vizuál, začnete s frameworkem bojovat. Přepisování cizích komponent bývá dražší než napsat si vlastní od nuly.


Jak si mezi tím vybrat

Nemám rád tabulky typu „nejlepší nástroj roku“, protože žádný takový neexistuje. Užitečnější je položit si čtyři otázky, které v praxi rozhodnou skoro vždycky.

Kdo bude ten kód udržovat? Pokud do stylů sahá i někdo, kdo nepíše JavaScript, vyškrtněte čtvrtý přístup. Pokud jste tři vývojáři v TypeScriptu, je to naopak nejsilnější varianta.

Jak dlouho ten projekt pojede? U pětiletého horizontu mají nativní CSS a konvence obrovskou výhodu: nemají závislosti, které mohou přestat být udržované. Migrace z opuštěné knihovny stojí víc než jakákoliv úspora na začátku.

Kolik unikátního designu tam skutečně je? Marketingový web s vlastním vizuálem a interní evidence faktur jsou dva různé světy. Ve druhém případě je psaní vlastního design systému čirá ztráta času.

Co vás na minulém projektu bolelo nejvíc? Tohle je asi nejlepší diagnostický nástroj. Pokud to byly kolize selektorů a strach něco smazat, potřebujete zapouzdření (CSS Modules, @scope). Pokud to byla nekonzistence, potřebujete tokeny. Pokud jste nikdy nevěděli, kam patří nový soubor, potřebujete metodiku. Tailwind zvládá první dvě potřeby velmi dobře, tu třetí, tedy architekturu a organizaci souborů, neřeší vůbec. A mnoho týmů si ho pořizuje právě kvůli ní.

Nic z toho se ostatně nevylučuje. Kombinace tokenů z Open Props, CSS Modules na zapouzdření a několika vlastních utility tříd je naprosto legitimní architektura a v praxi ji potkávám častěji než čistokrevné nasazení čehokoliv. Stejně tak je legitimní zůstat u Tailwindu, protože jste v něm rychlí a projekt to unese.

Jediné, co bych nedoporučoval, je vybírat podle toho, co je vidět na Twitteru. Nebo podle toho, co vám vygeneruje jazykový model, který statisticky ví, co se nejčastěji píše, ale netuší, kdo bude váš kód číst za dva roky.

CSS má dnes nativně skoro všechno, kvůli čemu jsme si kdysi pořizovali nástroje. Stojí za to se aspoň jednou za projekt zastavit a ověřit si, jestli ten nástroj ještě řeší problém, který doopravdy máme.

Komentáře

Odebírat
Upozornit na
guest
0 Komentářů
Nejstarší
Nejnovější Nejvíce hlasů
Vitex

… reposted this!

Tabulky proti trhu: proč státní správa prohrává boj o zkušené softwarové inženýry

Český statistický úřad uvádí pro vývojáře softwaru v roce 2025 medián mzdy více než 101 tisíc korun, v platové činil necelých 56 tisíc korun. Problém je, že o stejné lidi soutěží s firmami, které vedle peněz nabízejí moderní vybavení, možnost kombinovat práci z domova a z kanceláře a někdy také daňově výhodnější spolupráci na IČO.