Note: next-themes@0.4.6 emits a React 19 warning of the form
"Encountered a script tag while rendering React component" because
its <ThemeProvider> injects a FOUC-prevention <script> via
React.createElement("script", { dangerouslySetInnerHTML }). The
warning is non-blocking and is tracked upstream in next-themes for
post-0.4. We keep next-themes rather than reimplementing the
provider. See openspec/changes/polish-lesson-view-ux/design.md §D6.
A role="switch", checked while dark is active, wearing
ThemeSwitchTrack. The accessible name keeps naming the current theme
(Theme: Dark) at every width, as the header requires. Pressing it slides
the thumb first and applies the theme once the slide has played — see
useThemeChoice.
Until the theme is known on the client — the server render, the hydration
render, and before the provider has read storage — a disabled placeholder of
the same shape renders, so both passes emit the same HTML.
One press always reaches the other theme. There is no third state:
Immersion Cinema is a dark design, so dark is the default and light is the
alternate a learner opts into.
Theme switch backed by
next-themes.Note:
next-themes@0.4.6emits a React 19 warning of the form "Encountered a script tag while rendering React component" because its<ThemeProvider>injects a FOUC-prevention<script>viaReact.createElement("script", { dangerouslySetInnerHTML }). The warning is non-blocking and is tracked upstream in next-themes for post-0.4. We keepnext-themesrather than reimplementing the provider. Seeopenspec/changes/polish-lesson-view-ux/design.md§D6.A
role="switch", checked while dark is active, wearing ThemeSwitchTrack. The accessible name keeps naming the current theme (Theme: Dark) at every width, as the header requires. Pressing it slides the thumb first and applies the theme once the slide has played — seeuseThemeChoice.Until the theme is known on the client — the server render, the hydration render, and before the provider has read storage — a disabled placeholder of the same shape renders, so both passes emit the same HTML.
One press always reaches the other theme. There is no third state: Immersion Cinema is a dark design, so dark is the default and light is the alternate a learner opts into.