@atelier-ui/create-workspace 0.2.41 → 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 +33 -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 +220 -30
  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
@@ -0,0 +1,34 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3';
2
+ import { AtlButton } from '@atelier-ui/vue';
3
+
4
+ // A working pattern to copy: duplicate this file for another component from
5
+ // @atelier-ui/vue, or add more `args`/`argTypes` below.
6
+ const meta: Meta<typeof AtlButton> = {
7
+ title: 'AtlButton',
8
+ component: AtlButton,
9
+ tags: ['autodocs'],
10
+ render: (args) => ({
11
+ components: { AtlButton },
12
+ setup() {
13
+ return { args };
14
+ },
15
+ template: '<AtlButton v-bind="args">Button</AtlButton>',
16
+ }),
17
+ argTypes: {
18
+ variant: { control: 'select', options: ['primary', 'secondary', 'outline', 'danger'] },
19
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
20
+ disabled: { control: 'boolean' },
21
+ loading: { control: 'boolean' },
22
+ },
23
+ args: {
24
+ variant: 'primary',
25
+ size: 'md',
26
+ disabled: false,
27
+ loading: false,
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof AtlButton>;
33
+
34
+ export const Default: Story = {};
@@ -0,0 +1,38 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { dirname } from 'node:path';
3
+ import type { StorybookConfig } from '@storybook/vue3-vite';
4
+
5
+ const config: StorybookConfig = {
6
+ stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts)'],
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/vue3-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
+ // Vue's docgen server is opt-in under `@storybook/vue3-vite` until
29
+ // Storybook 11 — without this flag components.json has no props.
30
+ experimentalDocgenServer: true,
31
+ },
32
+ };
33
+
34
+ export default config;
35
+
36
+ function getAbsolutePath(value: string): string {
37
+ return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`)));
38
+ }
@@ -0,0 +1,29 @@
1
+ import type { Preview } from '@storybook/vue3';
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 vue from '@vitejs/plugin-vue';
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/vue 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
+ vue(),
17
+ storybookTest({
18
+ configDir: new URL('./.storybook', import.meta.url).pathname,
19
+ }),
20
+ ],
21
+ test: {
22
+ name: 'storybook:vue',
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,9 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
3
+ /// <reference types="vitest/globals" />
4
+ import { setProjectAnnotations } from '@storybook/vue3';
5
+ import * as projectAnnotations from './preview';
6
+
7
+ const project = setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
8
+
9
+ beforeAll(project.beforeAll);
@@ -22,10 +22,12 @@
22
22
 
23
23
  :root {
24
24
  /* Typography */
25
- --ui-font-family: 'Instrument Sans', ui-sans-serif, system-ui, -apple-system,
25
+ --ui-font-family:
26
+ 'Instrument Sans', ui-sans-serif, system-ui, -apple-system,
26
27
  BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
27
28
  --ui-font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
28
- --ui-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco,
29
+ --ui-font-mono:
30
+ 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco,
29
31
  'Courier New', monospace;
30
32
  /* Below the smallest readable size, for text inside a box too small to hold it:
31
33
  the initials in a 24px avatar and the count in its overflow badge. Named rather
@@ -74,9 +76,15 @@
74
76
  * padding, not one line among many). See ADR-0052.
75
77
  */
76
78
  --ui-row-inset: 0.25rem;
77
- --ui-row-height-sm: calc(var(--ui-control-height-sm) + 2 * var(--ui-row-inset));
78
- --ui-row-height-md: calc(var(--ui-control-height-md) + 2 * var(--ui-row-inset));
79
- --ui-row-height-lg: calc(var(--ui-control-height-lg) + 2 * var(--ui-row-inset));
79
+ --ui-row-height-sm: calc(
80
+ var(--ui-control-height-sm) + 2 * var(--ui-row-inset)
81
+ );
82
+ --ui-row-height-md: calc(
83
+ var(--ui-control-height-md) + 2 * var(--ui-row-inset)
84
+ );
85
+ --ui-row-height-lg: calc(
86
+ var(--ui-control-height-lg) + 2 * var(--ui-row-inset)
87
+ );
80
88
 
81
89
  --ui-control-height-sm: 2rem;
82
90
  --ui-control-height-md: 2.5rem;
@@ -104,8 +112,8 @@
104
112
  * role with --ui-letter-spacing-tight and an uppercased label role with
105
113
  * --ui-letter-spacing-uppercase yourself.
106
114
  */
107
- --ui-type-display: italic var(--ui-font-weight-normal) var(--ui-font-size-3xl) /
108
- var(--ui-line-height-tight) var(--ui-font-display);
115
+ --ui-type-display: italic var(--ui-font-weight-normal)
116
+ var(--ui-font-size-3xl) / var(--ui-line-height-tight) var(--ui-font-display);
109
117
  --ui-type-headline: var(--ui-font-weight-bold) var(--ui-font-size-2xl) /
110
118
  var(--ui-line-height-tight) var(--ui-font-family);
111
119
  --ui-type-title: var(--ui-font-weight-semibold) var(--ui-font-size-lg) /
@@ -292,9 +300,9 @@
292
300
  --ui-color-overlay: rgba(0, 0, 0, 0.5);
293
301
 
294
302
  /* Borders — split decorative from functional per WCAG 1.4.11 */
295
- --ui-color-border: #e2e8f0; /* decorative */
303
+ --ui-color-border: #e2e8f0; /* decorative */
296
304
  --ui-color-border-hover: #cbd5e1;
297
- --ui-color-border-strong: #64748b; /* functional ≥3:1 */
305
+ --ui-color-border-strong: #64748b; /* functional ≥3:1 */
298
306
 
299
307
  /* Text */
300
308
  --ui-color-text: #0f172a;
@@ -316,10 +324,10 @@
316
324
  --ui-opacity-disabled: 0.5;
317
325
 
318
326
  /* Border radius — bumped for modern feel */
319
- --ui-radius-sm: 0.5rem; /* was 0.375rem */
327
+ --ui-radius-sm: 0.5rem; /* was 0.375rem */
320
328
  --ui-radius-md: 0.625rem; /* was 0.5rem */
321
329
  --ui-radius-lg: 0.875rem; /* was 0.75rem */
322
- --ui-radius-xl: 1.25rem; /* was 1rem */
330
+ --ui-radius-xl: 1.25rem; /* was 1rem */
323
331
  --ui-radius-full: 9999px;
324
332
 
325
333
  /* ── Border widths ──────────────────────────────────────────────────────────
@@ -348,13 +356,15 @@
348
356
 
349
357
  /* Shadows — slate-tinted dual-layer (ambient + key light) */
350
358
  --ui-shadow-xs: 0 1px 2px 0 rgba(15, 23, 42, 0.05);
351
- --ui-shadow-sm: 0 1px 3px 0 rgba(15, 23, 42, 0.08),
352
- 0 1px 2px -1px rgba(15, 23, 42, 0.06);
353
- --ui-shadow-md: 0 4px 6px -1px rgba(15, 23, 42, 0.08),
354
- 0 2px 4px -2px rgba(15, 23, 42, 0.05);
355
- --ui-shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, 0.08),
359
+ --ui-shadow-sm:
360
+ 0 1px 3px 0 rgba(15, 23, 42, 0.08), 0 1px 2px -1px rgba(15, 23, 42, 0.06);
361
+ --ui-shadow-md:
362
+ 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
363
+ --ui-shadow-lg:
364
+ 0 10px 15px -3px rgba(15, 23, 42, 0.08),
356
365
  0 4px 6px -4px rgba(15, 23, 42, 0.04);
357
- --ui-shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, 0.10),
366
+ --ui-shadow-xl:
367
+ 0 20px 25px -5px rgba(15, 23, 42, 0.1),
358
368
  0 8px 10px -6px rgba(15, 23, 42, 0.04);
359
369
 
360
370
  /* Motion */
@@ -375,10 +385,10 @@
375
385
  * both of which are mode-dependent, so the ring follows the mode for free.
376
386
  * The danger variant exists so an invalid control does not have to restate
377
387
  * the formula (it did, in atl-input.css, until 2026-08-26). */
378
- --ui-focus-ring: 0 0 0 2px var(--ui-color-surface),
379
- 0 0 0 4px var(--ui-color-primary);
380
- --ui-focus-ring-danger: 0 0 0 2px var(--ui-color-surface),
381
- 0 0 0 4px var(--ui-color-danger);
388
+ --ui-focus-ring:
389
+ 0 0 0 2px var(--ui-color-surface), 0 0 0 4px var(--ui-color-primary);
390
+ --ui-focus-ring-danger:
391
+ 0 0 0 2px var(--ui-color-surface), 0 0 0 4px var(--ui-color-danger);
382
392
 
383
393
  /* Form controls */
384
394
  --ui-color-input-bg: var(--ui-color-surface-sunken);
@@ -450,18 +460,18 @@
450
460
  --ui-color-placeholder: #94a3b8;
451
461
 
452
462
  --ui-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
453
- --ui-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
454
- 0 1px 2px -1px rgba(0, 0, 0, 0.3);
455
- --ui-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.45),
456
- 0 2px 4px -2px rgba(0, 0, 0, 0.3);
457
- --ui-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5),
458
- 0 4px 6px -4px rgba(0, 0, 0, 0.25);
459
- --ui-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.5),
460
- 0 8px 10px -6px rgba(0, 0, 0, 0.25);
463
+ --ui-shadow-sm:
464
+ 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.3);
465
+ --ui-shadow-md:
466
+ 0 4px 6px -1px rgba(0, 0, 0, 0.45), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
467
+ --ui-shadow-lg:
468
+ 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.25);
469
+ --ui-shadow-xl:
470
+ 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 8px 10px -6px rgba(0, 0, 0, 0.25);
461
471
  }
462
472
  }
463
473
 
464
- [data-theme="dark"] {
474
+ [data-theme='dark'] {
465
475
  --ui-color-primary: var(--ui-color-teal-300);
466
476
  --ui-color-primary-hover: var(--ui-color-teal-200);
467
477
  --ui-color-primary-active: var(--ui-color-teal-100);
@@ -510,18 +520,18 @@
510
520
  --ui-color-placeholder: #94a3b8;
511
521
 
512
522
  --ui-shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
513
- --ui-shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.4),
514
- 0 1px 2px -1px rgba(0, 0, 0, 0.3);
515
- --ui-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.45),
516
- 0 2px 4px -2px rgba(0, 0, 0, 0.3);
517
- --ui-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5),
518
- 0 4px 6px -4px rgba(0, 0, 0, 0.25);
519
- --ui-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.5),
520
- 0 8px 10px -6px rgba(0, 0, 0, 0.25);
523
+ --ui-shadow-sm:
524
+ 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.3);
525
+ --ui-shadow-md:
526
+ 0 4px 6px -1px rgba(0, 0, 0, 0.45), 0 2px 4px -2px rgba(0, 0, 0, 0.3);
527
+ --ui-shadow-lg:
528
+ 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.25);
529
+ --ui-shadow-xl:
530
+ 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 8px 10px -6px rgba(0, 0, 0, 0.25);
521
531
  }
522
532
 
523
533
  /* Explicit light theme — declared last so it wins over prefers-color-scheme: dark */
524
- [data-theme="light"] {
534
+ [data-theme='light'] {
525
535
  --ui-color-primary: var(--ui-color-teal-700);
526
536
  --ui-color-primary-hover: var(--ui-color-teal-800);
527
537
  --ui-color-primary-active: var(--ui-color-teal-900);
@@ -570,10 +580,16 @@
570
580
  --ui-color-placeholder: #64748b;
571
581
 
572
582
  --ui-shadow-xs: 0 1px 2px 0 rgba(15, 23, 42, 0.05);
573
- --ui-shadow-sm: 0 1px 3px 0 rgba(15, 23, 42, 0.08), 0 1px 2px -1px rgba(15, 23, 42, 0.06);
574
- --ui-shadow-md: 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
575
- --ui-shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, 0.08), 0 4px 6px -4px rgba(15, 23, 42, 0.04);
576
- --ui-shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, 0.10), 0 8px 10px -6px rgba(15, 23, 42, 0.04);
583
+ --ui-shadow-sm:
584
+ 0 1px 3px 0 rgba(15, 23, 42, 0.08), 0 1px 2px -1px rgba(15, 23, 42, 0.06);
585
+ --ui-shadow-md:
586
+ 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
587
+ --ui-shadow-lg:
588
+ 0 10px 15px -3px rgba(15, 23, 42, 0.08),
589
+ 0 4px 6px -4px rgba(15, 23, 42, 0.04);
590
+ --ui-shadow-xl:
591
+ 0 20px 25px -5px rgba(15, 23, 42, 0.1),
592
+ 0 8px 10px -6px rgba(15, 23, 42, 0.04);
577
593
  }
578
594
 
579
595
  @media (prefers-reduced-motion: reduce) {