@adia-ai/web-components 0.8.30 → 0.8.32

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.
Files changed (120) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/components/accordion/accordion.d.ts +2 -2
  3. package/components/action-list/action-list.class.js +6 -2
  4. package/components/action-list/action-list.d.ts +1 -1
  5. package/components/agent-artifact/agent-artifact.d.ts +1 -1
  6. package/components/agent-feedback-bar/agent-feedback-bar.d.ts +2 -2
  7. package/components/agent-questions/agent-questions.d.ts +1 -1
  8. package/components/agent-reasoning/agent-reasoning.d.ts +3 -3
  9. package/components/agent-suggestions/agent-suggestions.d.ts +1 -1
  10. package/components/agent-trace/agent-trace.d.ts +1 -1
  11. package/components/alert/alert.d.ts +2 -2
  12. package/components/anchor-bar/anchor-bar.d.ts +2 -2
  13. package/components/avatar/avatar.class.js +0 -1
  14. package/components/avatar/avatar.css +16 -2
  15. package/components/badge/badge.css +30 -2
  16. package/components/breadcrumb/breadcrumb.css +15 -3
  17. package/components/button/button.d.ts +1 -1
  18. package/components/calendar-grid/calendar-grid.d.ts +2 -2
  19. package/components/card/card.css +21 -0
  20. package/components/card/card.yaml +9 -0
  21. package/components/chart/chart.d.ts +4 -4
  22. package/components/chart-legend/chart-legend.d.ts +1 -1
  23. package/components/chat-thread/chat-input.css +1 -1
  24. package/components/chat-thread/chat-input.js +9 -1
  25. package/components/chat-thread/chat-thread.class.js +6 -0
  26. package/components/chat-thread/chat-thread.d.ts +2 -2
  27. package/components/check/check.css +3 -3
  28. package/components/command/command.d.ts +2 -2
  29. package/components/context-menu/context-menu.d.ts +3 -3
  30. package/components/datetime-picker/datetime-picker.d.ts +5 -5
  31. package/components/demo-toggle/demo-toggle.d.ts +1 -1
  32. package/components/drawer/drawer.d.ts +2 -2
  33. package/components/heatmap/heatmap.d.ts +5 -5
  34. package/components/index.js +1 -0
  35. package/components/inline-edit/inline-edit.d.ts +4 -4
  36. package/components/input/input.a2ui.json +4 -1
  37. package/components/input/input.css +18 -0
  38. package/components/input/input.yaml +11 -1
  39. package/components/integration-card/integration-card.d.ts +4 -4
  40. package/components/link/link.d.ts +1 -1
  41. package/components/list/list-item.a2ui.json +6 -0
  42. package/components/list/list-item.yaml +13 -0
  43. package/components/list/list.class.js +28 -7
  44. package/components/list/list.d.ts +1 -1
  45. package/components/list-window/list-window.d.ts +5 -5
  46. package/components/menu/menu.css +2 -2
  47. package/components/menu/menu.d.ts +1 -1
  48. package/components/modal/modal.d.ts +1 -1
  49. package/components/nav/nav.css +8 -0
  50. package/components/nav/nav.d.ts +1 -1
  51. package/components/nav-group/nav-group.css +58 -9
  52. package/components/nav-group/nav-group.d.ts +1 -1
  53. package/components/nav-item/nav-item.css +7 -4
  54. package/components/nav-item/nav-item.d.ts +1 -1
  55. package/components/noodles/noodles.d.ts +3 -3
  56. package/components/option-card/option-card.css +1 -1
  57. package/components/pagination/pagination.d.ts +1 -1
  58. package/components/pane/pane.d.ts +1 -1
  59. package/components/password-strength/password-strength.d.ts +1 -1
  60. package/components/pipeline-status/pipeline-status.a2ui.json +14 -8
  61. package/components/pipeline-status/pipeline-status.d.ts +5 -4
  62. package/components/pipeline-status/pipeline-status.yaml +17 -11
  63. package/components/radio/radio.a2ui.json +4 -3
  64. package/components/radio/radio.css +2 -2
  65. package/components/radio/radio.yaml +7 -14
  66. package/components/radio-group/radio-group.a2ui.json +118 -0
  67. package/components/radio-group/radio-group.class.js +75 -0
  68. package/components/radio-group/radio-group.css +31 -0
  69. package/components/radio-group/radio-group.d.ts +20 -0
  70. package/components/radio-group/radio-group.examples.md +37 -0
  71. package/components/radio-group/radio-group.js +17 -0
  72. package/components/radio-group/radio-group.yaml +114 -0
  73. package/components/segmented/segmented.a2ui.json +5 -0
  74. package/components/segmented/segmented.class.js +20 -2
  75. package/components/segmented/segmented.yaml +5 -0
  76. package/components/stat/stat.css +12 -8
  77. package/components/stat/stat.js +66 -1
  78. package/components/stat/stat.yaml +3 -0
  79. package/components/stepper/stepper-item.a2ui.json +5 -0
  80. package/components/stepper/stepper-item.yaml +11 -0
  81. package/components/stepper/stepper.a2ui.json +38 -3
  82. package/components/stepper/stepper.class.js +199 -2
  83. package/components/stepper/stepper.css +30 -0
  84. package/components/stepper/stepper.d.ts +28 -1
  85. package/components/stepper/stepper.yaml +76 -2
  86. package/components/stream/stream.d.ts +3 -3
  87. package/components/swatch/swatch.class.js +1 -1
  88. package/components/swatch/swatch.d.ts +1 -1
  89. package/components/swiper/swiper.d.ts +3 -3
  90. package/components/table/table.a2ui.json +2 -2
  91. package/components/table/table.d.ts +7 -7
  92. package/components/table/table.yaml +9 -1
  93. package/components/table-toolbar/table-toolbar.d.ts +4 -4
  94. package/components/tabs/tabs.css +11 -3
  95. package/components/tabs/tabs.d.ts +1 -1
  96. package/components/tag/tag.a2ui.json +1 -1
  97. package/components/tag/tag.css +78 -13
  98. package/components/tag/tag.d.ts +12 -8
  99. package/components/tag/tag.examples.md +4 -4
  100. package/components/tag/tag.yaml +18 -7
  101. package/components/tags-input/tags-input.d.ts +4 -4
  102. package/components/timeline/timeline.d.ts +1 -1
  103. package/components/toc/toc.d.ts +1 -1
  104. package/components/toggle-group/toggle-group.d.ts +1 -1
  105. package/components/toggle-scheme/toggle-scheme.d.ts +1 -1
  106. package/components/tour/tour.d.ts +4 -4
  107. package/components/tree/tree.d.ts +1 -1
  108. package/core/icons-phosphor.js +129 -71
  109. package/core/provider.js +84 -23
  110. package/dist/host.min.css +1 -1
  111. package/dist/host.sheet.js +1 -1
  112. package/dist/theme-provider.min.js +1 -1
  113. package/dist/web-components.min.css +1 -1
  114. package/dist/web-components.min.js +89 -87
  115. package/dist/web-components.sheet.js +1 -1
  116. package/package.json +1 -1
  117. package/styles/api/sizing-fallback.css +32 -0
  118. package/styles/api/sizing.css +7 -3
  119. package/styles/components.css +1 -0
  120. package/styles/tokens.css +1 -0
