// 1994 CERN proposal · CSS 1 1996 · CSS Grid 2017 · :has() 2023 · OKLCH 2024 · view transitions 2024

CSS:DeclarativeStyle

CSS is not a general-purpose programming language — it is the declarative styling language Håkon Wium Lie proposed from CERN in 1994. In 30 years it climbed out of the IE6 dark age and, through Flexbox / Grid / Custom Properties / :has() / container queries, became the layered foundation of the Web's look. In 2026 it has natively reclaimed jobs SPA frameworks used to own — "the platform caught up." It is also, incidentally, Turing-complete.

1994Oct 10 · CERN proposal
Håkon Wium Lie
1996CSS 1 W3C REC
first standard
2017CSS Grid everywhere
"layout finally worked"
30 yrsStill evolving
:has · @container · OKLCH
3CSS
display: grid;--accent:has(input:checked)@containeroklch()transform: translate3dview-transitionanimation-timeline: scroll()flex: 1 1 0;@scopeplace-items: centermin(100%, 720px)
scroll
01

What is CSS

CSS = Cascading Style Sheets. It is declarative: you describe what you want to see, the browser decides how to arrange it. It is not a general-purpose programming language in the Turing-machine sense — yet, surprisingly (see §07), it is Turing-complete.

Declarative, not imperative paradigm

No loops, no assignments. You say display: flex and the browser arranges things — you describe the goal, not the steps. Same family as SQL / HTML / Make; nothing like JS / Python.

Cascading core

When multiple rules hit the same property, an (inline, ID, class, type) tuple decides which wins. This is what the "C" stands for — and the most common stumbling block for newcomers.

Operates on the DOM scope

CSS only speaks to elements that already exist. HTML provides structure, CSS provides look, JS provides behaviour — the content / style / behaviour separation is the Web's 30-year foundation.

Modular evolution spec

Since 2005 the W3C abandoned a single CSS 3 spec, splitting it into Selectors L4, Color L5, Containment L3 and friends. "CSS 3" is a label, not a version — which is why the language keeps moving 20 years on.

layout.imperative.jsImperative (JS)
// imperative: you write each step
const row = document.querySelector('.row');
const children = row.children;
const width = row.offsetWidth;
const per = width / children.length;
for (let i = 0; i < children.length; i++) {
  children[i].style.left = (i * per) + 'px';
  children[i].style.width = per + 'px';
}

// resize? do it all again
layout.cssDeclarative (CSS)
/* declarative: say what you want */
.row {
  display: flex;
  gap: 12px;
}
.row > * { flex: 1; }

/* responsive · accessible · GPU-composited
   the browser handles it all */
02

History : Timeline

From Håkon Wium Lie's 1994 CERN email, through the IE 6 dark decade, to the spring of 2017 when four browsers shipped Grid in the same season, to the 2023 trio of container queries / :has() / nesting — CSS's 30 years are a slow but unbroken curve.

  1. 1994·10·10

    Håkon Wium Lie's proposal at CERN

    October 10. From CERN, Norwegian engineer Håkon Wium Lie sends out an email titled "Cascading HTML Style Sheets — a proposal". The Web at the time had no notion of separated styling — HTML controlled its own look, browsers each painted differently. Lie's "cascade" idea was simple: author, user, browser each declare styles, merged by priority. The semantic model of CSS as we know it starts here.

  2. 1996·12·17

    CSS Level 1 — W3C Recommendation

    Late 1996: W3C ratifies CSS 1 as an official Recommendation. The scope is conservative: fonts, colours, alignment, margin/padding, simple selectors. But the structural break is done — style is separated from markup. Netscape briefly pushes JSSS (JavaScript Style Sheets) as a rival; it does not survive.

  3. 1998·05

    CSS 2 — positioning and media

    CSS 2 brings positioning (position: absolute / relative / fixed), media types (@media print), z-index, generated content (::before / ::after). Beautiful on paper — but browser implementations were too broken to rely on. This kicks off the dark decade of "table-based layout": nested <table> + 1×1 transparent-GIF spacers.

  4. 2001·08

    IE 6 ships — the CSS dark age begins

    IE6 ships the broken box model as a mainstream default, plus its own proprietary hacks: * html selectors only IE6 sees, _property underscore-prefix only IE6 parses. Front-end engineers learn to carry conditional comments <!--[if lte IE 6]> and a thick pile of hack cheatsheets. This era runs until roughly 2010 — Microsoft took nearly a decade to seriously update IE.

  5. 2003·05

    CSS Zen Garden launches

    Dave Shea's CSS Zen Garden: identical HTML, different CSS, radically different looks. The site argued more convincingly than any spec that "style can be fully independent of structure." A generation of front-end developers learns CSS by studying its submissions.

  6. 2005—2010

    CSS 3 modularised — no more "single CSS3 spec"

    W3C abandons the "single CSS 3 document" plan and breaks the language into independent modules: Selectors L3, Color L3, Media Queries L3, Backgrounds & Borders L3, and on. Each module ships its own Candidate / Recommendation. "CSS 3" becomes a marketing label, not a spec. This is what lets CSS evolve continuously for the next 15 years without log-jamming.

  7. 2009·06

    Media Queries Level 3

    The technical seed of responsive design: @media (max-width: 768px) becomes a Recommendation. Ethan Marcotte's 2010 article "Responsive Web Design" takes it mainstream. "Mobile sites" stop meaning a separate m.example.com domain and start meaning "one HTML + CSS that adapts."

  8. 2010—2018

    The preprocessor era — Sass / Less / Stylus

    CSS itself had no variables, no nesting, no mixins since 1996 — so the community built its own. Sass (2007), Less (2009), Stylus (2010) compile down to CSS. Around 2015, node-sass + gulp + grunt was the standard front-end pipeline. This era shaped what CSS itself later prioritised — --var, @nest, the lot.

  9. 2012·09

    Flexbox stabilises

    Flexbox reaches Candidate Recommendation; browser support lands progressively from 2013 (Chrome) → 2015 (Safari + IE 11). "Layout with flex" is now possible — goodbye float + clearfix, goodbye vertical-align: middle astrology. The first time layout has the right semantics.

  10. 2016·09

    CSS Custom Properties — native variables

    Chrome 49 (early 2016) → Firefox 31 → Safari 9.1: native CSS variables with --var + var(). The key thing: these are real runtime values, mutable from JS, swappable inside @media — Sass's compile-time variables cannot do this. From here on, "do we still need Sass?" begins to be a fair question.

  11. 2017·03

    CSS Grid lands in every browser the same year — "layout finally worked"

    March 2017: Firefox 52 → Chrome 57 → Safari 10.1 → Edge 16, all four major browsers shipping Grid Layout in the same spring. The most synchronised release in CSS history. Two-dimensional layout finally has native semantics — every "multi-column" layout before this was a hack. Jen Simmons's "Everything You Know About Web Design Just Changed" talk marks the moment.

  12. 2017·11

    Tailwind CSS — utility-first

    Adam Wathan ships Tailwind: "use class names as styles". Instantly polarising — OOCSS / BEM purists call it a regression to the 90s; the utility-first camp calls it the endgame: "design system in your CSS file." Whichever side, Tailwind drags the utility-first mindset back into the mainstream and directly shapes the later CSS Modules / CSS-in-JS debate.

  13. 2018—2023

    The rise and fall of CSS-in-JS — the platform caught up

    styled-components (2016) and Emotion (2017) put CSS inside JS, and trend hard for a few years. Post-2020 the trade-offs surface: runtime cost, SSR pain, weak tree-shaking. Around 2024, Tailwind + CSS Modules + plain CSS reclaim the mainstream. "The platform caught up" is the defining 2023-2025 narrative: :has(), container queries, nesting, color-mix, scroll-driven animations — everything that used to need JS now runs in CSS.

  14. 2023·02—08

    Container queries / :has() / nesting — three landmarks in one year

    Container queries (Chrome 105 2022-08, Firefox 110 2023-02, Safari 16 2022-09): elements size themselves to a parent container, not the global viewport — a feature requested for a decade. :has() (Safari 15.4 2022-03, Chrome 105, Firefox 121 2023-12): the "parent selector", long deemed unimplementable, finally ships. CSS Nesting: Sass-style nesting becomes native. Any of these alone would be a landmark; 2023 ships them together.

  15. 2024·03

    Scroll-driven animations — no JS required

    Chrome 115 (2023-07) → Firefox 127 (2024-06): animation-timeline: scroll() and view-timeline. Scroll- and viewport-driven animations now run entirely in CSS, no IntersectionObserver + RAF needed. The work continues — Safari is still catching up — but the model is clear: "animation = timeline + keyframes + trigger", and scroll is just one possible trigger.

  16. 2024·09

    View Transitions API — cross-page animation, natively

    Chrome 111 (2023) introduced same-document transitions; 2024 adds cross-document view transitions: a full-page navigation can now interpolate elements SwiftUI-style with not a line of JS. Safari and Firefox are following. A clear signal of "CSS reclaiming SPA-framework territory" — layout animations that used to require React / Vue are now a browser primitive.

  17. 2024·12

    Anchor Positioning — elements positioned relative to each other

    Chrome 125 ships anchor-name + position-anchor: tooltips, popovers and context menus no longer need JS to compute coordinates. One element declares itself an anchor; another positions itself relative to it, with the browser handling scroll, zoom and viewport-edge clipping. The kind of feature you wonder how the Web went 30 years without.

  18. 2025·06

    @scope reaches mainstream — local styles without tooling

    Chrome 118 (2023) shipped @scope early; by 2025 Firefox and Safari are in line. Style scoping no longer depends on CSS-Modules hash compilation or BEM naming discipline — write @scope (.card) {} directly. Another textbook "was tooling, now native" case.

  19. 2026·now

    CSS at 30 — the platform caught up

    CSS in 2026: variables, nesting, container queries, :has, scope, view transitions, scroll-driven animations, anchor positioning, OKLCH colour — all native. Sass and styled-components are optional, no longer required. Tailwind stays popular for DX, but the underlying platform is finally strong enough on its own. This very page is written in CSS — its live grid / container-query / :has() demos are themselves the answer.

