lit-css-vars
Version:
For easily creating and sharing typed CSS vars for the lit.dev ecosystem.
105 lines (104 loc) • 3.9 kB
JavaScript
import { addPrefix, getObjectTypedEntries } from '@augment-vir/common';
/**
* Set the given CSS var to the given value on the given element. Allows numeric values but converts
* them to strings (since the style.setProperty API only allows strings).
*
* @category Main
*/
export function setCssVarValue({ onElement, toValue, forCssVar, }) {
onElement.style.setProperty(String(forCssVar.name), String(toValue));
}
/**
* Set the given property's value to the given CSS var on the given element, using
* "element.style.setProperty".
*
* Note: this is a slow operation. Do not run this for many CSS vars at once. Instead, prefer
* {@link applyCssVarsViaStyleElement} for bulk CSS var setting.
*
* @category Main
*/
export function applyCssVar({ onElement, forProperty, toCssVar, }) {
onElement.style.setProperty(forProperty, String(toCssVar.value));
}
/**
* Create the global `<style>` element used by {@link applyCssVarsViaStyleElement} to set many CSS
* var values at once.
*
* @category Internal
*/
export function createCssVarStyleElement(
/** The id for the `<style>` element. This should, ideally, be a kebab-case string. */
styleKey,
/**
* Customize where the `<style>` element is attached.
*
* @default document.head
*/
context = document.head) {
if (styleKey.match(/\s/)) {
throw new Error(`Cannot use a style key with white space in it: '${styleKey}'`);
}
const existingElement = context.querySelector(`style#${styleKey}`);
if (existingElement instanceof HTMLStyleElement) {
return existingElement;
}
else {
const newStyleElement = globalThis.document.createElement('style');
newStyleElement.id = styleKey;
context.append(newStyleElement);
return newStyleElement;
}
}
/**
* Efficiently sets many CSS var values via a `<style>` element.
*
* @category Main
* @returns The created or existing `<style>` element in case you wish to reuse it.
*/
export function applyCssVarsViaStyleElement(
/**
* The CSS Var values to apply. The keys of this object are the CSS Var names and they will be
* set to the given values. `undefined` values will not be omitted (not set). The keys may
* include or omit the CSS required `'--'` prefix.
*/
cssVarValues,
/** The id for the `<style>` element. This should, ideally, be a kebab-case string. */
styleKey,
/**
* Customize where the `<style>` element is attached.
*
* @default document.head
*/
context = document.head) {
const styleElement = createCssVarStyleElement(styleKey, context);
const cssVarDeclarations = getObjectTypedEntries(cssVarValues).flatMap(([cssVarName, value,]) => {
if (value == undefined || value === '') {
return [];
}
const key = addPrefix({
value: cssVarName,
prefix: '--',
});
return [
` ${key}: ${value};`,
];
});
styleElement.textContent = `:root {\n ${cssVarDeclarations.join('\n ')}\n}`;
return styleElement;
}
/**
* Read the given CSS var's value on the given element. If "includeCascade" is set to true, the
* given elements styles are computed to retrieve cascaded CSS var values. If "includeCascade" is
* false, the CSS var is read directly off the element, which will only read values from the element
* on which the CSS var was directly set.
*
* WARNING: "includeCascade: true" is less performant because it runs "globalThis.getComputedStyle".
* However, in practice I've yet to actually see this be an issue (unless you're running this in an
* immediate infinite loop but of course don't do that).
*
* @category Main
*/
export function readCssVarValue({ onElement, forCssVar, includeCascade, }) {
const styleRoot = includeCascade ? globalThis.getComputedStyle(onElement) : onElement.style;
return styleRoot.getPropertyValue(String(forCssVar.name)).trim();
}