@fracazo/design-system 0.2.1 → 0.7.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.
Files changed (46) hide show
  1. package/DESIGN.md +11 -1
  2. package/README.md +78 -13
  3. package/css/motion.css +155 -0
  4. package/css/roles.css +3 -0
  5. package/dist/guardrails/eslint.d.ts +72 -5
  6. package/dist/guardrails/eslint.js +197 -29
  7. package/dist/guardrails/init.d.ts +2 -0
  8. package/dist/guardrails/init.js +65 -0
  9. package/dist/guardrails/intake.d.ts +2 -0
  10. package/dist/guardrails/intake.js +131 -0
  11. package/dist/src/index.d.ts +1 -0
  12. package/dist/src/index.js +1 -0
  13. package/dist/src/ui/offer-card.d.ts +52 -0
  14. package/dist/src/ui/offer-card.js +7 -0
  15. package/package.json +8 -3
  16. package/skills/product-design/SKILL.md +142 -0
  17. package/skills/product-design/coverage-gaps.md +41 -0
  18. package/skills/product-design/exemplars/calm-the-offering-cards.md +33 -0
  19. package/skills/product-design/exemplars/clamp-drift-to-named-roles.md +37 -0
  20. package/skills/product-design/exemplars/concentric-radii-and-button-optics.md +36 -0
  21. package/skills/product-design/exemplars/dialog-close-focus-visible.md +36 -0
  22. package/skills/product-design/exemplars/hero-glow-seam.md +34 -0
  23. package/skills/product-design/intake/2026-09-07.md +413 -0
  24. package/skills/product-design/references/components.md +43 -0
  25. package/skills/product-design/references/copy.md +25 -0
  26. package/skills/product-design/references/intake.md +66 -0
  27. package/skills/product-design/references/motion.md +22 -0
  28. package/skills/product-design/references/rules.md +319 -0
  29. package/skills/product-design/references/surfaces.md +50 -0
  30. package/skills/product-design/references/tokens.md +54 -0
  31. package/skills/product-design/references/type-and-space.md +42 -0
  32. package/skills/product-design/references/verification.md +35 -0
  33. package/template/CLAUDE.md +47 -0
  34. package/template/README.md +16 -0
  35. package/template/eslint.config.mjs +20 -0
  36. package/template/gitignore +44 -0
  37. package/template/next.config.ts +7 -0
  38. package/template/package.json +40 -0
  39. package/template/pnpm-workspace.yaml +14 -0
  40. package/template/postcss.config.mjs +7 -0
  41. package/template/src/app/globals.css +66 -0
  42. package/template/src/app/layout.tsx +55 -0
  43. package/template/src/app/page.tsx +59 -0
  44. package/template/src/components/ThemeSync.tsx +21 -0
  45. package/template/src/system/brands/starter.css +143 -0
  46. package/template/tsconfig.json +34 -0
package/DESIGN.md CHANGED
@@ -14,6 +14,11 @@ refuses to ship, and where each brand differs. It is deliberately short.
14
14
  The stylesheet carries the visual decisions; this file carries judgment,
15
15
  and gains a rule only when the same correction has been made twice.
16
16
 
17
+ Agents load `skills/product-design/SKILL.md` first: it names the request
18
+ mode, routes to the reference that applies, and cites rules by stable ID
19
+ from `skills/product-design/references/rules.md`. This file stays the
20
+ narrative those rules point back to.
21
+
17
22
  Read `css/roles.css` for the roles and the brand contract, the component
18
23
  source in `src/ui/*` for each component's intent, and the product's own
19
24
  CLAUDE.md for the rules that are local to it.
@@ -215,7 +220,7 @@ Source order is reading order; landmarks and heading levels are real;
215
220
 
216
221
  ## Components
217
222
 
218
- The package ships seventeen shadcn-based components under
223
+ The package ships eighteen components, seventeen shadcn-based and one house primitive, under
219
224
  `@fracazo/design-system/ui/*`. Each carries an intent block at the top of
