Files
grimoire/claudedocs/THEME_TOKEN_SYSTEM.md
Alejandro Gómez 78a2deeadd ai: theme
2025-12-15 17:30:18 +01:00

18 KiB

Grimoire Theme Token System

Overview

Comprehensive design token system for Grimoire's visual styling. This system abstracts all visual properties into semantic tokens that can be applied to any theme (dark, light, or custom). Built on industry-standard design token patterns used by Tailwind, Radix, and Material Design.

Status: Design Complete - Ready for Implementation Approach: Two-tier token system (Base + Semantic) Format: HSL for colors, CSS length units for spacing/sizing Philosophy: Theme-agnostic components, theme-specific values


Token Categories

1. Colors (Surfaces & Interactive)

Semantic color tokens for backgrounds, text, and UI elements.

Structure:

colors: {
  // Surfaces
  background: HSL;           // Main app background
  foreground: HSL;           // Main text color
  card: HSL;                 // Elevated surface
  "card-foreground": HSL;
  popover: HSL;              // Floating elements
  "popover-foreground": HSL;

  // Interactive Elements
  primary: HSL;              // Primary actions (buttons, links)
  "primary-foreground": HSL;
  secondary: HSL;            // Secondary actions
  "secondary-foreground": HSL;
  muted: HSL;                // Subtle backgrounds
  "muted-foreground": HSL;   // Secondary text
  accent: HSL;               // Highlight/emphasis (purple!)
  "accent-foreground": HSL;
  destructive: HSL;          // Errors, danger
  "destructive-foreground": HSL;

  // UI Elements
  border: HSL;               // Dividers, borders
  input: HSL;                // Input backgrounds
  ring: HSL;                 // Focus indicators
}

Current Dark Theme Values:

colors: {
  background: "222.2 84% 4.9%",       // Deep blue-gray
  foreground: "210 40% 98%",          // Almost white
  accent: "270 100% 70%",             // Grimoire purple!
  // ... full definition in grimoire-dark.ts
}

Light Theme Values:

colors: {
  background: "40 20% 97%",           // Warm off-white
  foreground: "222 84% 8%",           // Very dark
  accent: "270 100% 50%",             // Darker purple (contrast)
  // ... full definition in grimoire-light.ts
}

2. Gradients

Multi-stop gradient definitions for brand and decorative purposes.

Structure:

gradients: {
  brand: [HSL, HSL, HSL, HSL];  // Grimoire 4-color brand gradient
}

Current Values:

// Dark theme
gradients: {
  brand: [
    "43 100% 54%",    // Yellow
    "25 95% 61%",     // Orange
    "270 91% 65%",    // Purple
    "188 86% 53%",    // Cyan
  ],
}

// Light theme (adjusted for contrast)
gradients: {
  brand: [
    "43 100% 45%",    // Darker yellow
    "25 95% 50%",     // Darker orange
    "270 91% 50%",    // Darker purple
    "188 86% 40%",    // Darker cyan
  ],
}

Usage:

