Skip to main content

Writing CSS: How We Got to Where We Are Today

March 1, 2026 14 min read

If we zoom out and reflect on how writing CSS has evolved over the years, it is interesting to see how things have changed. That is exactly what we are going to do in this article by looking at the early days of writing CSS, reflecting on the more prominent concepts and strategies that have been used over the years, and evaluating the best practices that we use today.

Introduction

There is no shortage of opinions and ideas for how to write CSS. We’ve seen everything from the earliest practices of organizing a stylesheet by sectioning it with comments to breaking it up into Sass partials, all the way to more formalized strategies like BEM, SMACSS, ITCSS, and a bunch of other fancy acronyms. But we still never seem to get close to landing on any sort of “consensus” for how to do it, not that we need one.

There’s no “right” way to write CSS — that’s one of the greatest, but also most frustrating, things about it. In some ways, CSS is begging for structure. Yet, it also tends to revolt if we make it too rigid.

But if we zoom out and reflect on how writing CSS has evolved over the years, it is interesting to see not only how things have changed but how past ideas have influenced the methods, best practices, and even the latest features that we use to write CSS today.

The Early Days of CSS

The advent of Cascading Style Sheets was revolutionary and dates as far back as October 10, 1994, when pioneering developer Håkon Wium Lie published the first proposal for Cascading HTML Style Sheets. It’s a landmark of a document that introduces concepts we know and probably take for granted today, including the “linking” of a stylesheet to an HTML document and an early example of the syntax that would model how we write classes and chain properties together. There are no brackets, rulesets, or even classnames. The proposal is just that: a proposal.

h1.font.family = times

If you’re looking for context, the proposal was published a mere three days before Netscape’s browser — called Mosaic Netscape 0.9 — was introduced. This was a fairly turbulent time in the history of the web, one that we tend to refer to refer as the start of “The Browser Wars”. If you can believe it, HTML itself was getting out of control and at risk of losing out to alternative ways of writing markup. CSS, in a way, was an HTML enhancement to make it more competitive:

Not long after [Lie] began at CERN, the language of the web shifted. Realizing that the web’s audience could not stare at black text on a white background all day, the makers of Mosaic introduced a tag that let website creators add inline images to their website. Once the gate was open, more features rushed out. Mosaic added even more tags for colors and fonts and layout. Lie, and the team at CERN, could only sit on the sidelines and watch, a fact Lie would later comment on, saying, “It was like: ‘Darn, we need something quick, otherwise they’re going to destroy the HTML language.’” — CSS-Tricks, History of the Web, Chapter 8

We can continue the detailed history, but let’s instead skip a few critical years and time travel to 1997. Eric Meyer and his team began to document early standards for using cascading styles, including his seminal post, “Creating Your First Style Sheet”. It’s here that we can see some of the earliest examples of style rules that contain property-value pairs:

BODY {font-family: serif;}

In the beginning, it was a great revolution. There was no need to rewrite the attribute values on all pages to change, for example, the heading color; one small change in cascading styles was enough. This was a great time-saving breakthrough for developers everywhere, introducing structure to how styles are applied to web documents.

At the expense of glossing over other important advancements, let’s just say that CSS started to find its place in the evolving field of web design. However, there was still a long way to go in terms of writing structured CSS that operates at scale; that is, strategies for managing the cascade.

The Evolution of Managing Specificity

One way to avoid naming collisions is to increase the “strength of a selector”, which is called specificity. It works great in the short term, but usually gets out of control sooner or later.

CSS Specificity is like a rank, that determines which set of rules will be applied to an element instead of other sets of rules. If the browser finds two conflicting styles pointing to the same element, it will apply the rules with higher ‘ranking’.

Celé léta se psalo o tom, jak byla celá myšlenka kaskády pošetilá, špatně navržená nebo vymyšlená pro jednoduché webové stránky, a která nefunguje pro robustnější webové aplikace. To je taky důvod, proč existuje jen málo vývojářů, kteří chtějí základnímu principu specifičnosti porozumět.

