← All work
04BONES v2.0.0 · live from source

BONES Design System

BONES is the design system behind ianbone.com, and it is also a case study about itself. The tokens that style every page are the same tokens documented on this page, compiled through a build step and rendered live in front of you. Now that AI reads code about as fluently as people do, a system that stays legible right down in its source is a system that keeps scaling, and this page is the proof of that.

01/The pipeline

One TypeScript file holds every token. A build step compiles that file into a Tailwind theme, the whole site is styled from that theme, and this page reads the same file again to document itself. That’s four stages running off a single source of truth.

01
tokens.ts
typed source of truth
02
build-tokens
compiles → CSS
03
tokens.css
styles the site
04
this page
renders the source

02/Color · 3 scales · 15 semantic · 2 themes

There are two tiers to it. Primitives are the raw tonal scales, so three hues running 50 to 900 with no particular job attached to them yet. The semantic layer is where each token gets a job (text, icon, background, border), and every one of those points back at a primitive and resolves to a different step depending on the theme. Components only ever touch the semantic layer, so flipping the theme repaints the whole system without me editing a single component.

Primitives · tonal scales

paper
50
#fbfaf5
100
#f6f4ee
200
#ece8dd
300
#ddd8cb
400
#c8c1af
500
#aba38f
600
#857e6b
700
#625c4d
800
#423d32
900
#26231c
pine
50
#e4eae7
100
#b7c3bd
200
#94a69f
300
#617168
400
#45564f
500
#33514a
600
#1e3d36
700
#142a25
800
#12261f
900
#0c1a15
lime
50
#f7ffec
100
#edffd0
200
#ddfeac
300
#d0fe91
400
#b7ec74
500
#9ccf57
600
#7fac40
700
#61822f
800
#445a20
900
#283611

Aliases · legacy pigment names → scale

paperpaper.100linepaper.300inkpine.800ink-softpine.400pinepine.600pine-deeppine.700limelime.300lime-dimlime.400

Semantic · text

TokenLightDarkRole
fg
#12261f· pine.800
#f6f4ee· paper.100
Primary text.
fg-soft
#45564f· pine.400
#b7c3bd· pine.100
Secondary text, captions, meta.
fg-faint
#617168· pine.300
#94a69f· pine.200
De-emphasised labels and hints.
fg-accent
#1e3d36· pine.600
#d0fe91· lime.300
Brand-green emphasis — two-tone headlines, hover.
on-accent
#12261f· pine.800
#12261f· pine.800
Text that sits on an accent fill.

Semantic · icon

TokenLightDarkRole
icon
#12261f· pine.800
#f6f4ee· paper.100
Primary icons.
icon-soft
#45564f· pine.400
#b7c3bd· pine.100
Secondary icons.
icon-faint
#617168· pine.300
#94a69f· pine.200
De-emphasised icons.
icon-accent
#1e3d36· pine.600
#d0fe91· lime.300
Accent icons — markers, arrows.

Semantic · background

TokenLightDarkRole
surface
#f6f4ee· paper.100
#142a25· pine.700
Page background.
raised
#fbfaf5· paper.50
#1e3d36· pine.600
Cards, insets, anything lifted off the page.
accent
#d0fe91· lime.300
#d0fe91· lime.300
The single accent — links, markers, primary actions.
accent-strong
#b7ec74· lime.400
#b7ec74· lime.400
Accent hover / pressed state.

Semantic · border

TokenLightDarkRole
edge
#ddd8cb· paper.300
#33514a· pine.500
Hairline rules and component borders.
edge-strong
#c8c1af· paper.400
#45564f· pine.400
Emphasised borders — focus, active edges.

03/Type · 3 roles · 7 steps

Type is grouped by role. Each role commits to one family, so monospace (JetBrains Mono) carries all the structure and sans (Archivo) carries the reading, and its steps hold size, weight, and line-height as tokens. Weight is kept even across the system for now, though the axis is a first-class token on every step, so it’s ready to tune whenever I want to. Every specimen renders at its literal token values.

displaymonoBig mono headlines. One hero per page.
display
fluid · 400 · 1.05 · -0.03em

Good bones

title
fluid · 400 · 1.1 · -0.02em

Good bones

headingmonoMono structure below the headline — sub-heads, labels, numbers.
heading
1.25rem · 400 · 1.35 · -0.01em

Good bones

meta
0.8125rem · 400 · 1.5 · 0.02em

Good bones

annotation
0.6875rem · 400 · 1.45 · 0.08em

Good bones

bodysansSans reading text — the only place long-form prose lives.
body-lg
1.125rem · 400 · 1.65

Good bones

body
1rem · 400 · 1.65

Good bones

