{"_id":"@asnewyla/theme","name":"@asnewyla/theme","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@asnewyla/theme","version":"0.2.0","description":"Multi-brand theming — a ThemeProvider that scopes @asnewyla/tokens' CSS custom properties via a data-theme attribute on the document root","license":"MIT","repository":{"type":"git","url":"git+https://github.com/xavierDelaFuente/xd-components.git","directory":"packages/theme"},"publishConfig":{"access":"public"},"main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"sideEffects":false,"keywords":["react","theme","theming","design-tokens"],"peerDependencies":{"react":"^18.0.0 || ^19.0.0","react-dom":"^18.0.0 || ^19.0.0"},"devDependencies":{"@testing-library/jest-dom":"^6.4.0","@testing-library/react":"^16.0.0","@testing-library/user-event":"^14.5.0","@vitest/coverage-v8":"^2.0.0","jsdom":"^24.0.0","react":"^18.3.0","react-dom":"^18.3.0","tsup":"^8.1.0","typescript":"^5.5.0","vitest":"^2.0.0"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","type-check":"tsc --noEmit"},"_nodeVersion":"24.19.0","_id":"@asnewyla/theme@0.2.0","dist":{"integrity":"sha512-jLe4c0i/BKFM6IdNwSSX/E5suMYnKUELBHb3UEzg0Q5/+hPc+V2uxfizlnvmwxNYSgsKuAbIrI0k1Qd9LysVpg==","shasum":"00420c2a30ce394750402526009cb8f7919ffee4","tarball":"https://registry.npmjs.org/@asnewyla/theme/-/theme-0.2.0.tgz","fileCount":9,"unpackedSize":11135,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCGOL8AW4RvZeGvcDNdgRRqw2yJfNmxXcj5as/bVx5T+wIhAO2xUFtwnHo3YPpDLAIoOQysWxqXkUj5u+bjiYmFH3d/"}]},"_npmUser":{"name":"asnewyla","email":"asnewyla@gmail.com"},"directories":{},"maintainers":[{"name":"asnewyla","email":"asnewyla@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/theme_0.2.0_1787564632182_0.5040963078098395"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-24T09:43:51.881Z","0.2.0":"2026-08-24T09:43:52.395Z","modified":"2026-08-24T09:43:52.676Z"},"maintainers":[{"name":"asnewyla","email":"asnewyla@gmail.com"}],"description":"Multi-brand theming — a ThemeProvider that scopes @asnewyla/tokens' CSS custom properties via a data-theme attribute on the document root","keywords":["react","theme","theming","design-tokens"],"repository":{"type":"git","url":"git+https://github.com/xavierDelaFuente/xd-components.git","directory":"packages/theme"},"license":"MIT","readme":"# @asnewyla/theme\n\nMulti-brand theming for `@asnewyla/*` components — a `ThemeProvider` that\nswitches the active `--xd-*` token set at runtime.\n\n## Install\n\n```bash\nnpm install @asnewyla/theme\n```\n\n## Usage\n\nImport the theme stylesheets you want to offer (see `@asnewyla/tokens`),\nthen wrap your app:\n\n```tsx\nimport '@asnewyla/tokens/theme-paper.css';\nimport '@asnewyla/tokens/theme-sand.css';\nimport '@asnewyla/tokens/theme-lavender.css';\nimport { ThemeProvider } from '@asnewyla/theme';\n\nfunction App() {\n  const [theme, setTheme] = useState<'paper' | 'sand' | 'lavender'>('paper');\n  const [mode, setMode] = useState<'light' | 'dark'>();\n\n  return (\n    <ThemeProvider theme={theme} mode={mode}>\n      <YourApp />\n    </ThemeProvider>\n  );\n}\n```\n\n`theme` and `mode` are independent — use either alone, both together, or\nneither. Leaving `mode` unset (the default) lets `prefers-color-scheme`\ndrive light/dark automatically; leaving `theme` unset lets whichever\ntheme's bare fallback (or `@asnewyla/tokens/tokens.css`) apply.\n\n### Props\n\n| Prop | Type | Default |\n|---|---|---|\n| `theme` | `string` | — (unset: no theme override) |\n| `mode` | `'light' \\| 'dark'` | — (unset: follows `prefers-color-scheme`) |\n| `children` | `React.ReactNode` | — (required) |\n\n`theme` is a plain `string`, not a union of the example theme names above —\n`@asnewyla/theme` has no knowledge of which themes exist. It just sets\nwhatever value you give it as `data-theme` on `<html>`; the theme names\nthemselves are defined entirely by whichever `--xd-*` stylesheets you\nimport (`@asnewyla/tokens`'s or your own). `mode` is a real union, since\nevery `@asnewyla/tokens` theme file only ever matches `data-mode=\"light\"`\nor `\"dark\"` — anything else would silently match nothing.\n\n## How it works\n\n`ThemeProvider` doesn't render a wrapping element — it sets `data-theme`\nand `data-mode` on `document.documentElement` (`<html>`) via two\nindependent effects, one per prop, each removing its own attribute on\nunmount. Setting `data-theme`/`data-mode` on `<html>` (not a wrapping\n`<div>`) is deliberate: CSS custom property inheritance follows the real\nDOM tree, not the React tree, so a component that renders through a portal\n(a future `Dialog`, `Select`, `Toast`, mounted straight into\n`document.body`) sits outside `ThemeProvider`'s own JSX position but still\nunder `<html>` — only an attribute on the document root reliably reaches\nit.\n\nLeaving `theme` or `mode` unset doesn't set a default value for that\nattribute — it removes the attribute entirely, so the underlying CSS's own\nfallback (a bare `:root:not([data-theme])` block, or\n`prefers-color-scheme` for mode) takes over. A default of, say,\n`mode=\"light\"` would permanently defeat automatic dark-mode detection for\nevery consumer, since `@asnewyla/tokens`' dark-mode blocks are scoped\n`:not([data-mode=\"light\"])` specifically so the OS preference keeps\nworking until someone explicitly opts into a mode.\n\nSwitching either prop updates its attribute on the next render; React runs\nthe previous effect's cleanup before the new one, so there's no flash of\nan unset attribute in between.\n\n## License\n\nMIT\n","readmeFilename":"","_rev":"1-8c36a5ec73394f27f97cf9dc77508ce7"}