fix(a11y): land the light theme, with one token raised to clear WCAG AA

Closes #1973. Lands #1975 by @CoDe-ReDz.

The app shipped six themes, all dark, and "auto" stayed dark on a light-mode
OS — so a user looking for a light mode found nothing. Light text on a dark
background causes halation for people with astigmatism, which makes this an
accessibility gap rather than a preference.

One correction to the contributor's palette: --chrome-fg-muted at #586e75 gives
4.39:1 against --chrome-bg #eee8d5, just under the 4.5:1 AA threshold for
normal text. Raised to #4d5f66 (5.45:1) in both the explicit light block and
the prefers-color-scheme mirror, keeping it in the Solarized family.

For the record, the two contrast failures the review bot flagged as P1 are not
real: --color-fg-subtle measures 6.66:1 and --chrome-fg-dim 5.86:1, both
comfortably AA. The token that actually failed was one it did not mention.

Everything else the bots raised was already handled on the branch: both theme
labels go through t() with real translations in all 21 locales, and the
header's white-to-grey gradient is overridden for the explicit light theme and
the auto mirror alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ypcgSsh5j2PEonSJiAU1S
This commit is contained in:
Palash Debnath
2026-09-09 21:06:19 -07:00
co-authored by Claude Opus 5
parent cb4aaa1580
commit a31b50c5cb
4 changed files with 16 additions and 8 deletions
+1
View File
@@ -9,6 +9,7 @@ the frozen-backend fallback mirror it for their toolchains.
## [Unreleased]
**Highlights**
- A light theme, and System Auto now follows a light-mode OS instead of staying dark (#1973) — thanks @CoDe-ReDz!
- Generating from a one-character input now says the input was too short, instead of quoting a convolution error (#1826)
- First run asks about text size before the install, not after it (#1849)
- Cloning without a reference clip now says so, instead of naming library parameters you cannot set (#1879)
@@ -13,7 +13,12 @@ import { useAppStore, FONT_OPTIONS, FONT_STACKS } from '../../store';
import { SettingsSection, SettingRow, InfoHint, SettingsToggle } from './primitives';
const THEMES = [
{ id: 'auto', labelKey: 'settings.theme_auto', defaultLabel: 'System Auto', dot: 'linear-gradient(135deg, #fdf6e3 50%, #1d2021 50%)' },
{
id: 'auto',
labelKey: 'settings.theme_auto',
defaultLabel: 'System Auto',
dot: 'linear-gradient(135deg, #fdf6e3 50%, #1d2021 50%)',
},
{ id: 'gruvbox', label: 'Gruvbox', dot: '#d3869b' },
{ id: 'midnight', label: 'Midnight', dot: '#8b5cf6' },
{ id: 'nord', label: 'Nord', dot: '#88c0d0' },
@@ -205,7 +210,9 @@ export default function AppearancePanel() {
onClick={() => setTheme(th.id)}
onKeyDown={(e) => radioGroupKeyDown(e, themeIds, theme, setTheme)}
title={th.labelKey ? t(th.labelKey, { defaultValue: th.defaultLabel }) : th.label}
aria-label={th.labelKey ? t(th.labelKey, { defaultValue: th.defaultLabel }) : th.label}
aria-label={
th.labelKey ? t(th.labelKey, { defaultValue: th.defaultLabel }) : th.label
}
aria-checked={theme === th.id}
role="radio"
tabIndex={radioTabIndex(themeIds, theme, th.id)}
@@ -153,17 +153,17 @@ describe('AppearancePanel — navigation style picker', () => {
describe('AppearancePanel — theme selection', () => {
it('renders localized labels for the Auto and Light themes', () => {
render(<AppearancePanel />);
// The test runner uses the `defaultValue` from our t() hooks.
// This verifies the labels are correctly exposed to screen readers via aria-label.
const autoTheme = screen.getByRole('radio', { name: 'System Auto' });
const lightTheme = screen.getByRole('radio', { name: 'Light' });
expect(autoTheme).toBeInTheDocument();
expect(lightTheme).toBeInTheDocument();
// Verify it generates the correct data attribute for the OS-sync theme
expect(autoTheme).toHaveAttribute('data-radio-value', 'auto');
expect(lightTheme).toHaveAttribute('data-radio-value', 'light');
});
});
});
+2 -2
View File
@@ -591,7 +591,7 @@
--chrome-bg: #eee8d5;
--chrome-fg: #073642;
--chrome-fg-muted: #586e75;
--chrome-fg-muted: #4d5f66;
--chrome-fg-dim: #4a5a60;
--chrome-border: #d5ccb7;
@@ -629,7 +629,7 @@
--chrome-bg: #eee8d5;
--chrome-fg: #073642;
--chrome-fg-muted: #586e75;
--chrome-fg-muted: #4d5f66;
--chrome-fg-dim: #4a5a60;
--chrome-border: #d5ccb7;