04/Sizing · 0.25rem base unit

One ruler sits underneath every measurement on the site. The size scale is the primitive, so 4px steps with nothing arbitrary thrown in. Spacing and radius are semantic references into that scale, each one named for the job it actually does.

Primitive · size scale

00
10.25rem
20.5rem
30.75rem
41rem
51.25rem
61.5rem
82rem
102.5rem
123rem
164rem
205rem
246rem
328rem
0.50.125rem
1.50.375rem
2.50.625rem

Semantic · spacing

hairlinesize 10.25rem
tightsize 20.5rem
cozysize 41rem
defaultsize 61.5rem
comfortablesize 102.5rem
loosesize 164rem
sectionsize 246rem
pagesize 328rem

Semantic · radius

none0
sm0.125rem

05/Grid

Columns
12
Gutter
1.5rem
Max width
76rem
Margin
clamp(1rem, 4vw, 3rem)
Baseline
0.25rem

Press G to overlay it anywhere

12 columns · 1.5rem gutter

06/Primitives

Pages are built up from a small set of primitives rather than raw utilities, a bit like Lego blocks I can snap together. That’s what keeps the site consistent and the code easy to read, and it holds up whether it’s a person or a model doing the reading.

01/Label

Indexed mono eyebrow — opens every section

Title — mono section heading

Hairline


MetaRow
key / value in mono

07/Components · 4

A design system is more than just its tokens. These are the composable parts built on top of them, and each one ships with a machine-readable spec and gets rendered here in both themes straight from that same source. What you see in the preview is the actual component, and the table and the JSON next to it are its contract, so none of them can drift away from what the site really ships.

Button

Action

The system's one interactive primitive. Square, monospace, uppercase. Renders as an anchor when `href` is set, a native button otherwise.

light
dark
PropTypeDefaultDescription
variant'solid' | 'accent' | 'outline' | 'ghost''solid'Visual weight. solid inverts to the foreground; accent fills with lime; outline is a hairline; ghost is text-only until hover.
size'sm' | 'md''md'Padding and type scale.
hrefstringWhen provided, renders an <a> instead of a <button>.
disabledbooleanfalseDisables the native button.
children *ReactNodeButton label.
{ } Machine-readable spec
{
  "name": "Button",
  "category": "Action",
  "description": "The system's one interactive primitive. Square, monospace, uppercase. Renders as an anchor when `href` is set, a native button otherwise.",
  "keywords": [
    "button",
    "cta",
    "action",
    "link",
    "submit"
  ],
  "props": [
    {
      "name": "variant",
      "type": "'solid' | 'accent' | 'outline' | 'ghost'",
      "description": "Visual weight. solid inverts to the foreground; accent fills with lime; outline is a hairline; ghost is text-only until hover.",
      "default": "'solid'"
    },
    {
      "name": "size",
      "type": "'sm' | 'md'",
      "description": "Padding and type scale.",
      "default": "'md'"
    },
    {
      "name": "href",
      "type": "string",
      "description": "When provided, renders an <a> instead of a <button>."
    },
    {
      "name": "disabled",
      "type": "boolean",
      "description": "Disables the native button.",
      "default": "false"
    },
    {
      "name": "children",
      "type": "ReactNode",
      "description": "Button label.",
      "required": true
    }
  ],
  "examples": [
    {
      "label": "Primary",
      "code": "<Button>See the work</Button>"
    },
    {
      "label": "Accent",
      "code": "<Button variant=\"accent\">Get in touch</Button>"
    },
    {
      "label": "Outline, small",
      "code": "<Button variant=\"outline\" size=\"sm\">Résumé ↓</Button>"
    },
    {
      "label": "As link",
      "code": "<Button variant=\"ghost\" href=\"/work/\">All work →</Button>"
    }
  ],
  "accessibility": [
    "Renders a native <button> or <a> — full keyboard and screen-reader support for free.",
    "Visible focus ring uses the accent token with a surface-colored offset.",
    "Disabled state sets the native disabled attribute, not just styling."
  ]
}

Tag

Display

Small monospace chip for disciplines, statuses, and categories. The only component that uses the 2px corner radius; everything else is square.

