@sxl-studio/storybook-addon
v1.1.1
Published
Storybook addon for SXL Studio — displays Figma Embed, component info and design token status for linked components
Maintainers
Readme
@sxl-studio/storybook-addon
Storybook addon for SXL Studio — displays Figma Embed, component info, and design token status for components linked via the SXL Studio Figma plugin.
Changelog
See CHANGELOG.md (e.g. 1.1.1 registry matching fix; 1.1.0 preset CSP, /sxl-tokens, legacy filename alias, quieter logs).
Features
- Figma Embed — live Figma design iframe centered in the addon panel
- Component info — name, description, node ID, direct Figma link
- Contract — component properties and variant count
- Statuses — token assignment and design readiness badges
Installation
npm install @sxl-studio/storybook-addon --save-devSetup
1. Register the addon (main)
List @sxl-studio/storybook-addon in addons. The preset ships with the package and is applied when Storybook loads the addon (no separate preset import in main unless your setup requires the explicit @sxl-studio/storybook-addon/preset entry — see troubleshooting in docs).
Minimal main.ts:
export default {
addons: [
'@sxl-studio/storybook-addon',
// ...other addons
],
};With a shared Storybook config (internal design-system package, monorepo): append to the shared addons array — do not drop existing entries.
import sharedMain from '@your-org/storybook-vue/main';
const config = {
...sharedMain,
addons: [...(sharedMain.addons ?? []), '@sxl-studio/storybook-addon'],
};
export default config;2. Import the registry (preview)
The SXL Studio Figma plugin generates diff-code-connect.<figmaFileKey>.json (or an exported sxl-codeconnect.json). Put it in parameters.sxl.registry. The relative import path is up to your repo (e.g. ../../tokens/tokens/diff-code-connect.xxx.json).
fromDiffCodeConnect(raw)— explicit normalization; use if you prefer one code path.- Raw
import registry from '...json'— often enough for current plugin output.
Preset (automatic): when the addon preset runs, it:
- Serves a nearby
tokens/tokensfolder at/sxl-tokens/…(dev +storybook buildviastaticDirs) socompositionFilePathin the registry resolves without extra Vite config. - Can
resolve.aliasa stable filename likediff-code-connect.SXL-Components.jsonto the realdiff-code-connect.*.jsonwhen only one candidate exists in that folder (see CHANGELOG).
Minimal preview.ts:
import registry from '../path/to/diff-code-connect.<fileKey>.json';
export default {
parameters: {
sxl: { registry },
},
};With a shared preview — merge parameters so shared decorators/globals stay intact:
import sharedPreview from '@your-org/storybook-vue/preview';
import registry from '../../tokens/tokens/diff-code-connect.<fileKey>.json';
export default {
...sharedPreview,
parameters: {
...sharedPreview.parameters,
sxl: { registry },
},
};Alternative — converter:
import raw from '../diff-code-connect.<fileKey>.json';
import { fromDiffCodeConnect } from '@sxl-studio/storybook-addon';
export default {
parameters: {
sxl: { registry: fromDiffCodeConnect(raw) },
},
};3. Match stories to Figma components
Stories are matched to registry entries by explicit sxl.component / sxl.figmaNodeId, or by a heuristic on story title and file path. A registry with a single component is not shown on every story — unrelated stories show a “no integration” state until the story context matches or you set parameters manually.
Per-story matching:
export const Default = {
parameters: {
sxl: { component: 'WButton' },
},
};Or by Figma node ID:
export const Default = {
parameters: {
sxl: { figmaNodeId: '1:23' },
},
};You can also pass data directly without a registry:
export const Default = {
parameters: {
sxl: {
figmaUrl: 'https://www.figma.com/design/abc123?node-id=1-23',
description: 'Primary action button',
tokenStatus: 'assigned',
readiness: 'ready-for-dev',
},
},
};Registry format (sxl-codeconnect.json)
{
"version": 1,
"figmaFileKey": "abc123",
"figmaFileName": "Design System",
"entries": [
{
"nodeId": "1:23",
"displayName": "WButton",
"description": "Primary action button",
"figmaUrl": "https://www.figma.com/design/abc123?node-id=1-23",
"designEmbed": true,
"metadata": true,
"meta": {
"variantCount": 12,
"componentProperties": ["size", "variant", "disabled"],
"tokenStatus": "assigned",
"readiness": "ready-for-dev"
}
}
]
}Parameters reference
| Parameter | Type | Description |
|-----------|------|-------------|
| sxl.registry | SxlRegistry | Global registry object (set once in preview.ts) |
| sxl.component | string | Match entry by displayName |
| sxl.figmaNodeId | string | Match entry by Figma node ID |
| sxl.figmaUrl | string | Direct Figma URL (no registry needed) |
| sxl.description | string | Override description |
| sxl.tokenStatus | "assigned" \| "partial" \| "none" | Override token status |
| sxl.readiness | "complete" \| "ready-for-dev" \| "in-progress" \| "backlog" | Override readiness |
| sxl.compositionSources | Record<string, string> | Map repo-relative composition paths → raw JSON (recommended) |
| sxl.compositionFetchBaseUrl | string | Base URL to fetch() composition by path (e.g. static dir) |
| sxl.compositionDevProxyPrefix | string | Same-origin prefix (e.g. Vite proxy) so fetches avoid CORS to private Git |
| sxl.resolveComposition | (path) => Promise<string \| undefined> | Custom loader for composition file content |
| sxl.debugFigmaEmbed | boolean | Log embed URL and iframe load to the console |
Composition JSON (repo file, not inlined in diff)
The plugin writes only compositionFilePath (and flags) into diff-code-connect. The large JSON lives in your repo; Storybook must resolve it at runtime.
Option A — glob + raw import (Vite):
// preview.ts
const sources = import.meta.glob('../packages/ds/**/*.json', {
query: '?raw',
import: 'default',
eager: true,
}) as Record<string, string>;
function indexByBasename(map: Record<string, string>): Record<string, string> {
const out: Record<string, string> = {};
for (const [k, v] of Object.entries(map)) {
const rel = k.replace(/^.*?\/packages\//, 'packages/'); // align with compositionFilePath in diff
out[rel] = v;
}
return out;
}
export default {
parameters: {
sxl: {
registry: fromDiffCodeConnect(raw),
compositionSources: indexByBasename(sources),
},
},
};Match keys to compositionFilePath from the registry (same relative path as in the monorepo).
Option B — static dir + fetch:
// main.ts
export default {
staticDirs: [{ from: '../path/to/compositions', to: '/sxl-compositions' }],
};
// preview.ts
export default {
parameters: {
sxl: {
compositionFetchBaseUrl: `${import.meta.env.BASE_URL}sxl-compositions/`,
},
},
};Option C — private GitLab / CORS: the addon may build a GitLab /-/raw/... URL from registry.repository.url, but the browser cannot read cross-origin responses without Access-Control-Allow-Origin. Use a same-origin dev proxy and point the addon at it:
// preview.ts — prefix must match your Vite proxy path
export default {
parameters: {
sxl: {
compositionDevProxyPrefix: `${import.meta.env.BASE_URL}__sxl_git_raw/`,
},
},
};// main.ts — forward to your host; adjust target and auth as needed.
// Compose with mergeSxlFigmaFrameSrcHeader if you also need Figma iframe CSP.
import { mergeSxlFigmaFrameSrcHeader } from '@sxl-studio/storybook-addon/preset';
export default {
async viteFinal(config) {
const base = await mergeSxlFigmaFrameSrcHeader(config);
return {
...base,
server: {
...base.server,
proxy: {
...base.server?.proxy,
'/__sxl_git_raw': {
target: 'https://git.example.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/__sxl_git_raw/, ''),
},
},
},
};
},
};The addon also avoids a common mistake when repository.url already ends at .../src/components and compositionFilePath starts with components/... (no duplicated components/components/ segment in the raw URL).
Figma embed is blank (CSP)
The addon panel runs in the Storybook manager. If the browser’s Content-Security-Policy does not allow framing https://www.figma.com, the iframe stays empty (no error in React).
- Set
parameters.sxl.debugFigmaEmbed: truein preview and check the console for[SXL Studio addon] Figma embedwith the resolvedembedUrl. - In
.storybook/main.ts(Vite), merge dev server headers soframe-srcincludes Figma:
import { mergeSxlFigmaFrameSrcHeader } from '@sxl-studio/storybook-addon/preset';
export default {
async viteFinal(config) {
return mergeSxlFigmaFrameSrcHeader(config);
},
};- Confirm the design URL opens in a new tab via Open embed in new tab in the panel.
Figma’s embed URL format is documented at Figma — Embed; Storybook does not special-case iframes — CSP is enforced by the dev server and browser.
Requirements
- Storybook 8+
- React 18+ or 19+
License
MIT
