@smilodon/core 1.4.11 → 1.4.13

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.
package/dist/index.umd.js CHANGED
@@ -1355,6 +1355,7 @@
1355
1355
  maxSelections: 0,
1356
1356
  showRemoveButton: true,
1357
1357
  closeOnSelect: true,
1358
+ toggleOnTriggerClick: true,
1358
1359
  },
1359
1360
  scrollToSelected: {
1360
1361
  enabled: true,
@@ -1407,6 +1408,18 @@
1407
1408
  icon: '×',
1408
1409
  },
1409
1410
  callbacks: {},
1411
+ tracking: {
1412
+ enabled: false,
1413
+ events: true,
1414
+ styling: true,
1415
+ limitations: true,
1416
+ emitDiagnostics: false,
1417
+ maxEntries: 200,
1418
+ },
1419
+ limitations: {
1420
+ policies: {},
1421
+ autoMitigateRuntimeModeSwitch: true,
1422
+ },
1410
1423
  enabled: true,
1411
1424
  searchable: false,
1412
1425
  placeholder: 'Select an option...',
@@ -1875,6 +1888,8 @@
1875
1888
  * Enhanced Select Component
1876
1889
  * Implements all advanced features: infinite scroll, load more, busy state,
1877
1890
  * server-side selection, and full customization
1891
+ *
1892
+ * ✨ Redesigned with formal elegance, refined microinteractions, and polished UX
1878
1893
  */
1879
1894
  class EnhancedSelect extends HTMLElement {
1880
1895
  get classMap() {
@@ -1883,6 +1898,10 @@
1883
1898
  set classMap(map) {
1884
1899
  this._classMap = map;
1885
1900
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || map || this._groupHeaderRenderer));
1901
+ this._track('style', 'classMapChanged', {
1902
+ hasClassMap: Boolean(map),
1903
+ keys: map ? Object.keys(map) : [],
1904
+ });
1886
1905
  if (!this.isConnected)
1887
1906
  return;
1888
1907
  this._renderOptions();
@@ -1898,6 +1917,7 @@
1898
1917
  set groupHeaderRenderer(renderer) {
1899
1918
  this._groupHeaderRenderer = renderer;
1900
1919
  this._setGlobalStylesMirroring(Boolean(this._optionRenderer || this._classMap || renderer));
1920
+ this._track('style', 'groupHeaderRendererChanged', { enabled: Boolean(renderer) });
1901
1921
  if (!this.isConnected)
1902
1922
  return;
1903
1923
  this._renderOptions();
@@ -1916,6 +1936,9 @@
1916
1936
  this._mirrorGlobalStylesForCustomOptions = false;
1917
1937
  this._globalStylesObserver = null;
1918
1938
  this._globalStylesContainer = null;
1939
+ this._tracking = { events: [], styles: [], limitations: [] };
1940
+ this._suppressBlurClose = false;
1941
+ this._renderCycleId = 0;
1919
1942
  this._shadow = this.attachShadow({ mode: 'open' });
1920
1943
  this._uniqueId = `enhanced-select-${Math.random().toString(36).substr(2, 9)}`;
1921
1944
  this._rendererHelpers = this._buildRendererHelpers();
@@ -1990,6 +2013,7 @@
1990
2013
  if (this._boundArrowClick && this._arrowContainer) {
1991
2014
  this._arrowContainer.removeEventListener('click', this._boundArrowClick);
1992
2015
  }
2016
+ this._renderCycleId += 1;
1993
2017
  this._teardownGlobalStylesMirroring();
1994
2018
  }
1995
2019
  _setGlobalStylesMirroring(enabled) {
@@ -2000,6 +2024,7 @@
2000
2024
  return;
2001
2025
  }
2002
2026
  this._mirrorGlobalStylesForCustomOptions = enabled;
2027
+ this._track('style', 'globalStylesMirroringChanged', { enabled });
2003
2028
  if (enabled) {
2004
2029
  this._setupGlobalStylesMirroring();
2005
2030
  }
@@ -2092,8 +2117,48 @@
2092
2117
  const container = document.createElement('div');
2093
2118
  container.className = 'input-container';
2094
2119
  container.setAttribute('part', 'button');
2120
+ const inputStyles = this._config.styles.input;
2121
+ if (inputStyles && !this._config.styles.container) {
2122
+ const shellStyleKeys = [
2123
+ 'background',
2124
+ 'backgroundColor',
2125
+ 'border',
2126
+ 'borderColor',
2127
+ 'borderStyle',
2128
+ 'borderWidth',
2129
+ 'borderRadius',
2130
+ 'boxShadow',
2131
+ 'padding',
2132
+ 'height',
2133
+ 'minHeight',
2134
+ 'maxHeight',
2135
+ ];
2136
+ for (const key of shellStyleKeys) {
2137
+ const value = inputStyles[key];
2138
+ if (value != null && value !== '') {
2139
+ container.style[key] = value;
2140
+ }
2141
+ }
2142
+ }
2095
2143
  return container;
2096
2144
  }
