Design system
Foundations / Shared across every project
One flat, terminal‑leaning language across data visualizations, tools and sketches: OKLCH tokens, mono numerals, hairline panels and a 44px rhythm. Each project may add its own palette on top of these foundations rather than inventing a new look.
Semantic color
Every value is OKLCH and defined twice, once for light and once for dark. Components reference the token, never the value.
- background
Page canvas
- foreground
Primary text
- card
Panels, tiles, tables
- surface
Inset wells, chart backdrops
- muted
Quiet fills
- muted-foreground
Secondary text, labels
- primary
Active state, focus accent
- ring
Keyboard focus outline
- border
Hairlines, dividers
- destructive
Errors only
Typography
Inter is the only typeface across the portfolio. Numbers, timestamps and counts use its tabular figures through the .numeral utility, so digits stay aligned in charts, tables and readouts without switching fonts. Labels and eyebrows use small, muted all‑caps text with wide tracking. Headings step down by one size at a time: section titles lead, and a sub‑section title inside a card or panel sits a step smaller so it never reads at the size of the frame that holds it. A card or list‑item title at the fourth level steps down once more to the base size, so it still reads as a heading without competing with the sub‑section title above it.
- Page title (h1)Turning static charts into tools people can use
- Page subtitleTurning static charts into tools people can use
- Section title (h2)Turning static charts into tools people can use
- Sub-section title (h3)Turning static charts into tools people can use
- Minor heading (h4)Turning static charts into tools people can use
- BodyTurning static charts into tools people can use
- Eyebrow / labelTurning static charts into tools people can use
Layout and spacing
- px-5 / md:px-10 / lg:px-11Page gutters
- pt-11 (44px)Section rhythm between header, content and footer
- p-6Card padding
- gap-5Grid gap between cards
- mx-auto max-w-[1100px] / [1400px]Text-led pages / chart-led pages
Elevation
Elevation is reserved for interactive elements. Static panels stay flat; buttons, cards and controls cast a shadow so the action is visible before the cursor arrives.
- shadow-action
Resting shadow for buttons, links, toggles and select triggers.
- shadow-lift
Hover/focus lift for cards and controls that invite interaction.
Components
A lightweight catalog of the interactive pieces already in use. Each example is the real component rendered live, so the page stays current as the system evolves.
Actions
Buttons and linked cards share the same elevation language. A lone action in a container stretches to the container's full width on phones, then returns to its natural size from tablet widths up. Rows that hold several actions side by side keep their natural width and wrap instead.
Button
Every action in the portfolio renders the shared Button. Button labels use sentence case by default. Run actions are uppercase with wide tracking since their labels stay short. Default uses the cyan primary fill so actions read first. Outline is the secondary. Ghost is for in-chart and inline controls where the background should stay flat. Ghost 2 adds a leading icon for web and software UI actions such as populate, apply template, or load example. Shown as a side by side catalog of variants, so these keep their natural width and wrap on phones.
Run action buttons
The primary run action (Spin, Generate). Cyan fill keeps the call-to-action separate from nearby choice buttons. Use the disabled state while a process is running. The two shown are the same action in two states, so on phones each one renders full width the way it does in a real container, then sits at natural width from tablet widths up.
Run action with success state
A lone small run action that swaps its label and adds a checkmark icon to confirm the action worked. Stretched here because it is the only action in its container.
Secondary action buttons
Outline style for exports, downloads and other secondary steps where the system provides its own visible feedback. Same radius and elevation as the primary, but it sits behind the main action. Also one action in two states, so it follows the same full width on phones treatment.
Linked card treatment
Linked cards carry a resting shadow and lift on hover. Shown here without an actual link so the treatment can be reviewed in place. Static 'coming soon' cards stay flat.
02Static card
Toggles
Binary, single‑choice and multi‑choice states.
Theme toggle
The dark/light switch pinned to every page header. Preference persists locally in the browser.
Switch
Used for binary options such as including outlier seasons.
Toggle group
Single or multiple selection. Chart modes, league filters and the schools filter all use this. Standalone toggles such as the schools filter's Only buttons share the same selected treatment.
Selection
Commands, short options and data-item choices keep distinct treatments.
Segmented control
One bordered field split into equal parts for 2 to 3 short options. The active part fills. Used for the movie tool's prompt style.
Selection tile
One-of-many choices, from data items in visualizations to picks like templates. A neutral background, stronger hover border, foreground border and muted fill when selected, and a selection dot that can carry the item's own color, outlined until picked. Supporting lines sit under the label, clear of the dot: a primary detail such as a key value, then a muted secondary detail. Used across visualizations.
Step rail
Numbered steps on one hairline rule. The active step gets a 2px bar and is marked as the current step. Used for the roadmap topics.
Selection dot
Outlined when unselected, filled only when selected. A color tints the dot so it can carry the item's own identity, outlined until picked. Identity dots in legends stay filled.
Unselected Selected Colored, unselected Colored, selected
Inputs
Custom dropdowns and scrubbable sliders replace native controls for a consistent look.
Select
A Radix‑based custom dropdown used wherever a native select appeared before.
Range slider
Two‑thumb range for seasons or years. Values update as the thumbs move.
2005 – 2015Day slider
Scrubbable day‑of‑week control with a play/pause sweep.
AllSunMonTueWedThuFriSat
Navigation
Paged sets pick their pagination style by scenario, so controls stay proportional to the set.
Compact pagination
For small paged sets rendered inside a panel or chart, such as slides of mapped locations or pages of planets inside a hexbin cell. Stepping forward and back is all that is needed, so the control is just arrows around a 1 / 10 count. Arrows disable at the ends, and the control is announced as a Pagination region. If a set ever grows long enough that stepping is tedious, switch to numbered pagination instead.
Page 1 of 10. Only the count changes between scenarios.
Numbered pagination
For long tables where jumping to a page matters, such as the traffic records table. Previous and next, the first and last page numbers, and a window of page links around the current page with ellipses in the gaps. In narrow spaces the window drops to first, current and last. The current page carries aria-current and is not a link, so it never sits in the tab order.
Page 5 of 226. The window follows the current page, and the first and last pages stay reachable. Phones drop the neighbor pages so it fits.
Linear pagination
For guided, ordered flows where each step has a name, such as the roadmap topics. Labeled Previous and Next buttons carry the direction in words because the reader is moving through content, not sampling a dataset.
Topic 1 of 5. In narrow spaces Previous shortens to Prev, and screen readers still hear the full label.
Navigation arrow
Arrows mark links that go to another page: back links, section links and calls to action. They size to the surrounding text and stay hidden from screen readers, since the link text carries the meaning. Stepping through a set uses chevrons instead.
Back linkCall to actionExternal link
Links that leave the site open in a new tab, end with an up-right arrow, and tell screen readers they open a new tab, so every visitor gets the same warning. The arrow sizes to the surrounding text, inside sentences and lists alike.
Supporting copy with an external link (opens in a new tab) inside the sentence.
Loading
Three treatments, one rule: skeletons only when the final layout is known, a spinner with a short status line while a tool works, and a calm waiting state when nothing has been requested yet.
Route load skeleton
Pulse bars that preview the final layout while a page loads. Used on route loads only, never for generated results whose shape is unknown. Screen readers hear a short loading announcement.
Tool action spinner
One spinner beside a short line that names the work in progress. Used by the tools while a request runs. The text says what is happening, not just that something is.
Working on your request...Waiting state
Shown before the first request, when there is nothing to load yet. A quiet line of text invites the first action instead of faking activity.
Choose an option above to see results here.
Rules of the system
Overarching principles that apply to data visualizations, tools and sketches alike. They keep the portfolio coherent without forcing every project into the same mold.
- Every color is an OKLCH token. Components never hardcode hex or pure white, and every new palette is defined for both light and dark themes.
- Prose reads in the body typeface. Numbers, timestamps, status counts and code‑like labels use the mono numeral style so digits stay aligned as values change.
- Text meets 4.5:1 and graphical marks meet 3:1 against their background. Anything hoverable is also focusable, with a visible focus ring and a keyboard path.
- Controls and text links share a 2px cyan keyboard-focus outline with a 2px offset, darker in light mode and brighter in dark mode. Compact popup rows keep the outline inset to avoid clipping. SVG visualization marks retain their specialized indicators, and high-contrast mode uses the system highlight color.
- Elevation is reserved for interactive elements. Static panels, tables and chart backdrops stay flat.
- Corners stay sharp on static layout. Panels, tables, chart backdrops and skeleton loaders are square. The slight radius is reserved for actionable items such as buttons, inputs, selects and clickable cards.
- Primary actions read first through the accent fill. Secondary actions sit behind it with an outline or ghost treatment. Disabled states are honest and never hidden.
- Color carries meaning, but it is never the only signal. Status, category and role hues always sit next to text labels so the association is clear without relying on color alone.
- Motion is purposeful. Transitions guide attention or confirm a state change. They never decorate a static surface.
- Tool actions wait with one spinner beside a short text line that says what is happening. Skeletons stay on route loads where the final layout is known, never for generated results whose shape is unknown.
- Commands use buttons. Short option sets use segmented controls, steps use a step rail, and data-item choices use selection tiles with a dot and neutral selected fill.
- Build from the shared kit before inventing a new pattern. Each project extends the system rather than starting from scratch.