Vlastně je to docela pochopitelné, protože je samotná kalkulace poměrně komplexní, a je poměrně náročné se při psaní stylopisu zamýšlet nad jejich váhou. Doporučuji toto skvělé demo od Manuel Matuzovic, ve kterém si můžete vyzkoušet přidávat vyšší selektory a zorientovat se trochu v tom, jak to funguje.

Na následujícím obŕazku je vidět hodnocení náhodě vybraných selektorů:

CSS Specificity Selector

Pro to, abychom se vyhnuli problémům se specificitou je ale podstatné pochopit alespoň základní myšlenky a porozumět tomu, že takový mechanismus existuje a vědět, jak se těmto problémům vyhnout. Dnes existuje mnoho nástrojů a postupů, které nám umožňují se specificitou pracovat lépe, a proto se ani nemusíme zamýšlet jak funguje.

ID and classes

Na začátku jsme neměli moc možností. Změny specifičností selektorů jsme docíli zanořováním selektorů, změnou pořadí selektorů, psaní stylů inline a nebo využití !important. Častou praxí bylo rozdělit dokument na několik částí a těm dát jedinečný identifikátor ID, díky kterému jsme vytvořili další možnosti kombinací.

<header id="header">
<main id="main">
<footer id="footer">
#header a {text-decoration: none;}
#main a {text-decoration: underline;}
#footer a {font-size: 16px;}

Jelikož má selektor s ID vyšší specificitu, zamezíme konfliktu stejnojmených tříd a dočasně vyřešíme tento problém. To se původně zdálo jako dobrý nápad, ale problém je v tom, že ID jsou jedinečná, a to znamená, že:

  1. každý prvek má pouze jedno ID
  2. na stránce je pouze jeden prvek s tímto konkrétním ID

Proto je tento princip dlouhodobě nefunkční a nelze jej aplikovat u rozsáhlejších aplikací. Styly nemůžete znovu použít, a pokud pracujete na velkém projektu, zpomalí to proces vývoje. Je dobrým zvykem se zápisům ID v CSS vyhnout, protože takový kód pak může být více škálovatelný a modulární, čehož s ID nedocílíme.

!important

Dalším způsobem, jak řídit specificitu je pomocí !important. !important na konci zápisu at the end of a CSS value gets a specificity score of 10,000 points a tím přebije všechny deklarace.

Bohužel ani tento přístup není ideální, protože řeší problém jen krátkodobě. Tím, že budeme zanášet do kódu tento zápis neřeší problém s konflikty specifičnosti, protože se časem může stát, že budeme mít více pravidel, které se budou mezi sebou prát.

Navíc se použitím tohoto přístupu vyhýbáme využívat výhod CSS a děláme kód nepřehledný a opravdu těžko opravitelný a to je asi ten největší problém.

Spoléhat se na zvyšování specifičnosti při úpravě stylů obvykle vytváří efekt sněhové koule, což nutí každého v týmu k dalšímu zvýšení specifičnosti, čímž je přepisování stylů stále obtížnější. Proto je dobrým zvykem se zápisům !important vyhnout v případě, když řešíme problémy s konfliktem specifičnosti a je lepší zvážit jiný přístup.

!important má své místo a je vhodné ho využívat, pokud víme, proč ho využíváme. Nikdy bychom neměli využívat !important k řešení problémům s existujícím CSS.

Do not use !important reactively. Do not use !important to solve a specificity issue. Do not use !important in anger. — Harry Roberts

Ideální využití !important je u utility tříd. Ty fungují tak, že mají jedno konkrétní chování (nastavení velikosti textu, odsazení, změna barvy apod.) a používáme je tak, že k třídám CSS připíšeme právě tento selektor. Můžeme ho tak kdykoliv odebrat z HTML, víme přesně co dělá a hodí se pro rozšíření vlastnosti komponent, které mají v rozhraní výjimky.

.u-text-center {text-align: center;}
.u-float-left {float: left;}
.u-text-large {font-size: 48px;}

Na tomto principu byly postaveny utility-first frameworky jako jsou Tachyons, Basscss, Atomic CSS nebo Tailwind. Jak vznikly a k čemu jsou dobré?

