@aurodesignsystem-dev/auro-formkit 0.0.0-pr1387.2 → 0.0.0-pr1387.4

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 (45) hide show
  1. package/components/checkbox/demo/api.min.js +1 -1
  2. package/components/checkbox/demo/index.min.js +1 -1
  3. package/components/checkbox/dist/index.js +1 -1
  4. package/components/checkbox/dist/registered.js +1 -1
  5. package/components/combobox/demo/api.min.js +131 -55
  6. package/components/combobox/demo/index.min.js +131 -55
  7. package/components/combobox/dist/comboboxKeyboardStrategy.d.ts +4 -4
  8. package/components/combobox/dist/index.js +128 -52
  9. package/components/combobox/dist/registered.js +128 -52
  10. package/components/counter/demo/api.min.js +22 -4
  11. package/components/counter/demo/index.min.js +22 -4
  12. package/components/counter/dist/auro-counter.d.ts +7 -0
  13. package/components/counter/dist/index.js +22 -4
  14. package/components/counter/dist/registered.js +22 -4
  15. package/components/datepicker/demo/api.min.js +11 -41
  16. package/components/datepicker/demo/index.min.js +11 -41
  17. package/components/datepicker/dist/auro-datepicker.d.ts +0 -7
  18. package/components/datepicker/dist/index.js +11 -41
  19. package/components/datepicker/dist/registered.js +11 -41
  20. package/components/dropdown/demo/api.min.js +1 -1
  21. package/components/dropdown/demo/index.min.js +1 -1
  22. package/components/dropdown/dist/index.js +1 -1
  23. package/components/dropdown/dist/registered.js +1 -1
  24. package/components/form/demo/api.min.js +226 -120
  25. package/components/form/demo/index.min.js +226 -120
  26. package/components/input/demo/api.min.js +1 -1
  27. package/components/input/demo/index.min.js +1 -1
  28. package/components/input/dist/index.js +1 -1
  29. package/components/input/dist/registered.js +1 -1
  30. package/components/menu/demo/api.min.js +2 -2
  31. package/components/menu/demo/index.min.js +2 -2
  32. package/components/menu/dist/index.js +2 -2
  33. package/components/menu/dist/registered.js +2 -2
  34. package/components/radio/demo/api.min.js +1 -1
  35. package/components/radio/demo/index.min.js +1 -1
  36. package/components/radio/dist/index.js +1 -1
  37. package/components/radio/dist/registered.js +1 -1
  38. package/components/select/demo/api.min.js +63 -21
  39. package/components/select/demo/index.min.js +63 -21
  40. package/components/select/dist/index.js +60 -18
  41. package/components/select/dist/registered.js +60 -18
  42. package/components/select/dist/selectKeyboardStrategy.d.ts +3 -3
  43. package/custom-elements.json +17 -88
  44. package/package.json +4 -3
  45. package/components/dropdown/dist/keyboardUtils.d.ts +0 -18
