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', () => {
|
||||
expect(getNoteColor('default', true)).toBe('#2a2520');
|
||||
expect(getNoteColor('coral', true)).toBe('#4a2522');
|
||||
expect(getNoteColor('default', true)).toBe('#21201f');
|
||||
expect(getNoteColor('coral', true)).toBe('#3f1f1c');
|
||||
});
|
||||
|
||||
it('returns default color for unknown color name', () => {
|
||||
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', () => {
|
||||
@@ -23,3 +23,43 @@ describe('getNoteColor', () => {
|
||||
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 }> = {
|
||||
default: { bg: '#2a2520', label: 'Default' },
|
||||
coral: { bg: '#4a2522', label: 'Coral' },
|
||||
peach: { bg: '#4a3020', label: 'Peach' },
|
||||
sand: { bg: '#3a3520', label: 'Sand' },
|
||||
mint: { bg: '#2a3a22', label: 'Mint' },
|
||||
sage: { bg: '#223a32', label: 'Sage' },
|
||||
fog: { bg: '#222e3a', label: 'Fog' },
|
||||
storm: { bg: '#1e2a35', label: 'Storm' },
|
||||
dusk: { bg: '#352540', label: 'Dusk' },
|
||||
blossom: { bg: '#3a2830', label: 'Blossom' },
|
||||
clay: { bg: '#302e28', label: 'Clay' },
|
||||
chalk: { bg: '#2e2e30', label: 'Chalk' }
|
||||
default: { bg: '#21201f', label: 'Default' },
|
||||
coral: { bg: '#3f1f1c', label: 'Coral' },
|
||||
peach: { bg: '#3f2b1c', label: 'Peach' },
|
||||
sand: { bg: '#3c371b', label: 'Sand' },
|
||||
mint: { bg: '#2a3d1d', label: 'Mint' },
|
||||
sage: { bg: '#1f3d36', label: 'Sage' },
|
||||
fog: { bg: '#253946', label: 'Fog' },
|
||||
storm: { bg: '#1d283d', label: 'Storm' },
|
||||
dusk: { bg: '#3d2843', label: 'Dusk' },
|
||||
blossom: { bg: '#3d2129', label: 'Blossom' },
|
||||
clay: { bg: '#363126', label: 'Clay' },
|
||||
chalk: { bg: '#303036', label: 'Chalk' }
|
||||
};
|
||||
|
||||
export function getNoteColor(color: NoteColor, isDark: boolean): string {
|
||||
|
||||
@@ -81,7 +81,7 @@ test.describe.serial('Dark mode', () => {
|
||||
const noteCard = page.getByTestId('note-card').first();
|
||||
await expect(noteCard).toBeVisible();
|
||||
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)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -139,7 +139,7 @@ test.describe('Public Note Sharing', () => {
|
||||
await expect(publicPage.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
await expect(publicPage.getByTestId('shared-note')).toHaveCSS(
|
||||
'background-color',
|
||||
'rgb(34, 46, 58)'
|
||||
'rgb(37, 57, 70)'
|
||||
);
|
||||
|
||||
await context.close();
|
||||
|
||||
Reference in New Issue
Block a user