03

Language Essentials : CssAlphabet

The eight cards below are CSS's core machinery: cascade/specificity, box model, flexbox, grid, custom properties, :has(), container queries, OKLCH colour. The ninth riffs on "is CSS even a language?"

A

Cascade + specificity

The "C" in CSS: cascading. When multiple selectors hit the same property, an (inline, ID, class/pseudo/attr, type/pseudo-elem) tuple decides — higher wins. "!important" is the nuclear option, use sparingly.

.btn                        /* (0,0,1,0) */
#hero .btn                 /* (0,1,1,0) wins */
a:hover                    /* (0,0,1,1) */
[disabled]                 /* (0,0,1,0) */
B

Box model — content / padding / border / margin

Every element is a four-layer box. CSS 2 defaulted to content-box (width = content only); modern stylesheets nearly all flip to box-sizing: border-box so width includes padding+border — the "wrong" IE5 behaviour everyone after 2010 admits was actually more sensible.

*, *::before, *::after {
  box-sizing: border-box;
}

/* width 360px = total · not content */
C

Flexbox — one-dimensional layout

Spec stabilised in 2012, browsers caught up 2013-2015. One axis + main/cross: it killed three of the oldest CSS pain points — inline alignment, splitting leftover space, vertical centring.

.row {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
}
D

Grid — two-dimensional layout

2017 saw all four major browsers ship Grid in the same year — the most synchronised release in CSS history. Rows + columns at once, named areas, auto-placement. Every "multi-column" before this was a hack; Grid is when the word "layout" finally meant what it says.

.page {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
}
E

