Foundations

Six principles decide how every part of the system is built. When a question comes up that the documentation does not answer, these answer it.

1. Tokens, not styles

A component never asks for a raw value. It asks for a role: text on a surface, a pencil stroke, the display typeface. The preset decides what that role looks like, so a rebrand is a new preset rather than a rewrite.

.card {
  background: var(--sn-role-color-surface-container-lowest);
  color: var(--sn-role-color-on-surface);
  border-radius: var(--sn-radius-s);
  padding: var(--sn-space-m);
}

No colour value, font name or pixel size appears above, and none ever should. Palette and reference tokens are not even shipped, so a component cannot reach for them.

2. One decision in one place

Tokens live in levels, and each level may only point at the one below it. A hue is chosen once, in the reference level. A meaning is assigned once, in a role. A component then only names the meaning.

Because references only run downward, changing a decision means changing one line, and everything that depends on it follows. The levels are described on the Tokens page.

3. Intrinsic design

A layout should not ask what device it is on. It should ask how much room it has been given, and arrange itself from that. This is the principle that shapes most of the layout code.

  • Container queries, not viewport breakpoints. A component responds to the width of its own container, so the same component works in a narrow sidebar and in a wide column without knowing which one it is in. There are no @media (max-width) layout rules.
  • Layouts that wrap on their own. A sidebar pattern (a fixed basis with a large grow factor) puts two blocks side by side only when both fit, and stacks them otherwise. Grids use auto-fill with min(100%, 18rem) so columns appear as room allows.
  • Fluid values inside limits. Type sizes and larger gaps use clamp(), growing with the space available between a floor and a ceiling.
  • Line length is measured in characters. Text is capped by --sn-measure-body (65ch), not by a column count, so it stays readable at any width.
  • One unit for everything. Every spacing value is a fraction or multiple of one line-height unit of 1.5rem, so rhythm holds wherever the base size changes.
  • Logical properties. inline-size, margin-inline and padding-block replace width, left and top, so writing direction and mode do not break layouts.

The box below is a real container. Drag its lower right corner: the layout inside reflows by the width of the box, not the window.

MainTakes the room the side does not need.
OneAdapts to its box.
TwoAdapts to its box.
ThreeAdapts to its box.
FourAdapts to its box.
FiveAdapts to its box.
SixAdapts to its box.
/* the whole pattern, no breakpoints */
.split { display: flex; flex-wrap: wrap; gap: var(--sn-space-l); }
.split-side { flex-basis: 20rem; flex-grow: 1; }
.split-main { flex-basis: 0; flex-grow: 999; min-inline-size: 55%; }

.auto-grid { display: grid; gap: var(--sn-space-m);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr)); }

@container card (min-width: 30rem) { .card { flex-direction: row; } }

4. Explore everything, ship one thing

The source describes a large design space: 22 hues over two base palettes, four contrast modes, several typefaces, type scales and line heights. That is useful while designing and wasteful to send to a visitor.

The build resolves the one combination a site uses ahead of time and ships only its values, plus light and dark, the one axis that stays live. Before this extraction, the palette and reference files alone were about 7,800 lines of CSS sent to every visitor. The full switchable space still exists as a separate stylesheet that loads only when someone picks another preset.

5. Accessible by default

Accessibility is a property of the tokens and primitives, so components inherit it instead of remembering it.

  • Reader’s size is respected. On this site the root font size stays at 100% and sizes are in rem.
  • Reduced motion. Every duration token collapses to zero for readers who ask for it.
  • Focus is visible. :focus-visible outlines use --sn-focus-outline-width and --sn-focus-color.
  • Contrast is designed in. Every role has an on- partner made to sit on it.
  • Theme follows the reader. This site follows the operating system’s light or dark setting until the reader chooses.
  • Targets are large enough. Interactive elements on this site are at least 44 by 44 pixels.

6. One language, many contexts

A button on a website and a button in an application should feel like the same family without being identical. The name space includes a domain for this: website, web app, desktop app, mobile app.

Colour, palette and type voices are shared across domains. Component tokens, and parts of the role level, differ by domain: density, sizes, how much a component asks of the screen. Only the website domain exists so far. See Domains on the Tokens page.

Get the ones that don’t make the page.

Plenty of experiments never make it onto this page. The dispatch is where the half-finished ones go first.

Or follow along on X and LinkedIn.