@orange-halo/components 2.2.0 → 3.1.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.
Files changed (140) hide show
  1. package/custom-elements.json +565 -328
  2. package/dist/base.d.ts.map +1 -1
  3. package/dist/base.js +7 -0
  4. package/dist/base.js.map +1 -1
  5. package/dist/index.d.ts +2 -7
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +0 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/oh-accordion.d.ts +53 -8
  10. package/dist/oh-accordion.d.ts.map +1 -1
  11. package/dist/oh-accordion.js +83 -21
  12. package/dist/oh-accordion.js.map +1 -1
  13. package/dist/oh-alert.d.ts +26 -8
  14. package/dist/oh-alert.d.ts.map +1 -1
  15. package/dist/oh-alert.js +32 -14
  16. package/dist/oh-alert.js.map +1 -1
  17. package/dist/oh-badge.d.ts +32 -13
  18. package/dist/oh-badge.d.ts.map +1 -1
  19. package/dist/oh-badge.js +35 -16
  20. package/dist/oh-badge.js.map +1 -1
  21. package/dist/oh-breadcrumb.d.ts +39 -12
  22. package/dist/oh-breadcrumb.d.ts.map +1 -1
  23. package/dist/oh-breadcrumb.js +43 -21
  24. package/dist/oh-breadcrumb.js.map +1 -1
  25. package/dist/oh-button.d.ts +50 -24
  26. package/dist/oh-button.d.ts.map +1 -1
  27. package/dist/oh-button.js +66 -47
  28. package/dist/oh-button.js.map +1 -1
  29. package/dist/oh-card.d.ts +47 -19
  30. package/dist/oh-card.d.ts.map +1 -1
  31. package/dist/oh-card.js +110 -82
  32. package/dist/oh-card.js.map +1 -1
  33. package/dist/oh-checkbox-group.d.ts +56 -12
  34. package/dist/oh-checkbox-group.d.ts.map +1 -1
  35. package/dist/oh-checkbox-group.js +87 -45
  36. package/dist/oh-checkbox-group.js.map +1 -1
  37. package/dist/oh-checkbox.d.ts +55 -18
  38. package/dist/oh-checkbox.d.ts.map +1 -1
  39. package/dist/oh-checkbox.js +91 -59
  40. package/dist/oh-checkbox.js.map +1 -1
  41. package/dist/oh-cluster.d.ts +19 -14
  42. package/dist/oh-cluster.d.ts.map +1 -1
  43. package/dist/oh-cluster.js +19 -19
  44. package/dist/oh-cluster.js.map +1 -1
  45. package/dist/oh-container.d.ts +19 -16
  46. package/dist/oh-container.d.ts.map +1 -1
  47. package/dist/oh-container.js +26 -33
  48. package/dist/oh-container.js.map +1 -1
  49. package/dist/oh-drawer.d.ts +36 -14
  50. package/dist/oh-drawer.d.ts.map +1 -1
  51. package/dist/oh-drawer.js +57 -26
  52. package/dist/oh-drawer.js.map +1 -1
  53. package/dist/oh-footer.d.ts +37 -18
  54. package/dist/oh-footer.d.ts.map +1 -1
  55. package/dist/oh-footer.js +38 -19
  56. package/dist/oh-footer.js.map +1 -1
  57. package/dist/oh-grid.d.ts +27 -14
  58. package/dist/oh-grid.d.ts.map +1 -1
  59. package/dist/oh-grid.js +27 -19
  60. package/dist/oh-grid.js.map +1 -1
  61. package/dist/oh-icon.d.ts +21 -5
  62. package/dist/oh-icon.d.ts.map +1 -1
  63. package/dist/oh-icon.js +28 -5
  64. package/dist/oh-icon.js.map +1 -1
  65. package/dist/oh-input.d.ts +105 -21
  66. package/dist/oh-input.d.ts.map +1 -1
  67. package/dist/oh-input.js +207 -68
  68. package/dist/oh-input.js.map +1 -1
  69. package/dist/oh-link.d.ts +42 -19
  70. package/dist/oh-link.d.ts.map +1 -1
  71. package/dist/oh-link.js +56 -34
  72. package/dist/oh-link.js.map +1 -1
  73. package/dist/oh-modal.d.ts +29 -6
  74. package/dist/oh-modal.d.ts.map +1 -1
  75. package/dist/oh-modal.js +37 -16
  76. package/dist/oh-modal.js.map +1 -1
  77. package/dist/oh-navbar.d.ts +42 -19
  78. package/dist/oh-navbar.d.ts.map +1 -1
  79. package/dist/oh-navbar.js +44 -21
  80. package/dist/oh-navbar.js.map +1 -1
  81. package/dist/oh-progress.d.ts +26 -7
  82. package/dist/oh-progress.d.ts.map +1 -1
  83. package/dist/oh-progress.js +32 -13
  84. package/dist/oh-progress.js.map +1 -1
  85. package/dist/oh-radio-group.d.ts +55 -13
  86. package/dist/oh-radio-group.d.ts.map +1 -1
  87. package/dist/oh-radio-group.js +84 -44
  88. package/dist/oh-radio-group.js.map +1 -1
  89. package/dist/oh-radio.d.ts +56 -18
  90. package/dist/oh-radio.d.ts.map +1 -1
  91. package/dist/oh-radio.js +93 -60
  92. package/dist/oh-radio.js.map +1 -1
  93. package/dist/oh-select.d.ts +58 -16
  94. package/dist/oh-select.d.ts.map +1 -1
  95. package/dist/oh-select.js +72 -36
  96. package/dist/oh-select.js.map +1 -1
  97. package/dist/oh-skip-link.d.ts +33 -15
  98. package/dist/oh-skip-link.d.ts.map +1 -1
  99. package/dist/oh-skip-link.js +39 -26
  100. package/dist/oh-skip-link.js.map +1 -1
  101. package/dist/oh-stack.d.ts +17 -14
  102. package/dist/oh-stack.d.ts.map +1 -1
  103. package/dist/oh-stack.js +18 -20
  104. package/dist/oh-stack.js.map +1 -1
  105. package/dist/oh-tabs.d.ts +56 -30
  106. package/dist/oh-tabs.d.ts.map +1 -1
  107. package/dist/oh-tabs.js +89 -70
  108. package/dist/oh-tabs.js.map +1 -1
  109. package/dist/oh-textarea.d.ts +37 -6
  110. package/dist/oh-textarea.d.ts.map +1 -1
  111. package/dist/oh-textarea.js +40 -9
  112. package/dist/oh-textarea.js.map +1 -1
  113. package/dist/oh-toggle.d.ts +45 -12
  114. package/dist/oh-toggle.d.ts.map +1 -1
  115. package/dist/oh-toggle.js +68 -33
  116. package/dist/oh-toggle.js.map +1 -1
  117. package/dist/styles/typography.d.ts.map +1 -1
  118. package/dist/styles/typography.js +1 -2
  119. package/dist/styles/typography.js.map +1 -1
  120. package/package.json +3 -7
  121. package/dist/oh-button.figma.d.ts +0 -2
  122. package/dist/oh-button.figma.d.ts.map +0 -1
  123. package/dist/oh-button.figma.js +0 -18
  124. package/dist/oh-button.figma.js.map +0 -1
  125. package/dist/oh-checkbox-group.figma.d.ts +0 -2
  126. package/dist/oh-checkbox-group.figma.d.ts.map +0 -1
  127. package/dist/oh-checkbox-group.figma.js +0 -24
  128. package/dist/oh-checkbox-group.figma.js.map +0 -1
  129. package/dist/oh-hero.d.ts +0 -33
  130. package/dist/oh-hero.d.ts.map +0 -1
  131. package/dist/oh-hero.js +0 -248
  132. package/dist/oh-hero.js.map +0 -1
  133. package/dist/oh-input.figma.d.ts +0 -2
  134. package/dist/oh-input.figma.d.ts.map +0 -1
  135. package/dist/oh-input.figma.js +0 -36
  136. package/dist/oh-input.figma.js.map +0 -1
  137. package/dist/oh-radio-group.figma.d.ts +0 -2
  138. package/dist/oh-radio-group.figma.d.ts.map +0 -1
  139. package/dist/oh-radio-group.figma.js +0 -22
  140. package/dist/oh-radio-group.figma.js.map +0 -1
