@uni-design-system/uni-mcp 11.0.0 → 11.2.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.
@@ -7,16 +7,16 @@ import { z } from "zod";
7
7
  // src/data/uni-index.json
8
8
  var uni_index_default = {
9
9
  meta: {
10
- version: "11.0.0",
11
- builtAt: "2026-09-07T13:37:31.297Z",
10
+ version: "11.2.0",
11
+ builtAt: "2026-09-17T10:43:40.795Z",
12
12
  frameworks: [
13
13
  "angular"
14
14
  ],
15
15
  counts: {
16
- components: 85,
17
- tokens: 482,
16
+ components: 87,
17
+ tokens: 490,
18
18
  themes: 2,
19
- examples: 233
19
+ examples: 245
20
20
  }
21
21
  },
22
22
  components: [
@@ -27,7 +27,7 @@ var uni_index_default = {
27
27
  summary: "",
28
28
  description: "",
29
29
  category: "feedback",
30
- version: "11.0.0",
30
+ version: "11.2.0",
31
31
  bindings: {
32
32
  angular: {
33
33
  importPath: "@uni-design-system/uni-angular",
@@ -117,7 +117,7 @@ var uni_index_default = {
117
117
  summary: "Horizontal application bar: project leading controls (menu button, logo) with the `leading` attribute, actions with `trailing` (pushed to the far edge), and either pass `title` or project custom center content.",
118
118
  description: "Horizontal application bar: project leading controls (menu button, logo) with the `leading` attribute, actions with `trailing` (pushed to the far edge), and either pass `title` or project custom center content. Surface, divider, height, typography and spacing all resolve from `appBar` tokens. Place it inside your page's `<header>` landmark.",
119
119
  category: "components",
120
- version: "11.0.0",
120
+ version: "11.2.0",
121
121
  bindings: {
122
122
  angular: {
123
123
  importPath: "@uni-design-system/uni-angular",
@@ -190,7 +190,7 @@ var uni_index_default = {
190
190
  summary: "Identity avatar with a graceful fallback chain: image \u2192 initials from `name` \u2192 themed symbol.",
191
191
  description: "Identity avatar with a graceful fallback chain: image \u2192 initials from `name` \u2192 themed symbol. Coloring, radius, typeface and sizes all resolve from `avatar` theme tokens; `variant` picks the container palette.",
192
192
  category: "components",
193
- version: "11.0.0",
193
+ version: "11.2.0",
194
194
  bindings: {
195
195
  angular: {
196
196
  importPath: "@uni-design-system/uni-angular",
@@ -285,7 +285,7 @@ var uni_index_default = {
285
285
  summary: "Overlapping stack of avatars.",
286
286
  description: "Overlapping stack of avatars. `max` collapses the overflow into a themed \u201C+N\u201D chip (itself a `uni-avatar`). Overlap and separator ring resolve from `avatarGroup` theme tokens.",
287
287
  category: "components",
288
- version: "11.0.0",
288
+ version: "11.2.0",
289
289
  bindings: {
290
290
  angular: {
291
291
  importPath: "@uni-design-system/uni-angular",
@@ -334,7 +334,7 @@ var uni_index_default = {
334
334
  summary: "",
335
335
  description: "",
336
336
  category: "primitives",
337
- version: "11.0.0",
337
+ version: "11.2.0",
338
338
  bindings: {
339
339
  angular: {
340
340
  importPath: "@uni-design-system/uni-angular",
@@ -415,7 +415,7 @@ var uni_index_default = {
415
415
  summary: "",
416
416
  description: "",
417
417
  category: "feedback",
418
- version: "11.0.0",
418
+ version: "11.2.0",
419
419
  bindings: {
420
420
  angular: {
421
421
  importPath: "@uni-design-system/uni-angular",
@@ -512,7 +512,7 @@ var uni_index_default = {
512
512
  summary: "",
513
513
  description: "",
514
514
  category: "directives",
515
- version: "11.0.0",
515
+ version: "11.2.0",
516
516
  bindings: {
517
517
  angular: {
518
518
  importPath: "@uni-design-system/uni-angular",
@@ -536,7 +536,7 @@ var uni_index_default = {
536
536
  summary: 'The base layout primitive, applied as an attribute to any element so semantics stay yours: `<main box-layout [grow]="1" padding="md">`.',
537
537
  description: 'The base layout primitive, applied as an attribute to any element so semantics stay yours: `<main box-layout [grow]="1" padding="md">`. It is a directive, so it composes freely \u2014 with another layout attribute (`<div stack-layout uni-text="body-1-long">`) and with a component\'s own host element (`<uni-card box-layout padding="lg">`); Angular merges the host `class` bindings rather than letting one win. Sizing convention (height/width/min/max/inset): a **number is px and needs a binding** \u2014 `[height]="420"`; a **plain attribute is a CSS length string** \u2014 `height="420px"`. Spacing/radius/color inputs take theme tokens.',
538
538
  category: "layout",
539
- version: "11.0.0",
539
+ version: "11.2.0",
540
540
  bindings: {
541
541
  angular: {
542
542
  importPath: "@uni-design-system/uni-angular",
@@ -862,15 +862,17 @@ var uni_index_default = {
862
862
  {
863
863
  name: "fullWidth",
864
864
  kind: "input",
865
- type: "boolean",
865
+ type: "unknown",
866
866
  required: false,
867
+ default: "false, { transform: booleanAttribute }",
867
868
  description: ""
868
869
  },
869
870
  {
870
871
  name: "fullHeight",
871
872
  kind: "input",
872
- type: "boolean",
873
+ type: "unknown",
873
874
  required: false,
875
+ default: "false, { transform: booleanAttribute }",
874
876
  description: ""
875
877
  },
876
878
  {
@@ -942,7 +944,7 @@ var uni_index_default = {
942
944
  summary: 'WAI-ARIA breadcrumb: a `nav` landmark wrapping an ordered list; the last item is the current page (`aria-current="page"`), separators are decorative.',
943
945
  description: 'WAI-ARIA breadcrumb: a `nav` landmark wrapping an ordered list; the last item is the current page (`aria-current="page"`), separators are decorative. Items with `href` render as real links; without one they render as link-styled buttons emitting `itemClicked` \u2014 wire that to your router.',
944
946
  category: "components",
945
- version: "11.0.0",
947
+ version: "11.2.0",
946
948
  bindings: {
947
949
  angular: {
948
950
  importPath: "@uni-design-system/uni-angular",
@@ -1027,7 +1029,7 @@ var uni_index_default = {
1027
1029
  summary: "",
1028
1030
  description: "",
1029
1031
  category: "primitives",
1030
- version: "11.0.0",
1032
+ version: "11.2.0",
1031
1033
  bindings: {
1032
1034
  angular: {
1033
1035
  importPath: "@uni-design-system/uni-angular",
@@ -1036,25 +1038,25 @@ var uni_index_default = {
1036
1038
  {
1037
1039
  name: "disable",
1038
1040
  kind: "input",
1039
- type: "boolean | undefined",
1041
+ type: "unknown",
1040
1042
  required: false,
1041
- default: "false",
1043
+ default: "false, { transform: booleanAttribute }",
1042
1044
  description: ""
1043
1045
  },
1044
1046
  {
1045
1047
  name: "loading",
1046
1048
  kind: "input",
1047
- type: "boolean | undefined",
1049
+ type: "unknown",
1048
1050
  required: false,
1049
- default: "false",
1051
+ default: "false, { transform: booleanAttribute }",
1050
1052
  description: ""
1051
1053
  },
1052
1054
  {
1053
1055
  name: "fullWidth",
1054
1056
  kind: "input",
1055
- type: "boolean",
1057
+ type: "unknown",
1056
1058
  required: false,
1057
- default: "false",
1059
+ default: "false, { transform: booleanAttribute }",
1058
1060
  description: ""
1059
1061
  },
1060
1062
  {
@@ -1187,7 +1189,7 @@ var uni_index_default = {
1187
1189
  summary: "",
1188
1190
  description: "",
1189
1191
  category: "primitives",
1190
- version: "11.0.0",
1192
+ version: "11.2.0",
1191
1193
  bindings: {
1192
1194
  angular: {
1193
1195
  importPath: "@uni-design-system/uni-angular",
@@ -1219,7 +1221,7 @@ var uni_index_default = {
1219
1221
  summary: "Inline month calendar: single-date or start\u2013end range selection, day markers (availability dots), min/max fences and disabled dates.",
1220
1222
  description: "Inline month calendar: single-date or start\u2013end range selection, day markers (availability dots), min/max fences and disabled dates. One tab stop with a roving-tabindex `role=\"grid\"`; every day is a real button named with its full date. Values are plain ISO strings (`'YYYY-MM-DD'`), never `Date` objects, so bindings are timezone-free and serializable. Selection and today colours come from the theme's `primary` role pair; geometry and glyphs come from the `calendar` theme entry.",
1221
1223
  category: "forms",
1222
- version: "11.0.0",
1224
+ version: "11.2.0",
1223
1225
  bindings: {
1224
1226
  angular: {
1225
1227
  importPath: "@uni-design-system/uni-angular",
@@ -1460,7 +1462,7 @@ var uni_index_default = {
1460
1462
  summary: "Anchored coach-mark panel that dims the page and cuts a spotlight hole around its target.",
1461
1463
  description: 'Anchored coach-mark panel that dims the page and cuts a spotlight hole around its target. Both the scrim and the panel are `popover="manual"` elements shown in order, so they stack deterministically in the top layer above every app z-index, and every scrim piece is CSS-anchor-positioned to the target \u2014 the hole tracks scroll/resize/layout with zero listeners. The panel is a non-modal `role="dialog"` (deliberately no `aria-modal`): focus moves into it on open, Tab runs a "duet loop" over the panel\'s focusables plus the spotlit target while it stays interactive, and on close focus returns to the pre-open element \u2014 unless the user moved into the target, where it stays. Storage-free by rule: `key` and the `dismissed` output are the "don\'t show again" hooks; persistence is the app\'s business (e.g. cdk local-storage).',
1462
1464
  category: "components",
1463
- version: "11.0.0",
1465
+ version: "11.2.0",
1464
1466
  bindings: {
1465
1467
  angular: {
1466
1468
  importPath: "@uni-design-system/uni-angular",
@@ -1680,7 +1682,7 @@ var uni_index_default = {
1680
1682
  summary: "",
1681
1683
  description: "",
1682
1684
  category: "layout",
1683
- version: "11.0.0",
1685
+ version: "11.2.0",
1684
1686
  bindings: {
1685
1687
  angular: {
1686
1688
  importPath: "@uni-design-system/uni-angular",
@@ -1742,7 +1744,7 @@ var uni_index_default = {
1742
1744
  summary: "",
1743
1745
  description: "",
1744
1746
  category: "layout",
1745
- version: "11.0.0",
1747
+ version: "11.2.0",
1746
1748
  bindings: {
1747
1749
  angular: {
1748
1750
  importPath: "@uni-design-system/uni-angular",
@@ -1783,7 +1785,7 @@ var uni_index_default = {
1783
1785
  summary: "",
1784
1786
  description: "",
1785
1787
  category: "layout",
1786
- version: "11.0.0",
1788
+ version: "11.2.0",
1787
1789
  bindings: {
1788
1790
  angular: {
1789
1791
  importPath: "@uni-design-system/uni-angular",
@@ -1840,7 +1842,7 @@ var uni_index_default = {
1840
1842
  summary: "",
1841
1843
  description: "",
1842
1844
  category: "layout",
1843
- version: "11.0.0",
1845
+ version: "11.2.0",
1844
1846
  bindings: {
1845
1847
  angular: {
1846
1848
  importPath: "@uni-design-system/uni-angular",
@@ -1895,7 +1897,7 @@ var uni_index_default = {
1895
1897
  summary: "",
1896
1898
  description: "",
1897
1899
  category: "forms",
1898
- version: "11.0.0",
1900
+ version: "11.2.0",
1899
1901
  bindings: {
1900
1902
  angular: {
1901
1903
  importPath: "@uni-design-system/uni-angular",
@@ -1977,6 +1979,22 @@ var uni_index_default = {
1977
1979
  required: false,
1978
1980
  description: ""
1979
1981
  },
1982
+ {
1983
+ name: "labelHidden",
1984
+ kind: "input",
1985
+ type: "unknown",
1986
+ required: false,
1987
+ default: "false, { transform: booleanAttribute }",
1988
+ description: "Keep `label` as the accessible name, but do not draw it. For the shape where the thing being selected is already on screen beside the box \u2014 a table's select column, a row in a list \u2014 the name is needed by assistive tech and must not be printed. The string stays in the DOM, so a test querying by label text and a screen reader see the same words."
1989
+ },
1990
+ {
1991
+ name: "fullWidth",
1992
+ kind: "input",
1993
+ type: "unknown",
1994
+ required: false,
1995
+ default: "false, { transform: booleanAttribute }",
1996
+ description: "Stretch the control and its label across the container, making the whole width the hit target. Pair it with projected content for a clickable row."
1997
+ },
1980
1998
  {
1981
1999
  name: "indeterminate",
1982
2000
  kind: "model",
@@ -2048,28 +2066,50 @@ var uni_index_default = {
2048
2066
  title: "Form Signals",
2049
2067
  framework: "angular",
2050
2068
  code: '<!-- Storybook story: Form Signals -->\n<div>\n<uni-checkbox\n label="Subscribe to newsletter"\n variant="primary"\n [checked]="isChecked"\n [disabled]="isDisabled"\n [invalid]="isInvalid"\n [touched]="isTouched">\n</uni-checkbox>\n<div box-layout paddingTop="md">\n <p>Checked: {{ isChecked }}</p>\n <p>Touched: {{ isTouched }}</p>\n <p>Invalid: {{ isInvalid }}</p>\n <p>Disabled: {{ isDisabled }}</p>\n</div>\n<div row-layout gap="sm" paddingTop="sm">\n<button\n (click)="isTouched = true">\n Mark as Touched\n</button>\n<button\n (click)="isDisabled = !isDisabled">\n Toggle Disabled\n</button>\n<button\n (click)="isInvalid = !isInvalid">\n Toggle Invalid\n</button>\n</div>\n</div>'
2069
+ },
2070
+ {
2071
+ title: "Hidden Label",
2072
+ framework: "angular",
2073
+ code: '<!-- Storybook story: Hidden Label -->\n<div stack-layout gap="md">\n<div row-layout gap="sm" alignItems="center">\n <uni-checkbox label="Select every line from Northwind" labelHidden></uni-checkbox>\n <span uni-text="body-1-short">Northwind Traders</span>\n</div>\n<div row-layout gap="sm" alignItems="center">\n <uni-checkbox label="Select every line from Contoso" labelHidden></uni-checkbox>\n <span uni-text="body-1-short">Contoso Ltd</span>\n</div>\n</div>'
2074
+ },
2075
+ {
2076
+ title: "Row As Hit Target",
2077
+ framework: "angular",
2078
+ code: '<!-- Storybook story: Row As Hit Target -->\n<div stack-layout gap="xs">\n<uni-checkbox fullWidth>\n <span uni-text="body-2-long">Sofa, three seat</span>\n <span uni-text="caption">In stock</span>\n</uni-checkbox>\n<uni-checkbox fullWidth>\n <span uni-text="body-2-long">Armchair, walnut</span>\n <span uni-text="caption">Backordered</span>\n</uni-checkbox>\n</div>'
2079
+ },
2080
+ {
2081
+ title: "Sizes",
2082
+ framework: "angular",
2083
+ code: '<!-- Storybook story: Sizes -->\n<div stack-layout gap="md">\n<uni-checkbox size="sm" label="Small" checked></uni-checkbox>\n<uni-checkbox size="md" label="Medium" checked></uni-checkbox>\n<uni-checkbox size="lg" label="Large" checked></uni-checkbox>\n</div>'
2051
2084
  }
2052
2085
  ],
2053
2086
  guidelines: {
2054
- whenToUse: 'Binary choice control for forms: check to opt in, submit to apply. It implements\n`FormCheckboxControl` for the Signal Forms `[formField]` directive (or bind\n`[(checked)]` directly), exposes `aria-invalid` once touched, and supports an\n`indeterminate` model for "select all" parents \u2014 cleared automatically on the next\nuser interaction, matching native behavior.\nThe box color, check stroke (the variant\'s paired on-color), and size resolve from\nthe `checkbox` theme options.',
2087
+ whenToUse: 'Binary choice control for forms: check to opt in, submit to apply. It implements\n`FormCheckboxControl` for the Signal Forms `[formField]` directive (or bind\n`[(checked)]` directly), exposes `aria-invalid` once touched, and supports an\n`indeterminate` model for "select all" parents \u2014 cleared automatically on the next\nuser interaction, matching native behavior.\nThe box color, check stroke (the variant\'s paired on-color), the label\'s typeface\nand the gap beside it all resolve from the `checkbox` theme options; box geometry\ncomes from the theme\'s `sizes` block, so `size="sm"` gives a dense table its own\ncontrol without a second component.\n`checked` is a `model`. Bind it two-way, or hold the value in your own signal: if\nyour app declines a change the user made \u2014 a server refusal, an optimistic update\nthat rolls back to the same value \u2014 a one-way `[checked]` expression has not\nchanged, so Angular writes nothing and the box keeps the state the user left it\nin. The same applies to `indeterminate`, which a user interaction clears.',
2055
2088
  dos: [
2056
- "Use for choices that apply when the form is submitted"
2089
+ "Use for choices that apply when the form is submitted",
2090
+ "Reach for `labelHidden` rather than clipping the label with app CSS",
2091
+ "Project the row's content when the whole row should be the hit target"
2057
2092
  ],
2058
2093
  donts: [
2059
- "Use for instant on/off effects \u2014 that is a toggle"
2094
+ "Use for instant on/off effects \u2014 that is a toggle",
2095
+ "Wrap a checkbox in your own `<label>` \u2014 nesting is illegal; project instead",
2096
+ "Bind `[checked]` one way and expect a declined change to snap back"
2060
2097
  ],
2061
2098
  accessibility: [
2062
2099
  'Semantic `<input type="checkbox">` + `<label>`; Space toggles.',
2100
+ "`labelHidden` hides the label visually while keeping it as the accessible name.",
2101
+ "Projected content sits inside that same `<label>`, so it names and activates the control.",
2063
2102
  "`aria-invalid` reflects the error state once the field was touched or is dirty.",
2064
2103
  'The indeterminate visual state is carried by the native `indeterminate` property, announced as "mixed".'
2065
2104
  ]
2066
2105
  },
2067
2106
  relatedTokens: [
2068
2107
  "checkbox.behavior.motion",
2069
- "checkbox.behavior.size",
2070
2108
  "checkbox.behavior.boxColor",
2071
2109
  "checkbox.behavior.borderRadius",
2072
- "checkbox.behavior.focusRingGap"
2110
+ "checkbox.behavior.focusRingGap",
2111
+ "checkbox.behavior.textRole",
2112
+ "checkbox.behavior.gap"
2073
2113
  ]
2074
2114
  },
2075
2115
  {
@@ -2079,7 +2119,7 @@ var uni_index_default = {
2079
2119
  summary: "Form-bound, closed-set, single-select autocomplete: `FormValueControl<T | null>` over object `Options<T>`, type-to-filter, commit-on-select.",
2080
2120
  description: "Form-bound, closed-set, single-select autocomplete: `FormValueControl<T | null>` over object `Options<T>`, type-to-filter, commit-on-select. Typing produces a draft \u2014 a filter, never a value; the value changes only when an option commits, `clear()` runs, or the model is written from outside. Select semantics, not search: a real label, a chevron, no magnifier, nothing submits. For short lists with no need to type, prefer `uni-select` \u2014 the value contract is identical, so swapping is a template-only change.",
2081
2121
  category: "forms",
2082
- version: "11.0.0",
2122
+ version: "11.2.0",
2083
2123
  bindings: {
2084
2124
  angular: {
2085
2125
  importPath: "@uni-design-system/uni-angular",
@@ -2365,7 +2405,7 @@ emptyText="No countries found"
2365
2405
  summary: "",
2366
2406
  description: "",
2367
2407
  category: "feedback",
2368
- version: "11.0.0",
2408
+ version: "11.2.0",
2369
2409
  bindings: {
2370
2410
  angular: {
2371
2411
  importPath: "@uni-design-system/uni-angular",
@@ -2412,7 +2452,7 @@ emptyText="No countries found"
2412
2452
  summary: "",
2413
2453
  description: "",
2414
2454
  category: "forms",
2415
- version: "11.0.0",
2455
+ version: "11.2.0",
2416
2456
  bindings: {
2417
2457
  angular: {
2418
2458
  importPath: "@uni-design-system/uni-angular",
@@ -2511,7 +2551,7 @@ emptyText="No countries found"
2511
2551
  summary: "",
2512
2552
  description: "",
2513
2553
  category: "data",
2514
- version: "11.0.0",
2554
+ version: "11.2.0",
2515
2555
  bindings: {
2516
2556
  angular: {
2517
2557
  importPath: "@uni-design-system/uni-angular",
@@ -2751,7 +2791,7 @@ emptyText="No countries found"
2751
2791
  summary: "Date field with free-typed parsing and a popup calendar.",
2752
2792
  description: "Date field with free-typed parsing and a popup calendar. Type `aug 20`, `8/20/2026` or `2026-08-20`, or pick from the grid \u2014 the form gets the same canonical `'YYYY-MM-DD'` string either way. Parsing is `Intl`-driven (locale digit order and month names, never hardcoded); unreadable text stays in the field, flagged, with a `rejected` event. The popup is a native popover hosting the same `uni-calendar` an app could render inline.",
2753
2793
  category: "forms",
2754
- version: "11.0.0",
2794
+ version: "11.2.0",
2755
2795
  bindings: {
2756
2796
  angular: {
2757
2797
  importPath: "@uni-design-system/uni-angular",
@@ -2982,7 +3022,7 @@ emptyText="No countries found"
2982
3022
  summary: "One field for a date and a time: a thin composer seating a uni-date-input and a uni-time-input in one input-box chrome under one label, yielding one combined `'YYYY-MM-DDTHH:mm'` value.",
2983
3023
  description: "One field for a date and a time: a thin composer seating a uni-date-input and a uni-time-input in one input-box chrome under one label, yielding one combined `'YYYY-MM-DDTHH:mm'` value. The value emits only when both parts are set \u2014 a time without a day is not an answer \u2014 and clearing the date clears it. With `slotsFor`, the time part stays disabled until a day is chosen and offers exactly that day's slots: the scheduling flow in one attribute. Two honest tab stops (it is two questions); apps needing a different arrangement compose the primitives directly.",
2984
3024
  category: "forms",
2985
- version: "11.0.0",
3025
+ version: "11.2.0",
2986
3026
  bindings: {
2987
3027
  angular: {
2988
3028
  importPath: "@uni-design-system/uni-angular",
@@ -3175,7 +3215,7 @@ emptyText="No countries found"
3175
3215
  summary: "Text input that emits `change` only after the user pauses typing.",
3176
3216
  description: "Text input that emits `change` only after the user pauses typing. Wears the shared input chrome (themed color, border, typeface, focus ring) via `uni-input-box`; project adornments with the `pre-input` / `post-input` slots, matching `uni-input`'s convention. The ARIA passthrough inputs let a wrapping composite (e.g. SearchInput's combobox) annotate the real input element.",
3177
3217
  category: "forms",
3178
- version: "11.0.0",
3218
+ version: "11.2.0",
3179
3219
  bindings: {
3180
3220
  angular: {
3181
3221
  importPath: "@uni-design-system/uni-angular",
@@ -3323,7 +3363,7 @@ emptyText="No countries found"
3323
3363
  summary: "Modal dialog on the native `<dialog>` element, laid out as three rows: an optional `[dialog-header]`, the projected body, an optional `[dialog-buttons]`.",
3324
3364
  description: "Modal dialog on the native `<dialog>` element, laid out as three rows: an optional `[dialog-header]`, the projected body, an optional `[dialog-buttons]`. **The surface is never the scroll container.** It is a flex column that is `overflow: clip` on both axes; the header and buttons pin while only the body row scrolls. Unlike the drawer, which is deliberately viewport-tall, the dialog stays content-sized: it has no `height`, and grows until it hits `calc(100dvh - 2 x inset)` \u2014 the point at which the body starts to scroll. The theme's `padding` insets all three rows (which is what makes the default header read as a pill floating inside the surface); `bodyPadding` pads the scrolling row alone.",
3325
3365
  category: "feedback",
3326
- version: "11.0.0",
3366
+ version: "11.2.0",
3327
3367
  bindings: {
3328
3368
  angular: {
3329
3369
  importPath: "@uni-design-system/uni-angular",
@@ -3476,7 +3516,7 @@ uni-dialog
3476
3516
  summary: "",
3477
3517
  description: "",
3478
3518
  category: "feedback",
3479
- version: "11.0.0",
3519
+ version: "11.2.0",
3480
3520
  bindings: {
3481
3521
  angular: {
3482
3522
  importPath: "@uni-design-system/uni-angular",
@@ -3580,7 +3620,7 @@ uni-dialog
3580
3620
  summary: "",
3581
3621
  description: "",
3582
3622
  category: "feedback",
3583
- version: "11.0.0",
3623
+ version: "11.2.0",
3584
3624
  bindings: {
3585
3625
  angular: {
3586
3626
  importPath: "@uni-design-system/uni-angular",
@@ -3621,7 +3661,7 @@ uni-dialog
3621
3661
  summary: "",
3622
3662
  description: "",
3623
3663
  category: "layout",
3624
- version: "11.0.0",
3664
+ version: "11.2.0",
3625
3665
  bindings: {
3626
3666
  angular: {
3627
3667
  importPath: "@uni-design-system/uni-angular",
@@ -3677,7 +3717,7 @@ uni-dialog
3677
3717
  summary: "",
3678
3718
  description: "",
3679
3719
  category: "directives",
3680
- version: "11.0.0",
3720
+ version: "11.2.0",
3681
3721
  bindings: {
3682
3722
  angular: {
3683
3723
  importPath: "@uni-design-system/uni-angular",
@@ -3709,7 +3749,7 @@ uni-dialog
3709
3749
  summary: "Drawer with two modes sharing one three-row layout: - `side` \u2014 an in-flow `<aside>` that pushes content (dashboard sidenav); opening/closing animates its width, and the divider border primitive separates it from the page.",
3710
3750
  description: "Drawer with two modes sharing one three-row layout: - `side` \u2014 an in-flow `<aside>` that pushes content (dashboard sidenav); opening/closing animates its width, and the divider border primitive separates it from the page. - `over` \u2014 a native `<dialog>` in the top layer: focus trap, Escape and scrim backdrop come from the platform (same machinery as `uni-dialog`), sliding in from its edge. **The panel is never the scroll container.** It is a flex column of three rows \u2014 an optional `[uni-drawer-header]`, the projected body, an optional `[uni-drawer-buttons]` \u2014 and only the body scrolls. The panel itself is `overflow: clip` on both axes. That is what lets a header and a save bar pin while a long form scrolls between them, and it is why the theme's `padding` option lands on the body row rather than the panel: padding on a scrolling box scrolls away with its content. Surface, width, divider, elevation, padding, backdrop, scrim and background all resolve from `drawer` theme tokens.",
3711
3751
  category: "components",
3712
- version: "11.0.0",
3752
+ version: "11.2.0",
3713
3753
  bindings: {
3714
3754
  angular: {
3715
3755
  importPath: "@uni-design-system/uni-angular",
@@ -3900,7 +3940,7 @@ initialFocus="input"
3900
3940
  summary: "The drawer's pinned footer action row \u2014 the save bar of an editor panel.",
3901
3941
  description: "The drawer's pinned footer action row \u2014 the save bar of an editor panel. Sits outside the scrolling body, so the actions stay reachable however long the form is. Mirrors `[dialog-buttons]`; the difference is posture, which lives in the `drawerButtons` theme options rather than here.",
3902
3942
  category: "primitives",
3903
- version: "11.0.0",
3943
+ version: "11.2.0",
3904
3944
  bindings: {
3905
3945
  angular: {
3906
3946
  importPath: "@uni-design-system/uni-angular",
@@ -3997,7 +4037,7 @@ initialFocus="input"
3997
4037
  summary: "The drawer's pinned header row: a title, and optionally a close button.",
3998
4038
  description: "The drawer's pinned header row: a title, and optionally a close button. Sits outside the scrolling body, so it stays put while the form beneath it moves. Reached either by projecting it \u2014 `<div uni-drawer-header>` \u2014 or implicitly, by giving `uni-drawer` a `headline`, in which case the drawer renders one of these itself.",
3999
4039
  category: "components",
4000
- version: "11.0.0",
4040
+ version: "11.2.0",
4001
4041
  bindings: {
4002
4042
  angular: {
4003
4043
  importPath: "@uni-design-system/uni-angular",
@@ -4045,7 +4085,7 @@ initialFocus="input"
4045
4085
  summary: "",
4046
4086
  description: "",
4047
4087
  category: "components",
4048
- version: "11.0.0",
4088
+ version: "11.2.0",
4049
4089
  bindings: {
4050
4090
  angular: {
4051
4091
  importPath: "@uni-design-system/uni-angular",
@@ -4179,7 +4219,7 @@ initialFocus="input"
4179
4219
  summary: "",
4180
4220
  description: "",
4181
4221
  category: "layout",
4182
- version: "11.0.0",
4222
+ version: "11.2.0",
4183
4223
  bindings: {
4184
4224
  angular: {
4185
4225
  importPath: "@uni-design-system/uni-angular",
@@ -4283,7 +4323,7 @@ initialFocus="input"
4283
4323
  summary: "",
4284
4324
  description: "",
4285
4325
  category: "layout",
4286
- version: "11.0.0",
4326
+ version: "11.2.0",
4287
4327
  bindings: {
4288
4328
  angular: {
4289
4329
  importPath: "@uni-design-system/uni-angular",
@@ -4354,7 +4394,7 @@ initialFocus="input"
4354
4394
  summary: "Trigger for a {@link UniExpandComponent} region.",
4355
4395
  description: "Trigger for a {@link UniExpandComponent} region. Two shapes, chosen by whether `label` is set: - **Icon only** (default) \u2014 the bare chevron, sized for a Card Header. - **Labelled** \u2014 chevron plus a label and optional muted sublabel, as a single full-width button. Most real disclosures need a name for the section, and building that row by hand is the thing this saves; it also makes the label the button's accessible name rather than adjacent text.",
4356
4396
  category: "layout",
4357
- version: "11.0.0",
4397
+ version: "11.2.0",
4358
4398
  bindings: {
4359
4399
  angular: {
4360
4400
  importPath: "@uni-design-system/uni-angular",
@@ -4439,7 +4479,7 @@ initialFocus="input"
4439
4479
  summary: "",
4440
4480
  description: "",
4441
4481
  category: "forms",
4442
- version: "11.0.0",
4482
+ version: "11.2.0",
4443
4483
  bindings: {
4444
4484
  angular: {
4445
4485
  importPath: "@uni-design-system/uni-angular",
@@ -4597,7 +4637,7 @@ initialFocus="input"
4597
4637
  summary: "",
4598
4638
  description: "",
4599
4639
  category: "layout",
4600
- version: "11.0.0",
4640
+ version: "11.2.0",
4601
4641
  bindings: {
4602
4642
  angular: {
4603
4643
  importPath: "@uni-design-system/uni-angular",
@@ -4694,7 +4734,7 @@ initialFocus="input"
4694
4734
  summary: "",
4695
4735
  description: "",
4696
4736
  category: "layout",
4697
- version: "11.0.0",
4737
+ version: "11.2.0",
4698
4738
  bindings: {
4699
4739
  angular: {
4700
4740
  importPath: "@uni-design-system/uni-angular",
@@ -4726,7 +4766,7 @@ initialFocus="input"
4726
4766
  summary: "",
4727
4767
  description: "",
4728
4768
  category: "primitives",
4729
- version: "11.0.0",
4769
+ version: "11.2.0",
4730
4770
  bindings: {
4731
4771
  angular: {
4732
4772
  importPath: "@uni-design-system/uni-angular",
@@ -4786,7 +4826,7 @@ initialFocus="input"
4786
4826
  summary: "",
4787
4827
  description: "",
4788
4828
  category: "primitives",
4789
- version: "11.0.0",
4829
+ version: "11.2.0",
4790
4830
  bindings: {
4791
4831
  angular: {
4792
4832
  importPath: "@uni-design-system/uni-angular",
@@ -4889,7 +4929,7 @@ initialFocus="input"
4889
4929
  summary: "A button that lives inside a sentence.",
4890
4930
  description: "A button that lives inside a sentence. Every other button in the library owns a box. Even `ghost` is a flex box with a hit area, horizontal padding and a type scale of its own, so dropping one into a sentence takes it out of the run of text entirely. This one inherits that type \u2014 family, size, weight, line height and colour \u2014 and adds no padding, so a paragraph reads as one continuous run with an action in the middle of it. It is always a real `<button>`: it can *look* like a link (`underline`, `link`) without claiming to be navigation, so Enter and Space activate it and assistive tech announces a button rather than an unfollowable link. A glyph may sit alongside the label without disturbing that: it is sized in `em`, so it scales with the type it lands in rather than pinning a px size into someone else's paragraph. One limit comes from the element itself. Browsers blockify a `<button>` to `inline-block` whatever `display` it asks for, so the control is an atomic inline box: its own label wraps inside it, but the run cannot split across a line break the way the words around it do. `display: contents` would buy that at the cost of the button being focusable at all, which is not a trade worth making \u2014 keep labels to a few words, which is what an inline action wants anyway.",
4891
4931
  category: "primitives",
4892
- version: "11.0.0",
4932
+ version: "11.2.0",
4893
4933
  bindings: {
4894
4934
  angular: {
4895
4935
  importPath: "@uni-design-system/uni-angular",
@@ -5004,7 +5044,7 @@ initialFocus="input"
5004
5044
  summary: "",
5005
5045
  description: "",
5006
5046
  category: "forms",
5007
- version: "11.0.0",
5047
+ version: "11.2.0",
5008
5048
  bindings: {
5009
5049
  angular: {
5010
5050
  importPath: "@uni-design-system/uni-angular",
@@ -5197,9 +5237,9 @@ initialFocus="input"
5197
5237
  {
5198
5238
  name: "fullWidth",
5199
5239
  kind: "input",
5200
- type: "boolean",
5240
+ type: "unknown",
5201
5241
  required: false,
5202
- default: "false",
5242
+ default: "false, { transform: booleanAttribute }",
5203
5243
  description: ""
5204
5244
  },
5205
5245
  {
@@ -5289,7 +5329,7 @@ initialFocus="input"
5289
5329
  summary: "",
5290
5330
  description: "",
5291
5331
  category: "forms",
5292
- version: "11.0.0",
5332
+ version: "11.2.0",
5293
5333
  bindings: {
5294
5334
  angular: {
5295
5335
  importPath: "@uni-design-system/uni-angular",
@@ -5338,9 +5378,9 @@ initialFocus="input"
5338
5378
  {
5339
5379
  name: "fullWidth",
5340
5380
  kind: "input",
5341
- type: "boolean",
5381
+ type: "unknown",
5342
5382
  required: false,
5343
- default: "false",
5383
+ default: "false, { transform: booleanAttribute }",
5344
5384
  description: ""
5345
5385
  },
5346
5386
  {
@@ -5410,7 +5450,7 @@ initialFocus="input"
5410
5450
  summary: "",
5411
5451
  description: "",
5412
5452
  category: "components",
5413
- version: "11.0.0",
5453
+ version: "11.2.0",
5414
5454
  bindings: {
5415
5455
  angular: {
5416
5456
  importPath: "@uni-design-system/uni-angular",
@@ -5480,7 +5520,7 @@ initialFocus="input"
5480
5520
  summary: "",
5481
5521
  description: "",
5482
5522
  category: "layout",
5483
- version: "11.0.0",
5523
+ version: "11.2.0",
5484
5524
  bindings: {
5485
5525
  angular: {
5486
5526
  importPath: "@uni-design-system/uni-angular",
@@ -5584,7 +5624,7 @@ initialFocus="input"
5584
5624
  summary: "",
5585
5625
  description: "",
5586
5626
  category: "layout",
5587
- version: "11.0.0",
5627
+ version: "11.2.0",
5588
5628
  bindings: {
5589
5629
  angular: {
5590
5630
  importPath: "@uni-design-system/uni-angular",
@@ -5666,7 +5706,7 @@ initialFocus="input"
5666
5706
  summary: "",
5667
5707
  description: "",
5668
5708
  category: "forms",
5669
- version: "11.0.0",
5709
+ version: "11.2.0",
5670
5710
  bindings: {
5671
5711
  angular: {
5672
5712
  importPath: "@uni-design-system/uni-angular",
@@ -5760,7 +5800,7 @@ initialFocus="input"
5760
5800
  summary: "",
5761
5801
  description: "",
5762
5802
  category: "forms",
5763
- version: "11.0.0",
5803
+ version: "11.2.0",
5764
5804
  bindings: {
5765
5805
  angular: {
5766
5806
  importPath: "@uni-design-system/uni-angular",
@@ -5939,7 +5979,7 @@ initialFocus="input"
5939
5979
  summary: "",
5940
5980
  description: "",
5941
5981
  category: "feedback",
5942
- version: "11.0.0",
5982
+ version: "11.2.0",
5943
5983
  bindings: {
5944
5984
  angular: {
5945
5985
  importPath: "@uni-design-system/uni-angular",
@@ -6199,7 +6239,7 @@ initialFocus="input"
6199
6239
  summary: "",
6200
6240
  description: "",
6201
6241
  category: "feedback",
6202
- version: "11.0.0",
6242
+ version: "11.2.0",
6203
6243
  bindings: {
6204
6244
  angular: {
6205
6245
  importPath: "@uni-design-system/uni-angular",
@@ -6223,7 +6263,7 @@ initialFocus="input"
6223
6263
  summary: "Numeric field with locale-aware parsing, `Intl` formatting on commit, prefix/suffix adornments and steppers that hold to repeat.",
6224
6264
  description: 'Numeric field with locale-aware parsing, `Intl` formatting on commit, prefix/suffix adornments and steppers that hold to repeat. Not `<input type="number">`, and the first reason is a data-loss bug: per the HTML value sanitization algorithm, a number input whose text is not a valid floating-point number reports `value === \'\'`. Type `12,50` as most of Europe does, or paste `1,234.56` from a spreadsheet, and the app reads an empty field. This is `type="text"` with `role="spinbutton"`, which is the only way to keep the user\'s malformed text on screen and tell them about it. Chrome comes from `uni-input-box`, so error, disabled and focus states match every other field. All arithmetic runs on the cdk\'s exact decimal helpers.',
6225
6265
  category: "forms",
6226
- version: "11.0.0",
6266
+ version: "11.2.0",
6227
6267
  bindings: {
6228
6268
  angular: {
6229
6269
  importPath: "@uni-design-system/uni-angular",
@@ -6649,7 +6689,7 @@ initialFocus="input"
6649
6689
  summary: "Two linked numeric fields in one chrome, with one `{ start, end }` value \u2014 price filters, thresholds, tolerances.",
6650
6690
  description: "Two linked numeric fields in one chrome, with one `{ start, end }` value \u2014 price filters, thresholds, tolerances. `start`/`end` deliberately match `UniDateRange`, so the library has one range vocabulary, and they avoid colliding with the `min`/`max` **inputs**, which mean the fence rather than the value. It owns its commit path rather than nesting two `uni-number-input`s, because the two behaviours the spec asks for need *different* bounds: a stepper must be fenced at the other end, while a typed commit must reach the parent un-clamped so a backwards range can be swapped instead of destroyed. A child field applies one bound pair to both. The arithmetic, parsing and formatting are still the cdk's, shared with every other numeric control.",
6651
6691
  category: "forms",
6652
- version: "11.0.0",
6692
+ version: "11.2.0",
6653
6693
  bindings: {
6654
6694
  angular: {
6655
6695
  importPath: "@uni-design-system/uni-angular",
@@ -6940,7 +6980,7 @@ initialFocus="input"
6940
6980
  summary: "",
6941
6981
  description: "",
6942
6982
  category: "data",
6943
- version: "11.0.0",
6983
+ version: "11.2.0",
6944
6984
  bindings: {
6945
6985
  angular: {
6946
6986
  importPath: "@uni-design-system/uni-angular",
@@ -7041,7 +7081,7 @@ initialFocus="input"
7041
7081
  summary: "Anchored, top-layer panel on the native `popover` element \u2014 the browser owns positioning (CSS anchor positioning), stacking, and, in rich mode, light dismissal.",
7042
7082
  description: 'Anchored, top-layer panel on the native `popover` element \u2014 the browser owns positioning (CSS anchor positioning), stacking, and, in rich mode, light dismissal. Two modes share one implementation: - `rich` (default): a click-toggled disclosure. The projected `[trigger]` carries `aria-expanded`/`aria-controls`; with no trigger content the app drives `open` and no element claims controller ARIA. Focus stays on the trigger (APG disclosure) unless the panel marks an `[autofocus]` field, and returns to the trigger on close. - `tooltip`: hover/focus-triggered, `role="tooltip"` + `aria-describedby`, WCAG 1.4.13 dismissable/hoverable/persistent. Content must not contain focusable elements. Anchoring and control are separate: `anchor` re-anchors the panel to any element (or id) while the trigger keeps the disclosure semantics.',
7043
7083
  category: "layout",
7044
- version: "11.0.0",
7084
+ version: "11.2.0",
7045
7085
  bindings: {
7046
7086
  angular: {
7047
7087
  importPath: "@uni-design-system/uni-angular",
@@ -7233,7 +7273,7 @@ initialFocus="input"
7233
7273
  summary: "",
7234
7274
  description: "",
7235
7275
  category: "feedback",
7236
- version: "11.0.0",
7276
+ version: "11.2.0",
7237
7277
  bindings: {
7238
7278
  angular: {
7239
7279
  importPath: "@uni-design-system/uni-angular",
@@ -7297,7 +7337,7 @@ initialFocus="input"
7297
7337
  summary: "",
7298
7338
  description: "",
7299
7339
  category: "feedback",
7300
- version: "11.0.0",
7340
+ version: "11.2.0",
7301
7341
  bindings: {
7302
7342
  angular: {
7303
7343
  importPath: "@uni-design-system/uni-angular",
@@ -7369,7 +7409,7 @@ initialFocus="input"
7369
7409
  summary: "`\u2212 3 +` for cart lines, table cells and seat counts: the numeric core with no field chrome, no label and no room for either.",
7370
7410
  description: '`\u2212 3 +` for cart lines, table cells and seat counts: the numeric core with no field chrome, no label and no room for either. A separate component rather than a `chrome="bare"` flag on `uni-number-input`, because this control is defined by what it does *not* have \u2014 presets, affixes, expressions, four stepper layouts \u2014 and eight inputs are easier to write correctly than forty with a list of which ones to leave alone. The arithmetic, parsing and hold-to-repeat are the cdk\'s, shared with the field, so `1,200` and the keyboard map behave identically in both. The middle stays a real input by default: typing `12` beats tapping `+` eleven times. `editable=false` is for read-mostly tables.',
7371
7411
  category: "forms",
7372
- version: "11.0.0",
7412
+ version: "11.2.0",
7373
7413
  bindings: {
7374
7414
  angular: {
7375
7415
  importPath: "@uni-design-system/uni-angular",
@@ -7572,7 +7612,7 @@ initialFocus="input"
7572
7612
  summary: "",
7573
7613
  description: "",
7574
7614
  category: "forms",
7575
- version: "11.0.0",
7615
+ version: "11.2.0",
7576
7616
  bindings: {
7577
7617
  angular: {
7578
7618
  importPath: "@uni-design-system/uni-angular",
@@ -7662,6 +7702,22 @@ initialFocus="input"
7662
7702
  required: false,
7663
7703
  description: ""
7664
7704
  },
7705
+ {
7706
+ name: "labelHidden",
7707
+ kind: "input",
7708
+ type: "unknown",
7709
+ required: false,
7710
+ default: "false, { transform: booleanAttribute }",
7711
+ description: "Keep `label` as the group's accessible name, but do not draw it. The heading still carries the id `aria-labelledby` points at, so the group keeps its name; only the option rows are visible."
7712
+ },
7713
+ {
7714
+ name: "fullWidth",
7715
+ kind: "input",
7716
+ type: "unknown",
7717
+ required: false,
7718
+ default: "false, { transform: booleanAttribute }",
7719
+ description: "Stretch each option row across the container, so the whole width is the hit target."
7720
+ },
7665
7721
  {
7666
7722
  name: "name",
7667
7723
  kind: "input",
@@ -7740,29 +7796,68 @@ initialFocus="input"
7740
7796
  title: "Form Signals",
7741
7797
  framework: "angular",
7742
7798
  code: '<!-- Storybook story: Form Signals -->\n<div>\n<uni-radio\n label="Select your choice"\n variant="primary"\n [options]="options"\n [value]="currentValue"\n [disabled]="isDisabled"\n [invalid]="isInvalid"\n [touched]="isTouched"\n name="signals-demo">\n</uni-radio>\n<div box-layout paddingTop="md">\n <p>Value: {{ currentValue }}</p>\n <p>Touched: {{ isTouched }}</p>\n <p>Invalid: {{ isInvalid }}</p>\n <p>Disabled: {{ isDisabled }}</p>\n</div>\n<div row-layout gap="sm" paddingTop="sm">\n<button\n (click)="isTouched = true">\n Mark as Touched\n</button>\n<button\n (click)="isDisabled = !isDisabled">\n Toggle Disabled\n</button>\n<button\n (click)="isInvalid = !isInvalid">\n Toggle Invalid\n</button>\n</div>\n</div>'
7799
+ },
7800
+ {
7801
+ title: "Option Template",
7802
+ framework: "angular",
7803
+ code: '<!-- Storybook story: Option Template -->\n<uni-radio [options]="plans" value="pro" label="Plan" fullWidth>\n<ng-template uniRadioOption let-option>\n <span stack-layout gap="xxs">\n <span uni-text="body-2-long">{{ option.label }}</span>\n <span uni-text="caption">{{ descriptions[option.value] }}</span>\n </span>\n</ng-template>\n</uni-radio>'
7804
+ },
7805
+ {
7806
+ title: "Sizes",
7807
+ framework: "angular",
7808
+ code: '<!-- Storybook story: Sizes -->\n<div stack-layout gap="lg">\n<uni-radio size="sm" label="Small" [options]="options" value="sm"></uni-radio>\n<uni-radio size="md" label="Medium" [options]="options" value="sm"></uni-radio>\n<uni-radio size="lg" label="Large" [options]="options" value="sm"></uni-radio>\n</div>'
7743
7809
  }
7744
7810
  ],
7745
7811
  guidelines: {
7746
- whenToUse: "Single choice among a small set of visible options. The component renders the whole\ngroup from an `options` array (`{ label, value, disabled? }`), implements\n`FormValueControl<string>` for Signal Forms, and defaults each group to a unique\n`name` so multiple groups on a page never collide.\nRing, fill, and size resolve from the `radio` theme options; the selected dot wears\nthe active `variant` color.",
7812
+ whenToUse: "Single choice among a small set of visible options. The component renders the whole\ngroup from an `options` array (`{ label, value, disabled? }`), implements\n`FormValueControl<string>` for Signal Forms, and defaults each group to a unique\n`name` so multiple groups on a page never collide.\nRing and fill resolve from the `radio` theme options and the selected dot wears the\nactive `variant` color; circle geometry comes from the theme's `sizes` block. The\ngroup heading and the option labels have separate type roles (`groupTextRole` and\n`textRole`), so a group can have a hierarchy rather than two levels at one size.\n`value` is a `model`. Bind it two-way, or hold it in your own signal: a change your\napp declines does not snap back on its own, because a one-way `[value]` expression\nhas not changed and Angular writes nothing.",
7747
7813
  dos: [
7748
- "Use for 2\u20135 mutually exclusive options that should all be visible"
7814
+ "Use for 2\u20135 mutually exclusive options that should all be visible",
7815
+ "Give the heading and the options different type roles, so the group reads as one"
7749
7816
  ],
7750
7817
  donts: [
7751
- "Use for long option lists \u2014 that is a select"
7818
+ "Use for long option lists \u2014 that is a select",
7819
+ "Bind `[value]` one way and expect a declined change to snap back"
7752
7820
  ],
7753
7821
  accessibility: [
7754
7822
  '`role="radiogroup"` labelled by the group label via `aria-labelledby`.',
7823
+ "`labelHidden` hides that heading visually while it still names the group.",
7755
7824
  "Native radio inputs: arrow keys move selection within the group, Tab moves out.",
7756
7825
  "`aria-invalid` reflects the error state once the group was touched or is dirty."
7757
7826
  ]
7758
7827
  },
7759
7828
  relatedTokens: [
7760
- "radio.behavior.size",
7761
7829
  "radio.behavior.ringColor",
7762
7830
  "radio.behavior.fillColor",
7763
- "radio.behavior.motion"
7831
+ "radio.behavior.motion",
7832
+ "radio.behavior.textRole",
7833
+ "radio.behavior.groupTextRole",
7834
+ "radio.behavior.gap"
7764
7835
  ]
7765
7836
  },
7837
+ {
7838
+ id: "radio-option",
7839
+ name: "RadioOption",
7840
+ status: "stable",
7841
+ summary: "Content for each of a radio group's options, in place of the plain label string.",
7842
+ description: 'Content for each of a radio group\'s options, in place of the plain label string. A group renders N labels from an `options` array, so it cannot take `<ng-content />` the way `uni-checkbox` and `uni-toggle` do \u2014 there is no single slot to project into. A template is the shape that survives the repetition: it is instantiated once per option and handed that option, so a row can carry a description, a price or a badge and still be part of the `<label>` that drives the input. ```html <uni-radio [options]="plans"> <ng-template uniRadioOption let-option> <span uni-text="body-2-long">{{ option.label }}</span> </ng-template> </uni-radio> ```',
7843
+ category: "forms",
7844
+ version: "11.2.0",
7845
+ bindings: {
7846
+ angular: {
7847
+ importPath: "@uni-design-system/uni-angular",
7848
+ selectorOrTag: "ng-template[uniRadioOption]",
7849
+ api: []
7850
+ }
7851
+ },
7852
+ examples: [],
7853
+ guidelines: {
7854
+ whenToUse: "",
7855
+ dos: [],
7856
+ donts: [],
7857
+ accessibility: []
7858
+ },
7859
+ relatedTokens: []
7860
+ },
7766
7861
  {
7767
7862
  id: "ripple",
7768
7863
  name: "Ripple",
@@ -7770,7 +7865,7 @@ initialFocus="input"
7770
7865
  summary: "",
7771
7866
  description: "",
7772
7867
  category: "directives",
7773
- version: "11.0.0",
7868
+ version: "11.2.0",
7774
7869
  bindings: {
7775
7870
  angular: {
7776
7871
  importPath: "@uni-design-system/uni-angular",
@@ -7794,7 +7889,7 @@ initialFocus="input"
7794
7889
  summary: "",
7795
7890
  description: "",
7796
7891
  category: "layout",
7797
- version: "11.0.0",
7892
+ version: "11.2.0",
7798
7893
  bindings: {
7799
7894
  angular: {
7800
7895
  importPath: "@uni-design-system/uni-angular",
@@ -7849,7 +7944,7 @@ initialFocus="input"
7849
7944
  summary: "Native scroll area with theme-driven scrollbar styling.",
7850
7945
  description: "Native scroll area with theme-driven scrollbar styling. Scrollbars are styled entirely through Emotion-generated CSS: `::-webkit-scrollbar` rules cover Chromium/Safari, with a `scrollbar-width`/`scrollbar-color` fallback for Firefox.",
7851
7946
  category: "layout",
7852
- version: "11.0.0",
7947
+ version: "11.2.0",
7853
7948
  bindings: {
7854
7949
  angular: {
7855
7950
  importPath: "@uni-design-system/uni-angular",
@@ -7996,7 +8091,7 @@ initialFocus="input"
7996
8091
  summary: "Generic search field: shared input chrome with a leading magnifier, a clear button while there's a query, Enter to submit, and optional type-ahead \u2014 pass `suggestions` (refresh them from `change`) and the field becomes an ARIA combobox with a keyboard-navigable listbox.",
7997
8092
  description: "Generic search field: shared input chrome with a leading magnifier, a clear button while there's a query, Enter to submit, and optional type-ahead \u2014 pass `suggestions` (refresh them from `change`) and the field becomes an ARIA combobox with a keyboard-navigable listbox. Escape closes the list first, then clears the query.",
7998
8093
  category: "forms",
7999
- version: "11.0.0",
8094
+ version: "11.2.0",
8000
8095
  bindings: {
8001
8096
  angular: {
8002
8097
  importPath: "@uni-design-system/uni-angular",
@@ -8123,7 +8218,7 @@ initialFocus="input"
8123
8218
  summary: "",
8124
8219
  description: "",
8125
8220
  category: "forms",
8126
- version: "11.0.0",
8221
+ version: "11.2.0",
8127
8222
  bindings: {
8128
8223
  angular: {
8129
8224
  importPath: "@uni-design-system/uni-angular",
@@ -8210,9 +8305,9 @@ initialFocus="input"
8210
8305
  {
8211
8306
  name: "fullWidth",
8212
8307
  kind: "input",
8213
- type: "boolean",
8308
+ type: "unknown",
8214
8309
  required: false,
8215
- default: "false",
8310
+ default: "false, { transform: booleanAttribute }",
8216
8311
  description: ""
8217
8312
  },
8218
8313
  {
@@ -8293,7 +8388,7 @@ initialFocus="input"
8293
8388
  summary: "Loading placeholder painted with surface tokens.",
8294
8389
  description: "Loading placeholder painted with surface tokens. `text` renders one or more lines (the last line shortened, as real text would be), `rect` and `circle` render fixed shapes. The shimmer only animates when the user allows motion; it degrades to static blocks under `prefers-reduced-motion`. Color and radius are theme options with per-instance overrides, because one app routinely needs several: skeletons on a card and on the page background want different tints, and a pill placeholder wants a different corner than the text bars beside it.",
8295
8390
  category: "components",
8296
- version: "11.0.0",
8391
+ version: "11.2.0",
8297
8392
  bindings: {
8298
8393
  angular: {
8299
8394
  importPath: "@uni-design-system/uni-angular",
@@ -8444,7 +8539,7 @@ initialFocus="input"
8444
8539
  summary: "Bounded numeric input by pointer, for values where the *position* is the information: volume, opacity, weightings, price filters.",
8445
8540
  description: "Bounded numeric input by pointer, for values where the *position* is the information: volume, opacity, weightings, price filters. Custom thumbs rather than `<input type=\"range\">`, which the previous version used: one native range input cannot carry two thumbs, marks or a tooltip, and a second component for the range case would mean two keyboard maps to keep in step. The step model and the keyboard map are the cdk's, shared with the numeric fields, so nothing new is learned moving between them. All arithmetic on values runs through the cdk's exact decimal helpers \u2014 stepping `0.1` never yields `0.30000000000000004`. Only pointer *positions* use floats, and they are snapped to the grid before becoming a value.",
8446
8541
  category: "forms",
8447
- version: "11.0.0",
8542
+ version: "11.2.0",
8448
8543
  bindings: {
8449
8544
  angular: {
8450
8545
  importPath: "@uni-design-system/uni-angular",
@@ -8718,7 +8813,7 @@ initialFocus="input"
8718
8813
  summary: "",
8719
8814
  description: "",
8720
8815
  category: "feedback",
8721
- version: "11.0.0",
8816
+ version: "11.2.0",
8722
8817
  bindings: {
8723
8818
  angular: {
8724
8819
  importPath: "@uni-design-system/uni-angular",
@@ -8830,7 +8925,7 @@ initialFocus="input"
8830
8925
  summary: "",
8831
8926
  description: "",
8832
8927
  category: "data",
8833
- version: "11.0.0",
8928
+ version: "11.2.0",
8834
8929
  bindings: {
8835
8930
  angular: {
8836
8931
  importPath: "@uni-design-system/uni-angular",
@@ -8889,7 +8984,7 @@ initialFocus="input"
8889
8984
  summary: "",
8890
8985
  description: "",
8891
8986
  category: "layout",
8892
- version: "11.0.0",
8987
+ version: "11.2.0",
8893
8988
  bindings: {
8894
8989
  angular: {
8895
8990
  importPath: "@uni-design-system/uni-angular",
@@ -8944,7 +9039,7 @@ initialFocus="input"
8944
9039
  summary: 'KPI stat tile: muted label, large value (numbers auto-compact: 48234 \u2192 "48.2K"), an optional delta whose ink is decided by *direction \xD7 whether up is good* (never direction alone \u2014 churn going down is good news), and an optional decorative sparkline.',
8945
9040
  description: 'KPI stat tile: muted label, large value (numbers auto-compact: 48234 \u2192 "48.2K"), an optional delta whose ink is decided by *direction \xD7 whether up is good* (never direction alone \u2014 churn going down is good news), and an optional decorative sparkline. Frame, typography and inks are all `stat` theme tokens.',
8946
9041
  category: "components",
8947
- version: "11.0.0",
9042
+ version: "11.2.0",
8948
9043
  bindings: {
8949
9044
  angular: {
8950
9045
  importPath: "@uni-design-system/uni-angular",
@@ -9062,7 +9157,7 @@ initialFocus="input"
9062
9157
  summary: "",
9063
9158
  description: "",
9064
9159
  category: "primitives",
9065
- version: "11.0.0",
9160
+ version: "11.2.0",
9066
9161
  bindings: {
9067
9162
  angular: {
9068
9163
  importPath: "@uni-design-system/uni-angular",
@@ -9151,7 +9246,7 @@ initialFocus="input"
9151
9246
  summary: "One tab inside `uni-tabs`: a label for the tablist plus projected panel content.",
9152
9247
  description: "One tab inside `uni-tabs`: a label for the tablist plus projected panel content. Content is captured in a template and only instantiated while the tab is selected, so inactive panels cost nothing.",
9153
9248
  category: "components",
9154
- version: "11.0.0",
9249
+ version: "11.2.0",
9155
9250
  bindings: {
9156
9251
  angular: {
9157
9252
  importPath: "@uni-design-system/uni-angular",
@@ -9191,7 +9286,7 @@ initialFocus="input"
9191
9286
  summary: "WAI-ARIA tabs: roving tabindex, automatic activation (arrow keys move focus and select), Home/End, disabled tabs skipped.",
9192
9287
  description: "WAI-ARIA tabs: roving tabindex, automatic activation (arrow keys move focus and select), Home/End, disabled tabs skipped. Only the selected panel is instantiated. All styling resolves from `tabs` theme option tokens.",
9193
9288
  category: "components",
9194
- version: "11.0.0",
9289
+ version: "11.2.0",
9195
9290
  bindings: {
9196
9291
  angular: {
9197
9292
  importPath: "@uni-design-system/uni-angular",
@@ -9270,9 +9365,9 @@ initialFocus="input"
9270
9365
  name: "Tag",
9271
9366
  status: "stable",
9272
9367
  summary: "Compact chip for categories, states, filters and tokens.",
9273
- description: "Compact chip for categories, states, filters and tokens. Two orthogonal style axes: `variant` picks the colour role and `tone` picks the archetype (soft / solid / outline). Both live in the theme's `tag` entry, so a theme restyles every chip in the app without touching markup. Structurally a chip is **body + trailing action as siblings**, never nested buttons: an interactive chip whose body is a `<button>` cannot contain the remove `<button>` (invalid HTML, and the inner control becomes unreachable for keyboard users). Both stay independently operable.",
9368
+ description: "Compact chip for categories, states, filters and tokens. Two orthogonal style axes: `variant` picks the colour role and `tone` picks the archetype (soft / solid / outline). Both live in the theme's `tag` entry, so a theme restyles every chip in the app without touching markup. Two state classes ride alongside the tone class for the same reason: `tag-selected` and `tag-interactive` let the theme paint selection and hover beside the colours they belong to, instead of an app swapping `tone` by hand on every chip. Structurally a chip is **body + trailing action as siblings**, never nested buttons: an interactive chip whose body is a `<button>` cannot contain the remove `<button>` (invalid HTML, and the inner control becomes unreachable for keyboard users). Both stay independently operable.",
9274
9369
  category: "data",
9275
- version: "11.0.0",
9370
+ version: "11.2.0",
9276
9371
  bindings: {
9277
9372
  angular: {
9278
9373
  importPath: "@uni-design-system/uni-angular",
@@ -9304,7 +9399,7 @@ initialFocus="input"
9304
9399
  {
9305
9400
  name: "value",
9306
9401
  kind: "input",
9307
- type: "string | number",
9402
+ type: "T",
9308
9403
  required: false,
9309
9404
  description: ""
9310
9405
  },
@@ -9404,19 +9499,19 @@ initialFocus="input"
9404
9499
  type: "number",
9405
9500
  required: false,
9406
9501
  default: "0",
9407
- description: "Tab position of the chip's controls. A composite that owns its own roving focus \u2014 `uni-tag-input`, where the whole field is one tab stop \u2014 passes `-1` so Tab does not walk through every chip to reach the next control."
9502
+ description: "Tab position of the chip's controls. A composite that owns its own roving focus \u2014 `uni-tag-input` or `uni-tag-group`, where the whole thing is one tab stop \u2014 passes `-1` so Tab does not walk through every chip to reach the next control."
9408
9503
  },
9409
9504
  {
9410
9505
  name: "removed",
9411
9506
  kind: "output",
9412
- type: "UniTagValue",
9507
+ type: "T | undefined",
9413
9508
  required: false,
9414
9509
  description: ""
9415
9510
  },
9416
9511
  {
9417
9512
  name: "activated",
9418
9513
  kind: "output",
9419
- type: "UniTagValue",
9514
+ type: "T | undefined",
9420
9515
  required: false,
9421
9516
  description: ""
9422
9517
  },
@@ -9519,7 +9614,25 @@ initialFocus="input"
9519
9614
  }
9520
9615
  </div>
9521
9616
  <!-- args:
9522
- { interactive: true, selected: true, tone: 'solid' }
9617
+ { interactive: true, selected: true }
9618
+ -->`
9619
+ },
9620
+ {
9621
+ title: "Toggles",
9622
+ framework: "angular",
9623
+ code: `<!-- Storybook story: Toggles -->
9624
+ <div row-layout gap="sm" alignItems="center" style="flex-wrap:wrap">
9625
+ @for (facet of ['Design', 'Engineering', 'Legal', 'Finance']; track facet) {
9626
+ <uni-tag
9627
+ [interactive]="true"
9628
+ [label]="facet"
9629
+ [selected]="picked.includes(facet)"
9630
+ (activated)="toggle(facet)"
9631
+ />
9632
+ }
9633
+ </div>
9634
+ <!-- args:
9635
+ { label: 'nope@@x', invalid: true, removable: true, variant: 'warn' }
9523
9636
  -->`
9524
9637
  },
9525
9638
  {
@@ -9610,6 +9723,7 @@ initialFocus="input"
9610
9723
  "A static tag is **text, not a widget**: no role, no tab stop. Screen readers",
9611
9724
  "`removable` adds one real `<button>` named `Remove {label}`, overridable with",
9612
9725
  '`interactive` makes the body a `<button type="button">`; `selected` maps to',
9726
+ "`controlTabIndex` lets a composite that owns its own roving focus \u2014",
9613
9727
  "Icons, avatars, initials and dots are `aria-hidden` \u2014 the chip's text is its",
9614
9728
  "`invalid` sets `aria-invalid` and adds a non-colour cue."
9615
9729
  ]
@@ -9620,9 +9734,208 @@ initialFocus="input"
9620
9734
  "tag.behavior.gap",
9621
9735
  "tag.behavior.removeIcon",
9622
9736
  "tag.behavior.selectedIcon",
9623
- "tag.behavior.motion"
9737
+ "tag.behavior.motion",
9738
+ "tag.behavior.endInset"
9624
9739
  ]
9625
9740
  },
9741
+ {
9742
+ id: "tag-group",
9743
+ name: "TagGroup",
9744
+ status: "stable",
9745
+ summary: "A row of chips the user picks from: filters, facets, a set of labels.",
9746
+ description: "A row of chips the user picks from: filters, facets, a set of labels. The group **renders its own chips** from `items` rather than projecting them. That is what lets it own the things an app was otherwise wiring per chip \u2014 which one is selected, which one holds the tab stop \u2014 since a parent cannot write a projected child's inputs. `uni-tag` is unchanged by this component; `controlTabIndex` was already its hook for a composite that manages focus. Keyboard model is the toolbar pattern: one tab stop for the whole group, and the arrow keys move within it. Twelve filter chips should cost one Tab to pass, not twelve.",
9747
+ category: "data",
9748
+ version: "11.2.0",
9749
+ bindings: {
9750
+ angular: {
9751
+ importPath: "@uni-design-system/uni-angular",
9752
+ selectorOrTag: "uni-tag-group",
9753
+ api: [
9754
+ {
9755
+ name: "items",
9756
+ kind: "input",
9757
+ type: "readonly UniTagGroupItem<T>[]",
9758
+ required: false,
9759
+ default: "[]",
9760
+ description: ""
9761
+ },
9762
+ {
9763
+ name: "layout",
9764
+ kind: "input",
9765
+ type: "'wrap' | 'justify'",
9766
+ required: false,
9767
+ default: "'wrap'",
9768
+ description: "`wrap` lets chips keep their natural widths. `justify` stretches each row flush to both edges; once the group wraps, the last row is left ragged, the way justified text leaves its last line alone. Chips put the extra width around their labels, so tucked leads and remove controls stay tucked. Whether a group has wrapped is measured, not configured: the same group is one row on a desktop and three on a phone, so a flag would be wrong at one of those widths."
9769
+ },
9770
+ {
9771
+ name: "multiple",
9772
+ kind: "input",
9773
+ type: "boolean",
9774
+ required: false,
9775
+ default: "false",
9776
+ description: "Allow more than one chip at a time. Single select replaces instead."
9777
+ },
9778
+ {
9779
+ name: "value",
9780
+ kind: "model",
9781
+ type: "T[]",
9782
+ required: false,
9783
+ default: "[]",
9784
+ description: "The selected values \u2014 one array in both modes, as `uni-tag-input` keeps one array for its items. Single select simply never holds more than one, which spares callers a `T[] | T | null` union at every call site."
9785
+ },
9786
+ {
9787
+ name: "activated",
9788
+ kind: "output",
9789
+ type: "T | undefined",
9790
+ required: false,
9791
+ description: "The chip the user just activated, selected or deselected."
9792
+ },
9793
+ {
9794
+ name: "removed",
9795
+ kind: "output",
9796
+ type: "T | undefined",
9797
+ required: false,
9798
+ description: ""
9799
+ },
9800
+ {
9801
+ name: "chipTone",
9802
+ kind: "input",
9803
+ type: "TagTone | undefined",
9804
+ required: false,
9805
+ default: "undefined",
9806
+ description: ""
9807
+ },
9808
+ {
9809
+ name: "chipSize",
9810
+ kind: "input",
9811
+ type: "Size | undefined",
9812
+ required: false,
9813
+ default: "undefined",
9814
+ description: ""
9815
+ },
9816
+ {
9817
+ name: "chipVariant",
9818
+ kind: "input",
9819
+ type: "Variant | undefined",
9820
+ required: false,
9821
+ default: "undefined",
9822
+ description: ""
9823
+ },
9824
+ {
9825
+ name: "variant",
9826
+ kind: "input",
9827
+ type: "Variant",
9828
+ required: false,
9829
+ default: "'primary'",
9830
+ description: "Style archetype from the theme. Inherited from BaseComponent."
9831
+ },
9832
+ {
9833
+ name: "size",
9834
+ kind: "input",
9835
+ type: "Size",
9836
+ required: false,
9837
+ default: "'lg'",
9838
+ description: "Size token from the theme. Inherited from BaseComponent. Only has an effect where the component theme defines a `sizes` block."
9839
+ }
9840
+ ]
9841
+ }
9842
+ },
9843
+ examples: [
9844
+ {
9845
+ title: "Wrap",
9846
+ framework: "angular",
9847
+ code: `<!-- Storybook story: Wrap -->
9848
+ <div style="max-width: 520px">
9849
+ <uni-tag-group
9850
+ [items]="items"
9851
+ [value]="value"
9852
+ [multiple]="multiple"
9853
+ layout="justify"
9854
+ />
9855
+ </div>
9856
+ <!-- args:
9857
+ { value: ['engineering'] }
9858
+ -->`
9859
+ },
9860
+ {
9861
+ title: "Justify",
9862
+ framework: "angular",
9863
+ code: `<!-- Storybook story: Justify -->
9864
+ <div style="max-width: 520px">
9865
+ <uni-tag-group
9866
+ [items]="items"
9867
+ [value]="value"
9868
+ [multiple]="multiple"
9869
+ layout="justify"
9870
+ />
9871
+ </div>
9872
+ <!-- args:
9873
+ { layout: 'justify', value: ['design', 'security'] }
9874
+ -->`
9875
+ },
9876
+ {
9877
+ title: "Single Select",
9878
+ framework: "angular",
9879
+ code: `<!-- Storybook story: Single Select -->
9880
+ <div style="max-width: 520px">
9881
+ <uni-tag-group
9882
+ [items]="items"
9883
+ [value]="value"
9884
+ [multiple]="multiple"
9885
+ layout="justify"
9886
+ />
9887
+ </div>
9888
+ <!-- args:
9889
+ { multiple: false, value: ['legal'] }
9890
+ -->`
9891
+ },
9892
+ {
9893
+ title: "Mixed Chips",
9894
+ framework: "angular",
9895
+ code: `<!-- Storybook story: Mixed Chips -->
9896
+ <div style="max-width: 520px">
9897
+ <uni-tag-group
9898
+ [items]="items"
9899
+ [value]="value"
9900
+ [multiple]="multiple"
9901
+ layout="justify"
9902
+ />
9903
+ </div>
9904
+ <!-- args:
9905
+ {
9906
+ items: [
9907
+ { label: 'Starred', value: 'starred', iconName: 'star' },
9908
+ { label: 'Live', value: 'live', dot: true, variant: 'success' },
9909
+ { label: 'Alice Chen', value: 'alice', avatarName: 'Alice Chen' },
9910
+ { label: 'Blocked', value: 'blocked', variant: 'warn', tone: 'outline' },
9911
+ { label: 'Archived', value: 'archived', disabled: true },
9912
+ { label: 'Custom', value: 'custom', removable: true },
9913
+ ],
9914
+ }
9915
+ -->`
9916
+ }
9917
+ ],
9918
+ guidelines: {
9919
+ whenToUse: "A row of chips the user picks from: filters, facets, a set of labels. The group\nowns the two things an app was otherwise wiring on every chip \u2014 **which one is\nselected** and **which one holds the tab stop**.",
9920
+ dos: [
9921
+ "Give the group `multiple` when the filters combine, and leave it off when they",
9922
+ 'Reach for `layout="justify"` when the chips should read as a block rather than',
9923
+ "Put per-chip differences on the item (`variant`, `tone`, `iconName`)."
9924
+ ],
9925
+ donts: [
9926
+ "Don't bind `[selected]` on chips inside a group \u2014 that is the thing the group",
9927
+ "Don't use it for an open set the user types into; that is `uni-tag-input`.",
9928
+ "Don't reach for it as a generic chip row. With no selection to own it is just"
9929
+ ],
9930
+ accessibility: [
9931
+ '`role="toolbar"` with `aria-orientation="horizontal"`: one tab stop for the',
9932
+ "Arrow keys move within the group. Both axes work, because the row wraps \u2014",
9933
+ "The tab stop follows the last chip the user picked, so tabbing back into the",
9934
+ "Each chip announces its own state through `aria-pressed`, since each one is an"
9935
+ ]
9936
+ },
9937
+ relatedTokens: []
9938
+ },
9626
9939
  {
9627
9940
  id: "tag-input",
9628
9941
  name: "TagInput",
@@ -9630,7 +9943,7 @@ initialFocus="input"
9630
9943
  summary: "",
9631
9944
  description: "",
9632
9945
  category: "forms",
9633
- version: "11.0.0",
9946
+ version: "11.2.0",
9634
9947
  bindings: {
9635
9948
  angular: {
9636
9949
  importPath: "@uni-design-system/uni-angular",
@@ -9971,7 +10284,7 @@ placeholder="Name or address\u2026"
9971
10284
  summary: "The typography primitive, applied as an attribute to any element so semantics stay yours.",
9972
10285
  description: 'The typography primitive, applied as an attribute to any element so semantics stay yours. The attribute value is the typeface: `<h1 uni-text="display-small">`, `<span uni-text="caption">`, dynamic via `[uni-text]="role()"`. With no value, the typeface is inferred from the host element (h1 \u2192 headline-large, p \u2192 body-1-long, \u2026), falling back to `title-small`; the `typeface` input remains as an explicit override. It is a directive, so it stacks with anything else on the element \u2014 a layout attribute (`<div stack-layout uni-text="title-small">`) or a component\'s own host \u2014 rather than colliding with it.',
9973
10286
  category: "primitives",
9974
- version: "11.0.0",
10287
+ version: "11.2.0",
9975
10288
  bindings: {
9976
10289
  angular: {
9977
10290
  importPath: "@uni-design-system/uni-angular",
@@ -10073,7 +10386,7 @@ placeholder="Name or address\u2026"
10073
10386
  summary: "",
10074
10387
  description: "",
10075
10388
  category: "forms",
10076
- version: "11.0.0",
10389
+ version: "11.2.0",
10077
10390
  bindings: {
10078
10391
  angular: {
10079
10392
  importPath: "@uni-design-system/uni-angular",
@@ -10221,9 +10534,9 @@ placeholder="Name or address\u2026"
10221
10534
  {
10222
10535
  name: "fullWidth",
10223
10536
  kind: "input",
10224
- type: "boolean",
10537
+ type: "unknown",
10225
10538
  required: false,
10226
- default: "false",
10539
+ default: "false, { transform: booleanAttribute }",
10227
10540
  description: ""
10228
10541
  },
10229
10542
  {
@@ -10280,7 +10593,7 @@ placeholder="Name or address\u2026"
10280
10593
  summary: "",
10281
10594
  description: "",
10282
10595
  category: "primitives",
10283
- version: "11.0.0",
10596
+ version: "11.2.0",
10284
10597
  bindings: {
10285
10598
  angular: {
10286
10599
  importPath: "@uni-design-system/uni-angular",
@@ -10310,7 +10623,7 @@ placeholder="Name or address\u2026"
10310
10623
  summary: "Drop-in switcher for the themes registered via `UNI_THEMES`: a select over `ThemeService.themeOptions` that applies the choice through `selectTheme` (so it persists across reloads like any other theme selection).",
10311
10624
  description: "Drop-in switcher for the themes registered via `UNI_THEMES`: a select over `ThemeService.themeOptions` that applies the choice through `selectTheme` (so it persists across reloads like any other theme selection).",
10312
10625
  category: "components",
10313
- version: "11.0.0",
10626
+ version: "11.2.0",
10314
10627
  bindings: {
10315
10628
  angular: {
10316
10629
  importPath: "@uni-design-system/uni-angular",
@@ -10368,7 +10681,7 @@ placeholder="Name or address\u2026"
10368
10681
  summary: "Time field: a combobox over time options \u2014 the same listbox contract as uni-search-input and uni-tag-input.",
10369
10682
  description: "Time field: a combobox over time options \u2014 the same listbox contract as uni-search-input and uni-tag-input. Type `3p`, `930` or `15:00`, or pick `3:00 PM` from the list; the form always gets 24-hour `'HH:mm'` (`hour12` affects display only). The list is assistive, not exhaustive: any parseable time commits, unless `slots` pins the choices (a slot picker) \u2014 then a typed time must match one. Unreadable or unavailable text stays in the field, flagged, with a `rejected` event.",
10370
10683
  category: "forms",
10371
- version: "11.0.0",
10684
+ version: "11.2.0",
10372
10685
  bindings: {
10373
10686
  angular: {
10374
10687
  importPath: "@uni-design-system/uni-angular",
@@ -10583,7 +10896,7 @@ placeholder="Name or address\u2026"
10583
10896
  summary: "",
10584
10897
  description: "",
10585
10898
  category: "forms",
10586
- version: "11.0.0",
10899
+ version: "11.2.0",
10587
10900
  bindings: {
10588
10901
  angular: {
10589
10902
  importPath: "@uni-design-system/uni-angular",
@@ -10658,6 +10971,22 @@ placeholder="Name or address\u2026"
10658
10971
  required: false,
10659
10972
  description: ""
10660
10973
  },
10974
+ {
10975
+ name: "labelHidden",
10976
+ kind: "input",
10977
+ type: "unknown",
10978
+ required: false,
10979
+ default: "false, { transform: booleanAttribute }",
10980
+ description: "Keep `label` as the accessible name, but do not draw it \u2014 for a switch in a row or a table cell whose meaning is already on screen beside it. The string stays in the DOM, so a test querying by label text and a screen reader see the same words."
10981
+ },
10982
+ {
10983
+ name: "fullWidth",
10984
+ kind: "input",
10985
+ type: "unknown",
10986
+ required: false,
10987
+ default: "false, { transform: booleanAttribute }",
10988
+ description: "Stretch the control and its label across the container, making the whole width the hit target. Pair it with projected content for a clickable row."
10989
+ },
10661
10990
  {
10662
10991
  name: "checkedColor",
10663
10992
  kind: "input",
@@ -10784,18 +11113,32 @@ placeholder="Name or address\u2026"
10784
11113
  title: "Form Signals",
10785
11114
  framework: "angular",
10786
11115
  code: "<!-- Storybook story: Form Signals -->\n<toggle-form-demo />"
11116
+ },
11117
+ {
11118
+ title: "Hidden Label",
11119
+ framework: "angular",
11120
+ code: '<!-- Storybook story: Hidden Label -->\n<div stack-layout gap="md">\n<div row-layout gap="sm" alignItems="center">\n <span uni-text="body-2-long">Weekly digest</span>\n <uni-toggle label="Weekly digest" labelHidden checked></uni-toggle>\n</div>\n<div row-layout gap="sm" alignItems="center">\n <span uni-text="body-2-long">Product announcements</span>\n <uni-toggle label="Product announcements" labelHidden></uni-toggle>\n</div>\n</div>'
11121
+ },
11122
+ {
11123
+ title: "Row As Hit Target",
11124
+ framework: "angular",
11125
+ code: '<!-- Storybook story: Row As Hit Target -->\n<div stack-layout gap="xs">\n<uni-toggle fullWidth label="Two-factor authentication" labelHidden>\n <span uni-text="body-2-long">Two-factor authentication</span>\n <span uni-text="caption">Required for admin accounts</span>\n</uni-toggle>\n<uni-toggle fullWidth label="Session alerts" labelHidden>\n <span uni-text="body-2-long">Session alerts</span>\n <span uni-text="caption">Email me on a new sign-in</span>\n</uni-toggle>\n</div>'
10787
11126
  }
10788
11127
  ],
10789
11128
  guidelines: {
10790
- whenToUse: "A switch-style control for turning an option on or off with immediate effect \u2014\nprefer it over a checkbox when the change applies instantly (settings, feature\nswitches) rather than as part of a submitted form. It implements\n`FormCheckboxControl`, so it binds with the Signal Forms `[formField]` directive or a\ndirect `[(checked)]` model.\nTrack and knob colors resolve from the `toggle` theme options; geometry comes\nfrom its `sizes` block, and the checked state wears `checkedColor` \u2014 falling back\nto the active `variant`.",
11129
+ whenToUse: "A switch-style control for turning an option on or off with immediate effect \u2014\nprefer it over a checkbox when the change applies instantly (settings, feature\nswitches) rather than as part of a submitted form. It implements\n`FormCheckboxControl`, so it binds with the Signal Forms `[formField]` directive or a\ndirect `[(checked)]` model.\nTrack and knob colors resolve from the `toggle` theme options; geometry comes\nfrom its `sizes` block, and the checked state wears `checkedColor` \u2014 falling back\nto the active `variant`. The label's typeface and the gap beside it are theme\noptions too (`textRole`, `textColor`, `gap`).\n`checked` is a `model`. Bind it two-way, or hold the value in your own signal: if\nyour app declines a change the user made, a one-way `[checked]` expression has not\nchanged, so Angular writes nothing and the switch keeps the state the user left it\nin.",
10791
11130
  dos: [
10792
- "Use for instant on/off effects (settings, feature switches)"
11131
+ "Use for instant on/off effects (settings, feature switches)",
11132
+ "Project the row's content when the whole settings row should be the hit target"
10793
11133
  ],
10794
11134
  donts: [
10795
- "Use in submitted forms where nothing happens until save \u2014 that is a checkbox"
11135
+ "Use in submitted forms where nothing happens until save \u2014 that is a checkbox",
11136
+ "Bind `[checked]` one way and expect a declined change to snap back"
10796
11137
  ],
10797
11138
  accessibility: [
10798
11139
  'Semantic `<input type="checkbox">` with `role="switch"`, properly associated with its label.',
11140
+ "`labelHidden` hides the label visually while keeping it as the accessible name.",
11141
+ "Projected content sits inside that same `<label>`, so it names and activates the control.",
10799
11142
  "Space toggles; focus indicators are provided for keyboard navigation.",
10800
11143
  "Screen readers announce the label and on/off state."
10801
11144
  ]
@@ -10803,7 +11146,9 @@ placeholder="Name or address\u2026"
10803
11146
  relatedTokens: [
10804
11147
  "toggle.behavior.trackColor",
10805
11148
  "toggle.behavior.knobColor",
10806
- "toggle.behavior.motion"
11149
+ "toggle.behavior.motion",
11150
+ "toggle.behavior.textRole",
11151
+ "toggle.behavior.gap"
10807
11152
  ]
10808
11153
  },
10809
11154
  {
@@ -10813,7 +11158,7 @@ placeholder="Name or address\u2026"
10813
11158
  summary: "",
10814
11159
  description: "",
10815
11160
  category: "feedback",
10816
- version: "11.0.0",
11161
+ version: "11.2.0",
10817
11162
  bindings: {
10818
11163
  angular: {
10819
11164
  importPath: "@uni-design-system/uni-angular",
@@ -10901,7 +11246,7 @@ placeholder="Name or address\u2026"
10901
11246
  summary: 'A thin sequencer over one `uni-callout`: steps spotlight their targets, a footer walks Next/Back with dots-or-fraction progress, and `advanceOn` gates a step behind an interaction with its target (auto-advancing for clicks, unlocking Next otherwise \u2014 announced through one `role="status"` region).',
10902
11247
  description: 'A thin sequencer over one `uni-callout`: steps spotlight their targets, a footer walks Next/Back with dots-or-fraction progress, and `advanceOn` gates a step behind an interaction with its target (auto-advancing for clicks, unlocking Next otherwise \u2014 announced through one `role="status"` region). Escape or the close button skips the tour and reports the step. `active` is a two-way model, so a tour is deep-linkable and inspectable; steps whose declared target cannot be resolved are skipped (with a dev warning) in the direction of travel rather than erroring.',
10903
11248
  category: "components",
10904
- version: "11.0.0",
11249
+ version: "11.2.0",
10905
11250
  bindings: {
10906
11251
  angular: {
10907
11252
  importPath: "@uni-design-system/uni-angular",
@@ -11028,7 +11373,7 @@ placeholder="Name or address\u2026"
11028
11373
  summary: "",
11029
11374
  description: "",
11030
11375
  category: "layout",
11031
- version: "11.0.0",
11376
+ version: "11.2.0",
11032
11377
  bindings: {
11033
11378
  angular: {
11034
11379
  importPath: "@uni-design-system/uni-angular",
@@ -12590,13 +12935,13 @@ placeholder="Name or address\u2026"
12590
12935
  },
12591
12936
  {
12592
12937
  id: "typography.tag",
12593
- value: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":600}',
12938
+ value: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":500}',
12594
12939
  type: "typography",
12595
12940
  kind: "style",
12596
12941
  target: "emotion-css",
12597
12942
  themeValues: {
12598
- LightTheme: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":600}',
12599
- DarkTheme: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":600}'
12943
+ LightTheme: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":500}',
12944
+ DarkTheme: '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":500}'
12600
12945
  }
12601
12946
  },
12602
12947
  {
@@ -13258,18 +13603,6 @@ placeholder="Name or address\u2026"
13258
13603
  DarkTheme: "control"
13259
13604
  }
13260
13605
  },
13261
- {
13262
- id: "checkbox.behavior.size",
13263
- value: 20,
13264
- type: "number",
13265
- kind: "behavioral",
13266
- target: "component-prop",
13267
- appliesTo: "checkbox",
13268
- themeValues: {
13269
- LightTheme: 20,
13270
- DarkTheme: 20
13271
- }
13272
- },
13273
13606
  {
13274
13607
  id: "checkbox.behavior.boxColor",
13275
13608
  value: "surface",
@@ -13307,15 +13640,27 @@ placeholder="Name or address\u2026"
13307
13640
  }
13308
13641
  },
13309
13642
  {
13310
- id: "radio.behavior.size",
13311
- value: 20,
13312
- type: "number",
13643
+ id: "checkbox.behavior.textRole",
13644
+ value: "label",
13645
+ type: "string",
13313
13646
  kind: "behavioral",
13314
13647
  target: "component-prop",
13315
- appliesTo: "radio",
13648
+ appliesTo: "checkbox",
13316
13649
  themeValues: {
13317
- LightTheme: 20,
13318
- DarkTheme: 20
13650
+ LightTheme: "label",
13651
+ DarkTheme: "label"
13652
+ }
13653
+ },
13654
+ {
13655
+ id: "checkbox.behavior.gap",
13656
+ value: "sm",
13657
+ type: "string",
13658
+ kind: "behavioral",
13659
+ target: "component-prop",
13660
+ appliesTo: "checkbox",
13661
+ themeValues: {
13662
+ LightTheme: "sm",
13663
+ DarkTheme: "sm"
13319
13664
  }
13320
13665
  },
13321
13666
  {
@@ -13354,6 +13699,42 @@ placeholder="Name or address\u2026"
13354
13699
  DarkTheme: "control"
13355
13700
  }
13356
13701
  },
13702
+ {
13703
+ id: "radio.behavior.textRole",
13704
+ value: "label",
13705
+ type: "string",
13706
+ kind: "behavioral",
13707
+ target: "component-prop",
13708
+ appliesTo: "radio",
13709
+ themeValues: {
13710
+ LightTheme: "label",
13711
+ DarkTheme: "label"
13712
+ }
13713
+ },
13714
+ {
13715
+ id: "radio.behavior.groupTextRole",
13716
+ value: "label",
13717
+ type: "string",
13718
+ kind: "behavioral",
13719
+ target: "component-prop",
13720
+ appliesTo: "radio",
13721
+ themeValues: {
13722
+ LightTheme: "label",
13723
+ DarkTheme: "label"
13724
+ }
13725
+ },
13726
+ {
13727
+ id: "radio.behavior.gap",
13728
+ value: "sm",
13729
+ type: "string",
13730
+ kind: "behavioral",
13731
+ target: "component-prop",
13732
+ appliesTo: "radio",
13733
+ themeValues: {
13734
+ LightTheme: "sm",
13735
+ DarkTheme: "sm"
13736
+ }
13737
+ },
13357
13738
  {
13358
13739
  id: "dialog.behavior.borderRadius",
13359
13740
  value: "lg",
@@ -14938,6 +15319,42 @@ placeholder="Name or address\u2026"
14938
15319
  DarkTheme: "control"
14939
15320
  }
14940
15321
  },
15322
+ {
15323
+ id: "tag.behavior.endInset",
15324
+ value: "auto",
15325
+ type: "string",
15326
+ kind: "behavioral",
15327
+ target: "component-prop",
15328
+ appliesTo: "tag",
15329
+ themeValues: {
15330
+ LightTheme: "auto",
15331
+ DarkTheme: "auto"
15332
+ }
15333
+ },
15334
+ {
15335
+ id: "tagGroup.behavior.gap",
15336
+ value: "xs",
15337
+ type: "string",
15338
+ kind: "behavioral",
15339
+ target: "component-prop",
15340
+ appliesTo: "tagGroup",
15341
+ themeValues: {
15342
+ LightTheme: "xs",
15343
+ DarkTheme: "xs"
15344
+ }
15345
+ },
15346
+ {
15347
+ id: "tagGroup.behavior.rowGap",
15348
+ value: "xs",
15349
+ type: "string",
15350
+ kind: "behavioral",
15351
+ target: "component-prop",
15352
+ appliesTo: "tagGroup",
15353
+ themeValues: {
15354
+ LightTheme: "xs",
15355
+ DarkTheme: "xs"
15356
+ }
15357
+ },
14941
15358
  {
14942
15359
  id: "tagInput.behavior.chipGap",
14943
15360
  value: "xs",
@@ -16450,6 +16867,30 @@ placeholder="Name or address\u2026"
16450
16867
  DarkTheme: "control"
16451
16868
  }
16452
16869
  },
16870
+ {
16871
+ id: "toggle.behavior.textRole",
16872
+ value: "label",
16873
+ type: "string",
16874
+ kind: "behavioral",
16875
+ target: "component-prop",
16876
+ appliesTo: "toggle",
16877
+ themeValues: {
16878
+ LightTheme: "label",
16879
+ DarkTheme: "label"
16880
+ }
16881
+ },
16882
+ {
16883
+ id: "toggle.behavior.gap",
16884
+ value: "sm",
16885
+ type: "string",
16886
+ kind: "behavioral",
16887
+ target: "component-prop",
16888
+ appliesTo: "toggle",
16889
+ themeValues: {
16890
+ LightTheme: "sm",
16891
+ DarkTheme: "sm"
16892
+ }
16893
+ },
16453
16894
  {
16454
16895
  id: "tooltip.behavior.motion",
16455
16896
  value: "notification",
@@ -16859,7 +17300,7 @@ placeholder="Name or address\u2026"
16859
17300
  "typography.note": '{"fontFamily":"Roboto","fontSize":14,"lineHeight":22,"fontStyle":"italic"}',
16860
17301
  "typography.badge": '{"fontFamily":"Red Hat Display","fontSize":16,"lineHeight":24}',
16861
17302
  "typography.stat": '{"fontFamily":"Red Hat Display","fontSize":32,"lineHeight":38,"fontWeight":600,"letterSpacing":-0.32}',
16862
- "typography.tag": '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":600}',
17303
+ "typography.tag": '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":500}',
16863
17304
  "typography.input": '{"fontFamily":"Red Hat Display","fontSize":14,"lineHeight":24}'
16864
17305
  },
16865
17306
  componentOptions: {
@@ -16936,16 +17377,19 @@ placeholder="Name or address\u2026"
16936
17377
  },
16937
17378
  checkbox: {
16938
17379
  motion: "control",
16939
- size: 20,
16940
17380
  boxColor: "surface",
16941
17381
  borderRadius: 2,
16942
- focusRingGap: 2
17382
+ focusRingGap: 2,
17383
+ textRole: "label",
17384
+ gap: "sm"
16943
17385
  },
16944
17386
  radio: {
16945
- size: 20,
16946
17387
  ringColor: "outline",
16947
17388
  fillColor: "surface",
16948
- motion: "control"
17389
+ motion: "control",
17390
+ textRole: "label",
17391
+ groupTextRole: "label",
17392
+ gap: "sm"
16949
17393
  },
16950
17394
  dialog: {
16951
17395
  borderRadius: "lg",
@@ -17117,7 +17561,12 @@ placeholder="Name or address\u2026"
17117
17561
  gap: "xs",
17118
17562
  removeIcon: "close",
17119
17563
  selectedIcon: "check",
17120
- motion: "control"
17564
+ motion: "control",
17565
+ endInset: "auto"
17566
+ },
17567
+ tagGroup: {
17568
+ gap: "xs",
17569
+ rowGap: "xs"
17121
17570
  },
17122
17571
  tagInput: {
17123
17572
  chipGap: "xs",
@@ -17283,7 +17732,9 @@ placeholder="Name or address\u2026"
17283
17732
  toggle: {
17284
17733
  trackColor: "surface-variant",
17285
17734
  knobColor: "surface",
17286
- motion: "control"
17735
+ motion: "control",
17736
+ textRole: "label",
17737
+ gap: "sm"
17287
17738
  },
17288
17739
  tooltip: {
17289
17740
  motion: "notification",
@@ -17459,7 +17910,7 @@ placeholder="Name or address\u2026"
17459
17910
  "typography.note": '{"fontFamily":"Roboto","fontSize":14,"lineHeight":22,"fontStyle":"italic"}',
17460
17911
  "typography.badge": '{"fontFamily":"Red Hat Display","fontSize":16,"lineHeight":24}',
17461
17912
  "typography.stat": '{"fontFamily":"Red Hat Display","fontSize":32,"lineHeight":38,"fontWeight":600,"letterSpacing":-0.32}',
17462
- "typography.tag": '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":600}',
17913
+ "typography.tag": '{"fontFamily":"Red Hat Display","fontSize":15,"lineHeight":20,"fontWeight":500}',
17463
17914
  "typography.input": '{"fontFamily":"Red Hat Display","fontSize":14,"lineHeight":24}'
17464
17915
  },
17465
17916
  componentOptions: {
@@ -17536,16 +17987,19 @@ placeholder="Name or address\u2026"
17536
17987
  },
17537
17988
  checkbox: {
17538
17989
  motion: "control",
17539
- size: 20,
17540
17990
  boxColor: "surface",
17541
17991
  borderRadius: 2,
17542
- focusRingGap: 2
17992
+ focusRingGap: 2,
17993
+ textRole: "label",
17994
+ gap: "sm"
17543
17995
  },
17544
17996
  radio: {
17545
- size: 20,
17546
17997
  ringColor: "outline",
17547
17998
  fillColor: "surface",
17548
- motion: "control"
17999
+ motion: "control",
18000
+ textRole: "label",
18001
+ groupTextRole: "label",
18002
+ gap: "sm"
17549
18003
  },
17550
18004
  dialog: {
17551
18005
  borderRadius: "lg",
@@ -17717,7 +18171,12 @@ placeholder="Name or address\u2026"
17717
18171
  gap: "xs",
17718
18172
  removeIcon: "close",
17719
18173
  selectedIcon: "check",
17720
- motion: "control"
18174
+ motion: "control",
18175
+ endInset: "auto"
18176
+ },
18177
+ tagGroup: {
18178
+ gap: "xs",
18179
+ rowGap: "xs"
17721
18180
  },
17722
18181
  tagInput: {
17723
18182
  chipGap: "xs",
@@ -17883,7 +18342,9 @@ placeholder="Name or address\u2026"
17883
18342
  toggle: {
17884
18343
  trackColor: "surface-variant",
17885
18344
  knobColor: "surface",
17886
- motion: "control"
18345
+ motion: "control",
18346
+ textRole: "label",
18347
+ gap: "sm"
17887
18348
  },
17888
18349
  tooltip: {
17889
18350
  motion: "notification",
@@ -17921,6 +18382,36 @@ placeholder="Name or address\u2026"
17921
18382
  {
17922
18383
  package: "@uni-design-system/uni-angular",
17923
18384
  releases: [
18385
+ {
18386
+ version: "11.2.0",
18387
+ entries: [
18388
+ {
18389
+ bump: "minor",
18390
+ title: "The label on `uni-checkbox`, `uni-toggle` and `uni-radio` becomes a theme decision, can be named without being drawn, and can be replaced by content of your own.",
18391
+ body: "All three rendered their label as `<span uni-text=\"label\">` \u2014 a literal. `label` is the _caption above a field_ in most type scales, so a theme that makes it an uppercase eyebrow, as several do, rendered every checkbox sentence as a 9px badge beside a 20px box, with nothing in the theme able to reach it. `uni-radio` was worse: the group heading and each option shared that one role, so a group had no hierarchy at all. New **`textRole`** and **`textColor`** options fix it, and radio gets a second pair, **`groupTextRole`** / **`groupTextColor`**, for the heading. Both default to `label`, so no existing theme moves.\n\n**`labelHidden`** keeps `label` as the accessible name without drawing it \u2014 the select column, the row whose meaning is already on screen beside it. The string stays in the DOM, so a test querying by label text and a screen reader see the same words, and it renders through the shared `visuallyHidden` recipe, which is `position: fixed`: out of flow, so it claims none of the label row's `gap` and the control measures exactly its box. On radio it hides the group heading, which still carries the id `aria-labelledby` points at.\n\n**`<ng-content />` now sits inside the control's own `<label>`.** HTML forbids nesting a `<label>`, so the natural markup for \"the whole row toggles this\" is illegal the moment the row contains a checkbox \u2014 leaving apps to write a `div` with a click handler, a `stopPropagation` so the row does not toggle twice, and the name passed twice. Project the row's content instead and it is one control, natively associated, whole-row clickable and keyboard-operable, with one tab stop. A radio group renders N labels from data and so has no single slot: it takes an **`<ng-template uniRadioOption let-option>`** instead, instantiated once per option, inside that option's `<label>`. **`fullWidth`** on all three stretches the hit area to its container.\n\n**`gap`** is a spacing token now rather than a literal `8`, and radio's group column gets **`groupGap`** (unset stays at the 12px it has always drawn \u2014 not a step on the base scale, so resolving it as a token would warn on every miss).\n\n**Checkbox and radio get a `sizes` block**, the mechanism `toggle.sizes` moved to in 11.1.0: a dense table wants a 16px box where the same app's forms want 20px, and one number per theme cannot say so. `lg` is `BaseComponent`'s default and reproduces today's 20px exactly. `options.size` is now `@deprecated` and still **outranks** the block \u2014 themes are deep-merged over the base, so a theme written before this release would otherwise inherit our numbers and lose its own. Delete that key to opt in; it goes in 12.0.\n\nTwo rules change for everyone, both fixing something that was already wrong:\n- **The hosts get `display: inline-flex`.** They had no display at all, so the flex `<label>` inside made `<uni-checkbox>` and `<uni-toggle>` generate block boxes and fill their container \u2014 which is why a hidden-label checkbox could not be sized by measuring it, and why an app wanting one had to add `display: inline-flex` itself. A checkbox that was relying on that stretch now shrink-wraps; `fullWidth` is the way back.\n- **The box and the track get `flex-shrink: 0`**, which `uni-radio`'s circle already had. Without it a long label in a narrow column squashed the box into a rectangle. Projected content makes that far likelier, so it ships alongside.\n\n`labelHidden` and `fullWidth` take `booleanAttribute`, as `uni-inline-button` and `uni-expand-area` already do, so `<uni-checkbox labelHidden>` reads as true rather than binding the falsy empty string.\n\n**Every other valueless boolean attribute gets the same transform**, because they all had the same defect: `<button text-button fullWidth>` was silently inert, and so were the bare forms on `uni-input`, `uni-input-box`, `uni-select`, `uni-textarea` and `box-layout` \u2014 the last of which also covers `fullHeight`. `uni-button`'s **`disable`** and **`loading`** are in too: `<button text-button disable>` rendered an enabled button, which is the one in this set that could actually let a user do something they should not have been able to. Both now read `boolean` rather than `boolean | undefined`; `disable() || loading() || null` still removes the attribute entirely when neither is set, rather than rendering `disabled=\"false\"`, which the DOM reads as true. Property bindings (`[fullWidth]=\"true\"`) always worked and are unaffected; `box-layout`'s two inputs now read `boolean` rather than `boolean | undefined`, which narrows a read type without changing a rendered rule.\n\nThe four field components forward `fullWidth` into `box-layout`, whose transform coerced the empty string at the end of the chain \u2014 so their bare attribute _appeared_ to work while each component's own `fullWidth()` held `''`. That is why the guard for this asserts the input's value rather than the emitted CSS: a CSS-only assertion passes with the transform removed from any of the four.\n\n`pnpm docs:api` learned to read those transforms: `llms.txt` was printing `labelHidden: unknown = false, { transform: booleanAttribute }`, and the same leak had been mangling the existing `link`, `disable`, `initCollapsed` and every aliased input. They now report the type they accept and the default alone.",
18392
+ commit: "8f3b52e"
18393
+ },
18394
+ {
18395
+ bump: "patch",
18396
+ title: 'Fix `uni-tag-group`\'s `layout="justify"` doing nothing until the chips wrap.',
18397
+ body: "Justification was implemented with a filler child that lands on the last row and swallows its slack, which is what leaves that row ragged. But a group that fits on one row is _all_ last row, so `justify` had no visible effect at all until the chips overflowed \u2014 the common case for a filter row read as the feature being broken.\n\nWhether the group has wrapped is now **measured** rather than assumed: a single row stretches flush, and the last row is left alone only once there is more than one. A `ResizeObserver` on the host drives it, so it stays right as the viewport changes \u2014 which a flag could not be, since the same group is one row on a desktop and three on a phone.\n\nThe measurement cannot chase its own tail: flex breaks lines from each item's basis _before_ it grows anything, so neither the filler nor the growth this decision enables can move a chip to another row. The filler also now pulls back the gap in front of it, so its whole footprint is zero \u2014 on a row that happened to be exactly full, that gap alone was enough to push it onto a line of its own.",
18398
+ commit: "1d602c3"
18399
+ }
18400
+ ],
18401
+ dependencyBumps: []
18402
+ },
18403
+ {
18404
+ version: "11.1.0",
18405
+ entries: [
18406
+ {
18407
+ bump: "minor",
18408
+ title: "Selection, hover and density become theme decisions on `uni-tag`, and add `uni-tag-group`.",
18409
+ body: "`uni-tag` had the right axes but made the app do the work. `selected` painted nothing \u2014 it set `aria-pressed` and swapped the lead glyph \u2014 so every toggle chip in app markup carried a `[tone]=\"on() ? 'solid' : 'soft'\"` of its own. Interactive chips had no hover state at all. The selected check was added rather than reserved, so picking a chip reflowed the whole row.\n\n**Selection paints itself.** A selected chip adds a `tag-selected` class and an interactive one adds `tag-interactive`, both as much public theme contract as `&.tone-*` already was. The base theme's `tag` variants use them: selection promotes a chip to its role's solid pair at every tone (`solid` deepens instead of refilling), and `&.tag-interactive:hover` is a `brightness(0.95)` wash that darkens fill, ink and edge together. A theme restyling either writes the rules per variant, beside the colours they belong to.\n\n**A toggle chip holds the check's place.** Bind `selected` and the chip reserves the glyph's footprint while unselected, so picking one never resizes it. A chip that already has a lead needs no slot \u2014 the check replaces the avatar or icon at identical width.\n\n**`uni-tag-group`** is a row of chips the user picks from. It renders its own chips from `items` and owns the two things an app was otherwise wiring per chip: which one is selected (`value`, plus `multiple`) and which one holds the tab stop. Keyboard model is the toolbar pattern \u2014 one tab stop for the whole row, arrow keys within it, on both axes because the row wraps. `layout=\"justify\"` stretches each filled row flush to both edges and leaves the last row at its natural widths, the way justified text leaves its last line alone. It is themed as **`tagGroup`** (`gap`, `rowGap`, `chipSize`, `chipTone`, `chipVariant`), layout only \u2014 every colour decision stays on the chips, so a group restyles with them.\n\n**Visual changes to every existing chip**, including the chips inside `uni-tag-input`:\n- A lead element and the remove control now **tuck into the rounded end** instead of starting where the radius ends \u2014 each sits concentric with the curve it is in, so a small status dot sits further in than a full lead box. A lead is balanced against, so the **label sits centred**; the tuck costs less than the old gutter did, so that balance is largely paid for rather than added. A remove control is not balanced against on its own, since it reads as an affordance rather than as content \u2014 a chip with nothing but a remove button keeps its gutter and simply tucks the glyph. A chip whose ends are too square to flow into (`borderRadius: 'xs'`) keeps the size token's gutter at both ends.\n- The `tag` typeface drops from **weight 600 to 500**. Geometry is unchanged; what made these chips read louder than the ones they replaced was the weight, not the size.\n- `border-color` and `filter` join the transition list, so an outline\u2194solid change fades its edge instead of snapping while the fill animates, and the hover wash fades in.\n\nNew `tag` theme option **`endInset`** (`number | 'auto'`) sets how far that tuck goes; `auto` derives it from the chip so the lead sits concentric with the cap. The `tag` size tokens now state `paddingInline` rather than a `padding` shorthand, since the chip reads that number back to balance the label \u2014 a theme still stating the shorthand is parsed as before.\n\n`UniTagComponent` is now generic in its value (`UniTagComponent<T extends UniTagValue = UniTagValue>`), so `value`, `removed` and `activated` carry an app's own type and `$event` flows without a closure. The default type parameter leaves every existing use compiling unchanged.",
18410
+ commit: "d00b2fe"
18411
+ }
18412
+ ],
18413
+ dependencyBumps: []
18414
+ },
17924
18415
  {
17925
18416
  version: "11.0.0",
17926
18417
  entries: [
@@ -18753,6 +19244,30 @@ placeholder="Name or address\u2026"
18753
19244
  {
18754
19245
  package: "@uni-design-system/uni-core",
18755
19246
  releases: [
19247
+ {
19248
+ version: "11.2.0",
19249
+ entries: [
19250
+ {
19251
+ bump: "minor",
19252
+ title: "The label on `uni-checkbox`, `uni-toggle` and `uni-radio` becomes a theme decision, can be named without being drawn, and can be replaced by content of your own.",
19253
+ body: "All three rendered their label as `<span uni-text=\"label\">` \u2014 a literal. `label` is the _caption above a field_ in most type scales, so a theme that makes it an uppercase eyebrow, as several do, rendered every checkbox sentence as a 9px badge beside a 20px box, with nothing in the theme able to reach it. `uni-radio` was worse: the group heading and each option shared that one role, so a group had no hierarchy at all. New **`textRole`** and **`textColor`** options fix it, and radio gets a second pair, **`groupTextRole`** / **`groupTextColor`**, for the heading. Both default to `label`, so no existing theme moves.\n\n**`labelHidden`** keeps `label` as the accessible name without drawing it \u2014 the select column, the row whose meaning is already on screen beside it. The string stays in the DOM, so a test querying by label text and a screen reader see the same words, and it renders through the shared `visuallyHidden` recipe, which is `position: fixed`: out of flow, so it claims none of the label row's `gap` and the control measures exactly its box. On radio it hides the group heading, which still carries the id `aria-labelledby` points at.\n\n**`<ng-content />` now sits inside the control's own `<label>`.** HTML forbids nesting a `<label>`, so the natural markup for \"the whole row toggles this\" is illegal the moment the row contains a checkbox \u2014 leaving apps to write a `div` with a click handler, a `stopPropagation` so the row does not toggle twice, and the name passed twice. Project the row's content instead and it is one control, natively associated, whole-row clickable and keyboard-operable, with one tab stop. A radio group renders N labels from data and so has no single slot: it takes an **`<ng-template uniRadioOption let-option>`** instead, instantiated once per option, inside that option's `<label>`. **`fullWidth`** on all three stretches the hit area to its container.\n\n**`gap`** is a spacing token now rather than a literal `8`, and radio's group column gets **`groupGap`** (unset stays at the 12px it has always drawn \u2014 not a step on the base scale, so resolving it as a token would warn on every miss).\n\n**Checkbox and radio get a `sizes` block**, the mechanism `toggle.sizes` moved to in 11.1.0: a dense table wants a 16px box where the same app's forms want 20px, and one number per theme cannot say so. `lg` is `BaseComponent`'s default and reproduces today's 20px exactly. `options.size` is now `@deprecated` and still **outranks** the block \u2014 themes are deep-merged over the base, so a theme written before this release would otherwise inherit our numbers and lose its own. Delete that key to opt in; it goes in 12.0.\n\nTwo rules change for everyone, both fixing something that was already wrong:\n- **The hosts get `display: inline-flex`.** They had no display at all, so the flex `<label>` inside made `<uni-checkbox>` and `<uni-toggle>` generate block boxes and fill their container \u2014 which is why a hidden-label checkbox could not be sized by measuring it, and why an app wanting one had to add `display: inline-flex` itself. A checkbox that was relying on that stretch now shrink-wraps; `fullWidth` is the way back.\n- **The box and the track get `flex-shrink: 0`**, which `uni-radio`'s circle already had. Without it a long label in a narrow column squashed the box into a rectangle. Projected content makes that far likelier, so it ships alongside.\n\n`labelHidden` and `fullWidth` take `booleanAttribute`, as `uni-inline-button` and `uni-expand-area` already do, so `<uni-checkbox labelHidden>` reads as true rather than binding the falsy empty string.\n\n**Every other valueless boolean attribute gets the same transform**, because they all had the same defect: `<button text-button fullWidth>` was silently inert, and so were the bare forms on `uni-input`, `uni-input-box`, `uni-select`, `uni-textarea` and `box-layout` \u2014 the last of which also covers `fullHeight`. `uni-button`'s **`disable`** and **`loading`** are in too: `<button text-button disable>` rendered an enabled button, which is the one in this set that could actually let a user do something they should not have been able to. Both now read `boolean` rather than `boolean | undefined`; `disable() || loading() || null` still removes the attribute entirely when neither is set, rather than rendering `disabled=\"false\"`, which the DOM reads as true. Property bindings (`[fullWidth]=\"true\"`) always worked and are unaffected; `box-layout`'s two inputs now read `boolean` rather than `boolean | undefined`, which narrows a read type without changing a rendered rule.\n\nThe four field components forward `fullWidth` into `box-layout`, whose transform coerced the empty string at the end of the chain \u2014 so their bare attribute _appeared_ to work while each component's own `fullWidth()` held `''`. That is why the guard for this asserts the input's value rather than the emitted CSS: a CSS-only assertion passes with the transform removed from any of the four.\n\n`pnpm docs:api` learned to read those transforms: `llms.txt` was printing `labelHidden: unknown = false, { transform: booleanAttribute }`, and the same leak had been mangling the existing `link`, `disable`, `initCollapsed` and every aliased input. They now report the type they accept and the default alone.",
19254
+ commit: "8f3b52e"
19255
+ }
19256
+ ],
19257
+ dependencyBumps: []
19258
+ },
19259
+ {
19260
+ version: "11.1.0",
19261
+ entries: [
19262
+ {
19263
+ bump: "minor",
19264
+ title: "Selection, hover and density become theme decisions on `uni-tag`, and add `uni-tag-group`.",
19265
+ body: "`uni-tag` had the right axes but made the app do the work. `selected` painted nothing \u2014 it set `aria-pressed` and swapped the lead glyph \u2014 so every toggle chip in app markup carried a `[tone]=\"on() ? 'solid' : 'soft'\"` of its own. Interactive chips had no hover state at all. The selected check was added rather than reserved, so picking a chip reflowed the whole row.\n\n**Selection paints itself.** A selected chip adds a `tag-selected` class and an interactive one adds `tag-interactive`, both as much public theme contract as `&.tone-*` already was. The base theme's `tag` variants use them: selection promotes a chip to its role's solid pair at every tone (`solid` deepens instead of refilling), and `&.tag-interactive:hover` is a `brightness(0.95)` wash that darkens fill, ink and edge together. A theme restyling either writes the rules per variant, beside the colours they belong to.\n\n**A toggle chip holds the check's place.** Bind `selected` and the chip reserves the glyph's footprint while unselected, so picking one never resizes it. A chip that already has a lead needs no slot \u2014 the check replaces the avatar or icon at identical width.\n\n**`uni-tag-group`** is a row of chips the user picks from. It renders its own chips from `items` and owns the two things an app was otherwise wiring per chip: which one is selected (`value`, plus `multiple`) and which one holds the tab stop. Keyboard model is the toolbar pattern \u2014 one tab stop for the whole row, arrow keys within it, on both axes because the row wraps. `layout=\"justify\"` stretches each filled row flush to both edges and leaves the last row at its natural widths, the way justified text leaves its last line alone. It is themed as **`tagGroup`** (`gap`, `rowGap`, `chipSize`, `chipTone`, `chipVariant`), layout only \u2014 every colour decision stays on the chips, so a group restyles with them.\n\n**Visual changes to every existing chip**, including the chips inside `uni-tag-input`:\n- A lead element and the remove control now **tuck into the rounded end** instead of starting where the radius ends \u2014 each sits concentric with the curve it is in, so a small status dot sits further in than a full lead box. A lead is balanced against, so the **label sits centred**; the tuck costs less than the old gutter did, so that balance is largely paid for rather than added. A remove control is not balanced against on its own, since it reads as an affordance rather than as content \u2014 a chip with nothing but a remove button keeps its gutter and simply tucks the glyph. A chip whose ends are too square to flow into (`borderRadius: 'xs'`) keeps the size token's gutter at both ends.\n- The `tag` typeface drops from **weight 600 to 500**. Geometry is unchanged; what made these chips read louder than the ones they replaced was the weight, not the size.\n- `border-color` and `filter` join the transition list, so an outline\u2194solid change fades its edge instead of snapping while the fill animates, and the hover wash fades in.\n\nNew `tag` theme option **`endInset`** (`number | 'auto'`) sets how far that tuck goes; `auto` derives it from the chip so the lead sits concentric with the cap. The `tag` size tokens now state `paddingInline` rather than a `padding` shorthand, since the chip reads that number back to balance the label \u2014 a theme still stating the shorthand is parsed as before.\n\n`UniTagComponent` is now generic in its value (`UniTagComponent<T extends UniTagValue = UniTagValue>`), so `value`, `removed` and `activated` carry an app's own type and `$event` flows without a closure. The default type parameter leaves every existing use compiling unchanged.",
19266
+ commit: "d00b2fe"
19267
+ }
19268
+ ],
19269
+ dependencyBumps: []
19270
+ },
18756
19271
  {
18757
19272
  version: "11.0.0",
18758
19273
  entries: [
@@ -19361,6 +19876,16 @@ placeholder="Name or address\u2026"
19361
19876
  {
19362
19877
  package: "@uni-design-system/uni-react",
19363
19878
  releases: [
19879
+ {
19880
+ version: "11.2.0",
19881
+ entries: [],
19882
+ dependencyBumps: []
19883
+ },
19884
+ {
19885
+ version: "11.1.0",
19886
+ entries: [],
19887
+ dependencyBumps: []
19888
+ },
19364
19889
  {
19365
19890
  version: "11.0.0",
19366
19891
  entries: [
@@ -19632,6 +20157,16 @@ placeholder="Name or address\u2026"
19632
20157
  {
19633
20158
  package: "@uni-design-system/uni-mcp",
19634
20159
  releases: [
20160
+ {
20161
+ version: "11.2.0",
20162
+ entries: [],
20163
+ dependencyBumps: []
20164
+ },
20165
+ {
20166
+ version: "11.1.0",
20167
+ entries: [],
20168
+ dependencyBumps: []
20169
+ },
19635
20170
  {
19636
20171
  version: "11.0.0",
19637
20172
  entries: [
@@ -20870,7 +21405,7 @@ var BaseTypography = {
20870
21405
  fontFamily: "Red Hat Display",
20871
21406
  fontSize: 15,
20872
21407
  lineHeight: 20,
20873
- fontWeight: 600
21408
+ fontWeight: 500
20874
21409
  },
20875
21410
  input: {
20876
21411
  fontFamily: "Red Hat Display",
@@ -20965,14 +21500,26 @@ var SELECTION_ACCENTS = {
20965
21500
  var tagVariant = (c, role) => ({ [role]: {
20966
21501
  backgroundColor: c[`${role}-container`],
20967
21502
  color: c[`on-${role}-container`],
20968
- "&.tone-solid": {
21503
+ "&.tag-selected": {
20969
21504
  backgroundColor: c[role],
20970
21505
  color: c[`on-${role}`]
20971
21506
  },
21507
+ "&.tag-interactive:hover": { filter: "brightness(0.95)" },
21508
+ "&.tone-solid": {
21509
+ backgroundColor: c[role],
21510
+ color: c[`on-${role}`],
21511
+ "&.tag-selected": { filter: "brightness(0.9)" },
21512
+ "&.tag-selected.tag-interactive:hover": { filter: "brightness(0.85)" }
21513
+ },
20972
21514
  "&.tone-outline": {
20973
21515
  backgroundColor: "transparent",
20974
21516
  color: c[role],
20975
- borderColor: c[`on-${role}-container-border`] ?? c[role]
21517
+ borderColor: c[`on-${role}-container-border`] ?? c[role],
21518
+ "&.tag-selected": {
21519
+ backgroundColor: c[role],
21520
+ color: c[`on-${role}`],
21521
+ borderColor: c[role]
21522
+ }
20976
21523
  }
20977
21524
  } });
20978
21525
  var buildComponents = (c) => ({
@@ -21102,19 +21649,32 @@ var buildComponents = (c) => ({
21102
21649
  checkbox: {
21103
21650
  options: {
21104
21651
  motion: "control",
21105
- size: 20,
21106
21652
  boxColor: "surface",
21107
21653
  borderRadius: 2,
21108
- focusRingGap: 2
21654
+ focusRingGap: 2,
21655
+ textRole: "label",
21656
+ gap: "sm"
21657
+ },
21658
+ sizes: {
21659
+ sm: { height: 16 },
21660
+ md: { height: 18 },
21661
+ lg: { height: 20 }
21109
21662
  },
21110
21663
  variantOptions: SELECTION_ACCENTS
21111
21664
  },
21112
21665
  radio: {
21113
21666
  options: {
21114
- size: 20,
21115
21667
  ringColor: "outline",
21116
21668
  fillColor: "surface",
21117
- motion: "control"
21669
+ motion: "control",
21670
+ textRole: "label",
21671
+ groupTextRole: "label",
21672
+ gap: "sm"
21673
+ },
21674
+ sizes: {
21675
+ sm: { height: 16 },
21676
+ md: { height: 18 },
21677
+ lg: { height: 20 }
21118
21678
  },
21119
21679
  variantOptions: SELECTION_ACCENTS
21120
21680
  },
@@ -21313,7 +21873,8 @@ var buildComponents = (c) => ({
21313
21873
  gap: "xs",
21314
21874
  removeIcon: "close",
21315
21875
  selectedIcon: "check",
21316
- motion: "control"
21876
+ motion: "control",
21877
+ endInset: "auto"
21317
21878
  },
21318
21879
  fixed: {
21319
21880
  display: "inline-flex",
@@ -21330,13 +21891,25 @@ var buildComponents = (c) => ({
21330
21891
  ghost: {
21331
21892
  backgroundColor: "transparent",
21332
21893
  color: c["on-background"],
21333
- "&.tone-solid": {
21894
+ "&.tag-selected": {
21334
21895
  backgroundColor: c["surface-variant"],
21335
21896
  color: c["on-surface-variant"]
21336
21897
  },
21898
+ "&.tag-interactive:hover": { filter: "brightness(0.95)" },
21899
+ "&.tone-solid": {
21900
+ backgroundColor: c["surface-variant"],
21901
+ color: c["on-surface-variant"],
21902
+ "&.tag-selected": { filter: "brightness(0.9)" },
21903
+ "&.tag-selected.tag-interactive:hover": { filter: "brightness(0.85)" }
21904
+ },
21337
21905
  "&.tone-outline": {
21338
21906
  backgroundColor: "transparent",
21339
- borderColor: c.outline
21907
+ borderColor: c.outline,
21908
+ "&.tag-selected": {
21909
+ backgroundColor: c["surface-variant"],
21910
+ color: c["on-surface-variant"],
21911
+ borderColor: c.outline
21912
+ }
21340
21913
  }
21341
21914
  },
21342
21915
  disabled: {
@@ -21356,20 +21929,31 @@ var buildComponents = (c) => ({
21356
21929
  sm: {
21357
21930
  height: 20,
21358
21931
  fontSize: 12,
21359
- padding: "0 8px"
21932
+ paddingInline: 8
21360
21933
  },
21361
21934
  md: {
21362
21935
  height: 24,
21363
21936
  fontSize: 13,
21364
- padding: "0 10px"
21937
+ paddingInline: 10
21365
21938
  },
21366
21939
  lg: {
21367
21940
  height: 32,
21368
21941
  fontSize: 15,
21369
- padding: "0 12px"
21942
+ paddingInline: 12
21370
21943
  }
21371
21944
  }
21372
21945
  },
21946
+ tagGroup: {
21947
+ options: {
21948
+ gap: "xs",
21949
+ rowGap: "xs"
21950
+ },
21951
+ fixed: {
21952
+ display: "flex",
21953
+ flexWrap: "wrap",
21954
+ alignItems: "center"
21955
+ }
21956
+ },
21373
21957
  tagInput: { options: {
21374
21958
  chipGap: "xs",
21375
21959
  chipSize: "md",
@@ -21784,7 +22368,9 @@ var buildComponents = (c) => ({
21784
22368
  options: {
21785
22369
  trackColor: "surface-variant",
21786
22370
  knobColor: "surface",
21787
- motion: "control"
22371
+ motion: "control",
22372
+ textRole: "label",
22373
+ gap: "sm"
21788
22374
  },
21789
22375
  variantOptions: SELECTION_ACCENTS,
21790
22376
  sizes: {
@@ -23212,4 +23798,4 @@ export {
23212
23798
  buildStoredTheme,
23213
23799
  createUniServer
23214
23800
  };
23215
- //# sourceMappingURL=chunk-RAD6CBGV.js.map
23801
+ //# sourceMappingURL=chunk-MFGLCGV3.js.map