@codepress/design-kit 0.2.2 → 0.3.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/README.md +50 -6
- package/package.json +1 -1
- package/schema/design-kit.config.schema.json +1 -1
- package/skills/design-kit-setup/SKILL.md +6 -5
- package/skills/storybook-setup/SKILL.md +389 -0
- package/src/capture-stories.mjs +1 -1
- package/src/cli.mjs +15 -1
- package/src/init.mjs +3 -3
- package/src/templates/design-kit-skill.md +6 -0
package/README.md
CHANGED
|
@@ -48,15 +48,21 @@ needs nothing installed beforehand. Following it, the agent:
|
|
|
48
48
|
5. Builds the kit once, checks what it captured, and opens the pull request
|
|
49
49
|
with the counts, any gaps and a screenshot of one composed screen.
|
|
50
50
|
|
|
51
|
-
No Storybook yet and you want one?
|
|
51
|
+
No Storybook yet and you want one? Ask for it on its own, before or after the
|
|
52
|
+
kit:
|
|
52
53
|
|
|
53
54
|
```text
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
Set up Storybook in this repository, with stories for our components. Run
|
|
56
|
+
`npx --yes @codepress/design-kit storybook-guide` and follow what it prints.
|
|
57
|
+
Open one pull request and don't merge it.
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
`storybook-guide` prints a second skill. The agent installs Storybook 9 (8 for
|
|
61
|
+
older apps) without changing your React, Next.js or TypeScript versions,
|
|
62
|
+
writes a stories file next to each component covering every variant and the
|
|
63
|
+
empty, loading, error and long-text states, adds a tokens story, and lists
|
|
64
|
+
anything it skipped, with the reason. React with Vite or Next.js is supported
|
|
65
|
+
first; on another stack the agent says plainly what it did not try.
|
|
60
66
|
|
|
61
67
|
Once the pull request merges, the daily workflow uploads the kit, and the
|
|
62
68
|
`design-kit` skill it installed tells your agents to use it. Ask for a mockup
|
|
@@ -69,6 +75,30 @@ Mock up a Billing tab on our settings page, using the design kit.
|
|
|
69
75
|
The agent fetches the newest kit, composes the screen from your real frame and
|
|
70
76
|
components, and draws only what is new.
|
|
71
77
|
|
|
78
|
+
## Keeping the kit in sync
|
|
79
|
+
|
|
80
|
+
Nothing needs re-running after setup.
|
|
81
|
+
|
|
82
|
+
- **It rebuilds itself.** The workflow `init` wrote
|
|
83
|
+
(`.github/workflows/design-kit.yml`) builds the kit every day, and on
|
|
84
|
+
demand from "Run workflow" in the Actions tab or with
|
|
85
|
+
`gh workflow run design-kit.yml`. A day with no new commits since the last
|
|
86
|
+
kit is skipped. Each kit is kept for 30 days.
|
|
87
|
+
- **It captures what exists at that commit.** Every build captures the
|
|
88
|
+
stories and pages that exist at the commit it runs on. It never writes
|
|
89
|
+
stories, so a new component shows up in the kit once it has a story. With
|
|
90
|
+
`gaps.componentGlob` set, every component without one is listed in the
|
|
91
|
+
kit's `gaps.json`. The `design-kit` skill and `storybook-guide` ask agents
|
|
92
|
+
to add or update a component's story in the same pull request as the
|
|
93
|
+
component.
|
|
94
|
+
- **Agents always use the newest kit.** The installed `design-kit` skill
|
|
95
|
+
fetches the newest kit each time. When UI commits have landed since that
|
|
96
|
+
kit was built, the mockup's caption says so.
|
|
97
|
+
- **Re-run `init` only to start over** (`--force`). To pick up a new release
|
|
98
|
+
of this package, update the pinned version in the workflow
|
|
99
|
+
(`DESIGN_KIT_PACKAGE`); the "Run workflow" form's `package` input tries
|
|
100
|
+
another version for one run.
|
|
101
|
+
|
|
72
102
|
## Set up in your repository
|
|
73
103
|
|
|
74
104
|
`design-kit init` sets a kit up as one reviewable change. Run it in the
|
|
@@ -163,7 +193,7 @@ needs no config.
|
|
|
163
193
|
|
|
164
194
|
### `stories`
|
|
165
195
|
|
|
166
|
-
Captures every story of a Storybook 7 or
|
|
196
|
+
Captures every story of a Storybook 7, 8 or 9 static build (any renderer). The
|
|
167
197
|
build comes from `--storybook-dir`, `storybookDir`, or `buildCommand`, which
|
|
168
198
|
writes `staticDir`; without `staticDir`, `{out}` in the command names a
|
|
169
199
|
temporary folder that is removed afterwards.
|
|
@@ -222,6 +252,7 @@ design-kit capture pages [--config f] [--out dir] [--base-url url] [--only nam
|
|
|
222
252
|
design-kit kit info|find|show|compose ... [--kit dir]
|
|
223
253
|
design-kit validate [--config f]
|
|
224
254
|
design-kit setup-guide
|
|
255
|
+
design-kit storybook-guide
|
|
225
256
|
```
|
|
226
257
|
|
|
227
258
|
`setup-guide` prints the agent skill for setting a kit up in a repository
|
|
@@ -231,6 +262,19 @@ build the kit once and report what it captured. An agent without the skill
|
|
|
231
262
|
installed can run `npx --yes @codepress/design-kit setup-guide` and
|
|
232
263
|
follow what it prints.
|
|
233
264
|
|
|
265
|
+
`storybook-guide` prints the agent skill for a repository with no Storybook,
|
|
266
|
+
or one with components that have no stories, as one reviewable pull request.
|
|
267
|
+
The agent surveys the components and tokens, installs Storybook 9 (8 for
|
|
268
|
+
older apps) pinned to the repository's React, Next.js and TypeScript, and fixes
|
|
269
|
+
the usual first-build failures: peer-dependency conflicts, modules that throw
|
|
270
|
+
on a missing environment variable, `next/font`, `next/image` and SVG imports,
|
|
271
|
+
and `public/` assets. It writes one stories file per component, covering
|
|
272
|
+
every variant and the empty, loading, error and long-text states, adds a
|
|
273
|
+
tokens story, and lists anything it skipped, with the reason. It typechecks,
|
|
274
|
+
builds Storybook and checks that `capture stories` captures it. An existing
|
|
275
|
+
Storybook is extended, never re-initialised. React with Vite or Next.js is
|
|
276
|
+
supported first. Run `npx --yes @codepress/design-kit storybook-guide`.
|
|
277
|
+
|
|
234
278
|
`--only` re-captures just those stories (Storybook ids, with or without the
|
|
235
279
|
`story:` prefix and width) and copies every other row from `--base`, byte for
|
|
236
280
|
byte. The two captures share one kit folder and keep each other's files: run
|
package/package.json
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"stories": {
|
|
67
67
|
"type": "object",
|
|
68
68
|
"additionalProperties": false,
|
|
69
|
-
"description": "`design-kit capture stories`: every story of a Storybook 7 or
|
|
69
|
+
"description": "`design-kit capture stories`: every story of a Storybook 7, 8 or 9 static build.",
|
|
70
70
|
"properties": {
|
|
71
71
|
"root": {
|
|
72
72
|
"type": "string",
|
|
@@ -53,10 +53,11 @@ locally (step 3); a kit with pages has frames that new content can go into.
|
|
|
53
53
|
**No Storybook.** Propose one of these to the person, through the Orchestrator
|
|
54
54
|
when you work for one, in a short message with your recommendation:
|
|
55
55
|
|
|
56
|
-
- (a) **add Storybook** with stories for the components
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
stories next to the
|
|
56
|
+
- (a) **add Storybook** with stories for the components, covering their
|
|
57
|
+
default, empty, loading, error and long-content states: follow
|
|
58
|
+
`npx --yes @codepress/design-kit storybook-guide`, which installs a pinned
|
|
59
|
+
Storybook, writes a stories file next to each component, and adds the
|
|
60
|
+
`stories` section to the config `init` wrote;
|
|
60
61
|
- (b) **capture real pages** from the dev server (step 3);
|
|
61
62
|
- (c) **both**.
|
|
62
63
|
|
|
@@ -64,7 +65,7 @@ When nobody answers, do (b), plus (a) for components that are clearly reusable
|
|
|
64
65
|
(buttons, inputs, cards, dialogs, tables that several screens share). Say in the
|
|
65
66
|
pull request which you did and why.
|
|
66
67
|
|
|
67
|
-
**Supported first:** Storybook 7 and
|
|
68
|
+
**Supported first:** Storybook 7, 8 and 9 static builds of any renderer, page
|
|
68
69
|
capture of any URL, and new stories for React only. For another stack (Vue,
|
|
69
70
|
Svelte, Angular, an older Storybook), capture pages and say honestly in the
|
|
70
71
|
pull request that new stories were not written, and why.
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: storybook-setup
|
|
3
|
+
description: "Add Storybook to a repository, or extend the one it has, with a stories file for every existing component and a tokens story, as one reviewable pull request: survey the components and tokens, install a pinned Storybook that matches the repo's React, Next and TypeScript, fix the usual first-build failures, write stories for every variant and state, typecheck, build, and check the stories capture in a design kit. Use when asked to set up Storybook, add Storybook, add stories for our components, backfill missing stories, or build a component catalog."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Set up Storybook with stories for your components
|
|
7
|
+
|
|
8
|
+
Your job is **one reviewable pull request** that gives this repository a
|
|
9
|
+
working Storybook with a stories file for every component it already has,
|
|
10
|
+
plus a tokens story, so people can browse the library and a design kit
|
|
11
|
+
(`@codepress/design-kit`) can capture it. You never merge it.
|
|
12
|
+
|
|
13
|
+
Run the steps in order. The survey comes first because the most common
|
|
14
|
+
failure is a Storybook that ignores what the repository already has: the wrong
|
|
15
|
+
folders, no styles, a second Storybook next to the first.
|
|
16
|
+
|
|
17
|
+
Supported first: **React with Vite or Next.js.** On another renderer (Vue,
|
|
18
|
+
Svelte, Angular, web components) you may still install Storybook and write
|
|
19
|
+
stories under the same rules, but say plainly in the pull request that this
|
|
20
|
+
guide has not been tried on that stack.
|
|
21
|
+
|
|
22
|
+
## 1. Survey the repository
|
|
23
|
+
|
|
24
|
+
A bounded read, so you extend what exists instead of competing with it:
|
|
25
|
+
|
|
26
|
+
- **Stack and versions.** Read `react`, `react-dom`, `next`, `typescript`,
|
|
27
|
+
`vite` and the Node version (`engines`, `.nvmrc`, `.node-version`; when
|
|
28
|
+
none is recorded, the version its CI workflows use) from `package.json` and
|
|
29
|
+
the lockfile. In a monorepo, find the package that holds
|
|
30
|
+
the components; Storybook goes there.
|
|
31
|
+
- **Package manager.** Use the one the repository uses: the `packageManager`
|
|
32
|
+
field, or the lockfile (`pnpm-lock.yaml` pnpm, `yarn.lock` yarn,
|
|
33
|
+
`bun.lock`/`bun.lockb` bun, `package-lock.json` npm). Never add a second
|
|
34
|
+
lockfile.
|
|
35
|
+
- **The component library: every folder, not just `ui/`.** Components live in
|
|
36
|
+
`components/`, `ui/`, `features/*/components/`, `app/**/_components/`,
|
|
37
|
+
`src/shared/`, workspace packages and more. List every file that exports a
|
|
38
|
+
component that renders something, and note the conventions: file naming,
|
|
39
|
+
default or named exports, how props are typed.
|
|
40
|
+
- **The token layer.** CSS custom properties (`:root { --… }`), a Tailwind
|
|
41
|
+
theme (`tailwind.config.*`, or `@theme` in CSS for Tailwind 4), a theme
|
|
42
|
+
object for a CSS-in-JS `ThemeProvider`, or a design-tokens file.
|
|
43
|
+
- **The global stylesheet and providers.** What the app's entry imports
|
|
44
|
+
(`main.tsx`, `app/layout.tsx`, `pages/_app.tsx`): global CSS, fonts, and
|
|
45
|
+
providers components need to render (theme, router, i18n, data clients).
|
|
46
|
+
- **An existing Storybook.** `.storybook/`, `*.stories.*` files, `storybook`
|
|
47
|
+
or `@storybook/*` dependencies, a `storybook` script. **If Storybook already
|
|
48
|
+
exists, extend it: never run `storybook init` again** and never upgrade it
|
|
49
|
+
in this pull request (one older than Storybook 7 cannot be captured into a
|
|
50
|
+
design kit; say so). Match its story format exactly (CSF version, typing,
|
|
51
|
+
decorators, titles), keep its stories, and add stories only where they are
|
|
52
|
+
missing. A component with a stories file that lacks states gets those
|
|
53
|
+
states added to that file, never a second file.
|
|
54
|
+
- **A design kit.** A `design-kit.config.json` means stories will be captured
|
|
55
|
+
into a kit; step 6 checks they are.
|
|
56
|
+
|
|
57
|
+
Write the survey as one short paragraph (it goes in the pull request) before
|
|
58
|
+
you change anything.
|
|
59
|
+
|
|
60
|
+
## 2. Install Storybook (only when there is none)
|
|
61
|
+
|
|
62
|
+
**Pick the version.** Use **Storybook 9** (`storybook@9`): it is the newest
|
|
63
|
+
major that `design-kit capture stories` is checked against, and it supports
|
|
64
|
+
Next.js 14.1 through 16. Never use `storybook@latest`. Use **Storybook 8**
|
|
65
|
+
(`storybook@8`) instead when the app is below Storybook 9's floors: Node 20,
|
|
66
|
+
Vite 5, Next.js 14.1 or TypeScript 4.9. Check a framework's peer ranges with
|
|
67
|
+
`npm view @storybook/<framework>@9 peerDependencies`.
|
|
68
|
+
|
|
69
|
+
**Pick the builder deliberately, and record the choice for the pull request.**
|
|
70
|
+
|
|
71
|
+
- Vite app: `@storybook/react-vite`.
|
|
72
|
+
- Next.js app: `@storybook/nextjs` (webpack) is the default and handles
|
|
73
|
+
`next/font`, `next/image`, `next/navigation` and the app's PostCSS and
|
|
74
|
+
Tailwind setup. `@storybook/nextjs-vite` is faster and fits when the app
|
|
75
|
+
has no custom `webpack()` in `next.config.*` that its components depend on
|
|
76
|
+
(SVGR, raw loaders, aliases). When unsure, take `@storybook/nextjs`.
|
|
77
|
+
|
|
78
|
+
**Run init with the repository's package manager**, in the package that holds
|
|
79
|
+
the components:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pnpm dlx storybook@9 init --yes --no-dev --disable-telemetry --features docs
|
|
83
|
+
# npm: npx --yes storybook@9 init ... yarn: yarn dlx storybook@9 init ...
|
|
84
|
+
# bun: bunx storybook@9 init ...
|
|
85
|
+
# add --type nextjs, or --builder vite, when init detects the wrong one
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`--features docs` keeps init from adding its test-runner setup; step 3
|
|
89
|
+
removes docs (and the onboarding addon init adds anyway) unless the person
|
|
90
|
+
asked for docs pages. Ignore init's banner telling you to cancel and use
|
|
91
|
+
`@latest`: the pin is deliberate.
|
|
92
|
+
|
|
93
|
+
**Then check what init changed: `git diff package.json`.**
|
|
94
|
+
|
|
95
|
+
- Only Storybook packages may be added (`storybook`, `@storybook/*`, and
|
|
96
|
+
`eslint-plugin-storybook` when the repository uses ESLint). All of
|
|
97
|
+
them must be on the **same exact version**; write it without a `^` or `~`
|
|
98
|
+
range.
|
|
99
|
+
- `react`, `react-dom`, `next`, `typescript`, `vite` and every other existing
|
|
100
|
+
dependency must be unchanged. **Never downgrade or upgrade the app** to fit
|
|
101
|
+
Storybook; choose the Storybook version that fits the app.
|
|
102
|
+
- A `storybook` and a `build-storybook` script are fine, and so are the
|
|
103
|
+
`storybook-static` and `*storybook.log` lines init adds to `.gitignore`.
|
|
104
|
+
|
|
105
|
+
## 3. Configure it
|
|
106
|
+
|
|
107
|
+
Keep the configuration small and make it render the components as the app
|
|
108
|
+
does. When Storybook already existed, change its configuration only where the
|
|
109
|
+
stories need it to render and build (styles, providers, env placeholders,
|
|
110
|
+
loaders, `staticDirs`), and keep its addons as they are.
|
|
111
|
+
|
|
112
|
+
- **Addons.** Keep only what init added for the framework itself. Remove the
|
|
113
|
+
onboarding addon (`@storybook/addon-onboarding`) and its package, and
|
|
114
|
+
delete init's example stories folder (`stories/` or `src/stories/`, with
|
|
115
|
+
its Button, Header and Page examples). Remove `@storybook/addon-docs` (from
|
|
116
|
+
`addons` and `package.json`), any `tags: ["autodocs"]` and the `*.mdx`
|
|
117
|
+
glob unless the person asked for docs pages. Add no other addons. Set
|
|
118
|
+
`core: { disableTelemetry: true }` in `main.*` so builds stay quiet (for
|
|
119
|
+
builds you run against an existing Storybook, set
|
|
120
|
+
`STORYBOOK_DISABLE_TELEMETRY=1` instead).
|
|
121
|
+
- **Story globs.** Point `stories` in `.storybook/main.*` at the real
|
|
122
|
+
component folders from the survey, for example
|
|
123
|
+
`["../src/**/*.stories.@(ts|tsx)"]`, or one entry per folder or package.
|
|
124
|
+
- **Styles and tokens.** Import the global stylesheet or token layer in
|
|
125
|
+
`.storybook/preview.*` (`import "../src/styles/globals.css";`). With
|
|
126
|
+
Tailwind 3, make sure its `content` globs cover the stories files too. If
|
|
127
|
+
the app sets fonts or a theme class in its layout, reproduce that with a
|
|
128
|
+
decorator in `preview.*` (rename init's `preview.ts` to `preview.tsx` for
|
|
129
|
+
JSX). Wrap stories in the providers components need, the same way the app
|
|
130
|
+
does. **An unstyled Storybook is worse than none**: check one built story
|
|
131
|
+
actually looks like the app.
|
|
132
|
+
- **Landing page.** Land the sidebar on a real story, not a docs page. The
|
|
133
|
+
tokens story from step 4 works well: in `preview.*`, set
|
|
134
|
+
`parameters.options.storySort` to `{ order: ["Foundations", "*"] }`.
|
|
135
|
+
|
|
136
|
+
**Fix the first build's failures yourself.** These four hit almost every
|
|
137
|
+
fresh Storybook; fix them rather than report them:
|
|
138
|
+
|
|
139
|
+
1. **Peer-dependency conflicts.** Put every Storybook package on one exact
|
|
140
|
+
version, and pick the Storybook major whose peer ranges include the app's
|
|
141
|
+
React, Next and TypeScript (step 2). If init's own install fails, re-run
|
|
142
|
+
it with `--skip-install` and install with the repository's package manager.
|
|
143
|
+
Drop an addon that conflicts rather than force it. Never use `--force`,
|
|
144
|
+
and never change the app's versions. If a conflict is only in a transitive
|
|
145
|
+
Storybook dependency, an `overrides` (npm), `resolutions` (yarn) or
|
|
146
|
+
`pnpm.overrides` entry for that one package is acceptable; say so in the
|
|
147
|
+
pull request.
|
|
148
|
+
2. **Modules that throw at import on a missing environment variable**, for
|
|
149
|
+
example a config module that throws when `NEXT_PUBLIC_API_URL` or
|
|
150
|
+
`VITE_API_URL` is unset. Give Storybook placeholder values. **Never edit
|
|
151
|
+
the app's own validation.** Bundlers inline these variables at build time,
|
|
152
|
+
so pass them through Storybook's `env` option in `.storybook/main.*`, which
|
|
153
|
+
reaches `process.env` and `import.meta.env` in both the webpack and Vite
|
|
154
|
+
builders. Setting `process.env` at the top of `main.*` does not: the
|
|
155
|
+
webpack builder replaces `process.env` in the bundle.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// Storybook only: placeholders so modules that validate env at import
|
|
159
|
+
// time load. Never real values.
|
|
160
|
+
env: (config = {}) => ({
|
|
161
|
+
...config,
|
|
162
|
+
NEXT_PUBLIC_API_URL: config.NEXT_PUBLIC_API_URL ?? "https://api.example.invalid",
|
|
163
|
+
}),
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Never commit a real key, and never copy the app's `.env` files.
|
|
167
|
+
|
|
168
|
+
3. **`next/font`, `next/image` and SVGR imports.** Keep a Next.js framework
|
|
169
|
+
(`@storybook/nextjs` or `@storybook/nextjs-vite`), which handles
|
|
170
|
+
`next/font` and `next/image`. Components that use `next/navigation` need
|
|
171
|
+
`parameters: { nextjs: { appDirectory: true } }` in `preview.*`. A font
|
|
172
|
+
the app's layout applies (`className={inter.variable}` on `<html>`) needs
|
|
173
|
+
the same class in a `preview.*` decorator. With `next/font/google` the
|
|
174
|
+
font files load from Google when a story is viewed, so a captured kit
|
|
175
|
+
falls back to the system font; say so in the pull request
|
|
176
|
+
(`next/font/local` fonts are served by Storybook and captured). Storybook
|
|
177
|
+
does not run the app's custom `webpack()` from `next.config.*`, so an SVG
|
|
178
|
+
imported as a component (`import Logo from "./logo.svg"`) needs the loader
|
|
179
|
+
added in `webpackFinal`, the app's own `@svgr/webpack`:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
webpackFinal: async (config) => {
|
|
183
|
+
const rules = config.module?.rules ?? [];
|
|
184
|
+
for (const rule of rules)
|
|
185
|
+
if (rule && typeof rule === "object" && rule.test instanceof RegExp && rule.test.test(".svg"))
|
|
186
|
+
rule.exclude = /\.svg$/i;
|
|
187
|
+
rules.push({ test: /\.svg$/i, use: ["@svgr/webpack"] });
|
|
188
|
+
return config;
|
|
189
|
+
},
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
With a Vite builder, the app's `vite.config.*` plugins (such as
|
|
193
|
+
`vite-plugin-svgr`) are merged in; add the plugin or alias in `viteFinal`
|
|
194
|
+
only when the app configures it somewhere Storybook does not read.
|
|
195
|
+
|
|
196
|
+
4. **`public/` assets.** Images and fonts the components load by absolute
|
|
197
|
+
path (`/logo.svg`) 404 until `.storybook/main.*` has
|
|
198
|
+
`staticDirs: ["../public"]` (the path to the app's public folder).
|
|
199
|
+
|
|
200
|
+
## 4. Write the stories
|
|
201
|
+
|
|
202
|
+
**One stories file per component, next to the component**, named the way
|
|
203
|
+
the repository names files (`Button.tsx` and `Button.stories.tsx`). Use CSF 3
|
|
204
|
+
in the repository's typing style, for example:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import type { Meta, StoryObj } from "@storybook/react-vite"; // or @storybook/nextjs, @storybook/nextjs-vite; @storybook/react on Storybook 8
|
|
208
|
+
import { Button } from "./Button";
|
|
209
|
+
|
|
210
|
+
const meta = {
|
|
211
|
+
component: Button,
|
|
212
|
+
args: { children: "Save" },
|
|
213
|
+
} satisfies Meta<typeof Button>;
|
|
214
|
+
export default meta;
|
|
215
|
+
type Story = StoryObj<typeof meta>;
|
|
216
|
+
|
|
217
|
+
export const Primary: Story = {};
|
|
218
|
+
export const Secondary: Story = { args: { variant: "secondary" } };
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Each file covers, where it applies:
|
|
222
|
+
|
|
223
|
+
- **the default**, and **every variant and every size** the props allow;
|
|
224
|
+
- **hover, focus and disabled** for anything interactive. Disabled is a prop.
|
|
225
|
+
Focus is a `play` function that focuses the element
|
|
226
|
+
(`play: async ({ canvasElement }) => canvasElement.querySelector("button")?.focus()`).
|
|
227
|
+
Hover that the component drives itself (a tooltip, a menu) is a `play`
|
|
228
|
+
function too. A pure CSS `:hover` cannot be shown without a pseudo-state
|
|
229
|
+
addon, so say so in the coverage note instead of adding one;
|
|
230
|
+
- **empty, loading and error** states for anything that shows data or
|
|
231
|
+
results;
|
|
232
|
+
- **long text or overflow** for anything that holds text: a long label, a
|
|
233
|
+
long unbroken word or address, many items.
|
|
234
|
+
|
|
235
|
+
Name state stories so they do not shadow globals: `ErrorState` with
|
|
236
|
+
`name: "Error"`, never `export const Error`. A captured kit is static HTML, so
|
|
237
|
+
focus rings and other live states show in Storybook but not in the kit.
|
|
238
|
+
|
|
239
|
+
Use made-up fixture data (`example.com` addresses, invented names), never
|
|
240
|
+
real customer data. Write stories through the component's props; do not
|
|
241
|
+
refactor the app to make a state reachable. A state no prop can reach is
|
|
242
|
+
skipped for cause and listed.
|
|
243
|
+
|
|
244
|
+
**Add a tokens (foundations) story**, for example
|
|
245
|
+
`src/foundations/Tokens.stories.tsx` with the title `Foundations/Tokens`.
|
|
246
|
+
It renders the design language from the token layer the survey found: the
|
|
247
|
+
palette as labelled swatches (`background: var(--color-primary)`, or Tailwind
|
|
248
|
+
classes), the type scale (each size with its name), and the spacing ramp
|
|
249
|
+
(bars of each width). Radii and shadows can go in too. List the token names
|
|
250
|
+
from the token source, so a new token is one line.
|
|
251
|
+
|
|
252
|
+
**"Every component" means the whole catalog**: the shared primitives and
|
|
253
|
+
the composites and feature components (lists, panels, cards, headers,
|
|
254
|
+
forms). Skip a component **only for cause**: it cannot render apart from
|
|
255
|
+
live data (it fetches on its own and has no way to pass data in), it is
|
|
256
|
+
deprecated, or it is an internal detail of another component that is
|
|
257
|
+
already covered. Route files (`page.tsx`, `layout.tsx`) and the app root are
|
|
258
|
+
screens, not catalog components: give one a story when it renders from props
|
|
259
|
+
alone, and otherwise list it as skipped. List every skip, with its reason, in
|
|
260
|
+
the pull request. A large backfill may be split into several pull requests by
|
|
261
|
+
component family; say which family each covers and what is left. Silently
|
|
262
|
+
leaving components out is not allowed.
|
|
263
|
+
|
|
264
|
+
While you read the screens, note **inline UI that should become shared
|
|
265
|
+
components**: the same markup repeated across pages. Name each one under
|
|
266
|
+
"Recommended next" in the pull request. Do not extract it in this pull
|
|
267
|
+
request.
|
|
268
|
+
|
|
269
|
+
## 5. Typecheck, then build
|
|
270
|
+
|
|
271
|
+
1. **Typecheck first.** It is cheaper than a build, and it catches story type
|
|
272
|
+
errors that `storybook build` lets through. Run the repository's own
|
|
273
|
+
typecheck (`pnpm typecheck`, `pnpm exec tsc --noEmit`, `tsc -b`). Check
|
|
274
|
+
that its `include` covers the stories files and `.storybook/`. TypeScript
|
|
275
|
+
skips dot-folders in wildcards, so name the files: add
|
|
276
|
+
`".storybook/*.ts", ".storybook/*.tsx"` to the `include` the typecheck
|
|
277
|
+
uses. Fix every error.
|
|
278
|
+
2. **Build.** Run `storybook build` with the repository's package manager
|
|
279
|
+
(`pnpm exec storybook build --quiet`, `npx storybook build --quiet`). Fix
|
|
280
|
+
every error and warning that points at a story or the config. Then check
|
|
281
|
+
`storybook-static/index.json`: its story entries should match what you
|
|
282
|
+
wrote, and none of init's examples should be left.
|
|
283
|
+
3. Run the repository's lint and its other pull request checks that the
|
|
284
|
+
change touches (ESLint on the stories, a format check), and fix what this
|
|
285
|
+
change broke.
|
|
286
|
+
|
|
287
|
+
## 6. Check the design kit captures it
|
|
288
|
+
|
|
289
|
+
`design-kit capture stories` renders every story in a headless browser, so it
|
|
290
|
+
also proves each story renders without throwing. Write the kit to a scratch
|
|
291
|
+
folder **outside the repository**, never into it.
|
|
292
|
+
|
|
293
|
+
- **With a `design-kit.config.json`:** give it a `stories` section if it has
|
|
294
|
+
none:
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
"stories": {
|
|
298
|
+
"root": ".",
|
|
299
|
+
"buildCommand": "pnpm exec storybook build --quiet -o storybook-static",
|
|
300
|
+
"staticDir": "storybook-static",
|
|
301
|
+
"gaps": { "componentGlob": "src/**/[A-Z]*.{tsx,jsx}" }
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
(`root` is the folder holding `.storybook/`, relative to the config; use
|
|
306
|
+
the repository's package manager in `buildCommand`.) If the repository's
|
|
307
|
+
`.github/workflows/design-kit.yml` and `.claude/skills/design-kit/SKILL.md`
|
|
308
|
+
were written before Storybook existed, they skip stories too. To update
|
|
309
|
+
them, copy the repository (without `node_modules`) to a scratch folder,
|
|
310
|
+
run `npx --yes @codepress/design-kit init --force` there, and port only
|
|
311
|
+
the Storybook parts of those two files back: the "Build Storybook and
|
|
312
|
+
capture stories" step and the stories capture line. Then run, from the
|
|
313
|
+
folder that holds `design-kit.config.json`:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
npx --yes @codepress/design-kit capture stories --out "$TMPDIR/design-kit-check"
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
- **Without one:** check anyway with a scratch config outside the repository:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
cat > "$TMPDIR/design-kit-check.json" <<EOF
|
|
323
|
+
{"version": 1, "stories": {"root": "$PWD", "storybookDir": "$PWD/storybook-static",
|
|
324
|
+
"gaps": {"componentGlob": "src/**/[A-Z]*.{tsx,jsx}"}}}
|
|
325
|
+
EOF
|
|
326
|
+
npx --yes @codepress/design-kit capture stories --config "$TMPDIR/design-kit-check.json" --out "$TMPDIR/design-kit-check"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Capture needs Playwright and Chromium. When the repository has neither,
|
|
330
|
+
run `npx --yes -p @playwright/test playwright install chromium`, then
|
|
331
|
+
`npx --yes -p @codepress/design-kit -p @playwright/test design-kit capture stories ...`;
|
|
332
|
+
install nothing into the repository for it.
|
|
333
|
+
|
|
334
|
+
Capture what you just built: the `buildCommand` rebuilds Storybook, and with
|
|
335
|
+
a `storybookDir` rebuild first, because a stale build hides missing stories.
|
|
336
|
+
Read what it printed (`stories N, ... failed M`) and
|
|
337
|
+
`$TMPDIR/design-kit-check/gaps.json`:
|
|
338
|
+
|
|
339
|
+
- a **failed story** (it threw, or never rendered) is a bug in the story or
|
|
340
|
+
the config: fix it and capture again;
|
|
341
|
+
- a **`no story`** row names a component file with no stories file: write
|
|
342
|
+
one, or make sure it is in your skip list with its reason. Its path is
|
|
343
|
+
relative to the config file's folder.
|
|
344
|
+
|
|
345
|
+
Aim for zero failures and every `no story` row explained. The files in
|
|
346
|
+
`html/` are bare markup without CSS; to look at one story styled, find its id
|
|
347
|
+
and compose it into a page, then open that page in a browser:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
KIT="$TMPDIR/design-kit-check"
|
|
351
|
+
node "$KIT/kit.mjs" --kit "$KIT" find <component name> # ids like story:<storybook-id>@1280
|
|
352
|
+
node "$KIT/kit.mjs" --kit "$KIT" compose --frame <id> --out "$TMPDIR/story.html"
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Check it looks like the app, and take a screenshot for the pull request.
|
|
356
|
+
|
|
357
|
+
## 7. Open the pull request
|
|
358
|
+
|
|
359
|
+
One pull request; never merge it. When the survey and the checks found
|
|
360
|
+
nothing missing (Storybook builds, every component has its stories, the
|
|
361
|
+
capture has no failures and no unexplained `no story` rows), open no pull
|
|
362
|
+
request: report that proof instead. Otherwise the description says:
|
|
363
|
+
|
|
364
|
+
- **The survey**: where the components and tokens live, and whether
|
|
365
|
+
Storybook existed (extended, not re-initialised).
|
|
366
|
+
- **Versions and builder**: the Storybook version and why (9, or 8 because of
|
|
367
|
+
which floor), the framework package and why, and that React, Next and
|
|
368
|
+
TypeScript are unchanged.
|
|
369
|
+
- **First-build fixes**: each one you made (env placeholders, loaders,
|
|
370
|
+
`staticDirs`, overrides) and why.
|
|
371
|
+
- **Coverage**: the components covered, counted per folder ("14 of 15 in
|
|
372
|
+
`src/components`"), with the states each covers, and the tokens story.
|
|
373
|
+
- **Skipped**: every component or state left out, each with its reason.
|
|
374
|
+
- **Proof**: the typecheck and `storybook build` results, the capture's
|
|
375
|
+
story count, failures and `gaps.json` rows, and the screenshot.
|
|
376
|
+
- **Recommended next**: inline UI that should become shared components, and,
|
|
377
|
+
when the repository has no design kit yet, that
|
|
378
|
+
`npx --yes @codepress/design-kit setup-guide` sets one up.
|
|
379
|
+
- **Not tried**: anything this guide does not cover on this stack, said
|
|
380
|
+
plainly.
|
|
381
|
+
|
|
382
|
+
Never commit `storybook-static/`, a captured kit, or a real secret.
|
|
383
|
+
|
|
384
|
+
## Keeping stories current
|
|
385
|
+
|
|
386
|
+
After this pull request, when you add a component or change what one
|
|
387
|
+
renders, add or update its story in the same pull request, covering the
|
|
388
|
+
states in step 4, so the next design kit captures it. Say in the pull request
|
|
389
|
+
which stories you added or changed.
|
package/src/capture-stories.mjs
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -17,12 +17,18 @@ const SETUP_GUIDE = new URL(
|
|
|
17
17
|
import.meta.url,
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
+
/** The `storybook-setup` skill, printed by `design-kit storybook-guide`. */
|
|
21
|
+
const STORYBOOK_GUIDE = new URL(
|
|
22
|
+
"../skills/storybook-setup/SKILL.md",
|
|
23
|
+
import.meta.url,
|
|
24
|
+
);
|
|
25
|
+
|
|
20
26
|
const USAGE = `usage: design-kit <command> [options]
|
|
21
27
|
|
|
22
28
|
init set a kit up in this repository: the config, a daily
|
|
23
29
|
GitHub Actions build, and a skill for its agents
|
|
24
30
|
[--root <dir>] [--dry-run] [--force]
|
|
25
|
-
capture stories capture every story of a Storybook 7
|
|
31
|
+
capture stories capture every story of a Storybook 7-9 static build
|
|
26
32
|
[--config <file>] [--out <dir>] [--storybook-dir <dir>]
|
|
27
33
|
[--only <id,...> | --only-file <file>] [--base <previous kit>]
|
|
28
34
|
capture pages capture the configured pages of a running app
|
|
@@ -35,6 +41,9 @@ const USAGE = `usage: design-kit <command> [options]
|
|
|
35
41
|
[--config <file>]
|
|
36
42
|
setup-guide print the guide for setting a kit up in a repository as
|
|
37
43
|
one reviewable pull request (an agent skill)
|
|
44
|
+
storybook-guide print the guide for adding Storybook, with stories for
|
|
45
|
+
every existing component, as one reviewable pull request
|
|
46
|
+
(an agent skill)
|
|
38
47
|
|
|
39
48
|
The config is the nearest design-kit.config.json at or above the working
|
|
40
49
|
directory unless --config names one. Exit codes: 0 done, 1 a capture could
|
|
@@ -183,6 +192,11 @@ export async function main(argv = process.argv.slice(2), env = process.env) {
|
|
|
183
192
|
process.stdout.write(readFileSync(SETUP_GUIDE, "utf8"));
|
|
184
193
|
return 0;
|
|
185
194
|
}
|
|
195
|
+
if (command === "storybook-guide") {
|
|
196
|
+
parse(argv.slice(1), {});
|
|
197
|
+
process.stdout.write(readFileSync(STORYBOOK_GUIDE, "utf8"));
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
186
200
|
if (command === "validate") {
|
|
187
201
|
const options = parse(argv.slice(1), { config: "value" });
|
|
188
202
|
const config = loadConfig(options.config);
|
package/src/init.mjs
CHANGED
|
@@ -916,11 +916,11 @@ function todo(found) {
|
|
|
916
916
|
const sb = found.storybook;
|
|
917
917
|
if (!sb)
|
|
918
918
|
items.push(
|
|
919
|
-
"No Storybook: the config captures pages only. Decide whether to add Storybook with stories for the components the main screens use, capture real pages, or both.",
|
|
919
|
+
"No Storybook: the config captures pages only. Decide whether to add Storybook with stories for the components the main screens use (`npx --yes @codepress/design-kit storybook-guide` prints a guide for that), capture real pages, or both.",
|
|
920
920
|
);
|
|
921
|
-
else if (sb.major && (sb.major < 7 || sb.major >
|
|
921
|
+
else if (sb.major && (sb.major < 7 || sb.major > 9))
|
|
922
922
|
items.push(
|
|
923
|
-
`Storybook ${sb.version}: captures are supported for Storybook 7 and
|
|
923
|
+
`Storybook ${sb.version}: captures are supported for Storybook 7, 8 and 9 static builds; check that \`capture stories\` works, or use page capture.`,
|
|
924
924
|
);
|
|
925
925
|
items.push(
|
|
926
926
|
`Pages: replace the ${TODO} selectors in ${CONFIG_NAME} (pages[].ready and slots.main), add the app's main screens, and check startCommand and waitUrl. Until then the workflow skips page capture.`,
|
|
@@ -16,6 +16,12 @@ The kit is configured in `{{CONFIG}}` and built by the `Design kit` workflow
|
|
|
16
16
|
(`.github/workflows/design-kit.yml`). The format and every command are in the
|
|
17
17
|
`{{PACKAGE}}` README.
|
|
18
18
|
|
|
19
|
+
When this repository has Storybook and you add a component or change what
|
|
20
|
+
one renders, add or update its story in the same pull request, covering its
|
|
21
|
+
default, every variant and size, and the empty, loading, error and long-text
|
|
22
|
+
states that apply, so the next kit captures it. A component without a story
|
|
23
|
+
is missing from every kit until it gets one.
|
|
24
|
+
|
|
19
25
|
## 1. Fetch the newest kit
|
|
20
26
|
|
|
21
27
|
The workflow runs once a day and on demand (`workflow_dispatch`), and uploads
|