{
  "name": "ThemeProvider",
  "type": "utility",
  "description": "Owns the active theme and applies it as data-theme, so the token layer resolves the right semantic colors.",
  "export": "ThemeProvider",
  "path": "@gtmi/strix-react/theme",
  "props": [
    {
      "name": "initialTheme",
      "type": "light | dark",
      "description": "Theme to start from when nothing is persisted. Falls back to the system preference, then light."
    },
    {
      "name": "persistTheme",
      "type": "boolean",
      "default": "true",
      "description": "Persist the selection to localStorage."
    },
    {
      "name": "storageKey",
      "type": "string",
      "default": "theme-preference",
      "description": "Key used when persisting."
    }
  ],
  "exports": [
    {
      "name": "useTheme",
      "type": "() => { theme, resolvedTheme, setTheme }",
      "description": "Read and set the active theme. Throws outside a ThemeProvider, because application code calling it means to be themed."
    },
    {
      "name": "useOptionalTheme",
      "type": "() => { theme, resolvedTheme, setTheme }",
      "description": "Same shape, falling back to light with a no-op setter when there is no provider. This is what Strix's own components use, so theming stays opt-in."
    },
    {
      "name": "DEFAULT_THEME",
      "type": "'light'",
      "description": "The fallback theme."
    }
  ],
  "ai_tags": ["theme", "dark-mode", "provider", "context", "infrastructure"],
  "since": "0.2.0",
  "tokens_used": ["[data-theme=\"light\"]", "[data-theme=\"dark\"]"],
  "notes": "ThemeMode is light | dark only. maker-ds also has a `forward` mode, but Strix's token layer defines no values for it: accepting it would set data-theme=\"forward\", match no rule, and silently fall through to the light values - a theme that appears supported and is not. Exists as a context rather than a caller-set attribute because Strix's portalling components render into document.body, outside the element carrying data-theme, and must stamp the theme on their own portal root. The wrapper uses `display: contents` so it adds no layout box. The system preference is read once on mount as a fallback only, with no matchMedia listener, matching maker-ds: an OS switch takes effect on reload, and a live listener would fight a user's explicit choice. Every localStorage and matchMedia access is guarded, because both throw in private mode or under a hardened CSP, and losing a preference is acceptable where failing to render is not."
}
