mtrl 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +1 -1
  2. package/src/components/badge/_styles.scss +9 -9
  3. package/src/components/button/_styles.scss +0 -56
  4. package/src/components/button/button.ts +0 -2
  5. package/src/components/button/constants.ts +0 -6
  6. package/src/components/button/index.ts +2 -2
  7. package/src/components/button/types.ts +1 -7
  8. package/src/components/card/_styles.scss +67 -25
  9. package/src/components/card/api.ts +54 -3
  10. package/src/components/card/card.ts +33 -2
  11. package/src/components/card/config.ts +143 -21
  12. package/src/components/card/constants.ts +20 -19
  13. package/src/components/card/content.ts +299 -2
  14. package/src/components/card/features.ts +155 -4
  15. package/src/components/card/index.ts +31 -9
  16. package/src/components/card/types.ts +138 -15
  17. package/src/components/chip/chip.ts +1 -9
  18. package/src/components/chip/constants.ts +0 -10
  19. package/src/components/chip/index.ts +1 -1
  20. package/src/components/chip/types.ts +1 -4
  21. package/src/components/progress/_styles.scss +0 -65
  22. package/src/components/progress/config.ts +1 -2
  23. package/src/components/progress/constants.ts +0 -14
  24. package/src/components/progress/index.ts +1 -1
  25. package/src/components/progress/progress.ts +1 -4
  26. package/src/components/progress/types.ts +1 -4
  27. package/src/components/radios/_styles.scss +0 -45
  28. package/src/components/radios/api.ts +85 -60
  29. package/src/components/radios/config.ts +1 -2
  30. package/src/components/radios/constants.ts +0 -9
  31. package/src/components/radios/index.ts +1 -1
  32. package/src/components/radios/radio.ts +34 -11
  33. package/src/components/radios/radios.ts +2 -1
  34. package/src/components/radios/types.ts +1 -7
  35. package/src/components/slider/_styles.scss +149 -155
  36. package/src/components/slider/accessibility.md +59 -0
  37. package/src/components/slider/config.ts +4 -6
  38. package/src/components/slider/features/disabled.ts +41 -16
  39. package/src/components/slider/features/interactions.ts +153 -18
  40. package/src/components/slider/features/keyboard.ts +127 -6
  41. package/src/components/slider/features/structure.ts +32 -5
  42. package/src/components/slider/features/ui.ts +18 -8
  43. package/src/components/tabs/_styles.scss +285 -155
  44. package/src/components/tabs/api.ts +178 -400
  45. package/src/components/tabs/config.ts +46 -52
  46. package/src/components/tabs/constants.ts +85 -8
  47. package/src/components/tabs/features.ts +401 -0
  48. package/src/components/tabs/index.ts +60 -3
  49. package/src/components/tabs/indicator.ts +225 -0
  50. package/src/components/tabs/responsive.ts +144 -0
  51. package/src/components/tabs/scroll-indicators.ts +149 -0
  52. package/src/components/tabs/state.ts +186 -0
  53. package/src/components/tabs/tab-api.ts +258 -0
  54. package/src/components/tabs/tab.ts +255 -0
  55. package/src/components/tabs/tabs.ts +50 -31
  56. package/src/components/tabs/types.ts +324 -128
  57. package/src/components/tabs/utils.ts +107 -0
  58. package/src/components/textfield/_styles.scss +0 -98
  59. package/src/components/textfield/config.ts +2 -3
  60. package/src/components/textfield/constants.ts +0 -14
  61. package/src/components/textfield/index.ts +2 -2
  62. package/src/components/textfield/textfield.ts +0 -2
  63. package/src/components/textfield/types.ts +1 -4
  64. package/src/core/compose/component.ts +1 -1
  65. package/src/core/compose/features/badge.ts +79 -0
  66. package/src/core/compose/features/index.ts +3 -1
  67. package/src/styles/abstract/_theme.scss +106 -2
  68. package/src/components/card/actions.ts +0 -48
  69. package/src/components/card/header.ts +0 -88
  70. package/src/components/card/media.ts +0 -52
@@ -42,9 +42,107 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
42
42
  triggerEvent = () => ({ defaultPrevented: false })
43
43
  } = handlers;
44
44
 
