Toggle the color scheme from the rail header
Add a moon/sun button beside the rail's Add icon that flips this device between light and dark, stored under the same theme key the theme picker and ThemeInitScript read. Following the device stays on Settings > General, since the toggle is always an explicit choice.
This commit is contained in:
@@ -3,6 +3,7 @@ import Link from "@moduletrace/twui/layout/Link";
|
|||||||
import Wordmark from "@/src/components/general/wordmark";
|
import Wordmark from "@/src/components/general/wordmark";
|
||||||
import LogoIcon from "@/src/components/general/logo-icon";
|
import LogoIcon from "@/src/components/general/logo-icon";
|
||||||
import RailAddButton from "./rail-add-button";
|
import RailAddButton from "./rail-add-button";
|
||||||
|
import RailThemeToggle from "./rail-theme-toggle";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { useContext } from "react";
|
import { useContext } from "react";
|
||||||
import { AppContext } from "@/src/pages/__root";
|
import { AppContext } from "@/src/pages/__root";
|
||||||
@@ -47,7 +48,10 @@ export default function RailHeader({ show_add = true }: Props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
<Row className="hidden @rail:flex shrink-0 flex-nowrap items-center gap-1! border-0">
|
||||||
|
<RailThemeToggle />
|
||||||
{show_add ? <RailAddButton /> : null}
|
{show_add ? <RailAddButton /> : null}
|
||||||
</Row>
|
</Row>
|
||||||
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Button from "@moduletrace/twui/layout/Button";
|
||||||
|
import { Moon, Sun } from "lucide-react";
|
||||||
|
import useThemePreference from "@/src/hooks/use-theme-preference";
|
||||||
|
import resolveTheme from "@/src/utils/resolve-theme";
|
||||||
|
|
||||||
|
const DarkQuery = "(prefers-color-scheme: dark)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rail header's scheme toggle: one click flips between light and dark
|
||||||
|
* for this device, stored where `ThemeInitScript` reads it. Following the
|
||||||
|
* device stays on Settings > General, since a toggle is always an explicit
|
||||||
|
* choice. It renders after mount only, because the painted scheme is
|
||||||
|
* decided on the client.
|
||||||
|
*/
|
||||||
|
export default function RailThemeToggle() {
|
||||||
|
const { preference, setPreference } = useThemePreference();
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
const [system_is_dark, setSystemIsDark] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
|
||||||
|
const media = window.matchMedia(DarkQuery);
|
||||||
|
const onChange = () => setSystemIsDark(media.matches);
|
||||||
|
|
||||||
|
onChange();
|
||||||
|
media.addEventListener("change", onChange);
|
||||||
|
return () => media.removeEventListener("change", onChange);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!mounted) return null;
|
||||||
|
|
||||||
|
const is_dark =
|
||||||
|
(preference
|
||||||
|
? resolveTheme({ preference, system_is_dark })
|
||||||
|
: system_is_dark
|
||||||
|
? "dark"
|
||||||
|
: "light") == "dark";
|
||||||
|
const title = is_dark ? "Switch to the light theme" : "Switch to the dark theme";
|
||||||
|
const Icon = is_dark ? Moon : Sun;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
title={title}
|
||||||
|
aria-label={title}
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
color="text"
|
||||||
|
onClick={() => setPreference(is_dark ? "light" : "dark")}
|
||||||
|
className="hidden @rail:flex shrink-0 p-1.5! rounded-default-sm text-ink-secondary dark:text-ink-secondary hover:text-ink hover:dark:text-ink hover:bg-sunken hover:dark:bg-sunken transition-colors duration-[var(--m-fast)] focus-visible:ring-2 focus-visible:ring-ink"
|
||||||
|
>
|
||||||
|
<Icon size={18} aria-hidden />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user