Skip to content

Commit 7b9c31b

Browse files
author
Melissa Thompson
authored
fix(slider): focus state matches spec, supports forced-colors (#2217)
* fix(slider): focus state doesn't get active state style * fix(slider): high contrast mode works for standard variant * fix(slider): disabled slider gets default cursor * fix(slider): hover, focus in WHCM with prefers-color-scheme * docs(slider): focus and disabled implementations documented * refactor(slider): ramp variant high contrast properties * fix(slider): high contrast highlight when control is interacted with * chore(slider): adjust high contrast mode nesting * chore(slider): is-disabled styles in one place * fix(slider): is-focused class bubbles to controls parent, styles apply in storybook * fix(slider): disabled sliders have disabled input in docs site * chore(slider): add with focus story * fix(slider): keyboard focus works in storybook * chore(slider): forced-color-adjusts go in forced-colors media query
1 parent 5f7037d commit 7b9c31b

File tree

4 files changed

+91
-46
lines changed

4 files changed

+91
-46
lines changed

components/slider/index.css

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ governing permissions and limitations under the License.
4747
--spectrum-slider-label-top-to-text: var(--spectrum-component-top-to-text-100);
4848
--spectrum-slider-control-to-field-label: var(--spectrum-slider-control-to-field-label-large);
4949
--spectrum-slider-value-side-padding-inline: var(--spectrum-spacing-200);
50-
50+
5151
/* TODO: placeholder value for sideLabel variant value width */
5252
--spectrum-slider-value-inline-size: 18px;
5353
}
@@ -316,7 +316,6 @@ governing permissions and limitations under the License.
316316
outline: none;
317317

318318
&:active,
319-
&.is-focused,
320319
&.is-dragged {
321320
border-width: var(--mod-slider-handle-border-width-down, var(--spectrum-slider-handle-border-width-down));
322321
}
@@ -516,22 +515,6 @@ governing permissions and limitations under the License.
516515
}
517516
}
518517

519-
520-
.spectrum-Slider {
521-
&.is-disabled {
522-
cursor: default;
523-
524-
.spectrum-Slider-handle {
525-
cursor: default;
526-
pointer-events: none;
527-
}
528-
529-
.spectrum-Slider-tickLabel {
530-
color: var(--highcontrast-slider-label-text-color-disabled, var(--mod-slider-label-text-color-disabled, var(--spectrum-slider-label-text-color-disabled)));
531-
}
532-
}
533-
}
534-
535518
/* backwards compatibility: these are not required, but they make the handle go to the edegs and align right */
536519
.spectrum-Slider-handleContainer,
537520
.spectrum-Slider-trackContainer {
@@ -609,15 +592,7 @@ governing permissions and limitations under the License.
609592
.spectrum-Slider--ramp {
610593
.spectrum-Slider-handle {
611594
box-shadow: 0 0 0 var(--spectrum-slider-handle-gap) var(--highcontrast-slider-ramp-handle-border-color-active, var(--mod-sectrum-slider-ramp-handle-border-color-active, var(--spectrum-slider-ramp-handle-border-color-active)));
612-
background: var(--mod-slider-ramp-handle-background-color, var(--spectrum-slider-ramp-handle-background-color));
613-
614-
@media (forced-colors: active) {
615-
/* forced-color-adjust required to ensure the "circle" around the handle is transparent */
616-
forced-color-adjust: none;
617-
box-shadow: 0 0 0 var(--spectrum-slider-handle-gap) ButtonFace;
618-
border-color: ButtonText;
619-
background-color: ButtonFace;
620-
}
595+
background: var(--mod-slider-ramp-handle-background-color, var(--highcontrast-slider-ramp-handle-background-color, var(--spectrum-slider-ramp-handle-background-color)));
621596
}
622597
}
623598

@@ -648,13 +623,25 @@ governing permissions and limitations under the License.
648623
}
649624

