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
-
Semantic over Visual:
- ✅
accent(semantic: highlight/emphasis) - ❌
purple(visual: describes appearance)
- ✅
-
Purpose over Position:
- ✅
card-foreground(purpose: text on cards) - ❌
text-2(position: arbitrary numbering)
- ✅
-
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
:rootinsrc/index.css - Add all tokens to
.darkinsrc/index.css - Replace hardcoded colors with token references
- Update
src/styles/prism-theme.cssto use syntax tokens - Update gradient usage in components
Phase 4: Utilities
- Implement
themeToCSSVariables()insrc/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
- Complete Abstraction: All visual properties tokenized, no hardcoded values
- Type Safety: TypeScript enforces token structure and catches errors
- Validation: Automated checks for completeness and contrast
- Extensibility: Easy to add new themes (just define tokens)
- Portability: Import/export themes as JSON
- Maintainability: Single source of truth for visual properties
- Consistency: Enforced visual coherence across all components
- Accessibility: Built-in contrast validation (WCAG compliance)
- Future-Proof: Architecture supports user-defined themes
- Developer Experience: IntelliSense, autocomplete, documentation built-in
Next Steps
- Review this design - Confirm token structure meets requirements
- Approve naming conventions - Ensure token names are clear and semantic
- Begin Phase 2 - Create TypeScript theme definition files
- Validate with real data - Test token system with actual component usage
- Iterate - Refine token structure based on implementation feedback
References
- WCAG 2.1 Guidelines: https://www.w3.org/WAI/WCAG21/Understanding/
- Design Tokens W3C Community: https://design-tokens.github.io/community-group/
- HSL Color Model: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
- CSS Custom Properties: https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
- Tailwind Color System: https://tailwindcss.com/docs/customizing-colors
- Radix Themes: https://www.radix-ui.com/themes/docs/theme/overview
Document Version: 1.0 Created: 2025-12-15 Author: Claude (ultrathink analysis) Status: Design Complete - Ready for Implementation