@ai-matrx/design-system 0.8.0 → 0.10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,222 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0 — 2026-09-07
4
+
5
+ **THE MOTION SWEEP. Ten more primitives were animating with a host plugin this
6
+ package never declared and cannot declare — and three of the four consumers
7
+ never loaded it.** 0.8.0 and 0.9.0 closed the coin flip for `Accordion` and
8
+ `Collapsible` one primitive at a time. This release swept the rest of the
9
+ package for the same shape and found it everywhere at once.
10
+
11
+ Every floating surface here — `Dialog`, `AlertDialog`, `Sheet`,
12
+ `CommandDialog`, `Popover`, `DropdownMenu`, `ContextMenu`, `HoverCard`,
13
+ `Select`, `Tooltip` — carried a correctly spelled
14
+ `data-[state=open]:animate-in data-[state=closed]:animate-out
15
+ data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95
16
+ data-[side=bottom]:slide-in-from-top-2 …`. None of those is a Tailwind
17
+ utility. They come from the `tailwindcss-animate` plugin (or its v4 successor
18
+ `tw-animate-css`), which a host loads from its own CSS entry — something a
19
+ node_modules package can neither ship nor require.
20
+
21
+ Measured on each consumer's BUILT stylesheet before any of this landed
22
+ (`grep -c 'animate-in\|slide-in-from'`):
23
+
24
+ | Consumer | plugin | rules in built CSS |
25
+ |---|---|---|
26
+ | matrx-frontend | `@import "tw-animate-css"` in `globals.css` | present — animated |
27
+ | aidream/dashboard | none | **0** |
28
+ | aidream/workflow-studio | none | **0** |
29
+ | matrx-extend | `tailwindcss-animate` in `package.json`, **never loaded** (a Tailwind v3 plugin; extend is on v4 with no `@plugin` directive) | **0** |
30
+
31
+ So every dialog, dropdown, popover, tooltip, select and sheet in the two
32
+ aidream admin apps and the Chrome extension appeared instantly, with no
33
+ animation and no error, for as long as those components existed. The one host
34
+ that worked, worked by accident of its own CSS entry. That is the doctrine
35
+ sentence from `FEATURE.md` proven at scale: a primitive that leaves motion to
36
+ the host has not shipped motion, it has shipped a coin flip.
37
+
38
+ **The package now owns all of it.** `styles.css` gains a documented motion
39
+ layer: package-declared `@keyframes matrx-*`, applied through `matrx-`-prefixed
40
+ classes the components put on the DOM themselves, unlayered like the accordion
41
+ and collapsible rules so every consumer gets identical motion even where the
42
+ host plugin is still loaded.
43
+
44
+ | Motion | Class | Primitives |
45
+ |---|---|---|
46
+ | scrim fade | `matrx-motion-overlay` | Dialog, AlertDialog, Sheet, CommandDialog |
47
+ | centred card | `matrx-motion-dialog` | Dialog (desktop), AlertDialog, CommandDialog (desktop) |
48
+ | bottom sheet | `matrx-motion-bottom-sheet` | Dialog / CommandDialog below the mobile breakpoint |
49
+ | edge sheet | `matrx-motion-sheet-{top,bottom,left,right,center}` | Sheet, per `side` |
50
+ | popper | `matrx-motion-popper` | Popover, DropdownMenu, ContextMenu, HoverCard, Select, Tooltip |
51
+ | pulse | `matrx-pulse` | Skeleton |
52
+ | spin | `matrx-spin` | every inline busy indicator in the package |
53
+
54
+ Three things changed for the better on the way through:
55
+
56
+ - **The popper grows out of its trigger, not out of itself.** The rule reads
57
+ the anchor-facing origin Radix publishes — as a fallback CHAIN over
58
+ `--radix-popper-transform-origin` and each primitive's own alias
59
+ (`--radix-select-content-transform-origin`,
60
+ `--radix-tooltip-content-transform-origin`, …), because reusing one
61
+ primitive's measured variable on another is precisely the silent death
62
+ census row 19f recorded. The `zoom-in-95` original always scaled from the
63
+ element's centre.
64
+ - **Timing is a knob.** Every duration and easing resolves from a
65
+ `--matrx-motion-*` token in `tokens.css` (fast 150ms, base 200ms, slow
66
+ 300ms, sheet 500ms, plus the two easings and the popper travel distance). An
67
+ org that wants calmer motion sets seven values instead of overriding twenty
68
+ rules. `prefers-reduced-motion` is NOT one of them — it is honoured in the
69
+ stylesheet regardless.
70
+ - **`animate-pulse` / `animate-spin` came home too.** Those two ARE core
71
+ Tailwind, so they survived in a Tailwind host — but a Skeleton that does not
72
+ pulse is an empty grey box and a spinner that does not spin says the work
73
+ stopped. Both are four lines; the package owns them. Under reduced motion
74
+ they SLOW rather than stop, because stopping them removes information rather
75
+ than removing motion.
76
+
77
+ **Where the line is, stated in the sheet:** this layer owns keyframe
78
+ animation — motion that either happens or silently does not. It deliberately
79
+ does not own Tailwind's `transition-*` / `duration-*` / `ease-*` utilities the
80
+ primitives also carry (a hover colour, the progress bar's width, a close
81
+ button's opacity). Those are core Tailwind, present in every Tailwind host,
82
+ and their absence degrades to an instant-but-correct change rather than to a
83
+ surface that lies about its state. Re-implementing them here would mean
84
+ shipping a copy of Tailwind.
85
+
86
+ **Opt-out:** `animated={false}` on `DialogContent`, `AlertDialogContent`,
87
+ `SheetContent`, `PopoverContent`, `HoverCardContent`, `TooltipContent`,
88
+ `SelectContent` and `Skeleton` returns the surface to instant. It is not the
89
+ reduced-motion switch; the stylesheet already handles that.
90
+
91
+ Guards, all proven failing-then-passing by re-introducing a
92
+ `data-[state=open]:animate-in` on `PopoverContent`:
93
+
94
+ - `motion.ts` — ONE exported constant per motion shape, so the eleventh
95
+ primitive cannot invent an eleventh spelling. Exported publicly
96
+ (`ALL_MOTION_CLASSES`, `FORBIDDEN_HOST_MOTION_UTILITIES`) so a consumer can
97
+ audit its own CSS against the same list.
98
+ - `motion.test.tsx` — one case per primitive, asserting BOTH halves: the
99
+ rendered element carries the class, AND `styles.css` defines a rule for it
100
+ whose keyframe that same sheet declares. A class-name assertion alone would
101
+ have passed throughout the defect. Plus: every Sheet side wears its own edge
102
+ class and no other; the mobile bottom sheet does not wear the card's
103
+ keyframe; every opt-out actually opts out and leaves geometry alone.
104
+ - `styles.test.ts` — no component may reference a host-plugin utility again;
105
+ every keyframe referenced is declared; every keyframe is `matrx-` prefixed;
106
+ every overlay duration comes from a token; every motion class appears in a
107
+ `prefers-reduced-motion` block; the popper origin chain is complete; and the
108
+ class list is exhaustive in BOTH directions.
109
+ - The packed-tarball canary asserts thirteen rules, six keyframes, both origin
110
+ variables, the timing tokens, and — the assertion that would have caught
111
+ this whole release from outside — that **every keyframe the packed sheet
112
+ references is declared in that same packed sheet**, read from the artifact a
113
+ consumer installs with no host in the room.
114
+
115
+ ### Consumer action (C28)
116
+
117
+ - 🔶 **This is a VISIBLE change in three of the four consumers, on purpose.**
118
+ `aidream/dashboard`, `aidream/workflow-studio` and `matrx-extend` get open /
119
+ close animation on every dialog, dropdown, popover, tooltip, select and
120
+ sheet **for the first time**. They need no code change and no CSS: they
121
+ already import `@ai-matrx/design-system/styles.css`, so bumping the version
122
+ is the whole adoption. If a specific surface must not move, pass
123
+ `animated={false}` — do not re-add a host plugin.
124
+ - **matrx-frontend: `tw-animate-css` is no longer required by anything this
125
+ package renders.** Every `animate-in` / `slide-in-from-*` this package
126
+ emitted is gone. Audit your own components for remaining uses before
127
+ deleting the `@import "tw-animate-css"` from `app/globals.css` and the
128
+ dependency from `package.json`; the package no longer needs either.
129
+ - **matrx-extend: delete `tailwindcss-animate` from `package.json`.** It has
130
+ never been loaded — it is a Tailwind v3 plugin in a v4 app with no `@plugin`
131
+ directive, and the built stylesheet proves it contributed zero rules. It is
132
+ dead weight that also makes the app look like it has motion support.
133
+ - **Retiming is a token change now.** Do not override the motion rules; set
134
+ `--matrx-motion-duration-*` / `--matrx-motion-ease-*` /
135
+ `--matrx-motion-popper-travel`.
136
+ - New type exports: `PopoverContentProps`, `SelectContentProps`,
137
+ `SkeletonProps`. New value exports: `ALL_MOTION_CLASSES`,
138
+ `FORBIDDEN_HOST_MOTION_UTILITIES`, `MOTION_OVERLAY`, `MOTION_DIALOG`,
139
+ `MOTION_BOTTOM_SHEET`, `MOTION_POPPER`, `MOTION_SHEET`, `MOTION_PULSE`,
140
+ `MOTION_SPIN`.
141
+ - **Not fixed here, and named so it is not lost:** `@ai-matrx/kit`'s
142
+ `src/confirm/alert-dialog.tsx` carries the identical defect (`animate-in`,
143
+ `zoom-in-95`, `slide-in-from-top-[48%]`) and ships NO stylesheet at all, so
144
+ its confirm dialog is unanimated in the same three consumers. It cannot
145
+ import these rules: `@ai-matrx/design-system` depends on `@ai-matrx/kit`,
146
+ so the fix is a package-graph decision (move the confirm dialog, or invert
147
+ the dependency), not a class rename. `@ai-matrx/tap-target` has one
148
+ `animate-spin` in `src/tap-buttons.tsx` — core Tailwind, so it survives in
149
+ every current consumer, but it is the same class of host reliance and rides
150
+ the next tap-target release.
151
+
152
+ ## 0.9.0 — 2026-09-07
153
+
154
+ **`Collapsible` gets the open/close transition the package never shipped
155
+ (census row 19f, the tail 0.8.0 left open).** Until now `collapsible.tsx` was a
156
+ bare re-export of Radix, and Radix ships no motion — it only publishes the
157
+ measured height as `--radix-collapsible-content-height` and leaves the
158
+ keyframes to you. So every consumer invented the transition itself, and the
159
+ inventions are the reason this is a package change and not a host one:
160
+
161
+ - **Two matrx-frontend collapsibles had never animated, silently.**
162
+ `components/matrx/matrx-collapsible/matrx-collapsible.tsx` and
163
+ `enhanced-collapsible.tsx` carried `animate-accordion-down`/`-up` on a
164
+ COLLAPSIBLE panel. Those keyframes interpolate to
165
+ `--radix-ACCORDION-content-height`, which Radix never sets on a collapsible,
166
+ so the target height was always an undefined variable. No animation, no
167
+ error, for as long as the components existed. (Found while deleting the host
168
+ keyframes in 0.8.0; the dead classes were removed then, with the finding
169
+ recorded in place.)
170
+ - **Six more animated correctly — in exactly one app.** `ChatCollapsible`,
171
+ `ChatCollapsibleWrapper`, `TranscriptBlock`, `StructuredPlanBlock`,
172
+ `AdvancedCollapsible` and `armani-collapsible` used
173
+ `animate-slide-down`/`-up`, whose keyframes lived only in matrx-frontend's
174
+ `globals.css`. Same C26 silent host contract as the accordion's, wearing a
175
+ working coat: any other consumer of this package got nothing.
176
+
177
+ `CollapsibleContent` now applies `.matrx-collapsible-content overflow-hidden`
178
+ by default. The rule and its keyframes ship in `styles.css`, keyed off the
179
+ COLLAPSIBLE height variable, at the same 200ms ease-out as
180
+ `.matrx-accordion-content` so the two primitives agree on the same screen, and
181
+ `prefers-reduced-motion: reduce` drops the animation while keeping the
182
+ clipping. The panel also carries `data-slot="collapsible-content"`.
183
+
184
+ Unlike `AccordionContent`, this does NOT wrap children in a padding div —
185
+ collapsibles in the fleet pad their own bodies, and inserting a box would move
186
+ every one of them.
187
+
188
+ **Opt-out:** `<CollapsibleContent animated={false}>` returns bare Radix — no
189
+ class, no clipping, instant open — for the rare panel with a child that must
190
+ overflow its bounds (a non-portalled popover, a sticky header). It is not the
191
+ reduced-motion switch; the stylesheet already handles that.
192
+
193
+ Guards: `collapsible.test.tsx` (transition class present and not either host
194
+ utility; the keyframes read the collapsible variable and not the accordion's;
195
+ both reduced-motion states silenced; the opt-out removing class AND clipping),
196
+ `matrx-accordion-content` + `matrx-collapsible-content` added to
197
+ `styles.test.ts`'s required-rule list, and the packed-tarball canary now
198
+ asserts both rules, the collapsible height variable, and a reduced-motion
199
+ block actually ship in `dist/styles.css`. Proven failing-then-passing by
200
+ re-pointing the keyframes at the accordion variable.
201
+
202
+ ### Consumer action (C28)
203
+
204
+ - 🔶 **This is a VISIBLE change, on purpose.** Every `CollapsibleContent` in
205
+ every consumer now animates open and closed at 200ms where most of them
206
+ previously snapped. If a panel must not move or must not clip, pass
207
+ `animated={false}` — do not re-add host keyframes.
208
+ - **Delete host collapsible keyframes and the per-site classes that used
209
+ them.** In matrx-frontend that is `slide-down` / `slide-up` in
210
+ `app/globals.css`, the `--animate-slide-down` / `--animate-slide-up` theme
211
+ entries, and the `data-[state=open]:animate-slide-down
212
+ data-[state=closed]:animate-slide-up` pairs on the six components listed
213
+ above (all done in this same session, per THE SAME-SESSION LAW).
214
+ - **The two components named at the top of this entry —
215
+ `matrx-collapsible.tsx` and `enhanced-collapsible.tsx` — animate for the
216
+ first time ever** with this release. They need no code change; they get the
217
+ transition from the package.
218
+ - New type export: `CollapsibleContentProps`.
219
+
3
220
  ## 0.8.0 — 2026-09-07
4
221
 
5
222
  **Accordion — the last stand-in in the fleet (census row 19c).** Growth waves 1
package/README.md CHANGED
@@ -12,6 +12,22 @@ The small, semantic UI foundation shared by AI Matrx React applications — prim
12
12
 
13
13
  Where hosts had forked a primitive over DENSITY, density is a prop, not a fork: `Card size="sm|md|lg"`, `Table size="sm|md"`, `Accordion size="sm|md"`, `Switch`/`Avatar`/`Checkbox`/`Slider`/`RadioGroup`/`Toggle` `size`, `ResizableHandle size="xs".."4xl"`. It is declared once on the root and every item reads it from context, so a card cannot mix paddings and a radio group cannot mix control sizes.
14
14
 
15
+ **Motion ships with the component — ALL of it.** Every animated surface here
16
+ (`Dialog`, `AlertDialog`, `Sheet`, `CommandDialog`, `Popover`, `DropdownMenu`,
17
+ `ContextMenu`, `HoverCard`, `Select`, `Tooltip`, `Accordion`, `Collapsible`,
18
+ `Skeleton`) animates out of the box from keyframes in `styles.css`. **You never
19
+ define keyframes, add an `animate-*` utility, or install `tailwindcss-animate`
20
+ / `tw-animate-css` for anything this package renders** — that plugin is loaded
21
+ from a host's CSS entry, which is why three of the four apps that already used
22
+ this package had zero animation on every overlay and no error to show for it
23
+ (0.10.0). `prefers-reduced-motion` is honoured in the sheet.
24
+
25
+ Retiming is a token change, not an override — set `--matrx-motion-duration-fast`
26
+ / `-base` / `-slow` / `-sheet`, `--matrx-motion-ease-out` / `-in`, or
27
+ `--matrx-motion-popper-travel`. For one surface that must appear instantly,
28
+ pass `animated={false}`; `CollapsibleContent`'s opt-out additionally stops it
29
+ clipping its body, for the rare panel with a child that must overflow.
30
+
15
31
  **Mobile is the Drawer's job.** `Drawer` owns the iOS rules every host was re-deriving — `dvh` never `vh`, `pb-safe`/`pt-safe` safe-area padding, a 44px grab-handle target, `DrawerBody` as the `flex-1 min-h-0` scroll shard — and `direction` makes a top/side drawer a prop rather than a fork. Every touch control (`Slider`, `RadioGroup`, `ResizableHandle`) carries an invisible 44px target on coarse pointers, with desktop density untouched.
16
32
 
17
33
  ```bash