Hallmark's Token System for CSS Custom Properties: Architecture and Implementation

Hallmark drives its entire visual design through a layered CSS custom property architecture defined in site/css/tokens.css, using :root defaults for base tokens and [data-theme] attribute selectors to override colors, typography, and component shapes per theme.

The open-source Hallmark project (Nutlope/hallmark) implements a robust token system for CSS custom properties that enables instant theme switching without JavaScript frameworks. By centralizing all design decisions—from OKLCH color palettes to border radii—into cascading variables, the system allows any component to inherit theme-specific values automatically through standard CSS cascade inheritance.

Root Defaults: The Foundation Layer

The token system establishes comprehensive defaults within the :root selector in site/css/tokens.css (lines 14-100). This foundation provides the design language applied when no specific theme is active, defining every visual aspect through semantic naming conventions.

Color Palette Using OKLCH

Colors use the OKLCH color space for perceptual uniformity, ensuring consistent luminance across different hues. The naming follows functional roles rather than literal values:

  • --color-paper: Background surfaces
  • --color-ink: Primary text and icons
  • --color-accent: Interactive elements and highlights
  • --color-rule: Borders and dividers

Typography Scale and Font Stacks

Typography separates concerns across three font categories:

  • --font-display: Headlines and prominent text
  • --font-body: Paragraphs and long-form content
  • --font-label: UI elements and captions

The type scale spans --text-xs through --text-2xl, with a dedicated --text-display variable for hero headings. Supporting tokens like --tracking-display and --lh-* (line-height) fine-tune readability.

Layout, Spacing, and Animation

Layout tokens constrain visual density:

  • --space-xs through --space-xl: Incremental spacing utilities
  • --page-max: Maximum content width constraints
  • --measure: Optimal line length for readability
  • --section-gap: Vertical rhythm between major sections

Interaction tokens control motion:

  • --dur-fast, --dur-base, --dur-slow: Animation durations
  • --ease-*: Cubic-bezier curves for state transitions

UI Component Primitives

UI-specific tokens ensure consistent component styling:

  • --radius-card, --radius-pill, --radius-input: Border radii for specific elements
  • --rule-card: Border thickness
  • --shadow-card: Elevation shadows
  • --z-*: Z-index stacking contexts

Per-Theme Token Overrides

Individual themes override specific :root values using attribute selectors [data-theme="theme-name"] defined sequentially in tokens.css. Each block selectively redefines only the tokens necessary to achieve its visual identity, leaving unrelated properties unchanged to maintain design system integrity.

Color and Typography Remapping

The midnight theme demonstrates the override pattern (lines 20-46):

[data-theme="midnight"] {
  --color-paper:    oklch(15% 0.022 250);
  --color-ink:      oklch(90% 0.02 270);
  --font-display:   "Geist", "Futura", "Avenir Next", ui-sans-serif, sans-serif;
  --text-display:   clamp(2.75rem, 5.0vw + 1.0rem, 5.25rem);
  --tracking-display: -0.02em;
}

Themes may swap font families entirely—transitioning from serif editorial styles to monospaced technical aesthetics—or adjust typographic scales to accommodate different character widths and densities.

Layout Density Adjustments

Spacing and layout variables like --section-gap and --page-max are occasionally modified per theme to reflect different design philosophies, from airy editorial layouts to compact utility interfaces, without affecting component markup.

Component-Shape Token Overrides

Following the theme color and typography blocks, a secondary override layer adjusts shape tokens (--radius-*, --rule-card, --shadow-card) to control the physical appearance of cards, inputs, and buttons. This separation allows themes to maintain consistent colors while varying their "voice" through geometric styling.

The garden theme applies friendly, rounded aesthetics (lines 447-449):

[data-theme="garden"] {
  --radius-card: 10px;
  --radius-pill: 8px;
  --radius-input: 8px;
}

These overrides ensure that UI primitives inherit the intended visual character without requiring component-specific CSS modifications.

Implementing Themes in Practice

Activating Themes in HTML

Apply a theme by setting the data-theme attribute on the <html> element or any ancestor container:

<!DOCTYPE html>
<html data-theme="midnight" lang="en">
  <head>
    <link rel="stylesheet" href="css/tokens.css">
  </head>
  <body>
    <!-- Content inherits midnight tokens automatically -->
  </body>
</html>

Consuming Tokens in Component CSS

Components reference tokens using standard var() syntax, ensuring automatic theme inheritance regardless of which theme is active:

.card {
  background-color: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  border-radius: var(--radius-card);
  border: var(--rule-card) solid var(--color-rule);
  box-shadow: var(--shadow-card);
  padding: var(--space-md);
}

.pill {
  font-family: var(--font-label);
  border-radius: var(--radius-pill);
}

Programmatic Theme Switching

JavaScript can toggle themes instantly without page reloads by modifying the dataset on document.documentElement:

function setTheme(themeName) {
  document.documentElement.dataset.theme = themeName;
}

// Switch to garden theme - colors, fonts, and radii update immediately
setTheme('garden');

This update propagates instantly because all visual properties reference CSS custom properties rather than hardcoded values, enabling zero-cost runtime theme changes.

Key Source Files

Summary

  • Hallmark's architecture uses CSS custom properties defined in site/css/tokens.css to create a maintainable, themeable design system with single-source truth.
  • Root defaults in :root establish base values for OKLCH colors, typography stacks, spacing scales, and UI metrics that apply globally.
  • Per-theme overrides leverage [data-theme] selectors to remap specific tokens like --color-paper and --font-display without duplicating component code.
  • Component-shape tokens (--radius-card, --shadow-card) are overridden separately per theme to control physical appearance independently of color.
  • Themes activate via HTML data-theme attributes and propagate instantly through var() references, enabling runtime switching with zero repaint costs.

Frequently Asked Questions

How do I add a new theme to Hallmark's token system?

Add a [data-theme="your-name"] selector block to site/css/tokens.css following the existing theme definitions. Copy an existing theme's structure and redefine only the tokens requiring changes—such as --color-paper, --font-display, or --radius-card. The cascade automatically applies your overrides while inheriting undefined tokens from the :root defaults, ensuring consistency with the base system.

Why does Hallmark use OKLCH for color tokens instead of HEX or RGB?

The system uses OKLCH (Oklab Lightness, Chroma, Hue) because it provides perceptual uniformity, meaning color changes appear consistent to human vision regardless of hue. This prevents the luminance shifts common in HSL and ensures that --color-ink maintains readable contrast against --color-paper across different theme palettes, particularly important for accessibility in dark themes like midnight.

Can I use Hallmark's CSS custom properties in a React or Vue application?

Yes. Since the tokens are standard CSS custom properties, they function in any framework outputting HTML and CSS. Import site/css/tokens.css into your global stylesheets, then reference variables like var(--color-accent) in your component styles. For dynamic theme switching, update document.documentElement.dataset.theme via framework state management or the vanilla JavaScript setTheme() pattern demonstrated in site/js/main.js.

What happens if a token is missing in a theme definition?

If a theme override omits a specific token, the browser falls back to the :root default defined in the first block of tokens.css. This selective override approach means themes only declare values that differ from the base system, reducing CSS repetition and ensuring visual consistency for unspecified properties across all themes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →