lit-css-vars
Version:
For easily creating and sharing typed CSS vars for the lit.dev ecosystem.
127 lines (126 loc) • 4.7 kB
JavaScript
import { check } from '@augment-vir/assert';
import { addPrefix, combineErrorMessages, mapObjectValues, stringify, } from '@augment-vir/common';
import { css, CSSResult, unsafeCSS } from 'lit';
import { cssPropertyRegistry } from './css-property-registry.js';
import { setCssVarValue } from './setters-and-getters.js';
import { CssVarSyntaxName } from './syntax.js';
/**
* Creates an easy-to-use-in-lit mapping of the given CSS Var names and defaults. The input
* determines the CSS var names and their default values. The output is a mapping of the CSS var
* names to name and value objects that can be easily interpolated into lit's css keyed template
* strings.
*
* @category Main
* @example
*
* ```ts
* import {defineCssVars} from 'lit-css-vars';
*
* // creates a CSS var with name 'my-var' and default value of 50px.
* const myVars = defineCssVars({'my-var': '50px'});
* // using the CSS var name: this will be '--my-var'
* myVars['my-var'].name;
* // accessing the CSS var value for CSS; this will be: 'var(--my-var, 50px)'
* myVars['my-var'].value;
* ```
*/
export function defineCssVars(
/**
* The CSS var setup input. Keys of this input object become the CSS var names. Values of this
* input become the default value of the CSS vars.
*/
setup) {
const cssVarDefinitions = mapObjectValues(setup, (key, rawInputValue) => {
assertValidCssVarName(key);
const value = rawInputValue;
const isCssPropertyDefinition = check.isObject(value) &&
!(value instanceof CSSResult) &&
/** Exclude {@link SingleCssVarDefinition}. */
check.lacksKey(value, 'name');
const defaultValue = check.isString(value) || check.isNumber(value) || value instanceof CSSResult
? String(value)
: String(value.default);
const initialValue = check.isString(value) || check.isNumber(value) || value instanceof CSSResult
? String(value)
: String(('initialValue' in value && value.initialValue) || value.default);
const cssVarNameCssResult = unsafeCSS(addPrefix({
value: key.replace(/^-+/, ''),
prefix: '--',
}));
const finalDefinition = {
name: cssVarNameCssResult,
value: css `var(${cssVarNameCssResult}, ${unsafeCSS(defaultValue)})`,
syntax: check.isString(value) || check.isNumber(value) || value instanceof CSSResult
? CssVarSyntaxName.Any
: createSyntaxString('syntax' in value ? value.syntax : undefined),
default: defaultValue,
};
const cssPropertyName = String(finalDefinition.name);
if (!initialValue) {
throw new Error(`Initial value for CSS var ${cssPropertyName} cannot be empty.`);
}
if (isCssPropertyDefinition &&
cssPropertyRegistry.registerProperty({
inherits: true,
name: cssPropertyName,
initialValue,
syntax: finalDefinition.syntax,
})) {
const documentElement = globalThis.document?.documentElement;
if (documentElement) {
setCssVarValue({
forCssVar: finalDefinition,
onElement: globalThis.document.documentElement,
toValue: defaultValue,
});
}
}
return finalDefinition;
});
return cssVarDefinitions;
}
/**
* Asserts that the given string can be a valid CSS var name (excluding the `--` prefix).
*
* @category Internal
*/
export function assertValidCssVarName(value) {
try {
if (!check.isString(value)) {
throw new TypeError('Must be string.');
}
else if (!value.includes('-')) {
throw new Error('Must have at least one dash (-).');
}
else if (value.toLowerCase() !== value) {
throw new Error('Must be lowercase.');
}
}
catch (error) {
throw new Error(combineErrorMessages('Invalid CSS var name.', error, `Got '${stringify(value)}'`), {
cause: error,
});
}
}
/**
* Create a CSS engine compatible syntax string.
*
* @category Internal
*/
export function createSyntaxString(syntax) {
if (!syntax) {
return CssVarSyntaxName.Any;
}
else if (check.isString(syntax)) {
return syntax;
}
else if (syntax.union) {
return syntax.union.map((innerSyntax) => createSyntaxString(innerSyntax)).join(' | ');
}
else if (syntax.list) {
return `${createSyntaxString(syntax.list.values)}${syntax.list.separator}`;
}
else {
return syntax.raw;
}
}