npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pithyjs/design-system

v0.1.0-beta.1

Published

Comprehensive design system for PithyJS with runtime control, theming, and typography management.

Readme

@pithyjs/design-system

Comprehensive design system for PithyJS with runtime control, theming, and typography management.

Overview

The PithyJS Design System provides modular, accessible styling solutions for building consistent user interfaces. It includes two main modules:

🎨 Colors

Complete color theming system with one-seed generation and WCAG AA compliance.

  • One-seed generation - Full theme from a single color
  • 74+ semantic tokens - Brand, surfaces, text, status, borders, on-colors
  • 6 categorical tokens - categorical-1 … categorical-6, for encoding series identity in charts and nothing else
  • State variants - hover, active, selected, disabled
  • Runtime updates - Change colors dynamically
  • Light/dark modes - Complete dual-mode support

The categorical slots are seed-derived like everything else, but along a different axis: the six hue families rotate rigidly with the brand hue (so the slot ORDER, which is what carries the colour-vision guarantee, never moves) while each slot's lightness and chroma are solved against that theme's own surfaces. Adjacent slots are gated in CI under protanopia, deuteranopia and tritanopia for the seeds CI measures — including the two measured worst cases, not only comfortable ones — and each derivation reports what it achieved so a caller supplying its own surfaces can branch. Note the gate covers only adjacent pairs, so a scatter or bubble chart, where any two marks can touch, should cap its series count rather than lean on colour alone. See Categorical palette for the measured tables.

View Colors Documentation →

📝 Typography

Fluid typography system with automatic viewport scaling and variable font support.

  • Fluid typography - Smooth viewport-based scaling
  • Modular scales - Configurable ratio-based hierarchies
  • Variable fonts - Modern font technology support
  • Google Fonts - Seamless integration
  • Utility classes - Pre-built typographic styles

View Typography Documentation →

📐 Spacing

Fluid spacing system with container-driven responsiveness and density control.

  • Fluid spacing - CSS clamp() for smooth scaling
  • Container queries - cqw units with viewport fallback
  • Density presets - compact, comfortable, spacious
  • Semantic tokens - inline, stack, inset, squish, stretch, gutter, section
  • Utility classes - Pre-built spacing utilities

View Spacing Documentation →

🌑 Shadows

First-class shadow design tokens with a configurable shadow engine.

  • 5 elevation levels - xs, sm, md, lg, xl
  • Shadow engine - Ratio-based controls
  • Presets - Flat, Soft, Elevated, Dramatic
  • Light/dark mode - Automatic adaptation
  • Brand tinting - Optional color tinting

View Shadows Documentation →

⭕ Radius

First-class border radius system with shape presets.

  • 5 radius levels - xs, sm, md, lg, xl/pill
  • Shape presets - Sharp, Subtle, Rounded, Bubble
  • Computed scale - From base value
  • Utility classes - All corners, sides, individual

View Radius Documentation →

📏 Borders

Complete border design token system with semantic borders.

  • Two-layer tokens - Primitive + semantic
  • 5 width levels - none, hairline, thin, medium, thick
  • 5 border styles - none, solid, dashed, dotted, double
  • 11 semantic borders - card, control, divider, focus-ring, states
  • 4 presets - Minimal, Subtle, Standard, Strong

View Borders Documentation →

📐 Layout

CSS-only utility classes for display, flexbox, and grid layout.

  • Display utilities - block, flex, grid, hidden, contents
  • Flexbox - direction, wrap, justify, align, grow/shrink
  • Grid - columns, rows, spans, auto-flow, placement
  • Common patterns - center, vstack, hstack
  • Accessibility - sr-only, visibility

View Layout Documentation →

🧭 Theming Manifest

A single generated, typed description of every module's settable theming knobs — the shared theming vocabulary for the studio and the AI proxy.

  • Generated from source - aggregated from each module's tokens.json
  • Preset-first - presets, base scalars (type/range/default), and (colors only) per-token metadata
  • CI drift gate - regenerated and git diff-checked in quality-gates.yml
  • Importable - @pithyjs/design-system/manifest (subpath) or the package root
import { DESIGN_SYSTEM_MANIFEST } from '@pithyjs/design-system/manifest';

DESIGN_SYSTEM_MANIFEST.modules.radius.presets[0].presets;
// ["sharp", "subtle", "rounded", "bubble"]
DESIGN_SYSTEM_MANIFEST.modules.colors.colorTokens.filter((t) => t.hasStateVariants);

Regenerate after editing any tokens.json:

pnpm --filter @pithyjs/design-system run sync-manifest

Installation

npm install @pithyjs/design-system
pnpm add @pithyjs/design-system
yarn add @pithyjs/design-system

Quick Start

Colors

import { createThemeFromSeed, initTheme } from '@pithyjs/design-system/colors';

// Generate and apply theme from one color
const theme = createThemeFromSeed({ primary: '#3b82f6' });
initTheme(theme);
/* Use CSS variables */
.button {
  background: var(--color-primary);
  color: var(--color-on-primary);
}

.button:hover {
  background: var(--color-primary-hover);
}

Typography

import { initTypography } from '@pithyjs/design-system/typography';

// Initialize with defaults (fluid typography enabled by default)
initTypography();

// Or with custom configuration
initTypography({
  baseSize: 16,
  scale: 'major-third',  // 1.25
  lineHeight: 1.5,
  fluid: {
    enabled: true,
    mode: 'proportional',    // All text scales with viewport
    ratio: 0.35,             // Fluid scaling intensity (0.15-0.5)
    headingAmplification: 3, // Headings scale 3x more
  }
});
<!-- Use utility classes -->
<h1 class="display-1">Hero Headline</h1>
<p class="text-lg font-medium">Large, medium-weight text</p>
<article class="prose">Optimized long-form content</article>

Package Exports

The design system is organized as subpath exports:

// Colors
import { initTheme, getColor } from '@pithyjs/design-system/colors';
import '@pithyjs/design-system/colors/styles';  // CSS-only mode

// Typography
import { initTypography } from '@pithyjs/design-system/typography';
import '@pithyjs/design-system/typography/styles';  // CSS-only mode

// Spacing
import { initSpacing } from '@pithyjs/design-system/spacing';
import '@pithyjs/design-system/spacing/styles';  // CSS-only mode

// Shadows
import { initShadows } from '@pithyjs/design-system/shadows';
import '@pithyjs/design-system/shadows/styles';  // CSS-only mode

// Radius
import { initRadius } from '@pithyjs/design-system/radius';
import '@pithyjs/design-system/radius/styles';  // CSS-only mode

// Borders
import { initBorders } from '@pithyjs/design-system/borders';
import '@pithyjs/design-system/borders/styles';  // CSS-only mode

// Layout (CSS-only)
import '@pithyjs/design-system/layout/styles';

// Theming manifest (generated; types + DESIGN_SYSTEM_MANIFEST)
import { DESIGN_SYSTEM_MANIFEST } from '@pithyjs/design-system/manifest';

// Static palette serialization — build-time / Node only (see note below)
import { themeToCss, resolveTheme } from '@pithyjs/design-system/palette';

The ./palette subpath

@pithyjs/design-system/palette is a bundled, self-contained, DOM-free ESM file (dist/palette.js, produced by esbuild during pnpm build). It exists because the package's main entry is not loadable by bare Node: tsc under the repo-wide moduleResolution: "node" emits extensionless/directory relative specifiers, so import('@pithyjs/design-system') from Node throws ERR_UNSUPPORTED_DIR_IMPORT.

That matters for Vite plugins: Vite's config bundler externalizes bare specifiers to a resolved filesystem path that Node then imports, so a plugin importing the package root would break every vite.config load. Build-time consumers must import from ./palette, never from the root.

It exports only themeToCss, resolveTheme, and the types they need — no ThemeManager, no initTheme, nothing touching document.

Why prepare builds it

dist/ is gitignored, and designSystemPlugin imports this subpath as a value at plugin-construction time — before Vite starts. A fresh clone (or a git clean -xfd) would therefore fail pnpm dev and every SSG build with ERR_MODULE_NOT_FOUND before anything else ran. The prepare script builds only build:palette, not the whole package: it is the sole artifact needed at config-load time, and esbuild does it in ~50ms where a full tsc pass would add seconds to every install.

Features

Colors Features

✅ One-seed theme generation ✅ WCAG AA accessibility compliance ✅ 74+ semantic color tokens ✅ State variants (hover, active, selected, disabled) ✅ 5 shadow elevation levels ✅ Light/dark mode support ✅ CSS variables (--color-*) ✅ Runtime theme updates ✅ OKLCH color space with RGB fallbacks ✅ 222+ utility classes ✅ Theme scoping (multi-brand support) ✅ Export/import themes as JSON

Typography Features

✅ Fluid typography (viewport-based scaling) ✅ Proportional fluid mode (site-builder friendly) ✅ Modular type scales (8 built-in ratios) ✅ Variable font support ✅ Google Fonts integration ✅ Custom font upload (WOFF2) ✅ WCAG 2.x compliance ✅ CSS variables (--font-*, --fluid-ratio) ✅ Pre-built utility classes ✅ Accessibility-first design

Spacing Features

✅ Fluid spacing (container/viewport-based scaling) ✅ 5 spacing levels (xs, sm, md, lg, xl) ✅ 3 density presets (compact, comfortable, spacious) ✅ 7 semantic tokens (inline, stack, inset, squish, stretch, gutter, section) ✅ Negative spacing for overlap effects ✅ CSS variables (--space-*) ✅ Container query units with viewport fallback ✅ Pre-built utility classes ✅ CSS-only mode support

Shadows Features

✅ 5 elevation levels (xs, sm, md, lg, xl) ✅ Shadow engine with ratio-based controls ✅ 4 presets (Flat, Soft, Elevated, Dramatic) ✅ Light/dark mode support ✅ Optional brand tinting ✅ CSS variables (--shadow-*) ✅ Pre-built utility classes ✅ Theme integration sync

Radius Features

✅ 5 radius levels (xs, sm, md, lg, xl/pill) ✅ 4 shape presets (Sharp, Subtle, Rounded, Bubble) ✅ Computed scale from base value ✅ CSS variables (--radius-*) ✅ Pre-built utility classes (all corners, sides, individual) ✅ Theme integration sync

Borders Features

✅ Two-layer token system (primitive + semantic) ✅ 5 width levels (none, hairline, thin, medium, thick) ✅ 5 border styles (none, solid, dashed, dotted, double) ✅ 11 semantic borders (card, control, divider, focus-ring, states) ✅ 4 presets (Minimal, Subtle, Standard, Strong) ✅ CSS variables (--border-*) ✅ Pre-built utility classes ✅ Theme integration sync

Layout Features

✅ Display utilities (block, flex, grid, hidden, contents) ✅ Flex direction, wrap, justify, align ✅ Flex grow/shrink/basis shorthands ✅ Grid columns (1-12) and rows (1-6) ✅ Grid span, start/end positioning ✅ Grid auto-flow and placement ✅ Order utilities (1-12, first, last) ✅ Visibility and screen-reader utilities ✅ Common patterns (center, vstack, hstack) ✅ CSS-only (no JavaScript required)

Documentation

Module Documentation

Project Documentation

Implementation Details

Structure

