@transtyle/exporter-storybook 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +43 -0
- package/src/index.js +359 -0
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@transtyle/exporter-storybook",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Transtyle exporter for Storybook chrome theming and preview composition (SB 8–9).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@transtyle/ir": "0.1.0-alpha.0"
|
|
12
|
+
},
|
|
13
|
+
"transtyle": {
|
|
14
|
+
"kind": "exporter",
|
|
15
|
+
"name": "storybook",
|
|
16
|
+
"irSpec": "v0-draft",
|
|
17
|
+
"pluginApi": "0",
|
|
18
|
+
"targets": {
|
|
19
|
+
"storybook": [
|
|
20
|
+
">=8 <10"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"modes": [
|
|
24
|
+
"color-scheme"
|
|
25
|
+
],
|
|
26
|
+
"capabilities": [
|
|
27
|
+
"build",
|
|
28
|
+
"compose"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"src"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": { "access": "public" },
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/transtyle/transtyle.git",
|
|
38
|
+
"directory": "packages/exporter-storybook"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/transtyle/transtyle#readme",
|
|
41
|
+
"bugs": "https://github.com/transtyle/transtyle/issues",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @transtyle/exporter-storybook — themes Storybook's chrome (ThemeVars) and
|
|
3
|
+
* composes sibling targets into the preview. Spec: docs/specs/exporters/storybook.md.
|
|
4
|
+
*
|
|
5
|
+
* The meta-target: most of a design system is inexpressible in chrome theming
|
|
6
|
+
* and flows through preview composition instead — `options.previewTargets`
|
|
7
|
+
* lists sibling target INSTANCE names; core's ctx.siblings manifest supplies
|
|
8
|
+
* their artifact locations (never their resolutions — the no-cross-target-
|
|
9
|
+
* coupling invariant, plugins.md).
|
|
10
|
+
*
|
|
11
|
+
* Colors are emitted as hex: Storybook's theming pipeline (polished) does not
|
|
12
|
+
* parse oklch() — the per-target output-syntax choice ir.md provides for.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { droppedDimensions } from '@transtyle/ir';
|
|
16
|
+
|
|
17
|
+
const S = 'semantic.color.';
|
|
18
|
+
|
|
19
|
+
/** Per-exporter composition knowledge: main stylesheet + mode encoding. */
|
|
20
|
+
const SIBLING_PROFILES = {
|
|
21
|
+
shadcn: {
|
|
22
|
+
stylesheet: 'globals.transtyle.css',
|
|
23
|
+
encoding: '`.dark` class on <html>',
|
|
24
|
+
decorator: (p) => `root.classList.toggle('dark', scheme === 'dark');`,
|
|
25
|
+
// the canvas wears the DS canvas (F18) — mode-following via the sibling's own variables
|
|
26
|
+
canvas: ["document.body.style.background = 'var(--background)';", "document.body.style.color = 'var(--foreground)';"],
|
|
27
|
+
},
|
|
28
|
+
daisyui: {
|
|
29
|
+
stylesheet: 'daisyui.transtyle.css',
|
|
30
|
+
encoding: 'data-theme attribute',
|
|
31
|
+
decorator: (p) => `root.setAttribute('data-theme', \`${p}-\${scheme}\`);`,
|
|
32
|
+
},
|
|
33
|
+
bootstrap: {
|
|
34
|
+
stylesheet: 'bootstrap-theme.css',
|
|
35
|
+
encoding: 'data-bs-theme attribute',
|
|
36
|
+
decorator: () => `root.setAttribute('data-bs-theme', scheme);`,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
name: 'storybook',
|
|
42
|
+
|
|
43
|
+
// Validated by core against the target's `options` at load time (audit A8).
|
|
44
|
+
optionsSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
properties: {
|
|
48
|
+
previewTargets: { type: 'array', items: { type: 'string' } },
|
|
49
|
+
remBase: { type: 'number' },
|
|
50
|
+
brand: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
properties: { title: { type: 'string' }, url: { type: 'string' }, image: { type: 'string' } },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
emit(normalized, ctx) {
|
|
59
|
+
const modes = normalized.modeValues.filter((m) => normalized.modes[m]);
|
|
60
|
+
const native = normalized.defaultMode;
|
|
61
|
+
const remBase = ctx.targetConfig.options?.remBase ?? 16;
|
|
62
|
+
const coverage = [];
|
|
63
|
+
|
|
64
|
+
const variants = modes.map((mode) => buildThemeVars(normalized, mode, ctx, remBase, coverage));
|
|
65
|
+
// Mode dimensions this exporter doesn't express (T8, ir.md#modes) — a
|
|
66
|
+
// no-op unless the compile actually declares one, e.g. `density`.
|
|
67
|
+
coverage.push(...droppedDimensions(normalized.dimensionNames, ['color-scheme']));
|
|
68
|
+
|
|
69
|
+
const files = [
|
|
70
|
+
{ path: 'theme.transtyle.ts', contents: renderTheme(variants, ctx), kind: 'config' },
|
|
71
|
+
{ path: 'manager.transtyle.ts', contents: renderManager(native, ctx), kind: 'config' },
|
|
72
|
+
{ path: 'preview.transtyle.ts', contents: renderPreview(normalized, ctx, coverage), kind: 'config' },
|
|
73
|
+
{ path: 'usage.md', contents: renderUsage(ctx, coverage), kind: 'doc' },
|
|
74
|
+
];
|
|
75
|
+
return { files, coverage };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// ---------- ThemeVars construction ----------
|
|
80
|
+
|
|
81
|
+
function buildThemeVars(normalized, mode, ctx, remBase, coverage) {
|
|
82
|
+
const map = normalized.modes[mode];
|
|
83
|
+
const first = coverage.length === 0; // record coverage once (identical across modes)
|
|
84
|
+
|
|
85
|
+
const val = (p) => map.get(S + p)?.value;
|
|
86
|
+
const hx = (p) => {
|
|
87
|
+
const v = val(p);
|
|
88
|
+
return v ? ctx.formatHex(v).text : undefined;
|
|
89
|
+
};
|
|
90
|
+
const cov = (variable, slot, cls, note) => {
|
|
91
|
+
if (!first) return;
|
|
92
|
+
const fullSlot = slot.startsWith('semantic') ? slot : S + slot;
|
|
93
|
+
const entry = map.get(fullSlot);
|
|
94
|
+
// AL5 sweep: an absent slot used to fall through to `native` — the strongest
|
|
95
|
+
// coverage claim this exporter can make — because the class was derived from
|
|
96
|
+
// `provenance.kind`, and no entry means no provenance. The value was
|
|
97
|
+
// correctly skipped at emission (see the `v === undefined` guard below), so
|
|
98
|
+
// the report claimed five ThemeVars (appBorderColor, fontBase, fontCode,
|
|
99
|
+
// buttonBorder, inputBorder) that the theme did not contain. Absence is not
|
|
100
|
+
// coverage; it is the same `dropped` row Bootstrap emits for the same cause.
|
|
101
|
+
if (entry?.value === undefined) {
|
|
102
|
+
coverage.push({
|
|
103
|
+
variable,
|
|
104
|
+
slot: '—',
|
|
105
|
+
class: 'dropped',
|
|
106
|
+
note: `nothing to bind: this design system has no ${fullSlot}. Storybook's own default applies — author that slot to drive this variable.`,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const provKind = entry.provenance.kind;
|
|
111
|
+
const klass = cls ?? (provKind === 'derived' ? 'derived' : 'native');
|
|
112
|
+
coverage.push({ variable, slot: slot.startsWith('semantic') ? slot : S + slot, class: klass, ...(provKind && { provenance: provKind }), ...(note && { note }) });
|
|
113
|
+
};
|
|
114
|
+
const px = (radiusKey) => {
|
|
115
|
+
const e = map.get(`semantic.radius.${radiusKey}`);
|
|
116
|
+
if (!e) return undefined;
|
|
117
|
+
const m = /^([\d.]+)(rem|px)$/.exec(String(e.value));
|
|
118
|
+
if (!m) return undefined;
|
|
119
|
+
return Math.round(parseFloat(m[1]) * (m[2] === 'rem' ? remBase : 1));
|
|
120
|
+
};
|
|
121
|
+
const fontList = (k) => {
|
|
122
|
+
const e = map.get(`semantic.font.${k}`);
|
|
123
|
+
return e ? e.value.map((f) => (/[^a-z-]/.test(f) ? `"${f}"` : f)).join(', ') : undefined;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
cov('colorPrimary', 'primary.solid');
|
|
127
|
+
cov('colorSecondary', 'accent.solid', undefined, "SB's actual highlight color (F14)");
|
|
128
|
+
cov('appBg', 'elevation.1.surface');
|
|
129
|
+
cov('appContentBg', 'elevation.0.surface');
|
|
130
|
+
cov('appPreviewBg', 'elevation.0.surface', undefined, 'the canvas is the DS canvas, not chrome (F18)');
|
|
131
|
+
cov('appBorderColor', 'border');
|
|
132
|
+
cov('appBorderRadius', 'semantic.radius.md', 'approximated', `rem → px via remBase ${remBase} (F16)`);
|
|
133
|
+
cov('fontBase', 'semantic.font.sans');
|
|
134
|
+
cov('fontCode', 'semantic.font.mono');
|
|
135
|
+
cov('textColor', 'text.base');
|
|
136
|
+
cov('textInverseColor', 'text.inverse', 'native', 'engine-owned cross-mode read (F15)');
|
|
137
|
+
cov('textMutedColor', 'text.muted');
|
|
138
|
+
cov('barBg', 'elevation.1.surface');
|
|
139
|
+
cov('barTextColor', 'text.muted');
|
|
140
|
+
cov('barHoverColor', 'primary.solid-hover');
|
|
141
|
+
cov('barSelectedColor', 'ring', undefined, 'ring ← primary (F3); lightened in dark for visibility');
|
|
142
|
+
cov('buttonBg', 'neutral.tint');
|
|
143
|
+
cov('buttonBorder', 'border');
|
|
144
|
+
cov('booleanBg', 'neutral.tint');
|
|
145
|
+
cov('booleanSelectedBg', 'elevation.2.surface');
|
|
146
|
+
cov('inputBg', 'elevation.0.surface');
|
|
147
|
+
cov('inputBorder', 'border');
|
|
148
|
+
cov('inputTextColor', 'text.base');
|
|
149
|
+
cov('inputBorderRadius', 'semantic.radius.sm', 'approximated', `rem → px via remBase ${remBase} (F16)`);
|
|
150
|
+
if (first) {
|
|
151
|
+
coverage.push({ variable: 'brandTitle', slot: '—', class: 'approximated', note: 'config `name`, not a token (F17); brandUrl/brandImage via options.brand' });
|
|
152
|
+
coverage.push({ variable: '(chrome-inexpressible tokens)', slot: 'semantic.*', class: 'dropped', note: 'delivered through preview composition instead — see preview.transtyle.ts (dropped (chrome), validation-and-coverage.md)' });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const brand = ctx.targetConfig.options?.brand ?? {};
|
|
156
|
+
return {
|
|
157
|
+
mode,
|
|
158
|
+
vars: {
|
|
159
|
+
base: mode,
|
|
160
|
+
colorPrimary: hx('primary.solid'),
|
|
161
|
+
colorSecondary: hx('accent.solid'),
|
|
162
|
+
appBg: hx('elevation.1.surface'),
|
|
163
|
+
appContentBg: hx('elevation.0.surface'),
|
|
164
|
+
appPreviewBg: hx('elevation.0.surface'),
|
|
165
|
+
appBorderColor: hx('border'),
|
|
166
|
+
appBorderRadius: px('md'),
|
|
167
|
+
fontBase: fontList('sans'),
|
|
168
|
+
fontCode: fontList('mono'),
|
|
169
|
+
textColor: hx('text.base'),
|
|
170
|
+
textInverseColor: hx('text.inverse') ?? hx('text.base'),
|
|
171
|
+
textMutedColor: hx('text.muted'),
|
|
172
|
+
barBg: hx('elevation.1.surface'),
|
|
173
|
+
barTextColor: hx('text.muted'),
|
|
174
|
+
barHoverColor: hx('primary.solid-hover'),
|
|
175
|
+
barSelectedColor: hx('ring'),
|
|
176
|
+
buttonBg: hx('neutral.tint'),
|
|
177
|
+
buttonBorder: hx('border'),
|
|
178
|
+
booleanBg: hx('neutral.tint'),
|
|
179
|
+
booleanSelectedBg: hx('elevation.2.surface'),
|
|
180
|
+
inputBg: hx('elevation.0.surface'),
|
|
181
|
+
inputBorder: hx('border'),
|
|
182
|
+
inputTextColor: hx('text.base'),
|
|
183
|
+
inputBorderRadius: px('sm'),
|
|
184
|
+
brandTitle: brand.title ?? ctx.projectName,
|
|
185
|
+
...(brand.url && { brandUrl: brand.url }),
|
|
186
|
+
...(brand.image && { brandImage: brand.image }),
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ---------- rendering ----------
|
|
192
|
+
|
|
193
|
+
const header = (ctx) => [
|
|
194
|
+
'// GENERATED by transtyle — do not edit; source: ' + ctx.projectName + ' token files',
|
|
195
|
+
'// Target: storybook (>=8 <10) · rules standard@1',
|
|
196
|
+
].join('\n');
|
|
197
|
+
|
|
198
|
+
function renderTheme(variants, ctx) {
|
|
199
|
+
const lines = [
|
|
200
|
+
header(ctx),
|
|
201
|
+
'// Colors are hex, not OKLCH: Storybook\'s theming pipeline (polished) does not parse oklch().',
|
|
202
|
+
'',
|
|
203
|
+
"import { create } from 'storybook/theming';",
|
|
204
|
+
'',
|
|
205
|
+
];
|
|
206
|
+
for (const { mode, vars } of variants) {
|
|
207
|
+
lines.push(`export const ${mode} = create({`);
|
|
208
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
209
|
+
if (v === undefined) continue;
|
|
210
|
+
lines.push(` ${k}: ${typeof v === 'number' ? v : `'${v}'`},`);
|
|
211
|
+
}
|
|
212
|
+
lines.push('});', '');
|
|
213
|
+
}
|
|
214
|
+
return lines.join('\n');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function renderManager(native, ctx) {
|
|
218
|
+
return [
|
|
219
|
+
header(ctx),
|
|
220
|
+
'// Additive fragment: import from your own .storybook/manager.ts — we never',
|
|
221
|
+
'// overwrite user config files. Chrome is themed with the design system\'s',
|
|
222
|
+
`// NATIVE mode (\`${native}\` — the mode polarity rule): manager theming is`,
|
|
223
|
+
'// static per boot, so the chrome wears the DS\'s default face. Both variants',
|
|
224
|
+
'// live in theme.transtyle.ts for users who wire their own switch.',
|
|
225
|
+
'',
|
|
226
|
+
"import { addons } from 'storybook/manager-api';",
|
|
227
|
+
`import { ${native} } from './theme.transtyle';`,
|
|
228
|
+
'',
|
|
229
|
+
`addons.setConfig({ theme: ${native} });`,
|
|
230
|
+
'',
|
|
231
|
+
].join('\n');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function renderPreview(normalized, ctx, coverage) {
|
|
235
|
+
const native = normalized.defaultMode;
|
|
236
|
+
const modes = normalized.modeValues;
|
|
237
|
+
const previewTargets = ctx.targetConfig.options?.previewTargets ?? [];
|
|
238
|
+
const myOutput = ctx.targetConfig.output ?? 'dist/storybook';
|
|
239
|
+
|
|
240
|
+
const importLines = [];
|
|
241
|
+
const decoratorLines = [];
|
|
242
|
+
for (const name of previewTargets) {
|
|
243
|
+
const sibling = ctx.siblings.find((s) => s.name === name);
|
|
244
|
+
const profile = sibling && SIBLING_PROFILES[sibling.exporter];
|
|
245
|
+
if (!profile) {
|
|
246
|
+
coverage.push({ variable: `previewTargets.${name}`, slot: '—', class: 'unsupported', note: 'unknown sibling target or exporter without a composition profile' });
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
importLines.push(`import '${relPath(myOutput, sibling.output)}/${profile.stylesheet}'; // sibling: ${name} · mode encoding: ${profile.encoding}`);
|
|
250
|
+
decoratorLines.push(` ${profile.decorator(ctx.projectName)} // ${name}`);
|
|
251
|
+
if (profile.canvas && !decoratorLines.some((l) => l.includes('document.body.style'))) {
|
|
252
|
+
decoratorLines.push(...profile.canvas.map((l) => ` ${l} // canvas = DS canvas (F18), via ${name}'s variables`));
|
|
253
|
+
}
|
|
254
|
+
coverage.push({ variable: `previewTargets.${name}`, slot: '—', class: 'native', note: `composition by artifact path (${sibling.output}/${profile.stylesheet})` });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const bg = (mode) => {
|
|
258
|
+
const v = normalized.modes[mode]?.get(`${S}elevation.0.surface`)?.value;
|
|
259
|
+
return v ? ctx.formatHex(v).text : undefined;
|
|
260
|
+
};
|
|
261
|
+
coverage.push({ variable: 'globalTypes.colorScheme', slot: 'modes.color-scheme', class: 'native', note: 'mode dimension → SB global toolbar' });
|
|
262
|
+
coverage.push({ variable: 'backgrounds.options', slot: `${S}elevation.0.surface`, class: 'native' });
|
|
263
|
+
coverage.push({ variable: 'backgrounds.grid.cellSize', slot: '—', class: 'approximated', note: 'space.4 → px (defaulted scale); cellAmount/opacity are exporter defaults' });
|
|
264
|
+
|
|
265
|
+
return [
|
|
266
|
+
header(ctx),
|
|
267
|
+
'// Additive fragment: spread into your own .storybook/preview.ts.',
|
|
268
|
+
'// Sibling imports + the mode decorator are assembled from the build manifest',
|
|
269
|
+
'// (artifact path + declared mode encoding) via ctx.siblings — exporters never',
|
|
270
|
+
'// read each other\'s resolutions.',
|
|
271
|
+
'',
|
|
272
|
+
...importLines,
|
|
273
|
+
"import { " + modes.join(', ') + " } from './theme.transtyle';",
|
|
274
|
+
'',
|
|
275
|
+
'export const globalTypes = {',
|
|
276
|
+
' colorScheme: {',
|
|
277
|
+
" description: 'Design-system color scheme',",
|
|
278
|
+
' toolbar: {',
|
|
279
|
+
" title: 'Scheme',",
|
|
280
|
+
" icon: 'mirror',",
|
|
281
|
+
' items: [',
|
|
282
|
+
...modes.map((m) => ` { value: '${m}', title: '${m[0].toUpperCase()}${m.slice(1)}' },`),
|
|
283
|
+
' ],',
|
|
284
|
+
' dynamicTitle: true,',
|
|
285
|
+
' },',
|
|
286
|
+
' },',
|
|
287
|
+
'};',
|
|
288
|
+
'',
|
|
289
|
+
`export const initialGlobals = { colorScheme: '${native}' }; // the DS's native mode`,
|
|
290
|
+
'',
|
|
291
|
+
'// One decorator drives every sibling\'s mode encoding',
|
|
292
|
+
'export const decorators = [',
|
|
293
|
+
' (Story: any, context: any) => {',
|
|
294
|
+
` const scheme = context.globals.colorScheme ?? '${native}';`,
|
|
295
|
+
' const root = document.documentElement;',
|
|
296
|
+
...decoratorLines,
|
|
297
|
+
' return Story();',
|
|
298
|
+
' },',
|
|
299
|
+
'];',
|
|
300
|
+
'',
|
|
301
|
+
'export const parameters = {',
|
|
302
|
+
' docs: {',
|
|
303
|
+
` theme: ${native}, // docs pages follow the DS native mode (chrome-static in SB, F18)`,
|
|
304
|
+
' },',
|
|
305
|
+
' backgrounds: {',
|
|
306
|
+
' options: {',
|
|
307
|
+
...modes.map((m) => ` ${m}: { name: '${ctx.projectName} ${m}', value: '${bg(m)}' },`),
|
|
308
|
+
' },',
|
|
309
|
+
' grid: { cellSize: 16, cellAmount: 4, opacity: 0.35 },',
|
|
310
|
+
' },',
|
|
311
|
+
'};',
|
|
312
|
+
'',
|
|
313
|
+
].join('\n');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** POSIX relative path between two project-relative dirs (no node:path — exporters stay platform-pure). */
|
|
317
|
+
function relPath(from, to) {
|
|
318
|
+
const f = from.split('/').filter(Boolean);
|
|
319
|
+
const t = to.split('/').filter(Boolean);
|
|
320
|
+
let i = 0;
|
|
321
|
+
while (i < f.length && i < t.length && f[i] === t[i]) i++;
|
|
322
|
+
return [...f.slice(i).map(() => '..'), ...t.slice(i)].join('/') || '.';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function renderUsage(ctx, coverage) {
|
|
326
|
+
const counts = {};
|
|
327
|
+
for (const c of coverage) counts[c.class] = (counts[c.class] ?? 0) + 1;
|
|
328
|
+
const summary = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(' · ');
|
|
329
|
+
const previewTargets = ctx.targetConfig.options?.previewTargets ?? [];
|
|
330
|
+
return `# Using this Storybook theme
|
|
331
|
+
|
|
332
|
+
Generated from the **${ctx.projectName}** design system by transtyle (Storybook >=8 <10). Coverage: ${summary}.
|
|
333
|
+
|
|
334
|
+
All files are **additive fragments** — import them from your own \`.storybook/\` config; we never overwrite user files.
|
|
335
|
+
|
|
336
|
+
## Wire the chrome theme
|
|
337
|
+
|
|
338
|
+
\`\`\`ts
|
|
339
|
+
// .storybook/manager.ts
|
|
340
|
+
import '../path/to/dist/storybook/manager.transtyle';
|
|
341
|
+
\`\`\`
|
|
342
|
+
|
|
343
|
+
(or import \`{ light, dark }\` from \`theme.transtyle\` and call \`addons.setConfig\` yourself).
|
|
344
|
+
|
|
345
|
+
## Wire the preview
|
|
346
|
+
|
|
347
|
+
\`\`\`ts
|
|
348
|
+
// .storybook/preview.ts
|
|
349
|
+
export * from '../path/to/dist/storybook/preview.transtyle';
|
|
350
|
+
\`\`\`
|
|
351
|
+
|
|
352
|
+
This imports the sibling targets' stylesheets (${previewTargets.join(', ') || 'none configured'}), adds a **Scheme** toolbar bound to the design system's \`color-scheme\` modes, and sets DS canvases as Storybook backgrounds. Configure siblings via \`options.previewTargets\` in \`transtyle.config.json\`.
|
|
353
|
+
|
|
354
|
+
## Regenerating
|
|
355
|
+
|
|
356
|
+
Never edit these files — change the design system tokens and run \`transtyle build storybook\`.
|
|
357
|
+
See \`report.json\` for the full coverage/provenance breakdown.
|
|
358
|
+
`;
|
|
359
|
+
}
|