2145
+ _syncInputContainerMode() {
2146
+ if (!this._inputContainer || !this._input)
2147
+ return;
2148
+ const isMulti = this._config.selection.mode === 'multi';
2149
+ this._inputContainer.classList.toggle('input-container--multi', isMulti);
2150
+ this._inputContainer.classList.toggle('input-container--single', !isMulti);
2151
+ if (isMulti) {
2152
+ this._input.style.flex = '1 0 var(--select-multi-input-min-width, 96px)';
2153
+ this._input.style.width = 'auto';
2154
+ this._input.style.minWidth = 'var(--select-multi-input-min-width, 96px)';
2155
+ }
2156
+ else {
2157
+ this._input.style.flex = '1 1 auto';
2158
+ this._input.style.width = '100%';
2159
+ this._input.style.minWidth = '0';
2160
+ }
2161
+ }
2097
2162
  _createInput() {
2098
2163
  const input = document.createElement('input');
2099
2164
  input.setAttribute('part', 'input');
@@ -2103,6 +2168,35 @@
2103
2168
  input.placeholder = this._config.placeholder || 'Select an option...';
2104
2169
  input.disabled = !this._config.enabled;
2105
2170
  input.readOnly = !this._config.searchable;
2171
+ // Apply a direct inline reset so the input is only the writable text layer,
2172
+ // while the `.input-container` remains the sole visible control shell.
2173
+ input.style.all = 'unset';
2174
+ input.style.display = 'block';
2175
+ input.style.flex = '1 1 auto';
2176
+ input.style.width = '100%';
2177
+ input.style.maxWidth = '100%';
2178
+ input.style.minWidth = '0';
2179
+ input.style.minInlineSize = '0';
2180
+ input.style.minHeight = '0';
2181
+ input.style.padding = '0';
2182
+ input.style.margin = '0';
2183
+ input.style.border = '0';
2184
+ input.style.background = 'transparent';
2185
+ input.style.boxSizing = 'border-box';
2186
+ input.style.outline = 'none';
2187
+ input.style.font = 'inherit';
2188
+ input.style.fontFamily = 'inherit';
2189
+ input.style.lineHeight = 'inherit';
2190
+ input.style.color = 'inherit';
2191
+ input.style.alignSelf = 'center';
2192
+ input.style.appearance = 'none';
2193
+ input.style.webkitAppearance = 'none';
2194
+ input.style.boxShadow = 'none';
2195
+ input.style.borderRadius = '0';
2196
+ input.style.overflow = 'hidden';
2197
+ input.style.textOverflow = 'ellipsis';
2198
+ input.style.whiteSpace = 'nowrap';
2199
+ input.style.cursor = this._config.searchable ? 'text' : 'default';
2106
2200
  // Update readonly when input is focused if searchable
2107
2201
  input.addEventListener('focus', () => {
2108
2202
  if (this._config.searchable) {
@@ -2113,7 +2207,22 @@
2113
2207
  input.className += ' ' + this._config.styles.classNames.input;
2114
2208
  }
2115
2209
  if (this._config.styles.input) {
2116
- Object.assign(input.style, this._config.styles.input);
2210
+ const inputStyles = { ...this._config.styles.input };
2211
+ // Route shell-like styling to .input-container so the control appears as
2212
+ // a single styled input instead of two visually separate layers.
2213
+ delete inputStyles.background;
2214
+ delete inputStyles.backgroundColor;
2215
+ delete inputStyles.border;
2216
+ delete inputStyles.borderColor;
2217
+ delete inputStyles.borderStyle;
2218
+ delete inputStyles.borderWidth;
2219
+ delete inputStyles.borderRadius;
2220
+ delete inputStyles.boxShadow;
2221
+ delete inputStyles.padding;
2222
+ delete inputStyles.height;
2223
+ delete inputStyles.minHeight;
2224
+ delete inputStyles.maxHeight;
2225
+ Object.assign(input.style, inputStyles);
2117
2226
  }
2118
2227
  input.setAttribute('role', 'combobox');
2119
2228
  input.setAttribute('aria-expanded', 'false');
@@ -2157,7 +2266,7 @@
2157
2266
  container.className = 'dropdown-arrow-container';
2158
2267
  container.innerHTML = `
2159
2268
  <svg class="dropdown-arrow" part="arrow" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2160
- <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2269
+ <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
2161
2270
  </svg>
2162
2271
  `;
2163
2272
  return container;
@@ -2170,7 +2279,7 @@
2170
2279
  const icon = document.createElement('span');
2171
2280
  icon.className = 'clear-control-icon';
2172
2281
  icon.setAttribute('part', 'clear-icon');
2173
- icon.textContent = this._config.clearControl.icon || '×';
2282
+ icon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
2174
2283
  button.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
2175
2284
  button.appendChild(icon);
2176
2285
  this._clearControlIcon = icon;
@@ -2196,18 +2305,229 @@
2196
2305
  this._dropdown.id = listboxId;
2197
2306
  this._input.setAttribute('aria-controls', listboxId);
2198
2307
  this._input.setAttribute('aria-owns', listboxId);
2308
+ this._syncInputContainerMode();
2199
2309
  this._syncClearControlState();
2200
2310
  }
2201
2311
  _initializeStyles() {
2202
2312
  const style = document.createElement('style');
2203
2313
  style.textContent = `
2314
+ /* ═══════════════════════════════════════════════════════════════════════════
2315
+ ELEGANT SELECT COMPONENT — Refined Design System
2316
+ Formal aesthetics with sophisticated microinteractions
2317
+ ═══════════════════════════════════════════════════════════════════════════ */
2318
+
2204
2319
  :host {
2320
+ --select-primary: #1a1a2e;
2321
+ --select-primary-light: #16213e;
2322
+ --select-accent: #0f3460;
2323
+ --select-accent-hover: #e94560;
2324
+ --select-surface: #ffffff;
2325
+ --select-surface-elevated: #fafbfc;
2326
+ --select-border: #e1e5eb;
2327
+ --select-border-focus: #0f3460;
2328
+ --select-text: #1a1a2e;
2329
+ --select-text-muted: #6b7280;
2330
+ --select-text-placeholder: #9ca3af;
2331
+ --select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
2332
+ --select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
2333
+ --select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);
2334
+ --select-shadow-focus: 0 0 0 3px rgba(15, 52, 96, 0.12);
2335
+ --select-radius-sm: 6px;
2336
+ --select-radius-md: 10px;
2337
+ --select-radius-lg: 14px;
2338
+ --select-transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
2339
+ --select-transition-smooth: 250ms cubic-bezier(0.4, 0, 0.2, 1);
2340
+ --select-transition-bounce: 350ms cubic-bezier(0.34, 1.56, 0.64, 1);
2341
+ --select-badge-animation: badgeEnter 300ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
2342
+ --select-badge-enter-from-opacity: 0;
2343
+ --select-badge-enter-from-transform: scale(0.8) translateY(-4px);
2344
+ --select-badge-enter-to-opacity: 1;
2345
+ --select-badge-enter-to-transform: scale(1) translateY(0);
2346
+ --select-badge-hover-transform: scale(1.02);
2347
+ --select-badge-letter-spacing: 0.01em;
2348
+ --select-badge-hover-shadow: var(--select-shadow-md), inset 0 1px 0 rgba(255, 255, 255, 0.15);
2349
+ --select-badge-remove-focus-outline: 2px solid rgba(255, 255, 255, 0.5);
2350
+ --select-badge-remove-focus-offset: 1px;
2351
+ --select-badge-remove-hover-transform: scale(1.15) rotate(90deg);
2352
+ --select-badge-remove-active-transform: scale(0.95) rotate(90deg);
2353
+ --select-input-hover-border: var(--select-border-focus);
2354
+ --select-input-hover-shadow: var(--select-shadow-sm), 0 0 0 1px rgba(15, 52, 96, 0.05);
2355
+ --select-input-font-weight: 450;
2356
+ --select-input-letter-spacing: 0.01em;
2357
+ --select-input-disabled-opacity: 0.6;
2358
+ --select-separator-opacity: 0.6;
2359
+ --select-separator-active-opacity: 1;
2360
+ --select-separator-dark-bg: linear-gradient(
2361
+ to bottom,
2362
+ transparent 0%,
2363
+ var(--select-border) 20%,
2364
+ var(--select-border) 80%,
2365
+ transparent 100%
2366
+ );
2367
+ --select-arrow-open-transform: rotate(180deg);
2368
+ --select-clear-button-hover-transform: translateY(-50%) scale(1.1);
2369
+ --select-clear-button-active-transform: translateY(-50%) scale(0.95);
2370
+ --select-clear-button-focus-offset: 2px;
2371
+ --select-clear-icon-size: 14px;
2372
+ --select-dropdown-top: calc(100% + 6px);
2373
+ --select-dropdown-animation: dropdownEnter 200ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
2374
+ --select-dropdown-enter-from-opacity: 0;
2375
+ --select-dropdown-enter-from-transform: translateY(-8px) scale(0.98);
2376
+ --select-dropdown-enter-to-opacity: 1;
2377
+ --select-dropdown-enter-to-transform: translateY(0) scale(1);
2378
+ --select-dropdown-scroll-behavior: smooth;
2379
+ --select-dropdown-transform-origin: top center;
2380
+ --select-scrollbar-width: 6px;
2381
+ --select-scrollbar-thumb-radius: 3px;
2382
+ --select-option-hover-transform: translateX(2px);
2383
+ --select-option-font-weight: 450;
2384
+ --select-option-margin: 2px 0;
2385
+ --select-option-active-outline-offset: -2px;
2386
+ --select-option-selected-active-outline-offset: -2px;
2387
+ --select-option-selected-indicator-width: 3px;
2388
+ --select-option-selected-indicator-height: 60%;
2389
+ --select-option-selected-indicator-bg: var(--select-accent);
2390
+ --select-option-selected-indicator-radius: 0 2px 2px 0;
2391
+ --select-option-selected-indicator-left: 0;
2392
+ --select-option-selected-indicator-top: 50%;
2393
+ --select-option-selected-indicator-transform: translateY(-50%);
2394
+ --select-option-selected-indicator-animation: selectedIndicator 200ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
2395
+ --select-option-selected-indicator-from-height: 0;
2396
+ --select-option-selected-indicator-to-height: 60%;
2397
+ --select-option-selected-indicator-from-opacity: 0;
2398
+ --select-option-selected-indicator-to-opacity: 1;
2399
+ --select-option-pressed-transform: translateX(2px) scale(0.99);
2400
+ --select-option-selected-pressed-transform: scale(0.99);
2401
+ --select-button-hover-transform: translateY(-1px);
2402
+ --select-button-active-transform: translateY(0) scale(0.98);
2403
+ --select-button-font-weight: 550;
2404
+ --select-button-letter-spacing: 0.02em;
2405
+ --select-spinner-animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
2406
+ --select-searching-spinner-active-color: var(--select-accent);
2407
+ --select-badge-remove-margin-left: 2px;
2408
+ --select-badge-remove-radius: 50%;
2409
+ --select-badge-remove-font-weight: 600;
2410
+ --select-empty-gap: 8px;
2411
+ --select-searching-gap: 8px;
2412
+ --select-searching-spinner-size: 24px;
2413
+ --select-searching-spinner-border: 2px solid var(--select-border);
2414
+ --select-searching-spinner-animation: var(--select-spinner-animation);
2415
+ --select-group-header-separator-margin-top: 8px;
2416
+ --select-group-header-separator-padding-top: 14px;
2417
+ --select-group-header-separator-border-top: 1px solid var(--select-border);
2418
+ --select-reduced-motion-duration: 0.01ms;
2419
+ --select-reduced-motion-iteration-count: 1;
2420
+ --select-high-contrast-border-width: 2px;
2421
+ --select-high-contrast-indicator-width: 4px;
2422
+ --select-high-contrast-focus-outline-width: 3px;
2423
+ --select-high-contrast-focus-outline-color: Highlight;
2424
+ --select-touch-target-min-height: 44px;
2425
+ --select-badge-dark-bg: linear-gradient(135deg, var(--select-accent) 0%, #4f46e5 100%);
2426
+
2205
2427
  display: block;
2206
2428
  position: relative;
2207
2429
  width: var(--select-width, 100%);
2208
2430
  height: var(--select-height, auto);
2431
+ font-family: var(--select-font-family, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
2432
+ }
2433
+
2434
+ /* ─────────────────────────────────────────────────────────────────────────
2435
+ Selection Badges — Refined pill design with elegant interactions
2436
+ ───────────────────────────────────────────────────────────────────────── */
2437
+
2438
+ .selection-badge {
2439
+ display: inline-flex;
2440
+ align-items: center;
2441
+ gap: var(--select-badge-gap, 6px);
2442
+ flex: 0 1 auto;
2443
+ min-height: var(--select-badge-min-height, 26px);
2444
+ padding: var(--select-badge-padding, 4px 10px 4px 12px);
2445
+ margin: var(--select-badge-margin, 3px);
2446
+ background: var(--select-badge-bg, linear-gradient(135deg, var(--select-accent) 0%, var(--select-primary-light) 100%));
2447
+ color: var(--select-badge-color, #ffffff);
2448
+ border: var(--select-badge-border, none);
2449
+ border-radius: var(--select-badge-border-radius, 999px);
2450
+ box-shadow: var(--select-badge-shadow, var(--select-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, 0.1));
2451
+ box-sizing: border-box;
2452
+ font-size: var(--select-badge-font-size, 13px);
2453
+ font-weight: var(--select-badge-font-weight, 500);
2454
+ line-height: var(--select-badge-line-height, 1.2);
2455
+ letter-spacing: var(--select-badge-letter-spacing);
2456
+ max-width: var(--select-badge-max-width, 100%);
2457
+ overflow: hidden;
2458
+ transform: scale(1);
2459
+ transition:
2460
+ transform var(--select-transition-bounce),
2461
+ box-shadow var(--select-transition-fast),
2462
+ background var(--select-transition-fast);
2463
+ animation: var(--select-badge-animation);
2209
2464
  }
2210
2465
 
2466
+ @keyframes badgeEnter {
2467
+ 0% {
2468
+ opacity: var(--select-badge-enter-from-opacity);
2469
+ transform: var(--select-badge-enter-from-transform);
2470
+ }
2471
+ 100% {
2472
+ opacity: var(--select-badge-enter-to-opacity);
2473
+ transform: var(--select-badge-enter-to-transform);
2474
+ }
2475
+ }
2476
+
2477
+ .selection-badge:hover {
2478
+ box-shadow: var(--select-badge-hover-shadow);
2479
+ transform: var(--select-badge-hover-transform);
2480
+ }
2481
+
2482
+ .selection-badge-label {
2483
+ display: block;
2484
+ min-width: 0;
2485
+ overflow: hidden;
2486
+ text-overflow: ellipsis;
2487
+ white-space: nowrap;
2488
+ line-height: var(--select-badge-line-height, 1.2);
2489
+ }
2490
+
2491
+ .badge-remove {
2492
+ display: inline-flex;
2493
+ align-items: center;
2494
+ justify-content: center;
2495
+ width: var(--select-badge-remove-size, 18px);
2496
+ height: var(--select-badge-remove-size, 18px);
2497
+ padding: 0;
2498
+ margin-left: var(--select-badge-remove-margin-left);
2499
+ background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.2));
2500
+ border: var(--select-badge-remove-border, none);
2501
+ border-radius: var(--select-badge-remove-radius);
2502
+ color: var(--select-badge-remove-color, #ffffff);
2503
+ font-size: var(--select-badge-remove-font-size, 11px);
2504
+ font-weight: var(--select-badge-remove-font-weight);
2505
+ line-height: 1;
2506
+ cursor: pointer;
2507
+ flex: 0 0 auto;
2508
+ transition:
2509
+ background var(--select-transition-fast),
2510
+ transform var(--select-transition-bounce);
2511
+ }
2512
+
2513
+ .badge-remove:hover {
2514
+ background: var(--select-badge-remove-hover-bg, rgba(233, 69, 96, 0.9));
2515
+ transform: var(--select-badge-remove-hover-transform);
2516
+ }
2517
+
2518
+ .badge-remove:focus-visible {
2519
+ outline: var(--select-badge-remove-focus-outline);
2520
+ outline-offset: var(--select-badge-remove-focus-offset);
2521
+ }
2522
+
2523
+ .badge-remove:active {
2524
+ transform: var(--select-badge-remove-active-transform);
2525
+ }
2526
+
2527
+ /* ─────────────────────────────────────────────────────────────────────────
2528
+ Input Container — Sophisticated control shell
2529
+ ───────────��───────────────────────────────────────────────────────────── */
2530
+
2211
2531
  .select-container {
2212
2532
  position: relative;
2213
2533
  width: 100%;
@@ -2218,102 +2538,143 @@
2218
2538
  width: 100%;
2219
2539
  display: flex;
2220
2540
  align-items: center;
2221
- flex-wrap: wrap;
2541
+ flex-wrap: nowrap;
2222
2542
  gap: var(--select-input-gap, 6px);
2223
- padding: var(--select-input-padding, 6px 52px 6px 8px);
2543
+ padding: var(--select-input-padding, 10px 52px 10px 14px);
2224
2544
  height: var(--select-input-height, auto);
2225
- min-height: var(--select-input-min-height, 44px);
2545
+ min-height: var(--select-input-min-height, 48px);
2226
2546
  max-height: var(--select-input-max-height, 160px);
2227
2547
  overflow-y: var(--select-input-overflow-y, auto);
2228
- align-content: flex-start;
2229
- background: var(--select-input-bg, var(--select-bg, white));
2230
- border: var(--select-input-border, 1px solid var(--select-border-color, #d1d5db));
2231
- border-radius: var(--select-input-border-radius, 6px);
2548
+ align-content: center;
2549
+ background: var(--select-input-bg, var(--select-surface));
2550
+ border: var(--select-input-border, 1.5px solid var(--select-border));
2551
+ border-radius: var(--select-input-border-radius, var(--select-radius-md));
2552
+ box-shadow: var(--select-shadow-sm);
2232
2553
  box-sizing: border-box;
2233
- transition: all 0.2s ease;
2554
+ transition:
2555
+ border-color var(--select-transition-fast),
2556
+ box-shadow var(--select-transition-smooth),
2557
+ transform var(--select-transition-fast);
2558
+ }
2559
+
2560
+ .input-container:hover {
2561
+ border-color: var(--select-input-hover-border);
2562
+ box-shadow: var(--select-input-hover-shadow);
2563
+ }
2564
+
2565
+ .input-container.input-container--single {
2566
+ flex-wrap: nowrap;
2567
+ align-content: center;
2568
+ }
2569
+
2570
+ .input-container.input-container--multi {
2571
+ flex-wrap: wrap;
2572
+ align-content: flex-start;
2234
2573
  }
2235
2574
 
2236
2575
  .input-container:focus-within {
2237
- border-color: var(--select-input-focus-border, var(--select-border-focus-color, #667eea));
2238
- box-shadow: var(--select-input-focus-shadow, 0 0 0 3px rgba(102, 126, 234, 0.1));
2576
+ border-color: var(--select-input-focus-border, var(--select-border-focus));
2577
+ box-shadow: var(--select-shadow-focus), var(--select-shadow-sm);
2239
2578
  }
2240
2579
 
2241
- /* Gradient separator before arrow */
2580
+ /* Elegant separator line before arrow */
2242
2581
  .input-container::after {
2243
2582
  content: '';
2244
2583
  position: absolute;
2245
2584
  top: 50%;
2246
- right: var(--select-separator-position, var(--select-seperator-position, 40px));
2585
+ right: var(--select-separator-position, 42px);
2247
2586
  transform: translateY(-50%);
2248
- width: var(--select-separator-width, var(--select-seperator-width, 1px));
2249
- height: var(--select-separator-height, var(--select-seperator-height, 60%));
2250
- background: var(--select-separator-bg, var(--select-seperator-bg, var(--select-separator-gradient, var(--select-seperator-gradient, linear-gradient(
2587
+ width: var(--select-separator-width, 1px);
2588
+ height: var(--select-separator-height, 50%);
2589
+ background: var(--select-separator-bg, linear-gradient(
2251
2590
  to bottom,
2252
2591
  transparent 0%,
2253
- rgba(0, 0, 0, 0.1) 20%,
2254
- rgba(0, 0, 0, 0.1) 80%,
2592
+ var(--select-border) 20%,
2593
+ var(--select-border) 80%,
2255
2594
  transparent 100%
2256
- ))));
2595
+ ));
2257
2596
  pointer-events: none;
2258
2597
  z-index: 1;
2598
+ opacity: var(--select-separator-opacity);
2599
+ transition: opacity var(--select-transition-fast);
2259
2600
  }
2260
2601
 
2602
+ .input-container:hover::after,
2603
+ .input-container:focus-within::after {
2604
+ opacity: var(--select-separator-active-opacity);
2605
+ }
2606
+
2607
+ /* ─────────────────────────────────────────────────────────────────────────
2608
+ Dropdown Arrow — Smooth rotation with refined styling
2609
+ ───────────────────────────────────────────────────────────────────────── */
2610
+
2261
2611
  .dropdown-arrow-container {
2262
2612
  position: absolute;
2263
2613
  top: 0;
2264
2614
  right: 0;
2265
2615
  bottom: 0;
2266
- width: var(--select-arrow-width, 40px);
2267
- /* allow explicit height override even though container normally stretches */
2616
+ width: var(--select-arrow-width, 42px);
2268
2617
  height: var(--select-arrow-height, auto);
2269
2618
  display: flex;
2270
2619
  align-items: center;
2271
2620
  justify-content: center;
2272
2621
  cursor: pointer;
2273
- transition: background-color 0.2s ease;
2274
- border-radius: var(--select-arrow-border-radius, 0 4px 4px 0);
2622
+ border-radius: var(--select-arrow-border-radius, 0 var(--select-radius-md) var(--select-radius-md) 0);
2275
2623
  z-index: 2;
2624
+ transition: background-color var(--select-transition-fast);
2276
2625
  }
2277
2626
 
2278
2627
  .input-container.has-clear-control {
2279
- padding: var(--select-input-padding-with-clear, 6px 84px 6px 8px);
2628
+ padding: var(--select-input-padding-with-clear, 10px 84px 10px 14px);
2280
2629
  }
2281
2630
 
2282
2631
  .input-container.has-clear-control::after {
2283
- right: var(--select-separator-position-with-clear, var(--select-seperator-position-with-clear, 72px));
2632
+ right: var(--select-separator-position-with-clear, 74px);
2284
2633
  }
2285
2634
 
2286
2635
  .dropdown-arrow-container.with-clear-control {
2287
- right: var(--select-arrow-right-with-clear, 32px);
2636
+ right: var(--select-arrow-right-with-clear, 34px);
2288
2637
  }
2289
2638
 
2639
+ /* ─────────────────────────────────────────────────────────────────────────
2640
+ Clear Control — Minimal and elegant dismiss button
2641
+ ───────────────────────────────────────────────────────────────────────── */
2642
+
2290
2643
  .clear-control-button {
2291
2644
  position: absolute;
2292
2645
  top: 50%;
2293
- right: var(--select-clear-button-right, 6px);
2646
+ right: var(--select-clear-button-right, 8px);
2294
2647
  transform: translateY(-50%);
2295
- width: var(--select-clear-button-size, 24px);
2296
- height: var(--select-clear-button-size, 24px);
2648
+ width: var(--select-clear-button-size, 26px);
2649
+ height: var(--select-clear-button-size, 26px);
2297
2650
  border: var(--select-clear-button-border, none);
2298
- border-radius: var(--select-clear-button-radius, 999px);
2651
+ border-radius: var(--select-clear-button-radius, 50%);
2299
2652
  background: var(--select-clear-button-bg, transparent);
2300
- color: var(--select-clear-button-color, #6b7280);
2653
+ color: var(--select-clear-button-color, var(--select-text-muted));
2301
2654
  display: inline-flex;
2302
2655
  align-items: center;
2303
2656
  justify-content: center;
2304
2657
  cursor: pointer;
2305
2658
  z-index: 3;
2306
- transition: var(--select-clear-button-transition, all 0.2s ease);
2659
+ transition:
2660
+ background var(--select-transition-fast),
2661
+ color var(--select-transition-fast),
2662
+ transform var(--select-transition-bounce);
2307
2663
  }
2308
2664
 
2309
2665
  .clear-control-button:hover {
2310
- background: var(--select-clear-button-hover-bg, rgba(0, 0, 0, 0.06));
2311
- color: var(--select-clear-button-hover-color, #111827);
2666
+ background: var(--select-clear-button-hover-bg, rgba(233, 69, 96, 0.1));
2667
+ color: var(--select-clear-button-hover-color, var(--select-accent-hover));
2668
+ transform: var(--select-clear-button-hover-transform);
2669
+ }
2670
+
2671
+ .clear-control-button:active {
2672
+ transform: var(--select-clear-button-active-transform);
2312
2673
  }
2313
2674
 
2314
2675
  .clear-control-button:focus-visible {
2315
- outline: var(--select-clear-button-focus-outline, 2px solid rgba(102, 126, 234, 0.55));
2316
- outline-offset: 1px;
2676
+ outline: var(--select-clear-button-focus-outline, 2px solid var(--select-border-focus));
2677
+ outline-offset: var(--select-clear-button-focus-offset);
2317
2678
  }
2318
2679
 
2319
2680
  .clear-control-button[hidden] {
@@ -2321,260 +2682,332 @@
2321
2682
  }
2322
2683
 
2323
2684
  .clear-control-icon {
2324
- font-size: var(--select-clear-icon-size, 16px);
2325
- line-height: 1;
2326
- font-weight: var(--select-clear-icon-weight, 500);
2685
+ width: var(--select-clear-icon-size);
2686
+ height: var(--select-clear-icon-size);
2327
2687
  pointer-events: none;
2328
2688
  }
2329
2689
 
2330
- .dropdown-arrow-container:hover {
2331
- background-color: var(--select-arrow-hover-bg, rgba(102, 126, 234, 0.08));
2690
+ .clear-control-icon svg {
2691
+ width: 100%;
2692
+ height: 100%;
2332
2693
  }
2333
2694
 
2334
- .dropdown-arrow:hover {
2335
- /* legacy alias --select-arrow-hover for icon color */
2336
- color: var(--select-arrow-hover, var(--select-arrow-hover-color, #667eea));
2695
+ .dropdown-arrow-container:hover {
2696
+ background-color: var(--select-arrow-hover-bg, rgba(15, 52, 96, 0.05));
2337
2697
  }
2338
2698
 
2339
2699
  .dropdown-arrow {
2340
- width: var(--select-arrow-width, var(--select-arrow-size, 16px));
2341
- height: var(--select-arrow-height, var(--select-arrow-size, 16px));
2342
- color: var(--select-arrow-color, #667eea);
2343
- transition: transform 0.2s ease, color 0.2s ease;
2344
- transform: translateY(0);
2700
+ width: var(--select-arrow-size, 18px);
2701
+ height: var(--select-arrow-size, 18px);
2702
+ color: var(--select-arrow-color, var(--select-text-muted));
2703
+ transition:
2704
+ transform var(--select-transition-smooth),
2705
+ color var(--select-transition-fast);
2706
+ transform-origin: center;
2345
2707
  }
2346
2708
 
2347
2709
  .dropdown-arrow path {
2348
- stroke-width: var(--select-arrow-stroke-width, 2);
2710
+ stroke-width: var(--select-arrow-stroke-width, 1.5);
2349
2711
  }
2350
2712
 
2351
2713
  .dropdown-arrow-container:hover .dropdown-arrow {
2352
- color: var(--select-arrow-hover-color, #667eea);
2714
+ color: var(--select-arrow-hover-color, var(--select-accent));
2353
2715
  }
2354
2716
 
2355
2717
  .dropdown-arrow.open {
2356
- transform: rotate(180deg);
2718
+ transform: var(--select-arrow-open-transform);
2357
2719
  }
2358
2720
 
2359
- .select-input {
2360
- flex: 1;
2361
- width: var(--select-input-width, auto);
2362
- min-width: var(--select-input-min-width, 120px);
2363
- padding: var(--select-input-field-padding, 4px);
2364
- border: none;
2365
- font-size: var(--select-input-font-size, 14px);
2721
+ /* ─────────────────────────────────────────────────────────────────────────
2722
+ Input Field — Clean text entry
2723
+ ───────────────────────────────────────────────────────────────────────── */
2724
+
2725
+ .input-container > .select-input,
2726
+ input.select-input,
2727
+ .select-input[type="text"] {
2728
+ display: block;
2729
+ flex: 1 1 auto;
2730
+ width: var(--select-input-width, 100%);
2731
+ max-width: 100%;
2732
+ min-width: var(--select-input-min-width, 0);
2733
+ min-inline-size: 0;
2734
+ min-height: 0;
2735
+ padding: var(--select-input-field-padding, 0) !important;
2736
+ margin: 0 !important;
2737
+ border: 0 !important;
2738
+ font-size: var(--select-input-font-size, 15px);
2366
2739
  line-height: var(--select-input-line-height, 1.5);
2367
- color: var(--select-input-color, var(--select-text-color, #1f2937));
2368
- background: transparent;
2740
+ color: var(--select-input-color, var(--select-text));
2741
+ background: transparent !important;
2369
2742
  box-sizing: border-box;
2370
- outline: none;
2371
- font-family: var(--select-font-family, inherit);
2372
- }
2373
-
2374
- .select-input::placeholder {
2375
- color: var(--select-input-placeholder-color, var(--select-placeholder-color, #9ca3af));
2376
- }
2377
-
2378
- .selection-badge {
2379
- display: inline-flex;
2380
- align-items: center;
2381
- gap: var(--select-badge-gap, 4px);
2382
- padding: var(--select-badge-padding, 4px 8px);
2383
- margin: var(--select-badge-margin, 2px);
2384
- background: var(--select-badge-bg, #667eea);
2385
- color: var(--select-badge-color, white);
2386
- border-radius: var(--select-badge-border-radius, 4px);
2387
- font-size: var(--select-badge-font-size, 13px);
2388
- line-height: 1;
2389
- max-width: var(--select-badge-max-width, 100%);
2390
- white-space: nowrap;
2743
+ outline: none !important;
2744
+ font-weight: var(--select-input-font-weight);
2745
+ letter-spacing: var(--select-input-letter-spacing);
2746
+ align-self: center;
2747
+ appearance: none !important;
2748
+ -webkit-appearance: none !important;
2749
+ box-shadow: none !important;
2750
+ border-radius: 0 !important;
2391
2751
  overflow: hidden;
2392
2752
  text-overflow: ellipsis;
2753
+ white-space: nowrap;
2393
2754
  }
2394
2755
 
2395
- .badge-remove {
2396
- display: inline-flex;
2397
- align-items: center;
2398
- justify-content: center;
2399
- width: var(--select-badge-remove-size, 16px);
2400
- height: var(--select-badge-remove-size, 16px);
2401
- padding: 0;
2402
- margin-left: 4px;
2403
- background: var(--select-badge-remove-bg, rgba(255, 255, 255, 0.3));
2404
- border: none;
2405
- border-radius: 50%;
2406
- color: var(--select-badge-remove-color, white);
2407
- font-size: var(--select-badge-remove-font-size, 16px);
2408
- line-height: 1;
2409
- cursor: pointer;
2410
- transition: background 0.2s;
2756
+ .select-input::placeholder {
2757
+ color: var(--select-input-placeholder-color, var(--select-text-placeholder));
2758
+ font-weight: 400;
2411
2759
  }
2412
-
2413
- .badge-remove:hover {
2414
- background: var(--select-badge-remove-hover-bg, rgba(255, 255, 255, 0.5));
2760
+
2761
+ .input-container.input-container--multi > .select-input,
2762
+ .input-container.input-container--multi > input.select-input,
2763
+ .input-container.input-container--multi > .select-input[type="text"] {
2764
+ width: auto;
2765
+ min-width: var(--select-multi-input-min-width, 96px);
2766
+ flex: 1 0 var(--select-multi-input-min-width, 96px);
2415
2767
  }
2416
2768
 
2417
- .badge-remove:focus-visible {
2418
- outline: 2px solid var(--select-badge-remove-focus-outline, rgba(255, 255, 255, 0.8));
2419
- outline-offset: 2px;
2769
+ .input-container.input-container--single > .select-input,
2770
+ .input-container.input-container--single > input.select-input,
2771
+ .input-container.input-container--single > .select-input[type="text"] {
2772
+ width: var(--select-input-width, 100%);
2773
+ min-width: 0;
2774
+ flex: 1 1 auto;
2420
2775
  }
2421
2776
 
2422
2777
  .select-input:disabled {
2423
2778
  background-color: var(--select-disabled-bg, #f5f5f5);
2424
2779
  cursor: not-allowed;
2780
+ opacity: var(--select-input-disabled-opacity);
2425
2781
  }
2426
2782
 
2783
+ /* ─────────────────────────────────────────────────────────────────────────
2784
+ Dropdown Panel — Elegant floating container
2785
+ ───────────────────────────────────────────────────────────────────────── */
2786
+
2427
2787
  .select-dropdown {
2428
2788
  position: absolute;
2429
- scroll-behavior: smooth;
2430
- top: 100%;
2789
+ scroll-behavior: var(--select-dropdown-scroll-behavior);
2790
+ top: var(--select-dropdown-top);
2431
2791
  left: 0;
2432
2792
  right: 0;
2433
- margin-top: var(--select-dropdown-margin-top, 4px);
2434
- max-height: var(--select-dropdown-max-height, 300px);
2793
+ max-height: var(--select-dropdown-max-height, 320px);
2435
2794
  overflow: hidden;
2436
- background: var(--select-dropdown-bg, var(--select-bg, white));
2437
- border: 1px solid var(--select-dropdown-border, #ccc);
2438
- border-radius: var(--select-dropdown-border-radius, 4px);
2439
- box-shadow: var(--select-dropdown-shadow, 0 4px 6px rgba(0,0,0,0.1));
2795
+ background: var(--select-dropdown-bg, var(--select-surface));
2796
+ border: 1px solid var(--select-dropdown-border, var(--select-border));
2797
+ border-radius: var(--select-dropdown-border-radius, var(--select-radius-lg));
2798
+ box-shadow: var(--select-dropdown-shadow, var(--select-shadow-lg));
2440
2799
  z-index: var(--select-dropdown-z-index, 1000);
2800
+ transform-origin: var(--select-dropdown-transform-origin);
2801
+ animation: var(--select-dropdown-animation);
2802
+ }
2803
+
2804
+ @keyframes dropdownEnter {
2805
+ 0% {
2806
+ opacity: var(--select-dropdown-enter-from-opacity);
2807
+ transform: var(--select-dropdown-enter-from-transform);
2808
+ }
2809
+ 100% {
2810
+ opacity: var(--select-dropdown-enter-to-opacity);
2811
+ transform: var(--select-dropdown-enter-to-transform);
2812
+ }
2441
2813
  }
2442
2814
 
2443
2815
  .options-container {
2444
2816
  position: relative;
2445
- max-height: var(--select-options-max-height, 300px);
2817
+ max-height: var(--select-options-max-height, 320px);
2446
2818
  overflow: auto;
2447
- transition: opacity 0.2s ease-in-out;
2448
- background: var(--select-options-bg, var(--select-dropdown-bg, var(--select-bg, white)));
2819
+ padding: var(--select-options-padding, 6px);
2820
+ background: var(--select-options-bg, var(--select-surface));
2821
+
2822
+ /* Custom scrollbar styling */
2823
+ scrollbar-width: thin;
2824
+ scrollbar-color: var(--select-border) transparent;
2825
+ }
2826
+
2827
+ .options-container::-webkit-scrollbar {
2828
+ width: var(--select-scrollbar-width);
2449
2829
  }
2830
+
2831
+ .options-container::-webkit-scrollbar-track {
2832
+ background: transparent;
2833
+ }
2834
+
2835
+ .options-container::-webkit-scrollbar-thumb {
2836
+ background: var(--select-border);
2837
+ border-radius: var(--select-scrollbar-thumb-radius);
2838
+ }
2839
+
2840
+ .options-container::-webkit-scrollbar-thumb:hover {
2841
+ background: var(--select-text-muted);
2842
+ }
2843
+
2844
+ /* ─────────────────────────────────────────────────────────────────────────
2845
+ Group Headers — Refined section dividers
2846
+ ───────────────────────────────────────────────────────────────────────── */
2450
2847
 
2451
2848
  .group-header {
2452
- padding: var(--select-group-header-padding, 8px 12px);
2849
+ padding: var(--select-group-header-padding, 10px 12px 6px);
2453
2850
  font-weight: var(--select-group-header-weight, 600);
2454
- color: var(--select-group-header-color, #6b7280);
2455
- background-color: var(--select-group-header-bg, #f3f4f6);
2851
+ color: var(--select-group-header-color, var(--select-text-muted));
2852
+ background-color: var(--select-group-header-bg, var(--select-surface));
2456
2853
  text-align: var(--select-group-header-text-align, left);
2457
- font-size: var(--select-group-header-font-size, 12px);
2854
+ font-size: var(--select-group-header-font-size, 11px);
2458
2855
  text-transform: var(--select-group-header-text-transform, uppercase);
2459
- letter-spacing: var(--select-group-header-letter-spacing, 0.05em);
2856
+ letter-spacing: var(--select-group-header-letter-spacing, 0.08em);
2460
2857
  position: sticky;
2461
2858
  top: 0;
2462
2859
  z-index: 1;
2463
- border-bottom: var(--select-group-header-border-bottom, 1px solid #e5e7eb);
2860
+ border-bottom: var(--select-group-header-border-bottom, none);
2861
+ }
2862
+
2863
+ .group-header:not(:first-child) {
2864
+ margin-top: var(--select-group-header-separator-margin-top);
2865
+ padding-top: var(--select-group-header-separator-padding-top);
2866
+ border-top: var(--select-group-header-separator-border-top);
2464
2867
  }
2465
2868
 
2869
+ /* ─────────────────────────────────────────────────────────────────────────
2870
+ Options — Elegant hover and selection states
2871
+ ───────────────────────────────────────────────────────────────────────── */
2872
+
2466
2873
  .option {
2467
- padding: var(--select-option-padding, 8px 12px);
2874
+ position: relative;
2875
+ padding: var(--select-option-padding, 10px 14px);
2468
2876
  cursor: pointer;
2469
- color: var(--select-option-color, var(--select-text-color, #1f2937));
2470
- background: var(--select-option-bg, var(--select-dropdown-bg, var(--select-bg, white)));
2471
- transition: var(--select-option-transition, background-color 0.15s ease);
2877
+ color: var(--select-option-color, var(--select-text));
2878
+ background: var(--select-option-bg, transparent);
2879
+ transition:
2880
+ background var(--select-transition-fast),
2881
+ color var(--select-transition-fast),
2882
+ transform var(--select-transition-fast),
2883
+ box-shadow var(--select-transition-fast);
2472
2884
  user-select: none;
2473
2885
  font-size: var(--select-option-font-size, 14px);
2886
+ font-weight: var(--select-option-font-weight);
2474
2887
  line-height: var(--select-option-line-height, 1.5);
2475
- border: var(--select-option-border, none);
2476
- border-bottom: var(--select-option-border-bottom, none);
2477
- border-radius: var(--select-option-border-radius, 0);
2478
- box-shadow: var(--select-option-shadow, none);
2479
- transform: var(--select-option-transform, none);
2888
+ border-radius: var(--select-option-border-radius, var(--select-radius-sm));
2889
+ margin: var(--select-option-margin);
2480
2890
  }
2481
2891
 
2482
2892
  .option:hover {
2483
- background: var(--select-option-hover-bg, #f3f4f6);
2484
- color: var(--select-option-hover-color, #1f2937);
2893
+ background: var(--select-option-hover-bg, var(--select-surface-elevated));
2894
+ color: var(--select-option-hover-color, var(--select-text));
2895
+ transform: var(--select-option-hover-transform);
2485
2896
  }
2486
2897
 
2487
2898
  .option.selected {
2488
- background: var(--select-option-selected-bg, #e0e7ff);
2489
- color: var(--select-option-selected-color, #4338ca);
2490
- font-weight: var(--select-option-selected-weight, 500);
2491
- border: var(--select-option-selected-border, var(--select-option-border, none));
2492
- border-bottom: var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none));
2493
- border-radius: var(--select-option-selected-border-radius, var(--select-option-border-radius, 0));
2494
- box-shadow: var(--select-option-selected-shadow, var(--select-option-shadow, none));
2495
- transform: var(--select-option-selected-transform, var(--select-option-transform, none));
2899
+ background: var(--select-option-selected-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.08) 0%, rgba(15, 52, 96, 0.04) 100%));
2900
+ color: var(--select-option-selected-color, var(--select-accent));
2901
+ font-weight: var(--select-option-selected-weight, 550);
2902
+ }
2903
+
2904
+ .option.selected::before {
2905
+ content: '';
2906
+ position: absolute;
2907
+ left: var(--select-option-selected-indicator-left);
2908
+ top: var(--select-option-selected-indicator-top);
2909
+ transform: var(--select-option-selected-indicator-transform);
2910
+ width: var(--select-option-selected-indicator-width);
2911
+ height: var(--select-option-selected-indicator-height);
2912
+ background: var(--select-option-selected-indicator-bg);
2913
+ border-radius: var(--select-option-selected-indicator-radius);
2914
+ animation: var(--select-option-selected-indicator-animation);
2915
+ }
2916
+
2917
+ @keyframes selectedIndicator {
2918
+ 0% {
2919
+ height: var(--select-option-selected-indicator-from-height);
2920
+ opacity: var(--select-option-selected-indicator-from-opacity);
2921
+ }
2922
+ 100% {
2923
+ height: var(--select-option-selected-indicator-to-height);
2924
+ opacity: var(--select-option-selected-indicator-to-opacity);
2925
+ }
2496
2926
  }
2497
2927
 
2498
2928
  .option.selected:hover {
2499
- background: var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff));
2500
- color: var(--select-option-selected-hover-color, var(--select-option-selected-color, #4338ca));
2501
- border: var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)));
2502
- border-bottom: var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2503
- box-shadow: var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2504
- transform: var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2929
+ background: var(--select-option-selected-hover-bg, linear-gradient(135deg, rgba(15, 52, 96, 0.12) 0%, rgba(15, 52, 96, 0.06) 100%));
2505
2930
  }
2506
2931
 
2507
2932
  .option.active:not(.selected) {
2508
- background: var(--select-option-active-bg, #f3f4f6);
2509
- color: var(--select-option-active-color, #1f2937);
2510
- outline: var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45));
2511
- outline-offset: -2px;
2933
+ background: var(--select-option-active-bg, var(--select-surface-elevated));
2934
+ outline: var(--select-option-active-outline, 2px solid rgba(15, 52, 96, 0.3));
2935
+ outline-offset: var(--select-option-active-outline-offset);
2512
2936
  }
2513
2937
 
2514
2938
  .option.selected.active {
2515
- background: var(--select-option-selected-active-bg, var(--select-option-selected-bg, #e0e7ff));
2516
- color: var(--select-option-selected-active-color, var(--select-option-selected-color, #4338ca));
2517
- border: var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)));
2518
- border-bottom: var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2519
- box-shadow: var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2520
- transform: var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2521
- outline: var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(99, 102, 241, 0.45)));
2522
- outline-offset: -2px;
2939
+ outline: var(--select-option-selected-active-outline, 2px solid rgba(15, 52, 96, 0.4));
2940
+ outline-offset: var(--select-option-selected-active-outline-offset);
2523
2941
  }
2524
2942
 
2525
2943
  .option:active:not(.selected) {
2526
- background: var(--select-option-pressed-bg, #e5e7eb);
2944
+ background: var(--select-option-pressed-bg, rgba(15, 52, 96, 0.08));
2945
+ transform: var(--select-option-pressed-transform);
2527
2946
  }
2528
2947
 
2529
2948
  .option.selected:active {
2530
- background: var(--select-option-selected-pressed-bg, var(--select-option-selected-hover-bg, var(--select-option-selected-bg, #e0e7ff)));
2949
+ transform: var(--select-option-selected-pressed-transform);
2531
2950
  }
2532
2951
 
2952
+ /* ─────────────────────────────────────────────────────────────────────────
2953
+ Load More & Busy States — Refined feedback indicators
2954
+ ───────────────────────────────────────────────────────────────────────── */
2955
+
2533
2956
  .load-more-container {
2534
2957
  padding: var(--select-load-more-padding, 12px);
2535
2958
  text-align: center;
2536
- border-top: var(--select-divider-border, 1px solid #e0e0e0);
2537
- background: var(--select-load-more-bg, white);
2538
2959
  }
2539
2960
 
2540
2961
  .load-more-button {
2541
- padding: var(--select-button-padding, 8px 16px);
2542
- border: var(--select-button-border, 1px solid #1976d2);
2543
- background: var(--select-button-bg, white);
2544
- color: var(--select-button-color, #1976d2);
2545
- border-radius: var(--select-button-border-radius, 4px);
2962
+ padding: var(--select-button-padding, 10px 20px);
2963
+ border: var(--select-button-border, 1.5px solid var(--select-border));
2964
+ background: var(--select-button-bg, transparent);
2965
+ color: var(--select-button-color, var(--select-accent));
2966
+ border-radius: var(--select-button-border-radius, var(--select-radius-md));
2546
2967
  cursor: pointer;
2547
- font-size: var(--select-button-font-size, 14px);
2548
- font-family: var(--select-font-family, inherit);
2549
- transition: all 0.2s ease;
2968
+ font-size: var(--select-button-font-size, 13px);
2969
+ font-weight: var(--select-button-font-weight);
2970
+ letter-spacing: var(--select-button-letter-spacing);
2971
+ transition:
2972
+ background var(--select-transition-fast),
2973
+ border-color var(--select-transition-fast),
2974
+ color var(--select-transition-fast),
2975
+ transform var(--select-transition-bounce);
2550
2976
  }
2551
2977
 
2552
2978
  .load-more-button:hover {
2553
- background: var(--select-button-hover-bg, #1976d2);
2979
+ background: var(--select-button-hover-bg, var(--select-accent));
2980
+ border-color: var(--select-accent);
2554
2981
  color: var(--select-button-hover-color, white);
2982
+ transform: var(--select-button-hover-transform);
2983
+ }
2984
+
2985
+ .load-more-button:active {
2986
+ transform: var(--select-button-active-transform);
2555
2987
  }
2556
2988
 
2557
2989
  .load-more-button:disabled {
2558
2990
  opacity: var(--select-button-disabled-opacity, 0.5);
2559
2991
  cursor: not-allowed;
2992
+ transform: none;
2560
2993
  }
2561
2994
 
2562
2995
  .busy-bucket {
2563
- padding: var(--select-busy-padding, 16px);
2996
+ padding: var(--select-busy-padding, 20px);
2564
2997
  text-align: center;
2565
- color: var(--select-busy-color, #666);
2566
- background: var(--select-busy-bg, white);
2567
- font-size: var(--select-busy-font-size, 14px);
2998
+ color: var(--select-busy-color, var(--select-text-muted));
2999
+ background: var(--select-busy-bg, transparent);
3000
+ font-size: var(--select-busy-font-size, 13px);
2568
3001
  }
2569
3002
 
2570
3003
  .spinner {
2571
3004
  display: inline-block;
2572
- width: var(--select-spinner-size, 20px);
2573
- height: var(--select-spinner-size, 20px);
2574
- border: var(--select-spinner-border, 2px solid #ccc);
2575
- border-top-color: var(--select-spinner-active-color, #1976d2);
3005
+ width: var(--select-spinner-size, 22px);
3006
+ height: var(--select-spinner-size, 22px);
3007
+ border: var(--select-spinner-border, 2px solid var(--select-border));
3008
+ border-top-color: var(--select-spinner-active-color, var(--select-accent));
2576
3009
  border-radius: 50%;
2577
- animation: spin 0.6s linear infinite;
3010
+ animation: var(--select-spinner-animation);
2578
3011
  }
2579
3012
 
2580
3013
  @keyframes spin {
@@ -2582,61 +3015,97 @@
2582
3015
  }
2583
3016
 
2584
3017
  .empty-state {
2585
- padding: var(--select-empty-padding, 24px);
3018
+ padding: var(--select-empty-padding, 32px 24px);
2586
3019
  text-align: center;
2587
- color: var(--select-empty-color, #6b7280);
3020
+ color: var(--select-empty-color, var(--select-text-muted));
2588
3021
  font-size: var(--select-empty-font-size, 14px);
2589
- background: var(--select-empty-bg, white);
3022
+ background: var(--select-empty-bg, transparent);
2590
3023
  display: flex;
2591
3024
  flex-direction: column;
2592
3025
  align-items: center;
2593
3026
  justify-content: center;
2594
- gap: 6px;
2595
- min-height: var(--select-empty-min-height, 72px);
3027
+ gap: var(--select-empty-gap);
2596
3028
  }
2597
3029
 
2598
3030
  .searching-state {
2599
- padding: var(--select-searching-padding, 24px);
3031
+ padding: var(--select-searching-padding, 32px 24px);
2600
3032
  text-align: center;
2601
- color: var(--select-searching-color, #667eea);
3033
+ color: var(--select-searching-color, var(--select-accent));
2602
3034
  font-size: var(--select-searching-font-size, 14px);
2603
- font-style: italic;
2604
- background: var(--select-searching-bg, white);
2605
- animation: pulse 1.5s ease-in-out infinite;
3035
+ background: var(--select-searching-bg, transparent);
2606
3036
  display: flex;
2607
3037
  flex-direction: column;
2608
3038
  align-items: center;
2609
3039
  justify-content: center;
2610
- gap: 6px;
2611
- min-height: var(--select-searching-min-height, 72px);
3040
+ gap: var(--select-searching-gap);
2612
3041
  }
2613
3042
 
2614
- @keyframes pulse {
2615
- 0%, 100% { opacity: 1; }
2616
- 50% { opacity: 0.5; }
3043
+ .searching-state::before {
3044
+ content: '';
3045
+ width: var(--select-searching-spinner-size);
3046
+ height: var(--select-searching-spinner-size);
3047
+ border: var(--select-searching-spinner-border);
3048
+ border-top-color: var(--select-searching-spinner-active-color);
3049
+ border-radius: 50%;
3050
+ animation: var(--select-searching-spinner-animation);
2617
3051
  }
2618
3052
 
2619
- /* Error states */
3053
+ /* ─────────────────────────────────────────────────────────────────────────
3054
+ Error States — Clear visual feedback
3055
+ ───────────────────────────────────────────────────────────────────────── */
3056
+
2620
3057
  .select-input[aria-invalid="true"] {
2621
- border-color: var(--select-error-border, #dc2626);
3058
+ border-color: var(--select-error-border, #e94560);
2622
3059
  }
2623
3060
 
2624
3061
  .select-input[aria-invalid="true"]:focus {
2625
- border-color: var(--select-error-border, #dc2626);
2626
- box-shadow: 0 0 0 2px var(--select-error-shadow, rgba(220, 38, 38, 0.1));
2627
- outline-color: var(--select-error-border, #dc2626);
3062
+ border-color: var(--select-error-border, #e94560);
3063
+ box-shadow: 0 0 0 3px var(--select-error-shadow, rgba(233, 69, 96, 0.15));
2628
3064
  }
2629
3065
 
2630
- /* Accessibility: Reduced motion */
3066
+ /* ──────────────────────────────────────────────────��──────────────────────
3067
+ Accessibility — Reduced motion & High contrast
3068
+ ───────────────────────────────────────────────────────────────────────── */
3069
+
2631
3070
  @media (prefers-reduced-motion: reduce) {
2632
- * {
2633
- animation-duration: 0.01ms !important;
2634
- animation-iteration-count: 1 !important;
2635
- transition-duration: 0.01ms !important;
3071
+ *,
3072
+ *::before,
3073
+ *::after {
3074
+ animation-duration: var(--select-reduced-motion-duration) !important;
3075
+ animation-iteration-count: var(--select-reduced-motion-iteration-count) !important;
3076
+ transition-duration: var(--select-reduced-motion-duration) !important;
3077
+ }
3078
+
3079
+ .dropdown-arrow.open {
3080
+ transform: var(--select-arrow-open-transform);
3081
+ }
3082
+ }
3083
+
3084
+ @media (prefers-contrast: high) {
3085
+ .select-input:focus {
3086
+ outline-width: var(--select-high-contrast-focus-outline-width);
3087
+ outline-color: var(--select-high-contrast-focus-outline-color);
3088
+ }
3089
+
3090
+ .input-container {
3091
+ border-width: var(--select-high-contrast-border-width);
3092
+ }
3093
+
3094
+ .option.selected::before {
3095
+ width: var(--select-high-contrast-indicator-width);
2636
3096
  }
2637
3097
  }
2638
3098
 
2639
- /* Dark mode - Opt-in via class, data attribute, or ancestor context */
3099
+ /* Touch targets (WCAG 2.5.5) */
3100
+ .load-more-button,
3101
+ select-option {
3102
+ min-height: var(--select-touch-target-min-height);
3103
+ }
3104
+
3105
+ /* ─────────────────────────────────────────────────────────────────────────
3106
+ Dark Mode — Elegant dark theme
3107
+ ───────────────────────────────────────────────────────────────────────── */
3108
+
2640
3109
  :host(.dark-mode),
2641
3110
  :host([dark-mode]),
2642
3111
  :host([darkmode]),
@@ -2648,139 +3117,129 @@
2648
3117
  :host-context([darkmode]),
2649
3118
  :host-context([data-theme="dark"]),
2650
3119
  :host-context([theme="dark"]) {
2651
- /* map dark tokens to base option tokens so nested <select-option>
2652
- components also pick up dark mode via inherited CSS variables */
2653
- --select-option-bg: var(--select-dark-option-bg, #1f2937);
2654
- --select-option-color: var(--select-dark-option-color, #f9fafb);
2655
- --select-option-hover-bg: var(--select-dark-option-hover-bg, #374151);
2656
- --select-option-hover-color: var(--select-dark-option-hover-color, #f9fafb);
2657
- --select-option-selected-bg: var(--select-dark-option-selected-bg, #3730a3);
2658
- --select-option-selected-color: var(--select-dark-option-selected-text, #e0e7ff);
2659
-
2660
- .input-container {
2661
- background: var(--select-dark-bg, #1f2937);
2662
- border-color: var(--select-dark-border, #4b5563);
2663
- }
2664
-
2665
- .select-input {
2666
- color: var(--select-dark-text, #f9fafb);
2667
- }
2668
-
2669
- .select-input::placeholder {
2670
- color: var(--select-dark-placeholder, #6b7280);
2671
- }
2672
-
2673
- .select-dropdown {
2674
- background: var(--select-dark-dropdown-bg, #1f2937);
2675
- border-color: var(--select-dark-dropdown-border, #4b5563);
2676
- }
2677
-
2678
- .options-container {
2679
- background: var(--select-dark-options-bg, #1f2937);
2680
- }
2681
-
2682
- .option {
2683
- color: var(--select-dark-option-color, #f9fafb);
2684
- background: var(--select-dark-option-bg, #1f2937);
2685
- }
3120
+ --select-primary: #e5e5e5;
3121
+ --select-primary-light: #2a2a3e;
3122
+ --select-accent: #6366f1;
3123
+ --select-accent-hover: #f43f5e;
3124
+ --select-surface: #1a1a2e;
3125
+ --select-surface-elevated: #252540;
3126
+ --select-border: #3f3f5a;
3127
+ --select-border-focus: #6366f1;
3128
+ --select-text: #f5f5f5;
3129
+ --select-text-muted: #9ca3af;
3130
+ --select-text-placeholder: #6b7280;
3131
+ --select-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.2);
3132
+ --select-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
3133
+ --select-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4);
3134
+ --select-shadow-focus: 0 0 0 3px rgba(99, 102, 241, 0.25);
2686
3135
 
2687
- .option:hover {
2688
- background: var(--select-dark-option-hover-bg, #374151);
2689
- color: var(--select-dark-option-hover-color, #f9fafb);
2690
- }
2691
-
2692
- .option.selected {
2693
- background: var(--select-dark-option-selected-bg, #3730a3);
2694
- color: var(--select-dark-option-selected-text, #e0e7ff);
2695
- border: var(--select-dark-option-selected-border, var(--select-option-selected-border, var(--select-option-border, none)));
2696
- border-bottom: var(--select-dark-option-selected-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)));
2697
- box-shadow: var(--select-dark-option-selected-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)));
2698
- transform: var(--select-dark-option-selected-transform, var(--select-option-selected-transform, var(--select-option-transform, none)));
2699
- }
2700
-
2701
- .option.selected:hover {
2702
- background: var(--select-dark-option-selected-hover-bg, var(--select-dark-option-selected-bg, #3730a3));
2703
- color: var(--select-dark-option-selected-hover-color, var(--select-dark-option-selected-text, #e0e7ff));
2704
- border: var(--select-dark-option-selected-hover-border, var(--select-dark-option-selected-border, var(--select-option-selected-hover-border, var(--select-option-selected-border, var(--select-option-border, none)))));
2705
- border-bottom: var(--select-dark-option-selected-hover-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-hover-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
2706
- box-shadow: var(--select-dark-option-selected-hover-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-hover-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
2707
- transform: var(--select-dark-option-selected-hover-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-hover-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
2708
- }
2709
-
2710
- .option.active:not(.selected) {
2711
- background-color: var(--select-dark-option-active-bg, #374151);
2712
- color: var(--select-dark-option-active-color, #f9fafb);
2713
- outline: var(--select-dark-option-active-outline, 2px solid rgba(129, 140, 248, 0.55));
2714
- }
2715
-
2716
- /* Group header in dark mode */
2717
- .group-header {
2718
- color: var(--select-dark-group-header-color, var(--select-group-header-color, #6b7280));
2719
- background-color: var(--select-dark-group-header-bg, var(--select-group-header-bg, #374151));
2720
- }
3136
+ --select-option-bg: transparent;
3137
+ --select-option-color: var(--select-text);
3138
+ --select-option-hover-bg: var(--select-surface-elevated);
3139
+ --select-option-hover-color: var(--select-text);
3140
+ --select-option-selected-bg: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(99, 102, 241, 0.08) 100%);
3141
+ --select-option-selected-color: #a5b4fc;
3142
+ }
2721
3143
 
2722
- .option.selected.active {
2723
- background-color: var(--select-dark-option-selected-active-bg, var(--select-dark-option-selected-bg, #3730a3));
2724
- color: var(--select-dark-option-selected-active-color, var(--select-dark-option-selected-text, #e0e7ff));
2725
- border: var(--select-dark-option-selected-active-border, var(--select-dark-option-selected-border, var(--select-option-selected-active-border, var(--select-option-selected-border, var(--select-option-border, none)))));
2726
- border-bottom: var(--select-dark-option-selected-active-border-bottom, var(--select-dark-option-selected-border-bottom, var(--select-option-selected-active-border-bottom, var(--select-option-selected-border-bottom, var(--select-option-border-bottom, none)))));
2727
- box-shadow: var(--select-dark-option-selected-active-shadow, var(--select-dark-option-selected-shadow, var(--select-option-selected-active-shadow, var(--select-option-selected-shadow, var(--select-option-shadow, none)))));
2728
- transform: var(--select-dark-option-selected-active-transform, var(--select-dark-option-selected-transform, var(--select-option-selected-active-transform, var(--select-option-selected-transform, var(--select-option-transform, none)))));
2729
- outline: var(--select-dark-option-selected-active-outline, var(--select-dark-option-active-outline, var(--select-option-selected-active-outline, var(--select-option-active-outline, 2px solid rgba(129, 140, 248, 0.55)))));
2730
- outline-offset: -2px;
2731
- }
3144
+ :host(.dark-mode) .input-container,
3145
+ :host([dark-mode]) .input-container,
3146
+ :host([darkmode]) .input-container,
3147
+ :host([data-theme="dark"]) .input-container,
3148
+ :host([theme="dark"]) .input-container,
3149
+ :host-context(.dark-mode) .input-container,
3150
+ :host-context(.dark) .input-container,
3151
+ :host-context([dark-mode]) .input-container,
3152
+ :host-context([darkmode]) .input-container,
3153
+ :host-context([data-theme="dark"]) .input-container,
3154
+ :host-context([theme="dark"]) .input-container {
3155
+ background: var(--select-surface);
3156
+ border-color: var(--select-border);
3157
+ }
2732
3158
 
2733
- .selection-badge {
2734
- background: var(--select-dark-badge-bg, #4f46e5);
2735
- color: var(--select-dark-badge-color, #eef2ff);
2736
- }
3159
+ :host(.dark-mode) .input-container::after,
3160
+ :host([dark-mode]) .input-container::after,
3161
+ :host([darkmode]) .input-container::after,
3162
+ :host([data-theme="dark"]) .input-container::after,
3163
+ :host([theme="dark"]) .input-container::after,
3164
+ :host-context(.dark-mode) .input-container::after,
3165
+ :host-context(.dark) .input-container::after,
3166
+ :host-context([dark-mode]) .input-container::after,
3167
+ :host-context([darkmode]) .input-container::after,
3168
+ :host-context([data-theme="dark"]) .input-container::after,
3169
+ :host-context([theme="dark"]) .input-container::after {
3170
+ background: var(--select-separator-dark-bg);
3171
+ }
2737
3172
 
2738
- .badge-remove {
2739
- background: var(--select-dark-badge-remove-bg, rgba(255, 255, 255, 0.15));
2740
- color: var(--select-dark-badge-remove-color, #e5e7eb);
2741
- }
3173
+ :host(.dark-mode) .select-dropdown,
3174
+ :host([dark-mode]) .select-dropdown,
3175
+ :host([darkmode]) .select-dropdown,
3176
+ :host([data-theme="dark"]) .select-dropdown,
3177
+ :host([theme="dark"]) .select-dropdown,
3178
+ :host-context(.dark-mode) .select-dropdown,
3179
+ :host-context(.dark) .select-dropdown,
3180
+ :host-context([dark-mode]) .select-dropdown,
3181
+ :host-context([darkmode]) .select-dropdown,
3182
+ :host-context([data-theme="dark"]) .select-dropdown,
3183
+ :host-context([theme="dark"]) .select-dropdown {
3184
+ background: var(--select-surface);
3185
+ border-color: var(--select-border);
3186
+ }
2742
3187
 
2743
- .badge-remove:hover {
2744
- background: var(--select-dark-badge-remove-hover-bg, rgba(255, 255, 255, 0.3));
2745
- }
2746
-
2747
- .busy-bucket,
2748
- .empty-state {
2749
- color: var(--select-dark-busy-color, #9ca3af);
2750
- background: var(--select-dark-empty-bg, #111827);
2751
- }
3188
+ :host(.dark-mode) .options-container,
3189
+ :host([dark-mode]) .options-container,
3190
+ :host([darkmode]) .options-container,
3191
+ :host([data-theme="dark"]) .options-container,
3192
+ :host([theme="dark"]) .options-container,
3193
+ :host-context(.dark-mode) .options-container,
3194
+ :host-context(.dark) .options-container,
3195
+ :host-context([dark-mode]) .options-container,
3196
+ :host-context([darkmode]) .options-container,
3197
+ :host-context([data-theme="dark"]) .options-container,
3198
+ :host-context([theme="dark"]) .options-container {
3199
+ background: var(--select-surface);
3200
+ scrollbar-color: var(--select-border) transparent;
3201
+ }
2752
3202
 
2753
- .searching-state {
2754
- background: var(--select-dark-searching-bg, #111827);
2755
- }
2756
-
2757
- .input-container::after {
2758
- background: linear-gradient(
2759
- to bottom,
2760
- transparent 0%,
2761
- rgba(255, 255, 255, 0.1) 20%,
2762
- rgba(255, 255, 255, 0.1) 80%,
2763
- transparent 100%
2764
- );
2765
- }
3203
+ :host(.dark-mode) .selection-badge,
3204
+ :host([dark-mode]) .selection-badge,
3205
+ :host([darkmode]) .selection-badge,
3206
+ :host([data-theme="dark"]) .selection-badge,
3207
+ :host([theme="dark"]) .selection-badge,
3208
+ :host-context(.dark-mode) .selection-badge,
3209
+ :host-context(.dark) .selection-badge,
3210
+ :host-context([dark-mode]) .selection-badge,
3211
+ :host-context([darkmode]) .selection-badge,
3212
+ :host-context([data-theme="dark"]) .selection-badge,
3213
+ :host-context([theme="dark"]) .selection-badge {
3214
+ background: var(--select-badge-dark-bg);
2766
3215
  }
2767
-
2768
- /* Accessibility: High contrast mode */
2769
- @media (prefers-contrast: high) {
2770
- .select-input:focus {
2771
- outline-width: 3px;
2772
- outline-color: Highlight;
2773
- }
2774
-
2775
- .select-input {
2776
- border-width: 2px;
2777
- }
3216
+
3217
+ :host(.dark-mode) .group-header:not(:first-child),
3218
+ :host([dark-mode]) .group-header:not(:first-child),
3219
+ :host([darkmode]) .group-header:not(:first-child),
3220
+ :host([data-theme="dark"]) .group-header:not(:first-child),
3221
+ :host([theme="dark"]) .group-header:not(:first-child),
3222
+ :host-context(.dark-mode) .group-header:not(:first-child),
3223
+ :host-context(.dark) .group-header:not(:first-child),
3224
+ :host-context([dark-mode]) .group-header:not(:first-child),
3225
+ :host-context([darkmode]) .group-header:not(:first-child),
3226
+ :host-context([data-theme="dark"]) .group-header:not(:first-child),
3227
+ :host-context([theme="dark"]) .group-header:not(:first-child) {
3228
+ border-top-color: var(--select-border);
2778
3229
  }
2779
-
2780
- /* Touch targets (WCAG 2.5.5) */
2781
- .load-more-button,
2782
- select-option {
2783
- min-height: 44px;
3230
+
3231
+ :host(.dark-mode) .option.selected::before,
3232
+ :host([dark-mode]) .option.selected::before,
3233
+ :host([darkmode]) .option.selected::before,
3234
+ :host([data-theme="dark"]) .option.selected::before,
3235
+ :host([theme="dark"]) .option.selected::before,
3236
+ :host-context(.dark-mode) .option.selected::before,
3237
+ :host-context(.dark) .option.selected::before,
3238
+ :host-context([dark-mode]) .option.selected::before,
3239
+ :host-context([darkmode]) .option.selected::before,
3240
+ :host-context([data-theme="dark"]) .option.selected::before,
3241
+ :host-context([theme="dark"]) .option.selected::before {
3242
+ background: var(--select-accent);
2784
3243
  }
2785
3244
  `;
2786
3245
  // Insert as first child to ensure styles are processed first
@@ -2800,10 +3259,10 @@
2800
3259
  // delegate to the existing open/close helpers so we don't accidentally
2801
3260
  // drift out of sync with the logic in those methods (focus, events,
2802
3261
  // scroll-to-selected, etc.)
2803
- if (this._state.isOpen) {
3262
+ if (this._state.isOpen && this._config.selection.toggleOnTriggerClick !== false) {
2804
3263
  this._handleClose();
2805
3264
  }
2806
- else {
3265
+ else if (!this._state.isOpen) {
2807
3266
  this._handleOpen();
2808
3267
  }
2809
3268
  };
@@ -2826,7 +3285,7 @@
2826
3285
  this._inputContainer.addEventListener('pointerdown', (e) => {
2827
3286
  // Prevent propagation to document click listener but do NOT preventDefault.
2828
3287
  // Allow default so browser events (click) on newly opened options still fire.
2829
- e.stopPropagation();
3288
+ // e.stopPropagation(); // BUG: By stopping propagation here, the document click listener doesn't see it, which is fine for not closing it. But be very careful.
2830
3289
  const target = e.target;
2831
3290
  if (!this._config.enabled)
2832
3291
  return;
@@ -2834,21 +3293,23 @@
2834
3293
  return;
2835
3294
  if (target && target.closest('.clear-control-button'))
2836
3295
  return;
3296
+ // If we clicked the container, but not the input itself, we must prevent default
3297
+ // otherwise the browser moves focus from our input to the body, immediately triggering blur.
3298
+ if (target && !target.matches('.select-input')) {
3299
+ e.preventDefault();
3300
+ }
3301
+ const clickedInput = Boolean(target && target.matches('.select-input'));
3302
+ if (this._state.isOpen &&
3303
+ this._config.selection.toggleOnTriggerClick !== false &&
3304
+ !(clickedInput && this._config.searchable)) {
3305
+ this._handleClose();
3306
+ return;
3307
+ }
2837
3308
  const wasClosed = !this._state.isOpen;
2838
3309
  if (wasClosed) {
2839
3310
  this._handleOpen();
2840
3311
  }
2841
- else {
2842
- // Keep open while interacting directly with the input so users can
2843
- // place cursor/type without accidental collapse.
2844
- if (target === this._input) {
2845
- this._input.focus();
2846
- return;
2847
- }
2848
- // clicking other parts of the input container while open toggles close
2849
- this._handleClose();
2850
- }
2851
- // Focus the input (do not prevent default behavior)
3312
+ // Focus the input (do not prevent default behavior for input itself)
2852
3313
  this._input.focus();
2853
3314
  // If we just opened the dropdown, transfer pointer capture to the
2854
3315
  // options container so the subsequent pointerup lands there instead of
@@ -2890,7 +3351,7 @@
2890
3351
  return;
2891
3352
  }
2892
3353
  this._handleClose();
2893
- }, 0);
3354
+ }, 150);
2894
3355
  });