Custom properties (--var)

Universally supported since 2016. True runtime values: mutable from JS, overridable inside @media, inherited through subtrees — Sass compile-time variables cannot do this. They are also the typed foundation of Houdini.

:root { --accent: #1572B6; }

.btn {
  background: var(--accent);
}

// runtime: el.style.setProperty('--accent', '#fff')
F

The parent selector, :has()

For 20+ years, "select the parent" was considered impossible (performance horror). 2022-2023 Safari / Chrome / Firefox all shipped it — browsers found a lazy-evaluation implementation. The single biggest selector-level advance in a decade.

/* row with a checked child */
li:has(input:checked) {
  background: color-mix(in oklch, accent 15%, transparent);
}
G

Container queries (@container)

The next generation of responsive: elements react to their parent container's width, not the viewport. The same Card component is narrow in a sidebar, wide in the main column, and rearranges itself accordingly — the first component-level responsive primitive.

.card-wrap { container-type: inline-size; }

@container (max-width: 320px) {
  .card { flex-direction: column; }
}
H

oklch() / color-mix()

Perceptually uniform colour arrives in CSS. RGB and HSL distort luminance; OKLCH (L = luminance, C = chroma, H = hue) finally gives design systems a reliable "lighten by 10%." Adopted by major design-token systems through 2024-2025.

:root {
  --blue-500: oklch(50% 0.20 250);
  --blue-600: color-mix(in oklch, var(--blue-500), black 15%);
}

"Is CSS a programming language?"

The Stack Overflow / Reddit perennial. Strictly — it's a declarative DSL, not a general-purpose programming language (no I/O, no mutable state, no built-in abstraction over a Turing machine). But (see §07) HTML + CSS can encode Rule 110, so it is Turing-complete in the theoretical sense. Verdict: yes a language, no not a general-purpose one.

"CSS isn't a bad programming language; it's a great styling language. Critiquing it as if it were a programming language is a category error."

04

Live Demos : ItsActuallyCss

This section is actually running CSS — not screenshots, not code blocks. Open DevTools and tweak — it all updates live. The existence of this section is itself the argument: CSS can demonstrate itself.

flexbox.cssflex
flex: 1
flex: 2
flex: 1
.row { display: flex; gap: 10px; }
.row > * { flex: 1; }
.row > *:nth-child(2) { flex: 2; }
Flex: middle takes 2 parts, the sides 1 each, total adapts to the parent. Spec 2012, universal 2015, still the daily-driver.
grid.cssgrid
header
a
b
c
d
.page {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  grid-template-rows: 60px 60px;
}
.page > :first-child { grid-column: span 3; }
Grid: real 2-D layout — rows and columns at once. The first cell span 3 covers the row. All four browsers shipped this in the same 2017 spring.
container.css@container
A
Component-level responsive
Drag the right edge → narrows below 320 = stacks
↔ drag the bottom-right corner
.wrap { container-type: inline-size; }

@container (max-width: 320px) {
  .card { flex-direction: column; }
}
Container queries: not viewport, the parent container's width. Drag the bottom-right resize handle and watch it adapt. Wide support arrived in 2023 — a 10-year wait.
has.css:has()
label:has(input:checked) {
  background: linear-gradient(135deg, var(--css), var(--css-deep));
  color: white;
}
:has(): the "parent selector" — long thought impossible, shipped 2022-2023. No JavaScript in this demo. Tick a checkbox and watch the whole <label> recolour.
anim.css@keyframes
@keyframes float {
  50% { transform: translateY(-18px) scale(1.08); }
}
.box { animation: float 3s ease-in-out infinite; }
Keyframe animation: three boxes — one floats, one is round and floats, one spins. GPU-composited, zero JS — animation has always been CSS-native.
oklch.csscolor
50%
60%
70%
80%
30°
90°
150°
330°
/* perceptually uniform · OKLCH */
.s50 { background: oklch(50% 0.20 250); }
.s60 { background: oklch(60% 0.20 250); }
/* +10% L = perceptual +10% lightness */
OKLCH: perceptually uniform — "+10% L = +10% visual brightness." HSL cannot do this (50% yellow ≠ 50% blue visually). Design systems migrated heavily to OKLCH from 2024 onward.

Specificity calculator

CSS selector precedence = the tuple (inline, ID, class/pseudo/attr, type/pseudo-elem), compared lexicographically left-to-right.

Selector
inline
ID
.cls / :ps / [attr]
type / ::ps
*
0
0
0
0
p
0
0
0
1
p::first-line
0
0
0
2
.btn
0
0
1
0
a:hover
0
0
1
1
[type="text"]
0
0
1
0
#hero .btn
0
1
1
0
#hero #cta.btn:hover
0
2
2
0
inline style="color:red"
1
0
0
0
!important overrides all
(separate axis · not part of the tuple)
05

2010 vs 2026 : BeforeAfter

CSS's most interesting 30-year story is "used to be a hack, now is one line." Four side-by-sides below: same goal, top is actual code from 2007-2014, bottom is the native answer from 2017-2024.

Clearing floats — clearfixflex

clearfix.css2007
.row { overflow: hidden; }
.col { float: left; }
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

/* 真实工程里还套 zoom:1 给 IE6 */
flex.css2015+
.row {
  display: flex;
  gap: 12px;
}

/* done. */
06

Why CSS deserves real depth : WhyCss

"It's just changing colours" is the most common newcomer misread. People who use CSS deeply know it's declarative, a performance lever, the accessibility entry point, a 30-year-backward-compatible living layer, and the layer of the Web with the most momentum in 2026.

"Declarative" is the feature, not the flaw

CSS doesn't tell the browser how to lay things out — only what the result should be, and lets the layout engine decide. It looks inflexible; in reality it's the foundation of 30 years of backward compatibility, platform portability and accessibility. Anyone who has hand-drawn layout in Canvas understands.

/* 浏览器决定怎么算; you say what */
.row {
  display: flex;
  gap: 12px;
}

Performance is the browser's job, not yours

The three-stage pipeline — layout, paint, composite — is all GPU-accelerated by the browser. A single transform: translateZ(0) hoists an element onto its own GPU layer. Compared to hand-rolled React/Canvas diffs, CSS is a performance lever — provided you pick the right property (mutate transform, not top).

/* GPU-composited */
.fly { transform: translateY(-10px); }

/* relayout · slow */
.bad { top: -10px; }

Accessibility + OS preferences out of the box

prefers-reduced-motion, prefers-color-scheme, prefers-contrastsystem-level user preferences are queryable directly from CSS. No navigator probing, no JS listeners. Users really do toggle "reduce motion", and a single CSS rule respects it.

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms!important; }
}

