05//beans

Single origin.
Roasted light or dark.

Beans is the design system this site runs on. Two layers: a raw palette of warm neutrals and a single accent underneath, semantic tokens on top. Plus a small set of components, behaviors, and page shells.

Leans toward print. Long lines, lots of whitespace, headings that breathe.

This page is the reference. Every swatch and sample below is the live token, so what you see is what the site is using right now. Mode toggle's in the header for the dark version.

Foundations

Tokens, color, type, scale

02.01

Palette

The raw palette. Every semantic token below points back to a step on one of these ramps. Cream and roast do the heavy lifting. Accent is the only color in the system.

cream
50#faf3e7
100#f6ecdc
200#ece0d1
300#e4d4ba
400#d9c4a9
500#c9b193
600#b89a78
ink
100#e8d8c2
300#b48a64
500#6b4a2b
700#4a2e17
900#2a1a0c
roast
700#3d3023
800#2e241a
850#241d15
900#1c1612
950#15100b
taupe
300#c0a892
400#a88871
500#8a6e58
600#7a6250
accent
50#f7e8d8
100#efd0b1
200#e0a978
300#d28b4f
400#c67a3f
500#a8632b
600#8d5020
700#6e3d18
800#4f2c11
espresso
#38220f
foam
#dfd1b5

02.02

Color (semantic)

Surface, text, border, accent. Each token shows both its light and dark value side by side, so the page makes sense whichever mode you're in.

Surfaces

bgBase--bg-base
light#f6ecdc
dark#1c1612
bgRaised--bg-raised
light#faf3e7
dark#241d15
bgSunken--bg-sunken
light#ece0d1
dark#15100b
bgInverse--bg-inverse
light#1c1612
dark#f6ecdc

Text

textPrimary--text-primary
light#2a1a0c
dark#f6ecdc
textSecondary--text-secondary
light#4a2e17
dark#ece0d1
textMuted--text-muted
light#6b4a2b
dark#a88871
textSubtle--text-subtle
light#7a6250
dark#7a6250

Borders

borderSubtle--border-subtle
lightrgba(42, 26, 12, 0.10)
darkrgba(223, 209, 181, 0.08)
borderDefault--border-default
lightrgba(42, 26, 12, 0.18)
darkrgba(223, 209, 181, 0.16)
borderStrong--border-strong
lightrgba(42, 26, 12, 0.32)
darkrgba(223, 209, 181, 0.32)

Accent

accent--accent
light#a8632b
dark#c67a3f
accentHover--accent-hover
light#8d5020
dark#d28b4f
accentTint--accent-tint
lightrgba(168, 99, 43, 0.10)
darkrgba(198, 122, 63, 0.16)

02.03

Typography

Three families. Fraunces is the editorial voice, used for display. Inter does the long-form work. JetBrains Mono is the system voice for labels, code, and anything that wants to feel like terminal output.

Fraunces

Display · font-display

Mountain coffee, slowly.

Inter

Body sans · font-sans

The quick brown fox jumps over the lazy dog.

JetBrains Mono

Mono · font-mono

const space = { 1: "4px", 10: "128px" }

02.04

Type scale

Reference sizes for the whole site. Hero text uses clamp() in production so it scales with the viewport; what you see here are the desktop targets.

Display XL

display-xl4.5rem · 500 · lh 1.05

Display LG

display-lg3rem · 500 · lh 1.1

Heading 1

heading-12.25rem · 500 · lh 1.15

Heading 2

heading-21.75rem · 500 · lh 1.2

Heading 3

heading-31.375rem · 500 · lh 1.3

Body LG — lede paragraph copy.

body-lg1.125rem · 400 · lh 1.6

Body — long-form prose at the default reading size.

body1rem · 400 · lh 1.65

Body MD — the page-level prose default.

body-md0.9375rem · 400 · lh 1.6

Body SM — secondary copy.

body-sm0.875rem · 400 · lh 1.55

Caption — micro labels and helper text.

caption0.6875rem · 400 · lh 1.4

--space-6: 32px

mono0.8125rem · 400 · lh 1.5

LABEL · ALL CAPS

label0.75rem · 500 · lh 1.4

02.05

Spacing

Ten steps, and never anything off the scale. Page-level rhythm pulls from 9 and 10. Everything inside a component sticks to 1 through 7. Bar widths below are literal.

space-1
4px
space-2
8px
space-3
12px
space-4
16px
space-5
24px
space-6
32px
space-7
48px
space-8
64px
space-9
96px
space-10
128px

02.06

Radii

Six radii. Pill is for things that should be fully round (badges, switches). XL is reserved for hero surfaces. Everything else picks from the middle.

radius-xs4px
radius-sm6px
radius-md8px
radius-lg12px
radius-xl20px
radius-pill9999px

02.07

Elevation

Five depths. Light and dark shadows are tuned separately because dark surfaces need more contrast to read as raised.