@pithyjs/design-system/
├── src/
│   ├── colors/              # Colors module
│   │   ├── styles/         # SCSS fallbacks
│   │   ├── tests/          # Unit tests
│   │   ├── scripts/        # Build scripts
│   │   ├── math/           # Color algorithms
│   │   ├── accessibility/  # WCAG validation
│   │   └── README.md       # Module docs
│   │
│   ├── typography/          # Typography module
│   │   ├── styles/         # SCSS fallbacks
│   │   ├── core/           # Core functionality
│   │   ├── fonts/          # Font loading
│   │   ├── scales/         # Scale generation
│   │   └── README.md       # Module docs
│   │
│   ├── spacing/             # Spacing module
│   │   ├── styles/         # SCSS tokens & utilities
│   │   └── *.ts            # TypeScript source
│   │
│   ├── shadows/             # Shadows module
│   │   ├── styles/         # SCSS tokens & utilities
│   │   └── *.ts            # TypeScript source
│   │
│   ├── radius/              # Radius module
│   │   ├── styles/         # SCSS tokens & utilities
│   │   └── *.ts            # TypeScript source
│   │
│   ├── borders/             # Borders module
│   │   ├── styles/         # SCSS tokens & utilities
│   │   └── *.ts            # TypeScript source
│   │
│   ├── layout/              # Layout module (CSS-only)
│   │   └── styles/         # SCSS utilities
│   │
│   └── index.ts             # Main exports
│
└── README.md                # This file

Usage Modes

Both modules support multiple usage modes:

1. Full Runtime Mode (Recommended)

Maximum features and flexibility:

import { createThemeFromSeed, initTheme } from '@pithyjs/design-system/colors';
import { initTypography } from '@pithyjs/design-system/typography';

const theme = createThemeFromSeed({ primary: '#3b82f6' });
initTheme(theme);
initTypography({ scaleRatio: 1.25 });

2. CSS-Only Mode

Zero JavaScript, SSR-friendly. Typography needs five companion layers — they are not independent:

import '@pithyjs/design-system/colors/styles';
import '@pithyjs/design-system/radius/styles';
import '@pithyjs/design-system/borders/styles';
import '@pithyjs/design-system/a11y/styles';
import '@pithyjs/design-system/motion/styles';
import '@pithyjs/design-system/typography/styles';

This list used to read colors + typography only, and that was wrong. Typography's own rules reference eighteen properties from other layers, and colors/styles provides just six of them — it emits 92 custom properties and none of --radius-*, --border-width-*, --border-style-solid, --focus-ring-* or --motion-*. The other twelve silently resolved to nothing: code lost its radius and border, focus rings lost their width and offset, and link transitions lost their duration and easing. Nothing errored — an unresolved var() is simply dropped — so the page rendered, slightly wrong, with no signal.

Dropping a layer you are sure you do not need is safe to try: the entry-point test compiles this exact set and fails naming any property left unresolved. Adding a token that no layer here provides fails the same way.

3. Hybrid Mode

Progressive enhancement:

// Static CSS for instant render
import '@pithyjs/design-system/colors/styles';

// Enhance with runtime features
import { getThemeManager } from '@pithyjs/design-system/colors';
const theme = getThemeManager();
theme.toggleMode(); // Add dark mode toggle

4. Serialized Palette Mode (SSR/SSG)

Static output has no JavaScript, so the runtime theme manager never runs and every var(--color-*) is undefined. Serialize the resolved theme into ordinary CSS and put it in the build's stylesheet graph:

import { resolveTheme, themeToCss } from '@pithyjs/design-system';

// `resolveTheme` accepts the same `colors` config `initDesignSystem` takes:
// a seed produces a generated theme, anything else the default theme.
const css = themeToCss(resolveTheme({ seed: { primary: '#3b82f6' } }), {
  defaultMode: 'light',
});

Feed the same config to initDesignSystem() on the client and hydration writes byte-identical values — a visual no-op rather than a flash:

initDesignSystem({ colors: { seed: { primary: '#3b82f6' } } });

Unlike the static SCSS themes, this path computes state variants with the same generateStateVariants() math the runtime uses, so the two cannot drift. See src/colors/README.md.

Examples

See working examples:

Contributing

When adding features:

  1. Add @codex annotations for documentation
  2. Write tests
  3. Update progress trackers
  4. Add examples

Status

Colors Module

✅ Complete - All core features implemented (Phases 1-7)

Typography Module

✅ Complete - All core features implemented (11/12 phases at 100%, 158 tests passing)

Spacing Module

✅ Complete - All 7 phases implemented

Shadows Module

✅ Complete - All 6 phases implemented

Radius Module

✅ Complete - All 6 phases implemented

Borders Module

✅ Complete - All 6 phases implemented

Layout Module

✅ Complete - CSS-only utilities for display, flexbox, and grid

API Reference

Note: Methods listed below are grouped by module. Class methods are prefixed with their class name. For detailed per-module API docs, see the module READMEs linked above.

A11y

| API | Signature | Stability | | ------------------- | ---------------------------------------- | --------- | | A11yManager | class A11yManager | stable | | A11yManager.destroy | () => void | stable | | initA11y | (options?: A11yOptions) => A11yManager | stable | | getA11yManager | () => A11yManager \| null | stable | | destroyA11y | () => void | stable |

Borders

| API | Signature | Stability | | -------------------------------- | -------------------------------------------------------- | --------- | | BorderManager | class BorderManager | stable | | BorderManager.setPreset | (preset: BorderPreset) => void | stable | | BorderManager.getPreset | () => BorderPreset \| 'custom' | stable | | BorderManager.getBorder | (semantic: SemanticBorder) => string | stable | | BorderManager.setRingWidth | (width: RingWidthLevel) => void | stable | | BorderManager.setRingOffset | (offset: RingOffsetLevel) => void | stable | | BorderManager.setRingColor | (color: RingColor) => void | stable | | BorderManager.setRingColorCustom | (color: string) => void | stable | | BorderManager.setRingOffsetColor | (color: string) => void | stable | | BorderManager.destroy | () => void | stable | | initBorders | (options?: BorderOptions) => BorderManager | stable | | getBorderManager | () => BorderManager \| null | stable | | destroyBorders | () => void | stable | | DEFAULT_PRESET | BorderPreset | stable | | getPresetConfig | (preset: BorderPreset) => BorderPresetConfig | stable | | formatBorder | (def: SemanticBorderDefinition) => string | stable | | generateAllCSSVariables | (config: BorderPresetConfig) => Record<string, string> | stable | | generateRingCSSVariables | (config?: RingConfig) => Record<string, string> | stable | | getRingColorVar | (color: RingColor) => string | stable |