2895
3356
  // Input search
2896
3357
  this._input.addEventListener('input', (e) => {
@@ -2907,7 +3368,7 @@
2907
3368
  this._suppressBlurClose = true;
2908
3369
  setTimeout(() => {
2909
3370
  this._suppressBlurClose = false;
2910
- }, 0);
3371
+ }, 150); // Increased timeout to ensure click finishes before blur checks
2911
3372
  });
2912
3373
  // Delegated click listener for improved event handling (robust across shadow DOM)
2913
3374
  const handleOptionEvent = (e) => {
@@ -2938,9 +3399,6 @@
2938
3399
  }
2939
3400
  };
2940
3401
  this._optionsContainer.addEventListener('click', handleOptionEvent);
2941
- // also watch pointerup to catch cases where the pointerdown started outside
2942
- // (e.g. on the input) and the click never fires
2943
- this._optionsContainer.addEventListener('pointerup', handleOptionEvent);
2944
3402
  // Keyboard navigation
2945
3403
  this._input.addEventListener('keydown', (e) => this._handleKeydown(e));
2946
3404
  // Click outside to close — robust detection across shadow DOM and custom renderers
@@ -3038,17 +3496,6 @@
3038
3496
  this._dropdown.style.display = 'block';
3039
3497
  this._input.setAttribute('aria-expanded', 'true');
