theme-vir
Version:
Create an entire web theme.
160 lines (159 loc) • 6.46 kB
JavaScript
import { assert, check } from '@augment-vir/assert';
import { getObjectTypedEntries, log } from '@augment-vir/common';
import { defineCssVars, } from 'lit-css-vars';
/**
* Handles a color init value.
*
* @category Internal
*/
export function createColorCssVarDefault(fromName, init, defaultInit, colorsInit) {
const referenceKey = check.hasKey(init, 'refBackground')
? 'refBackground'
: check.hasKey(init, 'refForeground')
? 'refForeground'
: undefined;
const reference = referenceKey && check.hasKey(init, referenceKey) ? init[referenceKey] : undefined;
if (reference) {
const layerKey = referenceKey === 'refBackground' ? 'background' : 'foreground';
const referenced = colorsInit[reference];
if (!referenced) {
throw new Error(`Color theme ${referenceKey} reference '${reference}' does not exist. (Referenced from '${fromName}'.)`);
}
const colorValue = referenced[layerKey] ||
(layerKey === 'foreground'
? createColorCssVarDefault('default-fg', defaultInit.foreground, defaultInit, colorsInit)
: createColorCssVarDefault('default-bg', defaultInit.background, defaultInit, colorsInit));
return `var(--${reference}-${layerKey === 'foreground' ? 'fg' : 'bg'}, ${createColorCssVarDefault(reference, colorValue, defaultInit, colorsInit)})`;
}
else {
return init;
}
}
/**
* Default foreground/background color theme used in {@link ColorTheme}. Do not define a theme color
* with this name!
*
* @category Internal
*/
export const themeDefaultKey = 'theme-default';
/**
* Define a color theme.
*
* @category Color Theme
*/
export function defineColorTheme(defaultInit, allColorsInit) {
try {
if (themeDefaultKey in allColorsInit) {
throw new Error(`Cannot define theme color by name '${themeDefaultKey}', it is used internally.`);
}
const defaultColors = defineCssVars({
'default-fg': createColorCssVarDefault('default-fg', defaultInit.foreground, defaultInit, allColorsInit),
'default-bg': createColorCssVarDefault('default-bg', defaultInit.background, defaultInit, allColorsInit),
'default-inverse-fg': createColorCssVarDefault('default-inverse-fg', defaultInit.background, defaultInit, allColorsInit),
'default-inverse-bg': createColorCssVarDefault('default-inverse-bg', defaultInit.foreground, defaultInit, allColorsInit),
});
const cssVarsSetup = getObjectTypedEntries(allColorsInit).reduce((accum, [colorName, colorInit,]) => {
const names = createCssVarNames(colorName);
accum[names.foreground] = colorInit.foreground
? createColorCssVarDefault([
colorName,
'foreground',
].join(' '), colorInit.foreground, defaultInit, allColorsInit)
: `var(${defaultColors['default-fg'].name}, ${defaultColors['default-fg'].default})`;
accum[names.background] = colorInit.background
? createColorCssVarDefault([
colorName,
'background',
].join(' '), colorInit.background, defaultInit, allColorsInit)
: `var(${defaultColors['default-bg'].name}, ${defaultColors['default-bg'].default})`;
accum[names.foregroundInverse] =
`var(--${names.background}, ${accum[names.background]})`;
accum[names.backgroundInverse] =
`var(--${names.foreground}, ${accum[names.foreground]})`;
return accum;
}, {});
/**
* This has multiple `as` casts because `defineCssVars` complains that `cssVarsSetup` is too
* generic. That is indeed true, but in this use case we do not care because the resulting
* `cssVars` object is not directly exposed.
*/
const cssVars = defineCssVars(cssVarsSetup);
const colors = {};
const inverseColors = {};
getObjectTypedEntries(allColorsInit).forEach(([colorName, colorInit,]) => {
assert.isString(colorName);
const names = createCssVarNames(colorName);
const foreground = cssVars[names.foreground];
const background = cssVars[names.background];
const foregroundInverse = cssVars[names.foregroundInverse];
const backgroundInverse = cssVars[names.backgroundInverse];
assert.isDefined(foreground);
assert.isDefined(background);
assert.isDefined(foregroundInverse);
assert.isDefined(backgroundInverse);
colors[colorName] = {
foreground,
background,
init: colorInit,
name: colorName,
};
inverseColors[colorName] = {
foreground: foregroundInverse,
background: backgroundInverse,
init: colorInit,
name: colorName,
};
});
const themeDefaultColors = {
foreground: defaultColors['default-fg'],
background: defaultColors['default-bg'],
init: defaultInit,
name: themeDefaultKey,
};
const themeDefaultInverseColors = {
...themeDefaultColors,
foreground: defaultColors['default-inverse-fg'],
background: defaultColors['default-inverse-bg'],
};
return {
colors: {
[themeDefaultKey]: themeDefaultColors,
...colors,
},
inverse: {
[themeDefaultKey]: themeDefaultInverseColors,
...inverseColors,
},
init: {
colors: allColorsInit,
default: defaultInit,
},
};
}
catch (error) {
globalThis.setTimeout(() => log.error(error));
throw error;
}
}
function createCssVarNames(colorName) {
return {
foreground: [
colorName,
'fg',
].join('-'),
background: [
colorName,
'bg',
].join('-'),
foregroundInverse: [
colorName,
'inverse',
'fg',
].join('-'),
backgroundInverse: [
colorName,
'inverse',
'bg',
].join('-'),
};
}