Files
Miguel Ángel 9f4d9e64b2 feat(studio): a light and dark theme toggle in the header that remembers the choice (#4839)
* feat(studio): a light and dark theme toggle in the header that remembers the choice

The sun and moon button flips Studio between Graphite light and dark with a
circular reveal, saves the choice, and follows the system until one is made.

* fix(studio): studio opens light the first time, then keeps the saved theme

* fix(studio): drop the theme field's comment, the theme owner states the rule

* fix(studio): the theme toggle shows only in studio's own app and follows the theme on screen

A host that embeds StudioApp gets no toggle; the button reads the document's theme, the reveal drops the default crossfade, and reduced motion skips it.

* fix(studio): the theme reveal leaves other view transitions alone; one default for the toggle

The crossfade rule applies only while the toggle's own transition runs, StudioApp passes the header's single default through, and the reveal's reduced-motion and keyboard paths are tested.

* fix(studio): a second theme click mid-reveal keeps the crossfade off

Only the latest reveal clears the document marker; tests pin the marker while a reveal runs and the circle's radius.

* refactor(studio): the theme toggle shares its icon props and reads as plain functions

Simplify pass: one icon props object for the sun and moon, no useCallback around a handler nothing memoises, cannotReveal named for what it answers, the test reuses the theme reader.

* fix(studio): studio's own app turns the theme toggle on through context; tests parse index.html

App.tsx stays at main's size: main.tsx provides ShowThemeToggle and the header reads it, default off for hosts. The boot-script tests read index.html through DOMParser instead of an HTML regex.
2026-10-01 08:44:57 -07:00

23 lines
802 B
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>HyperFrames Studio</title>
<script>
// Before the first paint: the theme the toggle saved, else light (owner: src/utils/studioTheme.ts).
try {
if (JSON.parse(localStorage.getItem("hf-studio-ui-preferences") || "{}").theme !== "dark")
document.documentElement.dataset.theme = "paper";
} catch (e) {
document.documentElement.dataset.theme = "paper";
}
</script>
</head>
<body>
<div data-hf-id="hf-aph5" id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>