.text-grimoire-gradient {
  background: linear-gradient(
    to bottom,
    hsl(var(--gradient-brand-1)),
    hsl(var(--gradient-brand-2)),
    hsl(var(--gradient-brand-3)),
    hsl(var(--gradient-brand-4))
  );
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

3. Syntax Highlighting

Code syntax highlighting colors for Prism.js integration.

Structure:

syntax: {
  // General syntax tokens
  comment: HSL;
  keyword: HSL;
  string: HSL;
  function: HSL;
  number: HSL;
  operator: HSL;
  "operator-opacity"?: Opacity;

  // Diff-specific tokens (git diffs in code blocks)
  deleted: HSL;                // Removed lines
  "deleted-bg": HSL;
  "deleted-bg-opacity": Opacity;

  inserted: HSL;               // Added lines
  "inserted-bg": HSL;
  "inserted-bg-opacity": Opacity;

  coord: HSL;                  // Hunk headers (@@ -1,5 +1,7 @@)
  "coord-bg": HSL;
  "coord-bg-opacity": Opacity;
}

Current Values:

// Dark theme
syntax: {
  comment: "215 20.2% 70%",          // Medium gray
  keyword: "210 40% 98%",            // Bright
  deleted: "0 70% 75%",              // Light red
  "deleted-bg": "0 70% 60%",
  "deleted-bg-opacity": 0.1,
  // ... etc
}

// Light theme (inverted lightness)
syntax: {
  comment: "222 30% 35%",            // Dark gray
  keyword: "222 47% 11%",            // Very dark
  deleted: "0 70% 40%",              // Dark red
  "deleted-bg": "0 70% 60%",
  "deleted-bg-opacity": 0.1,
  // ... etc
}

Usage:

.token.deleted {
  color: hsl(var(--syntax-deleted));
  background: hsl(var(--syntax-deleted-bg) / var(--syntax-deleted-bg-opacity));
}

4. Effects & Transparency

Visual effects like shadows, overlays, and opacity values.

Structure:

effects: {
  "scrollbar-opacity": Opacity;        // Scrollbar thumb opacity
  "scrollbar-hover-opacity": Opacity;  // Hover state
  "overlay-opacity": Opacity;          // Modal/fullscreen overlays
  "preview-opacity": Opacity;          // Mosaic drag preview
  "shadow-color": HSL;                 // Shadow base color
  "shadow-blur": CSSLength;            // Shadow blur radius
}

Current Values (theme-agnostic):

effects: {
  "scrollbar-opacity": 0.2,
  "scrollbar-hover-opacity": 0.3,
  "overlay-opacity": 0.92,
  "preview-opacity": 0.3,
  "shadow-color": "0 0% 0%",           // Black for shadows
  "shadow-blur": "40px",
}

Usage:

*::-webkit-scrollbar-thumb {
  background-color: hsl(var(--foreground) / var(--scrollbar-opacity));
}

*::-webkit-scrollbar-thumb:hover {
  background-color: hsl(var(--foreground) / var(--scrollbar-hover-opacity));
}

[data-rmiz-modal-overlay] {
  background-color: hsl(var(--background) / var(--overlay-opacity)) !important;
}

5. Spacing & Sizing

Layout dimensions and component sizes.

Structure:

spacing: {
  "toolbar-height": CSSLength;    // Mosaic window toolbar
  "split-width": CSSLength;       // Window split handle
  "scrollbar-width": CSSLength;   // Scrollbar thickness
}

Current Values (theme-agnostic):

spacing: {
  "toolbar-height": "30px",
  "split-width": "4px",
  "scrollbar-width": "8px",
}

Usage:

.mosaic-window .mosaic-window-toolbar {
  height: var(--toolbar-height);
}

.mosaic-split.-row {
  width: var(--split-width);
}

6. Typography

Font families, sizes, and line heights.

Structure:

typography: {
  "font-family-mono": string;      // Monospace font stack
  "font-size-base": CSSLength;     // Base text size
  "font-size-code": CSSLength;     // Code block text size
  "line-height-base": string;      // Base line height
  "line-height-code": string;      // Code block line height
}

Current Values (theme-agnostic):

typography: {
  "font-family-mono": "'Oxygen Mono', monospace",
  "font-size-base": "1rem",
  "font-size-code": "0.75rem",
  "line-height-base": "1.5",
  "line-height-code": "1.5",
}

Usage:

body {
  font-family: var(--font-mono);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

code {
  font-size: var(--font-size-code);
  line-height: var(--line-height-code);
}

7. Geometry

Border radius and shape properties.

Structure:

geometry: {
  radius: CSSLength;    // Standard border radius
}

Current Values (theme-agnostic):

geometry: {
  radius: "0.5rem",  // 8px at default font size
}

Usage:

.button {
  border-radius: var(--radius);
}

/* Derived values */
.card {
  border-radius: calc(var(--radius) - 2px);  /* Slightly smaller */
}

8. Charts (Optional)

Data visualization color palette.

Structure:

charts: {
  1: HSL;
  2: HSL;
  3: HSL;
  4: HSL;
  5: HSL;
}

Current Values:

// Dark theme
charts: {
  1: "220 70% 50%",    // Blue
  2: "160 60% 45%",    // Green
  3: "30 80% 55%",     // Orange
  4: "280 65% 60%",    // Purple
  5: "340 75% 55%",    // Pink
}

// Light theme (adjusted)
charts: {
  1: "220 70% 45%",
  2: "160 60% 40%",
  3: "30 80% 50%",
  4: "280 65% 50%",
  5: "340 75% 50%",
}

TypeScript Schema

Complete type definitions for theme system:

// src/types/theme.ts

/**
 * HSL color format: "hue saturation% lightness%"
 * Example: "270 100% 70%"
 * Note: No hsl() wrapper - used as: hsl(var(--token))
 */
export type HSL = string;

/**
 * Opacity value between 0 and 1
 */
export type Opacity = number;

/**
 * CSS length value (px, rem, em, etc.)
 */
export type CSSLength = string;

/**
 * Complete Grimoire theme definition
 */
export interface GrimoireTheme {
  /** Unique identifier */
  id: string;

  /** Display name */
  name: string;

  /** Base type for contrast calculations */
  type: "light" | "dark";

  /** Optional metadata */
  author?: string;
  description?: string;
  version?: string;

  /** Color tokens */
  colors: {
    background: HSL;
    foreground: HSL;
    card: HSL;
    "card-foreground": HSL;
    popover: HSL;
    "popover-foreground": HSL;
    primary: HSL;
    "primary-foreground": HSL;
    secondary: HSL;
    "secondary-foreground": HSL;
    muted: HSL;
    "muted-foreground": HSL;
    accent: HSL;
    "accent-foreground": HSL;
    destructive: HSL;
    "destructive-foreground": HSL;
    border: HSL;
    input: HSL;
    ring: HSL;
  };

  /** Gradient definitions */
  gradients: {
    brand: [HSL, HSL, HSL, HSL];
  };

  /** Syntax highlighting */
  syntax: {
    comment: HSL;
    keyword: HSL;
    string: HSL;
    function: HSL;
    number: HSL;
    operator: HSL;
    "operator-opacity"?: Opacity;
    deleted: HSL;
    "deleted-bg": HSL;
    "deleted-bg-opacity": Opacity;
    inserted: HSL;
    "inserted-bg": HSL;
    "inserted-bg-opacity": Opacity;
    coord: HSL;
    "coord-bg": HSL;
    "coord-bg-opacity": Opacity;
  };

  /** Visual effects */
  effects: {
    "scrollbar-opacity": Opacity;
    "scrollbar-hover-opacity": Opacity;
    "overlay-opacity": Opacity;
    "preview-opacity": Opacity;
    "shadow-color": HSL;
    "shadow-blur": CSSLength;
  };

  /** Spacing & sizing */
  spacing: {
    "toolbar-height": CSSLength;
    "split-width": CSSLength;
    "scrollbar-width": CSSLength;
  };

  /** Typography */
  typography: {
    "font-family-mono": string;
    "font-size-base": CSSLength;
    "font-size-code": CSSLength;
    "line-height-base": string;
    "line-height-code": string;
  };

  /** Geometry */
  geometry: {
    radius: CSSLength;
  };
}

/**
 * Chart colors (optional, separate for flexibility)
 */
export interface ChartColors {
  1: HSL;
  2: HSL;
  3: HSL;
  4: HSL;
  5: HSL;
}

/**
 * Complete theme with chart colors
 */
export interface GrimoireThemeComplete extends GrimoireTheme {
  charts: ChartColors;
}

Usage Guidelines

HSL Format Requirements

Correct Format:

"270 100% 70%"           // Basic color
"270 100% 70% / 0.5"     // With inline opacity

Wrong:

"hsl(270 100% 70%)"      // Don't wrap in hsl()
"270, 100%, 70%"         // Don't use commas
"#B388FF"                // Don't use hex
"rgb(179, 136, 255)"     // Don't use RGB

Rationale: CSS variables need unwrapped values for flexible composition:

/* ✅ This works */
color: hsl(var(--accent));
background: hsl(var(--accent) / 0.1);

/* ❌ This doesn't */
color: var(--accent);  /* Missing hsl() */

Token Naming Conventions

  1. Semantic over Visual:

    • accent (semantic: highlight/emphasis)
    • purple (visual: describes appearance)
  2. Purpose over Position:

    • card-foreground (purpose: text on cards)
    • text-2 (position: arbitrary numbering)
  3. Component-Specific when Needed:

    • toolbar-height (specific to toolbars)
    • height-1 (generic, unclear usage)

CSS Variable Usage

Colors:

/* ✅ Correct */
color: hsl(var(--foreground));
background: hsl(var(--background));
border: 1px solid hsl(var(--border));

/* With opacity */
background: hsl(var(--accent) / 0.1);
color: hsl(var(--foreground) / var(--scrollbar-opacity));

/* ❌ Wrong */
color: var(--foreground);                    /* Missing hsl() */
background: hsl(var(--background), 0.9);     /* Wrong opacity syntax */

Gradients:

/* ✅ Correct */
background: linear-gradient(
  to bottom,
  hsl(var(--gradient-brand-1)),
  hsl(var(--gradient-brand-2)),
  hsl(var(--gradient-brand-3)),
  hsl(var(--gradient-brand-4))
);

/* ❌ Wrong */
background: var(--gradient-brand);  /* No single gradient variable exists */

Spacing & Sizing:

/* ✅ Correct - direct usage */
height: var(--toolbar-height);
width: var(--split-width);

/* ✅ Correct - with calc */
padding: calc(var(--toolbar-height) / 2);

Contrast Requirements

WCAG AA Standards (minimum):

  • Normal text (<18pt): 4.5:1 contrast ratio
  • Large text (≥18pt or bold ≥14pt): 3:1 contrast ratio
  • UI components: 3:1 contrast ratio

Testing:

import { calculateContrast } from "@/lib/theme-utils";

// Check contrast
const ratio = calculateContrast(
  theme.colors.foreground,
  theme.colors.background
);

if (ratio < 4.5) {
  console.warn("Insufficient contrast for normal text");
}

Common Fixes:

  • Light themes: Darken accent colors (50% instead of 70% lightness)
  • Dark themes: Lighten accent colors (70% instead of 50% lightness)
  • Always test with actual text, not just theory

Theme Creation Workflow

1. Start from Existing Theme

import { GRIMOIRE_DARK } from "@/lib/themes/grimoire-dark";

const MY_THEME: GrimoireThemeComplete = {
  ...GRIMOIRE_DARK,  // Start with dark theme
  id: "my-custom-theme",
  name: "My Custom Theme",
  // Override specific tokens...
};

2. Modify Colors

Focus on semantic tokens first:

colors: {
  ...GRIMOIRE_DARK.colors,
  background: "210 30% 10%",    // Slightly different blue
  accent: "150 80% 60%",         // Green accent instead of purple
}

3. Adjust Syntax Highlighting

Match your color scheme:

syntax: {
  ...GRIMOIRE_DARK.syntax,
  keyword: "150 80% 60%",        // Green keywords to match accent
  deleted: "0 80% 70%",          // Brighter red
}

4. Update Gradients

Keep brand consistency or create new:

gradients: {
  brand: [
    "150 80% 60%",  // Green
    "180 70% 50%",  // Cyan
    "210 60% 50%",  // Blue
    "240 70% 60%",  // Purple
  ],
}

5. Validate Theme

import { validateTheme } from "@/lib/theme-utils";

const validation = validateTheme(MY_THEME);

if (!validation.valid) {
  console.error("Theme validation failed:", validation.errors);
}

if (validation.warnings.length > 0) {
  console.warn("Theme warnings:", validation.warnings);
}

6. Test Visually

  • Load theme in browser
  • Check all UI components
  • Test with real content
  • Verify contrast with browser DevTools
  • Test in different screen sizes

7. Export & Share

import { exportTheme } from "@/lib/theme-utils";

const json = exportTheme(MY_THEME);
// Save to file or share with others

File Structure

src/
├── types/
│   └── theme.ts                    TypeScript interfaces
│
├── lib/
│   ├── themes/
│   │   ├── index.ts                Theme registry & utilities
│   │   ├── grimoire-dark.ts        Dark theme definition (const)
│   │   └── grimoire-light.ts       Light theme definition (const)
│   │
│   └── theme-utils.ts              Utilities (validate, convert, etc.)
│
├── index.css                       CSS variables (all tokens)
└── styles/
    └── prism-theme.css             Syntax highlighting styles

Implementation Phases

Phase 1: Type System

  • Create src/types/theme.ts
  • Define all interfaces
  • Document token types

Phase 2: Theme Definitions

  • Create src/lib/themes/grimoire-dark.ts
  • Create src/lib/themes/grimoire-light.ts
  • Create src/lib/themes/index.ts (registry)
  • Add complete token values for both themes

Phase 3: CSS Migration

  • Add all tokens to :root in src/index.css
  • Add all tokens to .dark in src/index.css
  • Replace hardcoded colors with token references
  • Update src/styles/prism-theme.css to use syntax tokens
  • Update gradient usage in components

Phase 4: Utilities

  • Implement themeToCSSVariables() in src/lib/theme-utils.ts
  • Implement applyTheme() for dynamic theme switching
  • Implement validateTheme() with contrast checking
  • Implement exportTheme() / importTheme() for sharing
  • Implement calculateContrast() (WCAG compliance)

Phase 5: Testing

  • Unit tests for validation functions
  • Unit tests for conversion utilities
  • Visual testing in both themes
  • Contrast testing with automated tools
  • Cross-browser compatibility testing

Benefits of This System

  1. Complete Abstraction: All visual properties tokenized, no hardcoded values
  2. Type Safety: TypeScript enforces token structure and catches errors
  3. Validation: Automated checks for completeness and contrast
  4. Extensibility: Easy to add new themes (just define tokens)
  5. Portability: Import/export themes as JSON
  6. Maintainability: Single source of truth for visual properties
  7. Consistency: Enforced visual coherence across all components
  8. Accessibility: Built-in contrast validation (WCAG compliance)
  9. Future-Proof: Architecture supports user-defined themes
  10. Developer Experience: IntelliSense, autocomplete, documentation built-in

Next Steps

  1. Review this design - Confirm token structure meets requirements
  2. Approve naming conventions - Ensure token names are clear and semantic
  3. Begin Phase 2 - Create TypeScript theme definition files
  4. Validate with real data - Test token system with actual component usage
  5. Iterate - Refine token structure based on implementation feedback

References


Document Version: 1.0 Created: 2025-12-15 Author: Claude (ultrathink analysis) Status: Design Complete - Ready for Implementation