Skip to content

theojluciano/OpticsThemeBuilder

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

36 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

image image

OpticsThemeBuilder 🎨

Generate accessible color palettes for Figma with automatic foreground color selection and WCAG contrast checking.

Features both a visual web UI for interactive editing and a CLI tool for automation.

Recent Updates ✨

v2.2 - Three-Tier Contrast Indicators (Current)

  • 🎯 AAA/AA/Fail System: Visual indicators now show three levels of WCAG compliance instead of just pass/fail
  • 🟒 AAA (β‰₯7:1): Green indicators for contrast ratios that meet strict AAA standards
  • 🟑 AA-only (β‰₯4.5:1, <7:1): Yellow/amber indicators for ratios that meet AA but not AAA standards
  • πŸ”΄ Fail (<4.5:1): Red indicators for ratios below minimum accessibility standards
  • 🏷️ Level Labels: Contrast ratios display with "AAA" or "AA" badges for quick identification
  • 🎨 Smart Borders: Card borders reflect worst-case status (red overrides yellow, yellow overrides green)
  • πŸ“Š Enhanced Summaries: Stats now show AAA count / AA count / Fail count (e.g., "24 / 8 / 6")
  • πŸ”§ DRY Refactoring: Extracted shared contrast utilities reducing duplication by 75+ lines
  • 🎨 Status Token Added: New --op-status-warning design token for consistent yellow/amber indicators

v2.1 - Code Refactoring & New Utilities

  • πŸ”§ Major Refactoring: Cleaner, DRY codebase with ~200 lines reduced while adding utilities
  • 🎨 Unified Color Builder: New createColorValue() utility eliminates duplication across 5+ locations
  • πŸ“Š Consolidated Scale Data: Optics scale data reduced from 6 objects (140 lines) to 1 structure (70 lines)
  • ⚑ Shared Utilities: New contrast failure collection and batch file export utilities
  • πŸ”„ Improved Parsing: HSL input now handles both normalized (0-1) and percentage (0-100%) formats
  • πŸ“¦ Better Patterns: Builder, Factory, and functional programming patterns throughout
  • βœ… 100% Test Coverage: All 169 tests passing with clean TypeScript compilation

v2.0 - Major UI Redesign

  • 🎨 Multiple Color Types: Manage Primary, Neutral, Secondary, Notice, Warning, Danger, Info, and custom color types in one workspace
  • πŸŽ›οΈ Per-Type Controls: Independent H/S color pickers for each color type
  • πŸ“¦ Unified Export: All enabled color types export to a single optics-{mode}.tokens.json file
  • 🧩 Collapsible Sections: Clean, organized interface with expand/collapse for each color type
  • πŸ“Š Inline Summaries: Per-type contrast summaries show pass/fail counts at a glance
  • β˜€οΈπŸŒ™ Header Controls: Mode toggle and export moved to header for better space utilization
  • 🎯 Consistent Styling: Centralized color picker styles in shared module for uniform appearance
  • βž• Custom Types: Add, rename, and delete custom color types as needed
  • 🎨 Optics Design System: UI now uses the Optics design system with semantic design tokens, including 4 color palettes (Primary, Neutral, Success, Danger) with 19 stops each, built-in light/dark mode support using CSS light-dark(), and semantic tokens for spacing (x-small, medium, large), typography, borders, and interactions

Table of Contents

Quick Start

Interactive UI (Recommended for visual editing)

cd optics-ui
npm install
npm run dev
# Open http://localhost:5173 in your browser
# Requires a modern browser (Chrome, Firefox, Safari, or Edge)

Then use the visual editor to:

  • Manage multiple color types (Primary, Neutral, Secondary, Notice, Warning, Danger, Info)
  • Add custom color types with independent color controls
  • Adjust luminosity for all 19 Optics stops with live contrast checking
  • Configure Light and Dark modes independently
  • Export all enabled color types to a single Figma tokens JSON file

CLI (For automation and scripting)

# Install globally
npm install -g opticsthemebuilder

# Generate an Optics palette
optics generate "#3b82f6" --name "primary" --optics

# Or run locally without installing
cd OpticsThemeBuilder
npm install
npm run build
node dist/cli.js generate "#3b82f6" --name "primary" --optics

Features

  • πŸ–₯️ Interactive Web UI: Svelte-based visual editor with multiple color type management, collapsible sections, live WCAG contrast checking, and real-time luminosity adjustments for all 19 Optics stops
  • 🎨 Multiple Color Types: Built-in support for Primary, Neutral, Secondary, Notice, Warning, Danger, and Info color scales, plus the ability to add custom color types
  • πŸŽ›οΈ Per-Type Color Controls: Each color type has independent hue and saturation controls with visual color pickers
  • πŸŒ“ Optics Scale Format: 19 semantic stops with light/dark mode support (plus-max to minus-max)
  • 🎨 Optics Design System: UI built with semantic design tokens - 4 color palettes (Primary, Neutral, Success, Danger) with 19 stops each, t-shirt sizing for spacing (x-small, medium, large), typography tokens, and built-in light/dark theme support via CSS light-dark() function
  • β™Ώ WCAG Compliant: Automatically calculates and validates contrast ratios (AA & AAA) with visual pass/fail indicators and per-type summaries
  • πŸ”„ Dual Foregrounds: Provides light and dark foreground options for each background, plus alternative foregrounds
  • πŸ“¦ Unified Figma Export: Export all enabled color types to a single Figma tokens JSON file per mode (light/dark)
  • πŸš€ CLI & Library: Use as a command-line tool or import into your Node.js project for automation
  • 🎯 Smart Color Scaling: Generates 16 perceptually-distributed color stops from any base color (CLI)

Installation

Interactive UI

No global installation needed. Clone the repo and run:

git clone <repository-url>
cd OpticsThemeBuilder/optics-ui
npm install
npm run dev

The UI uses the Optics design system with comprehensive documentation:

  • OPTICS_TOKEN_REFERENCE.md - Complete token reference guide
  • QUICK_REFERENCE.md - Printable cheat sheet
  • OPTICS_CSS_MIGRATION.md - Design system implementation details

CLI Tool

Install globally for command-line usage:

npm install -g opticsthemebuilder

Or use locally in a project:

npm install opticsthemebuilder

Interactive UI Usage

Managing Color Types:

  • Each color type (Primary, Neutral, Secondary, etc.) has its own collapsible section
  • Toggle color types on/off using the switch in each section header
  • Click section header to expand/collapse and view all 19 color stops
  • Each section shows a contrast summary (e.g., "32 / 38" = 32 passing tests out of 38 total)

Color Controls:

  • Each color type has independent H (Hue: 0-360Β°) and S (Saturation: 0-100%) controls in its header
  • Use the color picker for visual selection or type values directly
  • Changes apply to all stops within that color type

Adding Custom Color Types:

  • Click "+ Add Custom Color Type" at the bottom
  • Enter a name (e.g., "Accent", "Brand")
  • Set the base color using the picker or H/S values
  • Custom types can be renamed or deleted

Adjusting Luminosity: Each color stop card has three sliders:

  • Background: Background color lightness (0-100%)
  • On: Primary foreground lightness for text/icons
  • On-Alt: Alternative foreground option
  • Each displays live contrast ratios with pass/fail indicators (4.5:1 minimum for AA)

Light/Dark Modes:

  • Toggle between Light and Dark modes using the sun/moon icons in the header
  • Each mode has independent luminosity values for all stops
  • Each color type shows its own contrast summary

Export:

  • Click "Export for Figma" in the header
  • Downloads a single optics-{mode}.tokens.json file (e.g., optics-light.tokens.json)
  • The file contains all enabled color types with their current mode settings
  • Import the file directly into Figma as Variables

Understanding Contrast Indicators:

  • Green (AAA): Contrast ratio β‰₯7:1 - meets strict AAA standards, displays "AAA" badge
  • Yellow (AA): Contrast ratio β‰₯4.5:1 but <7:1 - meets minimum AA standards, displays "AA" badge
  • Red (Fail): Contrast ratio <4.5:1 - fails accessibility standards, no badge
  • Card borders reflect the worst test result (red overrides yellow, yellow overrides green)
  • Summary shows three counts: AAA / AA / Fail (e.g., "24 / 8 / 6" = 24 AAA, 8 AA-only, 6 failures)

Tips for achieving WCAG compliance:

  • Light backgrounds (L>50%) need dark foregrounds (L<40%)
  • Dark backgrounds (L<50%) need light foregrounds (L>60%)
  • Aim for 40%+ lightness difference for AA, 60%+ for AAA
  • Adjust sliders to turn yellow indicators green for better accessibility
  • Use the contrast summary to track AAA/AA/Fail counts per color type

UI Design System: The interactive UI is built using the Optics design system it generates:

  • 4 Color Palettes: Primary (blue), Neutral (gray), Success (green), Danger (red), each with 19 semantic stops
  • Semantic Tokens: Surface, text, border, and interaction tokens for consistent styling
  • Spacing Scale: T-shirt sizing from 3x-small (2px) to 5x-large (64px)
  • Typography System: Font sizes, weights, and line heights for visual hierarchy
  • Light/Dark Mode: Built-in theme support using CSS light-dark() function
  • Documentation: See optics-ui/OPTICS_TOKEN_REFERENCE.md for complete token reference

