Back to path
AdvancedPulse · Project 8 of 12 ~10h· 6 milestones

Extract a reusable design system and theming

Continues from the last build: You held it to a performance budget on a real mid-range phone. It loads fast now, but every screen still hand-rolls its own button, card, and dialog markup with hardcoded hex colors, and the dark mode toggle you bolted on last sprint misses half the surfaces.

It is fast on a real phone now, but the codebase is not proud of it.

CSS custom properties and design tokensTheming with prefers-color-schemeAccessible component primitivesFocus management and focus-visible stylingCompound component APIs with variantsReact portals for overlaysCSS ModulesDesign system documentation

What you'll build

A tokens.css file drives every color and spacing decision, four accessible primitives (Button, Field, Card, Dialog) replace the copy-pasted markup across the metrics, events, and segments screens, theming respects prefers-color-scheme with a persisted manual override, and a docs page renders every variant in both themes so regressions are visible at a glance.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

src/styles/globals.csscss
body {
  background: #ffffff;
  color: #16161a;
}

@media (prefers-color-scheme: dark) {
  body {
    background: #121214;
    color: #f2f2f4;
  }
}

.metric-card {
  background: #f7f7f8;
  border: 1px solid #d9d9de;
  border-radius: 10px;
  padding: 16px;
}

.button-primary {
  background: #2f6fed;
  color: white;
  border-radius: 10px;
  padding: 8px 16px;
  border: none;
}

.button-primary:focus {
  outline: none;
}

Reading this file

  • .metric-card { background: #f7f7f8;Hardcoded hex instead of a token, so this card never picks up dark mode even though body does.
  • @media (prefers-color-scheme: dark) { body {Dark mode only exists on body. Every other component was styled once and forgotten.
  • .button-primary:focus { outline: none;The classic accessibility bug: focus outline removed and never replaced with anything.
  • border-radius: 10px;A magic number repeated three times in this file alone; it should be one token.

The only theming that exists today: a single background flip with no tokens anywhere else.

That's 1 of 8 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Extract design tokens as CSS custom properties

    4 guided steps

    Tokens are the single source of truth. Once color and spacing decisions live in one file, a primitive never needs to know it is light or dark, it just reads a variable, and a theme flip becomes a one-line attribute change instead of a re-render of every component.

  2. 2

    Build the Button primitive with variants

    4 guided steps

    A single Button primitive means a design decision like "danger buttons get more padding" or "the focus ring is 2px" is made once, in one file, instead of being re-litigated (and inevitably drifting) in every screen that needs a button.

  3. 3

    Build Field and Card primitives with accessible associations

    4 guided steps

    Label-to-input association and error announcement are exactly the kind of accessibility detail that is easy to get right once and easy to forget every time it is copy-pasted; putting it inside the primitive means it can never be skipped again.

  4. 4

    Build an accessible Dialog primitive

    4 guided steps

    A dialog is the single hardest primitive to get right by hand, and the one most likely to be silently broken (no focus trap, no Escape handling, focus lost into the body on close). Building it once, correctly, means every future modal in Pulse inherits that correctness for free.

  5. 5

    Wire ThemeProvider: system preference, manual toggle, no flash

    4 guided steps

    Respecting the OS setting by default is the accessible baseline; persisting an explicit override respects a deliberate choice someone made. Doing both without a flash of the wrong theme is what makes theming feel like a finished feature instead of a demo.

  6. 6

    Migrate screens and ship a docs page

    4 guided steps

    A design system that is not actually used everywhere is just an unused folder. The migration is where the payoff shows up: three screens worth of duplicated styling collapse into compositions of four primitives, and the docs page becomes the fastest way for anyone on the team to visually spot a broken token before it ships.

What's inside when you start

2 starter files, ready to clone
6 guided milestones
6 full reference solutions
8 code blocks explained line-by-line
6 "is it working?" checks
4 interview questions it prepares you for

You'll walk away with

A tokens.css defining the full light and dark color, spacing, radius, shadow, and typography scale
Four accessible primitives (Button, Field, Card, Dialog) with variants, all reading tokens and none containing hardcoded colors
A ThemeProvider that respects prefers-color-scheme by default, persists a manual override, and avoids a flash of the wrong theme on load
MetricCard, the event list, and the segment dialog migrated to the new primitives with their old bespoke CSS deleted
A docs page rendering every primitive variant in both themes for at-a-glance regression checking

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building