@universal-material/web
Version:
Material web components
141 lines • 6.14 kB
JavaScript
import { argbFromHex, CorePalette, TonalPalette } from '@material/material-color-utilities';
import { CssVarBuilder } from './css-var-builder.js';
import { neutralColors, neutralVariantColors } from './neutral-colors.js';
const getCss = (selector, content) => `${selector} {
${content}}`;
export class ThemeBuilder {
#corePalette;
constructor(primaryColorHex) {
this.colors = [];
this.partial = false;
this.cssClass = null;
this.#corePalette = CorePalette.of(argbFromHex(primaryColorHex));
this.addColorFromPalette('primary', this.#corePalette.a1);
}
static create(primaryColorHex) {
return new ThemeBuilder(primaryColorHex);
}
static createPartial(primaryColorHex) {
const themeBuilder = new ThemeBuilder(primaryColorHex);
themeBuilder.partial = true;
return themeBuilder;
}
addColorFromHex(name, hex) {
const palette = TonalPalette.fromInt(argbFromHex(hex));
this.addColorFromPalette(name, palette);
return this;
}
addColorFromPalette(name, palette) {
this.colors.push({ name, lightTone: 40, darkTone: 80, tonalPalette: palette });
this.colors.push({ name: `on-${name}`, lightTone: 100, darkTone: 20, tonalPalette: palette });
this.colors.push({ name: `${name}-container`, lightTone: 90, darkTone: 30, tonalPalette: palette });
this.colors.push({ name: `on-${name}-container`, lightTone: 10, darkTone: 90, tonalPalette: palette });
this.colors.push({ name: `${name}-fixed`, fixedTone: 90, tonalPalette: palette });
this.colors.push({ name: `${name}-fixed-dim`, fixedTone: 80, tonalPalette: palette });
this.colors.push({ name: `on-${name}-fixed`, fixedTone: 10, tonalPalette: palette });
this.colors.push({ name: `on-${name}-fixed-variant`, fixedTone: 30, tonalPalette: palette });
return this;
}
addStaticColor(name, hex, tone = 80) {
const palette = TonalPalette.fromInt(argbFromHex(hex));
this.colors.push({ name, fixedTone: tone, tonalPalette: palette });
this.colors.push({ name: `on-${name}`, fixedTone: 15, tonalPalette: palette });
this.colors.push({ name: `${name}-container`, fixedTone: 90, tonalPalette: palette });
this.colors.push({ name: `on-${name}-container`, fixedTone: 15, tonalPalette: palette });
return this;
}
setCssClass(cssClass) {
this.cssClass = cssClass;
return this;
}
ensureCssClassStartsWithDot() {
if (!this.cssClass || this.cssClass.startsWith('.')) {
return;
}
this.cssClass = `.${this.cssClass}`;
}
ensureThemeColors() {
if (!this.colors.find(c => c.name === 'secondary')) {
this.addColorFromPalette('secondary', this.#corePalette.a2);
}
if (!this.colors.find(c => c.name === 'tertiary')) {
this.addColorFromPalette('tertiary', this.#corePalette.a3);
}
}
ensureStatusColors() {
if (!this.colors.find(c => c.name === 'success')) {
this.addStaticColor('success', '#198754', 60);
}
if (!this.colors.find(c => c.name === 'info')) {
this.addStaticColor('info', '#0dcaf0');
}
if (!this.colors.find(c => c.name === 'warning')) {
this.addStaticColor('warning', '#ffc107');
}
if (!this.colors.find(c => c.name === 'error')) {
this.addColorFromHex('error', '#b3261e');
}
}
getNeutralVariables() {
const builder = CssVarBuilder.create();
this.addColors(builder, neutralColors, this.#corePalette.n1);
builder
.add('--u-color-body', 'var(--u-color-surface)')
.add('--u-color-inverse-body', 'var(--u-color-inverse-surface)')
.add('--u-color-on-body', 'var(--u-color-on-surface)')
.add('--u-color-on-inverse-body', 'var(--u-color-on-inverse-surface)')
.add('--u-current-text-color', 'var(--u-color-on-body)');
return builder.build();
}
getNeutralVariantVariables() {
const builder = CssVarBuilder.create();
this.addColors(builder, neutralVariantColors, this.#corePalette.n2);
return builder.build();
}
getColorVariables(color) {
const builder = CssVarBuilder.create();
this.addToneColor(builder, color, color.tonalPalette);
return builder.build();
}
getColorsVariables() {
let variables = '';
for (const color of this.colors) {
variables += this.getColorVariables(color);
}
variables += this.getNeutralVariables();
variables += this.getNeutralVariantVariables();
return variables;
}
addColors(builder, colors, palette) {
for (const color of colors) {
this.addToneColor(builder, color, palette);
}
}
addToneColor(builder, color, palette) {
if (color.fixedTone !== undefined) {
builder.addFromArgb(color.name, palette.tone(color.fixedTone));
return;
}
const inverseName = `inverse-${color.name}`.replace('inverse-on', 'on-inverse');
builder
.addLightAndDarkFromArgb(color.name, palette.tone(color.lightTone), palette.tone(color.darkTone))
.addLightAndDarkFromArgb(inverseName, palette.tone(color.darkTone), palette.tone(color.lightTone));
if (color.name === 'surface' || color.name === 'on-surface') {
const prefix = color.name.startsWith('on-') ? 'on-' : '';
builder.addFromArgb(`${prefix}light-surface`, palette.tone(color.lightTone));
builder.addFromArgb(`${prefix}dark-surface`, palette.tone(color.darkTone));
}
}
build() {
this.ensureCssClassStartsWithDot();
const selector = this.cssClass ?? ':root';
this.ensureThemeColors();
if (!this.partial) {
this.ensureStatusColors();
}
const variables = `${getCss(selector, this.getColorsVariables())}
`;
return variables;
}
}
//# sourceMappingURL=theme-builder.js.map