CLI Usage

Generate a complete color palette from a single color:

# Standard format (16 stops)
optics generate "#3b82f6" --name "blue"

# Optics format (19 stops with light/dark mode)
optics generate "#3b82f6" --name "blue" --optics

This creates:

  • blue-figma.json - Figma Variables format (standard palette)
  • blue-light.tokens.json / blue-dark.tokens.json - Figma Variables format (Optics format, separate files for each mode)
  • blue-contrast-report.txt - WCAG contrast analysis report

Optics Format - Use --optics flag for semantic naming (plus-max, base, minus-max) with built-in light/dark mode support. See OPTICS_FORMAT.md for details.

Advanced CLI Options

# Standard format with custom options
optics generate "#3b82f6" \
  --name "primary" \
  --stops 16 \
  --output ./themes

# Optics format
optics generate "#3b82f6" \
  --name "primary" \
  --optics \
  --output ./themes

# Generate Figma Variables for Light mode only (Optics)
optics generate "#3b82f6" \
  --name "primary" \
  --optics \
  --mode light

# Generate both Light and Dark modes (default for Optics)
optics generate "#3b82f6" \
  --name "primary" \
  --optics \
  --mode both

Options:

  • --name, -n: Name for the palette (default: "palette")
  • --stops, -s: Number of color stops (default: 16, range: 2-100, ignored with --optics)
  • --output, -o: Output directory (default: "./output")
  • --mode, -m: Mode for Figma export (Optics only) - light, dark, or both (default: "both")
  • --optics: Generate using Optics scale format (19 stops with light-dark mode)

Understanding Contrast Reports

CLI Reports

Every CLI-generated palette includes a contrast report showing which color combinations meet WCAG standards:

## ⚠️  FAILURES SUMMARY

Found 3 combinations that FAIL WCAG AA standard (4.5:1):

❌ Dark Mode β€’ primary/minus-four (L:1%) β†’ primary/minus-four-on (L:0%)
   Background: #156af4 β†’ Foreground: #00040a
   Contrast: 4.30:1 (needs 4.5:1 minimum)

The report uses Optics naming (e.g., primary/minus-four-on) so you can immediately identify which stops need adjustment without matching hex codes.

Interactive UI Contrast Checking

The web UI provides real-time WCAG contrast validation with three-tier visual feedback:

  • Three-tier indicators:
    • 🟒 Green (AAA): β‰₯7:1 contrast ratio with "AAA" badge
    • 🟑 Yellow (AA): β‰₯4.5:1 but <7:1 with "AA" badge
    • πŸ”΄ Red (Fail): <4.5:1, no badge
  • Smart card borders: Overall border color reflects worst test (red > yellow > green)
  • Live contrast ratios: Each color stop card displays contrast for both "on" and "on-alt" foregrounds
  • Enhanced summaries: Shows AAA count / AA count / Fail count (e.g., "24 / 8 / 6")
  • Summary dashboard: Section-level and global statistics with color-coded counts
  • Mode-specific: Independent contrast checking for Light and Dark modes
  • Instant feedback: Contrast recalculates as you adjust luminosity sliders

This allows you to fine-tune your palette interactively to meet WCAG AA standards or exceed them with AAA compliance.

CLI Contrast Analysis

optics analyze "#3b82f6" "#ffffff"
# Output: Contrast ratio, WCAG AA/AAA pass/fail

Library Usage

Generate a Palette

import { generatePalette, exportToFigma, saveToFile } from 'opticsthemebuilder';

// Generate from hex color
const palette = generatePalette('#3b82f6', 'blue', 16);

console.log(palette.baseColor.hex); // "#3b82f6"
console.log(palette.stops.length);  // 16

// HSL input now supports both formats
const palette1 = generatePalette({ h: 220, s: 0.8, l: 0.5 }, 'blue'); // Normalized (0-1)
const palette2 = generatePalette({ h: 220, s: 80, l: 50 }, 'blue');   // Percentages (0-100)

// Each stop contains:
palette.stops.forEach(stop => {
  console.log({
    index: stop.stop,
    background: stop.background.hex,
    lightForeground: stop.foregrounds.light.hex,
    darkForeground: stop.foregrounds.dark.hex,
    recommended: stop.recommendedForeground,
    lightContrast: stop.foregrounds.light.contrast,
    darkContrast: stop.foregrounds.dark.contrast,
    wcagAA: stop.foregrounds.light.wcagAA
  });
});

Export to Different Formats

import { 
  generatePalette, 
  exportToFigma, 
  exportToJSON,
  exportToCSS,
  exportToTailwind,
  exportToDesignTokens,
  exportAll 
} from 'opticsthemebuilder';