package/dist/oh-radio.js CHANGED
@@ -1,28 +1,24 @@
1
1
  /**
2
2
  * oh-radio
3
3
  *
4
- * A chunky, playful radio button with hand-crafted feel.
5
- * Matches the inset/pressed aesthetic of oh-input.
6
- *
7
4
  * DESIGN DECISIONS:
8
5
  * ─────────────────
9
- * 1. CHUNKY & TACTILE
10
- * - Thick border (stroke.control)
11
- * - Inset shadow when unchecked (pressed into page)
12
- * - Fully circular (radio = circle)
6
+ * 1. LOOK FROM TOKENS
7
+ * - Border: stroke.control in color.border.emphasis
8
+ * - Circular: shape.pill
9
+ * - Shadow: offset by elevation.offset.* in elevation.color.secondary; hover and
10
+ * press move the circle by lift.hoverSm / lift.pressSm
13
11
  *
14
12
  * 2. CHECKED STATE
15
- * - Filled dot in center
16
- * - Primary color ring
17
- * - Shadow shifts to feel "activated"
13
+ * - Filled with color.brand.primary, a centre dot in color.text.onBrand,
14
+ * and the shadow in elevation.color.primary
18
15
  *
19
16
  * 3. ACCESSIBILITY
20
- * - Native radio hidden but accessible
17
+ * - A native radio, visually hidden but focusable and announced
21
18
  * - Label always required
22
- * - Focus ring matches input
23
- * - Works in radio groups via name attribute
24
- *
25
- * @element oh-radio
19
+ * - Focus underlines the label (a solid indicator)
20
+ * - Two oh-radios with the same name don't form a group (each native
21
+ * radio is in its own shadow root): use oh-radio-group for a set
26
22
  */
27
23
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
28
24
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -33,6 +29,44 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
33
29
  import { html, css, nothing } from "lit";
34
30
  import { customElement, property, state } from "lit/decorators.js";
35
31
  import { OhElement, baseStyles } from "./base.js";
