{"_id":"@ecopages/storybook-radiant-vite","_rev":"3-6e394e3702626f18e70a45f8d5af45d5","name":"@ecopages/storybook-radiant-vite","dist-tags":{"rc":"0.1.0-rc.0","latest":"0.1.0"},"versions":{"0.1.0-rc.0":{"name":"@ecopages/storybook-radiant-vite","version":"0.1.0-rc.0","keywords":["storybook","storybook-framework","radiant","ecopages","web-components","vite","ssr"],"license":"MIT","_id":"@ecopages/storybook-radiant-vite@0.1.0-rc.0","maintainers":[{"name":"andeeplus","email":"andrea.zanenghi@gmail.com"}],"dist":{"shasum":"6d8a09668e36bba10d50dd028661d27a8784b070","tarball":"https://registry.npmjs.org/@ecopages/storybook-radiant-vite/-/storybook-radiant-vite-0.1.0-rc.0.tgz","fileCount":87,"integrity":"sha512-rDV0bJA9ic3bZDpIHaAAoCNfAS+/wsQS/uZfFs/RdsDuE14Z3/noawzNUi4K1ZyVRhLBy9SGfocQOFVhyrMsZg==","signatures":[{"sig":"MEUCICM83m2uqZ8ypWXkEEhD4x6kG2fh/a51wiZ9+k6E8DhuAiEA0i1f4snG0ByL8jL959dgQbo2X6/NoDEH8txkqbQ3DgE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":230545},"type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"./preset":{"types":"./dist/preset.d.ts","default":"./dist/preset.js"},"./preview":{"types":"./dist/preview.d.ts","default":"./dist/preview.js"},"./package.json":"./package.json","./renderer-preset":{"types":"./dist/renderer-preset.d.ts","default":"./dist/renderer-preset.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json --noCheck","clean":"rm -rf dist","test:all":"pnpm run test"},"_npmUser":{"name":"andeeplus","email":"andrea.zanenghi@gmail.com"},"repository":{"url":"https://github.com/ecopages/radiant.git","type":"git","directory":"packages/storybook-radiant-vite"},"description":"Storybook framework for @ecopages/radiant — Vite builder, Radiant JSX renderer, and SSR story modes","directories":{},"sideEffects":["./dist/preview.js","./dist/index.js","./dist/entry-preview.js"],"_nodeVersion":"24.15.0","dependencies":{"esbuild":"^0.28.1","ts-dedent":"^2.2.0","@storybook/global":"^5.0.0","@storybook/builder-vite":"^10.5.2","@ecopages/vite-plugin-radiant":"0.1.0-rc.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.1.1","vitest":"^4.1.10","typescript":"~7.0.2","@types/node":"^24.13.2","@ecopages/jsx":"0.3.0-rc.2"},"peerDependencies":{"vite":"^8.0.0","storybook":"^10.5.2","@ecopages/jsx":">=0.3.0-rc.2","@ecopages/radiant":">=0.3.0-rc.2","@ecopages/signals":">=0.3.0-rc.2"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storybook-radiant-vite_0.1.0-rc.0_1787529231551_0.8116018762075983","host":"s3://npm-registry-packages-npm-production"}},"0.1.0-rc.1":{"name":"@ecopages/storybook-radiant-vite","version":"0.1.0-rc.1","keywords":["storybook","storybook-framework","radiant","ecopages","web-components","vite","ssr"],"license":"MIT","_id":"@ecopages/storybook-radiant-vite@0.1.0-rc.1","maintainers":[{"name":"andeeplus","email":"andrea.zanenghi@gmail.com"}],"dist":{"shasum":"2ab17dcf9f20ff384869a1c164360ea98600478f","tarball":"https://registry.npmjs.org/@ecopages/storybook-radiant-vite/-/storybook-radiant-vite-0.1.0-rc.1.tgz","fileCount":89,"integrity":"sha512-CoosrTgSmSKav/U2yCLMSwG9kdLbzPvNj39Tq6Qb18gn1QyhozYNQrg/j7uNy5bfwYhZysDR1jPvyA8kUzxKwQ==","signatures":[{"sig":"MEQCIFVIoc7BDaDVVZCP0u9fBLjmm9c8gOG883q5V5bT/udSAiAltnIbsgE+q20T+Niax62BqlZV221N/NVEwwv7cU3Gsw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ecopages%2fstorybook-radiant-vite@0.1.0-rc.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":245924},"type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"./preset":{"types":"./dist/preset.d.ts","default":"./dist/preset.js"},"./preview":{"types":"./dist/preview.d.ts","default":"./dist/preview.js"},"./package.json":"./package.json","./renderer-preset":{"types":"./dist/renderer-preset.d.ts","default":"./dist/renderer-preset.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json --noCheck","clean":"rm -rf dist","test:all":"pnpm run test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b01c23c7-a664-479e-8cb8-1949c8af5aaa"}},"repository":{"url":"https://github.com/ecopages/radiant.git","type":"git","directory":"packages/storybook-radiant-vite"},"description":"Storybook framework for @ecopages/radiant — Vite builder, Radiant JSX renderer, and SSR story modes","directories":{},"sideEffects":["./dist/preview.js","./dist/index.js","./dist/entry-preview.js"],"_nodeVersion":"24.19.0","dependencies":{"esbuild":"^0.28.1","ts-dedent":"^2.2.0","@storybook/global":"^5.0.0","@storybook/builder-vite":"^10.5.2","@ecopages/vite-plugin-radiant":"0.1.0-rc.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.1.1","vitest":"^4.1.10","typescript":"~7.0.2","@types/node":"^24.13.2","@ecopages/jsx":"0.3.0-rc.5"},"peerDependencies":{"vite":"^8.0.0","storybook":"^10.5.2","@ecopages/jsx":">=0.3.0-rc.5","@ecopages/radiant":">=0.3.0-rc.5","@ecopages/signals":">=0.3.0-rc.5"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/storybook-radiant-vite_0.1.0-rc.1_1788046419365_0.5465392932236086","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"_id":"@ecopages/storybook-radiant-vite@0.1.0","dist":{"shasum":"f1f4cf442b52b83ad786af42dfc3c9bb3ec86b78","tarball":"https://registry.npmjs.org/@ecopages/storybook-radiant-vite/-/storybook-radiant-vite-0.1.0.tgz","fileCount":89,"integrity":"sha512-CdR+9tlhsMltPMCDO8S8t82vPNjntYDDAPUqZnJxHBf9RWG44OPcoP1GRz2fBJbCCxGERNSovsDFILIf7jm24g==","signatures":[{"sig":"MEYCIQDWfSetSthIcH0h2pxeAALyUkVCZwPfP63pUTpgaMOGswIhAOZPjcXD66j4I8nr4lnBbqrZBPZkaBdAYcd5qoWvVzZt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCTs0Q5LRKAG41V8GE6a5algh5i0m8/k23b8qoYSjWJ0gIhAI7JflYl12DfizXWAg4Qpa57GW5/qR081D6kDluq1Gpn"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ecopages%2fstorybook-radiant-vite@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":245826},"name":"@ecopages/storybook-radiant-vite","type":"module","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"./preset":{"types":"./dist/preset.d.ts","default":"./dist/preset.js"},"./preview":{"types":"./dist/preview.d.ts","default":"./dist/preview.js"},"./package.json":"./package.json","./renderer-preset":{"types":"./dist/renderer-preset.d.ts","default":"./dist/renderer-preset.js"}},"license":"MIT","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && tsc -p tsconfig.build.json --noCheck","clean":"rm -rf dist","test:all":"pnpm run test"},"version":"0.1.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"b01c23c7-a664-479e-8cb8-1949c8af5aaa"}},"keywords":["storybook","storybook-framework","radiant","ecopages","web-components","vite","ssr"],"repository":{"url":"https://github.com/ecopages/radiant.git","type":"git","directory":"packages/storybook-radiant-vite"},"description":"Storybook framework for @ecopages/radiant — Vite builder, Radiant JSX renderer, and SSR story modes","directories":{},"maintainers":[{"name":"andeeplus","email":"andrea.zanenghi@gmail.com"}],"sideEffects":["./dist/preview.js","./dist/index.js","./dist/entry-preview.js"],"_nodeVersion":"24.21.0","dependencies":{"esbuild":"^0.28.1","ts-dedent":"^2.2.0","@storybook/global":"^5.0.0","@storybook/builder-vite":"^10.5.2","@ecopages/vite-plugin-radiant":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vite":"^8.1.1","vitest":"^4.1.10","typescript":"~7.0.2","@types/node":"^24.13.2","@ecopages/jsx":"0.3.0"},"peerDependencies":{"vite":"^8.0.0","storybook":"^10.5.2","@ecopages/jsx":">=0.3.0","@ecopages/radiant":">=0.3.0","@ecopages/signals":">=0.3.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/storybook-radiant-vite_0.1.0_1790632858073_0.3721850279026875"}}},"time":{"created":"2026-08-23T23:53:51.439Z","modified":"2026-09-28T22:00:58.617Z","0.1.0-rc.0":"2026-08-23T23:53:51.765Z","0.1.0-rc.1":"2026-08-29T23:33:39.516Z","0.1.0":"2026-09-28T22:00:58.175Z"},"license":"MIT","keywords":["storybook","storybook-framework","radiant","ecopages","web-components","vite","ssr"],"repository":{"url":"https://github.com/ecopages/radiant.git","type":"git","directory":"packages/storybook-radiant-vite"},"description":"Storybook framework for @ecopages/radiant — Vite builder, Radiant JSX renderer, and SSR story modes","maintainers":[{"name":"andeeplus","email":"andrea.zanenghi@gmail.com"}],"readme":"# `@ecopages/storybook-radiant-vite`\n\nStorybook **framework** for [`@ecopages/radiant`](https://www.npmjs.com/package/@ecopages/radiant): Vite builder, Radiant JSX renderer, and SSR → hydrate story modes.\n\nThis is **not** a React framework. Stories use `@ecopages/jsx` (`jsxImportSource`). The TypeScript option `\"jsx\": \"react-jsx\"` is only the compiler’s name for automatic JSX emit — the runtime is `@ecopages/jsx`.\n\n## Install\n\n```bash\nnpm install -D @ecopages/storybook-radiant-vite @ecopages/vite-plugin-radiant storybook \\\n  @ecopages/radiant @ecopages/jsx @ecopages/signals\n```\n\nPeer ranges: `storybook ^10.5.2`, `@ecopages/jsx`, `@ecopages/radiant`, and `@ecopages/signals` `>=0.3.0-rc.4`, and `vite ^8` (optional).\n\n## Configure Storybook\n\n### `.storybook/main.ts`\n\n```ts\nimport type { StorybookConfig } from '@ecopages/storybook-radiant-vite/node';\n\nconst config: StorybookConfig = {\n\tframework: {\n\t\tname: '@ecopages/storybook-radiant-vite',\n\t\toptions: {},\n\t},\n\tstories: ['../src/**/*.mdx', '../src/**/*.stories.@(ts|tsx)'],\n\taddons: ['@storybook/addon-a11y', '@storybook/addon-docs'],\n};\n\nexport default config;\n```\n\n`defineMain(config)` is available as an identity helper if you prefer that style; it does not add runtime behavior.\n\n### Framework options\n\nUse `globalStyleModules` when static SSR needs a stylesheet that is not reachable from the\nstory, view, or element modules. These modules are added to the sandboxed static-preview\niframe only; normal and hydrated previews continue to load their browser styles normally.\n\n```ts\nconst config: StorybookConfig = {\n\tframework: {\n\t\tname: '@ecopages/storybook-radiant-vite',\n\t\toptions: {\n\t\t\tglobalStyleModules: ['/src/styles/tailwind.css'],\n\t\t},\n\t},\n};\n```\n\n### TypeScript (required for JSX)\n\n```json\n{\n\t\"compilerOptions\": {\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"jsxImportSource\": \"@ecopages/jsx\",\n\t\t\"experimentalDecorators\": false,\n\t\t\"useDefineForClassFields\": true\n\t}\n}\n```\n\nImportant: `\"jsx\": \"react-jsx\"` does **not** pull in React. Pair it with `\"jsxImportSource\": \"@ecopages/jsx\"`.\n\n### `.storybook/preview.ts`\n\n```ts\nimport type { Preview } from '@ecopages/storybook-radiant-vite';\nimport '@ecopages/radiant/client/install-hydrator'; // optional if you rely on the framework preview entry\n\nconst preview: Preview = {\n\tparameters: {\n\t\tradiant: {\n\t\t\trenderMode: 'client',\n\t\t},\n\t},\n};\n\nexport default preview;\n```\n\nThe framework’s renderer preset already loads `install-hydrator` via its entry-preview bundle. Re-importing in your preview is harmless and makes the dependency explicit for Vitest setups.\n\n## Mental model\n\n```\nStory CSF\n  └─ parameters.radiant.renderMode\n        ├─ client        → render() → renderToCanvas mounts JSX / DOM in the iframe\n\t├─ ssr-hydrate   → POST /__radiant_ssr → inject markup → import client module → hydrate\n\t└─ ssr-static    → POST /__radiant_ssr → sandboxed iframe with markup (no client module)\n```\n\n| Layer                      | Package entry              | Runs where                   |\n| -------------------------- | -------------------------- | ---------------------------- |\n| Framework preset           | `./preset`                 | Node (Storybook server)      |\n| Renderer preset            | `./renderer-preset`        | Node → wires browser preview |\n| Preview / render           | `entry-preview` (internal) | Browser iframe               |\n| Public CSF types + testing | `.`                        | Browser / Vitest             |\n| Typed `main` config        | `./node`                   | Node                         |\n\n## Writing stories\n\n### `parameters.radiant` (radiant-ui and other design systems)\n\nPublished component packages should stay free of Storybook symbols. Declare SSR linking and component CSS in stories:\n\n```ts\nimport type { Meta, StoryObj } from '@ecopages/storybook-radiant-vite';\nimport { RuiAlert as RuiAlertElement } from './alert.script';\nimport { RuiAlert } from './alert';\n\nconst meta = {\n\ttitle: 'Components/Alert',\n\tcomponent: RuiAlert,\n\tparameters: {\n\t\tradiant: { element: RuiAlertElement, cssImports: ['./alert.css'] },\n\t},\n\targs: { variant: 'info' },\n} satisfies Meta<typeof RuiAlert>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n```\n\n- `parameters.radiant.renderMode` — `client` (default), `ssr-hydrate`, or `ssr-static`.\n- `parameters.radiant.element` — optional SSR host. Prefer a custom-element constructor; omit for presentational views. A JSX view function is accepted by the type but only constructors are linked at runtime.\n- `parameters.radiant.cssImports` — paths relative to the story file; the Vite stamp transform injects side-effect CSS imports. Source-only, never read at runtime.\n\nThose three are the whole authoring surface. The remaining `radiant` fields (`ssrModule`,\n`ssrExport`, `clientModule`, `viewModule`, `viewExport`, `storyModule`, `storyExport`) are\nresolved from `meta.component` and its module stamps — declare one only to override that\ninference. The SSR error banner names the specific field when resolution fails.\n\n> **Important**\n> `radiant` is the framework's contract, and SSR modes cross an HTTP boundary to the Vite\n> middleware. Only JSON-serializable data travels — `args`, module paths, export names. There\n> is no server-side callback hook, so put host state in `args`.\n\n### Project and addon parameters\n\n`parameters` stays open at the top level — `a11y`, `test`, `chromatic`, `viewport` and any\nother addon key type-check as usual. Only `radiant` is closed.\n\nStorybook types its own `Parameters` as `{ [name: string]: any }`, and intersecting that would\ncollapse `radiant` back to `any` — the index signature wins. The framework widens to an\n`unknown` index instead: arbitrary keys still pass, while `parameters.radiant` keeps its real\ntype. Inside `radiant` there is no index signature, so a key belonging to neither the authoring\nnor the derived set is an error under `satisfies`:\n\n```text\nerror TS2353: Object literal may only specify known properties, and 'dialogStage' does not\nexist in type 'RadiantAuthoredParameters & RadiantDerivedParameters'.\n```\n\nThat is the boundary worth enforcing: the framework reads those fields, and a stray key there\nmeans a story is using `radiant` as a config scratchpad.\n\n**Decorator options do not belong in `parameters` at all.** Write the decorator as a factory\nand configure it where it is applied:\n\n```ts\n// .storybook/with-stylesheets.ts\nimport type { Decorator } from '@ecopages/storybook-radiant-vite';\n\nexport function withStylesheets(entries: StylesheetEntry[]): Decorator {\n\treturn (Story) => {\n\t\t/* ...use `entries` directly... */\n\t\treturn Story();\n\t};\n}\n```\n\n```ts\nexport const DocsNavigation: Story = {\n\tdecorators: [withStylesheets([docsNavCss])],\n};\n```\n\nThe options are typed at the call site, the decorator needs no cast on `context.parameters`,\nand there is nothing to keep in sync between a parameter key and the code that reads it.\n\n**Prefer two decorators over one boolean.** Storybook composes `meta` and story decorators\nrather than letting a story replace one, so a `meta`-level decorator plus a per-story override\nis not expressible. That pressure is what produces flags like `trigger: false`. Split the\nbehaviours instead and apply each where it belongs:\n\n```ts\nconst meta = {\n\tdecorators: [withDialogRegistry], // every story needs the registry\n} satisfies Meta<typeof RuiDialog>;\n\nexport const Default: Story = {\n\tdecorators: [withDialogTrigger], // only stories that want a trigger\n};\n\nexport const Registry: Story = {\n\trender: () => /* renders its own triggers, so no trigger decorator */ null,\n};\n```\n\nLikewise, if a story wants entirely different content, that belongs in its `render` — not in a\nflag that makes a decorator substitute content for a `render: () => null`.\n\n**Always annotate with `satisfies`, never a wrapper function.** `Meta<T>` is a conditional\ntype, so TypeScript cannot infer `T` from a parameter typed by it — a `defineMeta(meta)`\nhelper would silently degrade every arg to `any`. `satisfies` contextually types the\nliteral, so `args`, `argTypes` and `render(args)` are all checked against the component's\nreal props while `typeof meta` keeps the literal types `StoryObj` needs.\n\n`export default` must also be the plain `meta` identifier: Storybook's CSF indexer\nstatically analyses the default export and does not accept a call expression.\n\n### Client JSX view\n\n```ts\nimport type { Meta, StoryObj } from '@ecopages/storybook-radiant-vite';\nimport { RadiantCounter } from '../components/radiant-counter';\nimport '../components/radiant-counter.script';\n\nconst meta = {\n\ttitle: 'Client/Counter',\n\tcomponent: RadiantCounter,\n\tparameters: { radiant: { renderMode: 'client' } },\n\targs: { count: 0, label: 'Counter' },\n} satisfies Meta<typeof RadiantCounter>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Default: Story = {};\n```\n\n`component` may be:\n\n1. A JSX function `(args) => JsxRenderable`\n2. A custom-element tag string (`'radiant-counter'`)\n3. A `RadiantElement` subclass (requires `@customElement` metadata)\n\n### SSR hydrate / static\n\nHost SSR needs a Vite-loadable **script module** that exports the element class. When\n`meta.component` is a stamped Radiant element or a linked JSX view, the framework infers that\nmodule and its export; set `ssrModule` and `ssrExport` only for an override. Args are applied on\nthe server as host properties (JSON only — no function hooks).\n\nPure JSX stories use their stamped CSF story module instead and do not need an element script\nmodule. The server replays the story render function with its resolved args.\n\n```ts\nimport type { Meta, StoryObj } from '@ecopages/storybook-radiant-vite';\nimport '../components/radiant-counter.script';\n\nconst SSR_MODULE = '/src/components/radiant-counter.script.tsx';\n\nconst meta = {\n  title: 'SSR/Counter',\n  component: 'radiant-counter',\n  args: { count: 6, label: 'SSR counter' },\n  parameters: {\n    radiant: {\n      renderMode: 'client',\n      ssrModule: SSR_MODULE,\n      ssrExport: 'RadiantCounter',\n    },\n  },\n  render: (args) => <radiant-counter count={args.count} label={args.label} />,\n} satisfies Meta;\n\nexport default meta;\n\nexport const SsrHydrate: StoryObj<typeof meta> = {\n  parameters: {\n    radiant: {\n      renderMode: 'ssr-hydrate',\n      ssrModule: SSR_MODULE,\n      ssrExport: 'RadiantCounter',\n    },\n  },\n};\n\nexport const SsrStatic: StoryObj<typeof meta> = {\n  parameters: {\n    radiant: {\n      renderMode: 'ssr-static',\n      ssrModule: SSR_MODULE,\n      ssrExport: 'RadiantCounter',\n    },\n  },\n};\n```\n\n### `parameters.radiant` contract\n\n| Field          | Required     | Notes                                                                                     |\n| -------------- | ------------ | ----------------------------------------------------------------------------------------- |\n| `renderMode`   | no           | `'client'` \\| `'ssr-hydrate'` \\| `'ssr-static'` (default `client`)                        |\n| `ssrModule`    | no           | Host SSR module; inferred from stamped `meta.component`, or an absolute Vite URL override |\n| `ssrExport`    | no           | Named class export; otherwise first export with `@customElement` metadata                 |\n| `clientModule` | no           | Module to `import()` after markup inject; defaults to SSR response `clientModuleSrc`      |\n| `viewModule`   | no           | View module for authored light-DOM SSR on host stories                                    |\n| `viewExport`   | no           | Named view export in `viewModule`                                                         |\n| `storyModule`  | auto-stamped | CSF module path; enables JSX story SSR when present                                       |\n| `storyExport`  | no           | Story export name (e.g. `Default`)                                                        |\n\nThere is **no** `initialize` callback. SSR goes over HTTP; only serializable `args` cross the boundary. Put host state in `args`.\n\n### CSF features replayed during SSR\n\n| Feature                                                      | SSR support                                                                                                              |\n| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |\n| `meta.render` / story `render` / `component` as JSX function | Yes (`composeStoryRender`)                                                                                               |\n| CSF `decorators` that wrap `() => JsxRenderable`             | Yes (reversed apply)                                                                                                     |\n| Stage helpers inside `render`                                | Preferred for composition stories                                                                                        |\n| `args` via stamped story module                              | Yes                                                                                                                      |\n| `parameters.radiant.*`                                       | Yes                                                                                                                      |\n| Storybook loaders                                            | No                                                                                                                       |\n| Storybook `<Story />` decorators                             | No                                                                                                                       |\n| `play` interactions on `ssr-static`                          | Storybook runs them in the browser, but static markup is inside a sandboxed iframe; treat static previews as visual-only |\n\nFor composed stories (dialog chrome, toast stage, etc.), put layout in `render` helpers rather than Storybook decorators that SSR cannot replay.\n\n### Toolbar override\n\nThe framework registers a toolbar global `radiantRenderMode`:\n\n| Value                                   | Effect                              |\n| --------------------------------------- | ----------------------------------- |\n| `story` (default)                       | Use `parameters.radiant.renderMode` |\n| `client` / `ssr-hydrate` / `ssr-static` | Force that mode for all stories     |\n\n## SSR pipeline (what actually runs)\n\n1. Preview calls `POST /__radiant_ssr` with `{ ssrModule, ssrExport?, viewModule?, viewExport?, storyModule?, storyExport?, args, mode }`.\n2. Vite middleware loads `@ecopages/vite-plugin-radiant/ssr` (`renderSsrComponent`) and your script/view/story modules.\n3. Preview packages (`storybook/test` and siblings) are installed on Node `globalThis` so `ssrLoadModule` can evaluate story and shared helper modules with the same real bindings as the iframe. Server SSR evaluates those imports; it does not invoke Storybook `play` functions.\n4. For stories declaring `parameters.radiant.element`, the view is server-rendered with CSF args (including JSX children) and injected as `authoredContent`.\n5. `@ecopages/*` stay **external** in SSR (via `radiant()`) so ALS / runtime shims are singletons.\n6. `renderSsrComponent` produces markup + assets (CSS via `radiant({ elements: true })`).\n7. For `ssr-hydrate`, preview inserts markup into its mount root, imports the view/client module, and relies on `install-hydrator`. For `ssr-static`, preview puts markup and its styles in a sandboxed iframe; no client module runs there.\n\n## Vitest / portable stories\n\n```ts\n// .storybook/vitest.setup.ts\nimport { beforeAll } from 'vitest';\nimport { setProjectAnnotations } from '@ecopages/storybook-radiant-vite';\nimport '@ecopages/radiant/client/install-hydrator';\nimport * as projectAnnotations from './preview';\n\nconst annotations = setProjectAnnotations([projectAnnotations]);\nbeforeAll(annotations.beforeAll);\n```\n\nAlso exported: `composeStories`, `composeStory`, `render`, `renderToCanvas`.\n\nNote: Storybook’s `compose*` helpers are not fully generic over custom renderers. This package adapts them with boundary casts so CSF types stay usable — treat that as a Storybook limitation, not a type-safe guarantee of every Storybook internal.\n\nPrefer `client` mode in Vitest. SSR modes need the Vite middleware (Storybook dev server).\n\n## Controllers\n\n`RadiantController` hosts (`data-controller`) are not auto-started by the renderer. After mount:\n\n```ts\nimport { startControllers } from '@ecopages/radiant/controller-registry';\n\nplay: async ({ canvasElement }) => {\n  startControllers(canvasElement);\n},\n```\n\nController SSR (`renderController`) is not wired in v1 — use client mode for controller demos.\n\n## Public entrypoints\n\n| Export                                             | Purpose                                                          |\n| -------------------------------------------------- | ---------------------------------------------------------------- |\n| `@ecopages/storybook-radiant-vite`                 | CSF types, `render` / `renderToCanvas`, portable stories helpers |\n| `@ecopages/storybook-radiant-vite/node`            | `StorybookConfig`, `FrameworkOptions`, `defineMain`              |\n| `@ecopages/storybook-radiant-vite/preset`          | Framework preset (`core`, `viteFinal`) — resolved by Storybook   |\n| `@ecopages/storybook-radiant-vite/renderer-preset` | Renderer preset (`previewAnnotations`) — resolved by Storybook   |\n| `@ecopages/storybook-radiant-vite/preview`         | Default `parameters` + toolbar `globalTypes`                     |\n\nThe root entry also exports `definePreview`, `defineRadiantComponent`, and `radiantSsr`, plus\nthe `Meta`, `StoryObj`, `Preview`, renderer, and `parameters.radiant` contract types.\n\n## Known limitations\n\n- **SSR args only** — no server-side function hooks over the middleware.\n- **No controller SSR** in the middleware yet.\n- **Shadow DOM** hosts cannot use SSR modes (Radiant constraint).\n- **Storybook UI still depends on React transitively** (`storybook` → `use-sync-external-store` → `react`, and `@storybook/addon-docs` → `react` / `@mdx-js/react`). That is Storybook’s manager/docs stack. Your stories do **not** import or render through React.\n- **Tag collisions** — two modules registering the same custom element tag will conflict if both load in one session.\n\n## Publishing\n\nThis package depends on `@ecopages/vite-plugin-radiant` for shared Vite primitives (JSX, decorators, SSR externals). Storybook-only code stays here: CSF types, SSR middleware, script-module stamps, and framework HMR.\n\nIt is published from this repo through Changesets. It shares a `fixed` version group with `@ecopages/vite-plugin-radiant`, so a bump to either package bumps both.\n\n## License\n\nMIT\n","readmeFilename":""}