const palette = generatePalette('#ff6b6b', 'red');

// Export to Figma Variables format (Light mode)
const figmaDataLight = exportToFigma(palette, 'Light');
console.log(JSON.stringify(figmaDataLight, null, 2));

// Export to Figma Variables format (Dark mode)
const figmaDataDark = exportToFigma(palette, 'Dark');
console.log(JSON.stringify(figmaDataDark, null, 2));

// Export to W3C Design Tokens format
const designTokens = exportToDesignTokens(palette);

// Export to JSON
const json = exportToJSON(palette);

// Export to CSS
const css = exportToCSS(palette);

// Export to Tailwind
const tailwind = exportToTailwind(palette);

// Export all formats to files
const files = exportAll(palette, './output');
console.log('Generated files:', files);

Check Contrast

import { getContrastRatio, meetsWCAG_AA, meetsWCAG_AAA } from 'opticsthemebuilder';
import { converter } from 'culori';

const toRgb = converter('rgb');

const background = toRgb('#3b82f6');
const foreground = toRgb('#ffffff');

const ratio = getContrastRatio(background, foreground);
console.log(`Contrast: ${ratio.toFixed(2)}:1`);

const passesAA = meetsWCAG_AA(ratio);
const passesAAA = meetsWCAG_AAA(ratio);

console.log('WCAG AA:', passesAA ? 'Pass' : 'Fail');
console.log('WCAG AAA:', passesAAA ? 'Pass' : 'Fail');

Advanced Utilities

Unified Color Builder

New in v2.1: Create complete color values in one function call.

import { createColorValue } from 'opticsthemebuilder';

// Create a color with HSL, RGB, and hex all at once
const color = createColorValue(220, 80, 50); // h, s% (0-100), l% (0-100)

console.log(color.hsl); // { h: 220, s: 0.8, l: 0.5 }
console.log(color.rgb); // { r: 0.2, g: 0.4, b: 0.8 }
console.log(color.hex); // "#3366CC"

Contrast Failure Collection

Utilities for checking and collecting WCAG contrast failures:

import { 
  collectFailureIfNeeded, 
  createContrastFailure,
  passesWCAG_AA 
} from 'opticsthemebuilder';

// Collect failures automatically
const failure = collectFailureIfNeeded(
  3.2,                    // contrast ratio
  'Light',                // mode
  'primary/base',         // stop identifier
  { hex: '#3366CC', hsl: { h: 220, s: 0.8, l: 0.5 } }, // background
  { hex: '#FFFFFF', hsl: { h: 0, s: 0, l: 1.0 } },     // foreground
  'on'                    // foreground type
);

if (failure) {
  console.log('WCAG AA failure detected:', failure);
}

// Or create failures manually
const customFailure = createContrastFailure(
  'Dark', 'secondary/plus-two', bg, fg, 'on-alt', 2.8
);

Batch File Export

Export multiple files with consistent error handling:

import { exportFiles, FileExport } from 'opticsthemebuilder';

const files: FileExport[] = [
  {
    label: 'Figma Light Mode',
    filename: 'primary-light.tokens.json',
    content: JSON.stringify(lightModeData)
  },
  {
    label: 'Figma Dark Mode',
    filename: 'primary-dark.tokens.json',
    content: JSON.stringify(darkModeData)
  },
  {
    label: 'Contrast Report',
    filename: 'contrast-report.txt',
    content: reportContent
  }
];

const exportedPaths = exportFiles('./output', files);
console.log('Exported:', exportedPaths);

Enhanced Color Utilities

import { 
  parseColor,     // Now handles both normalized and percentage HSL
  createHsl,      // Create HSL from percentage values
  hslToRgb,       // Convert HSL to RGB
  rgbToHsl        // Convert RGB to HSL
} from 'opticsthemebuilder';

// parseColor now auto-detects format
const color1 = parseColor({ h: 220, s: 0.8, l: 0.5 });  // Normalized
const color2 = parseColor({ h: 220, s: 80, l: 50 });    // Percentages
const color3 = parseColor('#3366CC');                    // Hex string
const color4 = parseColor('hsl(220, 80%, 50%)');        // HSL string

Complete Workflow Example

Here's how the new utilities work together in a real-world scenario:

import {
  createColorValue,
  collectFailureIfNeeded,
  exportFiles,
  generateOpticsPalette
} from 'opticsthemebuilder';

// 1. Generate custom color stops using the unified builder
const customStops = [
  createColorValue(220, 80, 95),  // Very light
  createColorValue(220, 80, 70),  // Medium light
  createColorValue(220, 80, 50),  // Base
  createColorValue(220, 80, 30),  // Medium dark
  createColorValue(220, 80, 15)   // Very dark
];

