@orange-halo/components 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/custom-elements.json +968 -362
  2. package/dist/base.d.ts.map +1 -1
  3. package/dist/base.js +1 -2
  4. package/dist/base.js.map +1 -1
  5. package/dist/index.d.ts +4 -8
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -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 +99 -29
  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 +96 -36
  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 +38 -19
  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 +66 -25
  24. package/dist/oh-breadcrumb.js.map +1 -1
  25. package/dist/oh-button.d.ts +49 -23
  26. package/dist/oh-button.d.ts.map +1 -1
  27. package/dist/oh-button.figma.js +1 -3
  28. package/dist/oh-button.figma.js.map +1 -1
  29. package/dist/oh-button.js +133 -82
  30. package/dist/oh-button.js.map +1 -1
  31. package/dist/oh-card.d.ts +47 -19
  32. package/dist/oh-card.d.ts.map +1 -1
  33. package/dist/oh-card.js +143 -96
  34. package/dist/oh-card.js.map +1 -1
  35. package/dist/oh-checkbox-group.d.ts +56 -12
  36. package/dist/oh-checkbox-group.d.ts.map +1 -1
  37. package/dist/oh-checkbox-group.figma.js +4 -6
  38. package/dist/oh-checkbox-group.figma.js.map +1 -1
  39. package/dist/oh-checkbox-group.js +114 -49
  40. package/dist/oh-checkbox-group.js.map +1 -1
  41. package/dist/oh-checkbox.d.ts +54 -17
  42. package/dist/oh-checkbox.d.ts.map +1 -1
  43. package/dist/oh-checkbox.js +117 -60
  44. package/dist/oh-checkbox.js.map +1 -1
  45. package/dist/oh-cluster.d.ts +19 -14
  46. package/dist/oh-cluster.d.ts.map +1 -1
  47. package/dist/oh-cluster.js +19 -19
  48. package/dist/oh-cluster.js.map +1 -1
  49. package/dist/oh-container.d.ts +19 -16
  50. package/dist/oh-container.d.ts.map +1 -1
  51. package/dist/oh-container.js +21 -23
  52. package/dist/oh-container.js.map +1 -1
  53. package/dist/oh-drawer.d.ts +28 -11
  54. package/dist/oh-drawer.d.ts.map +1 -1
  55. package/dist/oh-drawer.js +52 -24
  56. package/dist/oh-drawer.js.map +1 -1
  57. package/dist/oh-footer.d.ts +37 -18
  58. package/dist/oh-footer.d.ts.map +1 -1
  59. package/dist/oh-footer.js +38 -19
  60. package/dist/oh-footer.js.map +1 -1
  61. package/dist/oh-grid.d.ts +27 -14
  62. package/dist/oh-grid.d.ts.map +1 -1
  63. package/dist/oh-grid.js +28 -21
  64. package/dist/oh-grid.js.map +1 -1
  65. package/dist/oh-icon.d.ts +21 -5
  66. package/dist/oh-icon.d.ts.map +1 -1
  67. package/dist/oh-icon.js +27 -9
  68. package/dist/oh-icon.js.map +1 -1
  69. package/dist/oh-input.d.ts +110 -21
  70. package/dist/oh-input.d.ts.map +1 -1
  71. package/dist/oh-input.figma.js +2 -2
  72. package/dist/oh-input.figma.js.map +1 -1
  73. package/dist/oh-input.js +227 -71
  74. package/dist/oh-input.js.map +1 -1
  75. package/dist/oh-link.d.ts +42 -19
  76. package/dist/oh-link.d.ts.map +1 -1
  77. package/dist/oh-link.js +83 -38
  78. package/dist/oh-link.js.map +1 -1
  79. package/dist/oh-modal.d.ts +35 -6
  80. package/dist/oh-modal.d.ts.map +1 -1
  81. package/dist/oh-modal.js +90 -39
  82. package/dist/oh-modal.js.map +1 -1
  83. package/dist/oh-navbar.d.ts +42 -19
  84. package/dist/oh-navbar.d.ts.map +1 -1
  85. package/dist/oh-navbar.js +44 -20
  86. package/dist/oh-navbar.js.map +1 -1
  87. package/dist/oh-progress.d.ts +26 -7
  88. package/dist/oh-progress.d.ts.map +1 -1
  89. package/dist/oh-progress.js +35 -14
  90. package/dist/oh-progress.js.map +1 -1
  91. package/dist/oh-radio-group.d.ts +55 -13
  92. package/dist/oh-radio-group.d.ts.map +1 -1
  93. package/dist/oh-radio-group.figma.js +2 -2
  94. package/dist/oh-radio-group.figma.js.map +1 -1
  95. package/dist/oh-radio-group.js +122 -51
  96. package/dist/oh-radio-group.js.map +1 -1
  97. package/dist/oh-radio.d.ts +55 -17
  98. package/dist/oh-radio.d.ts.map +1 -1
  99. package/dist/oh-radio.js +120 -61
  100. package/dist/oh-radio.js.map +1 -1
  101. package/dist/oh-select.d.ts +59 -16
  102. package/dist/oh-select.d.ts.map +1 -1
  103. package/dist/oh-select.js +141 -69
  104. package/dist/oh-select.js.map +1 -1
  105. package/dist/oh-skip-link.d.ts +33 -15
  106. package/dist/oh-skip-link.d.ts.map +1 -1
  107. package/dist/oh-skip-link.js +43 -32
  108. package/dist/oh-skip-link.js.map +1 -1
  109. package/dist/oh-stack.d.ts +17 -14
  110. package/dist/oh-stack.d.ts.map +1 -1
  111. package/dist/oh-stack.js +18 -20
  112. package/dist/oh-stack.js.map +1 -1
  113. package/dist/oh-tabs.d.ts +55 -30
  114. package/dist/oh-tabs.d.ts.map +1 -1
  115. package/dist/oh-tabs.js +100 -76
  116. package/dist/oh-tabs.js.map +1 -1
  117. package/dist/oh-textarea.d.ts +129 -0
  118. package/dist/oh-textarea.d.ts.map +1 -0
  119. package/dist/oh-textarea.js +401 -0
  120. package/dist/oh-textarea.js.map +1 -0
  121. package/dist/oh-toggle.d.ts +45 -12
  122. package/dist/oh-toggle.d.ts.map +1 -1
  123. package/dist/oh-toggle.js +84 -34
  124. package/dist/oh-toggle.js.map +1 -1
  125. package/dist/styles/typography.js +1 -1
  126. package/package.json +7 -7
  127. package/dist/oh-hero.d.ts +0 -33
  128. package/dist/oh-hero.d.ts.map +0 -1
  129. package/dist/oh-hero.js +0 -246
  130. package/dist/oh-hero.js.map +0 -1
