Replace accent presets with the sunset-to-sea family

Swap Toffee/Slate/Evergreen/Lavender for rose/coral/amber/teal — four ramps
sharing one lightness/saturation curve (src/renderer/src/theme.ts). Default
accent is now teal. Updates ACCENT_COLORS (src/shared/ipc.ts), the settings
default + stale-value coercion (src/main/settings.ts), the dev/store fallbacks
(main.tsx, store/settings.ts), and the native window background to match the
new pageBackground.light (src/main/index.ts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 13:10:53 -04:00
parent 18e1d9a24d
commit af9300d5e3
6 changed files with 104 additions and 123 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ let mainWindow: BrowserWindow | null = null
// src/renderer/src/theme.ts. Set as the window's NATIVE background so the
// one-frame compositor repaint (when a tooltip/dropdown overlay first paints on
// Windows) shows the app's current color instead of a mismatched white flash.
const THEME_BACKGROUND = { light: '#f6f1ef', dark: '#161618' } as const
const THEME_BACKGROUND = { light: '#f7f7f8', dark: '#161618' } as const
// 'system' isn't a real background — resolve it against the OS's current
// preference (nativeTheme.themeSource defaults to 'system', so this tracks it
+6 -1
View File
@@ -23,7 +23,7 @@ const DEFAULTS: Settings = {
maxConcurrent: 2,
filenameTemplate: '%(title)s.%(ext)s',
theme: 'light',
accentColor: 'toffee',
accentColor: 'teal',
clipboardWatch: true,
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS,
proxy: '',
@@ -106,6 +106,11 @@ export function getSettings(): Settings {
if (!downloadOptionsEqual(sanitized, cur.downloadOptions)) {
s.set('downloadOptions', sanitized)
}
// Coerce an accentColor left over from a renamed/removed preset (e.g. an old
// 'toffee' or 'indigo' default) onto the current default, so it resolves cleanly.
if (!(ACCENT_COLORS as readonly string[]).includes(cur.accentColor)) {
s.set('accentColor', DEFAULTS.accentColor)
}
return s.store
}
+1 -1
View File
@@ -18,7 +18,7 @@ if (import.meta.env.DEV && !window.api) {
maxConcurrent: 2,
filenameTemplate: '%(title)s.%(ext)s',
theme: 'light',
accentColor: 'toffee',
accentColor: 'teal',
clipboardWatch: true,
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS,
proxy: '',
+1 -1
View File
@@ -12,7 +12,7 @@ const FALLBACK: Settings = {
maxConcurrent: 2,
filenameTemplate: '%(title)s.%(ext)s',
theme: 'light',
accentColor: 'toffee',
accentColor: 'teal',
clipboardWatch: true,
downloadOptions: DEFAULT_DOWNLOAD_OPTIONS,
proxy: '',
+94 -118
View File
@@ -6,88 +6,87 @@ import {
} from '@fluentui/react-components'
import type { AccentColor } from '@shared/ipc'
// Toffee-brown brand ramp. The product palette (50950) is remapped onto
// Fluent's 16-stop BrandVariants (10 = darkest … 160 = lightest) so the LIGHT
// primary lands on toffee-600 (#825e4a) at index 80, and the DARK primary on a
// lightened toffee-400 (#b5917d) — applied via toffeeDarkOverrides below.
const toffee: BrandVariants = {
10: '#17100d',
20: '#201713',
30: '#2e211a',
40: '#412f25',
50: '#4f3a2d',
60: '#614638',
70: '#735140',
80: '#825e4a',
90: '#946b54',
100: '#a2755d',
110: '#b5917d',
120: '#c7ac9e',
130: '#d3bcae',
140: '#dac8be',
150: '#ece3df',
160: '#f6f1ef'
// "Sunset-to-sea" accent family. All four ramps are built the same way — one
// shared lightness/saturation curve mapped onto Fluent's 16 BrandVariants stops:
// a deep near-black floor (idx 10), a white-text-safe LIGHT primary at idx 80, a
// bright DARK-mode lift at idx 110 (applied via buildDarkOverrides), and a near-
// white top (idx 160). The idx-80 primaries are tuned to equal luminance so none
// reads louder than the rest. Hues are three analogous warms — rose → coral →
// amber — plus teal as the cool complement that anchors the set.
const rose: BrandVariants = {
10: '#20090f',
20: '#3b0e1a',
30: '#581124',
40: '#75132d',
50: '#921637',
60: '#ae1941',
70: '#c91e4c',
80: '#dc2a5a',
90: '#e34670',
100: '#eb6186',
110: '#f17e9d',
120: '#f197af',
130: '#f0b0c1',
140: '#f0c9d3',
150: '#f2dfe4',
160: '#f8f2f3'
}
// Additional accent presets (Phase E). Each ramp reuses toffee's exact
// lightness curve at a different hue, so they read as siblings rather than
// arbitrary colors — only `buildDarkOverrides` below differs from toffee's
// hand-tuned dark lift.
const slate: BrandVariants = {
10: '#0f1215',
20: '#15191e',
30: '#1e242a',
40: '#2b323b',
50: '#343d48',
60: '#404c59',
70: '#4b5968',
80: '#566576',
90: '#617387',
100: '#6b7e94',
110: '#8998a9',
120: '#a6b2bf',
130: '#b7c0cb',
140: '#c4cbd4',
150: '#e1e5ea',
160: '#f1f2f5'
const coral: BrandVariants = {
10: '#210e08',
20: '#39150b',
30: '#541c0d',
40: '#6f230e',
50: '#8a2a0f',
60: '#a43111',
70: '#bc3915',
80: '#d3421a',
90: '#e85831',
100: '#f07554',
110: '#f5957a',
120: '#f4a893',
130: '#f3bdae',
140: '#f1d0c7',
150: '#f3e3de',
160: '#f8f3f1'
}
const evergreen: BrandVariants = {
10: '#0d1712',
20: '#12211a',
30: '#1a2e25',
40: '#254134',
50: '#2d4f3f',
60: '#37624e',
70: '#40735b',
80: '#498368',
90: '#549576',
100: '#5ca382',
110: '#7cb69b',
120: '#9dc8b4',
130: '#afd2c2',
140: '#bedacd',
150: '#deede6',
160: '#eff6f3'
const amber: BrandVariants = {
10: '#221907',
20: '#342507',
30: '#463107',
40: '#593d06',
50: '#6b4905',
60: '#7d5506',
70: '#8d6007',
80: '#9c6b0a',
90: '#c8890a',
100: '#f4a60a',
110: '#f7b634',
120: '#f8c359',
130: '#f3ce83',
140: '#f0daad',
150: '#f1e7d3',
160: '#f9f6f1'
}
const lavender: BrandVariants = {
10: '#120e16',
20: '#191320',
30: '#231b2d',
40: '#32273f',
50: '#3d2f4d',
60: '#4b3a5f',
70: '#58446f',
80: '#644e7e',
90: '#725890',
100: '#7d619e',
110: '#9781b1',
120: '#b1a0c5',
130: '#c0b2d0',
140: '#cbc0d8',
150: '#e5dfec',
160: '#f2f0f6'
const teal: BrandVariants = {
10: '#091f20',
20: '#0a2d2e',
30: '#0b3c3e',
40: '#0c4b4d',
50: '#0d595c',
60: '#0e686b',
70: '#117578',
80: '#148185',
90: '#18a8ad',
100: '#19d2d9',
110: '#33e4ea',
120: '#5ce6ea',
130: '#86e6e9',
140: '#afe7e9',
150: '#d3edee',
160: '#f2f8f8'
}
// Rounder corners — buttons/inputs ~10px, cards ~16px. NOT pills.
@@ -101,32 +100,8 @@ const friendlyRadii = {
// Dark mode keeps Fluent's NEUTRAL (charcoal) surfaces and uses the accent
// ramp only for buttons/links. The default dark brand is too low-contrast on
// near-black, so lift the brand to ramp-110 and flip on-brand text to dark for
// legible buttons. Toffee's lift was hand-tuned (hover/pressed/stroke shades
// don't land exactly on ramp stops); newer presets use buildDarkOverrides,
// which derives the same shape directly from each ramp's own stops.
const toffeeDarkOverrides = {
colorBrandBackground: '#b5917d',
colorBrandBackgroundHover: '#c19f8c',
colorBrandBackgroundPressed: '#a2755d',
colorBrandBackgroundSelected: '#b5917d',
colorNeutralForegroundOnBrand: '#1c1611',
colorCompoundBrandBackground: '#b5917d',
colorCompoundBrandBackgroundHover: '#c19f8c',
colorCompoundBrandBackgroundPressed: '#a2755d',
colorCompoundBrandForeground1: '#c7ac9e',
colorCompoundBrandForeground1Hover: '#d3bcae',
colorCompoundBrandForeground1Pressed: '#b5917d',
colorCompoundBrandStroke: '#b5917d',
colorCompoundBrandStrokeHover: '#c19f8c',
colorBrandForeground1: '#c7ac9e',
colorBrandForeground2: '#d3bcae',
colorBrandForegroundLink: '#c7ac9e',
colorBrandForegroundLinkHover: '#d3bcae',
colorBrandForegroundLinkPressed: '#b5917d',
colorBrandStroke1: '#b5917d',
colorBrandStroke2: '#5f4636'
} as const
// legible buttons. buildDarkOverrides derives that whole shape — hover/pressed/
// stroke shades and all — directly from each ramp's own stops.
function buildDarkOverrides(ramp: BrandVariants): Record<string, string> {
return {
colorBrandBackground: ramp[110],
@@ -174,10 +149,10 @@ function buildPreset(
}
export const ACCENT_PRESETS: Record<AccentColor, AccentPreset> = {
toffee: buildPreset('Toffee', toffee, toffeeDarkOverrides),
slate: buildPreset('Slate', slate, buildDarkOverrides(slate)),
evergreen: buildPreset('Evergreen', evergreen, buildDarkOverrides(evergreen)),
lavender: buildPreset('Lavender', lavender, buildDarkOverrides(lavender))
rose: buildPreset('Rose', rose, buildDarkOverrides(rose)),
coral: buildPreset('Coral', coral, buildDarkOverrides(coral)),
amber: buildPreset('Amber', amber, buildDarkOverrides(amber)),
teal: buildPreset('Teal', teal, buildDarkOverrides(teal))
}
export const ACCENT_OPTIONS: { value: AccentColor; label: string; swatch: string }[] = (
@@ -189,29 +164,30 @@ export const ACCENT_OPTIONS: { value: AccentColor; label: string; swatch: string
}))
export function getTheme(accent: AccentColor, mode: 'light' | 'dark'): Theme {
const preset = ACCENT_PRESETS[accent] ?? ACCENT_PRESETS.toffee
const preset = ACCENT_PRESETS[accent] ?? ACCENT_PRESETS.teal
return mode === 'dark' ? preset.dark : preset.light
}
// Page background behind the app shell. Light: warm toffee paper. Dark: neutral
// charcoal (NOT brown) — the accent shows up only on buttons/links, independent
// of which preset is chosen.
// Page background behind the app shell. Light: a clean, barely-cool off-white.
// Dark: neutral charcoal. Intentionally accent-INDEPENDENT — the accent shows up
// only on buttons/links, so this stays neutral whichever preset is chosen.
// Keep light/dark in sync with THEME_BACKGROUND in src/main/index.ts.
export const pageBackground = {
light: '#f6f1ef',
light: '#f7f7f8',
dark: '#161618'
}
// Per-kind thumbnail tints. Video vs audio differ by ramp DEPTH rather than a
// competing hue, keeping the monochrome-warm Studio look. Theme-aware (light/
// dark) but intentionally NOT accent-aware — these sit on the neutral card
// surface and shouldn't shift hue when the user picks a different accent.
// competing hue, keeping a monochrome look. Theme-aware (light/dark) but
// intentionally NOT accent-aware — a fixed neutral grey that sits quietly on the
// card surface under any of the accent presets, warm or cool.
export const thumbColors = {
light: {
video: { bg: '#ece3df', fg: '#825e4a' },
audio: { bg: '#dac8be', fg: '#5f4636' }
video: { bg: '#eeeef0', fg: '#5e616a' },
audio: { bg: '#e2e2e6', fg: '#4a4d55' }
},
dark: {
video: { bg: '#252025', fg: '#c7ac9e' },
audio: { bg: '#2b2620', fg: '#b5917d' }
video: { bg: '#26262a', fg: '#b5b6bd' },
audio: { bg: '#2c2c31', fg: '#a0a1aa' }
}
} as const
+1 -1
View File
@@ -77,7 +77,7 @@ export const COOKIE_BROWSERS = ['chrome', 'edge', 'firefox', 'brave', 'opera', '
export type CookieBrowser = (typeof COOKIE_BROWSERS)[number]
/** Accent color presets for the brand ramp (see src/renderer/src/theme.ts). */
export const ACCENT_COLORS = ['toffee', 'slate', 'evergreen', 'lavender'] as const
export const ACCENT_COLORS = ['rose', 'coral', 'amber', 'teal'] as const
export type AccentColor = (typeof ACCENT_COLORS)[number]
/** UI color theme: an explicit mode, or 'system' to follow the OS preference. */