Design System

The live token, theme, and material system this site runs on, shown proving itself.

← Back to Work
Token architecture · illustrative composition

The case

Problem

Four sections, one visual identity each (Work, Prateek OS, Creator, Personal). Left alone, that drifts: a stray hex here, a one-off spacing value there, and nothing lines up anymore.

What I built

Three token layers (primitive scales, semantic meaning, component and theme bindings) that every page reads from directly, a theme scope that rebinds only the semantic layer per section, and two build gates that reject a raw colour or off-scale type size before it ships.

Why it matters

The same discipline as type-checking, applied to visual consistency: the rules live in code, and CI enforces them.

Result

One semantic token set behind all four themes. This page is built entirely from the production tokens and components, so a regression here is a regression you can see.

System architecture

One idea, stated once: three layers, each consuming only the one below it. A page never reaches past the semantic layer; a theme scope only ever rebinds semantic tokens.

  1. 1Primitive

    Raw scales. Never themed, never read by a component.

    --neutral-900--space-6--radius-lg--duration-base--steel-400
  2. 2Semantic

    Meaning. The only layer a page or component reads.

    --surface-raised--text-primary--accent--border-strong--section-gap
  3. 3Component + theme

    Narrow bindings, plus four scopes that rebind a few semantic tokens each.

    --glass-tint--nav-text.theme-os.theme-personal

Two build gates keep it honest: lint:css (Stylelint) andcheck:tokens (a Node audit) both reject a raw colour or an off-scalefont-size anywhere outside global.css.

Themes & materials

A theme does more than swap the palette. The specimen below is the same markup and copy four times over, so every visible difference comes purely from the semantic rebind. The line under each card names what that scope also binds beyond colour.

Work

Identical block, themed by scope

Same structure and same copy in every card. Surface, the three text weights, the accent and the border all come from this scope.

Muted supporting lineDim caption line
Raised surface
Accent link →

Steel accent. Clean dark surface, no grain.

Prateek OS

Identical block, themed by scope

Same structure and same copy in every card. Surface, the three text weights, the accent and the border all come from this scope.

Muted supporting lineDim caption line
Raised surface
Accent link →

Red accent. Drives the sticky system diagram; steel/red technical atmosphere.

Creator

Identical block, themed by scope

Same structure and same copy in every card. Surface, the three text weights, the accent and the border all come from this scope.

Muted supporting lineDim caption line
Raised surface
Accent link →

Red accent. Film-grain body material, editorial serif headings, red ambient wash.

Personal

Identical block, themed by scope

Same structure and same copy in every card. Surface, the three text weights, the accent and the border all come from this scope.

Muted supporting lineDim caption line
Raised surface
Accent link →

Restrained warm accent. Full re-skin: archival-stock surfaces, warm ink hierarchy, paper grain, serif headings, dark selection.

The scope drives the palette, the contrast inspector, and the material specimen below.

Semantic palette, resolved under the scope

Surface / pagethe page ground--surface-page 
Surface / elevateda barely-raised wash--surface-page-elevated 
Surface / raisedthe card / panel surface--surface-raised 
Surface / raised 2a recessed well inside a card--surface-raised-2 
Surface / raised 3small filled marks in a visual--surface-raised-3 
Text / primaryheadings, body--text-primary 
Text / mutedsupporting copy--text-muted 
Text / dimcaptions, metadata--text-dim 
Accentlinks, hover, active state--accent 
Border / defaulta decorative hairline--border-default 
Border / stronga defined container edge--border-strong 
Glass / tintthe frosted dock material--glass-tint 

The frosted-glass material

The exact .glass material the production dock uses, laid across a bright band, a dark band, and a line of readable text so the blur, saturation, tint and edge are all visible at once. Above and below the bar is the same content un-glassed, for comparison. Switch the scope to Personal to see it rebind to archival-stock glass.

prefers-reduced-transparency: reduce rebinds this same material to a near-opaque tint with the blur removed; prefers-contrast: more sharpens its edge. Both are token-level changes in global.css, so the dock, its mobile flyout, and the mobile SectionRail strip all pick them up without any component-specific CSS. This browser's live state, right now:

Reduced transparency offIncreased contrast off

Production components

Every specimen here is the real shared component with sample data. If one breaks globally, it breaks on this page too, so this doubles as a regression surface.

Status badge: the four canonical states

LiveIn developmentNeeds reworkPlanned

The dock, and its states

The live dock is fixed at the bottom of this page. This static gallery shows its four canonical item states on the shared .glass material, drawn from the sameprimaryNav data and icon paths GlassNav uses.

HomeWorkCreatorPersonal
  • Home resting
  • Work active page
  • Creator hover / focus (label revealed)
  • Personal keyboard focus ring

4 destinations. Resting icons are --nav-text-muted; the active icon is --nav-accent; hover/focus lifts to --nav-text and reveals the label.

Section rail

The rail on the left of this page is the shared SectionRail component, running the same deterministic scrollspy as /work and /work/prateek-os. Same component, same contract, no second implementation.

Project cards & placeholder visuals

The real ProjectCard / PlaceholderVisual, with real project data, real tier, and a real destination, so they behave exactly as they do on /work.

Illustrative vs. real evidence

One media frame supports two kinds of evidence. Illustrative compositions keep an overlaid label so they are clearly presented as diagrams; real screenshots use the same frame with a caption below the image, keeping product evidence readable without implying that it is an illustration.

Illustrative mode · illustrative composition
AccessLens dashboard showing a failed regression policy result and a report summary of new, resolved, unchanged, and current findings across scanned routes.
Real evidence mode

CTA & link grammar

