diff --git a/src/App.test.tsx b/src/App.test.tsx index b7abbc7..1120df5 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -34,8 +34,11 @@ describe("React app dashboard state view", () => { ); expect(html).toContain('data-ui-theme-toggle="true"'); + expect(html).toContain('data-ui-theme-current="dark"'); expect(html).toContain('aria-label="Switch to light theme"'); expect(html).not.toContain("aria-pressed"); + expect(html).toContain("Theme"); expect(html).toContain("Dark"); + expect(html).toContain("Light"); }); }); diff --git a/src/lib/ui/components/ThemeToggle.tsx b/src/lib/ui/components/ThemeToggle.tsx index 1c8fb1a..d062a5b 100644 --- a/src/lib/ui/components/ThemeToggle.tsx +++ b/src/lib/ui/components/ThemeToggle.tsx @@ -15,15 +15,22 @@ export function ThemeToggle({ theme, onThemeChange }: ThemeToggleProps) { ); } diff --git a/src/lib/ui/components/render.test.tsx b/src/lib/ui/components/render.test.tsx index 14c1ff5..1a5076d 100644 --- a/src/lib/ui/components/render.test.tsx +++ b/src/lib/ui/components/render.test.tsx @@ -6,6 +6,7 @@ import { FooterCell } from "./FooterCell"; import { IconButton } from "./IconButton"; import { ServiceRow } from "./ServiceRow"; import { TelemetryCard } from "./TelemetryCard"; +import { ThemeToggle } from "./ThemeToggle"; import { dashboardPreviewFixtures } from "../fixtures"; describe("dashboard UI components", () => { @@ -144,4 +145,20 @@ describe("dashboard UI components", () => { expect(iconButton).toContain("class=\"icon-button custom-icon-action "); expect(iconButton).toContain("aria-expanded=\"false\""); }); + + test("renders the theme toggle as a segmented instrument control", () => { + const body = renderToString( + undefined} />, + ); + + expect(body).toContain('class="theme-toggle"'); + expect(body).toContain('data-ui-theme-current="light"'); + expect(body).toContain('aria-label="Switch to dark theme"'); + expect(body).toContain('class="theme-toggle__label"'); + expect(body).toContain('class="theme-toggle__switch"'); + expect(body).toContain('data-active="true"'); + expect(body).toContain("Theme"); + expect(body).toContain("Dark"); + expect(body).toContain("Light"); + }); }); diff --git a/src/lib/ui/components/styles.css b/src/lib/ui/components/styles.css index 086c0c4..15125f8 100644 --- a/src/lib/ui/components/styles.css +++ b/src/lib/ui/components/styles.css @@ -565,35 +565,90 @@ } .theme-toggle { - display: inline-grid; - grid-auto-flow: column; - gap: var(--ui-space-2); + display: grid; + grid-template-columns: 2.72rem minmax(0, 1fr); align-items: center; - justify-content: center; - min-width: 6.5rem; - min-height: 2.15rem; - border: var(--ui-border); - background: var(--ui-color-surface-raised); + width: 9.75rem; + min-width: 0; + min-height: 2.18rem; + border: var(--ui-border-strong); + background: var(--ui-color-surface-module); color: var(--ui-color-text); cursor: pointer; font: inherit; - font-size: 0.62rem; + font-size: 0.55rem; font-weight: 850; letter-spacing: 0; - padding: 0 var(--ui-space-3); + padding: 0.13rem; text-transform: uppercase; + box-shadow: var(--ui-shadow-hard); } .theme-toggle:hover { border-color: var(--ui-color-accent); - color: var(--ui-color-accent); } -.theme-toggle span { +.theme-toggle__label { + display: grid; + align-self: stretch; + place-items: center; + border-right: var(--ui-border); + color: var(--ui-color-muted); + font-size: 0.48rem; + font-weight: 800; line-height: 1; +} + +.theme-toggle__switch { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.1rem; + min-width: 0; + padding-left: 0.1rem; +} + +.theme-toggle__cell { + display: grid; + grid-template-columns: auto auto; + gap: 0.2rem; + align-items: center; + justify-content: center; + min-height: 1.68rem; + border: 1px solid transparent; + color: var(--ui-color-muted); + line-height: 1; + padding: 0 0.22rem; white-space: nowrap; } +.theme-toggle__cell[data-active="true"] { + border-color: var(--ui-color-accent); + background: var(--ui-color-accent); + color: var(--ui-color-accent-contrast); +} + +.theme-toggle__cell > span { + color: currentColor; +} + +.theme-toggle:hover .theme-toggle__cell[data-active="false"] { + border-color: var(--ui-color-line); + color: var(--ui-color-text); +} + +.theme-toggle .icon-glyph { + width: 0.82rem; + height: 0.82rem; + border: 0; + background: transparent; + color: currentColor; +} + +.theme-toggle .icon-glyph svg { + width: 0.78rem; + height: 0.78rem; +} + .icon-glyph { display: inline-grid; width: 1.55rem; diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png index 848033e..2c1e4f7 100644 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png and b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png differ diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png index 5b04c30..fc4484a 100644 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png and b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png differ diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png index 1f6baf4..9c8cd4e 100644 Binary files a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png and b/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png differ