Lots more theme stuff

This commit is contained in:
Gregory Schier
2023-03-07 21:52:21 -08:00
parent 6575121902
commit 620dd7d3ef
16 changed files with 219 additions and 108 deletions

View File

@@ -1,32 +1,18 @@
import { describe, expect, it } from 'vitest';
import { generateColorVariant, toTailwindVariable } from './theme';
describe('suite name', () => {
it('Generates dark variants', () => {
expect(generateColorVariant('blue', 50, 'dark')).toEqual('hsl(240,100%,5.0%)');
expect(generateColorVariant('blue', 100, 'dark')).toEqual('hsl(240,100%,10.0%)');
expect(generateColorVariant('blue', 200, 'dark')).toEqual('hsl(240,100%,20.0%)');
expect(generateColorVariant('blue', 300, 'dark')).toEqual('hsl(240,100%,30.0%)');
expect(generateColorVariant('blue', 400, 'dark')).toEqual('hsl(240,100%,40.0%)');
expect(generateColorVariant('blue', 500, 'dark')).toEqual('hsl(240,100%,50.0%)');
expect(generateColorVariant('blue', 600, 'dark')).toEqual('hsl(240,100%,60.0%)');
expect(generateColorVariant('blue', 700, 'dark')).toEqual('hsl(240,100%,70.0%)');
expect(generateColorVariant('blue', 800, 'dark')).toEqual('hsl(240,100%,80.0%)');
expect(generateColorVariant('blue', 900, 'dark')).toEqual('hsl(240,100%,90.0%)');
expect(generateColorVariant('blue', 950, 'dark')).toEqual('hsl(240,100%,95.0%)');
describe('Generate colors', () => {
it('Generates dark colors', () => {
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'dark', 0.2, 0.8)).toBe('hsl(0,0%,14.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'dark', 0.2, 0.8)).toBe('hsl(0,0%,77.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'dark', 0.4, 0.6)).toBe('hsl(0,0%,23.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'dark', 0.4, 0.6)).toBe('hsl(0,0%,59.0%)');
});
it('Generates light variants', () => {
expect(generateColorVariant('blue', 50, 'light')).toEqual('hsl(240,100%,95.0%)');
expect(generateColorVariant('blue', 100, 'light')).toEqual('hsl(240,100%,90.0%)');
expect(generateColorVariant('blue', 200, 'light')).toEqual('hsl(240,100%,80.0%)');
expect(generateColorVariant('blue', 300, 'light')).toEqual('hsl(240,100%,70.0%)');
expect(generateColorVariant('blue', 400, 'light')).toEqual('hsl(240,100%,60.0%)');
expect(generateColorVariant('blue', 500, 'light')).toEqual('hsl(240,100%,50.0%)');
expect(generateColorVariant('blue', 600, 'light')).toEqual('hsl(240,100%,40.0%)');
expect(generateColorVariant('blue', 700, 'light')).toEqual('hsl(240,100%,30.0%)');
expect(generateColorVariant('blue', 800, 'light')).toEqual('hsl(240,100%,20.0%)');
expect(generateColorVariant('blue', 900, 'light')).toEqual('hsl(240,100%,10.0%)');
expect(generateColorVariant('blue', 950, 'light')).toEqual('hsl(240,100%,5.0%)');
it('Generates light colors', () => {
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'light', 0.2, 0.8)).toBe('hsl(0,0%,77.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'light', 0.2, 0.8)).toBe('hsl(0,0%,14.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'light', 0.4, 0.6)).toBe('hsl(0,0%,59.0%)');
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'light', 0.4, 0.6)).toBe('hsl(0,0%,23.0%)');
});
});

View File

@@ -29,6 +29,8 @@ export type AppThemeLayer = 'root' | 'sidebar' | 'titlebar' | 'content' | 'above
export interface AppThemeLayerStyle {
colors: Record<AppThemeColor, string>;
blackPoint?: number;
whitePoint?: number;
}
interface ThemeColorObj {
@@ -51,7 +53,15 @@ export function generateCSS(t: AppTheme): ThemeColorObj[] {
for (const color of colorNames) {
const rawValue = rootColors[color];
if (!rawValue) continue;
colors.push(...generateColors(color, rawValue, t.appearance));
colors.push(
...generateColors(
color,
rawValue,
t.appearance,
t.layers.root?.blackPoint,
t.layers.root?.whitePoint,
),
);
}
return colors;
@@ -61,39 +71,45 @@ export function generateColors(
name: AppThemeColor,
color: string,
appearance: Appearance,
blackPoint = 0,
whitePoint = 1,
): ThemeColorObj[] {
const colors = [];
for (const variant of appThemeVariants) {
colors.push({ name, variant, cssColor: generateColorVariant(color, variant, appearance) });
colors.push({
name,
variant,
cssColor: generateColorVariant(color, variant, appearance, blackPoint, whitePoint),
});
}
return colors;
}
const lightnessMap: Record<Appearance, Record<AppThemeColorVariant, number>> = {
light: {
50: 1,
100: 0.8,
50: 0.98,
100: 0.88,
200: 0.7,
300: 0.5,
400: 0.3,
500: 0.1,
500: 0,
600: -0.2,
700: -0.3,
800: -0.5,
900: -0.7,
800: -0.45,
900: -0.6,
950: -0.8,
},
dark: {
50: -0.95,
100: -0.8,
200: -0.6,
300: -0.4,
400: -0.2,
500: 0,
600: 0.2,
700: 0.4,
800: 0.5,
900: 0.7,
50: -0.98,
100: -0.93,
200: -0.78,
300: -0.63,
400: -0.44,
500: -0.2,
600: 0,
700: 0.3,
800: 0.6,
900: 0.8,
950: 0.9,
},
};
@@ -102,10 +118,16 @@ export function generateColorVariant(
color: string,
variant: AppThemeColorVariant,
appearance: Appearance,
blackPoint = 0,
whitePoint = 1,
): string {
const { hsl } = parseColor(color || '');
const lightnessMod = lightnessMap[appearance][variant];
const newL = hsl[2] + (100 - hsl[2]) * lightnessMod;
// const lightnessMod = lightnessMap[appearance][variant];
const lightnessMod = (appearance === 'dark' ? 1 : -1) * ((variant / 1000) * 2 - 1);
const newL =
lightnessMod > 0
? hsl[2] + (100 * whitePoint - hsl[2]) * lightnessMod
: hsl[2] + hsl[2] * (1 - blackPoint) * lightnessMod;
return `hsl(${hsl[0]},${hsl[1]}%,${newL.toFixed(1)}%)`;
}

View File

@@ -8,15 +8,17 @@ const darkTheme: AppTheme = {
appearance: 'dark',
layers: {
root: {
blackPoint: 0.15,
whitePoint: 0.95,
colors: {
gray: '#69789b',
red: '#ff1c1c',
gray: '#656196',
red: '#ff2727',
orange: '#ff9411',
yellow: '#ffff1f',
green: '#35ff35',
blue: '#1365ff',
yellow: '#dede34',
green: '#39da39',
blue: '#2e91ff',
pink: '#ff74ff',
violet: '#873fff',
violet: '#a853ff',
},
},
},
@@ -27,8 +29,10 @@ const lightTheme: AppTheme = {
appearance: 'light',
layers: {
root: {
blackPoint: 0.1,
whitePoint: 1,
colors: {
gray: '#69789b',
gray: '#7f8fb0',
red: '#e13939',
orange: '#da881f',
yellow: '#e3b22d',
@@ -63,7 +67,7 @@ export function setAppearance(a?: Appearance) {
document.documentElement.setAttribute('data-appearance', appearance);
document.documentElement.setAttribute('data-theme', theme.name);
let existingStyleEl = document.head.querySelector(`style[data-theme-definition="${theme.name}"]`);
let existingStyleEl = document.head.querySelector(`style[data-theme-definition]`);
if (!existingStyleEl) {
const styleEl = document.createElement('style');
document.head.appendChild(styleEl);
@@ -71,11 +75,16 @@ export function setAppearance(a?: Appearance) {
}
existingStyleEl.textContent = [
`[data-theme="${theme.name}"] {`,
...generateCSS(theme).map(toTailwindVariable),
`/* ${darkTheme.name} */`,
`[data-appearance="dark"] {`,
...generateCSS(darkTheme).map(toTailwindVariable),
'}',
`/* ${lightTheme.name} */`,
`[data-appearance="light"] {`,
...generateCSS(lightTheme).map(toTailwindVariable),
'}',
].join('\n');
existingStyleEl.setAttribute('data-theme-definition', theme.name);
existingStyleEl.setAttribute('data-theme-definition', '');
}
export function getPreferredAppearance(): Appearance {