Utility-first CSS

Utility-first přístupy v CSS vznikly jako reakce na problémy s údržbou a škálovatelností tradičních CSS metodik. Vývojáři hledali způsob, jak omezit narůstající složitost stylopisů při růstu projektů. Kolem roku 2013-2015 začaly získávat popularitu, kdy vývojáři hledali alternativy k tradičním metodikám jako BEM nebo SMACSS.

Jedním z prvních významných utility-first CSS frameworků byl Tachyons, který vznikl kolem roku 2014. Tachyons představil koncept malých, jednozměrných CSS tříd, kde každá třída dělá přesně jednu věc. Tento přístup inspiroval mnohé pozdější frameworky.

Tento přístup představoval radikální odklon od tradičních CSS metodologií a začal získávat pozornost vývojářů hledajících řešení pro rostoucí složitost CSS v moderních webových aplikacích.

Nástup Tailwind CSS a JavaScriptových frameworků

Období mezi lety 2017-2019 bylo poznamenáno vzestupem Tailwind CSS, který se postupně stal dominantním utility-first frameworkem. Tailwind přinesl vyšší úroveň konfigurovatelnosti a flexibilitu, což umožnilo vývojářům přizpůsobit framework svým potřebám. Velkou výhodou, která zaznívala v anketách a v komunitě bylo to, že nepotřebovali rozumět tomu, jak nativní vlastnosti CSS jako specificita, dědičnost nebo kaskáda fungují.

S rostoucí popularitou moderních JavaScript frameworků vznikla také potřeba lepší integrace, což vedlo k vývoji specializovaných nástrojů a pluginů pro React, Vue a další frameworky v letech 2019-2022.

Aktuálně pozorujeme posun k vyspělejším nástrojům, které řeší původní nevýhody utility-first přístupů. Technologie jako PurgeCSS pro automatickou eliminaci nepoužitého kódu, kompilace pro generování pouze skutečně použitých utility tříd a pokročilé možnosti přizpůsobení posunuly tyto frameworky na novou úroveň efektivity.

K čemu jsou utility-classes dobré

Utility classes mají oprávněně své místo mezi frontendovými vývojáři a tento způsob se také uchytil u generativních nástrojů na tvorbu funkčních prototypů, a to díky své oblibě v ekosystémech moderních JavaScriptových frameworků. Existuje mnoho výhod, kdy se podle mého názoru vyplatí sáhnout po tomto frameworku:

  • Rychlý vývoj uživatelského rozhraní bez nutnosti psát vlastní CSS
  • Konzistence designu v rámci celé aplikace díky omezeným možnostem
  • Nízká míra konfliktů specificity CSS
  • Snadnější spolupráce v týmu díky standardizovaným stylům
  • Možnost předvídat chování stylu bez nutnosti kontrolovat samostatné CSS soubory

I přes spoustu výhod, které nám tento framework nabízí existuje mnoho situací, kdy je lepší se tomuto modernímu frameworku vyhnout:

  • Větší množství tříd v HTML kódu, který se může stát nepřehledným
  • Strmější křivka učení pro vývojáře zvyklé na tradiční CSS
  • Obtížnější realizace komplexních, unikátních designových prvků
  • Mohou vést k vynucenému opakování kódu v komplexnějších komponentách
  • Bez použití komponovaných systémů může být obtížná údržba konzistentního vzhledu při změnách designu

Co se týče samotného Tailwindu, jednou z hlavních nevýhod, kterou při práci na projektech vnímám, je frustrace z neustálého vypisování jednotlivých CSS vlastností, častého nahlížení do dokumentace a hledání způsobů, jak řešit věci, které by nativní CSS zvládli bez problémů. Hezký přehled o tom napsal Aleksandr Hovhannisyan ve svém článku Why I Don’t Like Tailwind CSS.

TODO: Explain why LLM like utility-first frameworks

CSS Modules

