feat(ui): add ThemeToggle — segmented Monitor/Sun/Moon control

Replaces the plain <select> theme dropdown with three icon buttons.
Defaults to "system" (Monitor), matching the existing behavior of
following the OS preference until the user picks an explicit override.
This commit is contained in:
iamdoubz committed 2026-07-02 12:56:51 -05:00
1 parent 4c40ebc939
commit 7ae146f99f
1 file changed
+72
+72
View File
@@ -0,0 +1,72 @@
<script lang="ts">
// Segmented System/Light/Dark control (T7.2, FR-UX-2), replacing the old
// plain <select> — three icon buttons instead of a text dropdown, defaults
// to "system" so the app follows the OS until the user picks an override.
import { Monitor, Sun, Moon } from "@lucide/svelte";
interface Props {
value: "system" | "light" | "dark";
onchange: (value: "system" | "light" | "dark") => void;
}
let { value, onchange }: Props = $props();
const options = [
{ id: "system", label: "Match system", Icon: Monitor },
{ id: "light", label: "Light", Icon: Sun },
{ id: "dark", label: "Dark", Icon: Moon },
] as const;
</script>
<div class="toggle" role="radiogroup" aria-label="Theme">
{#each options as opt (opt.id)}
<button
role="radio"
aria-checked={value === opt.id}
class:active={value === opt.id}
title={opt.label}
aria-label={opt.label}
onclick={() => onchange(opt.id)}
>
<opt.Icon size={15} strokeWidth={2} aria-hidden="true" />
</button>
{/each}
</div>
<style>
.toggle {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 2px;
background: var(--bg-subtle);
border: 1px solid var(--border);
border-radius: var(--radius-full);
}
button {
display: grid;
place-items: center;
width: 26px;
height: 26px;
padding: 0;
background: transparent;
border: none;
border-radius: var(--radius-full);
color: var(--fg-muted);
cursor: pointer;
transition:
background-color 150ms ease-out,
color 150ms ease-out;
}
button:hover {
color: var(--fg);
background: var(--bg-hover);
}
button.active {
background: var(--accent);
color: var(--accent-fg);
}
button:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
}
</style>