Colors — Theme Manager

| API | Signature | Stability | | -------------------------------- | ------------------------------------------------------------------------------ | ------------ | | ThemeManager | class ThemeManager | experimental | | ThemeManager.getMode | () => ThemeMode | stable | | ThemeManager.setMode | (mode: ThemeMode) => void | stable | | ThemeManager.toggleMode | () => ThemeMode | stable | | ThemeManager.getToken | (token: ColorToken) => OKLCHColor | stable | | ThemeManager.setToken | (token: ColorToken, value: ColorInput, mode?: ThemeMode) => void | stable | | ThemeManager.updateTokens | (updates: Partial<ThemeTokens>, mode?: ThemeMode) => void | stable | | ThemeManager.setTheme | (tokens: ThemeDefinition, options?: { mode?: ThemeMode }) => void | stable | | ThemeManager.getAllTokens | (mode?: ThemeMode) => ThemeTokens | stable | | ThemeManager.getVariableName | (token: ColorToken) => string | stable | | ThemeManager.getVariableValue | (token: ColorToken) => string | stable | | ThemeManager.getComputedToken | (token: ColorToken) => OKLCHColor \| null | stable | | ThemeManager.applyTheme | () => void | stable | | ThemeManager.exportTheme | () => ThemeDefinition | stable | | ThemeManager.importTheme | (theme: ThemeDefinition, validate?: boolean) => boolean | stable | | ThemeManager.onModeChange | (callback: (mode: ThemeMode) => void) => () => void | stable | | ThemeManager.getValidationStatus | () => ValidationStatus | experimental | | ThemeManager.getPreviewedTokens | () => ColorToken[] | experimental | | ThemeManager.previewColor | (token: ColorToken, value: ColorInput, mode?: ThemeMode) => void | experimental | | ThemeManager.previewColors | (updates: Partial<Record<ColorToken, ColorInput>>, mode?: ThemeMode) => void | experimental | | ThemeManager.validateAndApply | () => Promise<ValidationReport> | experimental | | ThemeManager.discardPreviews | () => void | experimental | | ThemeManager.destroy | () => void | stable |

Colors — Convenience Functions

| API | Signature | Stability | | ------------------- | -------------------------------------------------------------------------- | ------------ | | initTheme | (tokens?: ThemeDefinition, options?: CSSVariableOptions) => ThemeManager | stable | | getThemeManager | () => ThemeManager | stable | | destroyTheme | () => void | stable | | getColor | (token: ColorToken) => OKLCHColor | stable | | setColor | (token: ColorToken, value: ColorInput) => void | stable | | setTheme | (tokens: ThemeDefinition, options?: { mode?: ThemeMode }) => void | stable | | getValidationStatus | () => ValidationStatus | experimental | | getPreviewedTokens | () => ColorToken[] | experimental | | previewColor | (token: ColorToken, value: ColorInput) => void | experimental | | previewColors | (updates: Partial<Record<ColorToken, ColorInput>>) => void | experimental | | validateAndApply | () => Promise<ValidationReport> | experimental | | discardPreviews | () => void | experimental | | loadThemeFromCSS | () => ThemeDefinition | stable | | DEFAULT_THEME | ThemeDefinition | stable | | getDefaultTheme | (loadFromCSS?: boolean) => ThemeDefinition | stable |

Colors — Scoped Themes

| API | Signature | Stability | | ----------------- | ------------------------------------------------------------------------------------ | ------------ | | applyScopedTheme | (element: HTMLElement \| string, theme: ThemeDefinition, mode?: ThemeMode) => void | experimental | | removeScopedTheme | (element: HTMLElement \| string) => void | experimental |

Colors — Validation & Serialization

| API | Signature | Stability | | ---------------------- | ------------------------------------------------------------------------------------------------------------ | --------- | | validateThemeStructure | (theme: any) => ThemeValidationResult | stable | | validateThemeJSON | (json: string) => { valid: boolean; theme: ThemeDefinition \| null; errors: string[]; warnings: string[] } | stable | | exportThemeJSON | (theme: ThemeDefinition) => string \| null | stable | | importThemeJSON | (json: string) => ThemeDefinition \| null | stable |

Colors — Math & Conversion

| API | Signature | Stability | | ------------------- | --------------------------------------------------- | --------- | | formatOKLCH | (color: OKLCHColor) => string | stable | | formatRGB | (color: RGBColor) => string | stable | | parseOKLCH | (oklchString: string) => OKLCHColor \| null | stable | | getCSSVariableName | (token: string, prefix?: string) => string | stable | | hexToRgb | (hex: string) => RGBColor | stable | | oklchToHex | (color: OKLCHColor) => string | stable | | hexToOklch | (hex: string) => OKLCHColor | stable | | parseColor | (color: string) => OKLCHColor | stable | | oklchToRgb | (color: OKLCHColor) => RGBColor | stable | | rgbToOklch | (rgb: RGBColor) => OKLCHColor | stable | | normalizeHue | (hue: number) => number | stable | | interpolateHue | (hue1: number, hue2: number, t: number) => number | stable | | normalizeColorInput | (color: ColorInput) => OKLCHColor | stable | | isInGamut | (color: OKLCHColor) => boolean | stable | | gamutDistance | (color: OKLCHColor) => number | stable | | clipToGamut | (color: OKLCHColor) => OKLCHColor | stable | | oklchToRgbClipped | (color: OKLCHColor) => RGBColor | stable | | getMaxChroma | (l: number, h: number) => number | stable |

Colors — Accessibility