V období kolem roku 2015, kdy komunita experimentovala s utility-first přístupy a CSS-in-JS řešeními, se začala formovat další alternativa, která měla problém specifičnosti (konkrétně globálního rozsahu CSS) řešit. Přístup CSS Modules představili Mark Dalgleish a Glen Maddern; Mark ve své přednášce na ReactiveConf, Glen jako článek na svém blogu. Reagovali na stejné problémy jako CSS-in-JS, ale zachovali tradiční CSS syntax a workflow, který byl CSS vývojářům bližší.

CSS moduly nejsou oficiální specifikací ani implementací v prohlížeči, ale procesem v kroku sestavení (s pomocí Webpacku a podobných nástrojů), který mění názvy tříd a selektory tak, aby byly v rozsahu platnosti (tedy aby měli lokální scope a nedocházelo ke konfliktům).

Místo psaní prostého HTML musíme zapsat veškeré kódování do JavaScriptového souboru, například index.js.

import styles from './index.module.css';

element.innerHTML = `html<h1 class="${styles.title}">Hello, world!</h1>`;

Během kroku sestavení by kompilátor prohledal importovaný soubor styles.css, poté by prošel napsaný JavaScript a zpřístupnil třídu .title prostřednictvím styles.title. Náš krok sestavení by pak zpracoval obě tyto věci do nových, samostatných souborů HTML a CSS, přičemž nový řetězec znaků by nahradil třídu HTML i třídu selektoru CSS.

Out generated HTML and CSS would look like this:

<h1 class="_styles_title_4o587j">Hello, world!</h1>
._styles_title_4o587j {
  font-size: 24px;
}

Atribut třídy a selektor .title zcela zmizely a byly nahrazeny tímto zcela novým řetězcem; Náš původní CSS se prohlížeči vůbec nezobrazuje.

CSS Modules představovaly střední cestu mezi tradičním CSS a radikálnějšími CSS-in-JS přístupy. Umožnily týmům získat výhody modulárního

CSS bez nutnosti opustit známou CSS syntax, což z nich učinilo populární volbu pro projekty, kde byla prioritou postupná migrace.

Tento nový způsob přinesl řešení, jakým se dá poměrně složitý mechanismus specificity obcházet bez nutnosti pochopení. Myslím si ale, že by každý vývojář, který pracuje se styly měl znát výhody globálního rozsahu CSS, protože existuje mnoho využití, kde to dává naprostý smysl.

CSS-in-JS

Zatímco frontend komunita experimentovala s utility-first přístupy, jiný paradigma tiše nabírali na síle: CSS-in-JS. Tento přístup reagoval na rostoucí sofistikovanost JavaScriptové ekosystému a potřeby vyřešit výzvy při psaní CSS u komponentového přístupu. Tento způsob práce se stal populární i možná pro to, že nastupující generace vývojářů viděla výhodu v tom, že jim tento způsob dokázal vyřešit mnoho problémů, které by jinak museli řešit kdyby používali nativní CSS.

Myslím si, že ti, kdo navrhli a nakódovali stovky webů a dokázali se do jazyka CSS ponořit dost hluboko, mají spíše kladný vztah k nativnímu stylování a vidí v něm především výhody. Naproti tomu ti, kteří si tento způsob jen osahávali a hned sáhnuli k alternativě, nemají k psaní nativního CSS kladný vztah.

První promo pro JavaScriptový (React) svět

CSS-in-JS vznikly kolem roku 2014 jako odpověď na problémy s CSS v komponentových architekturách (hlavně) React aplikací. Vývojáři a se snažili poukázat na problémy CSS a často zmiňovali několik oblastí: globální scope, závislosti, eliminace mrtvého kódu, minifikace, sdílení konstant, izolace, non-deterministic resolution, izolace apod.

Christopher Chedeau byl jedním z prvních, který rozhýbal ledy kolem CSS-in-JS. Na konferenci NationJS 2014 nejdříve zdůraznil problémy, které ho s nativním CSS trápí a následně poukázal, jak jde tyto problém řešit pomocí CSS-in-JS a byl teda jedním z prvních propagátorů tohoto přístupu.

Průkopník CSS-in-JS: Radium