650625
.spectrum-Slider.is-disabled {
626+
cursor: default;
627+
628+
.spectrum-Slider-controls {
629+
cursor: default;
630+
}
631+
632+
.spectrum-Slider-tickLabel {
633+
color: var(--highcontrast-slider-label-text-color-disabled, var(--mod-slider-label-text-color-disabled, var(--spectrum-slider-label-text-color-disabled)));
634+
}
635+
651636
.spectrum-Slider-labelContainer {
652637
color: var(--highcontrast-slider-label-text-color-disabled, var(--mod-slider-label-text-color-disabled, var(--spectrum-slider-label-text-color-disabled)));
653638
}
654639

655640
.spectrum-Slider-handle {
656641
border-color: var(--highcontrast-slider-handle-border-color-disabled, var(--mod-slider-handle-border-color-disabled, var(--spectrum-slider-handle-border-color-disabled)));
657642
background: var(--highcontrast-slider-handle-disabled-background-color, var(--mod-slider-handle-disabled-background-color, var(--spectrum-slider-handle-disabled-background-color)));
643+
cursor: default;
644+
pointer-events: none;
658645

659646
&:hover,
660647
&:active {
@@ -703,14 +690,20 @@ governing permissions and limitations under the License.
703690
@media (forced-colors: active) {
704691

705692
.spectrum-Slider {
706-
707693
/* over-writes */
708694
--highcontrast-slider-track-color: ButtonText;
709695
--highcontrast-slider-track-fill-color: ButtonText;
710696
--highcontrast-slider-ramp-track-color: ButtonText;
711697
--highcontrast-slider-ramp-track-color-disabled: GrayText;
712698
--highcontrast-slider-tick-mark-color: ButtonText;
713-
699+
--highcontrast-slider-handle-border-color: ButtonText;
700+
--highcontrast-slider-handle-border-color-hover: Highlight;
701+
--highcontrast-slider-handle-border-color-down: Highlight;
702+
--highcontrast-slider-handle-border-color-key-focus: Highlight;
703+
--highcontrast-slider-handle-focus-ring-color-key-focus: CanvasText;
704+
--highcontrast-slider-handle-background-color: ButtonFace;
705+
--highcontrast-slider-ramp-handle-border-color-active: ButtonFace;
706+
--highcontrast-slider-ramp-handle-background-color: ButtonFace;
714707

715708
--spectrum-slider-track-color: ButtonText;
716709
--spectrum-slider-track-fill-color: ButtonText;
@@ -732,21 +725,43 @@ governing permissions and limitations under the License.
732725

733726
--spectrum-slider-track-color-disabled: GrayText;
734727
--spectrum-slider-track-fill-color-disabled: GrayText;
735-
728+
736729
--spectrum-slider-handle-border-color-disabled: GrayText;
737730

738731
--spectrum-slider-label-text-color: CanvasText;
739732
--spectrum-slider-label-text-color-disabled: GrayText;
740733

741734
--spectrum-slider-ramp-handle-border-color-active: ButtonText;
742735

743-
}
736+
.spectrum-Slider-handle.is-focused::before {
737+
forced-color-adjust: none;
738+
}
739+
740+
.spectrum-Slider--ramp .spectrum-Slider-handle {
741+
/* forced-color-adjust required to ensure the "circle" around the handle is transparent */
742+
forced-color-adjust: none;
743+
}
744+
745+
/* Slider control hover and focus colors for high contrast mode */
746+
&:not(.is-disabled) {
747+
.spectrum-Slider-controls:hover,
748+
.spectrum-Slider-controls:active,
749+
.spectrum-Slider-controls:focus-within,
750+
.spectrum-Slider-controls.is-focused {
751+
--highcontrast-slider-track-fill-color: Highlight;
752+
--highcontrast-slider-track-color: Highlight;
753+
--highcontrast-slider-handle-border-color: Highlight;
754+
--highcontrast-slider-ramp-track-color: Highlight;
755+
--highcontrast-slider-tick-mark-color: Highlight;
756+
}
757+
}
744758

745-
/* Slider handle turns transparent in high contrast mode for ramp */
746-
.spectrum-Slider.is-disabled {
747-
.spectrum-Slider-ramp + .spectrum-Slider-handle {
748-
fill: ButtonFace;
749-
background-color: ButtonFace;
759+
/* Slider handle turns transparent in high contrast mode for ramp */
760+
&.is-disabled {
761+
.spectrum-Slider-ramp + .spectrum-Slider-handle {
762+
fill: ButtonFace;
763+
background-color: ButtonFace;
764+
}
750765
}
751766
}
752767
}

components/slider/metadata/slider.yml

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ sections:
1515
1616
### Three Handles is included in the `range` variant
1717
When using a slider with three handles, classify it as a `range` variant to apply correct styling
18+
19+
### Indicating focus
20+
Focus must be bubbled up to the parent so descendants siblings can be styled.
21+
22+
Thus, implementations should add the following class to the `.spectrum-Slider` parent class in the following situations:
23+
24+
* `.is-disabled` - when the slider is disabled
25+
26+
Implementations should also bubble the following class to the `.spectrum-Slider-controls` parent class in the following situations:
27+
28+
* `.is-focused` - when the handle input is focused with the mouse or keyboard
1829
examples:
1930
- id: slider
2031
name: Standard
@@ -118,7 +129,7 @@ examples:
118129
</div>
119130
</div>
120131
</div>
121-
132+
122133
<div class="spectrum-Examples-item" style="width: 200px">
123134
<h4 class="spectrum-Heading spectrum-Heading--sizeXS spectrum-Examples-itemHeading">XL</h4>
124135
<div class="spectrum-Slider spectrum-Slider--sizeXL">
@@ -169,7 +180,7 @@ examples:
169180
<div class="spectrum-Slider-controls">
170181
<div class="spectrum-Slider-track"></div>
171182
<div class="spectrum-Slider-handle" style="left: 40%;">
172-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" disabled id="spectrum-Slider-input-6">
183+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" disabled id="spectrum-Slider-input-6" disabled>
173184
</div>
174185
<div class="spectrum-Slider-track"></div>
175186
</div>
@@ -218,7 +229,7 @@ examples:
218229
<div class="spectrum-Slider-controls">
219230
<div class="spectrum-Slider-track"></div>
220231
<div class="spectrum-Slider-handle" style="left: 40%;">
221-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" disabled id="spectrum-Slider-input-8">
232+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" disabled id="spectrum-Slider-input-8" disabled>
222233
</div>
223234
<div class="spectrum-Slider-track"></div>
224235
</div>
@@ -263,7 +274,7 @@ examples:
263274
<div class="spectrum-Slider-controls">
264275
<div class="spectrum-Slider-track" style="width: 25%;"></div>
265276
<div class="spectrum-Slider-handle" style="left: 70%;">
266-
<input type="range" class="spectrum-Slider-input" value="14" min="10" max="20" id="spectrum-Slider-input-11">
277+
<input type="range" class="spectrum-Slider-input" value="14" min="10" max="20" id="spectrum-Slider-input-11" disabled>
267278
</div>
268279
<div class="spectrum-Slider-track" style="width: 50%;"></div>
269280
<div class="spectrum-Slider-fill spectrum-Slider-fill--right" style="left: 50%; width: 20%"></div>
@@ -299,11 +310,11 @@ examples:
299310
<div class="spectrum-Slider-controls" role="presentation">
300311
<div class="spectrum-Slider-track" style="width: 20%;"></div>
301312
<div class="spectrum-Slider-handle" style="left: 20%;" role="presentation">
302-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" aria-label="min" disabled id="spectrum-Slider-input-13-0" aria-labelledby="spectrum-Slider-label-5 spectrum-Slider-input-5-0">
313+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" aria-label="min" disabled id="spectrum-Slider-input-13-0" aria-labelledby="spectrum-Slider-label-5 spectrum-Slider-input-5-0" disabled>
303314
</div>
304315
<div class="spectrum-Slider-track" style="left: 20%; right: 40%;"></div>
305316
<div class="spectrum-Slider-handle" style="left: 60%;" role="presentation">
306-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" aria-label="max" disabled id="spectrum-Slider-input-13-1" aria-labelledby="spectrum-Slider-label-5 spectrum-Slider-input-5-1">
317+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" aria-label="max" disabled id="spectrum-Slider-input-13-1" aria-labelledby="spectrum-Slider-label-5 spectrum-Slider-input-5-1" disabled>
307318
</div>
308319
<div class="spectrum-Slider-track" style="width: 40%;"></div>
309320
</div>
@@ -355,7 +366,7 @@ examples:
355366
</div>
356367
<div class="spectrum-Slider-handleContainer">
357368
<div class="spectrum-Slider-handle" style="left: 75%;">
358-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" id="spectrum-Slider-input-15">
369+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" id="spectrum-Slider-input-15" disabled>
359370
</div>
360371
</div>
361372
</div>
@@ -434,7 +445,7 @@ examples:
434445
</div>
435446
<div class="spectrum-Slider-handleContainer">
436447
<div class="spectrum-Slider-handle" style="left: 40%;">
437-
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" id="spectrum-Slider-input-17">
448+
<input type="range" class="spectrum-Slider-input" value="14" step="2" min="10" max="20" id="spectrum-Slider-input-17" disabled>
438449
</div>
439450
</div>
440451
</div>

components/slider/stories/slider.stories.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ Disabled.args = {
186186
isDisabled: true,
187187
};
188188

189+
export const WithFocus = Template.bind({});
190+
WithFocus.args = {
191+
...Default.args,
192+
variant: "with focus",
193+
isFocused: true,
194+
};
195+
189196
export const Gradient = Template.bind({});
190197
Gradient.args = {
191198
...Default.args,

components/slider/stories/template.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { html } from "lit";
22
import { classMap } from "lit/directives/class-map.js";
3-
import { styleMap } from "lit/directives/style-map.js";
43
import { ifDefined } from "lit/directives/if-defined.js";
4+
import { styleMap } from "lit/directives/style-map.js";
55

66
import { useArgs, useGlobals } from "@storybook/client-api";
77

@@ -142,6 +142,14 @@ export const Template = ({
142142
})}
143143
role=${ifDefined(values.length > 1 ? "group" : undefined)}
144144
aria-labelledby=${ifDefined(label && id ? `${id}-label` : undefined)}
145+
@focusin=${() => {
146+
const focusClass = { isFocused: true };
147+
updateArgs(focusClass);
148+
}}
149+
@focusout=${() => {
150+
const focusClass = { isFocused: false };
151+
updateArgs(focusClass);
152+
}}
145153
>
146154
<!-- Label region -->
147155
${label
@@ -175,7 +183,11 @@ export const Template = ({
175183
176184
<!-- Slider controls -->
177185
<div
178-
class="${rootClass}-controls"
186+
class=${classMap({
187+
[`${rootClass}-controls`]: true,
188+
"is-focused": isFocused,
189+
...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}),
190+
})}
179191
role=${ifDefined(isRamp ? "presentation" : undefined)}
180192
>
181193
${values.map((value, idx) => {

0 commit comments

Comments
 (0)