@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.
- package/DESIGN.md +11 -1
- package/README.md +78 -13
- package/css/motion.css +155 -0
- package/css/roles.css +3 -0
- package/dist/guardrails/eslint.d.ts +72 -5
- package/dist/guardrails/eslint.js +197 -29
- package/dist/guardrails/init.d.ts +2 -0
- package/dist/guardrails/init.js +65 -0
- package/dist/guardrails/intake.d.ts +2 -0
- package/dist/guardrails/intake.js +131 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/ui/offer-card.d.ts +52 -0
- package/dist/src/ui/offer-card.js +7 -0
- package/package.json +8 -3
- package/skills/product-design/SKILL.md +142 -0
- package/skills/product-design/coverage-gaps.md +41 -0
- package/skills/product-design/exemplars/calm-the-offering-cards.md +33 -0
- package/skills/product-design/exemplars/clamp-drift-to-named-roles.md +37 -0
- package/skills/product-design/exemplars/concentric-radii-and-button-optics.md +36 -0
- package/skills/product-design/exemplars/dialog-close-focus-visible.md +36 -0
- package/skills/product-design/exemplars/hero-glow-seam.md +34 -0
- package/skills/product-design/intake/2026-09-07.md +413 -0
- package/skills/product-design/references/components.md +43 -0
- package/skills/product-design/references/copy.md +25 -0
- package/skills/product-design/references/intake.md +66 -0
- package/skills/product-design/references/motion.md +22 -0
- package/skills/product-design/references/rules.md +319 -0
- package/skills/product-design/references/surfaces.md +50 -0
- package/skills/product-design/references/tokens.md +54 -0
- package/skills/product-design/references/type-and-space.md +42 -0
- package/skills/product-design/references/verification.md +35 -0
- package/template/CLAUDE.md +47 -0
- package/template/README.md +16 -0
- package/template/eslint.config.mjs +20 -0
- package/template/gitignore +44 -0
- package/template/next.config.ts +7 -0
- package/template/package.json +40 -0
- package/template/pnpm-workspace.yaml +14 -0
- package/template/postcss.config.mjs +7 -0
- package/template/src/app/globals.css +66 -0
- package/template/src/app/layout.tsx +55 -0
- package/template/src/app/page.tsx +59 -0
- package/template/src/components/ThemeSync.tsx +21 -0
- package/template/src/system/brands/starter.css +143 -0
- 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
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
|
20
|
-
|
|
|
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.
|
|
26
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
18
|
-
'
|
|
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 {};
|