Jako první knihovna, která vznikla je Radium. Tato knihovna se zaměřovala na inline zápis v React aplikacích a snažila se řešit problémy, které zmiňoval Christopher Chedeau ve své prezentaci. Jednalo se ale spíše o proof-of-concept ukazující možný směr, než že by se hodila pro dlouhodobě udržitelné komplexní aplikace.

<Button kind="primary">Radium Button</Button>
var Radium = require('radium');
var React = require('react');
var color = require('color');

class Button extends React.Component {
  static propTypes = {
    kind: PropTypes.oneOf(['primary', 'warning']).isRequired
  };

  render() {
    // Radium extends the style attribute to accept an array. It will merge
    // the styles in order. We use this feature here to apply the primary
    // or warning styles depending on the value of the `kind` prop. Since its
    // all just JavaScript, you can use whatever logic you want to decide which
    // styles are applied (props, state, context, etc).
    return (
      <button
        style={[
          styles.base,
          styles[this.props.kind]
        ]}>
        {this.props.children}
      </button>
    );
  }
}

Button = Radium(Button);

// You can create your style objects dynamically or share them for
// every instance of the component.
var styles = {
  base: {
    color: '#fff',

    // Adding interactive state couldn't be easier! Add a special key to your
    // style object (:hover, :focus, :active, or @media) with the additional rules.
    ':hover': {
      background: color('#0074d9').lighten(0.2).hexString()
    }
  },

  primary: {
    background: '#0074D9'
  },

  warning: {
    background: '#FF4136'
  }
};

Další následníci

Rok poté, co se začalo mluvit o revolučním přístupu v psaní CSS se objevil Aphrodite - přístup, který navázal na předchozí pokusy a implementoval nové funkce jako nastavení font-face, keyframes pro animace, nebo použití mimo React.

Tyto přístupy začali být populární mezi JavaScriptovými vývojáři čím dál více, a proto se nepřestávalo zkoumat a vytvářet nová řešení. Od roku 2016 postupně vznikaly Styled-components, Emotion, JSS, Linaria, Compiled, Vanilla Extract, Stitches nebo Panda CSS.

Všechny tyto CSS-in-JS knihovny sdílely několik klíčových principů a hlavně to, že automaticky generovaly unikátní názvy tříd po kompilaci čímž dokázali eliminovat kolize pojmenování selektorů a vyřešili tak věc, kterou trápilo mnoho vývojářů - globální scope CSS. Vesměs skoro všechny nástroje dokázali navíc generovat CSS s nízkou specificitou a spolu s unikátními třídami redukovali rizika konfliktů.

Problémy s CSS-in-JS

Cascade Layers

The Evolution of Naming Selectors

OOCSS

BEM

Other common systems

The Evolution of Maintainability

Separate Stylesheets

Build Systems

Pre-Processing (variables, compiling partials)

Post-Processing (optimization for production assets)

CSS-in-JS

CSS Modules

CSS Modules vznikly kolem roku 2015 jako reakce na problémy s globálním rozsahem tradičního CSS. Za tímto frameworkem stojí autoři Mark Dalgleish a Glen Maddern, kteří se tímto způsobem snažili vyřešit problém tak, že se při importu stylů do komponent třídy automaticky zneviditelní mimo komponentu samotnou a vygenerují se unikátní názvy tříd (např. .foo__1FUOu). Tím se stanou styly lokální jen pro danou komponentu.

Jak sami autoři píšou, existuje mnoho výhod proč je dobré tento přístup využívat.

CSS Custom Properties

CSS Nesting

Where We Are Today

Summing up today’s best practices based on historical evolution)

Conclusion

Sources

Utility-first

Preprocessors

CSS-in-JS

Tailwind CSS

Container Queries

CSS Modules

Specificity

List of methods

  • RSCSS
  • AMCSS
  • DoCSSa
  • CUBE CSS
  • SUIT CSS
  • Atomic CSS
  • Utility-first CSS
  • Tailwind CSS
  • Tachyons
  • Shed.css
  • Basscss
  • Expressive css
  • turretcss
  • CSS-in-JS
  • CSS modules
  • Styled Components
  • Cascade Layers
All posts