Skip to content

SpaceUI: Running an Open-Source Design System Like a Product

Context
Open source
Role
Designer, engineer, and maintainer
Built with
React, TypeScript, CSS, Vitest, Ladle
components
32
runtime dependencies
0
test coverage
93%
npm downloads / month
1,000+

The problem

TerraForge, my planet-building sandbox, has a very specific look: dark space, glowing glass, light catching the edge of things the way it catches a planet's atmosphere. Every button, chart, card, pop-up, and form control in the app was built to feel like that. Then I started building more space and science apps and hit the obvious question: do I copy all of that over, or turn it into something every app can share?

Off-the-shelf component libraries weren't the answer. They're built to look neutral, and neutral fights a visual language like this one at every turn. You end up overriding more than you use. So I pulled TerraForge's UI out into its own package, SpaceUI, and open-sourced it.

My role

All of it. I designed the visual language and engineered the library, the token system, the docs, the tests, and the packaging, and I maintain it. It was designed in code, not in Figma, and every component ran in TerraForge's production UI before it became a package. That meant the components showed up with their edge cases already found instead of imagined.

Key decisions

Only extract what a second product needs. That was my rule for drawing the line between the system and the app. If another app would want it, it went into SpaceUI. If it only made sense inside TerraForge, it stayed there.

Name tokens for the job, not the color. The system started with fifteen gray shades borrowed from Radix, and it was never obvious which one belonged where. I consolidated them into seven gray roles named by what they do: text, dimmed text, muted, border, surface, panel, and track. Change one, and every component playing that role moves with it. The rest of the token system follows the same idea across three tiers: palette roles named by purpose, surface color channels stored as plain RGB so each glass layer can pick its own transparency, and per-component properties. Some of those even carry layout. The carousel's visible-slide count is a CSS custom property that responds to media queries with no JavaScript at all. Because it's all CSS variables, any app, or any single panel inside one, can be rethemed in plain CSS with no fork and no build step.

Get to zero dependencies. SpaceUI used to lean on Radix, and I removed it for a few reasons at once. I wanted a package anyone could drop in without pulling in anything else. Radix added weight my apps didn't need. Its colors and primitives kept pushing against the lit-glass look. And the browser had caught up: native top-layer dialogs now handle things like focus trapping that used to need a library. Today a test fails the build if a Radix dependency ever comes back.

Treat the carousel like the hard problem it is. The carousel was the toughest component to get right: where it stops, how drag feels, how many slides show at each screen size. Most of my tests run in a fast simulated DOM, but that environment reports every element's size as zero, so it can't honestly test anything that measures. I split the test suite in two. Behavior and markup tests run in the simulated DOM, and anything that depends on layout, like carousel stopping points, drag, popover placement, and chart geometry, runs in real Chromium.

Enforce the system with tests, not willpower. Beyond behavior, I wrote guard suites that check the rules themselves. They fail on raw pixel spacing, off-scale control heights, or pixel font sizes, and they keep the Radix ban in place. Bundle size is measured on every CI run, and the coverage and size badges in the README come from a real build, so they can't drift out of date.

Build accessibility in from the start. Every component ships with keyboard navigation covered by tests, reduced-motion support, and native dialogs for focus trapping, with 290+ ARIA attributes across 20 distinct roles.

What happened

  • 32 components and 20 icons, React 19 and TypeScript, zero runtime dependencies, about 22 kB gzipped.
  • 16 releases on npm and 1,000+ downloads a month.
  • About 297 tests with 93% coverage.
  • It runs TerraForge in production, plus Exoplanet Explorer, an Android app I built as an extension of TerraForge.

It's also the clearest example I have of something I believe about design systems: the design language and the component should be the same thing. There's no "plain mode" in SpaceUI. A button is a lit-glass capsule. A slider is a glass tube with a moon in it. That's the point.

What I'd do next

Four things. Add light mode and themeable palettes so SpaceUI works beyond dark-only interfaces. Move the rest of my app suite onto it. Structure the docs and rules so AI coding agents can build with the system correctly, not just people. And generate a Figma kit from the tokens, so designers get the same source of truth the code already has.

Further reading