45
- // Event handlers
45
+ /**
46
+ * Clear any existing bubble hide timers
47
+ */
48
+ const clearBubbleHideTimer = () => {
49
+ if (state.valueHideTimer) {
50
+ clearTimeout(state.valueHideTimer);
51
+ state.valueHideTimer = null;
52
+ }
53
+ };
54
+
55
+ /**
56
+ * Hide all bubbles immediately
57
+ */
58
+ const hideAllBubbles = () => {
59
+ // Clear any pending hide timers
60
+ clearBubbleHideTimer();
61
+
62
+ // Hide both bubbles immediately
63
+ if (valueBubble) {
64
+ showValueBubble(valueBubble, false);
65
+ }
66
+ if (secondValueBubble) {
67
+ showValueBubble(secondValueBubble, false);
68
+ }
69
+ };
70
+
71
+ /**
72
+ * Clear keyboard focus indicators across all sliders in the document
73
+ * Not just for this instance, but for any slider thumb
74
+ */
75
+ const clearGlobalKeyboardFocus = () => {
76
+ // First clear local focus indicators
77
+ if (thumb) {
78
+ thumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
79
+ }
80
+
81
+ if (secondThumb) {
82
+ secondThumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
83
+ }
84
+
85
+ // Now look for all slider thumbs in the document with the focused class
86
+ // This covers cases where we switch between sliders
87
+ try {
88
+ const focusClass = state.component.getClass('slider-thumb--focused');
89
+ const allFocusedThumbs = document.querySelectorAll(`.${focusClass}`);
90
+
91
+ // Remove focus class from all thumbs
92
+ allFocusedThumbs.forEach(element => {
93
+ element.classList.remove(focusClass);
94
+ });
95
+
96
+ // Also blur the active element if it's a thumb
97
+ if (document.activeElement &&
98
+ document.activeElement.classList.contains(state.component.getClass('slider-thumb'))) {
99
+ (document.activeElement as HTMLElement).blur();
100
+ }
101
+ } catch (error) {
102
+ console.warn('Error clearing global keyboard focus:', error);
103
+ }
104
+ };
105
+
106
+ /**
107
+ * Show the active bubble with consistent behavior
108
+ * @param bubble Bubble element to show
109
+ */
110
+ const showActiveBubble = (bubble) => {
111
+ // First hide all bubbles
112
+ hideAllBubbles();
113
+
114
+ // Then show the active bubble if allowed by config
115
+ if (bubble && config.showValue) {
116
+ showValueBubble(bubble, true);
117
+ }
118
+ };
119
+
120
+ /**
121
+ * Hide the active bubble with optional delay
122
+ * @param bubble Bubble element to hide
123
+ * @param delay Delay in milliseconds before hiding
124
+ */
125
+ const hideActiveBubble = (bubble, delay = 0) => {
126
+ // Clear any existing timers first
127
+ clearBubbleHideTimer();
128
+
129
+ if (!bubble || !config.showValue) return;
130
+
131
+ if (delay > 0) {
132
+ // Set delayed hide
133
+ state.valueHideTimer = setTimeout(() => {
134
+ showValueBubble(bubble, false);
135
+ }, delay);
136
+ } else {
137
+ // Hide immediately
138
+ showValueBubble(bubble, false);
139
+ }
140
+ };
141
+
142
+ /**
143
+ * Handle thumb mouse down with improved bubble handling
144
+ */
46
145
  const handleThumbMouseDown = (e, isSecondThumb = false) => {
47
- console.log('handleThumbMouseDown', e)
48
146
  // Verify component exists and check if disabled
49
147
  if (!state.component || (state.component.disabled && state.component.disabled.isDisabled())) {
50
148
  return;
@@ -53,6 +151,12 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
53
151
  e.preventDefault();
54
152
  e.stopPropagation();
55
153
 
154
+ // First hide any existing visible bubbles
155
+ hideAllBubbles();
156
+
157
+ // Clear any keyboard focus indicators globally
158
+ clearGlobalKeyboardFocus();
159
+
56
160
  state.dragging = true;
57
161
  state.activeThumb = isSecondThumb ? secondThumb : thumb;
58
162
  state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
@@ -60,10 +164,8 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
60
164
  // Add dragging class to component element to style the thumb differently
61
165
  state.component.element.classList.add(`${state.component.getClass('slider')}--dragging`);
62
166
 
63
- // Show value bubble if it exists
64
- if (state.activeBubble) {
65
- showValueBubble(state.activeBubble, true);
66
- }
167
+ // Show active bubble
168
+ showActiveBubble(state.activeBubble);
67
169
 
68
170
  // Add global event listeners
69
171
  document.addEventListener('mousemove', handleMouseMove);
@@ -79,6 +181,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
79
181
  }
80
182
  };
81
183
 