3040
3498
  this._updateArrowRotation();
3041
- // Clear search query when opening to show all options
3042
- // This ensures we can scroll to selected item
3043
- if (this._config.searchable) {
3044
- this._state.searchQuery = '';
3045
- // Don't clear input value if it represents selection
3046
- // But if we want to search, we might want to clear it?
3047
- // Standard behavior: input keeps value (label), but dropdown shows all options
3048
- // until user types.
3049
- // However, our filtering logic uses _state.searchQuery.
3050
- // So clearing it here resets the filter.
3051
- }
3052
3499
  // Render options when opening
3053
3500
  this._renderOptions();
3054
3501
  this._setInitialActiveOption();
@@ -3294,24 +3741,24 @@
3294
3741
  }
3295
3742
  }
3296
3743
  _setActive(index) {
3297
- const options = Array.from(this._optionsContainer.children);
3298
3744
  // Clear previous active state
3299
- if (this._state.activeIndex >= 0 && options[this._state.activeIndex]) {
3300
- const prevOption = options[this._state.activeIndex];
3301
- // Check if it's a custom SelectOption or a lightweight DOM element
3302
- if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
3303
- prevOption.setActive(false);
3304
- }
3305
- else {
3306
- // Lightweight option - remove active class
3307
- prevOption.classList.remove('smilodon-option--active');
3308
- prevOption.setAttribute('aria-selected', 'false');
3745
+ if (this._state.activeIndex >= 0) {
3746
+ const prevOption = this._getOptionElementByIndex(this._state.activeIndex);
3747
+ if (prevOption) {
3748
+ // Check if it's a custom SelectOption or a lightweight DOM element
3749
+ if ('setActive' in prevOption && typeof prevOption.setActive === 'function') {
3750
+ prevOption.setActive(false);
3751
+ }
3752
+ else {
3753
+ // Lightweight option - remove active class
3754
+ prevOption.classList.remove('smilodon-option--active');
3755
+ }
3309
3756
  }
3310
3757
  }
3311
3758
  this._state.activeIndex = index;
3312
3759
  // Set new active state
3313
- if (options[index]) {
3314
- const option = options[index];
3760
+ const option = this._getOptionElementByIndex(index);
3761
+ if (option) {
3315
3762
  // Check if it's a custom SelectOption or a lightweight DOM element
3316
3763
  if ('setActive' in option && typeof option.setActive === 'function') {
3317
3764
  option.setActive(true);
@@ -3319,13 +3766,13 @@
3319
3766
  else {
3320
3767
  // Lightweight option - add active class
3321
3768
  option.classList.add('smilodon-option--active');
3322
- option.setAttribute('aria-selected', 'true');
3323
3769
  }
3324
3770
  if (typeof option.scrollIntoView === 'function') {
3771
+ // Don't scroll wildly when just opening with pre-selections
3325
3772
  option.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
3326
3773
  }
3327
3774
  // Announce position for screen readers
3328
- const total = options.length;
3775
+ const total = this._state.loadedItems.length;
3329
3776
  this._announce(`Item ${index + 1} of ${total}`);
3330
3777
  // Update aria-activedescendant using the actual option id when available
3331
3778
  const optionId = option.id || `${this._uniqueId}-option-${index}`;
@@ -3448,9 +3895,9 @@
3448
3895
  // FIX: Do not rely on this._optionsContainer.children[index] because filtering changes the children
3449
3896
  // Instead, use the index to update state directly
3450
3897
  const item = this._state.loadedItems[index];
3451
- // Debug: log selection attempt
3452
- if (!item)
3898
+ if (!item) {
3453
3899
  return;
3900
+ }
3454
3901
  const isCurrentlySelected = this._state.selectedIndices.has(index);
3455
3902
  // Keep active/focus styling aligned with the most recently interacted option.
3456
3903
  // Without this, a previously selected item may retain active classes/styles
@@ -3461,7 +3908,7 @@
3461
3908
  const wasSelected = this._state.selectedIndices.has(index);
3462
3909
  this._state.selectedIndices.clear();
3463
3910
  this._state.selectedItems.clear();
3464
- if (!wasSelected) {
3911
+ if (!wasSelected || !this._config.selection.allowDeselect) {
3465
3912
  // Select this option
3466
3913
  this._state.selectedIndices.add(index);
3467
3914
  this._state.selectedItems.set(index, item);
@@ -3523,16 +3970,34 @@
3523
3970
  });
3524
3971
  }
3525
3972
  _handleOptionRemove(index) {
3973
+ const item = this._state.selectedItems.get(index);
3526
3974
  const option = this._getOptionElementByIndex(index);
3527
- if (!option)
3528
- return;
3529
3975
  this._state.selectedIndices.delete(index);
3530
3976
  this._state.selectedItems.delete(index);
3531
- option.setSelected(false);
3977
+ if (option && 'setSelected' in option && typeof option.setSelected === 'function') {
3978
+ option.setSelected(false);
3979
+ }
3980
+ else if (option) {
3981
+ option.classList.remove('selected', 'sm-selected', 'smilodon-option--selected');
3982
+ option.setAttribute('aria-selected', 'false');
3983
+ const stateTokens = (option.dataset.smState || '')
3984
+ .split(' ')
3985
+ .map(token => token.trim())
3986
+ .filter(token => token && token !== 'selected');
3987
+ if (stateTokens.length > 0) {
3988
+ option.dataset.smState = stateTokens.join(' ');
3989
+ }
3990
+ else {
3991
+ delete option.dataset.smState;
3992
+ }
3993
+ }
3532
3994
  this._updateInputDisplay();
3995
+ this._renderOptions();
3533
3996
  this._emitChange();
3534
- const config = option.getConfig();
3535
- this._emit('remove', { item: config.item, index });
3997
+ const config = option && 'getConfig' in option && typeof option.getConfig === 'function'
3998
+ ? option.getConfig()
3999
+ : undefined;
4000
+ this._emit('remove', { item: config?.item ?? item, index });
3536
4001
  }
3537
4002
  _updateInputDisplay() {
3538
4003
  const selectedItems = Array.from(this._state.selectedItems.values());
@@ -3560,22 +4025,29 @@
3560
4025
  const badge = document.createElement('span');
3561
4026
  badge.className = 'selection-badge';
3562
4027
  badge.setAttribute('part', 'chip');
3563
- badge.textContent = getLabel(item);
4028
+ const badgeLabel = document.createElement('span');
4029
+ badgeLabel.className = 'selection-badge-label';
4030
+ badgeLabel.textContent = getLabel(item);
4031
+ badge.appendChild(badgeLabel);
3564
4032
  // Add remove button to badge
3565
- const removeBtn = document.createElement('button');
3566
- removeBtn.className = 'badge-remove';
3567
- removeBtn.setAttribute('part', 'chip-remove');
3568
- removeBtn.innerHTML = '×';
3569
- removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
3570
- removeBtn.addEventListener('click', (e) => {
3571
- e.stopPropagation();
3572
- this._state.selectedIndices.delete(index);
3573
- this._state.selectedItems.delete(index);
3574
- this._updateInputDisplay();
3575
- this._renderOptions();
3576
- this._emitChange();
3577
- });
3578
- badge.appendChild(removeBtn);
4033
+ if (this._config.selection.showRemoveButton !== false) {
4034
+ const removeBtn = document.createElement('button');
4035
+ removeBtn.type = 'button';
4036
+ removeBtn.className = 'badge-remove';
4037
+ removeBtn.setAttribute('part', 'chip-remove');
4038
+ removeBtn.innerHTML = '×';
4039
+ removeBtn.setAttribute('aria-label', `Remove ${getLabel(item)}`);
4040
+ removeBtn.addEventListener('pointerdown', (e) => {
4041
+ e.stopPropagation();
4042
+ e.preventDefault();
4043
+ });
4044
+ removeBtn.addEventListener('click', (e) => {
4045
+ e.stopPropagation();
4046
+ e.preventDefault();
4047
+ this._handleOptionRemove(index);
4048
+ });
4049
+ badge.appendChild(removeBtn);
4050
+ }
3579
4051
  this._inputContainer.insertBefore(badge, this._input);
3580
4052
  });
3581
4053
  }
@@ -3627,10 +4099,12 @@
3627
4099
  const option = this._getOptionElementByIndex(targetIndex);
3628
4100
  if (option) {
3629
4101
  // Use smooth scrolling with center alignment for better UX
3630
- option.scrollIntoView({
3631
- block: this._config.scrollToSelected.block || 'center',
3632
- behavior: 'smooth',
3633
- });
4102
+ if (typeof option.scrollIntoView === 'function') {
4103
+ option.scrollIntoView({
4104
+ block: this._config.scrollToSelected.block || 'center',
4105
+ behavior: 'smooth',
4106
+ });
4107
+ }
3634
4108
  // Also set it as active for keyboard navigation
3635
4109
  this._setActive(targetIndex);
3636
4110
  }
@@ -3680,13 +4154,168 @@
3680
4154
  }
3681
4155
  _emit(name, detail) {
3682
4156
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
4157
+ if (name !== 'diagnostic') {
4158
+ this._track('event', String(name), detail);
4159
+ }
4160
+ }
4161
+ _track(source, name, detail) {
4162
+ const cfg = this._config.tracking;
4163
+ if (!cfg?.enabled)
4164
+ return;
4165
+ if (source === 'event' && !cfg.events)
4166
+ return;
4167
+ if (source === 'style' && !cfg.styling)
4168
+ return;
4169
+ if (source === 'limitation' && !cfg.limitations)
4170
+ return;
4171
+ const entry = {
4172
+ timestamp: Date.now(),
4173
+ source,
4174
+ name,
4175
+ detail,
4176
+ };
4177
+ const bucket = source === 'event'
4178
+ ? this._tracking.events
4179
+ : source === 'style'
4180
+ ? this._tracking.styles
4181
+ : this._tracking.limitations;
4182
+ bucket.push(entry);
4183
+ const maxEntries = Math.max(10, cfg.maxEntries || 200);
4184
+ if (bucket.length > maxEntries) {
4185
+ bucket.splice(0, bucket.length - maxEntries);
4186
+ }
4187
+ if (cfg.emitDiagnostics) {
4188
+ this.dispatchEvent(new CustomEvent('diagnostic', {
4189
+ detail: entry,
4190
+ bubbles: true,
4191
+ composed: true,
4192
+ }));
4193
+ }
4194
+ }
4195
+ _getKnownLimitationDefinitions() {
4196
+ return [
4197
+ {
4198
+ id: 'variableItemHeight',
4199
+ title: 'Variable item height',
4200
+ description: 'Virtualization assumes fixed or estimated item heights; fully dynamic heights are not yet supported.',
4201
+ workaround: 'Use consistent item heights or set estimatedItemHeight to your dominant row size.',
4202
+ },
4203
+ {
4204
+ id: 'builtInFetchPaginationApi',
4205
+ title: 'Built-in fetch/pagination API',
4206
+ description: 'Core does not include a built-in fetchUrl/searchUrl pagination transport.',
4207
+ workaround: 'Use onSearch/onLoadMore callbacks and update data via setItems().',
4208
+ },
4209
+ {
4210
+ id: 'virtualizationOverheadSmallLists',
4211
+ title: 'Virtualization overhead for small lists',
4212
+ description: 'Virtualization can add slight overhead on very small lists.',
4213
+ workaround: 'Disable virtualization for tiny datasets when micro-latency is critical.',
4214
+ },
4215
+ {
4216
+ id: 'runtimeModeSwitching',
4217
+ title: 'Runtime single/multi mode switching',
4218
+ description: 'Switching between single and multi mode can require state reset for consistency.',
4219
+ workaround: 'Enable autoMitigateRuntimeModeSwitch or recreate/reset component state when toggling modes.',
4220
+ },
4221
+ {
4222
+ id: 'legacyBrowserSupport',
4223
+ title: 'Legacy browser support',
4224
+ description: 'Official support targets modern evergreen browsers.',
4225
+ },
4226
+ {
4227
+ id: 'webkitArchLinux',
4228
+ title: 'Playwright WebKit on Arch-based Linux',
4229
+ description: 'Native WebKit Playwright bundle depends on unavailable legacy system libraries on Arch-based distros.',
4230
+ workaround: 'Run WebKit E2E tests via Playwright Docker image.',
4231
+ },
4232
+ ];
4233
+ }
4234
+ _evaluateLimitationStatus(id) {
4235
+ const policyMode = this._config.limitations?.policies?.[id]?.mode ?? 'default';
4236
+ if (policyMode === 'suppress')
4237
+ return 'suppressed';
4238
+ if (id === 'runtimeModeSwitching' && this._config.limitations?.autoMitigateRuntimeModeSwitch) {
4239
+ return 'mitigated';
4240
+ }
4241
+ return 'active';
4242
+ }
4243
+ getKnownLimitations() {
4244
+ return this._getKnownLimitationDefinitions().map((limitation) => {
4245
+ const mode = this._config.limitations?.policies?.[limitation.id]?.mode ?? 'default';
4246
+ return {
4247
+ ...limitation,
4248
+ mode,
4249
+ status: this._evaluateLimitationStatus(limitation.id),
4250
+ };
4251
+ });
4252
+ }
4253
+ setLimitationPolicies(policies) {
4254
+ const next = {
4255
+ ...(this._config.limitations?.policies || {}),
4256
+ ...policies,
4257
+ };
4258
+ this.updateConfig({
4259
+ limitations: {
4260
+ ...(this._config.limitations || { autoMitigateRuntimeModeSwitch: true, policies: {} }),
4261
+ policies: next,
4262
+ },
4263
+ });
4264
+ this._track('limitation', 'policiesUpdated', { policies: next });
4265
+ }
4266
+ getTrackingSnapshot() {
4267
+ return {
4268
+ events: [...this._tracking.events],
4269
+ styles: [...this._tracking.styles],
4270
+ limitations: [...this._tracking.limitations],
4271
+ };
4272
+ }
4273
+ clearTracking(source) {
4274
+ if (!source || source === 'all') {
4275
+ this._tracking.events = [];
4276
+ this._tracking.styles = [];
4277
+ this._tracking.limitations = [];
4278
+ return;
4279
+ }
4280
+ if (source === 'event')
4281
+ this._tracking.events = [];
4282
+ if (source === 'style')
4283
+ this._tracking.styles = [];
4284
+ if (source === 'limitation')
4285
+ this._tracking.limitations = [];
4286
+ }
4287
+ getCapabilities() {
4288
+ return {
4289
+ styling: {
4290
+ classMap: true,
4291
+ optionRenderer: true,
4292
+ groupHeaderRenderer: true,
4293
+ cssCustomProperties: true,
4294
+ shadowParts: true,
4295
+ globalStyleMirroring: true,
4296
+ },
4297
+ events: {
4298
+ emitted: ['select', 'open', 'close', 'search', 'change', 'loadMore', 'remove', 'clear', 'error', 'diagnostic'],
4299
+ diagnosticEvent: true,
4300
+ },
4301
+ functionality: {
4302
+ multiSelect: true,
4303
+ searchable: true,
4304
+ infiniteScroll: true,
4305
+ loadMore: true,
4306
+ clearControl: true,
4307
+ groupedItems: true,
4308
+ serverSideSelection: true,
4309
+ runtimeModeSwitchMitigation: Boolean(this._config.limitations?.autoMitigateRuntimeModeSwitch),
4310
+ },
4311
+ limitations: this.getKnownLimitations(),
4312
+ };
3683
4313
  }
3684
4314
  _emitChange() {
3685
4315
  const selectedItems = Array.from(this._state.selectedItems.values());
3686
4316
  const getValue = this._config.serverSide.getValueFromItem || ((item) => item?.value ?? item);
3687
4317
  const selectedValues = selectedItems.map(getValue);
3688
4318
  const selectedIndices = Array.from(this._state.selectedIndices);
3689
- // Debug: log change payload
3690
4319
  this._emit('change', { selectedItems, selectedValues, selectedIndices });
3691
4320
  this._config.callbacks.onChange?.(selectedItems, selectedValues);
3692
4321
  }
@@ -3697,6 +4326,7 @@
3697
4326
  set optionRenderer(renderer) {
3698
4327
  this._optionRenderer = renderer;
3699
4328
  this._setGlobalStylesMirroring(Boolean(renderer || this._classMap));
4329
+ this._track('style', 'optionRendererChanged', { enabled: Boolean(renderer) });
3700
4330
  this._renderOptions();
3701
4331
  }
3702
4332
  /**
@@ -3861,7 +4491,22 @@
3861
4491
  * Update component configuration
3862
4492
  */
3863
4493
  updateConfig(config) {
3864
- this._config = selectConfig.mergeWithComponentConfig(config);
4494
+ const previousMode = this._config.selection.mode;
4495
+ this._config = this._mergeConfig(this._config, config);
4496
+ if (previousMode !== this._config.selection.mode &&
4497
+ this._config.limitations?.autoMitigateRuntimeModeSwitch) {
4498
+ this.clear();
4499
+ this._track('limitation', 'runtimeModeSwitchMitigated', {
4500
+ from: previousMode,
4501
+ to: this._config.selection.mode,
4502
+ });
4503
+ }
4504
+ else if (previousMode !== this._config.selection.mode) {
4505
+ this._track('limitation', 'runtimeModeSwitchDetected', {
4506
+ from: previousMode,
4507
+ to: this._config.selection.mode,
4508
+ });
4509
+ }
3865
4510
  // Update input state based on new config
3866
4511
  if (this._input) {
3867
4512
  this._input.readOnly = !this._config.searchable;
@@ -3874,7 +4519,7 @@
3874
4519
  this._clearControl.setAttribute('aria-label', this._config.clearControl.ariaLabel || 'Clear selection and search');
3875
4520
  }
3876
4521
  if (this._clearControlIcon) {
3877
- this._clearControlIcon.textContent = this._config.clearControl.icon || '×';
4522
+ this._clearControlIcon.innerHTML = `<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4L4 12M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
3878
4523
  }
3879
4524
  if (this._dropdown) {
3880
4525
  if (this._config.selection.mode === 'multi') {
@@ -3884,11 +4529,28 @@
3884
4529
  this._dropdown.removeAttribute('aria-multiselectable');
3885
4530
  }
3886
4531
  }
4532
+ this._syncInputContainerMode();
3887
4533
  // Re-initialize observers in case infinite scroll was enabled/disabled
3888
4534
  this._initializeObservers();
3889
4535
  this._syncClearControlState();
3890
4536
  this._renderOptions();
3891
4537
  }
4538
+ _mergeConfig(target, source) {
4539
+ const result = { ...target };
4540
+ for (const key in source) {
4541
+ if (!Object.prototype.hasOwnProperty.call(source, key))
4542
+ continue;
4543
+ const sourceValue = source[key];
4544
+ const targetValue = result[key];
4545
+ if (sourceValue && typeof sourceValue === 'object' && !Array.isArray(sourceValue)) {
4546
+ result[key] = this._mergeConfig(targetValue && typeof targetValue === 'object' ? targetValue : {}, sourceValue);
4547
+ }
4548
+ else {
4549
+ result[key] = sourceValue;
4550
+ }
4551
+ }
4552
+ return result;
4553
+ }
3892
4554
  _handleClearControlClick() {
3893
4555
  const shouldClearSelection = this._config.clearControl.clearSelection !== false;
3894
4556
  const shouldClearSearch = this._config.clearControl.clearSearch !== false;
@@ -3973,6 +4635,8 @@
3973
4635
  * Render options based on current state
3974
4636
  */
3975
4637
  _renderOptions() {
4638
+ this._renderCycleId += 1;
4639
+ const renderCycleId = this._renderCycleId;
3976
4640
  // Cleanup observer
3977
4641
  if (this._loadMoreTrigger && this._intersectionObserver) {
3978
4642
  this._intersectionObserver.unobserve(this._loadMoreTrigger);
@@ -4037,23 +4701,21 @@
4037
4701
  }
4038
4702
  else {
4039
4703
  // Normal rendering (flat list or filtered)
4040
- let hasRenderedItems = false;
4704
+ const filteredIndices = [];
4041
4705
  this._state.loadedItems.forEach((item, index) => {
4042
- // Apply filter if query exists
4043
4706
  if (query) {
4044
4707
  try {
4045
4708
  const label = String(getLabel(item)).toLowerCase();
4046
4709
  if (!label.includes(query))
4047
4710
  return;
4048
4711
  }
4049
- catch (e) {
4712
+ catch (_e) {
4050
4713
  return;
4051
4714
  }
4052
4715
  }
4053
- hasRenderedItems = true;
4054
- this._renderSingleOption(item, index, getValue, getLabel);
4716
+ filteredIndices.push(index);
4055
4717
  });
4056
- if (!hasRenderedItems && !this._state.isBusy) {
4718
+ if (filteredIndices.length === 0 && !this._state.isBusy) {
4057
4719
  const empty = document.createElement('div');
4058
4720
  empty.setAttribute('part', 'no-results');
4059
4721
  empty.className = 'empty-state';
@@ -4065,6 +4727,54 @@
4065
4727
  }
4066
4728
  this._optionsContainer.appendChild(empty);
4067
4729
  }
4730
+ else {
4731
+ const shouldIncrementalRender = this._config.virtualize !== false
4732
+ && this._state.groupedItems.length === 0
4733
+ && filteredIndices.length > 300;
4734
+ if (shouldIncrementalRender) {
4735
+ const chunkSize = 80;
4736
+ let cursor = 0;
4737
+ let maxRenderTarget = 0;
4738
+ if (this._state.selectedIndices.size > 0 && this._config.scrollToSelected.enabled) {
4739
+ const indices = Array.from(this._state.selectedIndices).sort((a, b) => a - b);
4740
+ const targetIndex = this._config.scrollToSelected.multiSelectTarget === 'first' ? indices[0] : indices[indices.length - 1];
4741
+ const filteredPos = filteredIndices.indexOf(targetIndex);
4742
+ if (filteredPos !== -1) {
4743
+ maxRenderTarget = filteredPos + 20; // Ensure we render up to the selection
4744
+ }
4745
+ }
4746
+ const renderChunk = () => {
4747
+ if (renderCycleId !== this._renderCycleId)
4748
+ return;
4749
+ const fragment = document.createDocumentFragment();
4750
+ const chunkEnd = Math.min(Math.max(cursor + chunkSize, maxRenderTarget), filteredIndices.length);
4751
+ maxRenderTarget = 0; // Reset after fast-forwarding
4752
+ for (; cursor < chunkEnd; cursor += 1) {
4753
+ const itemIndex = filteredIndices[cursor];
4754
+ const item = this._state.loadedItems[itemIndex];
4755
+ this._renderSingleOption(item, itemIndex, getValue, getLabel, fragment);
4756
+ }
4757
+ this._optionsContainer.appendChild(fragment);
4758
+ if (cursor < filteredIndices.length) {
4759
+ requestAnimationFrame(renderChunk);
4760
+ }
4761
+ else {
4762
+ if (renderCycleId !== this._renderCycleId)
4763
+ return;
4764
+ if (!this._state.isBusy && (this._config.loadMore.enabled || this._config.infiniteScroll.enabled) && this._state.loadedItems.length > 0) {
4765
+ this._addLoadMoreTrigger();
4766
+ }
4767
+ this._finalizePerfMarks();
4768
+ }
4769
+ };
4770
+ renderChunk();
4771
+ return;
4772
+ }
4773
+ filteredIndices.forEach((itemIndex) => {
4774
+ const item = this._state.loadedItems[itemIndex];
4775
+ this._renderSingleOption(item, itemIndex, getValue, getLabel);
4776
+ });
4777
+ }
4068
4778
  }
4069
4779
  // Append Busy Indicator if busy
4070
4780
  if (this._state.isBusy && this._config.busyBucket.enabled) {
@@ -4089,7 +4799,7 @@
4089
4799
  }
4090
4800
  this._finalizePerfMarks();
4091
4801
  }
4092
- _renderSingleOption(item, index, getValue, getLabel) {
4802
+ _renderSingleOption(item, index, getValue, getLabel, targetContainer = this._optionsContainer) {
4093
4803
  const isSelected = this._state.selectedIndices.has(index);
4094
4804
  const isDisabled = Boolean(item?.disabled);
4095
4805
  const optionId = `${this._uniqueId}-option-${index}`;
@@ -4105,7 +4815,7 @@
4105
4815
  disabled: isDisabled,
4106
4816
  id: optionId,
4107
4817
  });
4108
- this._optionsContainer.appendChild(optionElement);
4818
+ targetContainer.appendChild(optionElement);
4109
4819
  return;
4110
4820
  }
4111
4821
  const option = new SelectOption({
@@ -4134,20 +4844,16 @@
4134
4844
  option.dataset.smValue = String(val);
4135
4845
  }
4136
4846
  option.id = option.id || optionId;
4137
- option.addEventListener('click', (e) => {
4138
- e.stopPropagation(); // Prevent duplicate handling by delegation
4139
- const mouseEvent = e;
4140
- this._selectOption(index, {
4141
- shiftKey: mouseEvent.shiftKey,
4142
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4143
- });
4144
- });
4847
+ // Do NOT bind a native click listener here for SelectOption elements.
4848
+ // Like custom rendered options, they are fully handled by the `handleOptionEvent` delegator
4849
+ // on `this._optionsContainer` (line 1221).
4850
+ // Adding this listener causes the double-click toggle bug since both fire on selection!
4145
4851
  option.addEventListener('optionRemove', (event) => {
4146
4852
  const detail = event.detail;
4147
4853
  const targetIndex = detail?.index ?? index;
4148
4854
  this._handleOptionRemove(targetIndex);
4149
4855
  });
4150
- this._optionsContainer.appendChild(option);
4856
+ targetContainer.appendChild(option);
4151
4857
  }
4152
4858
  _normalizeCustomOptionElement(element, meta) {
4153
4859
  const optionEl = element instanceof HTMLElement ? element : document.createElement('div');
@@ -4234,35 +4940,9 @@
4234
4940
  optionEl.tabIndex = -1;
4235
4941
  }
4236
4942
  if (!this._customOptionBoundElements.has(optionEl)) {
4237
- optionEl.addEventListener('click', (e) => {
4238
- e.stopPropagation();
4239
- const current = e.currentTarget;
4240
- if (current.getAttribute('aria-disabled') === 'true')
4241
- return;
4242
- const parsedIndex = Number(current.dataset.index);
4243
- if (!Number.isFinite(parsedIndex))
4244
- return;
4245
- const mouseEvent = e;
4246
- this._selectOption(parsedIndex, {
4247
- shiftKey: mouseEvent.shiftKey,
4248
- toggleKey: mouseEvent.ctrlKey || mouseEvent.metaKey,
4249
- });
4250
- });
4251
- optionEl.addEventListener('keydown', (e) => {
4252
- if (e.key !== 'Enter' && e.key !== ' ')
4253
- return;
4254
- const current = e.currentTarget;
4255
- if (current.getAttribute('aria-disabled') === 'true')
4256
- return;
4257
- const parsedIndex = Number(current.dataset.index);
4258
- if (!Number.isFinite(parsedIndex))
4259
- return;
4260
- e.preventDefault();
4261
- this._selectOption(parsedIndex, {
4262
- shiftKey: e.shiftKey,
4263
- toggleKey: e.ctrlKey || e.metaKey,
4264
- });
4265
- });
4943
+ // Intentionally NOT binding native option click listeners for custom options!
4944
+ // All option interactions are globally handled by _optionsContainer.addEventListener('click', handleOptionEvent);
4945
+ // Re-attaching here causes the double-click toggle bug if a child component fails to stopPropagation.
4266
4946
  this._customOptionBoundElements.add(optionEl);
4267
4947
  }
4268
4948
  return optionEl;