A new mark: a padlock on a shield with the W as its keyhole, in the console's teal on a dark shield. It replaces the placeholder W tile in the sidebar, on the sign-in and setup pages, and as the favicon, with app icons and a web manifest. The SVG sources, a single-colour variant, a wordmark and a social preview image live in docs/brand. The console now has a theme choice: dark by default, light, or follow the system, switched from the sidebar or the Account page and remembered per browser. The light palette is a first-class theme rather than a media query, so both are laid out the same way. The README opens with the mark and gains a History section about the 2025 WGX installer this replaces.
32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
import { useState } from "react";
|
|
import { Monitor, Moon, Sun } from "lucide-react";
|
|
import { getThemeChoice, setThemeChoice, type ThemeChoice } from "../theme";
|
|
|
|
const options: { value: ThemeChoice; label: string; icon: typeof Moon }[] = [
|
|
{ value: "dark", label: "Dark", icon: Moon },
|
|
{ value: "light", label: "Light", icon: Sun },
|
|
{ value: "system", label: "System", icon: Monitor },
|
|
];
|
|
|
|
// Dark / Light / System. `compact` shows icons only, for the sidebar.
|
|
export function ThemeSwitch({ compact = false }: { compact?: boolean }) {
|
|
const [choice, setChoice] = useState<ThemeChoice>(getThemeChoice);
|
|
const pick = (v: ThemeChoice) => {
|
|
setThemeChoice(v);
|
|
setChoice(v);
|
|
};
|
|
return (
|
|
<div className="segmented" role="radiogroup" aria-label="Theme">
|
|
{options.map((o) => {
|
|
const Icon = o.icon;
|
|
return (
|
|
<button key={o.value} role="radio" aria-checked={choice === o.value} aria-label={o.label} title={o.label} className={choice === o.value ? "active" : ""} onClick={() => pick(o.value)}>
|
|
<Icon size={14} style={{ verticalAlign: -2 }} />
|
|
{!compact && <span style={{ marginLeft: 6 }}>{o.label}</span>}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|