light
Design systemsProduct designShipping
dark
Design systemsProduct designShipping
PropTypeDefaultDescription
children *ReactNodeTag label.
dotbooleanfalseDraw a small accent dot before the label.
{ } Machine-readable spec
{
  "name": "Tag",
  "category": "Display",
  "description": "Small monospace chip for disciplines, statuses, and categories. The only component that uses the 2px corner radius; everything else is square.",
  "keywords": [
    "tag",
    "chip",
    "badge",
    "label",
    "pill"
  ],
  "props": [
    {
      "name": "children",
      "type": "ReactNode",
      "description": "Tag label.",
      "required": true
    },
    {
      "name": "dot",
      "type": "boolean",
      "description": "Draw a small accent dot before the label.",
      "default": "false"
    }
  ],
  "examples": [
    {
      "label": "Plain",
      "code": "<Tag>Design systems</Tag>"
    },
    {
      "label": "With status dot",
      "code": "<Tag dot>Shipping</Tag>"
    }
  ],
  "accessibility": [
    "Non-interactive by default — purely presentational, so it carries no ARIA role.",
    "The accent dot is aria-hidden; meaning lives in the text label."
  ]
}

Card

Container

A lifted surface bounded by a hairline — the system's core container. Optional indexed eyebrow, label, and title; becomes a link when `href` is set.

light

01/Case study

Unmarked Apparel

Brand, storefront, and the admin platform behind it — designed and shipped solo.
dark

01/Case study

Unmarked Apparel

Brand, storefront, and the admin platform behind it — designed and shipped solo.
PropTypeDefaultDescription
indexstringTwo-digit eyebrow, e.g. "01", rendered in the accent color.
labelstringUppercase mono label above the title.
titleReactNodeMono heading.
childrenReactNodeCard body.
hrefstringTurns the whole card into a link with a hover border.
{ } Machine-readable spec
{
  "name": "Card",
  "category": "Container",
  "description": "A lifted surface bounded by a hairline — the system's core container. Optional indexed eyebrow, label, and title; becomes a link when `href` is set.",
  "keywords": [
    "card",
    "surface",
    "panel",
    "container",
    "tile"
  ],
  "props": [
    {
      "name": "index",
      "type": "string",
      "description": "Two-digit eyebrow, e.g. \"01\", rendered in the accent color."
    },
    {
      "name": "label",
      "type": "string",
      "description": "Uppercase mono label above the title."
    },
    {
      "name": "title",
      "type": "ReactNode",
      "description": "Mono heading."
    },
    {
      "name": "children",
      "type": "ReactNode",
      "description": "Card body."
    },
    {
      "name": "href",
      "type": "string",
      "description": "Turns the whole card into a link with a hover border."
    }
  ],
  "examples": [
    {
      "label": "Titled",
      "code": "<Card index=\"01\" label=\"Case study\" title=\"Unmarked Apparel\">Brand, storefront, and admin — solo.</Card>"
    },
    {
      "label": "As link",
      "code": "<Card href=\"/work/spabreaks/\" title=\"Spabreaks.com\" />"
    }
  ],
  "accessibility": [
    "When `href` is set the entire card is a single anchor — one focus stop, one predictable target.",
    "Hover and focus both shift the border to the foreground token for a clear affordance."
  ]
}

Field

Data entry

Labeled text input. Mono label above a square field, accent focus ring, optional hint. Generates and links an id from the label when none is given.

light

I reply within a day.

dark

I reply within a day.

PropTypeDefaultDescription
label *stringMono label, also the accessible name.
idstringField id; defaults to a slug of the label.
hintstringHelper or error text below the field.
placeholderstringNative placeholder, shown in the faint token.
{ } Machine-readable spec
{
  "name": "Field",
  "category": "Data entry",
  "description": "Labeled text input. Mono label above a square field, accent focus ring, optional hint. Generates and links an id from the label when none is given.",
  "keywords": [
    "field",
    "input",
    "text",
    "form",
    "label"
  ],
  "props": [
    {
      "name": "label",
      "type": "string",
      "description": "Mono label, also the accessible name.",
      "required": true
    },
    {
      "name": "id",
      "type": "string",
      "description": "Field id; defaults to a slug of the label."
    },
    {
      "name": "hint",
      "type": "string",
      "description": "Helper or error text below the field."
    },
    {
      "name": "placeholder",
      "type": "string",
      "description": "Native placeholder, shown in the faint token."
    }
  ],
  "examples": [
    {
      "label": "Basic",
      "code": "<Field label=\"Email\" placeholder=\"you@studio.com\" />"
    },
    {
      "label": "With hint",
      "code": "<Field label=\"Project\" hint=\"One line is plenty.\" />"
    }
  ],
  "accessibility": [
    "The <label> is linked to the input via htmlFor/id — clicking the label focuses the field.",
    "An id is derived from the label when omitted, so the association never silently breaks.",
    "Focus shows both a border shift and an accent ring."
  ]
}

Role
Design & Engineering

Year
2026

Disciplines
Design tokensDocumentationBuild toolingMeta

/Contact

Let’s build something with good bones.

bones.ianc@gmail.com

© 2026 Ian Bone

Built on BONES, this site’s own design system

Press G for the grid