@@ -1104,13 +1104,45 @@ function guardTouchPassthrough(element) {
1104
1104
  function restoreTriggerAfterClose(dropdown, focusTarget) {
1105
1105
  dropdown.trigger.inert = false;
1106
1106
 
1107
- if (dropdown.isBibFullscreen) {
1108
- requestAnimationFrame(() => {
1109
- if (!dropdown.isPopoverVisible) {
1110
- focusTarget.focus();
1111
- }
1112
- });
1107
+ requestAnimationFrame(() => {
1108
+ if (!dropdown.isPopoverVisible) {
1109
+ focusTarget.focus();
1110
+ }
1111
+ });
1112
+ }
1113
+
1114
+ /**
1115
+ * Computes display state once per keydown event.
1116
+ * Centralizes null-safety checks and makes the shared/modal/popover branching explicit.
1117
+ *
1118
+ * @param {HTMLElement} component - The component with a dropdown reference.
1119
+ * @param {Object} [options] - Optional config.
1120
+ * @param {HTMLElement} [options.dropdown] - Explicit dropdown reference. Falls back to component.dropdown.
1121
+ * @param {Function} [options.inputResolver] - Called with (component, ctx) to resolve the active input element.
1122
+ * @returns {{isExpanded: boolean, isModal: boolean, isPopover: boolean, activeInput: HTMLElement|null}}
1123
+ * isModal and isPopover reflect the display mode (fullscreen vs not) regardless of expanded state.
1124
+ */
1125
+ function createDisplayContext(component, options = {}) {
1126
+ const dd = options.dropdown || component.dropdown;
1127
+ // isPopoverVisible reflects as the `open` attribute.
1128
+ // It reports whether the bib is open in any mode (popover or modal).
1129
+ const isExpanded = Boolean(dd && dd.isPopoverVisible);
1130
+ const isFullscreen = Boolean(dd && dd.isBibFullscreen);
1131
+
1132
+ const ctx = {
1133
+ isExpanded,
1134
+ isModal: isFullscreen,
1135
+ isPopover: !isFullscreen,
1136
+ activeInput: null,
1137
+ };
1138
+
1139
+ if (options.inputResolver) {
1140
+ const resolvedInput = options.inputResolver(component, ctx);
1141
+ // Guard against resolvers returning undefined or non-HTMLElement values.
1142
+ ctx.activeInput = resolvedInput instanceof HTMLElement ? resolvedInput : null;
1113
1143
  }
1144
+
1145
+ return ctx;
1114
1146
  }
1115
1147
 
1116
1148
  /**
@@ -1118,12 +1150,14 @@ function restoreTriggerAfterClose(dropdown, focusTarget) {
1118
1150
  * Handles both sync and async handlers.
1119
1151
  * @param {HTMLElement} component - The component to attach the listener to.
1120
1152
  * @param {Object} strategy - Map of key names to handler functions.
1153
+ * @param {Object} [options] - Optional config passed to createDisplayContext.
1121
1154
  */
1122
- function applyKeyboardStrategy(component, strategy) {
1155
+ function applyKeyboardStrategy(component, strategy, options = {}) {
1123
1156
  component.addEventListener('keydown', async (evt) => {
1124
1157
  const handler = strategy[evt.key] || strategy.default;
1125
- if (handler) {
1126
- await handler(component, evt);
1158
+ if (typeof handler === 'function') {
1159
+ const ctx = createDisplayContext(component, options);
1160
+ await handler(component, evt, ctx);
1127
1161
  }
1128
1162
  });
1129
1163
  }
@@ -1135,39 +1169,58 @@ function applyKeyboardStrategy(component, strategy) {
1135
1169
  * @param {string} direction - 'up' or 'down'.
1136
1170
  * @param {Object} [options] - Optional config.
1137
1171
  * @param {Function} [options.showFn] - Called to open the dropdown when closed.
1172
+ * @param {Object} [options.ctx] - Display context to avoid re-checking visibility.
1138
1173
  */
1139
1174
  function navigateArrow(component, direction, options = {}) {
1140
- if (component.dropdown.isPopoverVisible) {
1175
+ const visible = options.ctx ? options.ctx.isExpanded : component.dropdown.isPopoverVisible;
1176
+ if (visible) {
1141
1177
  component.menu.navigateOptions(direction);
1142
1178
  } else if (options.showFn) {
1143
1179
  options.showFn();
1144
1180
  }
1145
1181
  }
1146
1182
 
1183
+ /**
1184
+ * Returns the clear button element from the active input's shadow
1185
+ * DOM, if available.
1186
+ * @param {Object} ctx - Display context with activeInput.
1187
+ * @returns {Element|null}
1188
+ */
1189
+ function getClearBtn(ctx) {
1190
+ const root = ctx && ctx.activeInput && ctx.activeInput.shadowRoot;
1191
+ if (!root) {
1192
+ return null;
1193
+ }
1194
+ return root.querySelector('.clearBtn');
1195
+ }
1196
+
1197
+ /**
1198
+ * Returns true when the clear button inside the active input's shadow DOM has focus.
1199
+ * Uses shadowRoot.activeElement to detect focus inside auro-button,
1200
+ * since Safari does not propagate :focus-within through shadow DOM.
1201
+ * @param {Object} ctx - Display context with activeInput.
1202
+ * @param {Element} [clearBtn=getClearBtn(ctx)] - Pre-fetched clear button element.
1203
+ * @returns {boolean}
1204
+ */
1205
+ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
1206
+ if (!clearBtn) {
1207
+ return false;
1208
+ }
1209
+ return Boolean(clearBtn.shadowRoot && clearBtn.shadowRoot.activeElement !== null);
1210
+ }
1211
+
1147
1212
  const comboboxKeyboardStrategy = {
1148
- async Enter(component, evt) {
1213
+ async Enter(component, evt, ctx) {
1149
1214
  // If the clear button has focus, let the browser activate it normally.
1150
1215
  // stopPropagation prevents parent containers (e.g., forms) from treating
1151
1216
  // Enter as a submit, but we must NOT call preventDefault — that would
1152
1217
  // block the browser's built-in "Enter activates focused button" behavior.
1153
- const isBibFullscreenActive =
1154
- component.dropdown &&
1155
- component.dropdown.isBibFullscreen &&
1156
- component.dropdown.isPopoverVisible;
1157
- const activeInput =
1158
- isBibFullscreenActive && component.inputInBib
1159
- ? component.inputInBib
1160
- : component.input;
1161
- const clearBtn =
1162
- activeInput && activeInput.shadowRoot
1163
- ? activeInput.shadowRoot.querySelector('.clearBtn')
1164
- : null;
1165
- if (clearBtn && clearBtn.shadowRoot && clearBtn.shadowRoot.activeElement !== null) {
1218
+ if (isClearBtnFocused(ctx)) {
1166
1219
  evt.stopPropagation();
1167
1220
  return;
1168
1221
  }
1169
1222
 
1170
- if (component.dropdown.isPopoverVisible && component.optionActive) {
1223
+ if (ctx.isExpanded && component.optionActive) {
1171
1224
  component.menu.makeSelection();
1172
1225
  await component.updateComplete;
1173
1226
  evt.preventDefault();
@@ -1184,20 +1237,20 @@ const comboboxKeyboardStrategy = {
1184
1237
  }
1185
1238
  },
1186
1239
 
1187
- Tab(component) {
1188
- if (!component.dropdown.isPopoverVisible) {
1240
+ Tab(component, _evt, ctx) {
1241
+ if (!ctx.isExpanded) {
1189
1242
  return;
1190
1243
  }
1191
1244
 
1192
- if (component.dropdown.isBibFullscreen) {
1193
- const clearBtn = component.inputInBib.shadowRoot.querySelector('.clearBtn');
1194
-
1195
- // Use shadowRoot.activeElement to detect focus inside auro-button,
1196
- // since Safari does not propagate :focus-within through shadow DOM.
1197
- const clearBtnHasFocus = clearBtn && clearBtn.shadowRoot && clearBtn.shadowRoot.activeElement !== null;
1245
+ if (ctx.isModal) {
1246
+ if (!ctx.activeInput) {
1247
+ return;
1248
+ }
1249
+ const clearBtn = getClearBtn(ctx);
1250
+ const clearBtnHasFocus = isClearBtnFocused(ctx, clearBtn);
1198
1251
 
1199
1252
  // Tab from input: if clear button exists and doesn't have focus, focus it
1200
- if (clearBtn && !clearBtnHasFocus && component.inputInBib.value) {
1253
+ if (clearBtn && !clearBtnHasFocus && ctx.activeInput.value) {
1201
1254
  // Force clear button container visible to work around Safari not
1202
1255
  // propagating :focus-within through shadow DOM boundaries, which
1203
1256
  // causes .wrapper:not(:focus-within) to hide .notification.clear.
@@ -1240,30 +1293,34 @@ const comboboxKeyboardStrategy = {
1240
1293
  component.hideBib();
1241
1294
  },
1242
1295
 
1243
- ArrowUp(component, evt) {
1244
- // If the clear button has focus, let the browser handle ArrowUp normally
1245
- if (component.clearBtnFocused) {
1296
+ ArrowUp(component, evt, ctx) {
1297
+ // If the clear button has focus, let the browser handle ArrowUp normally.
1298
+ if (isClearBtnFocused(ctx)) {
1246
1299
  return;
1247
1300
  }
1248
1301
 
1249
1302
  if (component.availableOptions.length > 0) {
1250
1303
  component.showBib();
1251
1304
  }
1305
+ // Read live visibility — ctx.isExpanded was computed before showBib() above,
1306
+ // so it wouldn't reflect the state change.
1252
1307
  if (component.dropdown.isPopoverVisible) {
1253
1308
  evt.preventDefault();
1254
1309
  navigateArrow(component, 'up');
1255
1310
  }
1256
1311
  },
1257
1312
 
1258
- ArrowDown(component, evt) {
1259
- // If the clear button has focus, let the browser handle ArrowDown normally
1260
- if (component.clearBtnFocused) {
1313
+ ArrowDown(component, evt, ctx) {
1314
+ // If the clear button has focus, let the browser handle ArrowDown normally.
1315
+ if (isClearBtnFocused(ctx)) {
1261
1316
  return;
1262
1317
  }
1263
1318
 
1264
1319
  if (component.availableOptions.length > 0) {
1265
1320
  component.showBib();
1266
1321
  }
1322
+ // Read live visibility — ctx.isExpanded was computed before showBib() above,
1323
+ // so it wouldn't reflect the state change.
1267
1324
  if (component.dropdown.isPopoverVisible) {
1268
1325
  evt.preventDefault();
1269
1326
  navigateArrow(component, 'down');
@@ -4835,7 +4892,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
4835
4892
  }
4836
4893
  };
4837
4894
 
4838
- var formkitVersion$2 = '202603191903';
4895
+ var formkitVersion$2 = '202603232258';
4839
4896
 
4840
4897
  let AuroElement$2 = class AuroElement extends LitElement {
4841
4898
  static get properties() {
@@ -12583,7 +12640,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
12583
12640
  }
12584
12641
  };
12585
12642
 
12586
- var formkitVersion$1 = '202603191903';
12643
+ var formkitVersion$1 = '202603232258';
12587
12644
 
12588
12645
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12589
12646
  // See LICENSE in the project root for license information.
@@ -13622,7 +13679,7 @@ class AuroBibtemplate extends LitElement {
13622
13679
  }
13623
13680
  }
13624
13681
 
13625
- var formkitVersion = '202603191903';
13682
+ var formkitVersion = '202603232258';
13626
13683
 
13627
13684
  var styleCss$1 = css`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
13628
13685
 
@@ -13966,6 +14023,19 @@ class AuroHelpText extends LitElement {
13966
14023
  // See LICENSE in the project root for license information.
13967
14024
 
13968
14025
 
14026
+ /**
14027
+ * Strips only trailing whitespace from an input value so residual spaces
14028
+ * from cursor editing don't break filtering or highlighting. Whitespace-only
14029
+ * values are preserved as-is to prevent a lone space from being treated as
14030
+ * empty input.
14031
+ * @param {string} value - Raw input value.
14032
+ * @returns {string} Normalized value.
14033
+ */
14034
+ function normalizeFilterValue(value) {
14035
+ const raw = value || '';
14036
+ return raw.trim() ? raw.trimEnd() : raw;
14037
+ }
14038
+
13969
14039
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
13970
14040
  /**
13971
14041
  * The `auro-combobox` element provides users with a way to select an option from a list of filtered or suggested options based on user input.
@@ -14479,6 +14549,8 @@ class AuroCombobox extends AuroElement {
14479
14549
 
14480
14550
  this.noMatchOption = undefined;
14481
14551
 
14552
+ const filterValue = normalizeFilterValue(this.input.value).toLowerCase();
14553
+
14482
14554
  this.options.forEach((option) => {
14483
14555
  let matchString = option.textContent.toLowerCase();
14484
14556
 
@@ -14495,12 +14567,12 @@ class AuroCombobox extends AuroElement {
14495
14567
  }
14496
14568
 
14497
14569
  // If input is empty, show all options (except static ones)
14498
- if (!this.input.value || this.input.value.length === 0) {
14570
+ if (!filterValue) {
14499
14571
  if (!option.hasAttribute('static')) {
14500
14572
  option.removeAttribute('hidden');
14501
14573
  this.availableOptions.push(option);
14502
14574
  }
14503
- } else if (matchString.includes(this.input.value.toLowerCase()) && !option.hasAttribute('static')) {
14575
+ } else if (matchString.includes(filterValue) && !option.hasAttribute('static')) {
14504
14576
  // only count options that match the typed input value AND are not static
14505
14577
  option.removeAttribute('hidden');
14506
14578
  this.availableOptions.push(option);
@@ -14531,7 +14603,7 @@ class AuroCombobox extends AuroElement {
14531
14603
  syncValuesAndStates() {
14532
14604
  // Only sync matchWord, don't set menu.value here since setMenuValue should handle that
14533
14605
  if (this.menu) {
14534
- this.menu.matchWord = this.input.value;
14606
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
14535
14607
  }
14536
14608
  const label = this.menu.currentLabel;
14537
14609
  this.updateTriggerTextDisplay(label || this.value);
@@ -14895,8 +14967,9 @@ class AuroCombobox extends AuroElement {
14895
14967
  this.updateTriggerTextDisplay(event.detail.label || event.detail.value);
14896
14968
 
14897
14969
  // Update match word for filtering
14898
- if (this.menu.matchWord !== this.input.value) {
14899
- this.menu.matchWord = this.input.value;
14970
+ const trimmedInput = normalizeFilterValue(this.input.value);
14971
+ if (this.menu.matchWord !== trimmedInput) {
14972
+ this.menu.matchWord = trimmedInput;
14900
14973
  }
14901
14974
 
14902
14975
  // Update available options based on selection
@@ -15034,7 +15107,7 @@ class AuroCombobox extends AuroElement {
15034
15107
  });
15035
15108
 
15036
15109
  // Run filtering inline — the re-entrant event won't reach this code.
15037
- this.menu.matchWord = this.inputInBib.value;
15110
+ this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
15038
15111
  this.optionActive = null;
15039
15112
  this.handleMenuOptions();
15040
15113
  this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
@@ -15048,7 +15121,7 @@ class AuroCombobox extends AuroElement {
15048
15121
 
15049
15122
  this.inputInBib.value = this.input.value;
15050
15123
 
15051
- this.menu.matchWord = this.input.value;
15124
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
15052
15125
  this.optionActive = null;
15053
15126
 
15054
15127
  if (!this.input.value) {
@@ -15100,7 +15173,10 @@ class AuroCombobox extends AuroElement {
15100
15173
  * @returns {void}
15101
15174
  */
15102
15175
  configureCombobox() {
15103
- applyKeyboardStrategy(this, comboboxKeyboardStrategy);
15176
+ applyKeyboardStrategy(this, comboboxKeyboardStrategy, {
15177
+ // In modal mode the input moves into the bib; route keyboard events to that element instead.
15178
+ inputResolver: (comp, ctx) => (ctx.isModal && comp.inputInBib ? comp.inputInBib : comp.input),
15179
+ });
15104
15180
 
15105
15181
  this.addEventListener('focusin', () => {
15106
15182
  this.touched = true;
@@ -15259,7 +15335,7 @@ class AuroCombobox extends AuroElement {
15259
15335
 
15260
15336
  // Sync the input and match word, but don't directly set menu.value again
15261
15337
  if (this.menu) {
15262
- this.menu.matchWord = this.input.value;
15338
+ this.menu.matchWord = normalizeFilterValue(this.input.value);
15263
15339
  }
15264
15340
 
15265
15341
  this.dispatchEvent(new CustomEvent('input', {
@@ -1470,7 +1470,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$2 {
1470
1470
  }
1471
1471
  };
1472
1472
 
1473
- var formkitVersion$1 = '202603191903';
1473
+ var formkitVersion$1 = '202603232258';
1474
1474
 
1475
1475
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1476
1476
  // See LICENSE in the project root for license information.
@@ -1789,6 +1789,25 @@ class AuroCounter extends i$2 {
1789
1789
  }
1790
1790
  }
1791
1791
 
1792
+ /**
1793
+ * Sets ariaDescribedByElements on the spinbutton to the slotted description elements,
1794
+ * bridging the shadow DOM boundary for screen readers.
1795
+ * @param {Event} event - The slotchange event.
1796
+ * @private
1797
+ */
1798
+ onDescriptionSlotChange(event) {
1799
+ const assignedNodes = event.target.assignedElements();
1800
+ const spinbutton = this.shadowRoot.querySelector('[role="spinbutton"]');
1801
+
1802
+ if (spinbutton) {
1803
+ if (assignedNodes.length > 0) {
1804
+ spinbutton.ariaDescribedByElements = assignedNodes;
1805
+ } else {
1806
+ spinbutton.ariaDescribedByElements = [];
1807
+ }
1808
+ }
1809
+ }
1810
+
1792
1811
  updated(changedProperties) {
1793
1812
  if (changedProperties.has("value")) {
1794
1813
  this.validate();
@@ -1844,11 +1863,10 @@ class AuroCounter extends i$2 {
1844
1863
  <label id="counter-label" class="label">
1845
1864
  <slot @slotchange="${this.onDefaultSlotChange}"></slot>
1846
1865
  </label>
1847
- <slot id="counter-description" name="description" class="body-xs"></slot>
1866
+ <slot name="description" class="body-xs" @slotchange="${this.onDescriptionSlotChange}"></slot>
1848
1867
  </div>
1849
1868
  <div
1850
1869
  part="counterControl"
1851
- aria-describedby="counter-description"
1852
1870
  aria-disabled="${o$2(this.disabled ? 'true' : undefined)}"
1853
1871
  aria-labelledby="counter-label"
1854
1872
  aria-valuemax="${this.max}"
@@ -5476,7 +5494,7 @@ class AuroHelpText extends i$2 {
5476
5494
  }
5477
5495
  }
5478
5496
 
5479
- var formkitVersion = '202603191903';
5497
+ var formkitVersion = '202603232258';
5480
5498
 
5481
5499
  let AuroElement$1 = class AuroElement extends i$2 {
5482
5500
  static get properties() {
@@ -1470,7 +1470,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$2 {
1470
1470
  }
1471
1471
  };
1472
1472
 
1473
- var formkitVersion$1 = '202603191903';
1473
+ var formkitVersion$1 = '202603232258';
1474
1474
 
1475
1475
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1476
1476
  // See LICENSE in the project root for license information.
@@ -1789,6 +1789,25 @@ class AuroCounter extends i$2 {
1789
1789
  }
1790
1790
  }
1791
1791
 
1792
+ /**
1793
+ * Sets ariaDescribedByElements on the spinbutton to the slotted description elements,
1794
+ * bridging the shadow DOM boundary for screen readers.
1795
+ * @param {Event} event - The slotchange event.
1796
+ * @private
1797
+ */
1798
+ onDescriptionSlotChange(event) {
1799
+ const assignedNodes = event.target.assignedElements();
1800
+ const spinbutton = this.shadowRoot.querySelector('[role="spinbutton"]');
1801
+
1802
+ if (spinbutton) {
1803
+ if (assignedNodes.length > 0) {
1804
+ spinbutton.ariaDescribedByElements = assignedNodes;
1805
+ } else {
1806
+ spinbutton.ariaDescribedByElements = [];
1807
+ }
1808
+ }
1809
+ }
1810
+
1792
1811
  updated(changedProperties) {
1793
1812
  if (changedProperties.has("value")) {
1794
1813
  this.validate();
@@ -1844,11 +1863,10 @@ class AuroCounter extends i$2 {
1844
1863
  <label id="counter-label" class="label">
1845
1864
  <slot @slotchange="${this.onDefaultSlotChange}"></slot>
1846
1865
  </label>
1847
- <slot id="counter-description" name="description" class="body-xs"></slot>
1866
+ <slot name="description" class="body-xs" @slotchange="${this.onDescriptionSlotChange}"></slot>
1848
1867
  </div>
1849
1868
  <div
1850
1869
  part="counterControl"
1851
- aria-describedby="counter-description"
1852
1870
  aria-disabled="${o$2(this.disabled ? 'true' : undefined)}"
1853
1871
  aria-labelledby="counter-label"
1854
1872
  aria-valuemax="${this.max}"
@@ -5476,7 +5494,7 @@ class AuroHelpText extends i$2 {
5476
5494
  }
5477
5495
  }
5478
5496
 
5479
- var formkitVersion = '202603191903';
5497
+ var formkitVersion = '202603232258';
5480
5498
 
5481
5499
  let AuroElement$1 = class AuroElement extends i$2 {
5482
5500
  static get properties() {
@@ -114,6 +114,13 @@ export class AuroCounter extends LitElement {
114
114
  * @private
115
115
  */
116
116
  private onDefaultSlotChange;
117
+ /**
118
+ * Sets ariaDescribedByElements on the spinbutton to the slotted description elements,
119
+ * bridging the shadow DOM boundary for screen readers.
120
+ * @param {Event} event - The slotchange event.
121
+ * @private
122
+ */
123
+ private onDescriptionSlotChange;
117
124
  updated(changedProperties: any): void;
118
125
  /**
119
126
  * Returns HTML for the help text and error message.
@@ -1420,7 +1420,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
1420
1420
  }
1421
1421
  };
1422
1422
 
1423
- var formkitVersion$1 = '202603191903';
1423
+ var formkitVersion$1 = '202603232258';
1424
1424
 
1425
1425
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1426
1426
  // See LICENSE in the project root for license information.
@@ -1739,6 +1739,25 @@ class AuroCounter extends LitElement {
1739
1739
  }
1740
1740
  }
1741
1741
 
1742
+ /**
1743
+ * Sets ariaDescribedByElements on the spinbutton to the slotted description elements,
1744
+ * bridging the shadow DOM boundary for screen readers.
1745
+ * @param {Event} event - The slotchange event.
1746
+ * @private
1747
+ */
1748
+ onDescriptionSlotChange(event) {
1749
+ const assignedNodes = event.target.assignedElements();
1750
+ const spinbutton = this.shadowRoot.querySelector('[role="spinbutton"]');
1751
+
1752
+ if (spinbutton) {
1753
+ if (assignedNodes.length > 0) {
1754
+ spinbutton.ariaDescribedByElements = assignedNodes;
1755
+ } else {
1756
+ spinbutton.ariaDescribedByElements = [];
1757
+ }
1758
+ }
1759
+ }
1760
+
1742
1761
  updated(changedProperties) {
1743
1762
  if (changedProperties.has("value")) {
1744
1763
  this.validate();
@@ -1794,11 +1813,10 @@ class AuroCounter extends LitElement {
1794
1813
  <label id="counter-label" class="label">
1795
1814
  <slot @slotchange="${this.onDefaultSlotChange}"></slot>
1796
1815
  </label>
1797
- <slot id="counter-description" name="description" class="body-xs"></slot>
1816
+ <slot name="description" class="body-xs" @slotchange="${this.onDescriptionSlotChange}"></slot>
1798
1817
  </div>
1799
1818
  <div
1800
1819
  part="counterControl"
1801
- aria-describedby="counter-description"
1802
1820
  aria-disabled="${ifDefined(this.disabled ? 'true' : undefined)}"
1803
1821
  aria-labelledby="counter-label"
1804
1822
  aria-valuemax="${this.max}"
@@ -5408,7 +5426,7 @@ class AuroHelpText extends LitElement {
5408
5426
  }
5409
5427
  }
5410
5428
 
5411
- var formkitVersion = '202603191903';
5429
+ var formkitVersion = '202603232258';
5412
5430
 
5413
5431
  let AuroElement$1 = class AuroElement extends LitElement {
5414
5432
  static get properties() {
@@ -1420,7 +1420,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
1420
1420
  }
1421
1421
  };
1422
1422
 
1423
- var formkitVersion$1 = '202603191903';
1423
+ var formkitVersion$1 = '202603232258';
1424
1424
 
1425
1425
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
1426
1426
  // See LICENSE in the project root for license information.
@@ -1739,6 +1739,25 @@ class AuroCounter extends LitElement {
1739
1739
  }
1740
1740
  }
1741
1741
 
1742
+ /**
1743
+ * Sets ariaDescribedByElements on the spinbutton to the slotted description elements,
1744
+ * bridging the shadow DOM boundary for screen readers.
1745
+ * @param {Event} event - The slotchange event.
1746
+ * @private
1747
+ */
1748
+ onDescriptionSlotChange(event) {
1749
+ const assignedNodes = event.target.assignedElements();
1750
+ const spinbutton = this.shadowRoot.querySelector('[role="spinbutton"]');
1751
+
1752
+ if (spinbutton) {
1753
+ if (assignedNodes.length > 0) {
1754
+ spinbutton.ariaDescribedByElements = assignedNodes;
1755
+ } else {
1756
+ spinbutton.ariaDescribedByElements = [];
1757
+ }
1758
+ }
1759
+ }
1760
+
1742
1761
  updated(changedProperties) {
1743
1762
  if (changedProperties.has("value")) {
1744
1763
  this.validate();
@@ -1794,11 +1813,10 @@ class AuroCounter extends LitElement {
1794
1813
  <label id="counter-label" class="label">
1795
1814
  <slot @slotchange="${this.onDefaultSlotChange}"></slot>
1796
1815
  </label>
1797
- <slot id="counter-description" name="description" class="body-xs"></slot>
1816
+ <slot name="description" class="body-xs" @slotchange="${this.onDescriptionSlotChange}"></slot>
1798
1817
  </div>
1799
1818
  <div
1800
1819
  part="counterControl"
1801
- aria-describedby="counter-description"
1802
1820
  aria-disabled="${ifDefined(this.disabled ? 'true' : undefined)}"
1803
1821
  aria-labelledby="counter-label"
1804
1822
  aria-valuemax="${this.max}"
@@ -5408,7 +5426,7 @@ class AuroHelpText extends LitElement {
5408
5426
  }
5409
5427
  }
5410
5428
 
5411
- var formkitVersion = '202603191903';
5429
+ var formkitVersion = '202603232258';
5412
5430
 
5413
5431
  let AuroElement$1 = class AuroElement extends LitElement {
5414
5432
  static get properties() {