fix(a11y): address bot review feedback (contrast, header, i18n, tests)
This commit is contained in:
@@ -13,14 +13,14 @@ import { useAppStore, FONT_OPTIONS, FONT_STACKS } from '../../store';
|
||||
import { SettingsSection, SettingRow, InfoHint, SettingsToggle } from './primitives';
|
||||
|
||||
const THEMES = [
|
||||
{ id: 'auto', label: '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' },
|
||||
{ id: 'solarized', label: 'Solarized', dot: '#268bd2' },
|
||||
{ id: 'rose-pine', label: 'Rosé Pine', dot: '#ebbcba' },
|
||||
{ id: 'catppuccin', label: 'Catppuccin', dot: '#cba6f7' },
|
||||
{ id: 'light', label: 'Light', dot: '#1d6b9f' },
|
||||
{ id: 'light', labelKey: 'settings.theme_light', defaultLabel: 'Light', dot: '#1d6b9f' },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -204,8 +204,8 @@ export default function AppearancePanel() {
|
||||
style={{ '--dot-color': th.dot }}
|
||||
onClick={() => setTheme(th.id)}
|
||||
onKeyDown={(e) => radioGroupKeyDown(e, themeIds, theme, setTheme)}
|
||||
title={th.label}
|
||||
aria-label={th.label}
|
||||
title={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)}
|
||||
|
||||
@@ -149,3 +149,21 @@ describe('AppearancePanel — navigation style picker', () => {
|
||||
useAppStore.getState().setNavStyle('rail');
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "لم يتم اختباره",
|
||||
"hf_source_app_label": "VoiceStudio (مشفّر، موصى به)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "متغير البيئة"
|
||||
"hf_source_env_label": "متغير البيئة",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Nicht getestet",
|
||||
"hf_source_app_label": "VoiceStudio (verschlüsselt, empfohlen)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Umgebungsvariable"
|
||||
"hf_source_env_label": "Umgebungsvariable",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -695,6 +695,8 @@
|
||||
"device": "Device & compute",
|
||||
"device_desc": "Live hardware and backend readouts.",
|
||||
"network_desc": "Proxy for downloads and model fetches.",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light",
|
||||
"translation_desc": "How dubbing translates dialogue, and which engine does it.",
|
||||
"translate_quality": "Translation quality",
|
||||
"translate_quality_desc": "Fast is literal and quick; Cinematic uses the LLM for natural phrasing.",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Sin probar",
|
||||
"hf_source_app_label": "VoiceStudio (cifrado, recomendado)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Variable de entorno"
|
||||
"hf_source_env_label": "Variable de entorno",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Non testé",
|
||||
"hf_source_app_label": "VoiceStudio (chiffré, recommandé)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Variable d’environnement"
|
||||
"hf_source_env_label": "Variable d’environnement",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "परीक्षण नहीं हुआ",
|
||||
"hf_source_app_label": "VoiceStudio (एन्क्रिप्ट किया गया, अनुशंसित)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "परिवेश चर"
|
||||
"hf_source_env_label": "परिवेश चर",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Belum diuji",
|
||||
"hf_source_app_label": "VoiceStudio (terenkripsi, disarankan)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Variabel lingkungan"
|
||||
"hf_source_env_label": "Variabel lingkungan",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Non testato",
|
||||
"hf_source_app_label": "VoiceStudio (crittografato, consigliato)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Variabile d’ambiente"
|
||||
"hf_source_env_label": "Variabile d’ambiente",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "未テスト",
|
||||
"hf_source_app_label": "VoiceStudio(暗号化済み、推奨)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "環境変数"
|
||||
"hf_source_env_label": "環境変数",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -575,7 +575,9 @@
|
||||
"generate_timeout_shadowed_badge": "외부에서 재정의됨",
|
||||
"generate_timeout_shadowed_note": "VoiceStudio 외부의 환경 변수(셸, .env 파일 또는 컨테이너)가 현재 이 값을 설정하고 있습니다 — 해당 변수가 제거될 때까지 여기에 저장한 값은 무시됩니다.",
|
||||
"generate_timeout_shadowed_toast": "저장되었지만, VoiceStudio 외부의 환경 변수가 현재 이 값을 설정하고 있습니다 — 해당 변수가 제거될 때까지 계속 사용됩니다.",
|
||||
"hf_token_not_checked": "테스트 안 함"
|
||||
"hf_token_not_checked": "테스트 안 함",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Niet getest",
|
||||
"hf_source_app_label": "VoiceStudio (versleuteld, aanbevolen)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Omgevingsvariabele"
|
||||
"hf_source_env_label": "Omgevingsvariabele",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Nie przetestowano",
|
||||
"hf_source_app_label": "VoiceStudio (zaszyfrowane, zalecane)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Zmienna środowiskowa"
|
||||
"hf_source_env_label": "Zmienna środowiskowa",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Não testado",
|
||||
"hf_source_app_label": "VoiceStudio (criptografado, recomendado)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Variável de ambiente"
|
||||
"hf_source_env_label": "Variável de ambiente",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Не проверен",
|
||||
"hf_source_app_label": "VoiceStudio (зашифровано, рекомендуется)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Переменная окружения"
|
||||
"hf_source_env_label": "Переменная окружения",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Inte testad",
|
||||
"hf_source_app_label": "VoiceStudio (krypterad, rekommenderas)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Miljövariabel"
|
||||
"hf_source_env_label": "Miljövariabel",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "ยังไม่ได้ทดสอบ",
|
||||
"hf_source_app_label": "VoiceStudio (เข้ารหัส แนะนำ)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "ตัวแปรสภาพแวดล้อม"
|
||||
"hf_source_env_label": "ตัวแปรสภาพแวดล้อม",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Test edilmedi",
|
||||
"hf_source_app_label": "VoiceStudio (şifrelenmiş, önerilen)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Ortam değişkeni"
|
||||
"hf_source_env_label": "Ortam değişkeni",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Не перевірено",
|
||||
"hf_source_app_label": "VoiceStudio (зашифровано, рекомендовано)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Змінна середовища"
|
||||
"hf_source_env_label": "Змінна середовища",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "Chưa kiểm tra",
|
||||
"hf_source_app_label": "VoiceStudio (được mã hóa, khuyên dùng)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "Biến môi trường"
|
||||
"hf_source_env_label": "Biến môi trường",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
@@ -840,7 +840,9 @@
|
||||
"generate_timeout_shadowed_badge": "已被外部覆盖",
|
||||
"generate_timeout_shadowed_note": "VoiceStudio 之外的一个环境变量(终端、.env 文件或容器)当前正在设置此值——在移除该变量之前,此处保存的值会被忽略。",
|
||||
"generate_timeout_shadowed_toast": "已保存,但 VoiceStudio 之外的一个环境变量当前正在设置此值——在移除该变量之前,将继续使用该值。",
|
||||
"hf_token_not_checked": "未测试"
|
||||
"hf_token_not_checked": "未测试",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"about": {
|
||||
"app": "应用",
|
||||
|
||||
@@ -353,7 +353,9 @@
|
||||
"hf_token_not_checked": "未測試",
|
||||
"hf_source_app_label": "VoiceStudio(已加密,建議)",
|
||||
"hf_source_cli_label": "HuggingFace CLI",
|
||||
"hf_source_env_label": "環境變數"
|
||||
"hf_source_env_label": "環境變數",
|
||||
"theme_auto": "System Auto",
|
||||
"theme_light": "Light"
|
||||
},
|
||||
"bootstrap": {
|
||||
"title": "VoiceStudio",
|
||||
|
||||
+18
-4
@@ -567,7 +567,7 @@
|
||||
[data-theme="light"] {
|
||||
--color-fg: #073642;
|
||||
--color-fg-muted: #586e75;
|
||||
--color-fg-subtle: #657b83;
|
||||
--color-fg-subtle: #4a5a60;
|
||||
--color-fg-inverse: #fdf6e3;
|
||||
|
||||
--color-bg: #fdf6e3;
|
||||
@@ -592,7 +592,7 @@
|
||||
--chrome-bg: #eee8d5;
|
||||
--chrome-fg: #073642;
|
||||
--chrome-fg-muted: #586e75;
|
||||
--chrome-fg-dim: #657b83;
|
||||
--chrome-fg-dim: #4a5a60;
|
||||
--chrome-border: #d5ccb7;
|
||||
|
||||
color-scheme: light;
|
||||
@@ -605,7 +605,7 @@
|
||||
[data-theme="auto"] {
|
||||
--color-fg: #073642;
|
||||
--color-fg-muted: #586e75;
|
||||
--color-fg-subtle: #657b83;
|
||||
--color-fg-subtle: #4a5a60;
|
||||
--color-fg-inverse: #fdf6e3;
|
||||
|
||||
--color-bg: #fdf6e3;
|
||||
@@ -630,7 +630,7 @@
|
||||
--chrome-bg: #eee8d5;
|
||||
--chrome-fg: #073642;
|
||||
--chrome-fg-muted: #586e75;
|
||||
--chrome-fg-dim: #657b83;
|
||||
--chrome-fg-dim: #4a5a60;
|
||||
--chrome-border: #d5ccb7;
|
||||
|
||||
color-scheme: light;
|
||||
@@ -7291,6 +7291,20 @@ button.dub-stepper__action:focus-visible {
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
/* Override header gradient for light modes */
|
||||
[data-theme="light"] .header-area h1 {
|
||||
background: linear-gradient(135deg, var(--chrome-fg) 0%, var(--chrome-fg-muted) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-theme="auto"] .header-area h1 {
|
||||
background: linear-gradient(135deg, var(--chrome-fg) 0%, var(--chrome-fg-muted) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
APP-WIDE BORDER / DIVIDER REMOVAL (owner-approved)
|
||||
Zero every decorative border/hairline token. Kept LAST in source order so
|
||||
|
||||
Reference in New Issue
Block a user