UNPKG

@poupe/theme-builder

Version:

Design token management and theme generation system for Poupe UI framework

442 lines (347 loc) 11.9 kB
# @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