shadow-xs
shadow-sm
shadow-md
shadow-lg
shadow-inner

Components

Forms

Each component shown once in its baseline form, with the variants and states it supports next to it. Sizes, disabled, invalid, focus, and the per-component oddities (indeterminate checkboxes, slider bubbles) all in one place.

Button

The primary action. Three variants, three sizes. Ghost is the quietest. Primary is for the one thing the page actually wants you to do.

Variants
Sizes

Label & Field

Two wrappers. Label is the semantic one. Field pairs label, control, and help or error text together. Every form input below uses them.

We'll never share it.
That doesn't look right.

Input

Single-line text input. Three sizes. Invalid and disabled look different from default focus on purpose.

Sizes
States

Textarea

Multi-line input. Same sizes and states as Input. Row count via the rows attribute.

Select

A native select, restyled. Three sizes. The option list is whatever picker the browser hands you, which keeps mobile accessible by default.

Sizes
States

Checkbox

Two states, plus indeterminate. Use indeterminate when a parent represents a mix of checked and unchecked children. Clicking it resolves to true or false.

States

Radio group

Pick one. Vertical by default. Switch to horizontal when the labels are short enough to read as a row.

Vertical
Horizontal

Switch

On/off, no confirmation. Good for settings that take effect immediately. Don't use it for anything destructive.

States

Slider

Range input. Turn on the bubble and it shows the current value above the thumb while you drag.

Default
With bubble

Behaviors

Small interaction patterns

Behaviors are the small directives that add interaction patterns to plain elements: keyboard handling, focus management, the kind of thing that's tedious to rewrite per component. The page itself demos two of them quietly: the mode toggle in the header, and the scroll-spy on the left rail of this page.

Mode toggle

Light/dark switching, persisted to localStorage. The toggle in the header is the real one. Flip it and every specimen on this page swaps with it.

Scroll spy

IntersectionObserver watching for which section is on screen. The left nav on this page is wired to it. Scroll and the active link follows.

Tab roving

Roving tabindex for tablists and toolbars. Tab into the group below, then use the arrow keys: focus moves between items without ever leaving the group. Home and End jump to the ends.

Tooltip

Hover or focus to surface a short label. Viewport-aware: it flips to the other side when it'd clip the edge.

Copy to clipboard

One button, confirms when it lands. Falls back through execCommand if the modern clipboard API isn't available.

--space-6: 32px

Templates

Page shells

Page templates are the shells everything else lives inside. Each one owns its own width, gutters, and section rhythm so pages don't have to reinvent layout. The sketches below are abstract, so read them as rhythm, not content.

<kc-page>

The bare shell. Sets the width and rhythm that everything else extends.

<kc-page-hero>

The top of a page. Tall and single-purpose. Lede, headline, and first action go here.

<kc-page-section>

Repeating content block. Optional title and kicker. There is a rule-line variant for the editorial pages.

<kc-page-band>

A tinted strip across the page. Plain, or a 3-up grid for metrics.

<kc-page-footer-cta>

The closing call-to-action. Prose on the left, arrow links on the right, with a top border to separate it from whatever is above.

<kc-page-marketing>

Marketing variant of Page. Same building blocks, but the column width and section rhythm are tuned for it.

<kc-page-doc>

Documentation shape. Sticky nav on the left, scrolling main on the right, scroll-spy on. This page is using it right now.

Audit

What's stable, what isn't

Where things stand. Stable means the API and visual treatment are settled — go ahead and build against it. WIP means the shape is there but I'm still landing details. Planned means it's on the list but doesn't exist yet.

Tokens — palette, color, type, spacing, radii, motionThe two-tier model is settled. Any new raw colors land on the existing ramps. Semantic names won’t change.
stable
Tokens — elevationFive depths, light and dark tuned separately. No more shadows planned.
stable
Forms components — Input, Textarea, Select, Checkbox, Radio, Switch, SliderPublic API is set. ControlValueAccessor wired. Size, invalid, and disabled behave the same across all of them.
stable
Field + Label wrappersSlot-based. Help and error text positions are fixed.
stable
Behaviors — mode toggle, scroll spy, tab roving, tooltip, copyThe core logic is framework-agnostic. The Angular adapters live in components/.
stable
Page templates — Page, Hero, Section, Band, Footer CTA, Marketing, DocDoc landed with this page. Article is still a seam.
stable
Page templates — ArticleNarrow prose column with a table of contents. Ships when the first article route does.
wip
Type-scale tokensTen steps, from display-xl down to label. Exported by beans and aliased into Tailwind via @theme, so pages use text-display-xl and friends.
stable
IconographyNo first-party icon set yet. Pages borrow glyphs ad-hoc for now. A curated set is on the list.
planned
Section markersThe dividers and band-end glyphs the doc references aren’t a published part of the system yet.
planned