@orange-halo/components 2.2.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 (124) hide show
  1. package/custom-elements.json +551 -314
  2. package/dist/index.d.ts +1 -7
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +0 -4
  5. package/dist/index.js.map +1 -1
  6. package/dist/oh-accordion.d.ts +53 -8
  7. package/dist/oh-accordion.d.ts.map +1 -1
  8. package/dist/oh-accordion.js +77 -15
  9. package/dist/oh-accordion.js.map +1 -1
  10. package/dist/oh-alert.d.ts +26 -8
  11. package/dist/oh-alert.d.ts.map +1 -1
  12. package/dist/oh-alert.js +32 -14
  13. package/dist/oh-alert.js.map +1 -1
  14. package/dist/oh-badge.d.ts +32 -13
  15. package/dist/oh-badge.d.ts.map +1 -1
  16. package/dist/oh-badge.js +35 -16
  17. package/dist/oh-badge.js.map +1 -1
  18. package/dist/oh-breadcrumb.d.ts +39 -12
  19. package/dist/oh-breadcrumb.d.ts.map +1 -1
  20. package/dist/oh-breadcrumb.js +39 -17
  21. package/dist/oh-breadcrumb.js.map +1 -1
  22. package/dist/oh-button.d.ts +49 -23
  23. package/dist/oh-button.d.ts.map +1 -1
  24. package/dist/oh-button.js +63 -44
  25. package/dist/oh-button.js.map +1 -1
  26. package/dist/oh-card.d.ts +47 -19
  27. package/dist/oh-card.d.ts.map +1 -1
  28. package/dist/oh-card.js +110 -82
  29. package/dist/oh-card.js.map +1 -1
  30. package/dist/oh-checkbox-group.d.ts +56 -12
  31. package/dist/oh-checkbox-group.d.ts.map +1 -1
  32. package/dist/oh-checkbox-group.figma.js +4 -6
  33. package/dist/oh-checkbox-group.figma.js.map +1 -1
  34. package/dist/oh-checkbox-group.js +86 -44
  35. package/dist/oh-checkbox-group.js.map +1 -1
  36. package/dist/oh-checkbox.d.ts +54 -17
  37. package/dist/oh-checkbox.d.ts.map +1 -1
  38. package/dist/oh-checkbox.js +85 -55
  39. package/dist/oh-checkbox.js.map +1 -1
  40. package/dist/oh-cluster.d.ts +19 -14
  41. package/dist/oh-cluster.d.ts.map +1 -1
  42. package/dist/oh-cluster.js +19 -19
  43. package/dist/oh-cluster.js.map +1 -1
  44. package/dist/oh-container.d.ts +19 -16
  45. package/dist/oh-container.d.ts.map +1 -1
  46. package/dist/oh-container.js +21 -23
  47. package/dist/oh-container.js.map +1 -1
  48. package/dist/oh-drawer.d.ts +28 -11
  49. package/dist/oh-drawer.d.ts.map +1 -1
  50. package/dist/oh-drawer.js +31 -14
  51. package/dist/oh-drawer.js.map +1 -1
  52. package/dist/oh-footer.d.ts +37 -18
  53. package/dist/oh-footer.d.ts.map +1 -1
  54. package/dist/oh-footer.js +38 -19
  55. package/dist/oh-footer.js.map +1 -1
  56. package/dist/oh-grid.d.ts +27 -14
  57. package/dist/oh-grid.d.ts.map +1 -1
  58. package/dist/oh-grid.js +27 -19
  59. package/dist/oh-grid.js.map +1 -1
  60. package/dist/oh-icon.d.ts +21 -5
  61. package/dist/oh-icon.d.ts.map +1 -1
  62. package/dist/oh-icon.js +21 -5
  63. package/dist/oh-icon.js.map +1 -1
  64. package/dist/oh-input.d.ts +104 -20
  65. package/dist/oh-input.d.ts.map +1 -1
  66. package/dist/oh-input.figma.js +2 -2
  67. package/dist/oh-input.figma.js.map +1 -1
  68. package/dist/oh-input.js +205 -66
  69. package/dist/oh-input.js.map +1 -1
  70. package/dist/oh-link.d.ts +42 -19
  71. package/dist/oh-link.d.ts.map +1 -1
  72. package/dist/oh-link.js +47 -29
  73. package/dist/oh-link.js.map +1 -1
  74. package/dist/oh-modal.d.ts +29 -6
  75. package/dist/oh-modal.d.ts.map +1 -1
  76. package/dist/oh-modal.js +37 -16
  77. package/dist/oh-modal.js.map +1 -1
  78. package/dist/oh-navbar.d.ts +42 -19
  79. package/dist/oh-navbar.d.ts.map +1 -1
  80. package/dist/oh-navbar.js +42 -19
  81. package/dist/oh-navbar.js.map +1 -1
  82. package/dist/oh-progress.d.ts +26 -7
  83. package/dist/oh-progress.d.ts.map +1 -1
  84. package/dist/oh-progress.js +31 -12
  85. package/dist/oh-progress.js.map +1 -1
  86. package/dist/oh-radio-group.d.ts +55 -13
  87. package/dist/oh-radio-group.d.ts.map +1 -1
  88. package/dist/oh-radio-group.figma.js +2 -2
  89. package/dist/oh-radio-group.figma.js.map +1 -1
  90. package/dist/oh-radio-group.js +83 -43
  91. package/dist/oh-radio-group.js.map +1 -1
  92. package/dist/oh-radio.d.ts +55 -17
  93. package/dist/oh-radio.d.ts.map +1 -1
  94. package/dist/oh-radio.js +87 -56
  95. package/dist/oh-radio.js.map +1 -1
  96. package/dist/oh-select.d.ts +58 -16
  97. package/dist/oh-select.d.ts.map +1 -1
  98. package/dist/oh-select.js +70 -33
  99. package/dist/oh-select.js.map +1 -1
  100. package/dist/oh-skip-link.d.ts +33 -15
  101. package/dist/oh-skip-link.d.ts.map +1 -1
  102. package/dist/oh-skip-link.js +38 -25
  103. package/dist/oh-skip-link.js.map +1 -1
  104. package/dist/oh-stack.d.ts +17 -14
  105. package/dist/oh-stack.d.ts.map +1 -1
  106. package/dist/oh-stack.js +18 -20
  107. package/dist/oh-stack.js.map +1 -1
  108. package/dist/oh-tabs.d.ts +55 -30
  109. package/dist/oh-tabs.d.ts.map +1 -1
  110. package/dist/oh-tabs.js +80 -65
  111. package/dist/oh-tabs.js.map +1 -1
  112. package/dist/oh-textarea.d.ts +37 -6
  113. package/dist/oh-textarea.d.ts.map +1 -1
  114. package/dist/oh-textarea.js +40 -9
  115. package/dist/oh-textarea.js.map +1 -1
  116. package/dist/oh-toggle.d.ts +45 -12
  117. package/dist/oh-toggle.d.ts.map +1 -1
  118. package/dist/oh-toggle.js +64 -29
  119. package/dist/oh-toggle.js.map +1 -1
  120. package/package.json +3 -7
  121. package/dist/oh-hero.d.ts +0 -33
  122. package/dist/oh-hero.d.ts.map +0 -1
  123. package/dist/oh-hero.js +0 -248
  124. package/dist/oh-hero.js.map +0 -1
