forked from mirror/crumbs-original
feat(theme): rebuild dark note colours as a consistent family
This commit is contained in:
committed by
Fred Cabassut
parent
365f941240
commit
9600c62288
@@ -8,13 +8,13 @@ describe('getNoteColor', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns dark color when isDark is true', () => {
|
it('returns dark color when isDark is true', () => {
|
||||||
expect(getNoteColor('default', true)).toBe('#2a2520');
|
expect(getNoteColor('default', true)).toBe('#21201f');
|
||||||
expect(getNoteColor('coral', true)).toBe('#4a2522');
|
expect(getNoteColor('coral', true)).toBe('#3f1f1c');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns default color for unknown color name', () => {
|
it('returns default color for unknown color name', () => {
|
||||||
expect(getNoteColor('unknown' as any, false)).toBe('#faf5eb');
|
expect(getNoteColor('unknown' as any, false)).toBe('#faf5eb');
|
||||||
expect(getNoteColor('unknown' as any, true)).toBe('#2a2520');
|
expect(getNoteColor('unknown' as any, true)).toBe('#21201f');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has matching keys in light and dark color maps', () => {
|
it('has matching keys in light and dark color maps', () => {
|
||||||
@@ -23,3 +23,43 @@ describe('getNoteColor', () => {
|
|||||||
expect(lightKeys).toEqual(darkKeys);
|
expect(lightKeys).toEqual(darkKeys);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** WCAG relative luminance. */
|
||||||
|
function relativeLuminance(hex: string): number {
|
||||||
|
const n = parseInt(hex.slice(1), 16);
|
||||||
|
const channels = [(n >> 16) & 255, (n >> 8) & 255, n & 255].map((v) => {
|
||||||
|
const s = v / 255;
|
||||||
|
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
|
||||||
|
});
|
||||||
|
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
function contrastRatio(a: string, b: string): number {
|
||||||
|
const [l1, l2] = [relativeLuminance(a), relativeLuminance(b)];
|
||||||
|
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('dark note colours', () => {
|
||||||
|
// Must track [data-theme="dark"] in src/app.css.
|
||||||
|
const DARK_TEXT = '#ece3d3';
|
||||||
|
const DARK_TEXT_MUTED = '#b3a695';
|
||||||
|
|
||||||
|
it('keeps body text above the AA threshold on every dark card', () => {
|
||||||
|
for (const [name, { bg }] of Object.entries(NOTE_COLORS_DARK)) {
|
||||||
|
expect(contrastRatio(bg, DARK_TEXT), `${name} (${bg})`).toBeGreaterThanOrEqual(4.5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps muted text above the AA threshold on every dark card', () => {
|
||||||
|
// This is the invariant the old palette broke: muted/struck-through text
|
||||||
|
// became unreadable on the lighter surfaces.
|
||||||
|
for (const [name, { bg }] of Object.entries(NOTE_COLORS_DARK)) {
|
||||||
|
expect(contrastRatio(bg, DARK_TEXT_MUTED), `${name} (${bg})`).toBeGreaterThanOrEqual(4.5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('holds the twelve surfaces within a narrow lightness band so they read as a family', () => {
|
||||||
|
const luminances = Object.values(NOTE_COLORS_DARK).map((c) => relativeLuminance(c.bg));
|
||||||
|
expect(Math.max(...luminances) - Math.min(...luminances)).toBeLessThan(0.03);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+12
-12
@@ -16,18 +16,18 @@ export const NOTE_COLORS: Record<NoteColor, { bg: string; label: string }> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const NOTE_COLORS_DARK: Record<NoteColor, { bg: string; label: string }> = {
|
export const NOTE_COLORS_DARK: Record<NoteColor, { bg: string; label: string }> = {
|
||||||
default: { bg: '#2a2520', label: 'Default' },
|
default: { bg: '#21201f', label: 'Default' },
|
||||||
coral: { bg: '#4a2522', label: 'Coral' },
|
coral: { bg: '#3f1f1c', label: 'Coral' },
|
||||||
peach: { bg: '#4a3020', label: 'Peach' },
|
peach: { bg: '#3f2b1c', label: 'Peach' },
|
||||||
sand: { bg: '#3a3520', label: 'Sand' },
|
sand: { bg: '#3c371b', label: 'Sand' },
|
||||||
mint: { bg: '#2a3a22', label: 'Mint' },
|
mint: { bg: '#2a3d1d', label: 'Mint' },
|
||||||
sage: { bg: '#223a32', label: 'Sage' },
|
sage: { bg: '#1f3d36', label: 'Sage' },
|
||||||
fog: { bg: '#222e3a', label: 'Fog' },
|
fog: { bg: '#253946', label: 'Fog' },
|
||||||
storm: { bg: '#1e2a35', label: 'Storm' },
|
storm: { bg: '#1d283d', label: 'Storm' },
|
||||||
dusk: { bg: '#352540', label: 'Dusk' },
|
dusk: { bg: '#3d2843', label: 'Dusk' },
|
||||||
blossom: { bg: '#3a2830', label: 'Blossom' },
|
blossom: { bg: '#3d2129', label: 'Blossom' },
|
||||||
clay: { bg: '#302e28', label: 'Clay' },
|
clay: { bg: '#363126', label: 'Clay' },
|
||||||
chalk: { bg: '#2e2e30', label: 'Chalk' }
|
chalk: { bg: '#303036', label: 'Chalk' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getNoteColor(color: NoteColor, isDark: boolean): string {
|
export function getNoteColor(color: NoteColor, isDark: boolean): string {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ test.describe.serial('Dark mode', () => {
|
|||||||
const noteCard = page.getByTestId('note-card').first();
|
const noteCard = page.getByTestId('note-card').first();
|
||||||
await expect(noteCard).toBeVisible();
|
await expect(noteCard).toBeVisible();
|
||||||
const bg = await noteCard.evaluate((el) => getComputedStyle(el).backgroundColor);
|
const bg = await noteCard.evaluate((el) => getComputedStyle(el).backgroundColor);
|
||||||
// Dark default color is #2a2520 = rgb(42, 37, 32); NOT light default rgb(250, 245, 235)
|
// Dark default color is #21201f = rgb(33, 32, 31); NOT light default rgb(250, 245, 235)
|
||||||
expect(bg).not.toBe('rgb(250, 245, 235)');
|
expect(bg).not.toBe('rgb(250, 245, 235)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ test.describe('Public Note Sharing', () => {
|
|||||||
await expect(publicPage.locator('html')).toHaveAttribute('data-theme', 'dark');
|
await expect(publicPage.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||||
await expect(publicPage.getByTestId('shared-note')).toHaveCSS(
|
await expect(publicPage.getByTestId('shared-note')).toHaveCSS(
|
||||||
'background-color',
|
'background-color',
|
||||||
'rgb(34, 46, 58)'
|
'rgb(37, 57, 70)'
|
||||||
);
|
);
|
||||||
|
|
||||||
await context.close();
|
await context.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user