32
+ /**
33
+ * A single radio button with its label.
34
+ *
35
+ * **Use it for** a one-off radio you manage yourself. **For a set of
36
+ * choices, use `oh-radio-group`.** Each `oh-radio` keeps its native radio
37
+ * inside its own shadow root, so two of them with the same `name` don't form
38
+ * a group: checking one doesn't uncheck the other, and arrow keys don't move
39
+ * between them. `oh-radio-group` renders the whole set in one place, with
40
+ * arrow-key navigation and one label for the group. For an independent
41
+ * on/off choice use `oh-checkbox`.
42
+ *
43
+ * **States:** unchecked, checked, hover, active, focus, error (set `error-text`).
44
+ *
45
+ * **Behaviour:** clicking the circle or label checks it. Checking sets
46
+ * `checked` and fires `oh-change`. The native radio sits in the shadow
47
+ * root, so it doesn't submit with a native form.
48
+ *
49
+ * **Accessibility:** a real native radio, visually hidden, carries the role
50
+ * and state; the drawn circle is decoration. Always set `label`: it's the
51
+ * radio's name. Focus underlines the label. The error message shows under
52
+ * the radio with an icon, sets `aria-invalid` and is linked with
53
+ * `aria-describedby`; it's announced (`role="alert"`) only when it appears
54
+ * after first render.
55
+ *
56
+ * **Look:** set by tokens — `stroke.control` in `color.border.emphasis`
57
+ * (border), `shape.pill` (circle), `typography.control.*` (label),
58
+ * `elevation.offset.*` and `elevation.color.secondary` (shadow), `lift.hoverSm` /
59
+ * `lift.pressSm` (movement on hover and press), `color.border.hover`
60
+ * (hover), `color.brand.primary` (and its hover and active tokens),
61
+ * `color.text.onBrand` and `elevation.color.primary` (checked),
62
+ * `color.feedback.error` and `elevation.color.error` (error).
63
+ *
64
+ * @element oh-radio
65
+ * @fires oh-change - The radio was checked. detail: { checked, value }
66
+ * @csspart circle - The drawn radio circle
67
+ * @csspart label - The label text
68
+ * @csspart error - The error message
69
+ */
36
70
  let OhRadio = class OhRadio extends OhElement {
37
71
  constructor() {
38
72
  super(...arguments);
@@ -40,10 +74,13 @@ let OhRadio = class OhRadio extends OhElement {
40
74
  this.label = "";
41
75
  this.name = "";
42
76
  this.value = "";
43
- this.error = false;
44
- this.errorMessage = "";
45
77
  /**
46
- * Force a visual state (for documentation/Figma sync only)
78
+ * Error shown with the control and linked to it. Setting it puts the
79
+ * control in its error state; clear it to leave that state.
80
+ */
81
+ this.errorText = "";
82
+ /**
83
+ * Force a visual state (for documentation only)
47
84
  * Values: 'hover' | 'focus'
48
85
  */
49
86
  this.forceState = "default";
@@ -58,11 +95,6 @@ let OhRadio = class OhRadio extends OhElement {
58
95
  static { this.styles = [
59
96
  baseStyles,
60
97
  css `
61
- /* ═══════════════════════════════════════════════════════════════
62
- ORANGE HALO RADIO
63
- Chunky • Tactile • Playful
64
- ═══════════════════════════════════════════════════════════════ */
65
-
66
98
  :host {
67
99
  display: inline-block;
68
100
  }
@@ -97,18 +129,18 @@ let OhRadio = class OhRadio extends OhElement {
97
129
  height: 24px;
98
130
  flex-shrink: 0;
99
131
 
100
- /* Chunky border, fully circular */
132
+ /* Border: stroke.control in color.border.emphasis; circular (shape.pill) */
101
133
  border: var(--oh-stroke-control) solid var(--oh-color-border-emphasis);
102
134
  border-radius: var(--oh-shape-pill);
103
135
 
104
- /* Offset shadow like primary button */
136
+ /* Offset shadow: elevation.offset.md in elevation.color.secondary */
105
137
  background-color: var(--oh-color-surface-base);
106
138
  box-shadow: var(--oh-elevation-offset-md) var(--oh-elevation-offset-md)
107
- 0 0 var(--oh-shadow-secondary);
139
+ 0 0 var(--oh-elevation-color-secondary);
108
140
 
109
141
  transition:
110
- transform var(--oh-motion-snappy) var(--oh-motion-ease-playful),
111
- box-shadow var(--oh-motion-snappy) var(--oh-motion-ease-playful),
142
+ transform var(--oh-motion-snappy) var(--oh-motion-ease-emphasized),
143
+ box-shadow var(--oh-motion-snappy) var(--oh-motion-ease-emphasized),
112
144
  background-color var(--oh-motion-quick) ease,
113
145
  border-color var(--oh-motion-quick) ease;
114
146
  }
@@ -118,41 +150,41 @@ let OhRadio = class OhRadio extends OhElement {
118
150
  content: "";
119
151
  position: absolute;
120
152
  top: 50%;
121
- left: 50%;
153
+ left: 50%; /* physical: centres the dot */
122
154
  width: 10px;
123
155
  height: 10px;
124
156
  background-color: var(--oh-color-text-on-brand);
125
157
  border-radius: var(--oh-shape-pill);
126
158
  transform: translate(-50%, -50%) scale(0);
127
159
  transition: transform var(--oh-motion-snappy)
128
- var(--oh-motion-ease-playful);
160
+ var(--oh-motion-ease-emphasized);
129
161
  }
130
162
 
131
163
  /* ═══════════════════════════════════════════════════════════════
132
164
  STATES
133
165
  ═══════════════════════════════════════════════════════════════ */
134
166
 
135
- /* Hover: lift up-left, shadow grows */
167
+ /* Hover: moves by lift.hoverSm; shadow offset elevation.offset.lg */
136
168
  .radio-wrapper:hover .radio-circle {
137
169
  border-color: var(--oh-color-border-hover);
138
170
  transform: translate(var(--oh-lift-hover-sm), var(--oh-lift-hover-sm));
139
171
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
140
- 0 0 var(--oh-shadow-secondary);
172
+ 0 0 var(--oh-elevation-color-secondary);
141
173
  }
142
174
 
143
- /* Active: press down-right, shadow shrinks */
175
+ /* Active: moves by lift.pressSm; shadow offset elevation.offset.xs */
144
176
  .radio-wrapper:active .radio-circle {
145
177
  transform: translate(var(--oh-lift-press-sm), var(--oh-lift-press-sm));
146
178
  box-shadow: var(--oh-elevation-offset-xs) var(--oh-elevation-offset-xs)
147
- 0 0 var(--oh-shadow-secondary);
179
+ 0 0 var(--oh-elevation-color-secondary);
148
180
  }
149
181
 
150
- /* Checked: primary color with warm shadow */
182
+ /* Checked: color.brand.primary fill, elevation.color.primary shadow */
151
183
  .native-radio:checked + .radio-circle {
152
184
  background-color: var(--oh-color-brand-primary);
153
185
  border-color: var(--oh-color-brand-primary);
154
186
  box-shadow: var(--oh-elevation-offset-md) var(--oh-elevation-offset-md)
155
- 0 0 var(--oh-shadow-primary);
187
+ 0 0 var(--oh-elevation-color-primary);
156
188
  }
157
189
 
158
190
  .native-radio:checked + .radio-circle::after {
@@ -165,7 +197,7 @@ let OhRadio = class OhRadio extends OhElement {
165
197
  border-color: var(--oh-color-brand-primary-hover);
166
198
  transform: translate(var(--oh-lift-hover-sm), var(--oh-lift-hover-sm));
167
199
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
168
- 0 0 var(--oh-shadow-primary);
200
+ 0 0 var(--oh-elevation-color-primary);
169
201
  }
170
202
 
171
203
  /* Checked + Active */
@@ -173,7 +205,7 @@ let OhRadio = class OhRadio extends OhElement {
173
205
  background-color: var(--oh-color-brand-primary-active);
174
206
  transform: translate(var(--oh-lift-press-sm), var(--oh-lift-press-sm));
175
207
  box-shadow: var(--oh-elevation-offset-xs) var(--oh-elevation-offset-xs)
176
- 0 0 var(--oh-shadow-primary);
208
+ 0 0 var(--oh-elevation-color-primary);
177
209
  }
178
210
 
179
211
  /* Focus: underline the label (accessible indicator) */
@@ -199,16 +231,16 @@ let OhRadio = class OhRadio extends OhElement {
199
231
  ERROR STATE
200
232
  ═══════════════════════════════════════════════════════════════ */
201
233
 
202
- :host([error]) .radio-circle {
234
+ :host([data-invalid]) .radio-circle {
203
235
  border-color: var(--oh-color-feedback-error);
204
236
  box-shadow: var(--oh-elevation-offset-md) var(--oh-elevation-offset-md)
205
- 0 0 var(--oh-shadow-error);
237
+ 0 0 var(--oh-elevation-color-error);
206
238
  }
207
239
 
208
- :host([error]) .radio-wrapper:hover .radio-circle {
240
+ :host([data-invalid]) .radio-wrapper:hover .radio-circle {
209
241
  border-color: var(--oh-color-feedback-error-hover);
210
242
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
211
- 0 0 var(--oh-shadow-error);
243
+ 0 0 var(--oh-elevation-color-error);
212
244
  }
213
245
 
214
246
  .error-message {
@@ -219,7 +251,9 @@ let OhRadio = class OhRadio extends OhElement {
219
251
  font-size: var(--oh-typography-body-base-font-size);
220
252
  color: var(--oh-color-feedback-error);
221
253
  margin-top: var(--oh-spacing-1);
222
- margin-left: calc(24px + var(--oh-spacing-3)); /* Align with label */
254
+ margin-inline-start: calc(
255
+ 24px + var(--oh-spacing-3)
256
+ ); /* Align with label */
223
257
  }
224
258
 
225
259
  .error-icon {
@@ -240,7 +274,7 @@ let OhRadio = class OhRadio extends OhElement {
240
274
  }
241
275
 
242
276
  /* ═══════════════════════════════════════════════════════════════
243
- FORCED STATES (for Figma Sync / documentation)
277
+ FORCED STATES (for documentation)
244
278
  ═══════════════════════════════════════════════════════════════ */
245
279
 
246
280
  /* Forced Hover - Unchecked */
@@ -248,7 +282,7 @@ let OhRadio = class OhRadio extends OhElement {
248
282
  border-color: var(--oh-color-border-hover);
249
283
  transform: translate(var(--oh-lift-hover-sm), var(--oh-lift-hover-sm));
250
284
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
251
- 0 0 var(--oh-shadow-secondary);
285
+ 0 0 var(--oh-elevation-color-secondary);
252
286
  }
253
287
 
254
288
  /* Forced Focus - Unchecked */
@@ -263,7 +297,7 @@ let OhRadio = class OhRadio extends OhElement {
263
297
  border-color: var(--oh-color-brand-primary-hover);
264
298
  transform: translate(var(--oh-lift-hover-sm), var(--oh-lift-hover-sm));
265
299
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
266
- 0 0 var(--oh-shadow-primary);
300
+ 0 0 var(--oh-elevation-color-primary);
267
301
  }
268
302
  `,
269
303
  ]; }
@@ -272,10 +306,14 @@ let OhRadio = class OhRadio extends OhElement {
272
306
  this.checked = target.checked;
273
307
  this.emit("oh-change", { checked: this.checked, value: this.value });
274
308
  }
309
+ updated(changed) {
310
+ // Styling hook for the error state (data-invalid on the host)
311
+ if (changed.has("errorText"))
312
+ this.toggleAttribute("data-invalid", !!this.errorText);
313
+ }
275
314
  willUpdate(changed) {
276
- if (this.hasUpdated &&
277
- (changed.has("error") || changed.has("errorMessage"))) {
278
- this._announceError = !!(this.error && this.errorMessage);
315
+ if (this.hasUpdated && changed.has("errorText")) {
316
+ this._announceError = !!this.errorText;
279
317
  }
280
318
  }
281
319
  render() {
@@ -290,16 +328,14 @@ let OhRadio = class OhRadio extends OhElement {
290
328
  name=${this.name}
291
329
  .value=${this.value}
292
330
  .checked=${this.checked}
293
- aria-invalid=${this.error ? "true" : "false"}
294
- aria-describedby=${this.error && this.errorMessage
295
- ? this._errorId
296
- : nothing}
331
+ aria-invalid=${this.errorText ? "true" : "false"}
332
+ aria-describedby=${this.errorText ? this._errorId : nothing}
297
333
  @change=${this._handleChange}
298
334
  />
299
335
  <span class="radio-circle" part="circle"></span>
300
336
  <span class="label" part="label">${this.label}</span>
301
337
  </label>
302
- ${this.error && this.errorMessage
338
+ ${this.errorText
303
339
  ? html `
304
340
  <div
305
341
  id=${this._errorId}
@@ -319,7 +355,7 @@ let OhRadio = class OhRadio extends OhElement {
319
355
  clip-rule="evenodd"
320
356
  />
321
357
  </svg>
322
- ${this.errorMessage}
358
+ ${this.errorText}
323
359
  </div>
324
360
  `
325
361
  : ""}
@@ -340,11 +376,8 @@ __decorate([
340
376
  property({ type: String })
341
377
  ], OhRadio.prototype, "value", void 0);
342
378
  __decorate([
343
- property({ type: Boolean, reflect: true })
344
- ], OhRadio.prototype, "error", void 0);
345
- __decorate([
346
- property({ type: String })
347
- ], OhRadio.prototype, "errorMessage", void 0);
379
+ property({ type: String, attribute: "error-text" })
380
+ ], OhRadio.prototype, "errorText", void 0);
348
381
  __decorate([
349
382
  property({ type: String, reflect: true, attribute: "force-state" })
350
383
  ], OhRadio.prototype, "forceState", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"oh-radio.js","sourceRoot":"","sources":["../src/oh-radio.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oh-radio.js","sourceRoot":"","sources":["../src/oh-radio.ts"],"names":[],"mappings":"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"}
@@ -1,31 +1,31 @@
1
1
  /**
2
2
  * oh-select
3
3
  *
4
- * A chunky, playful dropdown select with hand-crafted feel.
5
- * Custom-styled dropdown (not native) for full visual control.
4
+ * A custom dropdown select (not a native <select>), so the trigger and the
5
+ * list can be styled with tokens.
6
6
  *
7
7
  * DESIGN DECISIONS:
8
8
  * ─────────────────
9
- * 1. CHUNKY & TACTILE
10
- * - Thick border (stroke.control)
11
- * - Offset shadow (sticker effect like button)
12
- * - shape.control corners
13
- * - Solid warm fills
9
+ * 1. TRIGGER
10
+ * - A <button role="combobox">, with the same tokens as the other
11
+ * controls: stroke.control border in color.border.emphasis,
12
+ * shape.control corners, an outset shadow offset by
13
+ * elevation.offset.lg in elevation.color.secondary
14
+ * - Hover and press move it by lift.hoverMd / lift.pressSm, like oh-button
14
15
  *
15
16
  * 2. DROPDOWN PANEL
16
- * - Appears below trigger with shadow
17
- * - Options have hover/focus states
18
- * - Selected option highlighted
19
- * - Smooth open/close animation
17
+ * - A listbox below the trigger, on color.surface.highlight with
18
+ * shape.container corners
19
+ * - The highlighted option is underlined; the selected option is marked
20
+ * in color.brand.primary and set in typography.emphasis.fontWeight
21
+ * - Opens and closes with a transition (none under reduced motion)
20
22
  *
21
23
  * 3. ACCESSIBILITY
22
- * - role="combobox" with proper ARIA
23
- * - Keyboard navigation (arrows, enter, escape, home, end)
24
+ * - role="combobox" with aria-expanded, aria-controls and
25
+ * aria-activedescendant; focus stays on the trigger
26
+ * - Keyboard navigation (arrows, Enter, Space, Escape, Home, End, Tab)
24
27
  * - Type-ahead search
25
- * - Focus management
26
28
  * - Label always required
27
- *
28
- * @element oh-select
29
29
  */
30
30
  import { type PropertyValues } from "lit";
31
31
  import { OhElement } from "./base.js";
@@ -33,6 +33,48 @@ export interface SelectOption {
33
33
  label: string;
34
34
  value: string;
35
35
  }
36
+ /**
37
+ * A dropdown for choosing one option from a list.
38
+ *
39
+ * **Use it for** one choice from a list too long to show every option.
40
+ * **Don't use it for** a few options (use `oh-radio-group`, which shows them
41
+ * all), several choices (use `oh-checkbox-group`) or free text (use
42
+ * `oh-input` with `suggestions`).
43
+ *
44
+ * **Parts:** a trigger showing the selected option's label (or
45
+ * `placeholder`), and a list of `options` (`{ label, value }`). One size.
46
+ * **States:** rest, hover, active, focus, open, error (set `error-text`),
47
+ * required.
48
+ *
49
+ * **Behaviour:** click the trigger, or press Enter, Space or an arrow key,
50
+ * to open the list. Arrow keys, Home and End move the highlight, Enter or
51
+ * Space selects, Escape and Tab close, and clicking outside closes. Typing
52
+ * letters jumps to a matching option (and selects it while the list is
53
+ * closed). Selecting sets `value`, returns focus to the trigger and fires
54
+ * `oh-change`. It isn't form-associated: it doesn't submit with a native
55
+ * form, so read `value` or listen for `oh-change`. It has no `disabled`
56
+ * property.
57
+ *
58
+ * **Accessibility:** the trigger is a `combobox` controlling a `listbox`,
59
+ * with `aria-activedescendant` for the highlighted option, so focus stays
60
+ * on the trigger. Always set `label`: it names the trigger and the list.
61
+ * The error shows after the label and before the trigger, with an icon, sets
62
+ * `aria-invalid` and is linked with `aria-describedby`; it's announced
63
+ * (`role="alert"`) only when it appears after first render. Focus is a
64
+ * solid underline on the trigger text.
65
+ *
66
+ * **Look:** set by tokens — `stroke.control` in `color.border.emphasis`
67
+ * (border), `shape.control` (trigger corners), `shape.container` (list
68
+ * corners), `typography.control.*` (type), `elevation.offset.*` and
69
+ * `elevation.color.secondary` (shadow), `lift.hoverMd` / `lift.pressSm` (movement
70
+ * on hover and press), `color.border.hover` (hover), `focus.ringColor` and
71
+ * `color.surface.highlight` (open), `color.surface.hover` and
72
+ * `color.brand.primary` (selected option), `color.feedback.error` and
73
+ * `elevation.color.error` (error).
74
+ *
75
+ * @element oh-select
76
+ * @fires oh-change - An option was selected. detail: { value }
77
+ */
36
78
  export declare class OhSelect extends OhElement {
37
79
  static styles: import("lit").CSSResult[];
38
80
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"oh-select.d.ts","sourceRoot":"","sources":["../src/oh-select.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBACa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,4BA0QpB;IAGF,KAAK,SAAM;IAGX,WAAW,SAAsB;IAGjC,IAAI,SAAM;IAGV,OAAO,EAAE,YAAY,EAAE,CAAM;IAG7B,KAAK,SAAM;IAGX,QAAQ,UAAS;IAGjB,UAAU,SAAM;IAGhB,SAAS,SAAM;IAGf,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,iBAAiB,CAAM;IAG/B,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,QAAQ,CAAqB;IAGrC,OAAO,CAAC,QAAQ,CAAoB;IAEpC,OAAO,CAAC,QAAQ,CAAgE;IAChF,OAAO,CAAC,UAAU,CAAkE;IACpF,OAAO,CAAC,QAAQ,CAAgE;IAChF,OAAO,CAAC,SAAS,CAAiE;IAClF,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAuB;IAEhD,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,qBAAqB;IAqE7B,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,mBAAmB,CAIzB;IAEO,iBAAiB;IAKjB,oBAAoB;IAK7B;;;;OAIG;IAEH,OAAO,CAAC,cAAc,CAAS;IAE/B,UAAU,CAAC,OAAO,EAAE,cAAc;IAMlC,MAAM;CAoIP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
1
+ {"version":3,"file":"oh-select.d.ts","sourceRoot":"","sources":["../src/oh-select.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAE9D,OAAO,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBACa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,4BAoQpB;IAGF,KAAK,SAAM;IAGX,WAAW,SAAsB;IAGjC,IAAI,SAAM;IAGV,OAAO,EAAE,YAAY,EAAE,CAAM;IAG7B,KAAK,SAAM;IAGX,QAAQ,UAAS;IAGjB,UAAU,SAAM;IAGhB,SAAS,SAAM;IAGf,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,iBAAiB,CAAM;IAG/B,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,QAAQ,CAAqB;IAGrC,OAAO,CAAC,QAAQ,CAAoB;IAEpC,OAAO,CAAC,QAAQ,CAAgE;IAChF,OAAO,CAAC,UAAU,CAAkE;IACpF,OAAO,CAAC,QAAQ,CAAgE;IAChF,OAAO,CAAC,SAAS,CAAiE;IAClF,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,iBAAiB,CAAuB;IAEhD,OAAO,KAAK,eAAe,GAE1B;IAED,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,qBAAqB;IAqE7B,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,mBAAmB,CAIzB;IAEO,iBAAiB;IAKjB,oBAAoB;IAK7B;;;;OAIG;IAEH,OAAO,CAAC,cAAc,CAAS;IAE/B,UAAU,CAAC,OAAO,EAAE,cAAc;IAMlC,MAAM;CAoIP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
package/dist/oh-select.js CHANGED
@@ -1,31 +1,31 @@
1
1
  /**
2
2
  * oh-select
3
3
  *
4
- * A chunky, playful dropdown select with hand-crafted feel.
5
- * Custom-styled dropdown (not native) for full visual control.
4
+ * A custom dropdown select (not a native <select>), so the trigger and the
5
+ * list can be styled with tokens.
6
6
  *
7
7
  * DESIGN DECISIONS:
8
8
  * ─────────────────
9
- * 1. CHUNKY & TACTILE
10
- * - Thick border (stroke.control)
11
- * - Offset shadow (sticker effect like button)
12
- * - shape.control corners
13
- * - Solid warm fills
9
+ * 1. TRIGGER
10
+ * - A <button role="combobox">, with the same tokens as the other
11
+ * controls: stroke.control border in color.border.emphasis,
12
+ * shape.control corners, an outset shadow offset by
13
+ * elevation.offset.lg in elevation.color.secondary
14
+ * - Hover and press move it by lift.hoverMd / lift.pressSm, like oh-button
14
15
  *
15
16
  * 2. DROPDOWN PANEL
16
- * - Appears below trigger with shadow
17
- * - Options have hover/focus states
18
- * - Selected option highlighted
19
- * - Smooth open/close animation
17
+ * - A listbox below the trigger, on color.surface.highlight with
18
+ * shape.container corners
19
+ * - The highlighted option is underlined; the selected option is marked
20
+ * in color.brand.primary and set in typography.emphasis.fontWeight
21
+ * - Opens and closes with a transition (none under reduced motion)
20
22
  *
21
23
  * 3. ACCESSIBILITY
22
- * - role="combobox" with proper ARIA
23
- * - Keyboard navigation (arrows, enter, escape, home, end)
24
+ * - role="combobox" with aria-expanded, aria-controls and
25
+ * aria-activedescendant; focus stays on the trigger
26
+ * - Keyboard navigation (arrows, Enter, Space, Escape, Home, End, Tab)
24
27
  * - Type-ahead search
25
- * - Focus management
26
28
  * - Label always required
27
- *
28
- * @element oh-select
29
29
  */
30
30
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
31
31
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -36,6 +36,48 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
36
36
  import { html, css, nothing } from "lit";
37
37
  import { customElement, property, state, query } from "lit/decorators.js";
38
38
  import { OhElement, baseStyles } from "./base.js";
39
+ /**
40
+ * A dropdown for choosing one option from a list.
41
+ *
42
+ * **Use it for** one choice from a list too long to show every option.
43
+ * **Don't use it for** a few options (use `oh-radio-group`, which shows them
44
+ * all), several choices (use `oh-checkbox-group`) or free text (use
45
+ * `oh-input` with `suggestions`).
46
+ *
47
+ * **Parts:** a trigger showing the selected option's label (or
48
+ * `placeholder`), and a list of `options` (`{ label, value }`). One size.
49
+ * **States:** rest, hover, active, focus, open, error (set `error-text`),
50
+ * required.
51
+ *
52
+ * **Behaviour:** click the trigger, or press Enter, Space or an arrow key,
53
+ * to open the list. Arrow keys, Home and End move the highlight, Enter or
54
+ * Space selects, Escape and Tab close, and clicking outside closes. Typing
55
+ * letters jumps to a matching option (and selects it while the list is
56
+ * closed). Selecting sets `value`, returns focus to the trigger and fires
57
+ * `oh-change`. It isn't form-associated: it doesn't submit with a native
58
+ * form, so read `value` or listen for `oh-change`. It has no `disabled`
59
+ * property.
60
+ *
61
+ * **Accessibility:** the trigger is a `combobox` controlling a `listbox`,
62
+ * with `aria-activedescendant` for the highlighted option, so focus stays
63
+ * on the trigger. Always set `label`: it names the trigger and the list.
64
+ * The error shows after the label and before the trigger, with an icon, sets
65
+ * `aria-invalid` and is linked with `aria-describedby`; it's announced
66
+ * (`role="alert"`) only when it appears after first render. Focus is a
67
+ * solid underline on the trigger text.
68
+ *
69
+ * **Look:** set by tokens — `stroke.control` in `color.border.emphasis`
70
+ * (border), `shape.control` (trigger corners), `shape.container` (list
71
+ * corners), `typography.control.*` (type), `elevation.offset.*` and
72
+ * `elevation.color.secondary` (shadow), `lift.hoverMd` / `lift.pressSm` (movement
73
+ * on hover and press), `color.border.hover` (hover), `focus.ringColor` and
74
+ * `color.surface.highlight` (open), `color.surface.hover` and
75
+ * `color.brand.primary` (selected option), `color.feedback.error` and
76
+ * `elevation.color.error` (error).
77
+ *
78
+ * @element oh-select
79
+ * @fires oh-change - An option was selected. detail: { value }
80
+ */
39
81
  let OhSelect = class OhSelect extends OhElement {
40
82
  constructor() {
41
83
  super(...arguments);
@@ -71,11 +113,6 @@ let OhSelect = class OhSelect extends OhElement {
71
113
  static { this.styles = [
72
114
  baseStyles,
73
115
  css `
74
- /* ═══════════════════════════════════════════════════════════════
75
- ORANGE HALO SELECT
76
- Chunky • Playful • Tactile Dropdown
77
- ═══════════════════════════════════════════════════════════════ */
78
-
79
116
  :host {
80
117
  display: block;
81
118
  position: relative;
@@ -118,13 +155,13 @@ let OhSelect = class OhSelect extends OhElement {
118
155
  font-family: var(--oh-typography-control-font-family);
119
156
  font-size: var(--oh-typography-control-font-size-md);
120
157
  color: var(--oh-color-text-primary);
121
- text-align: left;
158
+ text-align: start;
122
159
 
123
160
  background-color: var(--oh-color-surface-base);
124
161
  border: var(--oh-stroke-control) solid var(--oh-color-border-emphasis);
125
162
  border-radius: var(--oh-shape-control);
126
163
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
127
- 0 0 var(--oh-shadow-secondary);
164
+ 0 0 var(--oh-elevation-color-secondary);
128
165
 
129
166
  cursor: pointer;
130
167
  -webkit-appearance: none;
@@ -132,8 +169,8 @@ let OhSelect = class OhSelect extends OhElement {
132
169
  appearance: none;
133
170
 
134
171
  transition:
135
- transform var(--oh-motion-snappy) var(--oh-motion-ease-playful),
136
- box-shadow var(--oh-motion-snappy) var(--oh-motion-ease-playful),
172
+ transform var(--oh-motion-snappy) var(--oh-motion-ease-emphasized),
173
+ box-shadow var(--oh-motion-snappy) var(--oh-motion-ease-emphasized),
137
174
  border-color var(--oh-motion-snappy) ease,
138
175
  background-color var(--oh-motion-quick) ease;
139
176
  }
@@ -171,13 +208,13 @@ let OhSelect = class OhSelect extends OhElement {
171
208
  border-color: var(--oh-color-border-hover);
172
209
  transform: translate(var(--oh-lift-hover-md), var(--oh-lift-hover-md));
173
210
  box-shadow: var(--oh-elevation-offset-2xl)
174
- var(--oh-elevation-offset-2xl) 0 0 var(--oh-shadow-secondary);
211
+ var(--oh-elevation-offset-2xl) 0 0 var(--oh-elevation-color-secondary);
175
212
  }
176
213
 
177
214
  .trigger:active {
178
215
  transform: translate(var(--oh-lift-press-sm), var(--oh-lift-press-sm));
179
216
  box-shadow: var(--oh-elevation-offset-sm) var(--oh-elevation-offset-sm)
180
- 0 0 var(--oh-shadow-secondary);
217
+ 0 0 var(--oh-elevation-color-secondary);
181
218
  }
182
219
 
183
220
  .trigger--open {
@@ -185,7 +222,7 @@ let OhSelect = class OhSelect extends OhElement {
185
222
  background-color: var(--oh-color-surface-highlight);
186
223
  }
187
224
 
188
- /* Focus: underline trigger text (accessible indicator, like button) */
225
+ /* Focus: underline the trigger text (a solid indicator, like oh-button) */
189
226
  .trigger:focus {
190
227
  outline: none;
191
228
  }
@@ -208,8 +245,7 @@ let OhSelect = class OhSelect extends OhElement {
208
245
  .dropdown {
209
246
  position: absolute;
210
247
  top: 100%;
211
- left: 0;
212
- right: 0;
248
+ inset-inline: 0;
213
249
  z-index: var(--oh-z-index-dropdown);
214
250
  margin-top: var(--oh-spacing-1);
215
251
 
@@ -217,7 +253,7 @@ let OhSelect = class OhSelect extends OhElement {
217
253
  border: var(--oh-stroke-control) solid var(--oh-color-border-emphasis);
218
254
  border-radius: var(--oh-shape-container);
219
255
  box-shadow: var(--oh-elevation-offset-2xl)
220
- var(--oh-elevation-offset-2xl) 0 0 var(--oh-shadow-secondary);
256
+ var(--oh-elevation-offset-2xl) 0 0 var(--oh-elevation-color-secondary);
221
257
 
222
258
  max-height: 0;
223
259
  overflow: hidden;
@@ -227,7 +263,7 @@ let OhSelect = class OhSelect extends OhElement {
227
263
  transition:
228
264
  max-height var(--oh-motion-gentle) ease,
229
265
  opacity var(--oh-motion-snappy) ease,
230
- transform var(--oh-motion-snappy) var(--oh-motion-ease-playful);
266
+ transform var(--oh-motion-snappy) var(--oh-motion-ease-emphasized);
231
267
  }
232
268
 
233
269
  .dropdown--open {
@@ -290,13 +326,13 @@ let OhSelect = class OhSelect extends OhElement {
290
326
  .trigger--error {
291
327
  border-color: var(--oh-color-feedback-error);
292
328
  box-shadow: var(--oh-elevation-offset-lg) var(--oh-elevation-offset-lg)
293
- 0 0 var(--oh-shadow-error);
329
+ 0 0 var(--oh-elevation-color-error);
294
330
  }
295
331
 
296
332
  .trigger--error:hover {
297
333
  border-color: var(--oh-color-feedback-error-hover);
298
334
  box-shadow: var(--oh-elevation-offset-2xl)
299
- var(--oh-elevation-offset-2xl) 0 0 var(--oh-shadow-error);
335
+ var(--oh-elevation-offset-2xl) 0 0 var(--oh-elevation-color-error);
300
336
  }
301
337
 
302
338
  .helper {
@@ -662,10 +698,10 @@ __decorate([
662
698
  property({ type: Boolean, reflect: true })
663
699
  ], OhSelect.prototype, "required", void 0);
664
700
  __decorate([
665
- property({ type: String })
701
+ property({ type: String, attribute: "helper-text" })
666
702
  ], OhSelect.prototype, "helperText", void 0);
667
703
  __decorate([
668
- property({ type: String })
704
+ property({ type: String, attribute: "error-text" })
669
705
  ], OhSelect.prototype, "errorText", void 0);
670
706
  __decorate([
671
707
  state()