M

    Case Study — Puzzle

    A systematic design, built to scale

    A design system built to be modular, scalable, and systematic — where components compose without colliding, growth costs almost nothing, and one set of rules governs every screen. This is the thinking behind it, from principles down to the components people actually touch.

    ScopeDesign system architecture
    StackPlain CSS · Zero runtime deps
    TypefaceInter
    ModesDark & light, one variable
    Design tokens Theming architecture Component library Accessibility Scalability CSS architecture

    Experience it yourself

    Click through the live, clickable prototype to feel how the controls, states, and interactions come together.

    Open prototype

    A design system is not a component library. A component library is what you get when designers and engineers solve the same problem fifteen times and staple the results together. A design system is what you get when you decide, before writing a single class name, what the contracts are.

    This system was built from that distinction. The contracts came first — the token layer, the naming model, the interaction rules, the elevation logic. The components are downstream of those decisions, not the source of them. Every element, from a button hover state to a table row separator, is derivable from the token layer without bespoke overrides.

    "A design system is not done when it is built. It is done when it holds up under the questions it was not built to answer."

    Three principles govern every decision: generalizability over specificity, constraint over flexibility, and correctness over convenience. A token that works everywhere is worth ten that work for one component; fewer well-defined values produce more coherent output than open-ended scales; and the easy answer is often the wrong one.

    One file, no runtime

    The system ships as a single HTML file with embedded CSS and JavaScript. No build step, no preprocessor, no runtime. Every token is a CSS custom property, every component a pure CSS class, and the entire theme flips — dark to light — by changing one variable.

    ~95Tokens designed & documented
    1Variable flips the full theme
    4Token layers, alpha → state
    0Runtime dependencies

    Three properties the system had to earn

    A design system isn't judged by how it looks in a sample screen. It's judged by how it behaves under pressure — when a new component lands, when the brand shifts, when a second team adopts it, when an edge case appears that nobody scoped. From the outset, I held the system to three properties. Every decision downstream had to defend one of them.

    01

    Modular — parts that compose, not pieces that collide

    Modularity means a component is self-contained and predictable: it declares what it needs, draws everything from shared foundations, and never reaches around the system to style itself. A button doesn't know what a card is. A card doesn't know what a modal is. Yet they sit together coherently because they're all assembled from the same small vocabulary of decisions. New parts slot in without renegotiating the existing ones — the hallmark of a modular system rather than a pile of components.

    02

    Scalable — adding more shouldn't cost more

    A brittle system gets more expensive with every addition; the tenth component is harder than the first, and the second theme doubles the maintenance. A scalable one flattens that curve. Here, a new component inherits the entire system for free, a rebrand is a handful of values, and a new mode is an override rather than a rewrite. The cost of growth stays roughly constant no matter how large the system gets — which is the only way a system survives contact with a real, growing product.

    03

    Systematic — one set of rules, applied everywhere

    Systematic means there are no special cases hiding in the corners. The same contrast logic governs body text and a disabled placeholder. The same elevation model governs a panel and a dropdown. The same focus treatment appears on every interactive element without exception. Consistency stops being a thing you police in review and becomes a property the system produces automatically — because everything is derived from the same source, divergence has nowhere to enter.

    Why these three, together: modularity makes the system composable, scalability makes it survivable, and systematic rigor makes it coherent. Drop any one and the other two erode — a modular-but-unsystematic system drifts, a systematic-but-unscalable one ossifies. The architecture exists to hold all three at once.

    A foundation in service of the components

    Those three principles don't enforce themselves — they need an architecture underneath that makes them the path of least resistance. That architecture is a layered foundation the components draw from, so that being modular, scalable, and systematic is simply easier than not being. The foundation isn't the product; the components are. But the components are only as trustworthy as what they're built on.

    The foundation is organized into four layers, each building on the one below, each with a distinct job and a distinct audience. No layer reaches past the one directly beneath it — that single discipline is what makes the whole system reversible, themeable, and safe to extend.

    LayerNameWhat it encodesWho consumes it
    0Alpha scaleWhite or black at stepped opacity valuesLayer 1 only
    1Semantic tokensMeaning: text contrast, border weight, surface elevation, overlay densityComponent tokens + CSS classes
    2Component tokensPer-variant values for buttons, inputs, selectors, table, segmented controlComponent CSS
    3State tokensConditions: disabled, error, focus, checkedComponent CSS, kept separate from scale
    The contract that makes theming work: No component reaches past Layer 1 into Layer 0. The alpha scale exists to be referenced, never used directly. That single rule is what lets the entire theme flip on one variable.

    From raw alpha to meaning

    The alpha scale is the mathematical foundation: thirteen steps of white at increasing opacity in dark mode, and the same steps in black for light mode. It's the only thing that changes between modes — every downstream token inherits the flip automatically.

    --alpha-5:   rgba(255,255,255,0.05);   /* dark mode */
    --alpha-5:   rgba(0,0,0,0.05);         /* light mode — only this changes */

    Layer 1 translates raw alpha values into meaning. These tokens don't describe colors — they describe intent. Text follows a five-step perceptual scale; borders reuse the same scale but name themselves by visual weight; overlays cover hover, pressed, and backdrop in three steps.

    Text contrast

    TokenDarkLightAlphaUsage
    --text-dark#FFFFFF#000000100%Buttons, critical labels
    --text-default#E8E7F1#1A1A1A90%Body text, data values
    --text-medium#9897AC#66666660%Secondary text, table headers
    --text-light#726F87#99999940%Placeholders, captions
    --text-disabled#3E3D4E#CCCCCC20%Disabled form fields

    Borders & overlays

    TokenAlphaPrimary usage
    --border-disabled5%Row separators, dividers — structure without attention
    --border-light10%Panel borders, card outlines, default input borders
    --border-medium20%Checkbox / radio / switch default state
    --border-default30%Intentional, clearly-visible separators
    --border-dark50%Hover state for inputs and selectors
    --overlay-light5%Subtle hover, nav hover, ghost button hover
    --overlay-medium10%Pressed state, table row hover, modal dimmer
    --overlay-dark20%Focus ring glow, stronger pressed state

    The button variant matrix

    Component tokens are not a separate system — they are aliases into Layer 1. They exist to make intent legible in CSS, so a theme change at the brand level propagates to every component automatically. Seven button variants cover every action classification, each with four states and three sizes, fully covered with no inline overrides.

    VariantRoleBackgroundTextHover method
    PrimaryPrimary CTAGreen #50FAABBlack::after overlay (+20% white)
    SecondarySecondary CTAPurple #C08FEEBlack::after overlay
    SubtleLow-emphasisSurface +5%Text 90%Direct swap to +10%
    GhostMinimalTransparentText 90%Direct swap to overlay-light
    WarningCautionaryYellow #F7DB60Black::after overlay
    DestructiveIrreversibleRed #E7366CWhite::after overlay
    DangerSoft destructiveSurface +5%Error redDirect swap
    An architecturally significant distinction: Solid buttons use a ::after pseudo-element overlay so their background can change for any reason — theming, context, state — without breaking hover logic. Transparent buttons swap backgrounds directly, because there's nothing to composite over.

    One variable, full inversion

    Most systems that support two modes maintain two parallel token sets — every color declared twice, every new component defined twice, maintenance cost growing forever. This system solves that architecturally. Every alpha-derived token references a single base; change the base and the entire derived cascade changes with it.

    :root            { --base: 255,255,255; }   /* dark mode */
    body.light-mode  { --base: 0,0,0; }         /* light mode */
    
    --text-default:  rgba(var(--base), 0.85);
    --border-light:  rgba(var(--base), 0.10);
    --overlay-medium:rgba(var(--base), 0.10);

    One assignment. Every text, border, and overlay token inverts correctly — no secondary declarations, no parallel scales. This isn't a trick: semi-transparent colors over a known background are mathematically equivalent to a computed hex. The pattern just makes that math explicit and controllable.

    The elevation exception

    The alpha-base pattern assumes a single axis of variation. Elevation breaks that assumption legitimately. In dark mode, raised surfaces are lighter than the background — depth is expressed through lightness. In light mode, raised surfaces look elevated through shadow, and their color is identical to the default surface. The direction of surface lightness inverts between modes, which no single alpha derivation can express.

    SurfaceDarkLightElevation method
    Sunken#121221 — darker#E6E6E6 — darkerColor delta only
    Default#171629 — base#FFFFFF — baseBaseline
    Raised#1D1C2E — lighter#FFFFFF — sameDark: lightness · Light: shadow
    A principled exception: Elevation tokens are the only ones assigned explicit per-mode values rather than derived from the alpha base. The semantic distinction between "raised" and "sunken" requires mode-specific knowledge that can't be abstracted — and recognizing the difference between a workaround and a principled exception is what makes a system trustworthy.

    Fixed colors, one workhorse size

    Brand colors do not change between modes. They are fixed, saturated, and high-contrast, designed to be legible against any surface; their hover and pressed variants are computed by applying +20% white and +20% black. Text on a brand color is always pure black or white, chosen by contrast ratio — black on green, purple, blue, and yellow; white on error red.

    ColorHexRole
    Primary#50FAABPrimary CTAs, checked state, success, brand
    Secondary#C08FEEFocus rings, links, secondary CTAs
    Natural#63C5FFInformational, data, neutral indicators
    Warning#F7DB60Cautionary actions, alerts, pending
    Error#E7366CDestructive actions, validation errors

    The typeface is Inter, chosen for optical clarity at small sizes, tabular numerals, and legibility in data-dense interfaces. The type scale has five steps — but 13px is the primary UI size, used for nearly everything: button labels, nav items, input text, table cells, form labels. Reducing the number of distinct sizes in active use increases coherence and prevents the visual noise of a scale used inconsistently.

    Global scales, not component scales

    A single five-step scale applies across spacing, padding, radius, and component heights. Tokens are not component-prefixed: --padding-md is a global contract any component can reference, never --button-padding-md. The radius scale encodes elevation hierarchy — smaller radii for things close to interaction (buttons, inputs), larger radii for containers, largest for the app shell — creating a visual nesting signal that reinforces surface hierarchy.

    RadiusTokenApplication
    4px--radius-xsBadges, tags, small annotations
    8px--radius-smAll interactive components: buttons, inputs, selectors, cells
    12px--radius-mdCards, overlays, dropdowns, modals
    16px--radius-lgPanels, primary content containers
    20px--radius-xlApp shell, outermost container

    Where the principles become tangible

    Foundations are invisible to the people who use the product — what they actually touch are the components. This is where modular, scalable, and systematic stop being abstractions and become buttons, fields, tables, and panels. The library covers the full surface of an application interface: seven button variants, a complete set of form controls, a data table with its full state model, a segmented control, and the layout shell that holds it all. Every one is a pure composition of shared decisions, with no hardcoded color or dimension anywhere — which is exactly what lets a change at the foundation ripple correctly through all of them.

    What makes it a system rather than a collection is that the components share contracts. Sizes come from one global height scale, so a 36px button lines up perfectly beside a 36px input and a 36px selector. Interaction states come from one shared model, so hover, focus, and disabled mean the same thing everywhere. A designer or engineer who learns one component has already learned the rules for all of them.

    Buttons — the most decision-dense component

    7 × 3 × 5 × 4

    The button carries a full matrix: seven variants, three sizes, five states, and four content configurations — every cell defined, none left to improvisation. The variants aren't decorative; each maps to a distinct action classification, so the interface communicates intent through form. A primary action looks primary; a destructive one is unmistakable; a soft, reversible negative reads differently from an irreversible one. That's the systematic principle doing real UX work: the visual language of the button tells the user what kind of decision they're about to make.

    The most consequential decision here was architectural — solid variants composite a translucent overlay on hover rather than swapping to a hardcoded hover color. It means a button's background can change for any reason (a rebrand, a context, a state) and the hover keeps working untouched. That's modularity at the component level: the hover behavior doesn't depend on knowing the color.

    Form controls — one contract, five components

    Shared behavior

    Inputs, selectors, checkboxes, radios, and switches look different but behave identically: a quiet border at rest, a clear strengthening on hover, a purple focus ring that's reserved exclusively for focus across the entire system, and a distinct error treatment that mirrors the focus geometry. Learn one field and you understand all of them — the systematic payoff.

    The disabled state shows the level of care that separates a real system from a styled mockup. The whole control doesn't dim — only the interactive content drops to a low opacity, while the label and helper text stay fully legible, because the user still needs to read why a field is unavailable. A blunt system greys out everything; a considered one dims only what stopped being interactive.

    Data table — the system's stress test

    Most complex

    The table is where a design system either proves itself or falls apart, because it stacks the most states: row hover, selection, selection-while-hovering, expandable sub-rows, sortable headers, inline controls, and status badges. None of it required a single bespoke value. The entire visual hierarchy is expressed through the shared surface and overlay model — hover and selection are overlays, sub-rows recess onto the sunken surface. The table didn't need its own design language; it spoke the system's.

    Row stateBackgroundBorder
    DefaultTransparent1px divider at border-disabled (5%)
    Hover--overlay-light (5%)Unchanged
    Selected--overlay-light (5%)Unchanged
    Selected + hover--overlay-medium (10%)Unchanged
    Sub-row--surface-sunkenUnchanged

    Segmented control — a principled exception

    Sub-tokens

    Most components map cleanly onto the global tokens. The segmented control didn't — its track-and-pill contrast needed values the shared overlays couldn't express. Rather than forcing it or littering the component with one-off hacks, it got its own small, named sub-token set. Knowing when not to reuse a token is as systematic as reusing one: the exception is contained, documented, and obvious, instead of leaking ambiguity into the rest of the system.

    Layout shell — panels, title bars, toolbars

    Surface levels

    The structural components encode hierarchy through three surface levels: the app canvas sits sunken, panels sit on the base surface, and overlays like modals and dropdowns rise above on the raised surface. Depth isn't decoration — it's information about what sits on top of what. The 52px title bar is sized to anchor the top of a panel and hold its ghost icon buttons with even breathing room, and the radius scale reinforces the same nesting: tight radii on the things you click, generous radii on the containers that hold them, the largest on the outermost shell.

    A constraint, not a cleanup pass

    Accessibility wasn't a checklist appended at the end — it was a constraint built into the foundation from the first decision. Every text color was computed against every surface it appears on and tested for WCAG 2.1 compliance, with the documentation rendering the contrast matrix live so ratios update in real time when the mode toggles. Because contrast is systematic, accessibility is too: there's no surface where text quietly fails, because every pairing was decided once, centrally, rather than rediscovered per screen.

    Text-default (90%)

    Passes AA for normal text and AAA for large text on all surfaces, both modes.

    Text-medium (60%)

    Passes AA for large text on all surfaces in both modes.

    Text-light (40%)

    Fails AA for normal text by design — used only for supplementary helper text and placeholders.

    Text-disabled (20%)

    Intentionally non-compliant — reduced contrast is the signal for unavailability.

    Why purple for focus, not green? Green is overloaded — brand color, checked state, success state. Purple is exclusive to interactivity and focus, making the focus ring immediately and unambiguously meaningful. It appears on every interactive component without exception, and is never suppressed.

    The principle, tested against real growth

    Scalability is easy to claim and hard to prove. The proof is in the three moments that break brittle systems — adding a component, changing the brand, supporting a new mode — and what each actually costs here.

    01

    Adding a component

    Any new component inherits the full token set with zero configuration. It uses --text-default, --border-light, --overlay-light — without knowing their values or which mode is rendering. A new token is created only when no existing token describes a genuinely new semantic need, and only then.

    02

    Rebranding

    A brand color change is one value. Because component tokens reference brand tokens and hover/pressed variants are computed from them, the entire button family updates in a single assignment. A full rebrand — new primary, secondary, and palette — takes five values. The cascade does the rest.

    03

    New modes

    A high-contrast mode would override a few semantic tokens — raising text and border opacity — without touching any component. The surface elevation tokens would need explicit per-mode values, as they do for dark and light today: the same accepted, expected exception to the derived-token model.

    What was chosen, what was rejected, and why

    No color-mix, no oklch, no runtime utilities

    color-mix() can compute hover states from base colors elegantly — and it was rejected. Browser support isn't complete across targets, output varies slightly between implementations, and most critically, the computation happens in the browser's style engine where it isn't trivially inspectable. When a hover state looks wrong, the developer needs the computed value. The pseudo-element overlay approach is more verbose but completely debuggable: every value is a token reference, every token has a computed hex, nothing is hidden.

    Global scale tokens, not component tokens

    Component-scoped tokens create parallel scales — --button-padding-md, --input-padding-md, all the same value, all needing updates together. Global tokens eliminate that: the component derives its size from the global scale, and if the scale changes, every component changes. (Layer 2 component tokens are the exception — they're semantic aliases, not size definitions.)

    Perceptual-weight names, not light / medium / dark

    The contrast scale was renamed from light/medium/dark to a perceptual-weight vocabulary. Directional names lie under inversion: --text-dark is white in dark mode and black in light mode — the name contradicts the outcome. A name describing perceptual weight is stable in both modes. It also stopped the contrast scale from sharing vocabulary with the elevation scale, eliminating the ambiguity between, say, surface-dark and text-dark.

    The hardest problems aren't visual

    Making a button look good is fast

    What's hard is making the token model hold up under mode inversion, compositing math, scale extension, accessibility constraints, and team growth. That requires deliberate architectural thinking, not visual polish.

    The right constraint is the whole trick

    The alpha-base pattern works because it encodes a constraint: all alpha-derived tokens share one axis of variation. That constraint is what makes theme switching a one-variable operation. Elevation breaks it legitimately, because the physics of visual depth genuinely differ between modes.

    Naming is not cosmetic

    A token named for a stable property — perceptual weight, not direction or hue — communicates correctly to every reader, including the one who wrote it six months earlier. A design system is done not when it's built, but when it holds up under the questions it was not built to answer.