Skip to main content
◆ Article

Strata Design System Rework Notes

Reworking a flat, undifferentiated interface into a layered language of five surface levels and eight hue tracks: the styles are still a single CSS file, but every block of content has a place again.

Astro CSS Design System
[ 00 ]

Contents

Jump straight to a section.

  • 4 sections
  • 6 min read

Browse the components demo page first, then read this article alongside the layering and split ideas it describes.

[ 01 ]

Layering and hue

Hierarchy is not decoration, it is structure: five surface levels plus a dedicated inset well layer

The rework started from a very concrete complaint: every block of content on the page looked equally heavy. Cards, tables, quotes and notes all shared the same grey background with a single border, so there was no break point while scrolling and readers could barely tell paragraphs apart by heading size alone. This is the most common side effect of flat design — once hierarchy is deleted, nothing is left to speak for it.

Strata's answer is to turn hierarchy into readable information instead of patching it with decoration. Two things: grade the surfaces, and give each section a hue.

Surfaces are split into five levels — void / base / surface-1 / surface-2 / surface-3, plus an inset well layer that sits darker than the page itself. All structure is carried by light and shade plus a 1px hairline, and static panels never take a shadow — show a border and a shadow at the same time and the edge goes muddy.

Each section in turn owns a hue track, and the hue appears in only three places: the monospace index badge, the hairline at the top of the section, and a soft tint in the background. Adjacent sections never share a background, so every segment has a clear entry and exit while scrolling.

css strata.css

      /* Semantic tokens — components never reference raw colour values */html.strata-root {  --base:      #060b14;  --surface-1: #0b111f;  --surface-3: #161f33;  --line:        rgba(255,255,255,.09);  --line-strong: rgba(255,255,255,.17);  --accent:    #3b82f6;   /* Accent: blue */} /* All component rules are scoped inside .strata; 0,1,1 outranks the site's global 0,1,0 */.strata .btn { background: var(--accent); color: var(--accent-ink); }
  
  • [*]band treatments6
  • [*]section hues8
  • [*]surface levels5 + inset
  • [*]hairline weights3
  • [*]hovercolour swap, no tween

“Flat design isn't about deleting hierarchy, it's about drawing hierarchy more accurately. A hairline is enough — there's no need to add another shadow.”

[ 02 ]

Component split

30 components, four layers of responsibility, one and the same class-name contract

The work was split along section boundaries into 30 components: base components handle the visuals, interactive components handle behaviour, and all of them expose their configuration through props while following the same class-name contract, so the body styles never collapse.

  • Surface layer: Panel / Card / StatCard / GlassTable / ListGroup
  • Structure layer: Band / SectionHeading / SpecList / Divider / KeyCap
  • Interaction layer: Modal / Toast / Tooltip / Dropdown / Tabs / SearchBox
  • Feedback layer: Alert / Progress / Spinner / Skeleton / Empty / StatusIndicator
  • Skeleton layer: ArticleShell / ArticlePart / Sidebar / EdgeRail
Band Panel CodeBlock Terminal
[ 03 ]

Interaction runtime

One round of event delegation, everything declared by attributes

No JS has to be written by hand anywhere on the page: opening modals, firing toasts, toggling dropdowns, copying code, sorting tables — all of it is declared with data-strata-* attributes, and the runtime does a single round of event delegation on the document. Keyboard semantics are complete — focus trapping in modals, Esc to close and restore focus, arrow-key roaming in tablists, ↑↓ selection in the command palette.

html usage.astro

      <!-- Interactions are declared with data-strata-* attributes — no JS on the page --><Button data-strata-open="confirmModal">Open modal</Button><Button data-strata-toast="Saved">Save</Button><CodeBlock code="npm i" />   <!-- The copy button wires itself up --> <!-- Typewriter / number count-up / scroll reveal are attribute-driven as well --><span data-strata-type data-words='["A","B"]'>A</span><span data-strata-count="128">0</span><div class="pg-reveal">Fades in when it enters the viewport</div>
  
Command palette⌘K
Close overlayESC
[ 04 ]

Light and dark theming

Only one set of semantic tokens changes; there is no dark: variant in any component

The theme changes one set of semantic tokens only; there is not a single dark: variant inside any component. Dark is the default, the choice is written to localStorage, and it is restored by an inline script placed before the stylesheet — so there is no white flash on refresh. To share a particular theme directly, just add ?mode=light.

The moment you switch, a 260ms cross-fade is applied to the whole page: it only transitions colour-like properties, so movement animations and the “hover never tweens colour” rule are unaffected.

data-mode localStorage no dark: variants

The whole system compresses down to 2.1kb of CSS (gzip). What really keeps it from going muddy isn't the new components but the decisions that look trivial: three hairline weights, six backgrounds, eight hues — and one rule that refuses to compromise: hover swaps colour, it never animates.

Related articles

Other long reads in the same layout: an interactive comparison, a player embed and an Android teardown.
Comments Leave your thoughts
Guide