package/dist/oh-input.js CHANGED
@@ -1,27 +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)
13
- *
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
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
18
10
  *
19
- * 3. FOCUS THICKENS
20
- * - An inset ring in focus.ringColor thickens the border inward
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
21
14
  * - Background switches to color.surface.highlight
22
- * - Clear visual hierarchy
23
15
  *
24
- * @element oh-input
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
21
+ *
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
25
31
  */
26
32
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
27
33
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -32,21 +38,91 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
32
38
  import { html, css, nothing } from "lit";
33
39
  import { customElement, property, query, state } from "lit/decorators.js";
34
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
+ */
35
87
  let OhInput = class OhInput extends OhElement {
36
88
  constructor() {
37
89
  super(...arguments);
90
+ /** The kind of text: text, email, password, search, tel or url */
38
91
  this.type = "text";
92
+ /** The text in the field */
39
93
  this.value = "";
94
+ /** Example text shown while the field is empty. Not a replacement for the label */
40
95
  this.placeholder = "";
96
+ /** The visible label. Always set one: it names the field */
41
97
  this.label = "";
98
+ /** Help shown under the field and linked to it */
42
99
  this.helperText = "";
100
+ /** Error shown before the field and linked to it. Setting it puts the field in its error state */
43
101
  this.errorText = "";
102
+ /** The name the value is submitted under */
103
+ this.name = "";
104
+ /** Must be filled in before the form submits */
44
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 = [];
45
116
  /**
46
117
  * Force a visual state (for documentation/Figma sync only)
47
118
  * Values: 'hover' | 'focus'
48
119
  */
49
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;
50
126
  /**
51
127
  * Errors that appear after first render (e.g. on submit) are announced via
52
128
  * role="alert". Errors present on page load are only linked with
@@ -54,14 +130,10 @@ let OhInput = class OhInput extends OhElement {
54
130
  */
55
131
  this._announceError = false;
56
132
  }
133
+ static { this.formAssociated = true; }
57
134
  static { this.styles = [
58
135
  baseStyles,
59
136
  css `
60
- /* ═══════════════════════════════════════════════════════════════
61
- ORANGE HALO INPUT
62
- Chunky • Inset • Rises on Focus
63
- ═══════════════════════════════════════════════════════════════ */
64
-
65
137
  :host {
66
138
  display: block;
67
139
  }
@@ -72,7 +144,7 @@ let OhInput = class OhInput extends OhElement {
72
144
  gap: var(--oh-spacing-2);
73
145
  }
74
146
 
75
- /* ─── Label: friendly and readable (16px minimum) ─── */
147
+ /* ─── Label: typography.control.* ─── */
76
148
  .label {
77
149
  font-family: var(--oh-typography-control-font-family);
78
150
  font-size: var(--oh-typography-control-font-size-sm);
@@ -88,7 +160,7 @@ let OhInput = class OhInput extends OhElement {
88
160
 
89
161
  /* ═══════════════════════════════════════════════════════════════
90
162
  INPUT FIELD
91
- Thick border, inset shadow, solid fill
163
+ Border, inset shadow and fill from tokens
92
164
  ═══════════════════════════════════════════════════════════════ */
93
165
 
94
166
  .input {
@@ -96,17 +168,18 @@ let OhInput = class OhInput extends OhElement {
96
168
  font-family: var(--oh-typography-control-font-family);
97
169
  color: var(--oh-color-text-primary);
98
170
 
99
- /* Solid fill — no gradients */
171
+ /* Fill: color.surface.base */
100
172
  background-color: var(--oh-color-surface-base);
101
173
 
102
- /* Thick illustrative border (WCAG 3:1 compliant) */
174
+ /* Border: input.base.borderWidth in color.border.emphasis (a UI
175
+ contrast pair checked by check:contrast) */
103
176
  border: var(--oh-input-base-border-width) solid
104
177
  var(--oh-color-border-emphasis);
105
178
  border-radius: var(--oh-shape-control);
106
179
 
107
- /* Inset offset shadow: pressed into page */
180
+ /* Inset shadow: elevation.offset.md in elevation.color.field */
108
181
  box-shadow: inset var(--oh-elevation-offset-md)
109
- var(--oh-elevation-offset-md) 0 0 var(--oh-shadow-field);
182
+ var(--oh-elevation-offset-md) 0 0 var(--oh-elevation-color-field);
110
183
 
111
184
  transition:
112
185
  border-color var(--oh-motion-snappy) ease,
@@ -118,8 +191,8 @@ let OhInput = class OhInput extends OhElement {
118
191
  color: var(--oh-color-text-secondary); /* Readable placeholder */
119
192
  }
120
193
 
121
- /* ─── Hover: border warms up ─── */
122
- .input:hover:not(:focus) {
194
+ /* ─── Hover: border in color.border.hover ─── */
195
+ .input:hover:not(:focus):not(:disabled) {
123
196
  border-color: var(--oh-color-border-hover);
124
197
  }
125
198
 
@@ -145,14 +218,14 @@ let OhInput = class OhInput extends OhElement {
145
218
 
146
219
  /* ═══════════════════════════════════════════════════════════════
147
220
  ERROR STATE
148
- Warm red, chunky — still follows the same language
221
+ color.feedback.error border, errorSubtle fill, elevation.color.error
149
222
  ═══════════════════════════════════════════════════════════════ */
150
223
 
151
224
  .input--error {
152
225
  border-color: var(--oh-color-feedback-error);
153
226
  background-color: var(--oh-color-feedback-error-subtle);
154
227
  box-shadow: inset var(--oh-elevation-offset-md)
155
- var(--oh-elevation-offset-md) 0 0 var(--oh-shadow-error);
228
+ var(--oh-elevation-offset-md) 0 0 var(--oh-elevation-color-error);
156
229
  }
157
230
 
158
231
  .input--error:focus {
@@ -164,9 +237,23 @@ let OhInput = class OhInput extends OhElement {
164
237
  HELPER TEXT
165
238
  ═══════════════════════════════════════════════════════════════ */
166
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
+
167
253
  .helper {
168
254
  font-family: var(--oh-typography-body-font-family);
169
255
  font-size: var(--oh-typography-body-base-font-size);
256
+ line-height: var(--oh-typography-body-base-line-height);
170
257
  color: var(--oh-color-text-secondary);
171
258
  letter-spacing: var(--oh-typography-control-letter-spacing);
172
259
  }
@@ -222,28 +309,57 @@ let OhInput = class OhInput extends OhElement {
222
309
  }
223
310
  `,
224
311
  ]; }
225
- _handleInput(event) {
226
- const target = event.target;
227
- this.value = target.value;
228
- this.emit("oh-input", { value: this.value, originalEvent: event });
312
+ connectedCallback() {
313
+ super.connectedCallback();
314
+ if (!this.hasUpdated)
315
+ this._defaultValue = this.value;
229
316
  }
230
- _handleChange(event) {
231
- this.emit("oh-change", { value: this.value, originalEvent: event });
317
+ /** The form this field belongs to */
318
+ get form() {
319
+ return this._internals.form;
232
320
  }
233
- focus() {
234
- this._input?.focus();
321
+ focus(options) {
322
+ this._input?.focus(options);
235
323
  }
236
324
  blur() {
237
325
  this._input?.blur();
238
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
+ }
239
337
  willUpdate(changed) {
240
338
  if (this.hasUpdated && changed.has("errorText")) {
241
339
  this._announceError = !!this.errorText;
242
340
  }
243
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
+ }
244
358
  render() {
245
359
  const hasError = !!this.errorText;
246
- 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 ?? [];
247
363
  const inputClasses = this.classMap({
248
364
  input: true,
249
365
  "input--error": hasError,
@@ -253,7 +369,11 @@ let OhInput = class OhInput extends OhElement {
253
369
  ${this.label
254
370
  ? html `
255
371
  <label
256
- class="label ${this.required ? "label--required" : ""}"
372
+ class=${this.classMap({
373
+ label: true,
374
+ "label--required": this.required,
375
+ "label--disabled": disabled,
376
+ })}
257
377
  part="label"
258
378
  for="input"
259
379
  >
@@ -261,28 +381,12 @@ let OhInput = class OhInput extends OhElement {
261
381
  </label>
262
382
  `
263
383
  : nothing}
264
-
265
- <input
266
- id="input"
267
- part="input"
268
- class=${inputClasses}
269
- type=${this.type}
270
- .value=${this.value}
271
- placeholder=${this.placeholder || nothing}
272
- autocomplete=${this.autocomplete || nothing}
273
- ?required=${this.required}
274
- aria-invalid=${hasError ? "true" : nothing}
275
- aria-describedby=${helperContent ? "helper" : nothing}
276
- @input=${this._handleInput}
277
- @change=${this._handleChange}
278
- />
279
-
280
384
  ${hasError
281
385
  ? html `
282
386
  <span
283
- id="helper"
387
+ id="error"
284
388
  class="helper helper--error"
285
- part="helper"
389
+ part="helper error"
286
390
  role=${this._announceError ? "alert" : nothing}
287
391
  >
288
392
  <svg
@@ -300,11 +404,34 @@ let OhInput = class OhInput extends OhElement {
300
404
  ${this.errorText}
301
405
  </span>
302
406
  `
303
- : this.helperText
304
- ? html `<span id="helper" class="helper" part="helper"
305
- >${this.helperText}</span
306
- >`
307
- : nothing}
407
+ : nothing}
408
+
409
+ <input
410
+ id="input"
411
+ part="input"
412
+ class=${inputClasses}
413
+ type=${this.type}
414
+ .value=${this.value}
415
+ placeholder=${this.placeholder || nothing}
416
+ autocomplete=${this.autocomplete || nothing}
417
+ list=${suggestions.length ? "suggestions" : nothing}
418
+ ?required=${this.required}
419
+ ?disabled=${disabled}
420
+ aria-invalid=${hasError ? "true" : nothing}
421
+ aria-describedby=${describedBy || nothing}
422
+ @input=${this._handleInput}
423
+ @change=${this._handleChange}
424
+ />
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}
308
435
  </div>
309
436
  `;
310
437
  }
@@ -322,14 +449,23 @@ __decorate([
322
449
  property({ type: String })
323
450
  ], OhInput.prototype, "label", void 0);
324
451
  __decorate([
325
- property({ type: String })
452
+ property({ type: String, attribute: "helper-text" })
326
453
  ], OhInput.prototype, "helperText", void 0);
327
454
  __decorate([
328
- property({ type: String })
455
+ property({ type: String, attribute: "error-text" })
329
456
  ], OhInput.prototype, "errorText", void 0);
457
+ __decorate([
458
+ property({ type: String, reflect: true })
459
+ ], OhInput.prototype, "name", void 0);
330
460
  __decorate([
331
461
  property({ type: Boolean, reflect: true })
332
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);
333
469
  __decorate([
334
470
  property({ type: String })
335
471
  ], OhInput.prototype, "autocomplete", void 0);
@@ -339,6 +475,9 @@ __decorate([
339
475
  __decorate([
340
476
  query("input")
341
477
  ], OhInput.prototype, "_input", void 0);
478
+ __decorate([
479
+ state()
480
+ ], OhInput.prototype, "_formDisabled", void 0);
342
481
  __decorate([
343
482
  state()
344
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,4BAuSpB;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"}
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"}
package/dist/oh-link.js CHANGED
@@ -1,29 +1,18 @@
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
14
- *
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
20
- *
21
- * 3. ACCESSIBILITY
22
- * - Underline always visible (not color-only)
23
- * - Clear focus state
24
- * - Proper contrast ratios
25
- *
26
- * @element oh-link
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).
27
16
  */
28
17
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
29
18
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -34,6 +23,40 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
34
23
  import { html, css, nothing } from "lit";
35
24
  import { customElement, property } from "lit/decorators.js";
36
25
  import { OhElement, baseStyles } from "./base.js";
26
+ /**
27
+ * A text link, for use inside body copy or on its own as a call to action.
28
+ *
29
+ * **Use it for** navigating to another page or place. **Don't use it for**
30
+ * actions that don't navigate (use `oh-button` instead).
31
+ *
32
+ * **Variants:** `inline` (default) sits in running text; `standalone` is
33
+ * larger and bolder for links on their own, and can show a trailing arrow
34
+ * (`arrow`). `inverse` is for links on accent surfaces.
35
+ *
36
+ * **Behaviour:** renders a native `<a>`, so Enter follows it and it takes
37
+ * focus in tab order. On hover the underline retracts and the arrow nudges
38
+ * forward. Keyboard focus (`:focus-visible`) shows a solid underline and a
39
+ * tint; mouse focus doesn't. Transitions are cut short under
40
+ * `prefers-reduced-motion`. With `target="_blank"` it adds an external icon,
41
+ * screen-reader text "(opens in new tab)" and `rel="noopener noreferrer"`
42
+ * when `rel` isn't set; the arrow is not shown on external links.
43
+ *
44
+ * **Accessibility:** the underline shows at rest and on focus, so links
45
+ * don't rely on colour. Write link text that makes sense out of context.
46
+ *
47
+ * **Look:** set by tokens — `color.text.link` the colour (`color.text.onAccent`
48
+ * when `inverse`); `decoration.underlineThickness`, `decoration.underlineDash`
49
+ * and `decoration.underlinePeriod` the underline; `color.brand.primary`
50
+ * (`color.text.onAccent` when `inverse`) and
51
+ * `decoration.underlineFocusThickness` the focus underline; `focus.glowColor`
52
+ * (`link.inverse.focusBackground` when `inverse`) the focus tint;
53
+ * `typography.body.fontFamily`, plus `typography.body.lg.fontSize` and
54
+ * `typography.emphasis.fontWeight` for `standalone`; `motion.slow` and
55
+ * `motion.snappy` the transitions.
56
+ *
57
+ * @element oh-link
58
+ * @slot - The link text.
59
+ */
37
60
  let OhLink = class OhLink extends OhElement {
38
61
  constructor() {
39
62
  super(...arguments);
@@ -47,11 +70,6 @@ let OhLink = class OhLink extends OhElement {
47
70
  static { this.styles = [
48
71
  baseStyles,
49
72
  css `
50
- /* ═══════════════════════════════════════════════════════════════
51
- ORANGE HALO LINK
52
- Warm • Playful • Retro-inspired
53
- ═══════════════════════════════════════════════════════════════ */
54
-
55
73
  :host {
56
74
  display: inline;
57
75
  }
@@ -74,7 +92,7 @@ let OhLink = class OhLink extends OhElement {
74
92
  text-decoration: none;
75
93
  }
76
94
 
77
- /* Dashed underline via pseudo-element (animatable) */
95
+ /* Underline via pseudo-element (animatable), shaped by decoration tokens */
78
96
  .link__text::after {
79
97
  content: "";
80
98
  position: absolute;
@@ -96,7 +114,7 @@ let OhLink = class OhLink extends OhElement {
96
114
 
97
115
  /* ═══════════════════════════════════════════════════════════════
98
116
  INLINE VARIANT
99
- Dashed underline shrinks right-to-left on hover
117
+ Underline shrinks right-to-left on hover
100
118
  ═══════════════════════════════════════════════════════════════ */
101
119
 
102
120
  .link--inline {
@@ -148,7 +166,7 @@ let OhLink = class OhLink extends OhElement {
148
166
 
149
167
  /* ═══════════════════════════════════════════════════════════════
150
168
  STANDALONE VARIANT
151
- CTA-style with hover lift, dashed underline shrinks on hover
169
+ CTA-style; underline shrinks on hover
152
170
  ═══════════════════════════════════════════════════════════════ */
153
171
 
154
172
  .link--standalone {