@@ -47,10 +47,10 @@ export class UINavItem extends UIElement {
47
47
  /** Visual treatment. Default ('') renders as a primary-rail item (reserved icon space, in-icon selected accent). 'section' renders flat — no icon space when absent, left-edge accent bar for selected. When the parent <nav-ui> carries variant="section", this item inherits it via CSS cascade unless an explicit variant is set. */
48
48
  variant: '' | 'section';
49
49
 
50
+ addEventListener(type: 'nav-select', listener: (ev: NavItemNavSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
50
51
  addEventListener<K extends keyof HTMLElementEventMap>(
51
52
  type: K,
52
53
  listener: (this: UINavItem, ev: HTMLElementEventMap[K]) => unknown,
53
54
  options?: boolean | AddEventListenerOptions,
54
55
  ): void;
55
- addEventListener(type: 'nav-select', listener: (ev: NavItemNavSelectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
56
56
  }
@@ -62,12 +62,12 @@ export class UINoodles extends UIElement {
62
62
  /** Bezier control point distance (0-1). Higher = wider curves */
63
63
  tension: number;
64
64
 
65
+ addEventListener(type: 'noodle-connect', listener: (ev: NoodlesNoodleConnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
66
+ addEventListener(type: 'noodle-disconnect', listener: (ev: NoodlesNoodleDisconnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
67
+ addEventListener(type: 'noodle-drag', listener: (ev: NoodlesNoodleDragEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
65
68
  addEventListener<K extends keyof HTMLElementEventMap>(
66
69
  type: K,
67
70
  listener: (this: UINoodles, ev: HTMLElementEventMap[K]) => unknown,
68
71
  options?: boolean | AddEventListenerOptions,
69
72
  ): void;
70
- addEventListener(type: 'noodle-connect', listener: (ev: NoodlesNoodleConnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
71
- addEventListener(type: 'noodle-disconnect', listener: (ev: NoodlesNoodleDisconnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
72
- addEventListener(type: 'noodle-drag', listener: (ev: NoodlesNoodleDragEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
73
73
  }
@@ -1,7 +1,7 @@
1
1
  /* Safari 17.x bug: `:scope:not(...):hover` (Flavor A) and `:scope[checked]`
2
2
  (Flavor B — attribute-removal restyle) both fail inside `@scope`.
3
3
  Selectors moved out. See docs/BROWSER-COMPAT.md §3a. */
4
- option-card-ui:not([checked]):not([disabled]):hover {
4
+ option-card-ui:not([checked]):not([disabled]):hover:not(:focus-visible) {
5
5
  background: var(--option-card-bg-hover);
6
6
  border-color: var(--option-card-border-hover);
7
7
  }
@@ -35,10 +35,10 @@ default; pass `size="sm"` for a denser numbered row.
35
35
  /** Visual variant — `default` (ghost buttons w/ hover bg) or `button` (1×1 bordered cells; active page filled). */
36
36
  variant: 'default' | 'button';
37
37
 
38
+ addEventListener(type: 'page-change', listener: (ev: PaginationPageChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
38
39
  addEventListener<K extends keyof HTMLElementEventMap>(
39
40
  type: K,
40
41
  listener: (this: UIPagination, ev: HTMLElementEventMap[K]) => unknown,
41
42
  options?: boolean | AddEventListenerOptions,
42
43
  ): void;
43
- addEventListener(type: 'page-change', listener: (ev: PaginationPageChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
44
44
  }
@@ -38,10 +38,10 @@ grabber when resizable).
38
38
  */
39
39
  side: '' | 'leading' | 'trailing';
40
40
 
41
+ addEventListener(type: 'toggle', listener: (ev: PaneToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
41
42
  addEventListener<K extends keyof HTMLElementEventMap>(
42
43
  type: K,
43
44
  listener: (this: UIPane, ev: HTMLElementEventMap[K]) => unknown,
44
45
  options?: boolean | AddEventListenerOptions,
45
46
  ): void;
46
- addEventListener(type: 'toggle', listener: (ev: PaneToggleEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
47
47
  }
@@ -50,10 +50,10 @@ meter.value = e.target.value)`.
50
50
  */
51
51
  value: string;
52
52
 
53
+ addEventListener(type: 'score-change', listener: (ev: PasswordStrengthScoreChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
53
54
  addEventListener<K extends keyof HTMLElementEventMap>(
54
55
  type: K,
55
56
  listener: (this: UIPasswordStrength, ev: HTMLElementEventMap[K]) => unknown,
56
57
  options?: boolean | AddEventListenerOptions,
57
58
  ): void;
58
- addEventListener(type: 'score-change', listener: (ev: PasswordStrengthScoreChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
59
59
  }
@@ -13,23 +13,29 @@
13
13
  }
14
14
  ],
15
15
  "properties": {
16
- "complete": {
17
- "description": "Component property: complete.",
18
- "type": "boolean",
19
- "default": false
20
- },
21
16
  "component": {
22
17
  "const": "PipelineStatus"
23
18
  },
24
19
  "message": {
25
- "description": "Component property: message.",
20
+ "description": "Current stage description shown alongside the stage label.",
26
21
  "type": "string",
27
22
  "default": ""
28
23
  },
29
24
  "stage": {
30
- "description": "Component property: stage.",
25
+ "description": "Current stage name (interpret, analyze, plan, generate, validate, render).",
31
26
  "type": "string",
32
27
  "default": ""
28
+ },
29
+ "status": {
30
+ "description": "Canonical stage-progress state. Set to `completed` to render the\nfinal state and collapse the history log.",
31
+ "type": "string",
32
+ "enum": [
33
+ "idle",
34
+ "active",
35
+ "completed",
36
+ "error"
37
+ ],
38
+ "default": "idle"
33
39
  }
34
40
  },
35
41
  "required": [
@@ -59,7 +65,7 @@
59
65
  },
60
66
  {
61
67
  "description": "Pipeline status marked complete; indicator switches to success state.",
62
- "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\": \"All 100 intents passed\",\n \"complete\": true\n }\n]",
68
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\": \"All 100 intents passed\",\n \"status\": \"completed\"\n }\n]",
63
69
  "name": "complete"
64
70
  }
65
71
  ],
@@ -13,10 +13,11 @@
13
13
  import { UIElement } from '../../core/element.js';
14
14
 
15
15
  export class UIPipelineStatus extends UIElement {
16
- /** Component property: complete. */
17
- complete: boolean;
18
- /** Component property: message. */
16
+ /** Current stage description shown alongside the stage label. */
19
17
  message: string;
20
- /** Component property: stage. */
18
+ /** Current stage name (interpret, analyze, plan, generate, validate, render). */
21
19
  stage: string;
20
+ /** Canonical stage-progress state. Set to `completed` to render the
21
+ final state and collapse the history log. */
22
+ status: 'idle' | 'active' | 'completed' | 'error';
22
23
  }
@@ -11,19 +11,25 @@ description: >-
11
11
  updates via the status property. Use for long-running agent or build
12
12
  pipelines; for multi-step wizards use <stepper-ui> instead.
13
13
  props:
14
- complete:
15
- description: 'Component property: complete.'
16
- type: boolean
17
- default: false
18
- reflect: false # declarative presentational attribute — CSS/author-time read, non-reactive by design
19
- message:
20
- description: 'Component property: message.'
14
+ stage:
15
+ description: 'Current stage name (interpret, analyze, plan, generate, validate, render).'
21
16
  type: string
22
17
  default: ''
23
- stage:
24
- description: 'Component property: stage.'
18
+ message:
19
+ description: 'Current stage description shown alongside the stage label.'
25
20
  type: string
26
21
  default: ''
22
+ status:
23
+ description: |-
24
+ Canonical stage-progress state. Set to `completed` to render the
25
+ final state and collapse the history log.
26
+ type: string
27
+ default: idle
28
+ enum:
29
+ - idle
30
+ - active
31
+ - completed
32
+ - error
27
33
  events: {}
28
34
  slots:
29
35
  default:
@@ -37,7 +43,7 @@ a2ui:
37
43
  rules:
38
44
  - rule: 'Single updating pipeline status indicator — status pill with optional progress bar.'
39
45
  reason: 'Long-running task indicator.'
40
- - rule: 'stage + complete + message attributes drive the current display state.'
46
+ - rule: 'stage + status + message attributes drive the current display state.'
41
47
  reason: 'State contract.'
42
48
  - rule: 'For multi-step wizards use <stepper-ui>; for chronological history use <timeline-ui>; pipeline-status is a single current-state pill.'
43
49
  reason: 'Sibling boundary.'
@@ -61,7 +67,7 @@ examples:
61
67
  - name: complete
62
68
  description: Pipeline status marked complete; indicator switches to success state.
63
69
  a2ui: "[\n {\n \"id\": \"root\",\n \"component\": \"PipelineStatus\",\n \"stage\": \"Done\",\n \"message\"\
64
- : \"All 100 intents passed\",\n \"complete\": true\n }\n]"
70
+ : \"All 100 intents passed\",\n \"status\": \"completed\"\n }\n]"
65
71
  keywords:
66
72
  - pipelinestatus
67
73
  - pipeline-status
@@ -84,8 +84,8 @@
84
84
  },
85
85
  "examples": [
86
86
  {
87
- "description": "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers.",
88
- "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"hdr\",\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"hdr\",\n \"component\": \"Header\",\n \"children\": [\n \"title\",\n \"desc\"\n ]\n },\n {\n \"id\": \"title\",\n \"component\": \"Text\",\n \"slot\": \"heading\",\n \"textContent\": \"Choose a Plan\",\n \"variant\": \"section\"\n },\n {\n \"id\": \"desc\",\n \"component\": \"Text\",\n \"slot\": \"description\",\n \"variant\": \"caption\",\n \"textContent\": \"Select the plan that works best for you\"\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"options\"\n ]\n },\n {\n \"id\": \"options\",\n \"component\": \"Column\",\n \"children\": [\n \"basic\",\n \"pro\",\n \"enterprise\"\n ],\n \"gap\": \"3\"\n },\n {\n \"id\": \"basic\",\n \"component\": \"Radio\",\n \"label\": \"Basic — $9/mo\",\n \"value\": \"basic\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"pro\",\n \"component\": \"Radio\",\n \"label\": \"Pro — $29/mo\",\n \"value\": \"pro\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"enterprise\",\n \"component\": \"Radio\",\n \"label\": \"Enterprise — $99/mo\",\n \"value\": \"enterprise\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"ftr\",\n \"component\": \"Footer\",\n \"children\": [\n \"confirm-btn\"\n ]\n },\n {\n \"id\": \"confirm-btn\",\n \"component\": \"Button\",\n \"text\": \"Confirm Plan\",\n \"variant\": \"primary\",\n \"block\": true,\n \"slot\": \"action\"\n }\n]",
87
+ "description": "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers. The options are wrapped in radio-group-ui (gh#729) — its own [label] IS the group's accessible name (role=radiogroup + aria-labelledby), not a sibling Text node.",
88
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Card\",\n \"children\": [\n \"hdr\",\n \"sec\",\n \"ftr\"\n ]\n },\n {\n \"id\": \"hdr\",\n \"component\": \"Header\",\n \"children\": [\n \"title\"\n ]\n },\n {\n \"id\": \"title\",\n \"component\": \"Text\",\n \"slot\": \"heading\",\n \"textContent\": \"Choose a Plan\",\n \"variant\": \"section\"\n },\n {\n \"id\": \"sec\",\n \"component\": \"Section\",\n \"children\": [\n \"options\"\n ]\n },\n {\n \"id\": \"options\",\n \"component\": \"RadioGroup\",\n \"label\": \"Select the plan that works best for you\",\n \"children\": [\n \"basic\",\n \"pro\",\n \"enterprise\"\n ]\n },\n {\n \"id\": \"basic\",\n \"component\": \"Radio\",\n \"label\": \"Basic — $9/mo\",\n \"value\": \"basic\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"pro\",\n \"component\": \"Radio\",\n \"label\": \"Pro — $29/mo\",\n \"value\": \"pro\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"enterprise\",\n \"component\": \"Radio\",\n \"label\": \"Enterprise — $99/mo\",\n \"value\": \"enterprise\",\n \"name\": \"plan\"\n },\n {\n \"id\": \"ftr\",\n \"component\": \"Footer\",\n \"children\": [\n \"confirm-btn\"\n ]\n },\n {\n \"id\": \"confirm-btn\",\n \"component\": \"Button\",\n \"text\": \"Confirm Plan\",\n \"variant\": \"primary\",\n \"block\": true,\n \"slot\": \"action\"\n }\n]",
89
89
  "name": "radio-group"
90
90
  }
91
91
  ],
@@ -102,7 +102,8 @@
102
102
  ],
103
103
  "name": "UIRadio",
104
104
  "related": [
105
- "button"
105
+ "button",
106
+ "radio-group"
106
107
  ],
107
108
  "slots": {
108
109
  "dot": {
@@ -2,11 +2,11 @@
2
2
  `:scope[checked] [descendant]` (Flavor B — attribute-removal restyle)
3
3
  both fail inside `@scope`. Selectors moved out. See
4
4
  docs/BROWSER-COMPAT.md §3a. */
5
- radio-ui:not([disabled]):hover [slot="dot"] {
5
+ radio-ui:not([disabled]):hover:not(:focus-visible) [slot="dot"] {
6
6
  border-color: var(--radio-border-hover);
7
7
  background: var(--radio-bg-hover);
8
8
  }
9
- radio-ui[checked]:not([disabled]):hover [slot="dot"] {
9
+ radio-ui[checked]:not([disabled]):hover:not(:focus-visible) [slot="dot"] {
10
10
  background: var(--radio-bg-checked-hover);
11
11
  border-color: var(--radio-bg-checked-hover);
12
12
  }
@@ -111,7 +111,7 @@ tokens:
111
111
  description: Override transition timing
112
112
  a2ui:
113
113
  rules:
114
- - "Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For radio groups, the canonical pattern is a column of bare <radio-ui label='…'> elements sharing a [name=] — no field-ui wrapper around each radio."
114
+ - "Self-labeling widget — use the [label] attribute directly; do NOT wrap in <field-ui>. The widget renders its own label inline via CSS attr() pattern. For radio groups, the canonical pattern is <radio-group-ui label='…'> wrapping bare <radio-ui label='…'> elements sharing a [name=] — radio-group-ui supplies the group's accessible name (role=radiogroup + aria-labelledby); a bare Column of radios cannot (gh#729)."
115
115
  - rule: 'Use the [name] attribute to group radios — exactly one is selected per name group; form-associated.'
116
116
  reason: 'Standard radio-group semantics; name= is the group key.'
117
117
  - rule: 'For button-style single-select clusters (visually richer) use <segmented-ui> + <segment-ui> instead.'
@@ -127,7 +127,7 @@ anti_patterns:
127
127
  rule: Use [label] on radio-ui directly; do not wrap in field-ui.
128
128
  examples:
129
129
  - name: radio-group
130
- description: "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers."
130
+ description: "Card with radio group for plan selection: Basic, Pro, and Enterprise tiers. The options are wrapped in radio-group-ui (gh#729) — its own [label] IS the group's accessible name (role=radiogroup + aria-labelledby), not a sibling Text node."
131
131
  a2ui: >-
132
132
  [
133
133
  {
@@ -143,8 +143,7 @@ examples:
143
143
  "id": "hdr",
144
144
  "component": "Header",
145
145
  "children": [
146
- "title",
147
- "desc"
146
+ "title"
148
147
  ]
149
148
  },
150
149
  {
@@ -154,13 +153,6 @@ examples:
154
153
  "textContent": "Choose a Plan",
155
154
  "variant": "section"
156
155
  },
157
- {
158
- "id": "desc",
159
- "component": "Text",
160
- "slot": "description",
161
- "variant": "caption",
162
- "textContent": "Select the plan that works best for you"
163
- },
164
156
  {
165
157
  "id": "sec",
166
158
  "component": "Section",
@@ -170,13 +162,13 @@ examples:
170
162
  },
171
163
  {
172
164
  "id": "options",
173
- "component": "Column",
165
+ "component": "RadioGroup",
166
+ "label": "Select the plan that works best for you",
174
167
  "children": [
175
168
  "basic",
176
169
  "pro",
177
170
  "enterprise"
178
- ],
179
- "gap": "3"
171
+ ]
180
172
  },
181
173
  {
182
174
  "id": "basic",
@@ -262,3 +254,4 @@ synonyms:
262
254
  - settings
263
255
  related:
264
256
  - button
257
+ - radio-group
@@ -0,0 +1,118 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/RadioGroup.json",
4
+ "title": "RadioGroup",
5
+ "description": "Groups a set of bare <radio-ui label=\"…\"> children under one accessible name — role=\"radiogroup\" on the host plus aria-labelledby pointing at its own [label]. Radio-ui already supports ancestor fieldset/[role=radiogroup] grouping for exclusivity + arrow-key navigation (unchanged by this wrapper); a plain layout container (Column) can carry neither the role nor the label association, which left the canonical questionnaire shape (radio.yaml's own example) with no programmatic question↔group link (gh#729). Use this wherever a question/prompt introduces a set of mutually-exclusive radio-ui options — it is the ONLY primitive that carries the group's accessible name; radio-ui itself stays self-labeling per-option.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "required": {
17
+ "description": "Marks the group as requiring a selection for form validation (visual + aria-required only — the constraint itself lives on each radio-ui's own [required]).",
18
+ "type": "boolean",
19
+ "default": false
20
+ },
21
+ "component": {
22
+ "const": "RadioGroup"
23
+ },
24
+ "label": {
25
+ "description": "The question or prompt text — rendered above the options and wired to the host via aria-labelledby. This is the group's accessible name; without it the group has none.",
26
+ "type": "string",
27
+ "default": ""
28
+ }
29
+ },
30
+ "required": [
31
+ "component",
32
+ "label"
33
+ ],
34
+ "unevaluatedProperties": false,
35
+ "x-adiaui": {
36
+ "anti_patterns": [
37
+ {
38
+ "description": "Wrapping radio-ui options in a bare Column/Section with the question as a sibling Text node instead of radio-group-ui's [label].",
39
+ "right": "<radio-group-ui label=\"How often do you exercise?\">\n <radio-ui label=\"Never\" name=\"exercise\"></radio-ui>\n <radio-ui label=\"Sometimes\" name=\"exercise\"></radio-ui>\n</radio-group-ui>\n",
40
+ "rule": "The question that introduces a radio-ui set belongs in radio-group-ui's [label], not a sibling Text node.",
41
+ "wrong": "<column>\n <text>How often do you exercise?</text>\n <radio-ui label=\"Never\" name=\"exercise\"></radio-ui>\n <radio-ui label=\"Sometimes\" name=\"exercise\"></radio-ui>\n</column>\n"
42
+ }
43
+ ],
44
+ "category": "input",
45
+ "composes": [],
46
+ "events": {},
47
+ "examples": [
48
+ {
49
+ "description": "A single scored-questionnaire item: the question as the group's accessible label, three bare radio-ui options sharing one [name=].",
50
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"RadioGroup\",\n \"label\": \"Little interest or pleasure in doing things?\",\n \"children\": [\"opt-0\", \"opt-1\", \"opt-2\"]\n },\n { \"id\": \"opt-0\", \"component\": \"Radio\", \"label\": \"Not at all\", \"name\": \"phq2-1\", \"value\": \"0\" },\n { \"id\": \"opt-1\", \"component\": \"Radio\", \"label\": \"Several days\", \"name\": \"phq2-1\", \"value\": \"1\" },\n { \"id\": \"opt-2\", \"component\": \"Radio\", \"label\": \"More than half the days\", \"name\": \"phq2-1\", \"value\": \"2\" }\n]",
51
+ "name": "questionnaire-item"
52
+ }
53
+ ],
54
+ "keywords": [
55
+ "radio",
56
+ "radiogroup",
57
+ "fieldset",
58
+ "legend",
59
+ "questionnaire",
60
+ "survey",
61
+ "poll",
62
+ "accessibility",
63
+ "aria-labelledby"
64
+ ],
65
+ "name": "UIRadioGroup",
66
+ "related": [
67
+ "radio",
68
+ "field"
69
+ ],
70
+ "slots": {
71
+ "default": {
72
+ "description": "radio-ui children sharing one [name=] — exclusivity and arrow-key navigation are unchanged; they still resolve via closest('fieldset, [role=radiogroup]'), which now matches this host."
73
+ },
74
+ "label": {
75
+ "description": "Auto-minted label element (mounted by radio-group-ui from the [label] prop) — do not author this slot directly."
76
+ }
77
+ },
78
+ "states": [
79
+ {
80
+ "description": "Default, ready for interaction.",
81
+ "name": "idle"
82
+ }
83
+ ],
84
+ "status": "stable",
85
+ "synonyms": {
86
+ "poll": [
87
+ "radio-group",
88
+ "questionnaire"
89
+ ],
90
+ "questionnaire": [
91
+ "radio-group",
92
+ "form",
93
+ "survey"
94
+ ],
95
+ "survey": [
96
+ "radio-group",
97
+ "questionnaire"
98
+ ]
99
+ },
100
+ "tag": "radio-group-ui",
101
+ "tokens": {
102
+ "--radio-group-gap": {
103
+ "description": "Gap between the label and the option stack, and between each option."
104
+ },
105
+ "--radio-group-label-color": {
106
+ "description": "Override the question/prompt label color."
107
+ },
108
+ "--radio-group-label-font-size": {
109
+ "description": "Override the question/prompt label font size."
110
+ },
111
+ "--radio-group-label-font-weight": {
112
+ "description": "Override the question/prompt label font weight."
113
+ }
114
+ },
115
+ "traits": [],
116
+ "version": 1
117
+ }
118
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Non-side-effect class export for `<radio-group-ui>`.
3
+ *
4
+ * Importing this file gives you the class(es) without auto-registering the tag.
5
+ * Useful for test isolation, subclassing with tag-name override, or selective
6
+ * composition.
7
+ *
8
+ * The auto-register path stays at `@adia-ai/web-components/components/radio-group`
9
+ * (which imports this file + calls `defineIfFree()`).
10
+ *
11
+ * @see ../../USAGE.md#registration--auto-vs-explicit
12
+ */
13
+
14
+ /**
15
+ * <radio-group-ui label="Choose a plan">
16
+ * <radio-ui label="Basic" name="plan" value="basic"></radio-ui>
17
+ * <radio-ui label="Pro" name="plan" value="pro"></radio-ui>
18
+ * </radio-group-ui>
19
+ *
20
+ * Programmatic question↔group association for a set of bare radio-ui
21
+ * options (gh#729). role="radiogroup" on the host + aria-labelledby
22
+ * pointing at an auto-minted label element rendered from [label] — the
23
+ * ONLY job this component does. Exclusivity + arrow-key navigation are
24
+ * unchanged: radio-ui's own #select/#onKey already resolve their group
25
+ * via `closest('fieldset, [role="radiogroup"]')` (radio.class.js:48),
26
+ * which now matches this host instead of falling through to
27
+ * `this.parentElement`.
28
+ *
29
+ * Not a form-associated element itself — it carries no value; each child
30
+ * radio-ui is its own form participant per the existing [name=] contract.
31
+ */
32
+ import { UIElement } from '../../core/element.js';
33
+
34
+ export class UIRadioGroup extends UIElement {
35
+ static properties = {
36
+ label: { type: String, default: '', reflect: true },
37
+ required: { type: Boolean, default: false, reflect: true },
38
+ };
39
+
40
+ static template = () => null;
41
+
42
+ #labelEl = null;
43
+
44
+ connected() {
45
+ this.setAttribute('role', 'radiogroup');
46
+ this.#ensureLabelElement();
47
+ if (this.#labelEl?.id) this.setAttribute('aria-labelledby', this.#labelEl.id);
48
+ }
49
+
50
+ disconnected() {
51
+ this.#labelEl = null;
52
+ }
53
+
54
+ render() {
55
+ if (this.#labelEl) this.#labelEl.textContent = this.label || '';
56
+ if (this.required) this.setAttribute('aria-required', 'true');
57
+ else this.removeAttribute('aria-required');
58
+ }
59
+
60
+ #ensureLabelElement() {
61
+ this.#labelEl = this.querySelector(':scope > [data-radio-group-label]');
62
+ if (!this.#labelEl) {
63
+ const el = document.createElement('div');
64
+ el.setAttribute('slot', 'label');
65
+ el.setAttribute('data-radio-group-label', '');
66
+ el.id = UIRadioGroup.#nextId();
67
+ el.textContent = this.label || '';
68
+ this.prepend(el);
69
+ this.#labelEl = el;
70
+ }
71
+ }
72
+
73
+ static #idSeq = 0;
74
+ static #nextId() { return `radio-group-label-${++UIRadioGroup.#idSeq}`; }
75
+ }
@@ -0,0 +1,31 @@
1
+ @scope (radio-group-ui) {
2
+ :where(:scope) {
3
+ /* ── Tokens ── */
4
+ --radio-group-gap: var(--a-space-2);
5
+ --radio-group-label-color: var(--md-sys-color-neutral-on-surface);
6
+ --radio-group-label-font-size: var(--a-ui-md);
7
+ --radio-group-label-font-weight: var(--a-weight-medium);
8
+ }
9
+
10
+ /* ── Base — vertical stack: label row, then each radio-ui option. ── */
11
+ :scope {
12
+ box-sizing: border-box;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--radio-group-gap);
16
+ }
17
+
18
+ :scope [slot="label"] {
19
+ color: var(--radio-group-label-color);
20
+ font-size: var(--radio-group-label-font-size);
21
+ font-weight: var(--radio-group-label-font-weight);
22
+ }
23
+
24
+ /* No accessible name set → hide the (empty) label row rather than
25
+ reserve blank space. label is required in practice (radio-group-ui's
26
+ entire purpose is carrying it); this only guards the transient/
27
+ misuse case. */
28
+ :scope:not([label]) [slot="label"] {
29
+ display: none;
30
+ }
31
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `<radio-group-ui>` — Groups a set of bare <radio-ui label="…"> children under one accessible name — role="radiogroup" on the host plus aria-labelledby pointing at its own [label]. Radio-ui already supports ancestor fieldset/[role=radiogroup] grouping for exclusivity + arrow-key navigation (unchanged by this wrapper); a plain layout container (Column) can carry neither the role nor the label association, which left the canonical questionnaire shape (radio.yaml's own example) with no programmatic question↔group link (gh#729). Use this wherever a question/prompt introduces a set of mutually-exclusive radio-ui options — it is the ONLY primitive that carries the group's accessible name; radio-ui itself stays self-labeling per-option.
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/radio-group
5
+ *
6
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
11
+ */
12
+
13
+ import { UIElement } from '../../core/element.js';
14
+
15
+ export class UIRadioGroup extends UIElement {
16
+ /** Marks the group as requiring a selection for form validation (visual + aria-required only — the constraint itself lives on each radio-ui's own [required]). */
17
+ required: boolean;
18
+ /** The question or prompt text — rendered above the options and wired to the host via aria-labelledby. This is the group's accessible name; without it the group has none. */
19
+ label: string;
20
+ }
@@ -0,0 +1,37 @@
1
+ # radio-group — Examples
2
+
3
+ ## label
4
+
5
+ ```html
6
+ <radio-group-ui label="Little interest or pleasure in doing things?">
7
+ <radio-ui label="Not at all" name="rg-demo-phq" value="0" checked></radio-ui>
8
+ <radio-ui label="Several days" name="rg-demo-phq" value="1"></radio-ui>
9
+ <radio-ui label="More than half the days" name="rg-demo-phq" value="2"></radio-ui>
10
+ </radio-group-ui>
11
+ ```
12
+
13
+ ## required
14
+
15
+ ```html
16
+ <radio-group-ui label="How would you rate your overall health?" required>
17
+ <radio-ui label="Excellent" name="rg-demo-health" value="excellent" required></radio-ui>
18
+ <radio-ui label="Good" name="rg-demo-health" value="good" required></radio-ui>
19
+ <radio-ui label="Fair" name="rg-demo-health" value="fair" required></radio-ui>
20
+ <radio-ui label="Poor" name="rg-demo-health" value="poor" required></radio-ui>
21
+ </radio-group-ui>
22
+ ```
23
+
24
+ ## Combinations
25
+
26
+ ```html
27
+ <col-ui gap="5">
28
+ <radio-group-ui label="Little interest or pleasure in doing things?">
29
+ <radio-ui label="Not at all" name="rg-demo-multi-1" value="0" checked></radio-ui>
30
+ <radio-ui label="Several days" name="rg-demo-multi-1" value="1"></radio-ui>
31
+ </radio-group-ui>
32
+ <radio-group-ui label="Feeling down, depressed, or hopeless?">
33
+ <radio-ui label="Not at all" name="rg-demo-multi-2" value="0"></radio-ui>
34
+ <radio-ui label="Several days" name="rg-demo-multi-2" value="1" checked></radio-ui>
35
+ </radio-group-ui>
36
+ </col-ui>
37
+ ```
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `<radio-group-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIRadioGroup } from '@adia-ai/web-components/components/radio-group/class';
8
+ *
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
10
+ */
11
+
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIRadioGroup } from './radio-group.class.js';
14
+
15
+ defineIfFree('radio-group-ui', UIRadioGroup);
16
+
17
+ export { UIRadioGroup };