slides.oddbird.net/layers/aea-denver22/

Cascading Layers of !mportance

slides.oddbird.net/layers/aea-denver22/

@ An Event Apart, Denver

Web for all. Web on everything.

W3C Mission, Design Principles

It is required that HTML be a common language between all platforms

WWW Project

…This implies no device-specific markup, or anything which requires control over fonts or colors.

WWW Project

The fact we can control a paper page is really a limitation of that medium.

– John Allsopp, 2000

@media prefers-reduced-motion

@supports container-type: inline-size

Animation of twitter-like posts appearing

We’re designing dynamic content with unknown collaborators on an infinite and unknowable canvas, across operating systems, interfaces, writing-modes, & languages

– me

HTML totally eliminates any visual creativity that a document’s designer might have.

– Roy Smith, 1993

The web would have become a giant fax machine where pictures of text would be passed along.

– Håkon Lie

This proposal tries to soften the tension between the author and the reader.

– Håkon Lie

The user/browser specifies initial preferences and hands the remaining influence over to the document.

– Håkon Lie

Provide hints that the browser may or may not use.

– Håkon Lie

<button style='color: blue'></button>
button { color: violet; }
<link rel="stylesheet" href="so-many-hints.css">
<style>...</style>

An ordered list (cascade) of style sheets. Referenced from the same document.

– Håkon Lie

💥🙈⁉️💥

<button style='color: blue'></button>
button { color: violet; }

Multiple or Unknown ⁉️

color: teal;
color: violet;
color: oklch(79.269% 0.171 70.67);

On every html element,
For every css property
We need exactly one value

Rules for… Cascading filters out
Inheritance fills in

Mechanical coin sorter on a table,
with a ramp at the top
and a small-to-large series of slots

Officially… 🖥 User Agent Styles

body { margin: 8px; }
a:link { color: blue; }
div, section, article, p, ol, ul /* etc */ {
display: block;
}
h1, h2, h3, h4 /* etc */ {
font-weight: bold;
font-size: something big I dunno;
}

👥 User Styles

Establish desired preferences

Cascade 🗺 Origins

  1. 🖥 User Agent (goes first)
  2. 👥 User (override browser)
  3. 🎨 Author (most powerful)

If conflicts arise the user should have the last word

– Håkon Lie

  1. ❗🎨 Author Important (goes first)
  2. ❗👥 User Important (override authors)
  3. ❗🖥 User Agent Important (most power)
  1. 🖥 User Agent Defaults
  2. 👥 User Preferences
  3. 🎨 Author Styles
  4. ❗🎨 Author Important
  5. ❗👥 User Important
  6. ❗🖥 User Agent Important

Often Isolated

  1. 🎨 Author Styles
  2. ❗🎨 Author Important
  3. ❗…
  4. ❗…

(out of order…) CSS Selectors & Specificity

Dealing With Selectors

.this-is a.selector {
/* declarations: here; */
}

Heuristic

a practical assumption that approximates the goal

More Specific Styles
Likely
More Important

Four layers

  1. Universal * (go first)
  2. Element types
  3. Reusable .classes & [attributes]
  4. Unique #IDs (most power)

Assumptions Often Fail

Especially “At Scale

/* …default table styles… */
table[rules=cols i] > tfoot > tr > td,
table[rules=cols i] > tfoot > tr > th,
table[rules=all i] > tfoot > tr > td,
table[rules=all i] > tfoot > tr > th
{
border-color: black;
}

One Flexible Layer

  1. Universal *
  2. Element types
  3. Reusable .classes & [attributes]
  4. Unique #IDs
.block .element.modifier { /* 3 */ }
.block__element--modifier { /* 1 */ }
.🤬-bootstrap {
font-weight: bold !important;
}

If only this horse was a little faster

– me

@layer settings {}
@layer tools {}
@layer generic {}
@layer elements {}
@layer objects {}
@layer components {}
@layer overrides {}
@layer reset {
audio[controls] { display: block; }
[hidden] { display: none !important; }
}
  1. @layer settings { … }
  2. @layer tools { … }
  3. @layer generic { … }
  4. @layer elements { … }
  5. @layer objects { … }
  6. @layer components { … }
  7. @layer overrides { … }
  1. @layer Utilities { … }
  2. @layer Components { … }
  3. @layer Themes { … }
  4. @layer Frameworks { … }
  5. @layer Resets { … }
@layer reset { /* least powerful */ }
@layer default { /* … */ }
@layer theme { /* … */ }
@layer components { /* more powerful */ }
/* unlayered styles: most powerful */
@layer framework {
#menu .dropdown .menu-item {
background: whitesmoke; /* ✅ specificity! */
}

