From ccd7d5c33957cbb3a3476a474227c6130142ee2c Mon Sep 17 00:00:00 2001 From: HUQIANTAO Date: Thu, 4 Jun 2026 20:19:31 +0800 Subject: [PATCH] feat(desktop): custom accent color picker + smooth theme transitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two related appearance improvements: 1. Custom accent color. lib/theme.ts learns a per-user accent stored at reasonix-accent in localStorage. applyAccent() validates (#rgb or #rrggbb), derives the foreground (warm ink on light accents, white on dark) and a 14% alpha soft tint, then writes --accent/--accent-fg/ --accent-soft to the document root in a single rAF frame. The picker in SettingsPanel -> Appearance is a native ; the reset button restores the default warm clay (#d97757). All three locales pick up the new keys (en + zh). 2. Theme transition. The body element now transitions background-color, color, and border-color over 180ms on a light/dark/auto toggle so a theme flip crossfades instead of snapping. A .theme-transitioning class is armed for two rAFs to give the browser a paint frame to capture the from-state (without the microtask hop, the change coalesces with the next paint and the transition is skipped). prefers-reduced-motion disables the transition. The transition is intentionally narrow (no transform/opacity/box-shadow) to keep streamed text and tool cards from repainting during the fade, and it doesn't run on initial mount — only on user-initiated changes from the Settings panel. --- desktop/frontend/src/styles.css | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/desktop/frontend/src/styles.css b/desktop/frontend/src/styles.css index 90878fb0e..3c8072db9 100644 --- a/desktop/frontend/src/styles.css +++ b/desktop/frontend/src/styles.css @@ -292,6 +292,45 @@ body { line-height: 1.6; -webkit-font-smoothing: antialiased; overflow: hidden; + /* Theme transition: crossfade the four surfaces and the accent triplet so a + light/dark/auto switch (or an accent picker drag) doesn't snap. The list + is intentionally narrow — only the colors actually used as "theme" — so a + streaming text delta or a tool card with a transient background color + (status badges) doesn't repaint during the fade. Disabled for users with + prefers-reduced-motion (and the system color-scheme change is + instantaneous on Linux WebKitGTK, where the transition property is + occasionally honored but the OS still reschedules a white flash). */ + transition: + background-color 180ms ease, + color 180ms ease, + border-color 180ms ease; +} + +@media (prefers-reduced-motion: reduce) { + body { + transition: none; + } +} + +/* Theme-transition active state. We add a class to for the duration of + a theme flip so the transition fires; without it the initial computed-style + change would skip the animation. The class is removed on the next rAF, which + gives the browser a paint frame to capture the from-values. */ +html.theme-transitioning, +html.theme-transitioning * { + transition: + background-color 180ms ease, + color 180ms ease, + border-color 180ms ease, + --accent 180ms ease, + --accent-fg 180ms ease, + --accent-soft 180ms ease !important; +} +@media (prefers-reduced-motion: reduce) { + html.theme-transitioning, + html.theme-transitioning * { + transition: none !important; + } } .app { @@ -5255,6 +5294,37 @@ body { } } +/* Accent color picker: a small swatch + hex readout, sized to match .set-row's + left label column. The native renders a square + swatch in WebKitGTK and a circle in Chromium — we can't normalize the + inner shape, so we constrain the input to a 28px square and let the OS + draw whatever it likes inside. The swatch + label get the click target; + clicking the surrounding row doesn't open the picker. */ +.set-accent { + width: 28px; + height: 28px; + border: 1px solid var(--border); + border-radius: 6px; + background: transparent; + padding: 0; + cursor: pointer; + flex: 0 0 auto; +} +.set-accent::-webkit-color-swatch-wrapper { + padding: 2px; +} +.set-accent::-webkit-color-swatch { + border: none; + border-radius: 4px; +} +.set-accent__hex { + font-family: var(--mono); + font-size: 11.5px; + color: var(--fg-dim); + text-transform: lowercase; + margin-left: 2px; +} + /* ── tool card extras: outcome line, nested sub-agent, multi-edit labels ───── */ .tool__summary { padding: 1px 11px 8px 30px;