UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

246 lines 11 kB
export const cssVariableTheme = { name: 'css-variable-theme', text: { primary: 'var(--shades-theme-text-primary)', secondary: 'var(--shades-theme-text-secondary)', disabled: 'var(--shades-theme-text-disabled)', }, button: { active: 'var(--shades-theme-button-active)', hover: 'var(--shades-theme-button-hover)', selected: 'var(--shades-theme-button-selected)', disabled: 'var(--shades-theme-button-disabled)', disabledBackground: 'var(--shades-theme-button-disabled-background)', }, background: { default: 'var(--shades-theme-background-default)', paper: 'var(--shades-theme-background-paper)', paperImage: 'var(--shades-theme-background-paper-image)', }, palette: { primary: { light: 'var(--shades-theme-palette-primary-light)', lightContrast: 'var(--shades-theme-palette-primary-light-contrast)', main: 'var(--shades-theme-palette-primary-main)', mainContrast: 'var(--shades-theme-palette-primary-main-contrast)', dark: 'var(--shades-theme-palette-primary-dark)', darkContrast: 'var(--shades-theme-palette-primary-dark-contrast)', }, secondary: { light: 'var(--shades-theme-palette-secondary-light)', lightContrast: 'var(--shades-theme-palette-secondary-light-contrast)', main: 'var(--shades-theme-palette-secondary-main)', mainContrast: 'var(--shades-theme-palette-secondary-main-contrast)', dark: 'var(--shades-theme-palette-secondary-dark)', darkContrast: 'var(--shades-theme-palette-secondary-dark-contrast)', }, error: { light: 'var(--shades-theme-palette-error-light)', lightContrast: 'var(--shades-theme-palette-error-light-contrast)', main: 'var(--shades-theme-palette-error-main)', mainContrast: 'var(--shades-theme-palette-error-main-contrast)', dark: 'var(--shades-theme-palette-error-dark)', darkContrast: 'var(--shades-theme-palette-error-dark-contrast)', }, warning: { light: 'var(--shades-theme-palette-warning-light)', lightContrast: 'var(--shades-theme-palette-warning-light-contrast)', main: 'var(--shades-theme-palette-warning-main)', mainContrast: 'var(--shades-theme-palette-warning-main-contrast)', dark: 'var(--shades-theme-palette-warning-dark)', darkContrast: 'var(--shades-theme-palette-warning-dark-contrast)', }, info: { light: 'var(--shades-theme-palette-info-light)', lightContrast: 'var(--shades-theme-palette-info-light-contrast)', main: 'var(--shades-theme-palette-info-main)', mainContrast: 'var(--shades-theme-palette-info-main-contrast)', dark: 'var(--shades-theme-palette-info-dark)', darkContrast: 'var(--shades-theme-palette-info-dark-contrast)', }, success: { light: 'var(--shades-theme-palette-success-light)', lightContrast: 'var(--shades-theme-palette-success-light-contrast)', main: 'var(--shades-theme-palette-success-main)', mainContrast: 'var(--shades-theme-palette-success-main-contrast)', dark: 'var(--shades-theme-palette-success-dark)', darkContrast: 'var(--shades-theme-palette-success-dark-contrast)', }, }, divider: 'var(--shades-theme-divider)', action: { hoverBackground: 'var(--shades-theme-action-hover-background)', selectedBackground: 'var(--shades-theme-action-selected-background)', activeBackground: 'var(--shades-theme-action-active-background)', focusRing: 'var(--shades-theme-action-focus-ring)', focusOutline: 'var(--shades-theme-action-focus-outline)', disabledOpacity: 'var(--shades-theme-action-disabled-opacity)', backdrop: 'var(--shades-theme-action-backdrop)', subtleBorder: 'var(--shades-theme-action-subtle-border)', }, shape: { borderRadius: { xs: 'var(--shades-theme-shape-border-radius-xs)', sm: 'var(--shades-theme-shape-border-radius-sm)', md: 'var(--shades-theme-shape-border-radius-md)', lg: 'var(--shades-theme-shape-border-radius-lg)', full: 'var(--shades-theme-shape-border-radius-full)', }, borderWidth: 'var(--shades-theme-shape-border-width)', }, shadows: { none: 'var(--shades-theme-shadows-none)', sm: 'var(--shades-theme-shadows-sm)', md: 'var(--shades-theme-shadows-md)', lg: 'var(--shades-theme-shadows-lg)', xl: 'var(--shades-theme-shadows-xl)', }, typography: { fontFamily: 'var(--shades-theme-typography-font-family)', fontSize: { xs: 'var(--shades-theme-typography-font-size-xs)', sm: 'var(--shades-theme-typography-font-size-sm)', md: 'var(--shades-theme-typography-font-size-md)', lg: 'var(--shades-theme-typography-font-size-lg)', xl: 'var(--shades-theme-typography-font-size-xl)', xxl: 'var(--shades-theme-typography-font-size-xxl)', xxxl: 'var(--shades-theme-typography-font-size-xxxl)', xxxxl: 'var(--shades-theme-typography-font-size-xxxxl)', }, fontWeight: { normal: 'var(--shades-theme-typography-font-weight-normal)', medium: 'var(--shades-theme-typography-font-weight-medium)', semibold: 'var(--shades-theme-typography-font-weight-semibold)', bold: 'var(--shades-theme-typography-font-weight-bold)', }, lineHeight: { tight: 'var(--shades-theme-typography-line-height-tight)', normal: 'var(--shades-theme-typography-line-height-normal)', relaxed: 'var(--shades-theme-typography-line-height-relaxed)', }, letterSpacing: { tight: 'var(--shades-theme-typography-letter-spacing-tight)', dense: 'var(--shades-theme-typography-letter-spacing-dense)', normal: 'var(--shades-theme-typography-letter-spacing-normal)', wide: 'var(--shades-theme-typography-letter-spacing-wide)', wider: 'var(--shades-theme-typography-letter-spacing-wider)', widest: 'var(--shades-theme-typography-letter-spacing-widest)', }, textShadow: 'var(--shades-theme-typography-text-shadow)', }, transitions: { duration: { fast: 'var(--shades-theme-transitions-duration-fast)', normal: 'var(--shades-theme-transitions-duration-normal)', slow: 'var(--shades-theme-transitions-duration-slow)', }, easing: { default: 'var(--shades-theme-transitions-easing-default)', easeOut: 'var(--shades-theme-transitions-easing-ease-out)', easeInOut: 'var(--shades-theme-transitions-easing-ease-in-out)', }, }, spacing: { xs: 'var(--shades-theme-spacing-xs)', sm: 'var(--shades-theme-spacing-sm)', md: 'var(--shades-theme-spacing-md)', lg: 'var(--shades-theme-spacing-lg)', xl: 'var(--shades-theme-spacing-xl)', }, zIndex: { drawer: 'var(--shades-theme-z-index-drawer)', appBar: 'var(--shades-theme-z-index-app-bar)', modal: 'var(--shades-theme-z-index-modal)', tooltip: 'var(--shades-theme-z-index-tooltip)', dropdown: 'var(--shades-theme-z-index-dropdown)', }, effects: { blurSm: 'var(--shades-theme-effects-blur-sm)', blurMd: 'var(--shades-theme-effects-blur-md)', blurLg: 'var(--shades-theme-effects-blur-lg)', blurXl: 'var(--shades-theme-effects-blur-xl)', }, }; /** * Builds a CSS transition string from property-duration-easing triplets. * @param specs - Array of [property, duration, easing] tuples * @returns A CSS transition string * @example * buildTransition( * ['background', cssVariableTheme.transitions.duration.normal, cssVariableTheme.transitions.easing.default], * ['opacity', cssVariableTheme.transitions.duration.fast, 'ease-out'], * ) */ export const buildTransition = (...specs) => specs.map(([prop, dur, ease]) => `${prop} ${dur} ${ease}`).join(', '); const FOCUS_STYLES_ID = 'shades-focus-visible-styles'; /** * Injects global `:focus-visible` styles using the theme's `focusOutline` CSS variable. * Ensures keyboard/spatial navigation focus is visible while mouse clicks produce no outline. * Safe to call multiple times — the style element is only created once. */ export const injectFocusVisibleStyles = () => { if (document.getElementById(FOCUS_STYLES_ID)) return; const style = document.createElement('style'); style.id = FOCUS_STYLES_ID; style.textContent = ` :focus-visible { outline: ${cssVariableTheme.action.focusOutline}; outline-offset: 2px; } :focus:not(:focus-visible) { outline: none; } `; document.head.appendChild(style); }; const extractVarName = (key) => key.replace(/^var\(/, '').replace(/[,)].*/, ''); export const setCssVariable = (key, value, root) => { root.style.setProperty(extractVarName(key), value); }; export const removeCssVariable = (key, root) => { root.style.removeProperty(extractVarName(key)); }; export const getCssVariable = (key, root = document.querySelector(':root')) => { return getComputedStyle(root).getPropertyValue(extractVarName(key)); }; const removeValue = (target, root) => { const keys = Object.keys(target); keys.forEach((key) => { if (typeof target[key] === 'object') { removeValue(target[key], root); } else { removeCssVariable(target[key], root); } }); }; const assignValue = (target, source, root, assignFn = setCssVariable) => { const keys = Object.keys(target); keys.forEach((key) => { if (typeof target[key] === 'object') { if (source[key] === undefined) { removeValue(target[key], root); } else { assignValue(target[key], source[key], root, assignFn); } } else if (source[key] === undefined) { removeCssVariable(target[key], root); } else { assignFn(target[key], source[key], root); } }); }; export const useThemeCssVariables = (theme, root) => { root ??= document.querySelector(':root'); assignValue(cssVariableTheme, theme, root); if (window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches) { setCssVariable(cssVariableTheme.transitions.duration.fast, '0s', root); setCssVariable(cssVariableTheme.transitions.duration.normal, '0s', root); setCssVariable(cssVariableTheme.transitions.duration.slow, '0s', root); } }; //# sourceMappingURL=css-variable-theme.js.map