/** * KodexaLabs Design Tokens * Unified design system for all KodexaLabs projects * * @packageDocumentation * @module @kodexalabs/design-tokens * @author KodexaLabs * @version 1.0.0 * * @example * ```typescript * // Import tokens * import { designTokens, colors, typography } from '@kodexalabs/design-tokens'; * * // Use in your components * const primaryColor = colors.brand.magenta.hsl; * const bodyFont = typography.fontFamily.body; * ``` * * @example * ```typescript * // Generate CSS variables * import { generateCSSVars, applyCSSVarsToDocument } from '@kodexalabs/design-tokens/css'; * * // Apply to document * applyCSSVarsToDocument('dark'); * ``` * * @example * ```javascript * // Use with Tailwind * import kodexaTokens from '@kodexalabs/design-tokens/tailwind'; * * export default { * plugins: [kodexaTokens], * }; * ``` */ // Export all tokens and types export { designTokens, darkThemeTokens, colors, typography, spacing, radius, shadow, motion, layout, borderWidth, zIndex, type DesignTokens, type ColorToken, } from './tokens'; // Re-export CSS utilities for convenience export { generateCSSVars, generateLightThemeCSS, generateDarkThemeCSS, generateCompleteCSS, applyCSSVarsToDocument, getCSSVar, setCSSVar, cssVarsString, lightThemeCSS, darkThemeCSS, } from './css-vars'; // Re-export Tailwind plugin export { kodexaDesignTokensPlugin, default as tailwindPlugin } from './tailwind-plugin'; /** * Utility: Deep merge two token objects * Useful for creating custom theme variations * * @param base - Base tokens object * @param overrides - Overrides to apply * @returns Merged tokens object * * @example * ```typescript * const customTheme = mergeTokens(designTokens, { * color: { * brand: { * magenta: createColorToken(336, '98%', '60%', '#ff3d92'), * }, * }, * }); * ``` */ export function mergeTokens>( base: T, overrides: Partial ): T { const result = { ...base }; for (const key in overrides) { const override = overrides[key]; const baseValue = base[key]; if (override && typeof override === 'object' && !Array.isArray(override)) { if (baseValue && typeof baseValue === 'object' && !Array.isArray(baseValue)) { result[key] = mergeTokens(baseValue, override) as T[Extract]; } else { result[key] = override as T[Extract]; } } else if (override !== undefined) { result[key] = override as T[Extract]; } } return result; } /** * Utility: Create a custom color token * * @param h - Hue (0-360) * @param s - Saturation (percentage string) * @param l - Lightness (percentage string) * @param hex - Hex color code * @returns ColorToken object * * @example * ```typescript * const customPink = createColorToken(330, '85%', '60%', '#f25ca2'); * ``` */ export function createColorToken( h: number, s: string, l: string, hex: string ): import('./tokens').ColorToken { const hsl = `hsl(${h} ${s} ${l})`; // Convert HSL to RGB const hNum = h / 360; const sNum = parseInt(s) / 100; const lNum = parseInt(l) / 100; let r: number, g: number, b: number; if (sNum === 0) { r = g = b = lNum; } else { const hue2rgb = (p: number, q: number, t: number): number => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1/6) return p + (q - p) * 6 * t; if (t < 1/2) return q; if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; }; const q = lNum < 0.5 ? lNum * (1 + sNum) : lNum + sNum - lNum * sNum; const p = 2 * lNum - q; r = hue2rgb(p, q, hNum + 1/3); g = hue2rgb(p, q, hNum); b = hue2rgb(p, q, hNum - 1/3); } const rgb = `rgb(${Math.round(r * 255)} ${Math.round(g * 255)} ${Math.round(b * 255)})`; return { h, s, l, hsl, rgb, hex }; } /** * Utility: Get a token value by path * * @param path - Dot-separated path to token (e.g., 'color.brand.magenta.hsl') * @returns Token value or undefined * * @example * ```typescript * const primaryColor = getTokenByPath('color.brand.magenta.hsl'); * // Returns: 'hsl(336 98% 52%)' * ``` */ export function getTokenByPath(path: string): any { const { designTokens } = require('./tokens'); const parts = path.split('.'); let current: any = designTokens; for (const part of parts) { if (current && typeof current === 'object' && part in current) { current = current[part]; } else { return undefined; } } return current; } /** * Utility: Convert hex color to HSL * * @param hex - Hex color code (with or without #) * @returns HSL color string * * @example * ```typescript * const hsl = hexToHSL('#ed1468'); * // Returns: 'hsl(338 85% 50%)' * ``` */ export function hexToHSL(hex: string): string { // Remove # if present hex = hex.replace(/^#/, ''); // Parse hex values const r = parseInt(hex.substring(0, 2), 16) / 255; const g = parseInt(hex.substring(2, 4), 16) / 255; const b = parseInt(hex.substring(4, 6), 16) / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h = 0; let s = 0; const l = (max + min) / 2; if (max !== min) { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break; case g: h = ((b - r) / d + 2) / 6; break; case b: h = ((r - g) / d + 4) / 6; break; } } return `hsl(${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)`; } /** * Utility: Convert RGB to HSL * * @param r - Red (0-255) * @param g - Green (0-255) * @param b - Blue (0-255) * @returns HSL color string * * @example * ```typescript * const hsl = rgbToHSL(237, 20, 104); * // Returns: 'hsl(338 85% 50%)' * ``` */ export function rgbToHSL(r: number, g: number, b: number): string { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h = 0; let s = 0; const l = (max + min) / 2; if (max !== min) { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break; case g: h = ((b - r) / d + 2) / 6; break; case b: h = ((r - g) / d + 4) / 6; break; } } return `hsl(${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%)`; }