Design system

This is the living design vocabulary for clmartin.dev. Every component on the site is rendered here from the same CSS it uses in production, so if something's broken, it shows up here first. I built this page as much for myself as for you. It's how I keep the rest of the site honest.

Colour tokens

5 colours, deliberately few

One accent, one text, one muted text, one border, one background. Anything more and I start making excuses.

--my-black #1a1919 Page background
--my-grey #333232 Borders, dividers, subtle surfaces
--my-white #c9c7c2 Primary text
--my-off-white #b3b2ae Muted / secondary text
--dark-pumpkin #ab4e11 Accents, hover states, link underlines, CTAs

Typography

Trebuchet MS stack

Body is Trebuchet MS with a system fallback stack. Code is JetBrains Mono (loaded locally where it appears). Line-height 1.5, base 1em. Nothing exotic. The goal is legibility, not flair.

h1

Shadows stretched long across the road.

h2

Shadows stretched long across the road.

h3

Shadows stretched long across the road.

h4

Shadows stretched long across the road.

body

Shadows stretched long across the road, chasing the last light of day as the hills folded into dusk.

inline code

print("Hello youuuu!")

Links

.underline-link

Solid 1.5px pumpkin underline, 4px offset. Hover thickens to 2.5px and the text lightens. Used on every inline link in prose. I tried a wavy underline early on. It looked cute for ten seconds and then started feeling like a spell-check squiggle.

Here's an inline sentence with an underline-link example so you can hover it and see the thickness transition.

Section heading

.section-heading

Every section on the site opens with a flex row: the heading on the left, optional meta on the right, a grey 1px underline, and a short pumpkin accent bar anchored to the h2. It's the one piece of ornament I allow myself.

Cards

.card / .card-lift

One base: 1px grey border, 0.5rem radius, pumpkin border on hover. Optional .card-lift adds a 2px translateY on hover, gated behind prefers-reduced-motion. Everything else (padding, gap, internal layout) is per-page.

.card

Hover me. The border lights up.

.card .card-lift

Hover me. Same border, plus a subtle lift.

Tags

.tag

Small bordered pills. Used for stack labels on project cards and anywhere a short one-word metadata chip is useful.

Swift 6 SwiftUI SvelteKit TypeScript Supabase Docker

Call-to-action buttons

.cta-primary / .cta-secondary

Two flavours: a filled pumpkin primary and an outlined secondary. The primary inverts on hover (fill drops, text becomes pumpkin) rather than darkening. Keeps things calm.

Lists

global default: unstyled

Default ul/ol have no bullets. Most lists on the site are semantic groupings that don't need them. Blog posts opt back into bullets via the .blog-post scope.

default ul
  • No bullets
  • No padding
  • Semantic grouping only
.blog-post ul
  • Bullets back on
  • Indented for prose
  • Feels like an article

Why these choices

  • One accent colour. Dark pumpkin (#ab4e11) is the only non-neutral on the site. Every hover, underline, and CTA pulls from it. If I ever find myself reaching for a second accent, that's a sign I'm trying to paper over a structural problem.
  • Dark by default, no toggle. I work in the dark. The site matches. A theme toggle is a lot of maintenance for a 2-minute visitor, and the one-colour palette doesn't translate cleanly to light mode anyway.
  • Solid underlines, not wavy. Wavy underlines read as errors (spell-check, linter warnings) and get noisy over long paragraphs. Solid + offset is calmer.
  • Cards over shadows. Every card is just a border with a hover colour change. No box-shadows, no gradients. Borders scale cleanly on any background and don't introduce a light source the rest of the page has to honour.
  • Reduced motion honoured. The card hover lift and external-link glyph nudge are both gated behind prefers-reduced-motion: no-preference. Motion should be a bonus, not a requirement.
  • One font family. Trebuchet MS for everything except monospace code blocks. No custom font loading, no FOUT, no layout shift.