@atelier-ui/create-workspace 0.2.42 → 0.2.43

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +29 -4
  2. package/package.json +1 -1
  3. package/src/generators/preset/files/contracts/README.md +29 -0
  4. package/src/generators/preset/files/contracts/button.contract.ts.template +20 -0
  5. package/src/generators/preset/files/contracts/types.ts.template +55 -0
  6. package/src/generators/preset/files/figma/snapshot.json +164 -0
  7. package/src/generators/preset/files/storybook/angular/atl-button.stories.ts.template +38 -0
  8. package/src/generators/preset/files/storybook/angular/main.ts.template +39 -0
  9. package/src/generators/preset/files/storybook/angular/preview.ts.template +30 -0
  10. package/src/generators/preset/files/storybook/angular/tsconfig.json +16 -0
  11. package/src/generators/preset/files/storybook/angular/vitest.config.ts.template +40 -0
  12. package/src/generators/preset/files/storybook/angular/vitest.setup.ts.template +14 -0
  13. package/src/generators/preset/files/storybook/react/atl-button.stories.tsx +31 -0
  14. package/src/generators/preset/files/storybook/react/main.ts.template +37 -0
  15. package/src/generators/preset/files/storybook/react/preview.tsx +29 -0
  16. package/src/generators/preset/files/storybook/react/vitest.config.ts.template +32 -0
  17. package/src/generators/preset/files/storybook/react/vitest.setup.ts.template +8 -0
  18. package/src/generators/preset/files/storybook/vue/atl-button.stories.ts.template +34 -0
  19. package/src/generators/preset/files/storybook/vue/main.ts.template +38 -0
  20. package/src/generators/preset/files/storybook/vue/preview.ts.template +29 -0
  21. package/src/generators/preset/files/storybook/vue/vitest.config.ts.template +32 -0
  22. package/src/generators/preset/files/storybook/vue/vitest.setup.ts.template +9 -0
  23. package/src/generators/preset/files/styles/tokens.css +59 -43
  24. package/src/generators/preset/files/tools/scripts/check-contracts.mjs +1644 -0
  25. package/src/generators/preset/files/tools/scripts/figma-snapshot-contracts.mjs +370 -0
  26. package/src/generators/preset/files/tools/scripts/lib/docgen.mjs +573 -0
  27. package/src/generators/preset/files/tools/scripts/lib/ts-eval.js +126 -0
  28. package/src/generators/preset/files/tools/scripts/preflight.mjs +163 -31
  29. package/src/generators/preset/files/tools/stylelint-rules/index.js +21 -0
  30. package/src/generators/preset/files/tools/stylelint-rules/no-primitive-token.js +362 -0
  31. package/src/generators/preset/files/tools/stylelint-rules/no-raw-color-literal.js +154 -0
  32. package/src/generators/preset/files/tools/stylelint-rules/no-token-bypass.js +497 -0
  33. package/src/generators/preset/files/tools/stylelint-rules/no-undeclared-token.js +122 -0
  34. package/src/generators/preset/files/tools/stylelint-rules/utils.js +71 -0
  35. package/src/generators/preset/preset.d.ts +1 -0
  36. package/src/generators/preset/preset.js +955 -5
  37. package/src/generators/preset/preset.js.map +1 -1
  38. package/src/generators/preset/schema.d.ts +1 -0
  39. package/src/generators/preset/schema.json +5 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 0.2.43 (2026-09-12)
