Skip to content
Building a Dark Design System with Tailwind CSS v4

Building a Dark Design System with Tailwind CSS v4

Al Amin Ahamed

Al Amin Ahamed

Senior Engineer

9 min read
𝕏 in

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

CSS
@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 𝕏 in
Al Amin Ahamed

Al Amin Ahamed

Senior software engineer & AI practitioner. Laravel, PHP, WordPress plugins, WooCommerce extensions.

About me →

One email a month. No noise.

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