// 2. Check contrast and collect failures
const failures = [];
customStops.forEach((bg, index) => {
  const whiteFg = createColorValue(0, 0, 100);
  const blackFg = createColorValue(0, 0, 0);
  
  // Check white foreground
  const whiteFailure = collectFailureIfNeeded(
    calculateContrast(bg, whiteFg),
    'Light',
    `stop-${index}`,
    bg,
    whiteFg,
    'white'
  );
  if (whiteFailure) failures.push(whiteFailure);
  
  // Check black foreground
  const blackFailure = collectFailureIfNeeded(
    calculateContrast(bg, blackFg),
    'Light',
    `stop-${index}`,
    bg,
    blackFg,
    'black'
  );
  if (blackFailure) failures.push(blackFailure);
});

// 3. Generate full Optics palette
const palette = generateOpticsPalette('#3366CC', 'primary');

// 4. Export everything in one batch
const files = [
  {
    label: 'Figma Light Mode',
    filename: 'primary-light.tokens.json',
    content: exportOpticsToFigma(palette, 'Light')
  },
  {
    label: 'Figma Dark Mode',
    filename: 'primary-dark.tokens.json',
    content: exportOpticsToFigma(palette, 'Dark')
  },
  {
    label: 'Contrast Report',
    filename: 'contrast-report.txt',
    content: generateReport(failures)
  }
];

const paths = exportFiles('./output', files);
console.log('βœ… Exported:', paths);

Color Input Formats

OpticsThemeBuilder accepts any valid CSS color format:

  • Hex: #3b82f6, #38f
  • RGB: rgb(59, 130, 246), rgba(59, 130, 246, 0.5)
  • HSL: hsl(217, 91%, 60%), hsla(217, 91%, 60%, 0.5)
  • HSL Object: { h: 217, s: 91, l: 60 } or { h: 217, s: 0.91, l: 0.6 } (both normalized and percentage formats)
  • Named Colors: blue, rebeccapurple, hotpink

Understanding the Output

Color Stops

Each palette contains 16 stops (by default) distributed from light to dark:

  • Stop 0-3: Very light shades (backgrounds, subtle UI)
  • Stop 4-7: Light to medium shades (hover states, secondary UI)
  • Stop 8-11: Medium to dark shades (primary buttons, active states)
  • Stop 12-15: Very dark shades (text, high contrast elements)

Figma Variables Import

The generated .tokens.json files use Figma's native Variables import format:

  1. Open Figma Variables Panel

    • Menu β†’ Libraries β†’ Variables
  2. Import Variables

    • Click "..." menu β†’ "Import variables"
    • Select your palette-light.tokens.json file
    • Import to create Light mode
  3. Import Dark Mode (optional)

    • Select your palette-dark.tokens.json file
    • Import to the same collection
    • Figma will automatically add the Dark mode

The format follows Figma's exact specification with:

  • Nested variable structure for organization
  • Color values in sRGB space (0-1 range)
  • Per-mode file structure
  • Proper $extensions metadata

See FIGMA_VARIABLES_FORMAT.md for complete format documentation.

Foreground Colors

For each background stop, you get two foreground options:

  • Light Foreground: Near-white color with subtle hue matching the background
  • Dark Foreground: Near-black color with subtle hue matching the background

The recommendedForeground field tells you which has better contrast.

WCAG Compliance

  • WCAG AA: Minimum 4.5:1 contrast for normal text, 3:1 for large text
  • WCAG AAA: Minimum 7:1 contrast for normal text, 4.5:1 for large text

Export Formats

Figma Variables (-figma.json)

Import directly into Figma using the Variables panel:

How to Import:

  1. Open your Figma file
  2. Open the Variables panel (right sidebar)
  3. Click the "Import" button
  4. Select your *-figma.json file
  5. All color variables will be imported into a new collection

The file structure matches Figma's Variables API format:

{
  "variableCollections": {
    "VariableCollectionId:blue": {
      "name": "blue",
      "modes": [...],
      "variableIds": [...]
    }
  },
  "variables": {
    "VariableID:blue/0": {
      "name": "blue/0",
      "resolvedType": "COLOR",
      "valuesByMode": {
        "default": {
          "r": 0.922,
          "g": 0.943,
          "b": 0.977,
          "a": 1
        }
      },
      "description": "Background color - Stop 0"
    }
  }
}

Each palette creates:

  • Background colors: blue/0 through blue/15
  • Light foregrounds: blue/0/fg-light through blue/15/fg-light
  • Dark foregrounds: blue/0/fg-dark through blue/15/fg-dark

JSON (.json)