| API | Signature | Stability | | ------------------------ | --------------------------------------------------------------------------------------------- | ------------ | | setContrastConfig | (config: Partial<ContrastConfig>) => void | stable | | getContrastConfig | () => ContrastConfig | stable | | resetContrastConfig | () => void | stable | | getLuminanceFromRgb | (rgb: RGBColor) => number | stable | | getLuminanceFromOklch | (color: OKLCHColor) => number | stable | | getContrastRatioOklch | (color1: ColorInput, color2: ColorInput) => number | stable | | getContrastRatioRgb | (rgb1: RGBColor, rgb2: RGBColor) => number | stable | | meetsWCAG_AA | (color1: ColorInput, color2: ColorInput, textSize?: TextSize) => boolean | stable | | meetsWCAG_AAA | (color1: ColorInput, color2: ColorInput, textSize?: TextSize) => boolean | stable | | getWCAGReport | (color1: ColorInput, color2: ColorInput) => WCAGReport | stable | | ensureWCAG_AA | (baseColor: ColorInput, color: ColorInput, textSize?: TextSize) => OKLCHColor | experimental | | generateOnColor | (baseColor: ColorInput, config?: OnColorConfig) => OKLCHColor | stable | | generateOnColors | (tokens: ThemeTokens) => ThemeTokens | stable | | validateOnColor | (baseColor: ColorInput, onColor: ColorInput, textSize?: TextSize) => ValidationResult | stable | | generateBestOnColor | (baseColor: ColorInput) => OKLCHColor | stable | | generateTintedOnColor | (baseColor: ColorInput, chromaReduction?: number) => OKLCHColor | experimental | | getOnColorDiagnostics | (tokens: ThemeTokens) => OnColorDiagnostics[] | stable | | generateContrastMatrix | (tokens: ThemeTokens, mode: 'light' \| 'dark', customPairs?: ColorPair[]) => ContrastMatrix | stable | | getFailedChecks | (matrix: ContrastMatrix) => ContrastCheckResult[] | stable | | generateThemeDiagnostics | (theme: ThemeDefinition) => ThemeDiagnostics | stable | | validateTheme | (theme: ThemeDefinition) => { passes: boolean; score: number; issues: number } | stable |

Colors — Derivation & Theme Generation

| API | Signature | Stability | | -------------------------------- | --------------------------------------------------------------------------------------------------------- | --------- | | selectDerivationStrategy | (primary: ColorInput) => DerivationStrategy | stable | | deriveColors | (primary: ColorInput, strategy?: DerivationStrategy, analogousOffset?: number) => DerivedColors | stable | | deriveSecondary | (primary: ColorInput, strategy?: DerivationStrategy) => OKLCHColor | stable | | deriveTertiary | (primary: ColorInput, strategy?: DerivationStrategy) => OKLCHColor | stable | | deriveColorsWithChromaAdjustment | (primary: ColorInput, strategy?: DerivationStrategy) => DerivedColors | stable | | applyHueTint | (baseColor: OKLCHColor, tintColor: OKLCHColor, intensity: number) => OKLCHColor | stable | | getDefaultTintIntensity | (type: 'text' \| 'surface' \| 'shadow') => number | stable | | generateStateVariants | (baseColor: OKLCHColor, mode: ThemeMode, config?: StateVariantConfig) => Record<ColorState, OKLCHColor> | stable | | shouldHaveStateVariants | (token: string) => boolean | stable | | createThemeFromSeed | (seed: ThemeSeed) => ThemeDefinition | stable | | createQuickTheme | (primaryColor: ColorInput) => ThemeDefinition | stable | | createThemeWithStrategy | (primaryColor: ColorInput, strategy: DerivationStrategy) => ThemeDefinition | stable | | createHarmonicTheme | (primaryColor: ColorInput) => ThemeDefinition | stable | | mapPalettesToTokens | (palettes: ThemePalettes, mapping: ToneMapping) => ThemeTokens | stable | | mapPalettesToTheme | (palettes: ThemePalettes, lightMapping?: ToneMapping, darkMapping?: ToneMapping) => ThemeDefinition | stable | | createCustomMapping | (base: ToneMapping, overrides: Partial<ToneMapping>) => ToneMapping | stable |

Colors — Tonal Palettes

| API | Signature | Stability | | ---------------------- | ------------------------------------------------------------------------------------ | ------------ | | generateBrandPalette | (seed: ColorInput, chromaCurve?: Partial<ChromaCurveConfig>) => TonalPalette | stable | | generateNeutralPalette | (seed: ColorInput, chromaCurve?: Partial<ChromaCurveConfig>) => TonalPalette | stable | | getTone | (palette: TonalPalette, tone: ToneStep) => OKLCHColor | stable | | getClosestTone | (targetLightness: number) => ToneStep | stable | | interpolateTones | (palette: TonalPalette, tone1: ToneStep, tone2: ToneStep, t: number) => OKLCHColor | experimental | | previewChromaCurve | (config: ChromaCurveConfig) => Partial<Record<ToneStep, number>> | stable | | deriveNeutralSeed | (brandSeed: ColorInput, chromaReduction?: number) => OKLCHColor | stable |

Colors — CSS Utilities

| API | Signature | Stability | | -------------------- | --------------------------------------------- | --------- | | generateAllUtilities | (config?: UtilityGeneratorConfig) => string | stable |

Layout

| API | Signature | Stability | | -------------------------- | --------------------------------------------------------- | --------- | | CONTAINER_BREAKPOINTS | Record<'sm'\|'md'\|'lg'\|'xl', string> | stable | | ContainerBreakpoint | type ContainerBreakpoint = 'sm' \| 'md' \| 'lg' \| 'xl' | stable | | CONTAINER_BREAKPOINT_NAMES | ContainerBreakpoint[] | stable |

Motion