The platform caught up — fewer wheels to reinvent

The 2015-2022 stack — Sass for variables, styled-components, Modernizr, Animate.css, polyfill soups — is largely unneeded in 2026. Native --var, @scope, :has, container queries, scroll animations have all landed. "The platform caught up" is the cleanest summary of the last three years in front-end.

/* 2018 — needed Sass */
/* 2026 — native */
.card {
  --pad: 16px;
  padding: var(--pad);

  & h2 { color: white; }
}

30 years of backward compatibility

CSS 1 code written in 1996 still runs today — no browser drops old specs. This is the Web platform's superpower: old code keeps working without maintenance, new features stack on through progressive enhancement. More stable than any "modern" language.

/* CSS 1 (1996) · still valid */
body {
  font-family: serif;
  color: #333;
}
07

Aside : CssIsTuringComplete

Yes — CSS is Turing-complete — not in the casual hand-wave sense, but in the "paper exists, runnable demo exists" sense.

TURING · RULE 110

HTML + CSS can simulate Rule 110

In 2011, Eli Fox-Epstein demonstrated an HTML + CSS construction simulating the Rule 110 cellular automaton. Since Rule 110 was proven Turing-complete by Matthew Cook in 2004, HTML + CSS is Turing-complete by reduction.

The trick: :checked stores state, :hover + label[for] provides the trigger, the sibling combinator ~ handles neighbour propagation. Each manual hover/click simulates one clock tick — slow, yes, but Turing-completeness does not require speed.

