Skip to content

Building a Dark Design System with Tailwind CSS v4

9 min read

Tailwind v4 flips the config paradigm: instead of a tailwind.config.js file, you define your tokens directly in CSS using the @theme directive. It feels more natural and keeps your design system co-located with your styles.

Here's how I built the dark theme for this portfolio.

Token Strategy

I started with four decisions:

  1. One background colour#0b0b0d (ink). Near-black, slightly blue-black.
  2. One accent colour — plum. A purple that reads as confident without being garish.
  3. Three text levels — primary (#f4f4f5), secondary (#a1a1aa), tertiary (#52525b)
  4. Type scale — 11px to 72px in steps that map to readable hierarchies

@theme Config

@import "tailwindcss"; @theme { /* Palette */ --color-ink: #0b0b0d; --color-ink-2: #111114; --color-ink-3: #18181c; --color-plum-300: #c4b5fd; --color-plum-500: #a78bfa; --color-plum-700: #7c3aed; --color-text: #f4f4f5; --color-text-2: #a1a1aa; --color-text-3: #52525b; /* Type scale */ --text-11: 0.6875rem; --text-12: 0.75rem; --text-13: 0.8125rem; --text-14: 0.875rem; --text-15: 0.9375rem; --text-16: 1rem; --text-18: 1.125rem; --text-22: 1.375rem; --text-26: 1.625rem; --text-32: 2rem; --text-48: 3rem; --text-72: 4.5rem; /* Border radius */ --radius-chip: 9999px; --radius-field: 0.375rem; --radius-card: 0.75rem; /* Fonts */ --font-display: 'Fraunces', serif; --font-sans: 'Inter', system-ui, sans-serif; --font-mono: 'JetBrains Mono', monospace; }

Contrast Strategy

Dark themes are deceptively easy to fail WCAG on. Low contrast text is the most common accessibility issue on dark sites.

My rules:

  • text-text on bg-ink: 15:1 contrast — AAA
  • text-text-2 on bg-ink: 6.4:1 — AA
  • text-text-3 on bg-ink: 3.5:1 — AA large text only (used only for labels and metadata)
  • Interactive elements always text-text or text-plum-300

The Plum Accent

Plum was chosen because it:

  1. Reads as technical/engineering (vs. a warm colour that feels marketing-y)
  2. Has enough saturation to create clear visual hierarchy without being garish
  3. Works in gradients (plum-700plum-300) for depth

For focus rings I use outline: 2px solid var(--color-plum-500) — visible on both dark backgrounds and plum elements.

Share X / Twitter LinkedIn
A

Al Amin Ahamed

Senior software engineer & AI practitioner. Building things in Laravel, PHP, and TypeScript.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.