Five kinds, each with one job and one position. Nothing floats in whitespace; every one is anchored to the thing it belongs to. The forward arrow is a single shared.cta-arrow element: it nudges 3px toward the destination on hover and keyboard focus, and holds still under reduced motion.

  1. Card-owned CTA.cta-owned

    Belongs to one card or block. Right-aligns to that block’s edge; reverts to the left below 40rem.

    In production: ProjectCard, the flagship and professional cards, the homepage OS teaser

  2. Section-level CTA.cta-section

    Belongs to a section. Sits at the section’s content origin, never floating in whitespace. Grey at rest, primary on hover.

    In production: “See all work”, “Visit Creator / Personal” on the homepage

  3. Back / context link.back-link

    Plain text link that follows the reading column back up one level.

    In production: “Back to Work” on every case study

  4. Cross-page prev / nextCrossSectionNav

    Paired navigation: previous points and aligns left, next points and aligns right. Bidirectional arrows.

    In production: the footer of /work, /creator, /personal and the case studies

  5. Inline contextual linka (in prose)

    Stays inline in a sentence, underlined. Never promoted to a button or given an arrow.

    In production: body copy across the case studies

Section-level link Card-owned CTA

Type & semantic tokens

Core scale: the reading hierarchy

  • Prateek Arora
  • Section heading
  • A supporting subheading
  • The opening line of a section, one size up from body.
  • Primary reading size for long-form paragraphs and case-study copy.
  • The secondary body size, used for supporting copy and dense UI text.

Utility roles: labels, never a hierarchy level

  • Button label
  • Section kicker
  • Caption / metadata

The homepage name and each identity’s editorial h1 are the only sanctioned bespokeclamp() sizes; check:tokens fails any other off-scalefont-size.

Spacing, as it is actually consumed

  • Page gutter--space-6 · 1.5remevery section’s inline padding
  • Content gap--space-10 · 2.5remstacked blocks within a section
  • Cross-nav lead-in--space-16 · 4remspace above the closing cross-nav
  • Section gap--section-gap · 5.5rembetween major page sections
The full primitive spacing scale (reference)
  • --space-0
  • --space-1
  • --space-2
  • --space-3
  • --space-4
  • --space-5
  • --space-6
  • --space-7
  • --space-8
  • --space-10
  • --space-12
  • --space-14
  • --space-16
  • --space-18
  • --space-20
  • --space-22
  • --space-24
  • --space-28
  • --space-32
  • --space-36

Radius & elevation

sm
md
lg
xl
full
--surface-raisedflat, no shadow
--shadow-glassthe dock, popovers
--shadow-elevatedcards, the unlock dialog

The cards sit on a lighter tonal surface so the two production shadows are actually visible; the values are unchanged. Cards bind --card-radius to --radius-lg; controls (buttons, badges, the dock) bind --control-radius to--radius-full. The glass material is a separate concern, inThemes & materials.

Accessibility: contrast inspector

Not a generic contrast calculator. This one resolves the live tokens under a theme scope, and it composites translucent tokens over a defined backdrop before measuring, so a hairline or the glass tint returns a real number, not an impossible one.

Aa

-- : 1

axe-core (serious / critical) is a build gate on /,/work, /work/prateek-os, /work/design-system,/creator, /personal, and the protected-route lock. Reduced motion, focus visibility over translucent surfaces, and the button-group keyboard contract are covered too.

Inspectable depth

Three interactive demos below. Each is wired to real production values; none of it is required to follow the argument above.

Motion

Every swatch moves the same object the same distance with the same easing (--ease-out-quad), so pressing play separates them purely by duration. The easing curves are compared underneath at one fixed duration.

  • --duration-fast · 150ms · hover / focus state changes (with --ease-out-quad)
  • --duration-base · 320ms · the dock pill expand and the rail width (--ease-out-quad)
  • --duration-slow · 640ms · the skill-reference draw-in (paired with --ease-out-expo)
  • --duration-slower · 900ms · the scroll-reveal slide-up (paired with --ease-out-expo)

Same 640ms, two easing curves

--ease-out-quad
--ease-out-expo

Under prefers-reduced-motion: reduce, global.css collapses every transition and animation site-wide to 0.001ms. The play button still resolves the end state instantly; nothing here behaves differently in kind, only in duration.

Interactive sandbox

Adjust the semantic tokens and watch the change propagate through a small composition. Scoped to this box only; nothing here touches :root or persists after you leave.

Composition

Semantic propagation

Heading, body, card, badge, link and border all read the sandbox tokens.

Against the production rules

  • Body text contrast
  • Radius
  • Accent

Responsive, for real

The live /work page rendered at three real viewport sizes and scaled to fit, never clipped or reflowed. Real breakpoints: the rail becomes a strip, the card grid stacks, the type re-fluids. Deferred until you load it so it never slows this page.

The system, enforced

The rules on this page are not aspirational. 10 automated checks run before any change ships, and this page is built entirely from the production tokens and components, so a regression here is a regression you can see.

The 10 gates
  • pnpm checkastro check / tsc, across .astro, .ts, .tsx
  • pnpm lintESLint
  • pnpm lint:cssStylelint, no raw colour literal in a style block
  • pnpm check:tokensNode audit: no raw hex/rgb/hsl or off-scale font-size outside the token layer
  • pnpm check:copyno em/en dashes in visitor copy; advisory AI-cadence list
  • pnpm format:checkPrettier
  • pnpm testVitest unit suite (includes the contrast math on this page)
  • pnpm test:e2ePlaywright, Chromium plus mobile Chromium
  • pnpm buildAstro / Cloudflare adapter production build
  • git diff --checkno whitespace errors

Built with

  • Astro
  • TypeScript
  • CSS custom properties
  • Stylelint
  • React

A three-layer token system, four theme scopes over one semantic layer, and a build that refuses a stray hex. A portfolio built the way a system is built.