184
+ /**
185
+ * Handle track mouse down with improved bubble handling
186
+ */
82
187
  const handleTrackMouseDown = (e) => {
83
188
  // Verify component exists and check if disabled
84
189
  if (!state.component || (state.component.disabled && state.component.disabled.isDisabled()) || !track) {
@@ -87,6 +192,12 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
87
192
 
88
193
  e.preventDefault();
89
194
 
195
+ // Hide any existing visible bubbles
196
+ hideAllBubbles();
197
+
198
+ // Clear any keyboard focus indicators globally
199
+ clearGlobalKeyboardFocus();
200
+
90
201
  // Determine which thumb to move based on click position
91
202
  let isSecondThumb = false;
92
203
 
@@ -146,6 +257,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
146
257
  handleThumbMouseDown(e, isSecondThumb);
147
258
  };
148
259
 
260
+ /**
261
+ * Handle mouse move with improved thumb and bubble switching
262
+ */
149
263
  const handleMouseMove = (e) => {
150
264
  if (!state.dragging || !state.activeThumb) return;
151
265
 
@@ -174,30 +288,50 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
174
288
  // For range slider, ensure thumbs don't cross
175
289
  if (config.range && state.secondValue !== null) {
176
290
  if (isSecondThumb) {
291
+ // Second thumb is active
292
+
177
293
  // Don't allow second thumb to go below first thumb
178
- if (newValue < state.value) {
294
+ if (newValue >= state.value) {
179
295
  state.secondValue = newValue;
180
296
  } else {
181
- // Thumbs are crossed, swap them
297
+ // Thumbs are crossed, need to swap them
298
+
299
+ // First hide current bubble
300
+ hideActiveBubble(state.activeBubble, 0);
301
+
302
+ // Swap values
182
303
  state.secondValue = state.value;
183
304
  state.value = newValue;
184
305
 
185
- // Swap active thumb and bubble
306
+ // Swap active elements
186
307
  state.activeThumb = thumb;
187
308
  state.activeBubble = valueBubble;
309
+
310
+ // Show new active bubble
311
+ showActiveBubble(state.activeBubble);
188
312
  }
189
313
  } else {
314
+ // First thumb is active
315
+
190
316
  // Don't allow first thumb to go above second thumb
191
- if (newValue > state.secondValue) {
317
+ if (newValue <= state.secondValue) {
192
318
  state.value = newValue;
193
319
  } else {
194
- // Thumbs are crossed, swap them
320
+ // Thumbs are crossed, need to swap them
321
+
322
+ // First hide current bubble
323
+ hideActiveBubble(state.activeBubble, 0);
324
+
325
+ // Swap values
195
326
  state.value = state.secondValue;
196
327
  state.secondValue = newValue;
197
328
 
198
- // Swap active thumb and bubble
329
+ // Swap active elements
199
330
  state.activeThumb = secondThumb;
200
331
  state.activeBubble = secondValueBubble;
332
+
333
+ // Show new active bubble
334
+ showActiveBubble(state.activeBubble);
201
335
  }
202
336
  }
203
337
  } else {
@@ -215,6 +349,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
215
349
  }
216
350
  };
217
351
 
352
+ /**
353
+ * Handle mouse up with consistent bubble hiding
354
+ */
218
355
  const handleMouseUp = (e) => {
219
356
  if (!state.dragging) return;
220
357
 
@@ -225,10 +362,9 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
225
362
  // Remove dragging class from component element
226
363
  state.component.element.classList.remove(`${state.component.getClass('slider')}--dragging`);
227
364
 
228
- // Hide value bubble
229
- if (state.activeBubble) {
230
- showValueBubble(state.activeBubble, false);
231
- }
365
+ // Hide bubble with delay
366
+ const currentBubble = state.activeBubble;
367
+ hideActiveBubble(currentBubble, 1000); // Hide after 1 second
232
368
 
233
369
  // Remove global event listeners
234
370
  document.removeEventListener('mousemove', handleMouseMove);
@@ -236,9 +372,8 @@ export const createInteractionHandlers = (config: SliderConfig, state, handlers)
236
372
  document.removeEventListener('touchmove', handleMouseMove);
237
373
  document.removeEventListener('touchend', handleMouseUp);
238
374
 
239
- // Reset active elements
375
+ // Reset active thumb
240
376
  state.activeThumb = null;
241
- state.activeBubble = null;
242
377
 
243
378
  try {
244
379
  // Trigger change event (only when done dragging)
@@ -21,12 +21,73 @@ export const createKeyboardHandlers = (state, handlers) => {
21
21
  triggerEvent
22
22
  } = handlers;
23
23
 
24
+ // Last focused thumb tracker to handle tab sequences properly
25
+ let lastFocusedThumb = null;
26
+
27
+ /**
28
+ * Clear any existing bubble hide timers
29
+ */
30
+ const clearBubbleHideTimer = () => {
31
+ if (state.valueHideTimer) {
32
+ clearTimeout(state.valueHideTimer);
33
+ state.valueHideTimer = null;
34
+ }
35
+ };
36
+
37
+ /**
38
+ * Hide all bubbles immediately
39
+ */
40
+ const hideAllBubbles = () => {
41
+ // Clear any pending hide timers
42
+ clearBubbleHideTimer();
43
+
44
+ // Hide both bubbles immediately
45
+ if (valueBubble) {
46
+ showValueBubble(valueBubble, false);
47
+ }
48
+ if (secondValueBubble) {
49
+ showValueBubble(secondValueBubble, false);
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Shows a bubble element with consistent behavior
55
+ */
56
+ const showBubble = (bubble) => {
57
+ // First hide all bubbles
58
+ hideAllBubbles();
59
+
60
+ // Then show the active bubble
61
+ if (bubble) {
62
+ showValueBubble(bubble, true);
63
+ }
64
+ };
65
+
66
+ /**
67
+ * Hides a bubble element with optional delay
68
+ */
69
+ const hideBubble = (bubble, delay = 0) => {
70
+ // Clear any existing timers
71
+ clearBubbleHideTimer();
72
+
73
+ if (!bubble) return;
74
+
75
+ if (delay > 0) {
76
+ state.valueHideTimer = setTimeout(() => {
77
+ showValueBubble(bubble, false);
78
+ }, delay);
79
+ } else {
80
+ showValueBubble(bubble, false);
81
+ }
82
+ };
83
+
24
84
  // Event handlers
25
85
  const handleKeyDown = (e, isSecondThumb = false) => {
26
86
  if (state.component.disabled && state.component.disabled.isDisabled()) return;
27
87
 
28
88
  const step = state.step || 1;
29
89
  let newValue;
90
+ let stepSize = step;
30
91
 
31
92
  // Determine which value to modify
32
93
  if (isSecondThumb) {
@@ -35,41 +96,71 @@ export const createKeyboardHandlers = (state, handlers) => {
35
96
  newValue = state.value;
36
97
  }
37
98
 
99
+ // Handle tab key specifically for range sliders
100
+ if (e.key === 'Tab') {
101
+ // Let the browser handle the tab navigation
102
+ // We'll deal with showing/hiding bubbles in the focus/blur handlers
103
+ return;
104
+ }
105
+
106
+ // Determine step size based on modifier keys
107
+ if (e.shiftKey) {
108
+ stepSize = step * 10; // Large step when Shift is pressed
109
+ }
110
+
111
+ let valueChanged = false;
112
+
38
113
  switch (e.key) {
39
114
  case 'ArrowRight':
115
+ case 'ArrowUp':
40
116
  e.preventDefault();
41
- newValue = Math.min(newValue + step, state.max);
117
+ newValue = Math.min(newValue + stepSize, state.max);
118
+ valueChanged = true;
42
119
  break;
43
120
 
44
121
  case 'ArrowLeft':
122
+ case 'ArrowDown':
45
123
  e.preventDefault();
46
- newValue = Math.max(newValue - step, state.min);
124
+ newValue = Math.max(newValue - stepSize, state.min);
125
+ valueChanged = true;
47
126
  break;
48
127
 
49
128
  case 'Home':
50
129
  e.preventDefault();
51
130
  newValue = state.min;
131
+ valueChanged = true;
52
132
  break;
53
133
 
54
134
  case 'End':
55
135
  e.preventDefault();
56
136
  newValue = state.max;
137
+ valueChanged = true;
57
138
  break;
58
139
 
59
140
  case 'PageUp':
60
141
  e.preventDefault();
61
142
  newValue = Math.min(newValue + (step * 10), state.max);
143
+ valueChanged = true;
62
144
  break;
63
145
 
64
146
  case 'PageDown':
65
147
  e.preventDefault();
66
148
  newValue = Math.max(newValue - (step * 10), state.min);
149
+ valueChanged = true;
67
150
  break;
68
151
 
69
152
  default:
70
153
  return; // Exit if not a handled key
71
154
  }
72
155
 
156
+ if (!valueChanged) return;
157
+
158
+ // Update active bubble reference
159
+ state.activeBubble = isSecondThumb ? secondValueBubble : valueBubble;
160
+
161
+ // Show value bubble during keyboard interaction
162
+ showBubble(state.activeBubble);
163
+
73
164
  // Update the value
74
165
  if (isSecondThumb) {
75
166
  state.secondValue = newValue;
@@ -88,16 +179,46 @@ export const createKeyboardHandlers = (state, handlers) => {
88
179
  const handleFocus = (e, isSecondThumb = false) => {
89
180
  if (state.component.disabled && state.component.disabled.isDisabled()) return;
90
181
 
91
- // Show value bubble
92
- showValueBubble(isSecondThumb ? secondValueBubble : valueBubble, true);
182
+ // Track the currently focused thumb for tab sequence handling
183
+ const currentThumb = isSecondThumb ? secondThumb : state.component.structure.thumb;
184
+
185
+ // If we're tabbing between thumbs, hide the previous bubble immediately
186
+ if (lastFocusedThumb && lastFocusedThumb !== currentThumb) {
187
+ hideAllBubbles();
188
+ }
189
+
190
+ // Update the last focused thumb
191
+ lastFocusedThumb = currentThumb;
192
+
193
+ // Add a class to indicate keyboard focus
194
+ currentThumb.classList.add(`${state.component.getClass('slider-thumb')}--focused`);
195
+
196
+ // Show value bubble on focus
197
+ const bubble = isSecondThumb ? secondValueBubble : valueBubble;
198
+ showBubble(bubble);
199
+
200
+ // Update active bubble reference
201
+ state.activeBubble = bubble;
93
202
 
94
203
  // Trigger focus event
95
204
  triggerEvent(SLIDER_EVENTS.FOCUS, e);
96
205
  };
97
206
 
98
207
  const handleBlur = (e, isSecondThumb = false) => {
99
- // Hide value bubble
100
- showValueBubble(isSecondThumb ? secondValueBubble : valueBubble, false);
208
+ // Remove keyboard focus class
209
+ const thumb = isSecondThumb ? secondThumb : state.component.structure.thumb;
210
+ thumb.classList.remove(`${state.component.getClass('slider-thumb')}--focused`);
211
+
212
+ // Only hide the bubble if we're not tabbing to another thumb
213
+ // This check prevents the bubble from flickering when tabbing between thumbs
214
+ const relatedTarget = e.relatedTarget;
215
+ const otherThumb = isSecondThumb ? state.component.structure.thumb : secondThumb;
216
+
217
+ if (!relatedTarget || relatedTarget !== otherThumb) {
218
+ // We're not tabbing to the other thumb, so we can hide the bubble
219
+ const bubble = isSecondThumb ? secondValueBubble : valueBubble;
220
+ hideBubble(bubble, 200);
221
+ }
101
222
 
102
223
  // Trigger blur event
103
224
  triggerEvent(SLIDER_EVENTS.BLUR, e);
@@ -3,7 +3,7 @@ import { SLIDER_COLORS, SLIDER_SIZES } from '../constants';
3
3
  import { SliderConfig } from '../types';
4
4
 
5
5
  /**
6
- * Creates the slider DOM structure following MD3 principles
6
+ * Creates the slider DOM structure following MD3 principles with improved accessibility
7
7
  * @param config Slider configuration
8
8
  * @returns Component enhancer with DOM structure
9
9
  */
@@ -15,6 +15,7 @@ export const withStructure = (config: SliderConfig) => component => {
15
15
  const value = config.value !== undefined ? config.value : min;
16
16
  const secondValue = config.secondValue !== undefined ? config.secondValue : null;
17
17
  const isRangeSlider = config.range && secondValue !== null;
18
+ const isDisabled = config.disabled === true;
18
19
 
19
20
  // Helper function to calculate percentage
20
21
  const getPercentage = (val) => ((val - min) / range) * 100;
@@ -41,10 +42,19 @@ export const withStructure = (config: SliderConfig) => component => {
41
42
  const valueBubble = createElement('slider-value');
42
43
  valueBubble.textContent = formatter(value);
43
44
 
44
- // Create thumb element
45
+ // Create thumb element with improved accessibility attributes
45
46
  const thumb = createElement('slider-thumb');
46
- thumb.setAttribute('tabindex', '0');
47
47
  thumb.setAttribute('role', 'slider');
48
+ thumb.setAttribute('aria-valuemin', String(min));
49
+ thumb.setAttribute('aria-valuemax', String(max));
50
+ thumb.setAttribute('aria-valuenow', String(value));
51
+ thumb.setAttribute('aria-orientation', 'horizontal');
52
+
53
+ // Set tabindex based on disabled state
54
+ thumb.setAttribute('tabindex', isDisabled ? '-1' : '0');
55
+ if (isDisabled) {
56
+ thumb.setAttribute('aria-disabled', 'true');
57
+ }
48
58
 
49
59
  // Set initial thumb position
50
60
  thumb.style.left = `${valuePercent}%`;
@@ -60,8 +70,17 @@ export const withStructure = (config: SliderConfig) => component => {
60
70
 
61
71
  if (isRangeSlider) {
62
72
  secondThumb = createElement('slider-thumb');
63
- secondThumb.setAttribute('tabindex', '0');
64
73
  secondThumb.setAttribute('role', 'slider');
74
+ secondThumb.setAttribute('aria-valuemin', String(min));
75
+ secondThumb.setAttribute('aria-valuemax', String(max));
76
+ secondThumb.setAttribute('aria-valuenow', String(secondValue));
77
+ secondThumb.setAttribute('aria-orientation', 'horizontal');
78
+
79
+ // Set tabindex based on disabled state
80
+ secondThumb.setAttribute('tabindex', isDisabled ? '-1' : '0');
81
+ if (isDisabled) {
82
+ secondThumb.setAttribute('aria-disabled', 'true');
83
+ }
65
84
 
66
85
  const secondPercent = getPercentage(secondValue);
67
86
  secondThumb.style.left = `${secondPercent}%`;
@@ -80,6 +99,14 @@ export const withStructure = (config: SliderConfig) => component => {
80
99
 
81
100
  // Add elements to the slider
82
101
  component.element.classList.add(component.getClass('slider'));
102
+
103
+ // Accessibility enhancement: Container is not focusable
104
+ component.element.setAttribute('tabindex', '-1');
105
+
106
+ // Set container aria attributes
107
+ component.element.setAttribute('role', 'none');
108
+ component.element.setAttribute('aria-disabled', isDisabled ? 'true' : 'false');
109
+
83
110
  component.element.appendChild(track);
84
111
  component.element.appendChild(ticksContainer); // Add ticks container
85
112
  component.element.appendChild(startDot);
@@ -217,7 +244,7 @@ export const withStructure = (config: SliderConfig) => component => {
217
244
  }
218
245
 
219
246
  // Apply disabled class if needed
220
- if (config.disabled) {
247
+ if (isDisabled) {
221
248
  component.element.classList.add(`${baseClass}--disabled`);
222
249
  }
223
250
  }
@@ -180,15 +180,25 @@ export const createUiHelpers = (config: SliderConfig, state) => {
180
180
  const adjustedLower = mapValueToVisualPercent(lowerPercent, edgeConstraint) + paddingPercent;
181
181
  const adjustedHigher = mapValueToVisualPercent(higherPercent, edgeConstraint) - paddingPercent;
182
182
 
183
- let trackLength = Math.max(0, adjustedHigher - adjustedLower);
184
- if (higherPercent - lowerPercent < paddingPercent * 2) {
185
- trackLength = Math.max(0, higherPercent - lowerPercent);
186
- }
183
+ // Calculate the actual percentage difference between thumbs
184
+ const valueDiffPercent = Math.abs(higherPercent - lowerPercent);
187
185
 
188
- activeTrack.style.display = 'block';
189
- activeTrack.style.width = `${trackLength}%`;
190
- activeTrack.style.left = `${adjustedLower}%`;
191
- activeTrack.style.height = '100%';
186
+ // Define a threshold below which we'll hide the active track
187
+ // This threshold is based on the thumb width plus some margin
188
+ const hideThreshold = (thumbSize / trackSize) * 100;
189
+
190
+ if (valueDiffPercent <= hideThreshold) {
191
+ // Thumbs are too close together, hide the active track
192
+ activeTrack.style.display = 'none';
193
+ } else {
194
+ // Normal display of active track
195
+ let trackLength = Math.max(0, adjustedHigher - adjustedLower);
196
+
197
+ activeTrack.style.display = 'block';
198
+ activeTrack.style.width = `${trackLength}%`;
199
+ activeTrack.style.left = `${adjustedLower}%`;
200
+ activeTrack.style.height = '100%';
201
+ }
192
202
  } else {
193
203
  // Single thumb slider
194
204
  const valuePercent = getPercentage(state.value);