diff --git a/app/globals.css b/app/globals.css index 0297c65ce..01c1fbcc1 100644 --- a/app/globals.css +++ b/app/globals.css @@ -171,6 +171,17 @@ } } +/* A tag lozenge's tint (KEYWORD_PALETTE fill): the hue at 10%, laid over the + page background rather than over whatever is behind the tag. A + see-through bg-amber-500/10 took on the row's colour, so on an unread, + selected or hovered row the tag text fell below 4.5:1. On the plain page + it looks the same as before. */ +@utility tag-fill-* { + --tag-fill: --alpha(--value(--color-*) / 10%); + background-color: var(--color-background); + background-image: linear-gradient(var(--tag-fill), var(--tag-fill)); +} + /* The default border colour. In the base layer, so a border colour utility (border-primary, border-transparent, border-destructive, border-input) diff --git a/stores/__tests__/keyword-palette-fill.test.ts b/stores/__tests__/keyword-palette-fill.test.ts new file mode 100644 index 000000000..d691538e6 --- /dev/null +++ b/stores/__tests__/keyword-palette-fill.test.ts @@ -0,0 +1,54 @@ +import { describe, it, expect, beforeAll } from 'vitest'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs'; +import os from 'os'; +import path from 'path'; +import type { Root } from 'postcss'; +import { KEYWORD_PALETTE } from '../settings-store'; + +/** + * A tag lozenge sits on list rows that change colour: unread, selected, + * hovered. A see-through fill took that colour on, and an amber tag's text + * fell below 4.5:1 on an unread row (the a11y guard's color-contrast). + * Build globals.css with Tailwind to see what each fill actually compiles to. + */ +let root: Root; + +beforeAll(async () => { + const postcss = (await import('postcss')).default; + const tailwind = (await import('@tailwindcss/postcss')).default; + const file = path.join(process.cwd(), 'app', 'globals.css'); + // Scan a folder holding only the palette's fills. + const base = mkdtempSync(path.join(os.tmpdir(), 'tag-fill-')); + try { + const fills = Object.values(KEYWORD_PALETTE).map((color) => color.fill).join(' '); + writeFileSync(path.join(base, 'fills.html'), ``); + root = (await postcss([tailwind({ base })]).process(readFileSync(file, 'utf8'), { from: file })).root; + } finally { + rmSync(base, { recursive: true, force: true }); + } +}, 60_000); + +/** The values `prop` takes in the class's rule, fallbacks included. */ +function values(cls: string, prop: string): string[] { + const found: string[] = []; + root.walkRules(`.${cls.replace(/\//g, '\\/')}`, (rule) => { + rule.walkDecls(prop, (decl) => { + found.push(decl.value); + }); + }); + return found; +} + +describe('KEYWORD_PALETTE fills', () => { + it('paint the tint over the page background, so the row behind a tag cannot lower its contrast', () => { + const seeThrough = Object.entries(KEYWORD_PALETTE) + .filter(([, color]) => { + const base = values(color.fill, 'background-color'); + return base.length === 0 + || base.some((v) => v !== 'var(--color-background)') + || values(color.fill, 'background-image').length === 0; + }) + .map(([key, color]) => `${key}: ${color.fill} -> ${values(color.fill, 'background-color').join(' | ') || 'no background'}`); + expect(seeThrough).toEqual([]); + }); +}); diff --git a/stores/__tests__/settings-store-keywords.test.ts b/stores/__tests__/settings-store-keywords.test.ts index 407475431..b804ef625 100644 --- a/stores/__tests__/settings-store-keywords.test.ts +++ b/stores/__tests__/settings-store-keywords.test.ts @@ -75,7 +75,7 @@ describe('settings-store keywords', () => { // at runtime and each has to carry its own utility prefix. Object.values(KEYWORD_PALETTE).forEach((entry) => { expect(entry.dot).toMatch(/^bg-/); - expect(entry.fill).toMatch(/^bg-/); + expect(entry.fill).toMatch(/^tag-fill-/); expect(entry.border).toMatch(/^border-/); expect(entry.text).toMatch(/^text-.* dark:text-/); expect(entry.rowTint).toMatch(/^bg-.* dark:bg-/); diff --git a/stores/settings-store.ts b/stores/settings-store.ts index 8c9a9d591..395397c3a 100644 --- a/stores/settings-store.ts +++ b/stores/settings-store.ts @@ -423,7 +423,11 @@ export interface KeywordColor { dot: string; /** The same solid colour as `dot`, for glyphs that take a text colour. */ icon: string; - /** Lozenge background. */ + /** + * Lozenge background. Opaque (tag-fill-*, app/globals.css) wherever `text` + * is a hue: a see-through fill takes on the row's colour, and the text's + * contrast with it. + */ fill: string; /** Lozenge border. */ border: string; @@ -445,47 +449,47 @@ export interface KeywordColor { */ export const KEYWORD_PALETTE: Record = { // light - 'red-light': { dot: 'bg-red-300', icon: 'text-red-300', fill: 'bg-red-300/10', border: 'border-red-300/30', text: 'text-red-600 dark:text-red-200', rowTint: 'bg-red-50/60 dark:bg-red-950/20' }, - 'orange-light': { dot: 'bg-orange-300', icon: 'text-orange-300', fill: 'bg-orange-300/10', border: 'border-orange-300/30', text: 'text-orange-600 dark:text-orange-200', rowTint: 'bg-orange-50/60 dark:bg-orange-950/20' }, - 'amber-light': { dot: 'bg-amber-300', icon: 'text-amber-300', fill: 'bg-amber-300/10', border: 'border-amber-300/30', text: 'text-amber-600 dark:text-amber-200', rowTint: 'bg-amber-50/60 dark:bg-amber-950/20' }, - 'yellow-light': { dot: 'bg-yellow-300', icon: 'text-yellow-300', fill: 'bg-yellow-300/10', border: 'border-yellow-300/30', text: 'text-yellow-600 dark:text-yellow-200', rowTint: 'bg-yellow-50/60 dark:bg-yellow-950/20' }, - 'lime-light': { dot: 'bg-lime-300', icon: 'text-lime-300', fill: 'bg-lime-300/10', border: 'border-lime-300/30', text: 'text-lime-600 dark:text-lime-200', rowTint: 'bg-lime-50/60 dark:bg-lime-950/20' }, - 'green-light': { dot: 'bg-green-300', icon: 'text-green-300', fill: 'bg-green-300/10', border: 'border-green-300/30', text: 'text-green-600 dark:text-green-200', rowTint: 'bg-green-50/60 dark:bg-green-950/20' }, - 'teal-light': { dot: 'bg-teal-300', icon: 'text-teal-300', fill: 'bg-teal-300/10', border: 'border-teal-300/30', text: 'text-teal-600 dark:text-teal-200', rowTint: 'bg-teal-50/60 dark:bg-teal-950/20' }, - 'cyan-light': { dot: 'bg-cyan-300', icon: 'text-cyan-300', fill: 'bg-cyan-300/10', border: 'border-cyan-300/30', text: 'text-cyan-600 dark:text-cyan-200', rowTint: 'bg-cyan-50/60 dark:bg-cyan-950/20' }, - 'blue-light': { dot: 'bg-blue-300', icon: 'text-blue-300', fill: 'bg-blue-300/10', border: 'border-blue-300/30', text: 'text-blue-600 dark:text-blue-200', rowTint: 'bg-blue-50/60 dark:bg-blue-950/20' }, - 'indigo-light': { dot: 'bg-indigo-300', icon: 'text-indigo-300', fill: 'bg-indigo-300/10', border: 'border-indigo-300/30', text: 'text-indigo-600 dark:text-indigo-200', rowTint: 'bg-indigo-50/60 dark:bg-indigo-950/20' }, - 'purple-light': { dot: 'bg-purple-300', icon: 'text-purple-300', fill: 'bg-purple-300/10', border: 'border-purple-300/30', text: 'text-purple-600 dark:text-purple-200', rowTint: 'bg-purple-50/60 dark:bg-purple-950/20' }, - 'pink-light': { dot: 'bg-pink-300', icon: 'text-pink-300', fill: 'bg-pink-300/10', border: 'border-pink-300/30', text: 'text-pink-600 dark:text-pink-200', rowTint: 'bg-pink-50/60 dark:bg-pink-950/20' }, - 'gray-light': { dot: 'bg-gray-300', icon: 'text-gray-300', fill: 'bg-gray-300/10', border: 'border-gray-300/30', text: 'text-gray-600 dark:text-gray-200', rowTint: 'bg-gray-50/60 dark:bg-gray-950/20' }, + 'red-light': { dot: 'bg-red-300', icon: 'text-red-300', fill: 'tag-fill-red-300', border: 'border-red-300/30', text: 'text-red-600 dark:text-red-200', rowTint: 'bg-red-50/60 dark:bg-red-950/20' }, + 'orange-light': { dot: 'bg-orange-300', icon: 'text-orange-300', fill: 'tag-fill-orange-300', border: 'border-orange-300/30', text: 'text-orange-600 dark:text-orange-200', rowTint: 'bg-orange-50/60 dark:bg-orange-950/20' }, + 'amber-light': { dot: 'bg-amber-300', icon: 'text-amber-300', fill: 'tag-fill-amber-300', border: 'border-amber-300/30', text: 'text-amber-600 dark:text-amber-200', rowTint: 'bg-amber-50/60 dark:bg-amber-950/20' }, + 'yellow-light': { dot: 'bg-yellow-300', icon: 'text-yellow-300', fill: 'tag-fill-yellow-300', border: 'border-yellow-300/30', text: 'text-yellow-600 dark:text-yellow-200', rowTint: 'bg-yellow-50/60 dark:bg-yellow-950/20' }, + 'lime-light': { dot: 'bg-lime-300', icon: 'text-lime-300', fill: 'tag-fill-lime-300', border: 'border-lime-300/30', text: 'text-lime-600 dark:text-lime-200', rowTint: 'bg-lime-50/60 dark:bg-lime-950/20' }, + 'green-light': { dot: 'bg-green-300', icon: 'text-green-300', fill: 'tag-fill-green-300', border: 'border-green-300/30', text: 'text-green-600 dark:text-green-200', rowTint: 'bg-green-50/60 dark:bg-green-950/20' }, + 'teal-light': { dot: 'bg-teal-300', icon: 'text-teal-300', fill: 'tag-fill-teal-300', border: 'border-teal-300/30', text: 'text-teal-600 dark:text-teal-200', rowTint: 'bg-teal-50/60 dark:bg-teal-950/20' }, + 'cyan-light': { dot: 'bg-cyan-300', icon: 'text-cyan-300', fill: 'tag-fill-cyan-300', border: 'border-cyan-300/30', text: 'text-cyan-600 dark:text-cyan-200', rowTint: 'bg-cyan-50/60 dark:bg-cyan-950/20' }, + 'blue-light': { dot: 'bg-blue-300', icon: 'text-blue-300', fill: 'tag-fill-blue-300', border: 'border-blue-300/30', text: 'text-blue-600 dark:text-blue-200', rowTint: 'bg-blue-50/60 dark:bg-blue-950/20' }, + 'indigo-light': { dot: 'bg-indigo-300', icon: 'text-indigo-300', fill: 'tag-fill-indigo-300', border: 'border-indigo-300/30', text: 'text-indigo-600 dark:text-indigo-200', rowTint: 'bg-indigo-50/60 dark:bg-indigo-950/20' }, + 'purple-light': { dot: 'bg-purple-300', icon: 'text-purple-300', fill: 'tag-fill-purple-300', border: 'border-purple-300/30', text: 'text-purple-600 dark:text-purple-200', rowTint: 'bg-purple-50/60 dark:bg-purple-950/20' }, + 'pink-light': { dot: 'bg-pink-300', icon: 'text-pink-300', fill: 'tag-fill-pink-300', border: 'border-pink-300/30', text: 'text-pink-600 dark:text-pink-200', rowTint: 'bg-pink-50/60 dark:bg-pink-950/20' }, + 'gray-light': { dot: 'bg-gray-300', icon: 'text-gray-300', fill: 'tag-fill-gray-300', border: 'border-gray-300/30', text: 'text-gray-600 dark:text-gray-200', rowTint: 'bg-gray-50/60 dark:bg-gray-950/20' }, // base - red: { dot: 'bg-red-500', icon: 'text-red-500', fill: 'bg-red-500/10', border: 'border-red-500/30', text: 'text-red-700 dark:text-red-300', rowTint: 'bg-red-50 dark:bg-red-950/30' }, - orange: { dot: 'bg-orange-500', icon: 'text-orange-500', fill: 'bg-orange-500/10', border: 'border-orange-500/30', text: 'text-orange-700 dark:text-orange-300', rowTint: 'bg-orange-50 dark:bg-orange-950/30' }, - amber: { dot: 'bg-amber-500', icon: 'text-amber-500', fill: 'bg-amber-500/10', border: 'border-amber-500/30', text: 'text-amber-700 dark:text-amber-300', rowTint: 'bg-amber-50 dark:bg-amber-950/30' }, - yellow: { dot: 'bg-yellow-500', icon: 'text-yellow-500', fill: 'bg-yellow-500/10', border: 'border-yellow-500/30', text: 'text-yellow-700 dark:text-yellow-300', rowTint: 'bg-yellow-50 dark:bg-yellow-950/30' }, - lime: { dot: 'bg-lime-500', icon: 'text-lime-500', fill: 'bg-lime-500/10', border: 'border-lime-500/30', text: 'text-lime-700 dark:text-lime-300', rowTint: 'bg-lime-50 dark:bg-lime-950/30' }, - green: { dot: 'bg-green-500', icon: 'text-green-500', fill: 'bg-green-500/10', border: 'border-green-500/30', text: 'text-green-700 dark:text-green-300', rowTint: 'bg-green-50 dark:bg-green-950/30' }, - teal: { dot: 'bg-teal-500', icon: 'text-teal-500', fill: 'bg-teal-500/10', border: 'border-teal-500/30', text: 'text-teal-700 dark:text-teal-300', rowTint: 'bg-teal-50 dark:bg-teal-950/30' }, - cyan: { dot: 'bg-cyan-500', icon: 'text-cyan-500', fill: 'bg-cyan-500/10', border: 'border-cyan-500/30', text: 'text-cyan-700 dark:text-cyan-300', rowTint: 'bg-cyan-50 dark:bg-cyan-950/30' }, - blue: { dot: 'bg-blue-500', icon: 'text-blue-500', fill: 'bg-blue-500/10', border: 'border-blue-500/30', text: 'text-blue-700 dark:text-blue-300', rowTint: 'bg-blue-50 dark:bg-blue-950/30' }, - indigo: { dot: 'bg-indigo-500', icon: 'text-indigo-500', fill: 'bg-indigo-500/10', border: 'border-indigo-500/30', text: 'text-indigo-700 dark:text-indigo-300', rowTint: 'bg-indigo-50 dark:bg-indigo-950/30' }, - purple: { dot: 'bg-purple-500', icon: 'text-purple-500', fill: 'bg-purple-500/10', border: 'border-purple-500/30', text: 'text-purple-700 dark:text-purple-300', rowTint: 'bg-purple-50 dark:bg-purple-950/30' }, - pink: { dot: 'bg-pink-500', icon: 'text-pink-500', fill: 'bg-pink-500/10', border: 'border-pink-500/30', text: 'text-pink-700 dark:text-pink-300', rowTint: 'bg-pink-50 dark:bg-pink-950/30' }, - gray: { dot: 'bg-gray-500', icon: 'text-gray-500', fill: 'bg-gray-500/10', border: 'border-gray-500/30', text: 'text-gray-700 dark:text-gray-300', rowTint: 'bg-gray-50 dark:bg-gray-950/30' }, + red: { dot: 'bg-red-500', icon: 'text-red-500', fill: 'tag-fill-red-500', border: 'border-red-500/30', text: 'text-red-700 dark:text-red-300', rowTint: 'bg-red-50 dark:bg-red-950/30' }, + orange: { dot: 'bg-orange-500', icon: 'text-orange-500', fill: 'tag-fill-orange-500', border: 'border-orange-500/30', text: 'text-orange-700 dark:text-orange-300', rowTint: 'bg-orange-50 dark:bg-orange-950/30' }, + amber: { dot: 'bg-amber-500', icon: 'text-amber-500', fill: 'tag-fill-amber-500', border: 'border-amber-500/30', text: 'text-amber-700 dark:text-amber-300', rowTint: 'bg-amber-50 dark:bg-amber-950/30' }, + yellow: { dot: 'bg-yellow-500', icon: 'text-yellow-500', fill: 'tag-fill-yellow-500', border: 'border-yellow-500/30', text: 'text-yellow-700 dark:text-yellow-300', rowTint: 'bg-yellow-50 dark:bg-yellow-950/30' }, + lime: { dot: 'bg-lime-500', icon: 'text-lime-500', fill: 'tag-fill-lime-500', border: 'border-lime-500/30', text: 'text-lime-700 dark:text-lime-300', rowTint: 'bg-lime-50 dark:bg-lime-950/30' }, + green: { dot: 'bg-green-500', icon: 'text-green-500', fill: 'tag-fill-green-500', border: 'border-green-500/30', text: 'text-green-700 dark:text-green-300', rowTint: 'bg-green-50 dark:bg-green-950/30' }, + teal: { dot: 'bg-teal-500', icon: 'text-teal-500', fill: 'tag-fill-teal-500', border: 'border-teal-500/30', text: 'text-teal-700 dark:text-teal-300', rowTint: 'bg-teal-50 dark:bg-teal-950/30' }, + cyan: { dot: 'bg-cyan-500', icon: 'text-cyan-500', fill: 'tag-fill-cyan-500', border: 'border-cyan-500/30', text: 'text-cyan-700 dark:text-cyan-300', rowTint: 'bg-cyan-50 dark:bg-cyan-950/30' }, + blue: { dot: 'bg-blue-500', icon: 'text-blue-500', fill: 'tag-fill-blue-500', border: 'border-blue-500/30', text: 'text-blue-700 dark:text-blue-300', rowTint: 'bg-blue-50 dark:bg-blue-950/30' }, + indigo: { dot: 'bg-indigo-500', icon: 'text-indigo-500', fill: 'tag-fill-indigo-500', border: 'border-indigo-500/30', text: 'text-indigo-700 dark:text-indigo-300', rowTint: 'bg-indigo-50 dark:bg-indigo-950/30' }, + purple: { dot: 'bg-purple-500', icon: 'text-purple-500', fill: 'tag-fill-purple-500', border: 'border-purple-500/30', text: 'text-purple-700 dark:text-purple-300', rowTint: 'bg-purple-50 dark:bg-purple-950/30' }, + pink: { dot: 'bg-pink-500', icon: 'text-pink-500', fill: 'tag-fill-pink-500', border: 'border-pink-500/30', text: 'text-pink-700 dark:text-pink-300', rowTint: 'bg-pink-50 dark:bg-pink-950/30' }, + gray: { dot: 'bg-gray-500', icon: 'text-gray-500', fill: 'tag-fill-gray-500', border: 'border-gray-500/30', text: 'text-gray-700 dark:text-gray-300', rowTint: 'bg-gray-50 dark:bg-gray-950/30' }, // dark - 'red-dark': { dot: 'bg-red-700', icon: 'text-red-700', fill: 'bg-red-700/10', border: 'border-red-700/30', text: 'text-red-800 dark:text-red-400', rowTint: 'bg-red-100 dark:bg-red-950/50' }, - 'orange-dark': { dot: 'bg-orange-700', icon: 'text-orange-700', fill: 'bg-orange-700/10', border: 'border-orange-700/30', text: 'text-orange-800 dark:text-orange-400', rowTint: 'bg-orange-100 dark:bg-orange-950/50' }, - 'amber-dark': { dot: 'bg-amber-700', icon: 'text-amber-700', fill: 'bg-amber-700/10', border: 'border-amber-700/30', text: 'text-amber-800 dark:text-amber-400', rowTint: 'bg-amber-100 dark:bg-amber-950/50' }, - 'yellow-dark': { dot: 'bg-yellow-700', icon: 'text-yellow-700', fill: 'bg-yellow-700/10', border: 'border-yellow-700/30', text: 'text-yellow-800 dark:text-yellow-400', rowTint: 'bg-yellow-100 dark:bg-yellow-950/50' }, - 'lime-dark': { dot: 'bg-lime-700', icon: 'text-lime-700', fill: 'bg-lime-700/10', border: 'border-lime-700/30', text: 'text-lime-800 dark:text-lime-400', rowTint: 'bg-lime-100 dark:bg-lime-950/50' }, - 'green-dark': { dot: 'bg-green-700', icon: 'text-green-700', fill: 'bg-green-700/10', border: 'border-green-700/30', text: 'text-green-800 dark:text-green-400', rowTint: 'bg-green-100 dark:bg-green-950/50' }, - 'teal-dark': { dot: 'bg-teal-700', icon: 'text-teal-700', fill: 'bg-teal-700/10', border: 'border-teal-700/30', text: 'text-teal-800 dark:text-teal-400', rowTint: 'bg-teal-100 dark:bg-teal-950/50' }, - 'cyan-dark': { dot: 'bg-cyan-700', icon: 'text-cyan-700', fill: 'bg-cyan-700/10', border: 'border-cyan-700/30', text: 'text-cyan-800 dark:text-cyan-400', rowTint: 'bg-cyan-100 dark:bg-cyan-950/50' }, - 'blue-dark': { dot: 'bg-blue-700', icon: 'text-blue-700', fill: 'bg-blue-700/10', border: 'border-blue-700/30', text: 'text-blue-800 dark:text-blue-400', rowTint: 'bg-blue-100 dark:bg-blue-950/50' }, - 'indigo-dark': { dot: 'bg-indigo-700', icon: 'text-indigo-700', fill: 'bg-indigo-700/10', border: 'border-indigo-700/30', text: 'text-indigo-800 dark:text-indigo-400', rowTint: 'bg-indigo-100 dark:bg-indigo-950/50' }, - 'purple-dark': { dot: 'bg-purple-700', icon: 'text-purple-700', fill: 'bg-purple-700/10', border: 'border-purple-700/30', text: 'text-purple-800 dark:text-purple-400', rowTint: 'bg-purple-100 dark:bg-purple-950/50' }, - 'pink-dark': { dot: 'bg-pink-700', icon: 'text-pink-700', fill: 'bg-pink-700/10', border: 'border-pink-700/30', text: 'text-pink-800 dark:text-pink-400', rowTint: 'bg-pink-100 dark:bg-pink-950/50' }, - 'gray-dark': { dot: 'bg-gray-700', icon: 'text-gray-700', fill: 'bg-gray-700/10', border: 'border-gray-700/30', text: 'text-gray-800 dark:text-gray-400', rowTint: 'bg-gray-100 dark:bg-gray-950/50' }, + 'red-dark': { dot: 'bg-red-700', icon: 'text-red-700', fill: 'tag-fill-red-700', border: 'border-red-700/30', text: 'text-red-800 dark:text-red-400', rowTint: 'bg-red-100 dark:bg-red-950/50' }, + 'orange-dark': { dot: 'bg-orange-700', icon: 'text-orange-700', fill: 'tag-fill-orange-700', border: 'border-orange-700/30', text: 'text-orange-800 dark:text-orange-400', rowTint: 'bg-orange-100 dark:bg-orange-950/50' }, + 'amber-dark': { dot: 'bg-amber-700', icon: 'text-amber-700', fill: 'tag-fill-amber-700', border: 'border-amber-700/30', text: 'text-amber-800 dark:text-amber-400', rowTint: 'bg-amber-100 dark:bg-amber-950/50' }, + 'yellow-dark': { dot: 'bg-yellow-700', icon: 'text-yellow-700', fill: 'tag-fill-yellow-700', border: 'border-yellow-700/30', text: 'text-yellow-800 dark:text-yellow-400', rowTint: 'bg-yellow-100 dark:bg-yellow-950/50' }, + 'lime-dark': { dot: 'bg-lime-700', icon: 'text-lime-700', fill: 'tag-fill-lime-700', border: 'border-lime-700/30', text: 'text-lime-800 dark:text-lime-400', rowTint: 'bg-lime-100 dark:bg-lime-950/50' }, + 'green-dark': { dot: 'bg-green-700', icon: 'text-green-700', fill: 'tag-fill-green-700', border: 'border-green-700/30', text: 'text-green-800 dark:text-green-400', rowTint: 'bg-green-100 dark:bg-green-950/50' }, + 'teal-dark': { dot: 'bg-teal-700', icon: 'text-teal-700', fill: 'tag-fill-teal-700', border: 'border-teal-700/30', text: 'text-teal-800 dark:text-teal-400', rowTint: 'bg-teal-100 dark:bg-teal-950/50' }, + 'cyan-dark': { dot: 'bg-cyan-700', icon: 'text-cyan-700', fill: 'tag-fill-cyan-700', border: 'border-cyan-700/30', text: 'text-cyan-800 dark:text-cyan-400', rowTint: 'bg-cyan-100 dark:bg-cyan-950/50' }, + 'blue-dark': { dot: 'bg-blue-700', icon: 'text-blue-700', fill: 'tag-fill-blue-700', border: 'border-blue-700/30', text: 'text-blue-800 dark:text-blue-400', rowTint: 'bg-blue-100 dark:bg-blue-950/50' }, + 'indigo-dark': { dot: 'bg-indigo-700', icon: 'text-indigo-700', fill: 'tag-fill-indigo-700', border: 'border-indigo-700/30', text: 'text-indigo-800 dark:text-indigo-400', rowTint: 'bg-indigo-100 dark:bg-indigo-950/50' }, + 'purple-dark': { dot: 'bg-purple-700', icon: 'text-purple-700', fill: 'tag-fill-purple-700', border: 'border-purple-700/30', text: 'text-purple-800 dark:text-purple-400', rowTint: 'bg-purple-100 dark:bg-purple-950/50' }, + 'pink-dark': { dot: 'bg-pink-700', icon: 'text-pink-700', fill: 'tag-fill-pink-700', border: 'border-pink-700/30', text: 'text-pink-800 dark:text-pink-400', rowTint: 'bg-pink-100 dark:bg-pink-950/50' }, + 'gray-dark': { dot: 'bg-gray-700', icon: 'text-gray-700', fill: 'tag-fill-gray-700', border: 'border-gray-700/30', text: 'text-gray-800 dark:text-gray-400', rowTint: 'bg-gray-100 dark:bg-gray-950/50' }, } as const; /** Palette laid out as the settings picker shows it: lighter, base, darker. */