220
225
  its source (one line, then Use for, Avoid when, Variants); that block is
221
226
  the contract and is updated whenever variants change. In brief:
@@ -245,6 +250,11 @@ the contract and is updated whenever variants change. In brief:
245
250
  bespoke. **Calendar** exists for range selection; a single date uses a
246
251
  native input. **SortableList** ranks a small capped set with drag,
247
252
  touch and keyboard.
253
+ - **OfferCard** is the one house primitive: a linked offer surface with
254
+ a media slot, tag, title, description and a source footer, for a grid
255
+ of peer offers. The whole card is one link; `highlighted` is its single
256
+ emphasis signal; hover motion is CSS transitions under motion-safe.
257
+ It lays out in a grid, never a carousel.
248
258
 
249
259
  A component that must import app code (stores, data clients, routes)
250
260
  does not belong in the package; it stays in the product, like
package/README.md CHANGED
@@ -1,13 +1,20 @@
1
1
  # @fracazo/design-system
2
2
 
3
- Roles, a brand contract, guardrails and components for a warm, evidence-led
4
- product design system. The package ships the half of the system that is the same
5
- for every product; each product supplies one brand file with its values.
6
- Change the brand file and the whole product re-skins; the roles never move.
3
+ An agent-native design system. Design decisions as code, so the quality bar lives in the tooling.
7
4
 