| API | Signature | Stability | | ------------------------ | -------------------------------------------------- | --------- | | MotionManager | class MotionManager | stable | | MotionManager.destroy | () => void | stable | | initMotion | (options?: MotionOptions) => MotionManager | stable | | getMotionManager | () => MotionManager \| null | stable | | destroyMotion | () => void | stable | | KEYFRAMES | Record<AnimationPattern, string> | stable | | injectKeyframes | (target?: Document) => HTMLStyleElement \| null | stable | | removeKeyframes | (target?: Document) => void | stable | | computeEffectiveDuration | (baseMs: number, intensity: number) => number | stable | | generateMotionVariables | (config: MotionConfig) => Record<string, string> | stable |

Radius

| API | Signature | Stability | | ----------------------- | ---------------------------------------------------------------------- | --------- | | RadiusManager.destroy | () => void | stable | | getRadiusManager | () => RadiusManager \| null | stable | | destroyRadius | () => void | stable | | generateRadiusTokens | (scale: RadiusScale) => RadiusTokens | stable | | generateAllCSSVariables | (scale: RadiusScale, tokens: RadiusTokens) => Record<string, string> | stable |

Shadows

| API | Signature | Stability | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------- | | ShadowManager | class ShadowManager | stable | | ShadowManager.destroy | () => void | stable | | initShadows | (options?: ShadowOptions) => ShadowManager | stable | | getShadowManager | () => ShadowManager \| null | stable | | destroyShadows | () => void | stable | | initShadowsWithSync | (themeManager: ThemeManagerLike, options?: ShadowOptions) => { shadowManager: ShadowManager; unsubscribe: () => void } | stable | | generateShadowTokens | (base: ShadowBase, ratios: ShadowRatios) => ShadowTokens | stable | | generateAllCSSVariables | (base: ShadowBase, ratios: ShadowRatios, tokens: ShadowTokens) => Record<string, string> | stable | | applyTinting | (base: ShadowBase, tintColor: [number, number, number], intensity: number) => ShadowBase | stable | | hexToRgbTuple | (hex: string) => [number, number, number] \| null | stable |

Shared

| API | Signature | Stability | | --------------- | ------------------------------------- | --------- | | detectThemeMode | (target?: HTMLElement) => ThemeMode | stable |

Examples

initA11y({ preset: 'strict' });
const manager = getA11yManager();
manager?.setControlSize('lg');
destroyA11y();
resolveA11yState({ preset: 'strict' }).controlSize; // 'lg'
const vars = a11yCssVars(resolveA11yState({ preset: 'default' }));
vars['--focus-ring-width']; // '2px'
import { initBorders } from '@pithyjs/design-system/borders';

// Initialize with default preset
initBorders();

// Initialize with specific preset
initBorders({ preset: 'strong' });

// Initialize with overrides
initBorders({
  preset: 'standard',
  overrides: {
    card: { width: 'medium', style: 'solid', colorVar: 'var(--color-primary)' }
  }
});
const manager = getBorderManager();
manager?.setPreset('minimal');
destroyBorders();
const config = getPresetConfig('minimal');
console.log(config.card.width); // 'none'
formatBorder({ width: 'thin', style: 'solid', colorVar: 'var(--color-border)' });
// => "2px solid var(--color-border)"

formatBorder({ width: 'none', style: 'none', colorVar: 'var(--color-border)' });
// => "none"
const config = getPresetConfig('standard');
const vars = generateAllCSSVariables(config);
// Apply to element
Object.entries(vars).forEach(([name, value]) => {
  document.documentElement.style.setProperty(name, value);
});
const vars = generateRingCSSVariables({ width: 'default', offset: 'default' });
// Apply to element
Object.entries(vars).forEach(([name, value]) => {
  document.documentElement.style.setProperty(name, value);
});
getRingColorVar('primary');
// => "var(--color-primary)"
const vars = borderCssVars(getPresetConfig('strong'), { width: 'default', offset: 'default' }, 'strong');
vars['--border-width-thin']; // '3px'
const slots = deriveCategoricalSlots({
  brandHue: 250,
  brandChroma: 0.16,
  surfaces: [tokens.bg, tokens.surface, tokens['surface-variant']],
});
const { slots, meetsContrastFloor, worstContrast } =
  deriveCategoricalSlotsChecked({ brandHue, brandChroma, surfaces });

