Design system
One system, 12 themes
Every color, size and curve on this site comes from one set of tokens, and a theme is only a different set of values for them. This page renders the tokens with the same CSS the site uses, so it is always current. Change the theme from the knob in the header and watch it follow.
Principles
- Color means a byte role. In every theme, one hue is a status byte, one is data, one is meta. Decoration never borrows them.
- Hardware, sparingly. LEDs for what's on, keycaps that travel, a beat ruler at big breaks, a step grid behind the hero. Cues, never costume.
- Quiet until touched. Links and controls rest calm, answer on hover with a fine pointer, and always answer on press.
- Fast, honest motion. Under 300 ms, custom ease-out curves, nothing on keyboard shortcuts, no movement with reduced motion.
Themes
Each theme is a plain data file in src/themes/: colors for light, dark or both, three or four fonts, and a few
type and shape settings. src/themes/registry.ts lists them.
A first visit gets one at random from those with the visitor’s light or dark mode, and keeps it; the first, Field Guide, shows without JavaScript.
Every theme and mode is tested for WCAG AA contrast (tests/themes.test.ts).
-
01 Field Guide
Warm paper and ink, with an ember status byte. A printed reference you can play.
Archivo · Atkinson Hyperlegible Next · Atkinson Hyperlegible Mono
-
02 Rhythm
A drum machine at night: charcoal panel, condensed silkscreen, and red-orange-yellow step lights.
Sofia Sans Condensed · Sofia Sans · JetBrains Mono
-
03 Alloy
Bead-blasted aluminum, black print and one orange key. A pocket synth’s industrial design.
Instrument Sans · Martian Mono
-
04 Score
An engraved edition: ivory paper, serif type, oxblood markings, and staves for section breaks.
Fraunces · Newsreader · IBM Plex Mono
-
05 Phosphor
A sequencer on an amber CRT: all monospace, amber phosphor, a faint glow on what’s lit.
IBM Plex Mono
-
06 Piano Roll
A DAW at 2 a.m.: slate lanes, a bar ruler, lime notes and rounded clips.
DM Sans · DM Mono
-
07 Blueprint
The spec as a technical drawing: drafting grid, condensed engineering type, square corners. Cyanotype in the dark.
IBM Plex Sans · IBM Plex Mono
-
08 Swiss
International style: white, black and one red; a tight grotesk set big, flush left, square.
Inter Tight · Inter · JetBrains Mono
-
09 Riso
A risograph zine: cream stock, blue and fluorescent-pink inks printed a touch off-register.
Bricolage Grotesque · Space Mono
-
10 Analog
A 1970s synth panel: cream enamel, burnt orange and avocado, wide extended legends. Walnut at night.
Jost · Red Hat Mono · Michroma
-
11 After Hours
The club at 3 a.m.: violet-black, magenta and cyan light, a wide rounded display face.
Unbounded · Sora · JetBrains Mono
-
12 Graphite
A modern dev tool: cool neutrals, crisp hairlines, one electric blue. The quiet option.
Geist · Geist Mono
Color
The semantic color tokens, as the current theme sets them. Components only use these names; a theme or a mode swaps the values.
Byte roles
-
90
Status
Status bytes, links, the active section
--accent--accent-soft -
3C
Data
Data bytes, success
--data--data-soft -
FF
Meta
Meta events, SysEx
--meta--meta-soft -
!
Warn
Cautions, unverified
--warn--warn-soft
Surfaces
-
Sunken
Inputs, code, readouts
--surface-sunken -
Page
The paper everything sits on
--bg -
Raised
Cards, panels, tables
--surface -
Floating
Dialogs, selected segments
--surface-raised -
Fill
Hover fills, quiet badges
--surface-2
Ink and lines
-
Ink
Headings and body
--ink -
Ink 2
Secondary text
--ink-2 -
Muted
Meta, hints (4.5:1 on paper)
--muted -
Control
Field borders (3:1)
--control -
Line strong
Raised edges
--line-strong -
Line
Hairlines and dividers
--line -
Focus
Keyboard focus only
--focus -
Sky
Chart series
--sky -
Olive
Chart series
--olive
Type
Each theme picks its own faces: one for display, one for text, a mono for bytes, and sometimes a fourth for the
small uppercase labels. The theme cards above list them. Sizes are in rem so they follow the reader's text size.
Tracking tightens as type grows (--tr-display −0.03em) and opens on uppercase labels
(--tr-label 0.08em); leading tightens from 1.6 on body to 1.0 on display.
-
The field guide
-
Message decoder
-
Running status
-
Pitch bend ±2 semitones
-
A Note On is three bytes.
-
Status bytes have the top bit set; data bytes never do.
-
Open a MIDI file
-
Header and track chunks, delta times, meta events.
-
Paste or type bytes, e.g. B0 07 64
-
COMMERCIAL USE OK
-
C4 C#4 D4
90 3C 64 Bytes are mono, bold, tabular, with a 0 that can't be mistaken for O.
Space
A 4px base. Layouts snap to these steps; one-off values are a bug.
-
--s-0 -
--s-1 -
--s-2 -
--s-3 -
--s-4 -
--s-5 -
--s-6 -
--s-7 -
--s-8 -
--s-9
Radii
Nested corners step down one size per level, so inner and outer curves stay concentric.
-
--r-2xsKeys, tiny marks -
--r-xsByte chips, badges, kbd -
--r-smButtons, inputs, segments -
--r-mdCards, tables, code -
--r-lgPanels, dialogs -
--r-pillChips, search
Elevation
Five levels. Most themes use short shadows lit from above, with a highlight on the top edge, deeper in dark mode. Printed and drawn themes use rules or ink offsets instead.
-
Sunken
A well you type or read into.
--shadow-inset -
Page
Flat paper.
none -
Raised
Resting cards and buttons.
--shadow-1 -
Panel
Tools; cards on hover.
--shadow-2 -
Floating
Search palette, dialogs.
--shadow-3
Motion
Custom curves, because the CSS keywords are too weak, and never ease-in for UI. Press the buttons:
each dot uses its curve at --d-slow, and you can reverse it mid-flight because these are transitions,
not keyframes.
-
Ease out
Anything entering or responding to input.
--ease-outcubic-bezier(0.23, 1, 0.32, 1) -
Ease in-out
Something already on screen moving to a new place.
--ease-in-outcubic-bezier(0.77, 0, 0.175, 1) -
Drawer
Menus and sheets sliding open.
--ease-drawercubic-bezier(0.32, 0.72, 0, 1)
| Token | Value | Use |
|---|---|---|
--d-press | 100ms | Press feedback (scale 0.97) |
--d-exit | 120ms | Exits: faster than entrances |
--d-fast | 140ms | Hover and color changes |
--d-base | 200ms | Popovers, toggles, chevrons |
--d-slow | 280ms | Dialogs, menus, theme change |
Rules of thumb
- Don't animate what people trigger from the keyboard dozens of times (the search palette opens instantly on ⌘K).
- Hover effects only on devices with a fine pointer:
@media (hover: hover) and (pointer: fine). - Things leave the way they came, and faster than they arrived: the same
--ease-outat--d-exit. - With reduced motion, movement goes and color and opacity changes stay.
Focus
One ring for everything: --focus-width (2px, 3px with increased contrast) of --focus,
offset --focus-offset from the edge, following the element's own radius. Text fields swap their
border for the focus color and add a soft halo. Press Tab through the components below.
MIDI marks
A few hardware cues, kept small. Each theme recolors them, and none carries meaning on its own: an LED always sits next to a label or a state that says the same thing.
LED
.led, lit with .on. The MIDI IN light, the current section, the chosen theme. Color --led, bleed --led-blur.
Keycaps
Buttons have a lit top edge and a heavier lip; pressed, they travel down 1px and lose the lip.
Beat ruler
.rule-beats: a timeline's ticks for the big breaks between sections. Themes redraw it: step lights, staves, a heavy rule.
Texture
.textured draws the theme's --texture (a step grid, a piano roll, staves, dots) behind the hero, faded out from a focal point.
Icons
One set, drawn for this site in src/lib/icons.mjs: a 24px grid with a 2px safe area, one 2px
stroke with round caps and joins, 2px corner radii, and solid fills only for the media glyphs and the star.
Use <Icon name="…" /> in markup or iconSvg() in scripts; the favicons and
social cards are generated from the same brand mark.
-
search -
menu -
close -
sun -
moon -
code -
upload -
play -
pause -
stop -
star -
chevron-down -
chevron-right -
arrow-right -
arrow-up-right -
check -
copy -
plug -
send -
knob -
shuffle -
file -
din
- 2px safe area: nothing crosses it
- Keylines: circle r 9, square 16, upright 14 × 18
- Tall shapes (file, pause) use the upright keyline
- Stroke 2 at 24px; scaled with the icon, so it is 1.33px at 16px
Components
Buttons
Heights --h-sm 32, --h-md 40, --h-lg 48; 44px minimum on touch screens. One primary per view.
Chips and segmented controls
Chips filter (any number on). A segmented control picks one of a few.
Fields
Inputs are sunken wells, so they never read as cards.
Marks
Callouts
Good to know
A Note On with velocity 0 is a Note Off.
Watch out
Free to use is not free to share.
Note
Running status lets a sender skip a status byte that repeats.
Cards and tables
- Docs Messages Status and data bytes, channel voice and mode messages, running status.
- Tool Message decoder Paste raw bytes and see each message decoded.
| Status | Message | Data |
|---|---|---|
| 8n | Note Off | note, velocity |
| 9n | Note On | note, velocity |
| Bn | Control Change | controller, value |