package/dist/oh-input.js CHANGED
@@ -1,28 +1,33 @@
1
1
  /**
2
2
  * oh-input
3
3
  *
4
- * A chunky, inset input field that feels pressed into the page.
5
- * On focus, it rises to meet you.
6
- *
7
4
  * DESIGN DECISIONS:
8
5
  * ─────────────────
9
- * 1. CHUNKY & CONFIDENT
10
- * - Thick border (stroke.control) that feels illustrative
11
- * - Solid fill, no subtle gradients
12
- * - shape.control corners (matches button)
6
+ * 1. LOOK FROM TOKENS
7
+ * - Border: input.base.borderWidth (stroke.control) in color.border.emphasis
8
+ * - Corners: shape.control (same as oh-button)
9
+ * - Shadow: an inset shadow offset by elevation.offset.md in elevation.color.field
13
10
  *
14
- * 2. INSET SHADOW (PRESSED IN)
15
- * - Shadow offset to top-left (inset)
16
- * - Feels pressed into the surface
17
- * - Opposite of button's outset sticker effect
11
+ * 2. FOCUS IS A SOLID RING
12
+ * - An inset ring in focus.ringColor inside the border, so focus doesn't
13
+ * shift the layout
14
+ * - Background switches to color.surface.highlight
18
15
  *
19
- * 3. FOCUS RISES UP
20
- * - Inset shadow disappears
21
- * - Outset shadow appears (field lifts)
22
- * - Border turns brand orange
23
- * - Clear visual hierarchy
16
+ * 3. A REAL FORM CONTROL
17
+ * - Form-associated, like oh-textarea: its value submits with the form
18
+ * under its name and shows in form.elements, `required` blocks
19
+ * submission, a form reset restores the starting value, and a
20
+ * disabled <fieldset> disables it
24
21
  *
25
- * @element oh-input
22
+ * 4. SUGGESTIONS ARE NATIVE
23
+ * - `suggestions` renders a <datalist> inside the component (a list
24
+ * outside it can't reach an input in the shadow root). The browser
25
+ * draws the list and handles the keyboard and screen readers, so
26
+ * people can still type anything: it suggests, it doesn't restrict
27
+ *
28
+ * 5. ERRORS BEFORE THE FIELD
29
+ * - Error text sits after the label and before the field, with an icon,
30
+ * so it's read before the field is reached. Helper text sits after
26
31
  */
