Skip to main content
Joshua Briley.
Design system / Reference

The Editorial-Technical system.

Cool paper, near-black ink, and a single denim-navy accent. Hanken Grotesk for headings, Geist Mono for labels. Small radii, hairline borders, heavy whitespace. Every page on the site composes from the tokens and components documented here.

Foundations / 01

Colour tokens.

The full palette is eight tokens. There are no bright fills, no gradients. Contrast and rhythm come from ink on paper, with the navy accent reserved for one mark per view. Every value is a CSS custom property in :root.

paper #FAFBFC

--paper

Page background (near-white cool)

raised #FFFFFF

--raised

Cards, inputs

ink #1A1B1E

--ink

Headings, body, dark sections

ink-muted #565760

--ink-muted

Body min on paper (AA)

ink-faint #9A9AA1

--ink-faint

Decorative only, never body text

line #E5E7EB

--line

Hairline borders, dividers

line-strong #D4D7DD

--line-strong

Emphasised hairline

accent #2E5C8A

--accent

One denim-navy accent, used sparingly

Contrast contract

WCAG AA throughout. On paper, body copy never goes lighter than ink-muted; ink-faint is decorative only (rules, dividers, disabled marks). On ink surfaces, text stays at text-paper or text-paper/70 and above.

Typography / 02

Two typefaces, one scale.

Hanken Grotesk carries display and body; Geist Mono carries every label, index, and eyebrow. The Heading primitive splits semantic level from visual size, so the document outline never breaks to satisfy a layout.

Heading · display-xl

Display extra-large

Heading · display-lg

Display large

Heading · display-md

Display medium

Heading · h2

Section heading (h2)

Heading · h3

Subsection heading (h3)

Heading · h4

Minor heading (h4)

Text · body sizes

size xl: lead paragraph, comfortable and unhurried.

size lg: secondary lead, muted ink.

size base: standard body copy at ink-muted.

size sm: captions and supporting notes.

size xs: fine print, still AA on paper.

Geist Mono · labels

Foundations / 01

The .mono-label eyebrow: uppercase, tracked, ink-muted. Numbered to anchor each section.

const tokens = await build();

Inline mono for code, indices, and spec rails.

Joshua Briley. Wordmark · Hanken

Primitives / 03

The smallest reusable pieces.

Each primitive enforces accessibility through its API: semantic defaults, required labels, decoupled visual sizing. They render the same on every page.

Button: variants & sizes

primary and navy are for dark surfaces (shown below); secondary is the outlined action on ink.

Button: on ink surfaces

Reserved for ink sections: paper fill or a hairline outline, never the paper-surface primary.

Link: inline / arrow / nav

Read the approach . Inline links carry a quiet navy underline.

Selected work

Active nav Inactive nav

Badge: mono label chip

Default With icon Accent tick Solid ink Small

A hairline tile in Geist Mono with a small tick: accent on coral/sunset, neutral otherwise.

Logo: lockup / monogram / wordmark

Joshua Briley. Joshua Briley.

A machined monogram plate plus a tight grotesk wordmark. The wordmark carries the accessible name; the monogram is decorative.

Icon: Phosphor set

Decorative (aria-hidden) by default. Pass label to expose a meaningful icon to assistive tech.

Patterns / 04

Compositions used across pages.

A small set of patterns built from the primitives above: a section header, a metrics row, a code window, and an FAQ. Every page draws from this set.

SectionHeader

Pattern

A centred section header.

Eyebrow Badge, balanced display heading, and an optional muted lead: the standard opener for a content section.

Metric: a row of measured outcomes

8

colour tokens drive everything

2

typefaces: Hanken + Geist Mono

100%

components keyboard-operable

6 1

button implementations

Oversized ink figure, sans label, optional before→after. Illustrative values shown. Use only real, defensible numbers in production.

CodeBlock: framed code window

Field.astro ts
// Field.astro: accessibility wired in by construction.
// label[for], input id, aria-describedby (hint + error),
// aria-invalid and aria-required are all derived, never hand-set.
const hintId = hint ? `${id}-hint` : undefined;
const errorId = error ? `${id}-error` : undefined;
const describedBy =
  [hintId, errorId].filter(Boolean).join(' ') || undefined;
// You cannot create a mislabeled input through this API.
A real component API, shown (not asserted). Dependency-free chrome carries the polish.

FAQ: native details/summary, zero JS

Why only one accent colour?
Restraint reads as deliberate. A single navy mark per view keeps attention where it belongs and never competes with content.
How is accessibility handled?
By construction. Primitives bake in semantic structure, required labels, and AA contrast, so misuse is hard to reach for in the first place.
What carries motion?
Two shared easings plus scroll-reveal: transform and opacity only, and it all pauses under prefers-reduced-motion.

Forms / 05

Accessibility wired in by default.

Field auto-generates ids and connects label, hint, error, aria-describedby, aria-invalid, and aria-required. You cannot create a mislabeled input through this API.

Field examples

Only used to reply.

A few sentences is plenty.

ChoiceGroup: fieldset + legend

How healthy is your design system?

FormAlert: status & alert

Thanks. Your message is on its way.

I’ll reply within one business day, usually sooner.

Motion & spacing / 06

Quiet, deliberate motion.

Animation is restrained and reversible. Transform and opacity only; nothing loops loudly, and everything yields to reduced-motion.

Scroll reveal

.scroll-reveal

Blocks fade up 18px on enter over 0.9s. Disabled entirely under prefers-reduced-motion.

Easing: out

cubic-bezier(.22, 1, .36, 1)

--ease-out: entrances, hovers, and arrow nudges.

Easing: in/out

cubic-bezier(.65, 0, .35, 1)

--ease-inout: looping or symmetric motion (e.g. the token-propagation demo).

Section padding scale

Section exposes three vertical rhythms: tight (py-14 / md:py-20), default (py-20 / md:py-28), and loose (py-24 / md:py-36). Container caps line length at narrow / default / wide, and Grid handles 1-4 responsive columns. Heavy whitespace does the spacing work. No decorative fills.