@uni-design-system/uni-angular 8.3.1 → 9.0.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,529 @@
1
1
  # @uni-design-system/uni-angular
2
2
 
3
+ ## 9.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`c0c6056`](https://github.com/uni-design-system/uni/commit/c0c6056c61e994a45af9c379f3c99f55eebcb79a) Thanks [@gaenglish](https://github.com/gaenglish)! - Box learns `flex` / `shrink` / `basis` and `marginInline`.
8
+
9
+ **`flex`.** `grow` emits `flex-grow` alone, which leaves `flex-basis: auto` — so
10
+ it cannot express `flex: 1`, and any layout wanting siblings to share space
11
+ evenly regardless of content width had to stay in CSS. `[flex]="1"` now emits
12
+ the shorthand; `shrink` and `basis` cover the rest. `grow` is unchanged, so no
13
+ existing layout moves.
14
+
15
+ These three go through a new `ThemeService.styleIfSet()`, which treats only
16
+ `undefined` as unset — the shared `style()` helper drops falsy values, which
17
+ would have silently swallowed `[shrink]="0"`, the single most useful value.
18
+
19
+ **`marginInline`.** `margin: 0 auto` on a max-width container had no Box
20
+ equivalent, so page shells kept an inline style for it. `marginInline="auto"`
21
+ centers such a container, and a spacing token works too.
22
+
23
+ ```html
24
+ <main box-layout maxWidth="1200px" marginInline="auto" padding="lg">…</main>
25
+ ```
26
+
27
+ Only the inline axis is exposed. Block margins collapse and fight `gap`, which
28
+ is why the primitives carry no margin otherwise — inline margins do neither, so
29
+ this is a deliberate line rather than a crack in the token-only surface.
30
+
31
+ - [`8250162`](https://github.com/uni-design-system/uni/commit/8250162c35e418f976080904df4c20783feeb6e2) Thanks [@gaenglish](https://github.com/gaenglish)! - Every deprecated API is removed. The library now carries no `@deprecated`
32
+ symbols at all.
33
+
34
+ **Per-component duration options → the `motion` scale.** Six components carried
35
+ their own duration knob that predated the motion scale and _won over_ it:
36
+ `expand.transitionSpeed`, `callout.transitionMs`, `radio.transitionSpeed`,
37
+ `menuItem.transitionSpeed`, `alert.transitionSpeed` and
38
+ `snackbar.transitionDelay`. All are gone, along with the precedence branch each
39
+ one required — timing now comes from the token, full stop.
40
+
41
+ Retime the token instead; one edit covers every component pointing at it. To
42
+ retime a single component, define a token of your own and point that
43
+ component's `motion` option at it:
44
+
45
+ ```ts
46
+ createTheme({
47
+ …,
48
+ motion: { productive: { duration: 110, easing: 'ease' } },
49
+ components: { menuItem: { options: { motion: 'productive' } } },
50
+ });
51
+ ```
52
+
53
+ A `duration: 0` token is how a theme opts out of motion — that is what
54
+ `transitionSpeed: 0` used to mean. Both showcase themes are migrated this way
55
+ (Carbon to a 110ms `productive` token, Wellsourced to an `instant` one).
56
+
57
+ **Options that never did anything.** `card.transitionSpeed` and
58
+ `inputBox.transitionSpeed` were read by nothing and never had been. Delete them
59
+ from your theme; nothing replaces them.
60
+
61
+ **Renames and obsolete APIs**
62
+ - `inputBox.typeFace` → `typeface` (the casing every other component uses).
63
+ - `uni-tooltip`'s `appendToBody` input — inert since the tooltip moved to the
64
+ native top layer, which escapes any overflow context by itself.
65
+ - Box's `elevation` input → `shadow`, in both the Angular and React packages.
66
+ It was a second name for the same thing.
67
+ - The Angular `icons` re-export → import `BaseIcons` from
68
+ `@uni-design-system/uni-core`. The default set ships with every theme.
69
+
70
+ **The HSL color legacy is gone from uni-core.** `uniColor`, `randomRangeValue`,
71
+ `CategorySaturation` and `CategoryLightness` are removed, superseded by the
72
+ deterministic OKLCH engine (`generateThemes` / `generatePalette`) — same input,
73
+ same theme, WCAG-checked. `RoleHues` and the `UniColor` type go with them: they
74
+ were reachable only through `uniColor`, and `RoleHues` had gone stale enough to
75
+ hold saturation values in a table of hues.
76
+
77
+ **Deferred output renames.** Three outputs were held back because renaming is
78
+ breaking; this is that release. Each also drops an eslint escape it needed for
79
+ shadowing a native event name or using an `on` prefix.
80
+
81
+ | Component | Before | After |
82
+ | -------------------- | ----------------- | ---------------- |
83
+ | `uni-debounce-input` | `(change)` | `(valueChange)` |
84
+ | `uni-search-input` | `(change)` | `(searchChange)` |
85
+ | `uni-search-input` | `(search)` | `(searchSubmit)` |
86
+ | `dragAndDrop` | `(onFileDropped)` | `(fileDropped)` |
87
+
88
+ **`uni-dropdown`'s `color` input → `containerColor`,** completing the rule the
89
+ layout directives set: every container-pair input in the library is now
90
+ `containerColor`, and plain `color` always means the CSS property.
91
+
92
+ **`ThemeService.getSpacing('none')` now returns `0`, not the string `'none'`.**
93
+ `'none'` is not a valid length, so it was silently dropped wherever it landed —
94
+ `uni-menu` carried a comment working around exactly that, which is now deleted.
95
+
96
+ - [`c0c6056`](https://github.com/uni-design-system/uni/commit/c0c6056c61e994a45af9c379f3c99f55eebcb79a) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-input` gets a `type`, and the input chrome stops needing a wrapper div.
97
+
98
+ **Types.** `uni-input` was text-only with no `type` at all, so every email, URL,
99
+ number and phone field had to fall back to `uni-input-box` plus a hand-written
100
+ native `<input>`, and `type="password"` could not go through it. It now takes
101
+ the text-like types: `text` (default), `email`, `password`, `search`, `tel`,
102
+ `url`, `number`.
103
+
104
+ Non-text types (`checkbox`, `radio`, `file`, `range`, `color`) stay out — they
105
+ break both the input chrome and the `FormValueControl<string>` value contract —
106
+ as do `date` / `time` / `datetime-local`, which have dedicated components.
107
+
108
+ **Native passthroughs.** `autocomplete`, `inputMode`, `list` (a `<datalist>` id),
109
+ `step` and `spellcheck` are plain passthroughs; an unset one emits no attribute.
110
+ `readonly`, `name`, `min`, `max`, `minLength`, `maxLength` and `pattern` are
111
+ Signal Forms' own optional control inputs, so the `[field]` directive syncs them
112
+ from your validators exactly as it already syncs `required` — and they are
113
+ reflected onto the native element so the browser contributes too. Signal Forms
114
+ treats multiple `pattern`s as all-must-match, which the native attribute cannot
115
+ express, so it is reflected only when there is exactly one.
116
+
117
+ `uni-textarea` gains `readonly`, `name`, `minLength`, `maxLength`, `autocomplete`
118
+ and `spellcheck`; `uni-debounce-input` gains `type`, `autocomplete` and
119
+ `inputMode`.
120
+
121
+ **Sizing.** `uni-input-box`'s host is `display: contents`, so a width or layout
122
+ attribute set on the element itself was silently dropped and every call site
123
+ needed a wrapper `<div>`. It now takes `width`, `fullWidth` and `grow` (joining
124
+ `minWidth`), which reach the real box inside; `uni-input`, `uni-textarea` and
125
+ `uni-select` forward all four. The `display: contents` behavior is now documented
126
+ too — it stays surprising even once sizing works.
127
+
128
+ Both `uni-input` and `uni-debounce-input` now accept the adornment slots as
129
+ either element or attribute selectors (`<span pre-input>` as well as
130
+ `<pre-input>`), which had drifted apart between them.
131
+
132
+ - [`c0c6056`](https://github.com/uni-design-system/uni/commit/c0c6056c61e994a45af9c379f3c99f55eebcb79a) Thanks [@gaenglish](https://github.com/gaenglish)! - The layout primitives and `uni-text` are now **directives**, so they compose.
133
+
134
+ They were components with attribute-only selectors, which meant any two of them
135
+ on one element threw NG0300 ("multiple components match"). `<div row-layout
136
+ uni-text="title-small">` — the most natural thing to write — threw in dev only,
137
+ so it reached production as a silent style mismatch. `<uni-card box-layout>`
138
+ was blocked for the same reason, and `box.component.ts` documented the
139
+ wrap-it-in-a-div workaround as permanent.
140
+
141
+ All eight are pure host-styling wrappers (an `<ng-content>`-only template plus a
142
+ `[class]` host binding), so as directives they render identically and now stack
143
+ freely — with each other, with `uni-text`, and with a component's own host
144
+ element. Angular reconciles the host `class` bindings additively, so each
145
+ contributor's styles survive rather than one silently winning; there is a spec
146
+ pinning that.
147
+
148
+ ```html
149
+ <div row-layout uni-text="title-small" padding="md">Heading</div>
150
+ <uni-card box-layout padding="lg">…</uni-card>
151
+ ```
152
+
153
+ **BREAKING: class renames.** `UniBoxComponent` → `UniBoxDirective`, and likewise
154
+ for Row, Stack, Center, Wrap, Grid, GridArea and Text. No compatibility aliases:
155
+ a major is where the break belongs, and an alias shipped on day one of a major
156
+ tends to survive to the next one. Rename the symbols in your `imports: []`.
157
+
158
+ **BREAKING: the layout directives' `color` is now `containerColor`.**
159
+
160
+ Composing the two directives exposed a name collision. `color` was an input on
161
+ both — a _container pair_ on the layout primitives, the CSS `color` property on
162
+ `uni-text` — so on a shared element one binding fed both: the box painted the
163
+ surface, the text took the same token, and the text rendered as ink on identical
164
+ ink. A deprecated alias would have kept that path alive, so there isn't one.
165
+
166
+ `color` belongs to `uni-text`, which maps it straight to the CSS property. The
167
+ container pair is an invented concept and now says so:
168
+
169
+ ```html
170
+ <!-- before -->
171
+ <div box-layout color="surface">
172
+ <!-- after -->
173
+ <div box-layout containerColor="surface"></div>
174
+ </div>
175
+ ```
176
+
177
+ This applies to all seven layout directives (Box and its subclasses) and to
178
+ `uni-scroll-area`, which is attribute-selected too and carried the identical
179
+ hazard. `backgroundColor`, which sets only the background and no paired
180
+ on-color, is unchanged, as is `color` on `uni-icon` / `uni-skeleton` /
181
+ `uni-badge` — there it already means a foreground color, the same sense as
182
+ `uni-text`'s.
183
+
184
+ Codemod: rename `color` → `containerColor` on any element carrying a `*-layout`
185
+ or `scroll-area` attribute; leave `color` alone everywhere else.
186
+
187
+ The rename fixed the input collision; a second one sat underneath it in CSS.
188
+ `containerColor` emits a background **and** its paired on-color, so both
189
+ directives write `color` to the element — at equal specificity, which left the
190
+ cascade to Emotion's insertion order (text won on `row-layout`, lost on
191
+ `scroll-area`). An explicit `uni-text` `color` is now emitted at doubled
192
+ specificity, so it deterministically wins; with no explicit color, the
193
+ container's on-color still shows through as intended.
194
+
195
+ `uni-dropdown` still names its container pair `color`. It is an element selector
196
+ (`<uni-dropdown>`), so the collision needs someone to put `uni-text` on a
197
+ component host — possible, but not the natural path the layout attributes are.
198
+
199
+ **New: `UNI_LAYOUT` and `UNI_FORMS`.** These are attribute selectors, so an
200
+ element carrying one whose directive was never imported compiles cleanly and
201
+ silently does nothing. Spreading a family is the cheapest guard:
202
+
203
+ ```ts
204
+ imports: [...UNI_LAYOUT];
205
+ ```
206
+
207
+ - [`c0c6056`](https://github.com/uni-design-system/uni/commit/c0c6056c61e994a45af9c379f3c99f55eebcb79a) Thanks [@gaenglish](https://github.com/gaenglish)! - The spacing scale is open, and `createTheme` finally accepts one.
208
+
209
+ The scale was a closed seven-name union on a doubling curve (2/4/8/16/32/64px).
210
+ Real layouts are rarely built exclusively on one — the gaps between 8 and 16,
211
+ and 16 and 32, are where a lot of real spacing lives — and there was no way to
212
+ add a step, because **`ThemeConfig` had no `spacing` field at all**:
213
+ `createTheme` hardcoded the base scale. The Wellsourced showcase theme had to
214
+ bolt its scale on after the fact with a post-hoc spread.
215
+
216
+ Three changes, which only work together:
217
+ - `NullableSize` gains a `(string & {})` arm, so any name the theme defines is a
218
+ valid `padding` / `gap` / `marginInline` value while the seven named steps keep
219
+ their autocomplete. `Size` itself stays closed — it also types _component_
220
+ sizes, where an arbitrary name has nothing to resolve against.
221
+ - `Spacing` is spelled out as named-optional-keys plus an index signature
222
+ (mirroring `Typography`), rather than a `Partial<Record<…>>` that would
223
+ collapse to a plain string record and lose the named steps.
224
+ - `createTheme({ spacing })` merges over the base scale.
225
+
226
+ ```ts
227
+ createTheme({ id, name, colors, spacing: { tight: '6px', snug: '10px' } });
228
+ ```
229
+
230
+ ```html
231
+ <div stack-layout padding="tight" gap="snug">…</div>
232
+ ```
233
+
234
+ Because the scale is open, a mistyped token can no longer be a compile error. It
235
+ is dropped — an `undefined` CSS value simply does not render — and
236
+ `ThemeService` now warns once per unknown token in development, naming the
237
+ tokens the active theme does define. Scaffolded `uni-theme.ts` files carry a
238
+ `spacing` block so the static theme file stays the editable source of truth.
239
+
240
+ **Behavior change:** `xxl` was in the `Size` union but defined by no base theme,
241
+ so `padding="xxl"` type-checked and rendered nothing. It is now `128px`,
242
+ completing the doubling — any element relying on the silent drop will start
243
+ showing spacing.
244
+
245
+ ### Patch Changes
246
+
247
+ - Updated dependencies [[`8250162`](https://github.com/uni-design-system/uni/commit/8250162c35e418f976080904df4c20783feeb6e2), [`c0c6056`](https://github.com/uni-design-system/uni/commit/c0c6056c61e994a45af9c379f3c99f55eebcb79a)]:
248
+ - @uni-design-system/uni-core@9.0.0
249
+
250
+ ## 8.4.0
251
+
252
+ ### Minor Changes
253
+
254
+ - [`1aca747`](https://github.com/uni-design-system/uni/commit/1aca747c7bdff6cf72a9fa349e77a6e7985b97c7) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-callout` and `uni-expand` now read their timing from the theme's `motion`
255
+ scale, so every animated surface in the library is retimed from one place.
256
+ They were the last two carrying their own motion options — in different units,
257
+ under different names (`transitionMs` in milliseconds, `transitionSpeed` in
258
+ seconds).
259
+
260
+ A third token joins `popup` and `panel`:
261
+
262
+ | Token | Default | Used by |
263
+ | -------- | ----------------- | --------------------- |
264
+ | `reveal` | 350ms ease-in-out | expand, expand-toggle |
265
+
266
+ `reveal` is a _base_ speed, not a final duration: `uni-expand` still scales it
267
+ by content height (√-of-height, clamped) so short regions stay snappy and tall
268
+ ones aren't rushed, and its easing now drives the reveal curve, which was
269
+ hardcoded. `uni-expand-toggle` resolves the token the same way, so the chevron
270
+ and the region cannot drift apart. `uni-callout` maps onto `panel`, whose
271
+ 250ms matches what it already used.
272
+
273
+ **Not breaking.** `transitionMs` and `transitionSpeed` are deprecated but
274
+ still honoured, and deliberately outrank `motion` — a theme that set either
275
+ keeps precisely its current timing rather than being retimed underneath it.
276
+ They are removed next major. Per-instance inputs, like `uni-expand`'s
277
+ `transitionSpeed`, still outrank everything.
278
+
279
+ Nothing moves differently by default: callout renders 0.25s ease and expand
280
+ 0.35s ease-in-out exactly as before, verified against the rendered styles.
281
+
282
+ - [`e459f91`](https://github.com/uni-design-system/uni/commit/e459f916e6f097041d04c8209e896b8fd7d11362) Thanks [@gaenglish](https://github.com/gaenglish)! - New `createAnnouncer()` in the CDK's a11y helpers: the polite live region a
283
+ form control uses for its running commentary — commits, clears, refused
284
+ entries, result counts — changes a sighted user sees but that are otherwise
285
+ silent to a screen reader.
286
+
287
+ `uni-combobox`, `uni-tag-input`, `uni-time-input`, `uni-date-input`,
288
+ `uni-calendar` and `uni-tour` now share it instead of carrying byte-identical
289
+ copies. The helper holds no DOM and no styling: the `role="status"` element
290
+ stays in each component's own template, where its placement and
291
+ visually-hidden class already belong.
292
+
293
+ This fixes a real bug in `uni-tour`, which had a plain signal rather than a
294
+ copy of the shared idiom. Assistive tech reads a live region when its content
295
+ _changes_, so writing the identical string is a no-op — its "Next available"
296
+ gate message was announced on the first step that used it and silently dropped
297
+ on every later one. `createAnnouncer` breaks the equality with a trailing
298
+ space, inaudible to a screen reader, alternating between the two forms so
299
+ nothing accumulates.
300
+
301
+ Consuming the helper directly:
302
+
303
+ ```ts
304
+ protected readonly announcer = createAnnouncer();
305
+ // this.announcer.announce('Alabama selected.');
306
+ ```
307
+
308
+ ```html
309
+ <span role="status" aria-live="polite" [class]="srOnly"> {{ announcer.message() }} </span>
310
+ ```
311
+
312
+ The region must already be in the DOM when the component renders — one added
313
+ at the moment it gains text is not reliably announced.
314
+
315
+ - [`6e2cfb1`](https://github.com/uni-design-system/uni/commit/6e2cfb1f9bfd82c7165e2a853b1729abdda5194a) Thanks [@gaenglish](https://github.com/gaenglish)! - Home and End now move the caret in the combobox-style controls —
316
+ `uni-combobox`, `uni-search-input`, `uni-tag-input` — instead of jumping to
317
+ the ends of the suggestion list. APG reserves those keys for text editing in
318
+ an editable combobox, and a field that claims them makes its own text
319
+ un-navigable exactly when you are most likely to be editing it: with the list
320
+ open. `uni-time-input` already behaved this way.
321
+
322
+ Nothing is lost. ArrowUp on a closed list already opens it on the last option,
323
+ ArrowDown on the first, and navigation wraps at both ends, so every position
324
+ Home/End reached is still one keystroke away.
325
+
326
+ `uni-multi-select-dropdown` keeps them: its roving focus rides the option
327
+ checkboxes rather than a text field, so there is no caret with a better claim.
328
+
329
+ `ListboxNavigation` carries the switch as `homeEndNavigates`, defaulting to
330
+ false — off is the right default for a control built around a text input,
331
+ which is every consumer but one. If you build on the CDK helper directly and
332
+ want the old behavior, pass `homeEndNavigates: true`.
333
+
334
+ This also fixes a sharper bug in `uni-search-input` and `uni-tag-input`, where
335
+ Home/End reached the navigation helper unconditionally: pressing either not
336
+ only moved the active option but _opened a closed suggestion list_.
337
+
338
+ - [`dd33baa`](https://github.com/uni-design-system/uni/commit/dd33baa821922f257980cc3543dcac0ba118a0c3) Thanks [@gaenglish](https://github.com/gaenglish)! - The four listbox popups — `uni-search-input`, `uni-tag-input`,
339
+ `uni-time-input` and `uni-combobox` — now render in the browser's top layer,
340
+ anchored to their field, instead of as absolutely-positioned children of it.
341
+ Put any of them inside a card, a table cell, a scroll area or a dialog and the
342
+ suggestion list is no longer clipped by that ancestor's `overflow`. The browser
343
+ tracks the field natively, so the list follows on scroll and resize with no
344
+ listeners, and flips above the field near the bottom of the viewport.
345
+
346
+ They also open the way `uni-dropdown` does now — the same 100 ms scale-and-fade
347
+ — so every popup panel in the library animates alike instead of the listboxes
348
+ alone snapping into place. The origin is measured from where the popup actually
349
+ opened, so one that flips above its field near the bottom of the viewport still
350
+ grows out of the edge it is attached to. Under `prefers-reduced-motion` there is
351
+ no transition at all.
352
+
353
+ Nothing changes in the components' APIs or in how they dismiss. The popups use
354
+ `popover="manual"`, not `auto`: these controls already own dismissal through
355
+ focusout, Escape and commit, and `auto`'s light-dismiss fires on pointerdown
356
+ outside the popup — which includes their own input, so it would close the list
357
+ on every click into the field.
358
+
359
+ Positioning is gated on CSS anchor positioning support, checked together with
360
+ the top layer rather than separately. Browsers that have `popover` but not
361
+ anchors — Safari 17 through 25 — keep the previous in-flow popup, which still
362
+ clips inside `overflow: hidden` ancestors but stays on its field; promoting it
363
+ there would strand the list a viewport height down the page, since a top-layer
364
+ element has no positioned ancestor to resolve against.
365
+
366
+ Shared plumbing lives in `components/forms/listbox-popup.ts` alongside
367
+ `listboxPopupStyles()`, which grew an optional `anchor` and now emits the
368
+ in-flow rules as the base with the anchored ones in an `@supports` block.
369
+
370
+ - [`2910c67`](https://github.com/uni-design-system/uni/commit/2910c67c9ac0c5b47be7f67c4407514aa22a4f60) Thanks [@gaenglish](https://github.com/gaenglish)! - `Option.disabled` now works in the three remaining `Options<T>` consumers, not
371
+ just `uni-combobox`: `uni-select`, `uni-multi-select-dropdown` and
372
+ `uni-multi-select`. Marking an individual choice `disabled` shows it without
373
+ offering it — the plan that needs an upgrade, the channel that needs a verified
374
+ phone number — instead of dropping it from the array and leaving the list
375
+ lying about what exists.
376
+
377
+ `uni-select` passes it to the native `<option>`, so the platform handles
378
+ skipping, greying and announcement. The two multi-selects disable the option's
379
+ checkbox and refuse the toggle even when called directly. In
380
+ `uni-multi-select-dropdown` the arrow keys step over disabled rows and Home/End
381
+ land on the nearest enabled option — `ListboxNavigation` already knew how, it
382
+ just was not being told which rows were disabled — so focus never parks on a
383
+ checkbox that cannot take it.
384
+
385
+ `selectAll()` on both multi-selects now selects only enabled options. A
386
+ disabled option is not committable, so nothing may commit one on the user's
387
+ behalf; `deselectAll()` still clears everything. If you relied on `selectAll()`
388
+ returning every value including disabled ones, note that only options you have
389
+ explicitly marked `disabled` are affected. Whole-field `disabled` is unchanged
390
+ and still disables the entire control.
391
+
392
+ - [`6f76c1d`](https://github.com/uni-design-system/uni/commit/6f76c1d3cc1ba2ed5e2747b6d2142d16cb4f537b) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-alert`, `uni-snackbar`, `uni-radio` and `uni-menu-item` now read their
393
+ timing from the theme's `motion` scale, which completes the migration: every
394
+ animated component in the library is retimed from one place.
395
+
396
+ Two tokens join `popup`, `panel` and `reveal`:
397
+
398
+ | Token | Default | Used by |
399
+ | -------------- | ----------------- | ---------------- |
400
+ | `notification` | 350ms ease-in-out | alert, snackbar |
401
+ | `control` | 300ms ease | radio, menu-item |
402
+
403
+ **One thing moves differently:** `menu-item`'s hover transition goes from
404
+ 0.35s to 0.3s, joining `radio` on the shared `control` token. The two were
405
+ never deliberately different — `radio`'s own option documented its 0.3 as
406
+ "matching menuItem", which was not true — so this corrects drift rather than
407
+ changing a decision. A theme that wants the old timing can set the deprecated
408
+ `transitionSpeed`, or retime `control`. Everything else is byte-identical,
409
+ verified against the rendered styles.
410
+
411
+ **Deprecated, still honoured, removed next major:**
412
+ `alert.transitionSpeed`, `snackbar.transitionDelay`, `radio.transitionSpeed`
413
+ and `menuItem.transitionSpeed` all still work and still outrank `motion`, so a
414
+ theme that set them keeps its exact timing. `menu-item`'s escape hatch is
415
+ intact too: with neither the option nor a token set it still renders no
416
+ transition at all, and a token with `duration: 0` is the way to ask for
417
+ instant now.
418
+
419
+ `card.transitionSpeed` and `input-box.transitionSpeed` are also deprecated,
420
+ for a different reason: **neither was ever read by its component.** Setting
421
+ them has never had any effect. They are removed next major and nothing needs
422
+ to replace them.
423
+
424
+ `uni-skeleton` is deliberately left out. Its shimmer is a loop rather than a
425
+ transition, and folding a repeating animation into a scale built around
426
+ entering and leaving would make the scale mean two different things.
427
+
428
+ - [`228c17f`](https://github.com/uni-design-system/uni/commit/228c17f9fb7a4e4eb8d6a9cd0a6301d02ddd3dd3) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-skeleton` picks up the knobs an app theme could not reach. `color`, `highlightColor` and `borderRadius` are now inputs that take a token name and fall back to the theme option, because one screen routinely needs several of each — text bars and a pill chip do not share a corner (`borderRadius="max"`), and a skeleton on a card wants a different tint than one on the page background.
429
+
430
+ The shimmer is now a band swept with `transform` rather than an animated `background-position`: it composites instead of repainting every frame, and its geometry is themeable via two new options, `direction` (`'ltr' | 'rtl'`, default `'ltr'` — the sweep previously ran right-to-left) and `highlightWidth` (band width as a percentage of the block, default `40`). Both ends of the band are the base color, so it dissolves into the block with no alpha and no fringing.
431
+
432
+ A new `label` input announces a standalone skeleton: set it and the host becomes `role="status"` with visually hidden text instead of `aria-hidden="true"`. Unlabelled skeletons stay `aria-hidden`, so container-level `aria-busy` patterns are unchanged.
433
+
434
+ - [`eb795a3`](https://github.com/uni-design-system/uni/commit/eb795a3874c4642e571651dfdebd1a392d62d5ab) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-snackbar` now renders in the browser's top layer, so it can no longer be
435
+ covered by a high `z-index` or clipped by an `overflow: hidden` or transformed
436
+ ancestor. It was the last overlay in the library still competing on stacking
437
+ order: a `<dialog>` opened with `.show()`, which is the _non-modal_ form and
438
+ never enters the top layer, left it relying on `zIndex: Z_INDEX.dialog`. That
439
+ held only because apps mount the bar near the root — anywhere else, a
440
+ confirmation of what just happened could be silently buried.
441
+
442
+ The bar is now a `popover="manual"`. Manual rather than auto because a
443
+ snackbar must not light-dismiss: a click anywhere else on the page would tear
444
+ it away from someone still reading it. It is not `showModal()` either — that
445
+ would make the rest of the page inert to announce a transient message.
446
+
447
+ `role="status"`, the auto-close timer, its pause-on-hover and pause-on-focus
448
+ behaviour, the entry and exit animations, and the `[(show)]` / `open()` /
449
+ `close()` API are all unchanged.
450
+
451
+ The element behind the component changed from `<dialog>` to `<div>`: the bar
452
+ is never modal, and `<dialog>`'s `open` attribute would have been a second,
453
+ competing notion of "shown" alongside the popover's own state. Styles that
454
+ reach inside the component to target `uni-snackbar dialog` need updating —
455
+ `uni-snackbar [role="status"]` is the stable selector.
456
+
457
+ - [`3b51ace`](https://github.com/uni-design-system/uni/commit/3b51acee92659526c1c8c91668b37db8c5933162) Thanks [@gaenglish](https://github.com/gaenglish)! - Overlay timing is now a theme token. `UniTheme` gains a `motion` scale
458
+ alongside `radii`, `shadows` and the rest, and the overlays point at it by
459
+ name instead of carrying their own hardcoded durations.
460
+
461
+ Until now a theme could slow a skeleton shimmer but not a dropdown: `expand`,
462
+ `skeleton` and `callout` each exposed their own motion option while
463
+ `uni-dropdown` hardcoded 100ms and the combobox-style listbox popups copied
464
+ that constant. Retiming the library meant editing components.
465
+
466
+ Two tokens ship, because two things genuinely move differently — a small panel
467
+ attached to a control snaps, a larger free-floating surface settles:
468
+
469
+ | Token | Default | Used by |
470
+ | ------- | ------------------ | --------------------------------------------------------------------------- |
471
+ | `popup` | 100ms linear, ×0.8 | dropdown, menu, multi-select, combobox, search-input, tag-input, time-input |
472
+ | `panel` | 250ms ease | popover |
473
+
474
+ A token carries `duration` (ms), `easing`, and an optional `scale` for panels
475
+ that grow into place — one token rather than separate duration and easing
476
+ scales, because they are one design decision: slowing a panel without
477
+ softening its curve reads as sluggish rather than calm.
478
+
479
+ ```ts
480
+ createTheme({
481
+ id: 'Calm',
482
+ name: 'Calm',
483
+ colors,
484
+ motion: { popup: { duration: 240, easing: 'ease-out', scale: 0.95 } },
485
+ });
486
+ ```
487
+
488
+ Tokens you don't restate keep their base values, and a component can still
489
+ point at a token of its own through its `motion` option.
490
+
491
+ Nothing moves differently by default — every current duration, easing and
492
+ scale is preserved exactly, verified against the rendered styles. Themes that
493
+ predate the scale keep working: `createTheme` fills it in, the validator does
494
+ not require it, and a theme registered as JSON without it resolves to the base
495
+ timing rather than failing. `motionSafe` remains the floor, so a theme decides
496
+ how overlays move for people who want movement, never whether they move at
497
+ all.
498
+
499
+ `callout`'s existing `transitionMs` option and the `expand`/`skeleton`
500
+ durations are untouched for now; folding them into this scale is a follow-up.
501
+
502
+ ### Patch Changes
503
+
504
+ - [`676ec55`](https://github.com/uni-design-system/uni/commit/676ec5549cf4b10c549538aebd8f5a56fc0cb68a) Thanks [@gaenglish](https://github.com/gaenglish)! - `uni-dropdown` now uses the CDK's overlay helpers instead of its own copies of
505
+ them. It predates `cdk/overlay`, so it had been carrying a duplicate
506
+ placement-to-`transform-origin` map, a duplicate focus-restore rule, a
507
+ duplicate discrete-transition block, and hand-written anchor and toggle-state
508
+ code. `TRANSFORM_ORIGINS` had no consumers at all as a result — the shared
509
+ constant existed while the one component that needed it used its own copy.
510
+
511
+ No behaviour change: the dropdown's 100 ms linear scale-and-fade, its measured
512
+ transform origin, its focus restore and its ARIA wiring are all identical,
513
+ verified against the rendered styles. Every export in `cdk/overlay` now has a
514
+ consumer, and the component is 35 lines shorter.
515
+
516
+ `discreteOverlayTransition()` takes an optional fourth argument, a
517
+ `transition-timing-function`. Omitted, nothing is emitted and the CSS initial
518
+ value stands, so existing callers are untouched.
519
+
520
+ One real inconsistency fixed along the way: the listbox popups
521
+ (`uni-combobox`, `uni-search-input`, `uni-tag-input`, `uni-time-input`) were
522
+ introduced to match `uni-dropdown`'s animation but ran on the default `ease`,
523
+ while the dropdown uses `linear` — so a combobox and a multi-select dropdown
524
+ in the same form opened at visibly different rates. They now share the
525
+ dropdown's easing exactly.
526
+
3
527
  ## 8.3.1
4
528
 
5
529
  ### Patch Changes