27
32
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
28
33
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -33,21 +38,91 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
33
38
  import { html, css, nothing } from "lit";
34
39
  import { customElement, property, query, state } from "lit/decorators.js";
35
40
  import { OhElement, baseStyles } from "./base.js";
41
+ /**
42
+ * A single-line text field that submits with a native form, with optional
43
+ * typing suggestions.
44
+ *
45
+ * **Use it for** short free text: names, email addresses, search terms.
46
+ * **Don't use it for** several lines (use `oh-textarea`) or a choice from a
47
+ * fixed list (use `oh-select`, or `oh-radio-group` for a few options).
48
+ * `suggestions` offers values while still allowing any text.
49
+ *
50
+ * **Types:** `text` (default), `email`, `password`, `search`, `tel`, `url`.
51
+ * One size. **States:** rest, hover, focus, error (set `error-text`),
52
+ * disabled, required.
53
+ *
54
+ * **Behaviour:** form-associated: the value submits under `name`, `required`
55
+ * blocks submission (validity comes from the inner `<input>`), a form reset
56
+ * restores the starting value, and a disabled `<fieldset>` disables it.
57
+ * Fires `oh-input` as the text changes and `oh-change` when it's committed.
58
+ * `suggestions` renders a native `<datalist>`, so the browser handles its
59
+ * keyboard and screen reader support. `autocomplete` is passed to the inner
60
+ * `<input>`.
61
+ *
62
+ * **Accessibility:** always set `label`: it's the field's name. The error
63
+ * shows after the label and before the field, with an icon, and is linked
64
+ * with `aria-describedby` and `aria-invalid`; it's announced (`role="alert"`)
65
+ * only when it appears after first render. Helper text is linked the same
66
+ * way (it's hidden while an error shows). Focus is a solid inset ring.
67
+ * Set `autocomplete` for personal data (WCAG 1.3.5).
68
+ *
69
+ * **Look:** set by tokens — `input.base.borderWidth` (`stroke.control`) in
70
+ * `color.border.emphasis` (border), `shape.control` (corners),
71
+ * `typography.control.*` (type), `input.size.md.minHeight` (height),
72
+ * `elevation.offset.md` and `elevation.color.field` (inset shadow),
73
+ * `color.border.hover` (hover), `focus.ringColor` and
74
+ * `color.surface.highlight` (focus), `color.feedback.error`,
75
+ * `color.feedback.errorSubtle` and `elevation.color.error` (error),
76
+ * `color.surface.disabled`, `color.border.disabled` and
77
+ * `color.text.disabled` (disabled).
78
+ *
79
+ * @element oh-input
80
+ * @fires oh-input - Whenever the text changes: typing, or picking a suggestion. detail: { value, originalEvent }
81
+ * @fires oh-change - When the value is committed (on blur or Enter). detail: { value, originalEvent }
82
+ * @csspart label - The visible label
83
+ * @csspart input - The text field
84
+ * @csspart helper - The helper text, and the error message (which also has the error part)
85
+ * @csspart error - The error message
86
+ */
36
87
  let OhInput = class OhInput extends OhElement {
37
88
  constructor() {
38
89
  super(...arguments);
90
+ /** The kind of text: text, email, password, search, tel or url */
39
91
  this.type = "text";
92
+ /** The text in the field */
40
93
  this.value = "";
94
+ /** Example text shown while the field is empty. Not a replacement for the label */
41
95
  this.placeholder = "";
96
+ /** The visible label. Always set one: it names the field */
42
97
  this.label = "";
98
+ /** Help shown under the field and linked to it */
43
99
  this.helperText = "";
100
+ /** Error shown before the field and linked to it. Setting it puts the field in its error state */
44
101
  this.errorText = "";
102
+ /** The name the value is submitted under */
103
+ this.name = "";
104
+ /** Must be filled in before the form submits */
45
105
  this.required = false;
106
+ /** Not editable, and left out of the form's data */
107
+ this.disabled = false;
108
+ /**
109
+ * Values offered while typing, shown by the browser as a list under the
110
+ * field. They suggest, they don't restrict: any text is still allowed.
111
+ * Set it as a property (an array of strings) or, in HTML, as a JSON array:
112
+ * suggestions='["Option one","Option two"]'. Browsers also offer the
113
+ * field's autofill history, so set autocomplete="off" to show only these.
114
+ */
115
+ this.suggestions = [];
46
116
  /**
47
117
  * Force a visual state (for documentation/Figma sync only)
48
118
  * Values: 'hover' | 'focus'
49
119
  */
50
120
  this.forceState = "default";
121
+ this._internals = this.attachInternals();
122
+ /** The starting value, restored when the form resets */
123
+ this._defaultValue = "";
124
+ /** Set when a disabled <fieldset> around it disables it */
125
+ this._formDisabled = false;
51
126
  /**
52
127
  * Errors that appear after first render (e.g. on submit) are announced via
53
128
  * role="alert". Errors present on page load are only linked with
@@ -55,14 +130,10 @@ let OhInput = class OhInput extends OhElement {
55
130
  */
56
131
  this._announceError = false;
57
132
  }
133
+ static { this.formAssociated = true; }
58
134
  static { this.styles = [
59
135
  baseStyles,
60
136
  css `
61
- /* ═══════════════════════════════════════════════════════════════
62
- ORANGE HALO INPUT
63
- Chunky • Inset • Rises on Focus
64
- ═══════════════════════════════════════════════════════════════ */
65
-
66
137
  :host {
67
138
  display: block;
68
139
  }
@@ -73,7 +144,7 @@ let OhInput = class OhInput extends OhElement {
73
144
  gap: var(--oh-spacing-2);
74
145
  }
75
146
 
76
- /* ─── Label: friendly and readable (16px minimum) ─── */
147
+ /* ─── Label: typography.control.* ─── */
77
148
  .label {
78
149
  font-family: var(--oh-typography-control-font-family);
79
150
  font-size: var(--oh-typography-control-font-size-sm);
@@ -89,25 +160,28 @@ let OhInput = class OhInput extends OhElement {
89
160
 
90
161
  /* ═══════════════════════════════════════════════════════════════
91
162
  INPUT FIELD
92
- Thick border, inset shadow, solid fill
163
+ Border, inset shadow and fill from tokens
93
164
  ═══════════════════════════════════════════════════════════════ */
94
165
 
95
166
  .input {
96
167
  width: 100%;
97
168
  font-family: var(--oh-typography-control-font-family);
98
169
  color: var(--oh-color-text-primary);
99
-
100
- /* Solid fill — no gradients */
170
+
171
+ /* Fill: color.surface.base */
101
172
  background-color: var(--oh-color-surface-base);
102
-
103
- /* Thick illustrative border (WCAG 3:1 compliant) */
104
- border: var(--oh-input-base-border-width) solid var(--oh-color-border-emphasis);
173
+
174
+ /* Border: input.base.borderWidth in color.border.emphasis (a UI
175
+ contrast pair checked by check:contrast) */
176
+ border: var(--oh-input-base-border-width) solid
177
+ var(--oh-color-border-emphasis);
105
178
  border-radius: var(--oh-shape-control);
106
-
107
- /* Inset offset shadow: pressed into page */
108
- box-shadow: inset var(--oh-elevation-offset-md) var(--oh-elevation-offset-md) 0 0 var(--oh-shadow-field);
109
-
110
- transition:
179
+
180
+ /* Inset shadow: elevation.offset.md in elevation.color.field */
181
+ box-shadow: inset var(--oh-elevation-offset-md)
182
+ var(--oh-elevation-offset-md) 0 0 var(--oh-elevation-color-field);
183
+
184
+ transition:
111
185
  border-color var(--oh-motion-snappy) ease,
112
186
  box-shadow var(--oh-motion-snappy) ease,
113
187
  background-color var(--oh-motion-quick) ease;
@@ -117,8 +191,8 @@ let OhInput = class OhInput extends OhElement {
117
191
  color: var(--oh-color-text-secondary); /* Readable placeholder */
118
192
  }
119
193
 
120
- /* ─── Hover: border warms up ─── */
121
- .input:hover:not(:focus) {
194
+ /* ─── Hover: border in color.border.hover ─── */
195
+ .input:hover:not(:focus):not(:disabled) {
122
196
  border-color: var(--oh-color-border-hover);
123
197
  }
124
198
 
@@ -127,7 +201,7 @@ let OhInput = class OhInput extends OhElement {
127
201
  outline: none;
128
202
  background-color: var(--oh-color-surface-highlight);
129
203
  border-color: var(--oh-focus-ring-color);
130
-
204
+
131
205
  /* Inset ring + border, even on all sides */
132
206
  box-shadow: inset 0 0 0 2px var(--oh-focus-ring-color);
133
207
  }
@@ -144,13 +218,14 @@ let OhInput = class OhInput extends OhElement {
144
218
 
145
219
  /* ═══════════════════════════════════════════════════════════════
146
220
  ERROR STATE
147
- Warm red, chunky — still follows the same language
221
+ color.feedback.error border, errorSubtle fill, elevation.color.error
148
222
  ═══════════════════════════════════════════════════════════════ */
149
223
 
150
224
  .input--error {
151
225
  border-color: var(--oh-color-feedback-error);
152
226
  background-color: var(--oh-color-feedback-error-subtle);
153
- box-shadow: inset var(--oh-elevation-offset-md) var(--oh-elevation-offset-md) 0 0 var(--oh-shadow-error);
227
+ box-shadow: inset var(--oh-elevation-offset-md)
228
+ var(--oh-elevation-offset-md) 0 0 var(--oh-elevation-color-error);
154
229
  }
155
230
 
156
231
  .input--error:focus {
@@ -162,9 +237,23 @@ let OhInput = class OhInput extends OhElement {
162
237
  HELPER TEXT
163
238
  ═══════════════════════════════════════════════════════════════ */
164
239
 
240
+ /* ─── Disabled: color.*.disabled tokens, no shadow ─── */
241
+ .input:disabled {
242
+ cursor: not-allowed;
243
+ color: var(--oh-color-text-disabled);
244
+ background-color: var(--oh-color-surface-disabled);
245
+ border-color: var(--oh-color-border-disabled);
246
+ box-shadow: none;
247
+ }
248
+
249
+ .label--disabled {
250
+ color: var(--oh-color-text-disabled);
251
+ }
252
+
165
253
  .helper {
166
254
  font-family: var(--oh-typography-body-font-family);
167
255
  font-size: var(--oh-typography-body-base-font-size);
256
+ line-height: var(--oh-typography-body-base-line-height);
168
257
  color: var(--oh-color-text-secondary);
169
258
  letter-spacing: var(--oh-typography-control-letter-spacing);
170
259
  }
@@ -220,28 +309,57 @@ let OhInput = class OhInput extends OhElement {
220
309
  }
221
310
  `,
222
311
  ]; }
223
- _handleInput(event) {
224
- const target = event.target;
225
- this.value = target.value;
226
- this.emit("oh-input", { value: this.value, originalEvent: event });
312
+ connectedCallback() {
313
+ super.connectedCallback();
314
+ if (!this.hasUpdated)
315
+ this._defaultValue = this.value;
227
316
  }
228
- _handleChange(event) {
229
- this.emit("oh-change", { value: this.value, originalEvent: event });
317
+ /** The form this field belongs to */
318
+ get form() {
319
+ return this._internals.form;
230
320
  }
231
- focus() {
232
- this._input?.focus();
321
+ focus(options) {
322
+ this._input?.focus(options);
233
323
  }
234
324
  blur() {
235
325
  this._input?.blur();
236
326
  }
327
+ formResetCallback() {
328
+ this.value = this._defaultValue;
329
+ }
330
+ formDisabledCallback(disabled) {
331
+ this._formDisabled = disabled;
332
+ }
333
+ formStateRestoreCallback(state) {
334
+ if (typeof state === "string")
335
+ this.value = state;
336
+ }
237
337
  willUpdate(changed) {
238
338
  if (this.hasUpdated && changed.has("errorText")) {
239
339
  this._announceError = !!this.errorText;
240
340
  }
241
341
  }
342
+ updated(changed) {
343
+ if (changed.has("value"))
344
+ this._internals.setFormValue(this.value);
345
+ if (changed.has("value") ||
346
+ changed.has("required") ||
347
+ changed.has("type")) {
348
+ this._internals.setValidity(this._input.validity, this._input.validationMessage, this._input);
349
+ }
350
+ }
351
+ _handleInput(event) {
352
+ this.value = event.target.value;
353
+ this.emit("oh-input", { value: this.value, originalEvent: event });
354
+ }
355
+ _handleChange(event) {
356
+ this.emit("oh-change", { value: this.value, originalEvent: event });
357
+ }
242
358
  render() {
243
359
  const hasError = !!this.errorText;
244
- const helperContent = hasError ? this.errorText : this.helperText;
360
+ const disabled = this.disabled || this._formDisabled;
361
+ const describedBy = hasError ? "error" : this.helperText ? "helper" : "";
362
+ const suggestions = this.suggestions ?? [];
245
363
  const inputClasses = this.classMap({
246
364
  input: true,
247
365
  "input--error": hasError,
@@ -250,8 +368,12 @@ let OhInput = class OhInput extends OhElement {
250
368
  <div class="input-wrapper">
251
369
  ${this.label
252
370
  ? html `
253
- <label
254
- class="label ${this.required ? "label--required" : ""}"
371
+ <label
372
+ class=${this.classMap({
373
+ label: true,
374
+ "label--required": this.required,
375
+ "label--disabled": disabled,
376
+ })}
255
377
  part="label"
256
378
  for="input"
257
379
  >
@@ -259,6 +381,30 @@ let OhInput = class OhInput extends OhElement {
259
381
  </label>
260
382
  `
261
383
  : nothing}
384
+ ${hasError
385
+ ? html `
386
+ <span
387
+ id="error"
388
+ class="helper helper--error"
389
+ part="helper error"
390
+ role=${this._announceError ? "alert" : nothing}
391
+ >
392
+ <svg
393
+ class="error-icon"
394
+ viewBox="0 0 20 20"
395
+ fill="currentColor"
396
+ aria-hidden="true"
397
+ >
398
+ <path
399
+ fill-rule="evenodd"
400
+ d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"
401
+ clip-rule="evenodd"
402
+ />
403
+ </svg>
404
+ ${this.errorText}
405
+ </span>
406
+ `
407
+ : nothing}
262
408
 
263
409
  <input
264
410
  id="input"
@@ -267,30 +413,25 @@ let OhInput = class OhInput extends OhElement {
267
413
  type=${this.type}
268
414
  .value=${this.value}
269
415
  placeholder=${this.placeholder || nothing}
416
+ autocomplete=${this.autocomplete || nothing}
417
+ list=${suggestions.length ? "suggestions" : nothing}
270
418
  ?required=${this.required}
419
+ ?disabled=${disabled}
271
420
  aria-invalid=${hasError ? "true" : nothing}
272
- aria-describedby=${helperContent ? "helper" : nothing}
421
+ aria-describedby=${describedBy || nothing}
273
422
  @input=${this._handleInput}
274
423
  @change=${this._handleChange}
275
424
  />
276
-
277
- ${hasError
278
- ? html `
279
- <span
280
- id="helper"
281
- class="helper helper--error"
282
- part="helper"
283
- role=${this._announceError ? "alert" : nothing}
284
- >
285
- <svg class="error-icon" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
286
- <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
287
- </svg>
288
- ${this.errorText}
289
- </span>
290
- `
291
- : this.helperText
292
- ? html `<span id="helper" class="helper" part="helper">${this.helperText}</span>`
293
- : nothing}
425
+ ${suggestions.length
426
+ ? html `<datalist id="suggestions">
427
+ ${suggestions.map((s) => html `<option value=${s}></option>`)}
428
+ </datalist>`
429
+ : nothing}
430
+ ${this.helperText && !hasError
431
+ ? html `<span id="helper" class="helper" part="helper"
432
+ >${this.helperText}</span
433
+ >`
434
+ : nothing}
294
435
  </div>
295
436
  `;
296
437
  }
@@ -308,20 +449,35 @@ __decorate([
308
449
  property({ type: String })
309
450
  ], OhInput.prototype, "label", void 0);
310
451
  __decorate([
311
- property({ type: String })
452
+ property({ type: String, attribute: "helper-text" })
312
453
  ], OhInput.prototype, "helperText", void 0);
313
454
  __decorate([
314
- property({ type: String })
455
+ property({ type: String, attribute: "error-text" })
315
456
  ], OhInput.prototype, "errorText", void 0);
457
+ __decorate([
458
+ property({ type: String, reflect: true })
459
+ ], OhInput.prototype, "name", void 0);
316
460
  __decorate([
317
461
  property({ type: Boolean, reflect: true })
318
462
  ], OhInput.prototype, "required", void 0);
463
+ __decorate([
464
+ property({ type: Boolean, reflect: true })
465
+ ], OhInput.prototype, "disabled", void 0);
466
+ __decorate([
467
+ property({ type: Array })
468
+ ], OhInput.prototype, "suggestions", void 0);
469
+ __decorate([
470
+ property({ type: String })
471
+ ], OhInput.prototype, "autocomplete", void 0);
319
472
  __decorate([
320
473
  property({ type: String, reflect: true, attribute: "force-state" })
321
474
  ], OhInput.prototype, "forceState", void 0);
322
475
  __decorate([
323
476
  query("input")
324
477
  ], OhInput.prototype, "_input", void 0);
478
+ __decorate([
479
+ state()
480
+ ], OhInput.prototype, "_formDisabled", void 0);
325
481
  __decorate([
326
482
  state()
327
483
  ], OhInput.prototype, "_announceError", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"oh-input.js","sourceRoot":"","sources":["../src/oh-input.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oh-input.js","sourceRoot":"","sources":["../src/oh-input.ts"],"names":[],"mappings":"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"}
package/dist/oh-link.d.ts CHANGED
@@ -1,32 +1,55 @@
1
1
  /**
2
2
  * oh-link
3
3
  *
4
- * A warm, playful link with retro-inspired styling.
5
- * Two variants: inline (for body text) and standalone (for CTAs).
6
- *
7
4
  * DESIGN DECISIONS:
8
5
  * ─────────────────
9
- * 1. INLINE VARIANT
10
- * - Dotted underline (quirky, retro feel)
11
- * - Becomes solid on hover
12
- * - Warm brand orange
13
- * - Subtle but distinctive
6
+ * 1. Two variants: `inline` for links inside body text, `standalone` for
7
+ * links that stand on their own (CTAs, "View all").
8
+ * 2. The underline is a pseudo-element so it can transition. Its shape comes
9
+ * from the `decoration.underline*` tokens; it retracts on hover.
10
+ * 3. Keyboard focus swaps in a solid underline (`color.brand.primary`,
11
+ * `decoration.underlineFocusThickness`) as the indicator; the
12
+ * `focus.glowColor` tint behind it is decoration only.
13
+ * 4. Links are never colour-only: the underline is visible at rest.
14
+ * 5. `target="_blank"` adds an icon, a visually hidden "(opens in new tab)"
15
+ * and `rel="noopener noreferrer"` (unless `rel` is set).
16
+ */
17
+ import { OhElement } from "./base.js";
18
+ export type LinkVariant = "inline" | "standalone";
19
+ /**
20
+ * A text link, for use inside body copy or on its own as a call to action.
14
21
  *
15
- * 2. STANDALONE VARIANT
16
- * - Bolder presence for CTAs
17
- * - Offset shadow on hover (mini sticker effect)
18
- * - Lift animation like other components
19
- * - Arrow icon option
22
+ * **Use it for** navigating to another page or place. **Don't use it for**
23
+ * actions that don't navigate (use `oh-button` instead).
20
24
  *
21
- * 3. ACCESSIBILITY
22
- * - Underline always visible (not color-only)
23
- * - Clear focus state
24
- * - Proper contrast ratios
25
+ * **Variants:** `inline` (default) sits in running text; `standalone` is
26
+ * larger and bolder for links on their own, and can show a trailing arrow
27
+ * (`arrow`). `inverse` is for links on accent surfaces.
28
+ *
29
+ * **Behaviour:** renders a native `<a>`, so Enter follows it and it takes
30
+ * focus in tab order. On hover the underline retracts and the arrow nudges
31
+ * forward. Keyboard focus (`:focus-visible`) shows a solid underline and a
32
+ * tint; mouse focus doesn't. Transitions are cut short under
33
+ * `prefers-reduced-motion`. With `target="_blank"` it adds an external icon,
34
+ * screen-reader text "(opens in new tab)" and `rel="noopener noreferrer"`
35
+ * when `rel` isn't set; the arrow is not shown on external links.
36
+ *
37
+ * **Accessibility:** the underline shows at rest and on focus, so links
38
+ * don't rely on colour. Write link text that makes sense out of context.
39
+ *
40
+ * **Look:** set by tokens — `color.text.link` the colour (`color.text.onAccent`
41
+ * when `inverse`); `decoration.underlineThickness`, `decoration.underlineDash`
42
+ * and `decoration.underlinePeriod` the underline; `color.brand.primary`
43
+ * (`color.text.onAccent` when `inverse`) and
44
+ * `decoration.underlineFocusThickness` the focus underline; `focus.glowColor`
45
+ * (`link.inverse.focusBackground` when `inverse`) the focus tint;
46
+ * `typography.body.fontFamily`, plus `typography.body.lg.fontSize` and
47
+ * `typography.emphasis.fontWeight` for `standalone`; `motion.slow` and
48
+ * `motion.snappy` the transitions.
25
49
  *
26
50
  * @element oh-link
51
+ * @slot - The link text.
27
52
  */
28
- import { OhElement } from "./base.js";
29
- export type LinkVariant = "inline" | "standalone";
30
53
  export declare class OhLink extends OhElement {
31
54
  static styles: import("lit").CSSResult[];
32
55
  href: string;
@@ -1 +1 @@
1
- {"version":3,"file":"oh-link.d.ts","sourceRoot":"","sources":["../src/oh-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;AAElD,qBACa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,4BAmSpB;IAGF,IAAI,SAAM;IAGV,MAAM,SAAM;IAGZ,GAAG,SAAM;IAGT,OAAO,EAAE,WAAW,CAAY;IAGhC,KAAK,UAAS;IAGd,OAAO,UAAS;IAEhB,MAAM;CAiCP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB;CACF"}
1
+ {"version":3,"file":"oh-link.d.ts","sourceRoot":"","sources":["../src/oh-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,SAAS,EAAc,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBACa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,4BAkSpB;IAGF,IAAI,SAAM;IAGV,MAAM,SAAM;IAGZ,GAAG,SAAM;IAGT,OAAO,EAAE,WAAW,CAAY;IAGhC,KAAK,UAAS;IAGd,OAAO,UAAS;IAEhB,MAAM;CAyDP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB;CACF"}