if (!meetsContrastFloor) {
  // worstContrast is what the tightest mark/surface pair actually measures.
}
categoricalSlotForIndex(0); // 'categorical-1'
categoricalSlotForIndex(5); // 'categorical-6'
categoricalSlotForIndex(6); // null — NOT 'categorical-1'
const cssVar = categoricalCssVarForIndex(2);
element.style.fill = cssVar ? `var(${cssVar})` : 'var(--color-text-muted)';
// Load SSR theme into runtime
const theme = loadThemeFromCSS();
initTheme(theme);
// Full runtime mode - use hardcoded fallback
const theme = getDefaultTheme();
// Hybrid/SSR mode - load from CSS
const theme = getDefaultTheme(true);
const css = generateAllUtilities();
// Write to file or inject into document
setTheme(importedTheme, { mode: 'dark' });
// Custom theme
const theme = createThemeFromSeed({ primary: '#3b82f6' });
initTheme(theme);
// Default theme
initTheme();
// SSR hydration - load from CSS
initTheme(getDefaultTheme(true));
destroyTheme();
setColor('primary', '#3b82f6');
setColor('primary', 'rgb(59, 130, 246)');
setColor('primary', { l: 65, c: 0.16, h: 250 });
// Apply theme to admin panel
applyScopedTheme('.admin-panel', adminTheme);
// Apply theme to specific element
const modal = document.querySelector('.modal');
applyScopedTheme(modal, modalTheme, 'dark');
// Serialize the configured theme into a stylesheet for static output:
const css = themeToCss(resolveTheme(config.colors), { defaultMode: 'dark' });
const result = validateThemeStructure(theme);
if (!result.valid) {
  console.error('Theme validation failed:', result.errors);
}
const { valid, theme, errors } = validateThemeJSON(jsonString);
if (valid && theme) {
  initTheme(theme);
}
const json = exportThemeJSON(theme);
if (json) {
  localStorage.setItem('theme', json);
}
const theme = importThemeJSON(localStorage.getItem('theme'));
if (theme) {
  initTheme(theme);
}
colorTokenCssVars('--color-primary', { l: 48, c: 0.16, h: 250 });
// [
//   ['--color-primary', 'oklch(48% 0.16 250)'],
//   ['--color-primary-rgb', '0, 96, 166'],
//   ['--color-primary-l', '48%'],
//   ['--color-primary-c', '0.16'],
//   ['--color-primary-h', '250'],
// ]
const pairs = themeModeCssVars(theme.dark, 'dark');
for (const [name, value] of pairs) el.style.setProperty(name, value);
for (const name of colorTokenCssVarNames('--color-primary')) {
  el.style.removeProperty(name);
}
resolveIconographyConfig({ preset: 'spacious' }).scaleFactor; // 1.125
resolveIconographyConfig({ scaleFactor: 1.5 }).preset;        // 'custom'
const vars = iconographyCssVars(resolveIconographyConfig({ preset: 'compact' }));
vars['--icon-size-md']; // '18px'
resolveTheme(undefined);                    // DEFAULT_THEME
resolveTheme({ mode: 'dark' });             // DEFAULT_THEME
resolveTheme({ seed: { primary: '#7c3aed' } }); // generated theme
import { injectKeyframes } from '@pithyjs/design-system/motion';
injectKeyframes(); // adds <style id="pithy-motion-keyframes"> to <head>
initMotion({ preset: 'snappy' });
const manager = getMotionManager();
manager?.setPreset('calm');
destroyMotion();
resolveMotionConfig({ preset: 'snappy' }).intensity; // 0.7
const vars = motionCssVars(resolveMotionConfig({ preset: 'calm' }));
vars['--motion-intensity']; // '1.2'
const vars = generateMotionVariables(config);
// vars['--motion-duration-fast'] === '70ms'  (100 * 0.7 for snappy)
// vars['--motion-easing-standard'] === 'cubic-bezier(0.2, 0, 0, 1)'
const manager = getRadiusManager();
manager?.setPreset('sharp');
destroyRadius();
resolveRadiusState({ preset: 'bubble' }).scale.baseRadius; // 8
const vars = radiusCssVars(resolveRadiusState({ preset: 'rounded' }));
vars['--radius-md']; // '9px'
const tokens = generateRadiusTokens({ baseRadius: 4, scaleFactor: 1.5, maxRadius: 16, pill: 9999 });
console.log(tokens.md); // "9px"
import { initTheme, getThemeManager } from '@pithyjs/design-system/colors';
import { initShadowsWithSync } from '@pithyjs/design-system/shadows';
const theme = initTheme(myTheme);
const { shadowManager, unsubscribe } = initShadowsWithSync(theme, { preset: 'soft' });
initShadows({ preset: 'soft' });
const manager = getShadowManager();
manager?.setPreset('dramatic');
destroyShadows();
resolveShadowState({ preset: 'dramatic' }).ratios.depthRatio; // 2
const vars = shadowCssVars(resolveShadowState({ preset: 'elevated' }), 'light');
vars['--shadow-sm']; // '0px 4px 8px 0px rgba(0, 0, 0, 0.144)'
const tokens = generateShadowTokens(defaultBase.light, shadowPresets.elevated);
console.log(tokens.md); // "0px 3px 6px 0px rgba(0, 0, 0, 0.070)"
const mode = detectThemeMode();
// Returns "dark" if data-theme="dark" or system prefers dark
resolveSpacingState({ density: 'compact' }).density; // 'compact'
const vars = spacingCssVars(resolveSpacingState({ density: 'compact' }));
vars['--space-density-ratio']; // '0.9'
const css = designSystemToCss(
  { typography: { bodyFont: 'Inter' }, shadows: { preset: 'elevated' } },
  { defaultMode: 'light' }
);
const vars = resolveDesignSystemVars({ shadows: { preset: 'elevated' } });
vars.light['--shadow-sm']; // '0px 4px 8px 0px rgba(0, 0, 0, 0.144)'
const matrix = generateContrastMatrix(theme.light, 'light');
console.log(`Pass rate: ${matrix.summary.passRate}%`);
matrix.checks.filter(c => !c.passes).forEach(c => {
  console.log(`FAIL: ${c.pair.purpose} - ${c.ratio.toFixed(2)}:1`);
});
const diagnostics = generateThemeDiagnostics(theme);
console.log(`Score: ${diagnostics.overall.score}/100 (${diagnostics.overall.grade})`);
console.log(`Level: ${diagnostics.overall.level}`);
diagnostics.overall.recommendations.forEach(rec => console.log(`- ${rec}`));
const { colors, meetsContrastFloor, worstContrast } = deriveCategoricalPalette({
  brandHue: 250,
  brandChroma: 0.16,
  surfaces: [tokens.bg, tokens.surface, tokens['surface-variant']],
});
if (!meetsContrastFloor) {
  // worstContrast is the real number — decide, do not render it blind.
}
const slots = deriveCategoricalColors({
  brandHue: 250,
  brandChroma: 0.16,
  surfaces: [tokens.bg, tokens.surface, tokens['surface-variant']],
});
// Stricter requirements
setContrastConfig({ normalText: 5.0, largeText: 3.5 });
// More lenient for large UI elements
setContrastConfig({ graphical: 2.5 });
getContrastRatioOklch("#3b82f6", "#ffffff")
getContrastRatioOklch({ l: 65, c: 0.16, h: 250 }, { l: 100, c: 0, h: 0 })
meetsWCAG_AA("#3b82f6", "#ffffff") // true
meetsWCAG_AA({ l: 65, c: 0.16, h: 250 }, "#fff")
meetsWCAG_AAA("#3b82f6", "#ffffff")
getWCAGReport("#3b82f6", "#ffffff")
ensureWCAG_AA("#3b82f6", "#888888") // Adjusts gray to meet AA on blue
// Auto-select strategy based on chroma
const { secondary, tertiary, strategy } = deriveColors("#3b82f6")
console.log(strategy) // "complementary"
// Explicit strategy
const colors = deriveColors("#3b82f6", DerivationStrategy.TRIADIC)
// Analogous with custom offset
const colors = deriveColors("#3b82f6", DerivationStrategy.ANALOGOUS, 45)
const secondary = deriveSecondary("#3b82f6")
const secondary2 = deriveSecondary("#3b82f6", DerivationStrategy.COMPLEMENTARY)
const tertiary = deriveTertiary("#3b82f6")
const colors = deriveColorsWithChromaAdjustment("#3b82f6")
normalizeColorInput("#3b82f6")  // Returns { l: 65, c: 0.16, h: 250 }
normalizeColorInput({ l: 65, c: 0.16, h: 250 })  // Returns same object
const onPrimary = generateOnColor("#3b82f6") // White or black, whichever passes
const onSurface = generateOnColor({ l: 99, c: 0, h: 0 }) // Dark text for light surface
const tokens = mapPalettesToTokens(palettes, LIGHT_MODE_MAPPING);
const withOnColors = generateOnColors(tokens);
const valid = validateOnColor("#3b82f6", "#ffffff")
console.log(valid.passes) // true
console.log(valid.ratio)  // 8.2
const onColor = generateBestOnColor("#888888") // Mid-gray - could go either way
// Blue background gets slightly blue-tinted text
const onBlue = generateTintedOnColor("#3b82f6")
const diagnostics = getOnColorDiagnostics(tokens)
diagnostics.forEach(d => {
  console.log(`${d.token}: ${d.ratio.toFixed(2)}:1 ${d.passes ? '✓' : '✗'}`)
})
const theme = mapPalettesToTheme(palettes)
const customTheme = mapPalettesToTheme(palettes, customLightMap, customDarkMap)
const customLight = createCustomMapping(LIGHT_MODE_MAPPING, {
  primary: 70,  // Slightly lighter primary
  text: 15,     // Darker text
})
const variants = generateStateVariants(primaryColor, 'light');
// { hover: {...}, active: {...}, selected: {...}, disabled: {...} }
// Simplest usage - just a hex color
const theme = createThemeFromSeed({ primary: "#3b82f6" })
// With custom secondary
const theme = createThemeFromSeed({
  primary: "#3b82f6",
  secondary: "#ec4899",
  tertiary: "auto",
})
// Full control
const theme = createThemeFromSeed({
  primary: "#3b82f6",
  derivation: "complementary",
  harmonicStatuses: true,
  statuses: {
    success: "#10b981",
    danger: "#ef4444",
  },
})
const theme = createQuickTheme("#3b82f6")
const theme = createThemeWithStrategy("#3b82f6", "triadic")
const theme = createHarmonicTheme("#3b82f6")
const tintedText = applyHueTint(
  { l: 20, c: 0.02, h: 0 },  // neutral gray text
  { l: 65, c: 0.16, h: 250 }, // blue primary
  0.05  // 5% tint
);
// Result: text with slight blue hue shift
generateBrandPalette("#3b82f6")
generateBrandPalette({ l: 65, c: 0.16, h: 250 })
generateNeutralPalette("#3b82f6") // Will use low chroma
generateNeutralPalette({ l: 50, c: 0.02, h: 250 })
deriveNeutralSeed("#3b82f6") // Creates low-chroma blue neutral
deriveNeutralSeed({ l: 65, c: 0.16, h: 250 }, 0.90) // 90% reduction
destroyTypography();
const computed = computeTypography(mergeTypographyConfig({ baseSize: 18 }));
computed.fontSizes.h1; // '2.6975rem'
const vars = typographyCssVars(mergeTypographyConfig({ bodyFont: 'Inter' }));
vars['--font-family-body']; // "'Inter', -apple-system, …"
typographyStaticTokenCssVars()['--leading-snug']; // '1.375'
const fluidSizes = calculateProportionalFluidSizes({
  baseSize: 0.9,           // Base size in rem
  ratio: 0.35,             // vw multiplier
  headingAmplification: 3, // Headings scale 3x more
});

