@ai-matrx/design-system 0.9.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,154 @@
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
+
3
152
  ## 0.9.0 — 2026-09-07
4
153
 
5
154
  **`Collapsible` gets the open/close transition the package never shipped
package/README.md CHANGED
@@ -12,7 +12,21 @@ 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.** `Accordion` and `Collapsible` animate open and closed out of the box (200ms, `prefers-reduced-motion` honoured) from rules in `styles.css` — you never define keyframes or add an `animate-*` utility for them. `CollapsibleContent` also clips its body while it moves; pass `animated={false}` for the rare panel with a child that must overflow its bounds.
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.
16
30
 
17
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.
18
32