8
- Built for [BirthGuide](https://birthguide.com.au) and
9
- [birthplans.app](https://www.birthplans.app), designed to start the next
10
- product from.
5
+ When a team ships faster, the design review queue is the first thing that breaks. Either every change waits on a designer, or the bar drops quietly. Neither works.
6
+
7
+ This package moves the bar out of the review queue and into the tooling, where it holds whether a designer is in the room or not.
8
+
9
+ It lives in three places.
10
+
11
+ **Lint.** Eight ESLint rules catch what a reviewer would: colour literals, radius literals, stock palette, dark pairs, arbitrary sizes, focus rings, text on dark surfaces. The build fails before anyone posts a screenshot.
12
+
13
+ **Components.** Each of the eighteen components carries its own guidance in JSDoc: use for, avoid when, variants. The decision sits where it gets made, not in a doc nobody opens.
14
+
15
+ **The agent skill.** AI tools load the design rules before they build or review any UI, route to the reference that applies, and cite rules by stable ID. The system proposes, the human commits.
16
+
17
+ Built for BirthGuide and birthplans.app, and designed to start the next product from.
11
18
 
12
19
  ## What is in the package
13
20
 
@@ -15,15 +22,46 @@ product from.
15
22
  |---|---|
16
23
  | `css/roles.css` | The system: the dark variant, the Tailwind v4 `@theme` mapping, the radius ramp, fluid type roles, band rhythm, the eight aliasing semantics, and the **brand contract** at the top |
17
24
  | `ds-check-brand` | Holds a brand file to the contract: nothing missing, nothing extra |
25
+ | `ds-intake` | Collects design-relevant commits from product repos into an intake packet the agent proposes and a human commits (see the skill's `references/intake.md`) |
18
26
  | `ds-build-brand-css` | Composes the plain-CSS token file a product serves publicly (e.g. `/brand.css`) |
19
- | `@fracazo/design-system` and `./ui/*` | `cn` and seventeen shadcn-based components (button, card, dialog, form, select, sortable-list and the rest), each with intent JSDoc: use for, avoid when, variants |
20
- | `@fracazo/design-system/eslint` | Two guardrails: no raw colours and no arbitrary fluid type sizes in a `className` |
27
+ | `ds-init` and `template/` | Writes a new product: Next 16, Tailwind v4, this package, a blank brand file and the guardrails on, pinned to the package version that wrote it |
28
+ | `css/motion.css` | The animation vocabulary the components use (enter, exit, accordion, the fade, zoom, blur and slide utilities); `roles.css` imports it |
29
+ | `@fracazo/design-system` and `./ui/*` | `cn` and eighteen components (button, card, dialog, form, select, sortable-list, offer-card and the rest), each with intent JSDoc: use for, avoid when, variants |
30
+ | `@fracazo/design-system/eslint` | Eight guardrails as an ESLint plugin, one per rule ID: colour literals, arbitrary clamp sizes, dark pairs, radius literals, stock palette, `focus:` rings, text on always-dark surfaces, em dashes |
21
31
  | `demo/index.html` | A showcase page that renders the roles in both modes off a served `/brand.css` |
32
+ | `skills/product-design/` | The agent skill: request modes, routed references, rules with stable IDs, exemplars, coverage gaps. Point your CLAUDE.md or AGENTS.md at its `SKILL.md` |
22
33
  | `DESIGN.md` | The written authority: who the reader is, the priority order, how a page is composed, the rejection list, one short chapter per brand |
23
34
 
24
35
  Brand files live in each product repo, not here. The contract is what keeps
25
- them honest. A new product starts from the `design-system-starter` template:
26
- Next 16, Tailwind v4, this package, a blank brand file and the guardrails on.
36
+ them honest.
37
+
38
+ ## Start a product
39
+
40
+ ```bash
41
+ pnpm dlx --package @fracazo/design-system ds-init my-product
42
+ ```
43
+
44
+ That writes `template/` into `my-product`: Next 16, Tailwind v4, this
45
+ package, every role with an achromatic placeholder in
46
+ `src/system/brands/starter.css`, the house base layer in `globals.css`, the
47
+ pre-paint dark script, `designSystemGuardrails()` with no exemptions, and
48
+ `pnpm lint` running ESLint plus `ds-check-brand`. It refuses a non-empty
49
+ directory. Then:
50
+
51
+ 1. `pnpm install`.
52
+ 2. Rename the brand file to the product and update the two paths that name
53
+ it: the `@import` in `globals.css` and the `brand:*` scripts.
54
+ 3. Replace every value in the brand file, light and dark. Keep the property
55
+ names; `pnpm brand:contract` holds you to them. `DESIGN.md` says what
56
+ each role is for and which six semantics are meant to diverge in dark.
57
+ 4. Pick the typeface in `layout.tsx` and point the brand file's `@theme`
58
+ block at its variable.
59
+ 5. Rewrite the top of `CLAUDE.md`, delete `page.tsx`, build the first
60
+ surface. `pnpm lint && pnpm typecheck && pnpm build` before every merge.
61
+
62
+ The template is proven against the package it ships with: its brand file
63
+ passes the contract and a product written from it lints, typechecks and
64
+ builds before a release.
27
65
 
28
66
  ## Consume it
29
67
 
@@ -38,7 +76,8 @@ pnpm add @fracazo/design-system
38
76
  ```
39
77
 
40
78
  Import the roles, then exactly one brand file, at the top of your global
41
- stylesheet. Order matters: roles first.
79
+ stylesheet. Order matters: roles first. `roles.css` also brings in the
80
+ motion vocabulary the components use, so no animation library is needed.
42
81
 
43
82
  ```css
44
83
  @import "tailwindcss";
@@ -89,10 +128,35 @@ export default defineConfig([
89
128
  designSystemGuardrails({
90
129
  files: ['src/**/*.{ts,tsx}'],
91
130
  ignores: ['src/components/pdf/**', 'src/lib/email.ts'],
131
+ // Clean rules are errors by default; stock palette, focus: rings,
132
+ // on-dark text and em dashes start as warnings. Turn each up once
133
+ // the product is clean, or down while a pass is pending.
134
+ severity: { 'no-stock-palette': 'error', 'no-arbitrary-clamp': 'warn' },
92
135
  }),
93
136
  ])
94
137
  ```
95
138
 
139
+ A deliberate one-off names the rule and the reason:
140
+
141
+ ```tsx
142
+ {/* eslint-disable-next-line design-system/no-radius-literal -- phone bezel, not a UI corner */}
143
+ ```
144
+
145
+ ## Tell your agent when to load the skill
146
+
147
+ In the product's CLAUDE.md or AGENTS.md:
148
+
149
+ ```
150
+ When shaping, building, reviewing or writing copy for user-facing UI, load
151
+ node_modules/@fracazo/design-system/skills/product-design/SKILL.md first.
152
+ Skip it for backend-only work, telemetry, generated files and tests with no
153
+ shipped UI.
154
+ ```
155
+
156
+ The skill names the request mode, routes to the reference that applies and
157
+ cites rules by stable ID. `DESIGN.md` is the narrative those rules point
158
+ back to.
159
+
96
160
  ## The two tiers, in one paragraph
97
161
 
98
162
  Primitives hold literals (`--brand`, `--band`, `--ink`) and live in the brand
@@ -102,7 +166,8 @@ primary, accent, ring and their foregrounds) or hold a brand-tuned literal
102
166
  in the brand file (the six that diverge in dark: secondary, muted, border,
103
167
  input, muted-foreground, accent-foreground). Dark border and input as
104
168
  translucent hairlines is a design decision, not duplication; never "fix" a
105
- divergent semantic by aliasing it.
169
+ divergent semantic by aliasing it. Change the brand file and the whole product
170
+ re-skins; the roles never move.
106
171
 
107
172
  ## Versioning
108
173
 
package/css/motion.css ADDED
@@ -0,0 +1,155 @@
1
+ /* =============================================================================
2
+ Design system motion (css/motion.css)
3
+
4
+ The animation vocabulary the package's components and the products use:
5
+ enter and exit keyframes driven by custom properties, the accordion
6
+ height keyframes, and the utilities that set those properties. Imported
7
+ by roles.css, so one import covers roles and motion. Class names and
8
+ values match tw-animate-css 1.4.0 for exactly the subset in use, so
9
+ dropping that import changes nothing on screen; the subset is the point.
10
+ Add a utility here only when a component or a product needs it.
11
+
12
+ Duration and easing read --tw-duration and --tw-ease first, so Tailwind's
13
+ duration-* and ease-* utilities tune a single animation; the defaults are
14
+ 150ms ease for enter and exit, 200ms ease-out for the accordion.
15
+ ============================================================================= */
16
+
17
+ /* Non-inherited so a child that animates never picks up its parent's offsets. */
18
+ @property --tw-animation-delay { syntax: "*"; inherits: false; initial-value: 0s; }
19
+ @property --tw-animation-direction { syntax: "*"; inherits: false; initial-value: normal; }
20
+ @property --tw-animation-duration { syntax: "*"; inherits: false; }
21
+ @property --tw-animation-fill-mode { syntax: "*"; inherits: false; initial-value: none; }
22
+ @property --tw-animation-iteration-count { syntax: "*"; inherits: false; initial-value: 1; }
23
+ @property --tw-enter-blur { syntax: "*"; inherits: false; initial-value: 0; }
24
+ @property --tw-enter-opacity { syntax: "*"; inherits: false; initial-value: 1; }
25
+ @property --tw-enter-rotate { syntax: "*"; inherits: false; initial-value: 0; }
26
+ @property --tw-enter-scale { syntax: "*"; inherits: false; initial-value: 1; }
27
+ @property --tw-enter-translate-x { syntax: "*"; inherits: false; initial-value: 0; }
28
+ @property --tw-enter-translate-y { syntax: "*"; inherits: false; initial-value: 0; }
29
+ @property --tw-exit-blur { syntax: "*"; inherits: false; initial-value: 0; }
30
+ @property --tw-exit-opacity { syntax: "*"; inherits: false; initial-value: 1; }
31
+ @property --tw-exit-rotate { syntax: "*"; inherits: false; initial-value: 0; }
32
+ @property --tw-exit-scale { syntax: "*"; inherits: false; initial-value: 1; }
33
+ @property --tw-exit-translate-x { syntax: "*"; inherits: false; initial-value: 0; }
34
+ @property --tw-exit-translate-y { syntax: "*"; inherits: false; initial-value: 0; }
35
+
36
+ @theme inline {
37
+ /* Fractions the numbered utilities resolve against (fade-in-0, zoom-in-95). */
38
+ --percentage-0: 0;
39
+ --percentage-5: .05;
40
+ --percentage-10: .1;
41
+ --percentage-15: .15;
42
+ --percentage-20: .2;
43
+ --percentage-25: .25;
44
+ --percentage-30: .3;
45
+ --percentage-35: .35;
46
+ --percentage-40: .4;
47
+ --percentage-45: .45;
48
+ --percentage-50: .5;
49
+ --percentage-55: .55;
50
+ --percentage-60: .6;
51
+ --percentage-65: .65;
52
+ --percentage-70: .7;
53
+ --percentage-75: .75;
54
+ --percentage-80: .8;
55
+ --percentage-85: .85;
56
+ --percentage-90: .9;
57
+ --percentage-95: .95;
58
+ --percentage-100: 1;
59
+ --percentage-translate-full: 1;
60
+
61
+ /* animate-in / animate-out */
62
+ --animate-in: enter var(--tw-animation-duration, var(--tw-duration, .15s)) var(--tw-ease, ease) var(--tw-animation-delay, 0s) var(--tw-animation-iteration-count, 1) var(--tw-animation-direction, normal) var(--tw-animation-fill-mode, none);
63
+ --animate-out: exit var(--tw-animation-duration, var(--tw-duration, .15s)) var(--tw-ease, ease) var(--tw-animation-delay, 0s) var(--tw-animation-iteration-count, 1) var(--tw-animation-direction, normal) var(--tw-animation-fill-mode, none);
64
+
65
+ @keyframes enter {
66
+ from {
67
+ opacity: var(--tw-enter-opacity, 1);
68
+ transform: translate3d(var(--tw-enter-translate-x, 0), var(--tw-enter-translate-y, 0), 0) scale3d(var(--tw-enter-scale, 1), var(--tw-enter-scale, 1), var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0));
69
+ filter: blur(var(--tw-enter-blur, 0));
70
+ }
71
+ }
72
+ @keyframes exit {
73
+ to {
74
+ opacity: var(--tw-exit-opacity, 1);
75
+ transform: translate3d(var(--tw-exit-translate-x, 0), var(--tw-exit-translate-y, 0), 0) scale3d(var(--tw-exit-scale, 1), var(--tw-exit-scale, 1), var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0));
76
+ filter: blur(var(--tw-exit-blur, 0));
77
+ }
78
+ }
79
+
80
+ /* Accordion content height; Radix supplies the variable. */
81
+ --animate-accordion-down: accordion-down var(--tw-animation-duration, var(--tw-duration, .2s)) var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) var(--tw-animation-iteration-count, 1) var(--tw-animation-direction, normal) var(--tw-animation-fill-mode, none);
82
+ --animate-accordion-up: accordion-up var(--tw-animation-duration, var(--tw-duration, .2s)) var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) var(--tw-animation-iteration-count, 1) var(--tw-animation-direction, normal) var(--tw-animation-fill-mode, none);
83
+
84
+ @keyframes accordion-down {
85
+ from { height: 0; }
86
+ to { height: var(--radix-accordion-content-height, auto); }
87
+ }
88
+ @keyframes accordion-up {
89
+ from { height: var(--radix-accordion-content-height, auto); }
90
+ to { height: 0; }
91
+ }
92
+ }
93
+
94
+ /* Enter */
95
+ @utility fade-in { --tw-enter-opacity: 0; }
96
+ @utility fade-in-* {
97
+ --tw-enter-opacity: calc(--value(number) / 100);
98
+ --tw-enter-opacity: --value(--percentage-*, [*]);
99
+ }
100
+ @utility zoom-in { --tw-enter-scale: 0; }
101
+ @utility zoom-in-* {
102
+ --tw-enter-scale: calc(--value(number) * 1%);
103
+ --tw-enter-scale: calc(--value(ratio));
104
+ --tw-enter-scale: --value(--percentage-*, [*]);
105
+ }
106
+ @utility blur-in { --tw-enter-blur: 20px; }
107
+ @utility blur-in-* {
108
+ --tw-enter-blur: calc(--value(number) * 1px);
109
+ --tw-enter-blur: --value(--blur-*, [*]);
110
+ }
111
+ @utility slide-in-from-top { --tw-enter-translate-y: -100%; }
112
+ @utility slide-in-from-top-* {
113
+ --tw-enter-translate-y: calc(--value(integer) * var(--spacing) * -1);
114
+ --tw-enter-translate-y: calc(--value(--percentage-*, --percentage-translate-*) * -100%);
115
+ --tw-enter-translate-y: calc(--value(ratio) * -100%);
116
+ --tw-enter-translate-y: calc(--value(--translate-*, [percentage], [length]) * -1);
117
+ }
118
+ @utility slide-in-from-bottom { --tw-enter-translate-y: 100%; }
119
+ @utility slide-in-from-bottom-* {
120
+ --tw-enter-translate-y: calc(--value(integer) * var(--spacing));
121
+ --tw-enter-translate-y: calc(--value(--percentage-*, --percentage-translate-*) * 100%);
122
+ --tw-enter-translate-y: calc(--value(ratio) * 100%);
123
+ --tw-enter-translate-y: --value(--translate-*, [percentage], [length]);
124
+ }
125
+ @utility slide-in-from-left { --tw-enter-translate-x: -100%; }
126
+ @utility slide-in-from-left-* {
127
+ --tw-enter-translate-x: calc(--value(integer) * var(--spacing) * -1);
128
+ --tw-enter-translate-x: calc(--value(--percentage-*, --percentage-translate-*) * -100%);
129
+ --tw-enter-translate-x: calc(--value(ratio) * -100%);
130
+ --tw-enter-translate-x: calc(--value(--translate-*, [percentage], [length]) * -1);
131
+ }
132
+ @utility slide-in-from-right { --tw-enter-translate-x: 100%; }
133
+ @utility slide-in-from-right-* {
134
+ --tw-enter-translate-x: calc(--value(integer) * var(--spacing));
135
+ --tw-enter-translate-x: calc(--value(--percentage-*, --percentage-translate-*) * 100%);
136
+ --tw-enter-translate-x: calc(--value(ratio) * 100%);
137
+ --tw-enter-translate-x: --value(--translate-*, [percentage], [length]);
138
+ }
139
+
140
+ /* Exit */
141
+ @utility fade-out { --tw-exit-opacity: 0; }
142
+ @utility fade-out-* {
143
+ --tw-exit-opacity: calc(--value(number) / 100);
144
+ --tw-exit-opacity: --value(--percentage-*, [*]);
145
+ }
146
+ @utility zoom-out { --tw-exit-scale: 0; }
147
+ @utility zoom-out-* {
148
+ --tw-exit-scale: calc(--value(number) * 1%);
149
+ --tw-exit-scale: calc(--value(ratio));
150
+ --tw-exit-scale: --value(--percentage-*, [*]);
151
+ }
152
+ @utility slide-out-to-top { --tw-exit-translate-y: -100%; }
153
+ @utility slide-out-to-bottom { --tw-exit-translate-y: 100%; }
154
+ @utility slide-out-to-left { --tw-exit-translate-x: -100%; }
155
+ @utility slide-out-to-right { --tw-exit-translate-x: 100%; }
package/css/roles.css CHANGED
@@ -61,6 +61,9 @@
61
61
  highlight primitive in light and follows it into dark by itself.
62
62
  ============================================================================= */
63
63
 
64
+ /* Motion vocabulary (animate-in, fade-in-0, accordion-down ...). */
65
+ @import "./motion.css";
66
+
64
67
  @custom-variant dark (&:is(.dark *));
65
68
 
66
69
  @theme inline {
@@ -1,21 +1,88 @@
1
+ type Node = {
2
+ type: string;
3
+ [key: string]: unknown;
4
+ };
5
+ type ReportDescriptor = {
6
+ node?: Node;
7
+ loc?: {
8
+ line: number;
9
+ column: number;
10
+ } | {
11
+ start: {
12
+ line: number;
13
+ column: number;
14
+ };
15
+ end: {
16
+ line: number;
17
+ column: number;
18
+ };
19
+ };
20
+ messageId: string;
21
+ data?: Record<string, string>;
22
+ };
23
+ type RuleContext = {
24
+ report(descriptor: ReportDescriptor): void;
25
+ sourceCode: {
26
+ text: string;
27
+ };
28
+ };
29
+ type RuleModule = {
30
+ meta: {
31
+ type: 'problem' | 'suggestion';
32
+ docs: {
33
+ description: string;
34
+ url?: string;
35
+ };
36
+ messages: Record<string, string>;
37
+ schema: [];
38
+ };
39
+ create(context: RuleContext): Record<string, (node: Node) => void>;
40
+ };
41
+ export declare const rules: Record<string, RuleModule>;
42
+ /**
43
+ * @deprecated Selector arrays for `no-restricted-syntax`, kept so configs
44
+ * written against 0.2 and 0.3 keep working. Use designSystemGuardrails()
45
+ * or the plugin's rules instead; they carry per-rule IDs and severities.
46
+ */
1
47
  type Restriction = {
2
48
  selector: string;
3
49
  message: string;
4
50
  };
5
51
  export declare const noArbitraryColour: Restriction[];
6
52
  export declare const noArbitraryTypeClamp: Restriction[];
53
+ export type RuleId = keyof typeof rules & string;
54
+ export type Severity = 'error' | 'warn' | 'off';
55
+ /** The plugin object, for configs that want to wire rules by hand. */
56
+ export declare const plugin: {
57
+ meta: {
58
+ name: string;
59
+ version: string;
60
+ };
61
+ rules: Record<string, RuleModule>;
62
+ };
63
+ /** Defaults: clean rules are errors; rules that need a cleanup pass first are warnings. */
64
+ export declare const defaultSeverity: Record<RuleId, Severity>;
7
65
  export interface GuardrailOptions {
8
66
  /** Glob(s) the rules apply to. Default: src/**\/*.{ts,tsx}. */
9
67
  files?: string[];
10
- /** Glob(s) exempt from the rules: renderers that genuinely cannot use CSS variables. */
68
+ /** Glob(s) exempt from every rule: renderers that genuinely cannot use CSS variables. */
11
69
  ignores?: string[];
70
+ /** Per-rule overrides of the default severities. */
71
+ severity?: Partial<Record<RuleId, Severity>>;
12
72
  }
13
- /** A flat-config block: spread it into your eslint.config array. */
14
- export declare function designSystemGuardrails({ files, ignores, }?: GuardrailOptions): {
73
+ /** A flat-config block: put it in your eslint.config array. */
74
+ export declare function designSystemGuardrails({ files, ignores, severity }?: GuardrailOptions): {
15
75
  files: string[];
16
76
  ignores: string[];
17
- rules: {
18
- 'no-restricted-syntax': (string | Restriction)[];
77
+ plugins: {
78
+ 'design-system': {
79
+ meta: {
80
+ name: string;
81
+ version: string;
82
+ };
83
+ rules: Record<string, RuleModule>;
84
+ };
19
85
  };
86
+ rules: Record<string, Severity>;
20
87
  };
21
88
  export {};