Complete palette data with all color information:

{
  "name": "blue",
  "baseColor": {
    "hsl": { "h": 217, "s": 0.91, "l": 0.60 },
    "rgb": { "r": 0.231, "g": 0.510, "b": 0.965 },
    "hex": "#3b82f6"
  },
  "stops": [...]
}

CSS Custom Properties (.css)

Ready-to-use CSS variables:

:root {
  --blue-0: #ebf1f9;
  --blue-0-rgb: 235, 241, 249;
  --blue-0-hsl: 217, 55%, 95%;
  --blue-0-fg-light: #fafafa;
  --blue-0-fg-dark: #131416;
  --blue-0-fg: #131416; /* recommended */
}

Tailwind Config (.js)

Add to your tailwind.config.js:

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        'blue': {
          0: '#ebf1f9',
          1: '#e7eef9',
          // ... etc
        }
      }
    }
  }
}

Use Cases

Design Systems

Generate consistent color scales for your entire design system:

optics generate "#3b82f6" --name "primary" --output ./design-tokens
optics generate "#10b981" --name "success" --output ./design-tokens
optics generate "#ef4444" --name "error" --output ./design-tokens
optics generate "#f59e0b" --name "warning" --output ./design-tokens

Accessibility Audits

Quickly check if your color combinations meet WCAG standards:

optics analyze "#your-brand-blue" "#ffffff"

Figma Variables Import

Generate palettes and import them directly into Figma:

# Generate Figma-compatible file
optics generate "#3b82f6" --name "primary"

# Import the *-figma.json file in Figma:
# 1. Open Variables panel
# 2. Click "Import" button  
# 3. Select primary-figma.json
# 4. Your color system is now ready to use!

All variables include descriptions with contrast ratios and WCAG compliance information.

Dark Mode Themes

Generate complementary light and dark mode palettes by using different base lightness values.

Algorithm Details

Perceptual Distribution

Colors are distributed using an easing function to create more stops in the mid-range where the human eye is most sensitive to changes.

Saturation Adjustment

  • Very light colors (L > 90%): Saturation is reduced to avoid garish pastels
  • Very dark colors (L < 20%): Saturation is reduced to maintain depth
  • Mid-range colors: Saturation is maintained or slightly boosted

Foreground Generation

  • Light foregrounds: ~98% lightness, minimal saturation
  • Dark foregrounds: ~8% lightness, slight saturation for warmth
  • Both maintain the base color's hue for visual coherence

TypeScript Support

Full TypeScript definitions included:

import { ColorPalette, ColorStop, HSLColor } from 'opticsthemebuilder';

const palette: ColorPalette = generatePalette('#3b82f6');
const stop: ColorStop = palette.stops[0];
const hsl: HSLColor = stop.background.hsl;

Requirements & Dependencies

CLI & Library:

  • Node.js 14+, NPM/Yarn
  • Dependencies: culori (colors), commander (CLI)

Interactive UI:

  • Node.js 14+, NPM/Yarn, modern browser
  • Dependencies: svelte, culori, vite, typescript

Optics Scale Format

The Optics format provides an alternative color scale system with semantic naming and built-in light/dark mode support:

  • 19 semantic stops: plus-max, plus-eight, ..., base, ..., minus-eight, minus-max
  • Light/Dark modes: Automatic theme switching with CSS light-dark() function
  • Three colors per stop: background, "on" (primary foreground), "on-alt" (alternative foreground)
  • F-stop naming: Photography-inspired names for intuitive luminosity control

The Interactive UI uses the Optics format: The web interface is built with the Optics design system, featuring semantic design tokens for colors, spacing (t-shirt sizing: x-small, medium, large), typography, and more. See optics-ui/OPTICS_TOKEN_REFERENCE.md for the complete implementation.

Quick Start with Optics Format

# Generate Optics palette
optics generate "#3b82f6" --name "primary" --optics

# Use in CSS
:root {
  --op-color-primary-base: light-dark(hsl(217 91% 40%), hsl(217 91% 38%));
  --op-color-primary-on-base: light-dark(hsl(217 91% 100%), hsl(217 91% 100%));
}

Programmatic Usage

import { generateOpticsPalette, exportOpticsToCSS } from 'opticsthemebuilder';

const palette = generateOpticsPalette('#3b82f6', 'primary');
const css = exportOpticsToCSS(palette);

πŸ“– Full Documentation: See OPTICS_FORMAT.md for complete guide, examples, and API reference.

πŸ’‘ Examples: Run node example-optics.js to see comprehensive usage examples.

Figma Export Compatibility

βœ… Fully compatible with Figma Variables import

The --optics format generates Figma-compatible exports that match the Design Tokens structure expected by Figma's Variables plugin.