.menu-item {
background: lightcyan;
}
}
@layer framework {
#menu .dropdown .menu-item {
background: whitesmoke;
}
}
@layer override {
.menu-item {
background: lightcyan; /* ✅ layer! */
}
}
@layer one {}
@layer two {}
@layer one {}
@layer three {}
@layer one {}
@layer generic {
audio[controls] { display: block; }
}

@layer theme { /* … */ }

/* still a lower layer than "theme" styles */
@layer generic {
[hidden] { display: none !important; }
}
@layer generic {
audio[controls] { display: block; }
[hidden] { display: none !important; }
}

@layer theme { /* … */ }

Explicit Layer Order

Define it once, at the start

/* establish layer order */
@layer one, two, three;

/* add code to layers as needed */
@import url(two.css) layer(two);
@layer three {}
@layer one {}
@layer two {}
@layer components {
@layer state {}
}

/* access nested layers */
@layer components.state {}
/* system.css */
@layer theme {}
@layer components {}
@import url(system.css) layer(system);

@layer system.theme {}
@layer system.components {}
@import url(system.css) layer(system);

@layer system.theme {}
@layer system.components {}
@layer system.custom {}
@import url(bootstrap.css) layer(bootstrap.theirs);

@layer bootstrap.ours {
/* anything here will override bootstrap */
}
@layer components {
@layer defaults, themes, state;
}

Language Heuristics Don’t Boss Us Around

Like Origins, ❗️mportant Layers Reverse

  1. Resets
  2. Themes
  3. Components
  4. ❗mportant Components
  5. ❗mportant Themes
  6. ❗mportant Resets
@layer reset {
audio[controls] { display: block; }
[hidden] { display: none !important; }
}

Prioritize -> Layers
Protect -> Importance

Getting Started

my recommendations, your milage may vary

/* add as the first styles */
@layer reset, framework, components, utilities;
<style>/* keep this before linked styles */
@layer reset, framework, components, utilities;
</style>
<link rel="stylesheet" href="">
<link rel="stylesheet" href="">

Then Layer Dependencies

*Including inter-org dependencies

Clearly define… Each Tool’s Priority

Clearly define… Tool-Specific Overrides

@import url(bootstrap.css) layer(bootstrap.vendor);

@layer bootstrap.overrides {
/* anything here will override bootstrap */
}
/* Vue example */
<template></template>
<script></script>

<style>
@layer components {
/* all our component styles */
}
</style>

X Overrides Y
Because
‘Components’ Override ‘Defaults’

Encourages Nuanced & Explicit Priorities

[id="example"] {
/* ID selector, class specificity */
}
:where(#example) {
/* ID selector, zero specificity */
}

👎🏼 Override Styles
In Previous Code

(often in lower layers)

👍🏼 Protect Styles
From Future Code

(often in higher layers)

Use unlayered for Prototyping & DeBugging

1. Avoid Naming Conflicts

(across large teams & projects)

2. By Expressing Membership

(through lower boundaries & proximity)

.light-theme a { color: purple; }
.dark-theme a { color: plum; }
@scope (.light-theme) {
a { /* similar to simple nesting… */ }
}
@scope (.dark-theme) {
a { /* but the _closer_ scope root wins… */ }
}
.title { /* global */ }
.post .title { /* nested */ }

.post__title { /* BEM */ }
Media component with contents that are out of scope
wireframe of a site, with multiple nested components

Build-tools Provide Scoped Styles

BEM, CSS Modules, Vue, JSX, Stylable, etc

.post__title { /* BEM */ }
.title[data-JKGHJ] { /* Vue */ }
@scope (.media) to (.content) {
img { /* only images that are "in scope" */ }
}
<article>
<style scoped>
p { color: green; }
</style>
<p>This paragraph will be green.</p>
</article>

<p>This paragraph won't!</p>
<article>
<style>
@scope {
p { color: green; }
}
</style>
<p>This paragraph will be green.</p>
</article>

<p>This paragraph won't!</p>
Diagram shows a widget with solid boundaries,
which cannot be penetrated
in either direction
(global styles can't get in, widget styles can't get out)

Broad-Reaching Scopes
&&
Narrow Component Scopes

@scope ([data-scope='media']) to ([data-scope]) {
/* Stop scopes from overlapping? */
}

prototype… Now in Chromium Canary

about://flags - experimental web platform features

The cascade Expresses Layers of Priority

Yoda leans on a walking stick among leaves

A Jedi uses the [Cascade] for knowledge and defense, never for attack.

– Yoda (almost)