Chapter 2: CSS and the Separation of Presentation
Structure and presentation: the web’s first great architectural separation.
In 1994, a Norwegian programmer named Håkon Wium Lie was working at CERN, in the same lab where Tim Berners-Lee had recently invented the web. The web was growing fast, and it had a problem. Every author was inventing their own visual style by mixing presentation directives into the markup — <font> tags, <center>, <b>, color attributes — all of it living inside the document, tangled up with its meaning. Lie wrote a proposal called Cascading HTML Style Sheets that would let presentation live separately, in its own language, layered on top of structure. Bert Bos, a Dutch researcher who had been working on similar ideas independently, joined him as co-author. By 1996 the W3C published the first CSS specification. By 1998 CSS Level 2 was out. The W3C CSS Working Group has spent the thirty years since quietly extending the language while most of the frontend industry layered abstractions over it.
What CSS introduced was a layer separation. HTML described what a document meant. CSS described how that meaning should be presented. The two could vary independently. A heading could be a heading before anyone decided how large it should be. A list could be a list before anyone decided whether it should appear as bullets, cards, a menu, or a horizontal navigation bar. A form could be a form before anyone decided the spacing, colors, borders, or layout.
This separation still matters. Modern frontend applications often collapse meaning and presentation back together through component abstractions. A component can package markup, style, behavior, and state into a single unit, which feels tidy until you need to vary one without varying the others. CSS was the first reminder that the browser is a platform where meaning, presentation, environment, and user preference interact through layers — not a place where pixels appear from a single source.
The Cascade Was a Feature
Section titled “The Cascade Was a Feature”The cascade gets a lot of complaints, and some of them are fair. Specificity surprises people. Global selectors cause accidental coupling. Large stylesheets become archaeology projects no one wants to inherit. The cascade itself, though, is one of the browser’s original context systems, and it does something the rest of frontend architecture is still figuring out how to do well.
A paragraph inside an article should be able to look different from a paragraph inside a card. A link in a navigation bar should be able to look different from a link inside body copy. A form field in a compact sidebar may need different spacing from a field in a full-page editor. A user with prefers-reduced-motion enabled should experience a different presentation from one who hasn’t expressed that preference. A component inside a narrow container shouldn’t necessarily behave the same as the same component inside a wide one.
The cascade lets styles flow from broad defaults to local overrides. Authors can define rules at different levels of scope. Presentation can respond to the document tree, the media environment, user settings, and now container size and cascade layers. The browser, in other words, has long had a runtime adaptation system. We just didn’t always recognize it as one.
This is the runtime-adaptation system the platform has had since the beginning: presentation that adapts to context, declaratively, without JavaScript orchestration. The cascade was always doing this. The modern platform has made it dramatically more powerful.
Structure First
Section titled “Structure First”The value of CSS depends on HTML having structure worth styling.
Consider this:
<div class="title">My Library</div><div class="menu"> <span>Books</span> <span>Authors</span></div><div class="content"> <div class="section-title">Recently Added</div> <div class="item">Kindred</div> <div class="item">A Wizard of Earthsea</div></div>A stylesheet can make this look acceptable. The structure itself, though, says almost nothing. The title isn’t a heading. The menu isn’t navigation. The items aren’t a list. The menu entries aren’t links. The document has visual structure but barely any semantic structure.
Now compare:
<header> <h1>My Library</h1> <nav aria-label="Primary"> <a href="/books.html">Books</a> <a href="/authors.html">Authors</a> </nav></header>
<main> <section> <h2>Recently Added</h2> <ul> <li><a href="/books/kindred.html">Kindred</a></li> <li><a href="/books/earthsea.html">A Wizard of Earthsea</a></li> </ul> </section></main>This document still needs presentation, but it already has meaning. CSS can now enhance a structure that browsers, screen readers, search engines, and other tools can understand. The styling becomes additive, not foundational.
Modern frontend often forgets this. Styling is easier to make robust when the underlying document is meaningful, and component systems either preserve that property or hide it. The cleanest components are the ones whose rendered DOM, viewed in isolation, would still be a coherent semantic document.
CSS as the First Design System
Section titled “CSS as the First Design System”Before design tokens, component libraries, utility classes, CSS-in-JS, and theme providers, CSS already contained the beginnings of a design system.
A stylesheet could define a type scale, a color palette, spacing units, layout rules, and interaction states. It could create shared presentation rules for repeated structures. It could express hierarchy and consistency across a site. The vocabulary of “design system” came later, but the practice was there from the start.
Even a small document-oriented app can build a primitive token layer in a few lines:
:root { --color-bg: #ffffff; --color-text: #1f2933; --color-accent: #375dfb; --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 1rem; --radius-md: 0.5rem;}
body { margin: 0; font-family: system-ui, sans-serif; color: var(--color-text); background: var(--color-bg);}
a { color: var(--color-accent);}
.card { padding: var(--space-3); border: 1px solid color-mix(in oklab, var(--color-text), transparent 85%); border-radius: var(--radius-md);}This isn’t a complete design system, but it’s already a system. It defines shared values. It names them. It lets components consume them without hard-coding every decision. The browser resolves it all through the cascade, with no JavaScript required.
Kitsune takes this seriously. Its component layer doesn’t require a JavaScript runtime to change a theme. Its styling model uses CSS custom properties, cascade layers, slots, parts, and container queries because the browser already has a powerful styling runtime. The role of code is to consume and compose the platform’s primitives.
Where CSS Went, and Where It Caught Up
Section titled “Where CSS Went, and Where It Caught Up”CSS got hard once applications got hard. Stylesheets grew. Global selectors collided. Naming became fraught. Scoping was a problem the language hadn’t solved natively. Layout was painful before flexbox and grid. A whole ecosystem grew up in response — preprocessors, methodologies, CSS-in-JS, Tailwind’s utility-first approach — each addressing real limitations the platform had at the time. The next chapter walks through that ecosystem in detail, because the stories are worth telling on their own terms.
The other half of the story is that the platform didn’t stand still. Between roughly 2016 and 2024, CSS shipped a wave of architectural features that fundamentally changed what the language can do natively — custom properties, cascade layers, container queries, :has(), view transitions, modern color functions, @scope — championed by a small community of CSS specialists working through the W3C CSS Working Group. Miriam Suzanne, Rachel Andrew, Una Kravets, Bramus Van Damme, Adam Argyle, Jen Simmons, and others delivered, in five years, more language-level architecture than CSS had received in the previous fifteen. The deep architectural treatment of modern CSS as a runtime adaptation system lives in Part II. For this chapter, the direction is the point: CSS in 2026 is a different language than it was a decade ago, and many of the workarounds developed during the harder years are now solving problems the platform has caught up with.
Presentation Is Not Just Paint
Section titled “Presentation Is Not Just Paint”One trap in frontend thinking is treating presentation as superficial.
Presentation affects interaction. It affects comprehension. It affects accessibility. It affects performance. It affects whether an application can adapt to device, container, theme, preference, and context.
Spacing reveals hierarchy. Color signals state. Motion explains continuity or causes discomfort. Focus styles make keyboard interaction possible or impossible. Layout makes information visible or buries it. A disabled state clarifies an unavailable action or creates confusion. A form error guides recovery or strands the user. Each of these is part of how the application behaves, not just how it looks.
Structure and presentation are different layers, but the separation can’t go all the way — it can’t become a separation of meaning from responsibility. A semantic form without visible focus states is incomplete. A beautiful button built from the wrong element is incomplete. A layout that adapts visually but destroys reading order is incomplete. HTML and CSS are different layers; they have to work together as one application surface.
Modern frontend architecture should treat CSS as a first-class system, not a side effect.
What This Means for Modern Frontend
Section titled “What This Means for Modern Frontend”CSS demonstrated, thirty years ago, that browser-native layers can cooperate without one layer owning everything. HTML described meaning. CSS adapted presentation. JavaScript added behavior. Events communicated. Forms transacted. Each layer kept its role; the browser coordinated them.
Modern frontend often collapses too much into components. A component becomes the markup, the styling, the state, the data fetch, the analytics event, the validation rule, the permission check, the error logging, and the storage interaction all at once. That feels modular because the code sits in one folder, but it’s not modular — it’s a bundle of concerns wearing modular clothing.
CSS still shows the older and better pattern: separate the layer, define the contract, let the browser do the work.
In Kitsune, components don’t own every product capability, the runtime doesn’t own presentation, and styling doesn’t require JavaScript orchestration for every state change. Native elements keep their semantics. CSS carries tokens, themes, adaptation, and local presentation rules — through the platform’s primitives, decorated where decoration adds value.
The browser already knows how to coordinate layers. The work of the rest of this book is largely about building with that knowledge instead of working around it.
Exercise: Style the Document App
Section titled “Exercise: Style the Document App”Return to the tiny document application from Chapter 1.
Add a stylesheet that does the following:
- Defines a small token layer:
:root { --color-bg: ...; --color-text: ...; --color-accent: ...; --space-1: ...; --space-2: ...; --space-3: ...; --radius-md: ...;}-
Styles semantic regions:
header,nav,main,section,form,footer. -
Styles interactive elements: links, buttons, inputs, textareas, focus states, validation states.
-
Adds a responsive layout using modern CSS — try CSS Grid for the page-level layout, flexbox for component-level alignment, and at least one container query for a card or list that adapts to its container width rather than the viewport.
-
Adds at least one user-preference media query, such as:
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto; }}After the styling is in place, inspect the HTML again and sit with these questions:
- Did the stylesheet depend on meaningful structure? Where would it have been harder if everything were a
<div>? - Which presentation decisions became reusable through tokens? Which would have been awkward to vary if they had been hard-coded?
- What behavior did focus states and validation states support? Try the form again, with the keyboard only.
- Where did CSS act more like runtime adaptation than static decoration? Resize the window. Try the container query at different widths. Toggle reduced motion in your OS settings.
- If you removed the stylesheet entirely, what would still work? What would break? What does that tell you about which responsibilities CSS is carrying and which are HTML’s?
The point isn’t to create a beautiful app. The point is to feel CSS as architecture — a separate, coordinated layer doing real work that the rest of the platform relies on.