2
+
3
+ ### 🚀 Features
4
+
5
+ - **scaffold:** Prettier ships enforced, and a types script does not ship at all ([543dc719](https://github.com/DominikPieper/atelier-ui/commit/543dc719))
6
+ - **scaffold:** a generated workspace gets the CSS-discipline rules on day one ([68b097c8](https://github.com/DominikPieper/atelier-ui/commit/68b097c8))
7
+ - **lint:** the linter is a gate, where the invariant fits in one file ([e4e6a02f](https://github.com/DominikPieper/atelier-ui/commit/e4e6a02f))
8
+ - **gates:** a gate that measured nothing is not a gate that found nothing ([a45a6852](https://github.com/DominikPieper/atelier-ui/commit/a45a6852))
9
+ - **gates:** check:manifest-parity — the three manifests, diffed — ADR-0121 S6a ([fbd5fd31](https://github.com/DominikPieper/atelier-ui/commit/fbd5fd31))
10
+ - **spec:** the contract on every Docs tab, imported by every story meta — ADR-0121 S5b ([30dd54c0](https://github.com/DominikPieper/atelier-ui/commit/30dd54c0))
11
+ - **create-workspace:** the scaffold ships the contract loop — ADR-0121 S4 ([74aa6405](https://github.com/DominikPieper/atelier-ui/commit/74aa6405))
12
+ - **create-workspace:** Storybook and the skills ship together — ADR-0123 ([79b60e91](https://github.com/DominikPieper/atelier-ui/commit/79b60e91))
13
+
14
+ ### 🩹 Fixes
15
+
16
+ - **lint:** a broken allowlists module made the staleness check silent ([a41ff023](https://github.com/DominikPieper/atelier-ui/commit/a41ff023))
17
+ - **gates:** check:contracts skips package-imported components before docgen ([caa76332](https://github.com/DominikPieper/atelier-ui/commit/caa76332))
18
+ - **skill:** the Day-2 loop, walked as a participant — twenty findings, five blockers ([e1fe1409](https://github.com/DominikPieper/atelier-ui/commit/e1fe1409))
19
+
20
+ ### ❤️ Thank You
21
+
22
+ - Claude Fable 5.1
23
+ - Claude Opus 5 (1M context)
24
+ - Dominik Pieper @DominikPieper
25
+
1
26
  ## 0.2.42 (2026-09-10)
2
27
 
3
28
  This was a version bump only for create-workspace to align it with other projects, there were no code changes.
@@ -286,11 +311,11 @@ This was a version bump only for create-workspace to align it with other project
286
311
 
287
312
  ### 🚀 Features
288
313
 
289
- - ⚠️ **docs:** rename Llm prefix to Atl across docs, generated artifacts, and tooling docs ([965787d](https://github.com/DominikPieper/atelier-ui/commit/965787d))
314
+ - ⚠️ **docs:** rename Llm prefix to Atl across docs, generated artifacts, and tooling docs ([965787d](https://github.com/DominikPieper/atelier-ui/commit/965787d))
290
315
 
291
- ### ⚠️ Breaking Changes
316
+ ### ⚠️ Breaking Changes
292
317
 
293
- - **docs:** rename Llm prefix to Atl across docs, generated artifacts, and tooling docs ([965787d](https://github.com/DominikPieper/atelier-ui/commit/965787d))
318
+ - **docs:** rename Llm prefix to Atl across docs, generated artifacts, and tooling docs ([965787d](https://github.com/DominikPieper/atelier-ui/commit/965787d))
294
319
 
295
320
  ### ❤️ Thank You
296
321
 
@@ -576,4 +601,4 @@ This was a version bump only for create-workspace to align it with other project
576
601
  ### ❤️ Thank You
577
602
 
578
603
  - Claude Opus 4.7 (1M context)
579
- - Dominik Pieper @DominikPieper
604
+ - Dominik Pieper @DominikPieper
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelier-ui/create-workspace",
3
- "version": "0.2.42",
3
+ "version": "0.2.43",
4
4
  "description": "Nx preset for Atelier workshop workspaces",
5
5
  "keywords": [
6
6
  "nx",
@@ -0,0 +1,29 @@
1
+ # Contracts
2
+
3
+ A micro-contract (`types.ts`, ADR-0121 Decision 3) is the one hand-authored spec
4
+ artefact per component: which Figma master it is, and where Figma and code differ on
5
+ purpose (`figmaOnly`, `codeOnly`, `axisMap`, `probes`). Everything else — props,
6
+ defaults, unions, variant matrices, descriptions, token lists — is forbidden here; it
7
+ lives in the component's types/JSDoc, a story, or the master.
8
+
9
+ The planned check reads each `<name>.contract.ts` statically with
10
+ `tools/scripts/lib/ts-eval.js`'s `parseExportedVars`, so a contract must stay a plain
11
+ object literal (no imports of values, no computed members).
12
+
13
+ Every component story meta whose component has a contract must import it and set
14
+ `contract` in its `parameters` (`docs-block.ts`'s `ContractBlock` reads
15
+ `parameters.contract` to render the "Contract" section on the docs page); a story file
16
+ that has a contract but doesn't wire it in is `[CONTRACT-IMPORT]` — an error where
17
+ `docs-block.ts` ships beside the contracts (the Atelier monorepo does), a warning in a
18
+ scaffolded workspace until it ships the block.
19
+
20
+ To add one: create `<kebab-selector-without-atl-prefix>.contract.ts` in the contracts
21
+ directory (`AtlButton` → `button.contract.ts`), `import type { ComponentContract } from
22
+ './types';`, and `export const contract = { ... } satisfies ComponentContract;` —
23
+ `satisfies`, not a type annotation, so an extra key fails the build.
24
+
25
+ This file is shared verbatim between two locations: `libs/spec/src/contracts/README.md`
26
+ in this repo, and `<app>/src/contracts/README.md` in a scaffolded workspace
27
+ (`create-workspace`'s preset, kept byte-identical by `tools/scripts/sync-preflight.mjs`).
28
+ That is why it says "the contracts directory" above rather than naming a path — the
29
+ directory itself is `libs/spec/src/contracts/` here and `<app>/src/contracts/` there.
@@ -0,0 +1,20 @@
1
+ import type { ComponentContract } from './types';
2
+
3
+ export const contract = {
4
+ component: 'AtlButton',
5
+ figmaNodeId: '129:20',
6
+ codeOnly: [
7
+ {
8
+ name: 'type',
9
+ reason:
10
+ "Native HTML button 'type' passthrough ('button'|'submit'|'reset') — a form-semantics attribute the button element always carries, not a Figma-drawn visual axis.",
11
+ },
12
+ ],
13
+ figmaOnly: [
14
+ {
15
+ name: 'hasIcon',
16
+ reason:
17
+ 'Master description (2026-08-27, ADR-0058): a Figma-side slot toggle for placing an Icon instance (Instance Swap → Icon library), a legitimate authoring affordance but not part of the component contract — AtlButtonSpec has no hasIcon.',
18
+ },
19
+ ],
20
+ } satisfies ComponentContract;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The micro-contract (ADR-0121, Decision 3): the ONLY hand-authored spec artefact per
3
+ * component. It records what no derived artefact can know — which Figma master this
4
+ * component is, and where Figma and code differ on purpose. Everything else about the
5
+ * component is derived: props, types, defaults and descriptions from the docgen manifest;
6
+ * variant coverage from the stories; geometry and paint from the rendered story and the
7
+ * Figma snapshot.
8
+ *
9
+ * Forbidden here, by design: props, defaults, unions, variant matrices, prose
10
+ * descriptions, token lists. If you want to write one of those, the place is the
11
+ * component's types and JSDoc, a story, or the master. A contract that regrows into a
12
+ * metadata file has failed.
13
+ *
14
+ * Contracts are plain object literals — no imports of values, no computed members — so
15
+ * `tools/scripts/lib/ts-eval.js` can read them statically, the way it reads the metadata
16
+ * registry.
17
+ */
18
+ export interface ComponentContract {
19
+ /** Spec selector, e.g. `'AtlButton'`. Must equal the master's `selector` in `tools/figma/snapshot.json`. */
20
+ component: string;
21
+ /** The master's `COMPONENT_SET` (or lone `COMPONENT`) node id in the Atelier file, e.g. `'129:20'`. */
22
+ figmaNodeId: string;
23
+ /**
24
+ * Figma properties — variant axes, Booleans, Texts, instance swaps — that have no code
25
+ * prop on purpose, each with the reason. The `state` axis is NOT listed: interaction
26
+ * states are CSS pseudo-classes by repo-wide convention (ADR-0114) and the check knows it.
27
+ */
28
+ figmaOnly?: ReadonlyArray<{ name: string; reason: string }>;
29
+ /**
30
+ * Code props or events that have no Figma property on purpose, each with the reason
31
+ * (e.g. `open` — "false renders nothing", ADR-0056). Domain: only string-literal enum
32
+ * props — the code's own axes — ever need an entry here, for the same reason
33
+ * `[ENUM-UNDRAWN]` only ever reports enum props. Booleans, strings, numbers, callbacks
34
+ * and events with no Figma counterpart are normal (behaviour and content) and never
35
+ * need one.
36
+ */
37
+ codeOnly?: ReadonlyArray<{ name: string; reason: string }>;
38
+ /**
39
+ * A Figma axis that maps to a code prop under another name or value set, e.g. the
40
+ * `selection` axis (`checked` | `unchecked`) that is `checked: boolean` in code.
41
+ * Verbatim-equal axes need no entry. `codeProp` may be dotted (`Child.prop`, e.g.
42
+ * `AtlStep.completed`) when the prop lives on a CHILD component's own manifest rather
43
+ * than the story's primary component — the check resolves it there. Several entries
44
+ * may target the same `figmaAxis` (e.g. AtlStepper's `state` axis maps to three
45
+ * different `AtlStep` flags, one entry each).
46
+ */
47
+ axisMap?: ReadonlyArray<{
48
+ figmaAxis: string;
49
+ codeProp: string;
50
+ values?: Readonly<Record<string, string | number | boolean | null>>;
51
+ reason: string;
52
+ }>;
53
+ /** Only when the root frame is not the comparable layer: the part a parity probe measures instead. */
54
+ probes?: ReadonlyArray<{ part: string; selector: string; reason: string }>;
55
+ }
@@ -0,0 +1,164 @@
1
+ {
2
+ "meta": {
3
+ "fileKey": "QMnDD8uZQPldPrlCwZZ58T",
4
+ "figmaLastModified": "2026-09-10T04:18:29Z",
5
+ "projectedFrom": "tools/figma/snapshot.json"
6
+ },
7
+ "components": [
8
+ {
9
+ "selector": "AtlButton",
10
+ "nodeId": "129:20",
11
+ "name": "Inputs/AtlButton",
12
+ "description": "Action button. Maps to AtlButtonSpec in libs/spec.\n\nUse when: a user-initiated action — submit, save, delete, navigate. Pick the visual based on hierarchy: `primary` for the screen's main action (one per screen), `secondary` for supporting actions, `outline` for tertiary/inline, `danger` for destructive actions (delete, irreversible).\n\nDon't use when: the action is a navigation link (use a real `<a>` element, not a styled button), the choice is between two states (use Form/AtlToggle), or the user is filling a form field (use Form/AtlInput / Form/AtlSelect).\n\nAPI surface (Figma → spec):\n- Variant `variant`: primary | secondary | outline | danger — maps to AtlButtonSpec.variant\n- Variant `size`: sm | md | lg — maps to AtlButtonSpec.size\n- Variant `state`: default | hover | focus | active — interaction state\n- Boolean `disabled`: maps to AtlButtonSpec.disabled\n- Boolean `loading`: maps to AtlButtonSpec.loading — shows spinner, disables interaction\n- Boolean `hasIcon`: toggles the leading icon slot (Instance Swap → Icon library)\n\nA11y: native HTML <button>. When the button has no visible label (icon-only), supply `aria-label` — required by spec. Disabled state uses HTML `disabled` (not aria-disabled) for buttons since they're never expected to remain in the tab order.\n\nStatus: structural restructure 2026-04-27 — `state` enum slimmed to interaction (default/hover/focus/active); `loading` and `disabled` moved out as Booleans matching AtlButtonSpec; `hasIcon` Boolean added for icon-slot toggle.\n\nDeclared Boolean properties that reference no layer (2026-08-27, ADR-0058). check:figma reads these lines, so the reason is stated where a designer opening the master sees it:\n- Boolean `hasIcon`: declared but unbound — and it maps to nothing: `AtlButtonSpec` has no `hasIcon`. It is a Figma-side slot toggle for placing an Icon instance, which is a legitimate authoring affordance but not part of the component contract. Bind it to the icon slot layer when that slot exists, or drop it.",
13
+ "variantAxes": {
14
+ "variant": [
15
+ "primary",
16
+ "secondary",
17
+ "outline",
18
+ "danger"
19
+ ],
20
+ "size": [
21
+ "sm",
22
+ "md",
23
+ "lg"
24
+ ],
25
+ "state": [
26
+ "default",
27
+ "focus",
28
+ "hover",
29
+ "active"
30
+ ]
31
+ },
32
+ "properties": {
33
+ "disabled#507:192": "BOOLEAN",
34
+ "loading#507:217": "BOOLEAN",
35
+ "hasIcon#507:242": "BOOLEAN",
36
+ "variant": "VARIANT",
37
+ "size": "VARIANT",
38
+ "state": "VARIANT"
39
+ },
40
+ "variants": [
41
+ {
42
+ "variant": "primary",
43
+ "size": "sm",
44
+ "state": "default"
45
+ },
46
+ {
47
+ "variant": "primary",
48
+ "size": "md",
49
+ "state": "default"
50
+ },
51
+ {
52
+ "variant": "primary",
53
+ "size": "lg",
54
+ "state": "default"
55
+ },
56
+ {
57
+ "variant": "secondary",
58
+ "size": "sm",
59
+ "state": "default"
60
+ },
61
+ {
62
+ "variant": "secondary",
63
+ "size": "md",
64
+ "state": "default"
65
+ },
66
+ {
67
+ "variant": "secondary",
68
+ "size": "lg",
69
+ "state": "default"
70
+ },
71
+ {
72
+ "variant": "outline",
73
+ "size": "sm",
74
+ "state": "default"
75
+ },
76
+ {
77
+ "variant": "outline",
78
+ "size": "md",
79
+ "state": "default"
80
+ },
81
+ {
82
+ "variant": "outline",
83
+ "size": "lg",
84
+ "state": "default"
85
+ },
86
+ {
87
+ "variant": "primary",
88
+ "size": "md",
89
+ "state": "focus"
90
+ },
91
+ {
92
+ "variant": "primary",
93
+ "size": "md",
94
+ "state": "hover"
95
+ },
96
+ {
97
+ "variant": "primary",
98
+ "size": "md",
99
+ "state": "active"
100
+ },
101
+ {
102
+ "variant": "danger",
103
+ "size": "sm",
104
+ "state": "default"
105
+ },
106
+ {
107
+ "variant": "danger",
108
+ "size": "md",
109
+ "state": "default"
110
+ },
111
+ {
112
+ "variant": "danger",
113
+ "size": "lg",
114
+ "state": "default"
115
+ },
116
+ {
117
+ "variant": "danger",
118
+ "size": "md",
119
+ "state": "hover"
120
+ },
121
+ {
122
+ "variant": "danger",
123
+ "size": "md",
124
+ "state": "active"
125
+ },
126
+ {
127
+ "variant": "danger",
128
+ "size": "md",
129
+ "state": "focus"
130
+ },
131
+ {
132
+ "variant": "secondary",
133
+ "size": "md",
134
+ "state": "hover"
135
+ },
136
+ {
137
+ "variant": "secondary",
138
+ "size": "md",
139
+ "state": "active"
140
+ },
141
+ {
142
+ "variant": "secondary",
143
+ "size": "md",
144
+ "state": "focus"
145
+ },
146
+ {
147
+ "variant": "outline",
148
+ "size": "md",
149
+ "state": "hover"
150
+ },
151
+ {
152
+ "variant": "outline",
153
+ "size": "md",
154
+ "state": "active"
155
+ },
156
+ {
157
+ "variant": "outline",
158
+ "size": "md",
159
+ "state": "focus"
160
+ }
161
+ ]
162
+ }
163
+ ]
164
+ }
@@ -0,0 +1,38 @@
1
+ import type { Meta, StoryObj } from '@storybook/angular-vite';
2
+ import { argsToTemplate } from '@storybook/angular-vite';
3
+ import { AtlButton } from '@atelier-ui/angular';
4
+
5
+ // A working pattern to copy: duplicate this file for another component from
6
+ // @atelier-ui/angular, or add more `args`/`argTypes` below.
7
+ const meta: Meta<AtlButton> = {
8
+ title: 'AtlButton',
9
+ component: AtlButton,
10
+ tags: ['autodocs'],
11
+ render: (args) => ({
12
+ props: args,
13
+ template: `<atl-button ${argsToTemplate(args)}>Button</atl-button>`,
14
+ }),
15
+ argTypes: {
16
+ variant: {
17
+ control: 'select',
18
+ options: ['primary', 'secondary', 'outline', 'danger'],
19
+ },
20
+ size: {
21
+ control: 'select',
22
+ options: ['sm', 'md', 'lg'],
23
+ },
24
+ disabled: { control: 'boolean' },
25
+ loading: { control: 'boolean' },
26
+ },
27
+ args: {
28
+ variant: 'primary',
29
+ size: 'md',
30
+ disabled: false,
31
+ loading: false,
32
+ },
33
+ };
34
+
35
+ export default meta;
36
+ type Story = StoryObj<AtlButton>;
37
+
38
+ export const Default: Story = {};
@@ -0,0 +1,39 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { dirname } from 'node:path';
3
+ import type { StorybookConfig } from '@storybook/angular-vite';
4
+
5
+ const config: StorybookConfig = {
6
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
7
+ addons: [
8
+ // Emits manifests/{docs,components}.json, which is what lets the
9
+ // storybookjs/mcp skills' docs-list/docs-show tools and dev tools
10
+ // (stories-preview, get-storybook-story-instructions) work against this
11
+ // workspace's own components. addon-vitest below is what makes the
12
+ // skills' `test-run` tool work too, once a local Storybook is running —
13
+ // see vitest.config.ts and CLAUDE.md's "The contract loop".
14
+ '@storybook/addon-mcp',
15
+ getAbsolutePath('@storybook/addon-vitest'),
16
+ getAbsolutePath('@storybook/addon-a11y'),
17
+ getAbsolutePath('@storybook/addon-docs'),
18
+ ],
19
+ framework: {
20
+ name: getAbsolutePath('@storybook/angular-vite'),
21
+ options: {},
22
+ },
23
+ docs: {},
24
+ features: {
25
+ // Read by Storybook's core-server at build time and by addon-mcp's
26
+ // docs-toolset gate on a dev server.
27
+ componentsManifest: true,
28
+ // Angular's docgen server is opt-in until Storybook 11 for some adapters;
29
+ // explicit here so `components.json` carries real prop tables, not just
30
+ // id/name.
31
+ experimentalDocgenServer: true,
32
+ },
33
+ };
34
+
35
+ export default config;
36
+
37
+ function getAbsolutePath(value: string): string {
38
+ return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
39
+ }
@@ -0,0 +1,30 @@
1
+ import type { Preview } from '@storybook/angular-vite';
2
+
3
+ // Side-effect import: Vite injects tokens.css globally into the page
4
+ import '../src/styles/tokens.css';
5
+
6
+ const preview: Preview = {
7
+ parameters: {
8
+ a11y: {
9
+ // 'error' - fail CI on a11y violations (chosen — ADR-0121 S0)
10
+ // 'todo' - show a11y violations in the test UI only
11
+ // 'off' - skip a11y checks entirely
12
+ test: 'error',
13
+ },
14
+ controls: {
15
+ matchers: {
16
+ color: /(background|color)$/i,
17
+ date: /Date$/i,
18
+ },
19
+ },
20
+ backgrounds: {
21
+ options: {
22
+ light: { name: 'light', value: '#ffffff' },
23
+ subtle: { name: 'subtle', value: '#f5f5f5' },
24
+ dark: { name: 'dark', value: '#1a1a2e' },
25
+ },
26
+ },
27
+ },
28
+ };
29
+
30
+ export default preview;
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "",
5
+ "types": ["node"],
6
+ "ignoreDeprecations": "6.0"
7
+ },
8
+ "include": [
9
+ "../src/**/*.stories.ts",
10
+ "../src/**/*.stories.tsx",
11
+ "../src/**/*.mdx",
12
+ "./preview.ts",
13
+ "./main.ts"
14
+ ],
15
+ "exclude": ["../src/**/*.spec.ts", "../src/**/*.test.ts"]
16
+ }
@@ -0,0 +1,40 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
3
+ // Same plugin libs/angular/vite.config.mts uses in the monorepo. Without it,
4
+ // `@angular/common`'s partially-Ivy-compiled `PlatformLocation` factory has no
5
+ // linker/JIT compiler wired up in this Vite pipeline, and merely importing
6
+ // `@storybook/angular-vite` in vitest.setup.ts throws "The injectable
7
+ // 'PlatformLocation' needs to be compiled using the JIT compiler, but
8
+ // '@angular/compiler' is not available" before a single story loads.
9
+ // React/Vue don't need an equivalent because their frameworks ship no such
10
+ // partially-compiled npm packages.
11
+ import angular from '@analogjs/vite-plugin-angular';
12
+ import { playwright } from '@vitest/browser-playwright';
13
+
14
+ // Named `vitest.config.ts` (not `vitest.storybook.config.ts`, the name the
15
+ // monorepo's own libs/angular uses) so `@storybook/addon-vitest`'s `test-run`
16
+ // tool finds it directly: its VitestManager walks up from a story's
17
+ // `.storybook` directory looking for the nearest vitest/vite config, and
18
+ // this is the standard filename it looks for first. The monorepo needs a
19
+ // root `vitest.config.mjs` with an explicit `projects: [...]` list because
20
+ // each library's own file has a non-standard name; a single-app scaffold
21
+ // doesn't need that indirection.
22
+ export default defineConfig({
23
+ plugins: [
24
+ angular(),
25
+ storybookTest({
26
+ configDir: new URL('./.storybook', import.meta.url).pathname,
27
+ }),
28
+ ],
29
+ test: {
30
+ name: 'storybook:angular',
31
+ globals: true,
32
+ browser: {
33
+ enabled: true,
34
+ headless: true,
35
+ provider: playwright(),
36
+ instances: [{ browser: 'chromium' }],
37
+ },
38
+ setupFiles: ['.storybook/vitest.setup.ts'],
39
+ },
40
+ });
@@ -0,0 +1,14 @@
1
+ /// <reference types="vitest/globals" />
2
+ // '@storybook/angular-vite', not '@storybook/angular' (which the monorepo's
3
+ // own libs/angular/.storybook/vitest.setup.ts imports): this scaffold
4
+ // deliberately never installs '@storybook/angular' — its peer on
5
+ // @angular-devkit/build-angular collides with a freshly scaffolded Angular
6
+ // app's own build tooling (see the STORYBOOK_FRAMEWORK_PACKAGE comment in
7
+ // preset.ts). '@storybook/angular-vite' re-exports the same
8
+ // setProjectAnnotations.
9
+ import { setProjectAnnotations } from '@storybook/angular-vite';
10
+ import * as projectAnnotations from './preview';
11
+
12
+ const project = setProjectAnnotations([projectAnnotations]);
13
+
14
+ beforeAll(project.beforeAll);
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { AtlButton } from '@atelier-ui/react';
3
+
4
+ // A working pattern to copy: duplicate this file for another component from
5
+ // @atelier-ui/react, or add more `args`/`argTypes` below.
6
+ const meta: Meta<typeof AtlButton> = {
7
+ title: 'AtlButton',
8
+ component: AtlButton,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ variant: {
12
+ control: 'select',
13
+ options: ['primary', 'secondary', 'outline', 'danger'],
14
+ },
15
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
16
+ disabled: { control: 'boolean' },
17
+ loading: { control: 'boolean' },
18
+ },
19
+ args: {
20
+ variant: 'primary',
21
+ size: 'md',
22
+ disabled: false,
23
+ loading: false,
24
+ children: 'Button',
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+ type Story = StoryObj<typeof AtlButton>;
30
+
31
+ export const Default: Story = {};
@@ -0,0 +1,37 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { dirname } from 'node:path';
3
+ import type { StorybookConfig } from '@storybook/react-vite';
4
+
5
+ const config: StorybookConfig = {
6
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
7
+ addons: [
8
+ // Emits manifests/{docs,components}.json, which is what lets the
9
+ // storybookjs/mcp skills' docs-list/docs-show tools and dev tools
10
+ // (stories-preview, get-storybook-story-instructions) work against this
11
+ // workspace's own components. addon-vitest below is what makes the
12
+ // skills' `test-run` tool work too, once a local Storybook is running —
13
+ // see vitest.config.ts and CLAUDE.md's "The contract loop".
14
+ '@storybook/addon-mcp',
15
+ getAbsolutePath('@storybook/addon-vitest'),
16
+ getAbsolutePath('@storybook/addon-a11y'),
17
+ getAbsolutePath('@storybook/addon-docs'),
18
+ ],
19
+ framework: {
20
+ name: getAbsolutePath('@storybook/react-vite'),
21
+ options: {},
22
+ },
23
+ docs: {},
24
+ features: {
25
+ // Read by Storybook's core-server at build time and by addon-mcp's
26
+ // docs-toolset gate on a dev server. React defaults its own docgen server
27
+ // (react-docgen), unlike Angular and Vue — no experimentalDocgenServer
28
+ // flag needed here.
29
+ componentsManifest: true,
30
+ },
31
+ };
32
+
33
+ export default config;
34
+
35
+ function getAbsolutePath(value: string): string {
36
+ return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
37
+ }
@@ -0,0 +1,29 @@
1
+ import type { Preview } from '@storybook/react';
2
+
3
+ import '../src/styles/tokens.css';
4
+
5
+ const preview: Preview = {
6
+ parameters: {
7
+ a11y: {
8
+ // 'error' - fail CI on a11y violations (chosen — ADR-0121 S0)
9
+ // 'todo' - show a11y violations in the test UI only
10
+ // 'off' - skip a11y checks entirely
11
+ test: 'error',
12
+ },
13
+ controls: {
14
+ matchers: {
15
+ color: /(background|color)$/i,
16
+ date: /Date$/i,
17
+ },
18
+ },
19
+ backgrounds: {
20
+ options: {
21
+ light: { name: 'light', value: '#ffffff' },
22
+ subtle: { name: 'subtle', value: '#f5f5f5' },
23
+ dark: { name: 'dark', value: '#1a1a2e' },
24
+ },
25
+ },
26
+ },
27
+ };
28
+
29
+ export default preview;
@@ -0,0 +1,32 @@
1
+ import { defineConfig } from 'vitest/config';
2
+ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
3
+ import react from '@vitejs/plugin-react';
4
+ import { playwright } from '@vitest/browser-playwright';
5
+
6
+ // Named `vitest.config.ts` (not `vitest.storybook.config.ts`, the name the
7
+ // monorepo's own libs/react uses) so `@storybook/addon-vitest`'s `test-run`
8
+ // tool finds it directly: its VitestManager walks up from a story's
9
+ // `.storybook` directory looking for the nearest vitest/vite config, and
10
+ // this is the standard filename it looks for first. The monorepo needs a
11
+ // root `vitest.config.mjs` with an explicit `projects: [...]` list because
12
+ // each library's own file has a non-standard name; a single-app scaffold
13
+ // doesn't need that indirection.
14
+ export default defineConfig({
15
+ plugins: [
16
+ react(),
17
+ storybookTest({
18
+ configDir: new URL('./.storybook', import.meta.url).pathname,
19
+ }),
20
+ ],
21
+ test: {
22
+ name: 'storybook:react',
23
+ globals: true,
24
+ browser: {
25
+ enabled: true,
26
+ headless: true,
27
+ provider: playwright(),
28
+ instances: [{ browser: 'chromium' }],
29
+ },
30
+ setupFiles: ['.storybook/vitest.setup.ts'],
31
+ },
32
+ });
@@ -0,0 +1,8 @@
1
+ import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
2
+ /// <reference types="vitest/globals" />
3
+ import { setProjectAnnotations } from '@storybook/react';
4
+ import * as projectAnnotations from './preview';
5
+
6
+ const project = setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
7
+
8
+ beforeAll(project.beforeAll);