Files
TREK/client/src/hooks/useIsDark.ts
T
Maurice 3472d06e62 feat(admin): rebuild the addons page as a tile grid with sub-shelves
The three stacked sections become one grid of cards per group, with the
type shown on the tile itself. Bag tracking, the collab features and the
photo providers hang off their parent addon as compact shelf rows
instead of full-width siblings, and each group header counts how many of
its addons are on.

Along the way:
- naver_list_import, airtrail and llm_parsing get catalog entries, so
  their names and descriptions translate like everyone else
- dark detection moves into a shared useIsDark hook that watches the
  .dark class instead of re-deriving settings + matchMedia locally
- the ToggleSwitch knob uses --accent-text so a light accent in dark
  mode no longer renders white-on-white
2026-09-01 16:19:21 +02:00

30 lines
1.2 KiB
TypeScript

import { useState, useEffect } from 'react'
/**
* Returns true while the dark palette is active, read off the `.dark` class on
* `<html>` — the single source of truth that `applyAppearance()` writes.
*
* Deriving it from the settings store instead (`dark_mode === 'auto' &&
* matchMedia(...)`) duplicates theme state into React and misses an OS-level
* switch under `auto`, because nothing re-renders when the media query flips.
* The observer sees every path, including the pre-paint replay in
* `public/theme-boot.js`.
*
* Only for the handful of places that need the flag in JS — an `<img src>` that
* differs per theme. Colours belong on tokens, not on this hook.
*/
export function useIsDark(): boolean {
const [dark, setDark] = useState<boolean>(
() => typeof document !== 'undefined' && document.documentElement.classList.contains('dark'),
)
useEffect(() => {
if (typeof document === 'undefined') return
const mo = new MutationObserver(() => setDark(document.documentElement.classList.contains('dark')))
mo.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
return () => mo.disconnect()
}, [])
return dark
}