@poupe/theme-builder
Version:
Design token management and theme generation system for Poupe UI framework
444 lines (348 loc) • 11.9 kB
Markdown
<!-- cspell:words vars -->
# @poupe/theme-builder
[![jsDocs.io][jsdocs-badge]][jsdocs-url]
[![npm version][npm-badge]][npm-url]
[![License: MIT][license-badge]][license-url]
Design token management and theme generation system for Material Design 2025
themes with automatic dark mode, CSS variables, and image-based color
extraction.
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Theme Creation](#basic-theme-creation)
- [CSS Theme Generation](#css-theme-generation)
- [Theme from Image](#theme-from-image)
- [Color Manipulation](#color-manipulation)
- [API Reference](#api-reference)
- [Main Exports](#main-exports)
- [Core Utilities](#core-utilities)
- [Server Utilities](#server-utilities)
- [Color System](#color-system)
- [CSS Variables](#css-variables)
- [Dark Mode](#dark-mode)
- [Integration with Poupe Ecosystem](#integration-with-poupe-ecosystem)
- [Requirements](#requirements)
- [License](#license)
## Features
- 🎨 Material Design 2025 color system with automatic dark themes
- 🌓 Built-in dark mode support with CSS custom properties
- 🖼️ Generate color palettes from images using Material algorithms
- 🔧 Advanced color manipulation utilities (HCT, RGB, HSL, ARGB)
- 📦 Lightweight, tree-shakable API with TypeScript support
- 🧩 CSS variables generation for external customization
- 🎭 Theme scheme variants (vibrant, expressive, content, etc.)
- 🛠️ Server-side utilities for build-time theme generation
- 🎯 Material Design 3 state layer colors for interactive states
## Installation
```bash
# npm
npm install -D @poupe/theme-builder
# yarn
yarn add -D @poupe/theme-builder
# pnpm
pnpm add -D @poupe/theme-builder
```
## Usage
### Basic Theme Creation
```typescript
import { makeTheme } from '@poupe/theme-builder'
// Create Material Design 2025 theme with color roles
const { dark, light } = makeTheme({
primary: '#1976d2',
secondary: '#9c27b0',
tertiary: '#ff4081',
}, 'vibrant', 0.0)
// Access color roles
console.log(light.primary.value) // Primary color in light theme
console.log(dark.onPrimary.value) // Text on primary in dark theme
// Theme with state colors included by default
console.log(light['primary-hover']) // Primary hover state
console.log(dark['on-primary-disabled']) // Disabled text on primary
// Use CSS color-mix() for runtime state color generation
// Fourth parameter accepts additional theme generation options
const runtimeTheme = makeTheme({
primary: '#1976d2',
}, 'content', 0.0, { useColorMix: true })
```
### CSS Theme Generation
```typescript
import { makeCSSTheme } from '@poupe/theme-builder'
// Generate CSS variables for dark/light themes
const cssTheme = makeCSSTheme({
primary: '#6750A4',
secondary: '#958DA5',
error: '#B3261E',
}, {
scheme: 'content',
contrastLevel: 0.5,
prefix: 'md-',
darkMode: ['.dark', 'media'], // Multiple selectors + aliases
lightMode: '.light',
})
// Built-in responsive aliases
makeCSSTheme(colors, {
darkMode: ['dark', 'mobile'], // Uses media queries
lightMode: ['light', 'desktop'] // Now supports arrays too
})
// Advanced selector configuration
const advancedTheme = makeCSSTheme(colors, {
darkMode: ['mobile', '.dark-mode'], // Mobile + custom class
lightMode: ['desktop', '.light-mode'], // Desktop + custom class
})
// Use generated CSS variables
console.log(cssTheme.vars.primary) // '--md-primary'
console.log(cssTheme.styles) // CSS rule objects
```
### Theme from Image
```typescript
import { fromImageElement } from '@poupe/theme-builder'
// Extract color from image element
async function createImageTheme(imageElement: HTMLImageElement) {
const seedColor = await fromImageElement(imageElement)
const { dark, light } = makeTheme({
primary: seedColor.toHex(),
}, 'expressive')
return { dark, light }
}
```
### Color Manipulation
```typescript
import {
hct, colord, hexString, makeTonalPalette
} from '@poupe/theme-builder/core'
// HCT color space (Hue, Chroma, Tone)
const color = hct('#1976d2')
const lighter = color.withTone(80) // Lighter variant
const muted = color.withChroma(30) // Lower saturation
// Advanced color utilities
const c = colord('#1976d2')
console.log(c.toHsl()) // HSL representation
console.log(c.lighten(0.2).toHex()) // Lightened color
// Format colors
console.log(hexString(lighter)) // Convert to hex string
// Create tonal palette with full tone range (0-100)
const palette = makeTonalPalette('#1976d2')
console.log(palette.tone(50)) // Medium tone
console.log(palette.tone(90)) // Light tone
console.log(palette.tone(10)) // Dark tone
// Harmonize colors to create cohesive palettes
const primary = hct('#1976d2')
const harmonized = makeTonalPalette('#9c27b0', primary)
```
### State Layer Colors
Material Design 3 interactive state support:
```typescript
import {
makeStandardStateVariants,
getStateColorMixParams,
stateLayerOpacities
} from '@poupe/theme-builder'
// Generate state variants for theme colors
const theme = makeTheme({ primary: '#6750A4' })
const stateColors = makeStandardStateVariants(theme.light)
// Access state colors
console.log(stateColors['primary-hover']) // 8% opacity
console.log(stateColors['primary-focus']) // 12% opacity
console.log(stateColors['primary-pressed']) // 12% opacity
console.log(stateColors['on-primary-disabled']) // 38% opacity
// Get CSS color-mix parameters for dynamic theming
const params = getStateColorMixParams('primary', 'hover', '--md-')
// Returns: {
// state: 'hover',
// baseColor: '--md-primary',
// onColor: '--md-on-primary',
// opacityPercent: 8
// }
// Material Design 3 opacity values
console.log(stateLayerOpacities.hover) // 0.08
console.log(stateLayerOpacities.focus) // 0.12
console.log(stateLayerOpacities.pressed) // 0.12
console.log(stateLayerOpacities.dragged) // 0.16
console.log(stateLayerOpacities.disabled) // 0.12
console.log(stateLayerOpacities.onDisabled) // 0.38
```
## API Reference
### Main Exports
```typescript
import {
// Theme generation
makeTheme,
makeCSSTheme,
// CSS utilities
assembleCSSColors,
defaultCSSThemeOptions,
// Image extraction
fromImageElement,
// State layer colors
makeStandardStateVariants,
makeCustomStateVariants,
// Color utilities (re-exported from core)
hct,
colord,
hexString,
rgbaString,
} from '@poupe/theme-builder'
```
### Core Utilities
Color manipulation and formatting utilities:
```typescript
import {
// Color types and creation
type Hct,
type HexColor,
hct,
colord,
// Color formatting
hexString,
rgbaString,
// Color conversion
argb,
rgb,
// State layer utilities
stateLayerOpacities,
makeStateLayerColors,
makeStateVariants,
getStateColorMixParams,
// CSS utilities (re-exported)
formatCSSRules,
} from '@poupe/theme-builder/core'
```
### Server Utilities
Server-side color processing, validation, and CSS response utilities:
```typescript
import {
// Parameter processing
getColorParam,
getThemeSchemeParam,
// CSS formatting utilities
stringifyCSSRulesArray,
stringifyCSSRulesArrayStream,
stringifyCSSRulesArrayAsStream,
stringifyCSSRulesArrayAsResponse,
stringifyCSSRulesArrayAsStreamingResponse,
// Color types (re-exported)
type HexColor,
hct,
colord,
} from '@poupe/theme-builder/server'
// Convert to CSS string (no trailing newline)
const cssString = stringifyCSSRulesArray([
{ '.theme': { '--primary': '#6750A4' } }
])
// Convert with camelCase to kebab-case normalization
const normalizedCSS = stringifyCSSRulesArray([
{ fontSize: '16px', backgroundColor: 'blue' }
], { normalizeProperties: true })
// Result: 'font-size: 16px;\nbackground-color: blue;'
// Create ReadableStream (perfect for Cloudflare Workers)
const stream = stringifyCSSRulesArrayAsStream([
{ '.theme': { '--primary': '#6750A4' } }
])
const response = new Response(stream, {
headers: { 'Content-Type': 'text/css' }
})
// Create Response object with headers
const response = stringifyCSSRulesArrayAsResponse([
{ '.theme': { '--primary': '#6750A4' } }
])
// Create streaming Response for large CSS files
const streamingResponse = stringifyCSSRulesArrayAsStreamingResponse([
// Large array of CSS rules
])
```
## Color System
Material Design 2025 color roles and theming:
- **Primary**: Main brand color and variants (primary, primaryDim,
onPrimary, primaryContainer, onPrimaryContainer)
- **Secondary**: Supporting colors (secondary, secondaryDim,
onSecondary, secondaryContainer, onSecondaryContainer)
- **Tertiary**: Accent colors (tertiary, tertiaryDim, onTertiary,
tertiaryContainer, onTertiaryContainer)
- **Error**: Error states (error, onError, errorContainer,
onErrorContainer)
- **Neutral**: Surface and outline colors (surface, onSurface,
outline, etc.)
```typescript
const { dark, light } = makeTheme({
primary: '#6750A4',
secondary: '#958DA5',
tertiary: '#B58392',
neutral: '#938F94',
neutralVariant: '#948F94',
error: '#B3261E',
}, 'content', 0.0)
// Access all color roles
console.log(light.primaryContainer.value) // Primary container color
console.log(dark.onSurfaceVariant.value) // Text on surface variant
```
## CSS Variables
Automatic CSS custom property generation:
```typescript
const cssTheme = makeCSSTheme({
primary: '#6750A4',
}, {
prefix: 'md-', // Variable prefix
darkMode: '.dark', // Dark mode selector
lightMode: '.light', // Light mode selector (optional)
darkSuffix: '-dark', // Dark variable suffix
lightSuffix: '-light', // Light variable suffix
})
// Generated variables
cssTheme.vars.primary // '--md-primary'
cssTheme.vars.onPrimary // '--md-on-primary'
cssTheme.styles // CSS rule objects
```
## Dark Mode
Built-in dark mode support with flexible selectors and aliases:
```typescript
// Class-based dark mode (default)
makeCSSTheme(colors, { darkMode: '.dark' })
// Media query dark mode using built-in alias
makeCSSTheme(colors, { darkMode: 'media' })
// Multiple selectors
makeCSSTheme(colors, { darkMode: ['.dark', '.theme-dark'] })
// Built-in responsive aliases
makeCSSTheme(colors, {
darkMode: ['dark', 'mobile'], // Uses media queries
lightMode: 'light'
})
// Custom selectors
makeCSSTheme(colors, {
darkMode: '[data-theme="dark"]',
lightMode: '[data-theme="light"]'
})
// Disable dark mode
makeCSSTheme(colors, { darkMode: false })
```
### Built-in Selector Aliases
The theme builder includes convenient aliases for common media queries:
- `'media'` or `'dark'` → `'@media (prefers-color-scheme: dark)'`
- `'light'` → `'@media (prefers-color-scheme: light)'`
- `'mobile'` → `'@media (max-width: 768px)'`
- `'tablet'` → `'@media (min-width: 769px) and (max-width: 1024px)'`
- `'desktop'` → `'@media (min-width: 1025px)'`
```typescript
// Using aliases for responsive theming
const cssTheme = makeCSSTheme(colors, {
darkMode: ['dark', 'tablet'], // Dark mode + tablet screens
lightMode: ['light', 'desktop'], // Light mode + desktop screens
})
```
## Integration with Poupe Ecosystem
- [@poupe/css](../@poupe-css) - CSS utility library
- [@poupe/tailwindcss](../@poupe-tailwindcss) - TailwindCSS integration
- [@poupe/vue](../@poupe-vue) - Vue components library
- [@poupe/nuxt](../@poupe-nuxt) - Nuxt integration
## Requirements
- Node.js >=20.19.1
- TypeScript-friendly environment
## License
MIT licensed.
<!-- Badge references -->
[jsdocs-badge]: https://img.shields.io/badge/jsDocs.io-reference-blue
[jsdocs-url]: https://www.jsdocs.io/package/@poupe/theme-builder
[npm-badge]: https://img.shields.io/npm/v/@poupe/theme-builder.svg
[npm-url]: https://www.npmjs.com/package/@poupe/theme-builder
[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg
[license-url]: ../../LICENCE.txt