mirror of
https://github.com/bretzel-app/crumbs.git
synced 2026-10-12 20:06:57 +02:00
feat(theme): give the dark colour picker vivid identifiable chips
This commit is contained in:
committed by
Fred Cabassut
parent
5f1017a359
commit
f35e1f7bbf
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { COLOR_OPTIONS, getNoteColor } from '$lib/utils/colors.js';
|
||||
import { COLOR_OPTIONS, getNoteChip } from '$lib/utils/colors.js';
|
||||
import { getIsDarkMode } from '$lib/utils/theme.svelte.js';
|
||||
import type { NoteColor } from '$lib/types/index.js';
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<button
|
||||
onclick={() => onSelect(option.value)}
|
||||
class="h-8 w-8 rounded-full border-2 transition-transform hover:scale-110 {selected === option.value ? 'border-[var(--text)]' : 'border-[var(--border-subtle)]'}"
|
||||
style="background-color: {getNoteColor(option.value, getIsDarkMode())}"
|
||||
style="background-color: {getNoteChip(option.value, getIsDarkMode())}"
|
||||
title={option.label}
|
||||
data-testid="color-{option.value}"
|
||||
></button>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getNoteColor, NOTE_COLORS, NOTE_COLORS_DARK } from '$lib/utils/colors.js';
|
||||
import {
|
||||
getNoteColor,
|
||||
getNoteChip,
|
||||
NOTE_COLORS,
|
||||
NOTE_COLORS_DARK,
|
||||
NOTE_CHIPS_DARK
|
||||
} from '$lib/utils/colors.js';
|
||||
import type { NoteColor } from '$lib/types/index.js';
|
||||
|
||||
describe('getNoteColor', () => {
|
||||
it('returns light color when isDark is false', () => {
|
||||
@@ -84,3 +91,38 @@ describe('dark note colours', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNoteChip', () => {
|
||||
it('returns the card colour in light mode, where pastels already are the cards', () => {
|
||||
for (const color of Object.keys(NOTE_COLORS) as NoteColor[]) {
|
||||
expect(getNoteChip(color, false)).toBe(getNoteColor(color, false));
|
||||
}
|
||||
});
|
||||
|
||||
it('returns a distinct vivid chip in dark mode, not the card surface', () => {
|
||||
for (const color of Object.keys(NOTE_COLORS_DARK) as NoteColor[]) {
|
||||
expect(getNoteChip(color, true)).not.toBe(getNoteColor(color, true));
|
||||
}
|
||||
});
|
||||
|
||||
it('falls back to default for an unknown colour', () => {
|
||||
expect(getNoteChip('unknown' as any, true)).toBe(NOTE_CHIPS_DARK.default);
|
||||
});
|
||||
|
||||
it('has matching keys with the colour maps', () => {
|
||||
expect(Object.keys(NOTE_CHIPS_DARK).sort()).toEqual(Object.keys(NOTE_COLORS_DARK).sort());
|
||||
});
|
||||
|
||||
it('keeps every pair of chips far enough apart to tell apart at 28px', () => {
|
||||
// The regression this guards: at low chroma coral/peach/blossom and
|
||||
// fog/storm collapse into each other.
|
||||
const entries = Object.entries(NOTE_CHIPS_DARK);
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
for (let j = i + 1; j < entries.length; j++) {
|
||||
const [[nameA, a], [nameB, b]] = [entries[i], entries[j]];
|
||||
const distance = rgbDistance(a, b);
|
||||
expect(distance, `${nameA} vs ${nameB}`).toBeGreaterThan(25);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,3 +39,29 @@ export const COLOR_OPTIONS = Object.entries(NOTE_COLORS).map(([value, { label }]
|
||||
value: value as NoteColor,
|
||||
label
|
||||
}));
|
||||
|
||||
/**
|
||||
* Picker swatches are labels, not surfaces. A 28px circle at card lightness
|
||||
* reads as a grey dot — small patches lose apparent saturation — so the dark
|
||||
* picker uses vivid mid-lightness chips that share the card's hue but not its
|
||||
* lightness. Light mode needs no equivalent: there the pastels are the cards.
|
||||
*/
|
||||
export const NOTE_CHIPS_DARK: Record<NoteColor, string> = {
|
||||
default: '#8d857c',
|
||||
coral: '#d04f43',
|
||||
peach: '#cd7c42',
|
||||
sand: '#c7b138',
|
||||
mint: '#7bbd4c',
|
||||
sage: '#47b89d',
|
||||
fog: '#609abe',
|
||||
storm: '#547bc0',
|
||||
dusk: '#a965bd',
|
||||
blossom: '#c05d78',
|
||||
clay: '#b5954a',
|
||||
chalk: '#c3c3c8'
|
||||
};
|
||||
|
||||
export function getNoteChip(color: NoteColor, isDark: boolean): string {
|
||||
if (!isDark) return getNoteColor(color, false);
|
||||
return NOTE_CHIPS_DARK[color] ?? NOTE_CHIPS_DARK.default;
|
||||
}
|
||||
|
||||
@@ -84,4 +84,30 @@ test.describe.serial('Dark mode', () => {
|
||||
// Dark default color is #21201f = rgb(33, 32, 31); NOT light default rgb(250, 245, 235)
|
||||
expect(bg).not.toBe('rgb(250, 245, 235)');
|
||||
});
|
||||
|
||||
test('Scenario: Colour picker swatches are identifiable in dark mode', async ({
|
||||
authenticatedPage: page
|
||||
}) => {
|
||||
// Given dark theme is active
|
||||
await page.goto('/settings/preferences');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.getByTestId('pref-theme-dark').click();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
|
||||
// When the user opens the colour picker while editing a note
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.getByTestId('new-note-btn').click();
|
||||
await page.getByTestId('note-title-input').fill('Swatch Test');
|
||||
await page.getByTestId('color-picker-toggle').click();
|
||||
await expect(page.getByTestId('color-picker')).toBeVisible();
|
||||
|
||||
// Then a swatch shows its vivid chip rather than the muted card surface
|
||||
await expect(page.getByTestId('color-coral')).toHaveCSS(
|
||||
'background-color',
|
||||
'rgb(208, 79, 67)'
|
||||
);
|
||||
|
||||
await page.getByTestId('close-editor-btn').click();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user