Of limited practical value — no one writes Fibonacci in CSS. But it shows CSS's expressive power is quietly substantial, and it's the supporters' trump card in the eternal "is CSS a programming language?" debate. Verdict: a complete language, but the wrong tool for general computation.

08

Ecosystem / Docs / Tools : Ecosystem

CSS has no single owner — the W3C CSSWG drives the standard, MDN + web.dev teach it, the tool chain runs Sass → PostCSS → Tailwind. Below: the key pillars of CSS's 30-year ecosystem.

09

vs HTML / JS : CssVsTheRest

The Web's three layers: HTML for structure, CSS for appearance, JavaScript (TS) for behaviour. Each is its own language; together they form progressive enhancement — usable without JS, readable without CSS.

HTMLCSSJavaScript
OriginTim Berners-Lee · 1991Håkon Wium Lie · 1994Brendan Eich · 1995
ParadigmStructural markupDeclarative stylingImperative / multi-paradigm
Turing-completeNo (pure markup)Yes (combined with HTML · Rule 110)Yes (general-purpose)
Evolution paceSlow · WHATWG living standardMedium · modular (Color L5, Selectors L4...)Fast · TC39 yearly
Mutable stateNoneNone (user-driven: :hover / :checked)Fully mutable (heap / closures)
Side effects / IONone (browser parses)None (browser renders)Yes (fetch / DOM / Worker...)
Backward compat30+ years, never breaks30 years, never breaksStrict; occasional TC39 deprecations
GPU-acceleratedNative (transform / filter)Manual (canvas / WebGL / WebGPU)
Accessibility hooksSemantic elements + ARIAprefers-* media queriesManual (focus / live regions)
File sizeSmall · direct gzipMedium · < 100KB gzipped typicalLarge · frameworks reach MB
10

Outlook : TheRoadAhead

CSS in 2026: the platform has caught up, but it's still moving. Scroll-driven animations, view transitions, anchor positioning, Houdini and the Typed OM are four directions on the visible road — each reclaiming work that JS frameworks used to own.

HOT · 2026+

Scroll-driven animations + view transitions — SPA-framework jobs reclaimed by the platform

For a decade, entry animations, scroll-driven parallax, and cross-page morphs all required React / Vue / Framer Motion + IntersectionObserver + RAF. 2024-2026: animation-timeline + view-transition native — one CSS rule does the same job 10× faster, with no flicker, no jank. Safari is still catching up but the direction is settled.

The bigger story: CSS is genuinely reclaiming territory from SPA frameworks for the first time. :has() took back DOM querying; now animation. "The platform caught up" isn't a slogan, it's an observation.

JS + IntersectionObserver (2018)~5%
Native animation-timeline (2026)100%
ANCHOR

Anchor positioning — tooltips and popovers without JS

A 30-year pain point: making a tooltip follow a button, or having a popover dodge viewport edges, used to require getBoundingClientRect() + resize listeners. Chrome 125 (Dec 2024) ships anchor-name, Firefox and Safari following. A long-missing piece finally arriving.

HOUDINI

Houdini Paint API — CSS plugins

The CSS engine used to be a black box to developers. The Houdini Paint and Layout APIs expose paint(myThing) to JS — JS becomes a brush for CSS. Canvas-style patterns, custom masks, procedural textures all become CSS. The spec is full; browser implementations vary.

TYPED-OM

Typed OM + typed custom properties

CSS has always been a string soup: JS reads el.style.width and gets "320px", splits it by hand. The Typed OM makes it CSSUnitValue(320, "px") — addable, subtractable, comparable. Combined with @property giving custom properties types, defaults, and inheritance flags, CSS gains its first real "type system".