Import into Figma

  1. Generate your palette:

    optics generate "#2a659f" --name "primary" --optics --format figma
  2. In Figma:

    • Open the Variables panel (right sidebar)
    • Click the book icon (Import/Export)
    • Select Import variables
    • Choose your *-optics-figma.json file
    • Figma imports as a collection with Light/Dark modes
  3. Use in designs:

    • Select any frame or element
    • In color picker, click the πŸ”— icon
    • Choose from your imported variables
    • Switch between Light/Dark modes to see values update

What Gets Imported

  • Collection: Named after your palette (e.g., "Primary Colors")
  • Modes: Light and Dark (114 tokens total)
  • Tokens per mode: 57 variables
    • 19 background colors (*-bg)
    • 19 primary foregrounds (*-on)
    • 19 alternative foregrounds (*-on-alt)
  • Contrast info: Each foreground includes contrast ratio in description

Format Validation

The export format has been validated against Figma plugin exports:

  • βœ… Matches Figma's Design Tokens structure
  • βœ… Includes all required fields ($codeSyntax, $scopes, $type, $value)
  • βœ… Supports multi-mode theming (Light/Dark)
  • βœ… Compatible with Figma Variables import/export

πŸ“– See FORMAT_VALIDATION.md for detailed format comparison and validation.

Code Architecture

Refactoring Improvements (v2.1 & v2.2)

The codebase has undergone significant refactoring to improve maintainability, reduce duplication, and establish better patterns. See REFACTORING.md for complete details.

Key Architecture Decisions

1. Unified Color Builder Pattern

  • Single createColorValue() function replaces 5+ duplicate implementations
  • Generates HSL, RGB, and hex values in one call
  • Eliminates manual conversion chains

2. Consolidated Data Structures

  • Optics scale data: 6 separate objects (140 lines) β†’ 1 structured array (70 lines)
  • Related data kept together logically
  • Self-documenting configuration objects

3. Functional Programming Patterns

  • Declarative flatMap() instead of imperative loops
  • Immutable data transformations
  • Cleaner, more readable code flow

4. Shared Utilities

  • Contrast failure collection extracted to reusable functions
  • Batch file export with consistent error handling
  • Display logic separated from business logic

5. Contrast Level Utilities (v2.2)

  • getContrastLevel() - Single source of truth for AAA/AA/Fail determination
  • getContrastLabel() - Consistent badge display ("AAA", "AA", "")
  • getOverallStatus() - Priority-based status calculation (fail > aa > aaa)
  • calculateContrastStats() - DRY stats aggregation for all components
  • Eliminates 75+ lines of duplicate contrast logic

6. Enhanced Type Safety

  • Comprehensive TypeScript interfaces
  • ContrastLevel type for three-tier system
  • Better IntelliSense support
  • Compile-time error detection

Code Quality Metrics

  • Lines Reduced: ~275 lines removed while adding utilities (v2.1: 200, v2.2: 75)
  • Test Coverage: 100% maintained (169 tests passing)
  • Duplication: Major patterns eliminated across 8+ locations
  • Build Status: Clean TypeScript compilation with zero errors
  • Component Efficiency: ColorStopCard reduced from 40 to 9 lines of template markup

Design Patterns Used

  • Builder Pattern: createColorValue(), createFigmaColorValue()
  • Factory Pattern: Unified color creation with consistent output
  • Single Responsibility: Each utility has one clear purpose
  • DRY Principle: Code duplication eliminated throughout

File Organization

src/
β”œβ”€β”€ generator.ts              # Standard palette generation
β”œβ”€β”€ optics-generator.ts       # Optics scale generation (consolidated data)
β”œβ”€β”€ color-utils.ts            # Unified color builder & utilities
β”œβ”€β”€ contrast.ts               # WCAG contrast calculations
β”œβ”€β”€ contrast-report-utils.ts  # Shared reporting utilities
β”œβ”€β”€ exporter.ts               # Standard export formats
β”œβ”€β”€ optics-exporter.ts        # Optics export formats
β”œβ”€β”€ figma-utils.ts            # Figma token creation
β”œβ”€β”€ file-utils.ts             # File I/O and batch export
β”œβ”€β”€ console-utils.ts          # CLI output formatting

optics-ui/src/lib/
β”œβ”€β”€ utils/
β”‚   β”œβ”€β”€ colors.ts             # Color utilities + contrast level functions
β”‚   └── contrast-stats.ts     # Shared contrast statistics calculation
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ Summary.svelte        # Uses calculateContrastStats()
β”‚   β”œβ”€β”€ ColorTypeSection.svelte  # Uses calculateContrastStats()
β”‚   └── ColorStopCard.svelte  # Uses getContrastLevel() utilities
β”œβ”€β”€ cli.ts                    # Command-line interface
β”œβ”€β”€ index.ts                  # Public API exports
└── types.ts                  # TypeScript type definitions