// Result:
// {
//   body: 'clamp(1rem, calc(0.9rem + 0.35vw), 1.4rem)',
//   h1: 'clamp(2.1rem, calc(2.475rem + 1.4vw), 4.5rem)',
//   ...
// }

Testing

1,073 tests across 41 test files, all passing.

| Module | Test Files | Tests | Key Coverage | | ----------- | ---------- | ----- | ------------------------------------------------------------------------------------------------ | | Colors | 6 | ~300+ | Theme generation, color math, accessibility, tonal palettes, utils, ThemeManager | | Typography | 6 | ~150+ | Fluid scale, font loader, Google Fonts, accessibility validation, diagnostics, TypographyManager | | Spacing | 3 | ~80+ | Token computation, density presets, SpacingManager | | Shadows | 4 | ~100+ | Token generation, ShadowManager, theme sync integration, generated presets | | Radius | 3 | ~60+ | Token generation, RadiusManager, generated presets | | Borders | 4 | ~80+ | Border formatting, presets, ring tokens, BorderManager | | A11y | 2 | ~50+ | A11yManager, CSS variable tokens | | Iconography | 3 | ~60+ | Token computation, IconographyManager, generated tokens | | Motion | 4 | ~80+ | Keyframe injection, MotionManager, token generation, generated presets | | Init | 1 | ~30+ | Unified initialization, selective module init | | Token Infra | 5 | ~80+ | Computation utils, formatters, validation, orchestrator, snapshot tests |

Run all tests:

cd packages/design-system && pnpm test

License

ISC