Best Practices

  1. Consistent Color Handling: All color operations use the unified builder
  2. Declarative Style: Functional array methods over imperative loops
  3. Shared Logic: Common patterns extracted to utilities
  4. Type Safety: Full TypeScript coverage with no any types
  5. Test Coverage: Every utility function has comprehensive tests

Extensibility

The refactored architecture makes it easy to:

  • Add new color scale formats
  • Implement custom export formats
  • Create plugin systems
  • Extend WCAG validation rules
  • Add performance optimizations

Testing

OpticsThemeBuilder includes a comprehensive test suite ensuring code quality and reliability.

Test Suite Overview

Test Suites: 6 passed, 6 total
Tests:       169 passed, 169 total
Coverage:    100% (all tested modules)
Execution:   ~1.5-2 seconds

What's Tested

βœ… Color Utilities (32 tests)

  • Color parsing (hex, RGB, HSL, named colors)
  • Color space conversions (HSL ↔ RGB)
  • Type conversions and edge cases

βœ… Contrast Calculations (24 tests)

  • WCAG 2.0 compliant luminance calculations
  • Contrast ratios (1:1 to 21:1)
  • AA/AAA compliance checking

βœ… File Operations (21 tests)

  • File I/O with directory creation
  • JSON formatting
  • Special character handling

βœ… Figma Format (31 tests)

  • Color conversion to Figma sRGB
  • Token creation and validation
  • Variable ID generation

βœ… Contrast Reports (28 tests)

  • Report generation and formatting
  • Failure summaries
  • WCAG level determination

βœ… Palette Generation (43 tests)

  • Color stop creation
  • Lightness distribution
  • Saturation adjustment
  • Foreground selection

Running Tests

# Run all tests
npm test

# Run with coverage report
npm run test:coverage

# Watch mode (for development)
npm run test:watch

# Run specific test suite
npm test -- contrast.test

# Verbose output
npm test -- --verbose

Coverage Report

All core modules have 100% coverage:

File                      | % Stmts | % Branch | % Funcs | % Lines |
--------------------------|---------|----------|---------|---------|
color-utils.ts            |     100 |      100 |     100 |     100 |
contrast.ts               |     100 |      100 |     100 |     100 |
file-utils.ts             |     100 |      100 |     100 |     100 |
figma-utils.ts            |     100 |      100 |     100 |     100 |
contrast-report-utils.ts  |     100 |      100 |     100 |     100 |
generator.ts              |     100 |      100 |     100 |     100 |

Test Documentation

For detailed information about the test suite:

Quality Assurance

The test suite ensures:

  • βœ… Accurate color calculations - WCAG compliant contrast ratios
  • βœ… Reliable file operations - Safe I/O with edge case handling
  • βœ… Valid Figma exports - Correct token structure and format
  • βœ… Consistent palette generation - Deterministic color scales
  • βœ… No regressions - All changes validated automatically

License

ISC

Troubleshooting

UI Issues:

  • Sliders not responding? Refresh page or restart dev server (npm run dev in optics-ui/)
  • Can't achieve 4.5:1 contrast? Increase lightness difference between bg/fg (40%+ recommended)
  • Export downloads one file per color type? Make sure you're using the latest version (now exports all enabled types to one file)
  • Color type not showing in export? Check that it's toggled on (switch in section header)
  • Color picker values not updating? Try using the number inputs directly or refresh the page

CLI Issues:

  • Type errors? Run npm run check to validate TypeScript
  • Colors look wrong? Verify H (0-360) and S (0-100) are in valid ranges

Architecture Notes:

  • Color picker styles are centralized in src/lib/styles/shared.module.css for consistency
  • Each color type is managed independently with its own H/S values
  • Export format combines all enabled types into a single JSON structure per mode

Contributing

Contributions welcome! Please open an issue or PR.

Before contributing, please review:

Code Style:

  • Follow existing patterns (Builder, Factory, DRY principles)
  • Use TypeScript with full type coverage
  • Add tests for new functionality
  • Keep utilities focused and reusable

Author

Built with ❀️ for designers and developers who care about accessibility.


Pro Tips:

  • Start with medium saturation (70-90%) for best results
  • For CLI: Use medium lightness (40-60%) base colors to generate full tint/shade ranges
  • Achieving WCAG AA requires 40%+ lightness difference between backgrounds and foregrounds

About

Generate accessible color palettes for Figma with automatic foreground color selection and WCAG contrast checking.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors