The Slider
component allows a user to slide a single thumb along a horizontal or vertical axis, representing a min/max range.
2DSlider
- allows 2-dimensional sliding, used for color picking or 2d panning.
https://codesandbox.io/s/sliders-xi0zw
Fabric Slider docs
Stardust Slider docs
Material UI Slider docs
BaseUI Slider docs
Chakra Slider docs
Carbon Slider docs
AntD Slider docs
TODO: Consult the prop wizard to derive consistently defined props.
Name | Type | Default value |
---|
Name | Type |
---|---|
as | string |
className | string |
defaultValue | number | number[] |
disabled | boolean |
marks | { value: number; label: string; size: 's' | 'm' | 'l' }[] |
max | number |
min | number |
name | The form name, is injected on the hidden input element. |
onChange | (ev: Event, value: number) => void |
originFromZero | boolean |
step | number |
value | number | number[] |
vertical | boolean |
To be discussed:
Name | Concern |
---|---|
label/errorDescription | Should be a concern of Form |
ariaLabel/getA11yValueMessageOnChange | Should this be in accessibility ? |
https://developer.microsoft.com/en-us/fabric#/controls/web/slider
Name | Type | Notes |
---|---|---|
ariaLabel | string | |
ariaValueText | (value: number) => string | |
buttonProps | React.HTMLAttributes | |
className | string | |
componentRef | RefObject | |
defaultValue | number | |
disabled | boolean | |
label | string | |
max | number | |
min | number | |
onChange | (value: number) => void | This is the wrong signature and should be resolved to be (ev, number) |
onChanged | (ev, number) => void | This is the correct signature but the wrong prop name. Should be removed |
originFromZero | boolean | Deprecate in favor of marks . |
showValue | boolean | Note it defaults to true, which should violate a naming convention. valueHidden might be a better name |
snapToStep | boolean | Consider deprecating; this has no value. |
step | number | The difference between the two adjacent values of the Slider |
styles | IStyleFunctionOrObject<ISliderStyleProps, ISliderStyles> | Should be deprecated in favor of recomposition. |
theme | ITheme | Should not show up in the public props contract. |
value | number | |
valueFormat | (value: number) => string | Could be depreacted; consider slots override? |
vertical | boolean |
Name | Type | Notes |
---|---|---|
accessibility | "sliderBehavior" any | Why would a user need this as a prop? |
animation | AnimationProp | Why would a user need this as a prop on slider? |
as | React.ElementType | |
className | string | |
defaultValue | string | number | |
design | ComponentDesign | What is the use case for this? |
fluid | boolean | Stretching should be the default, this is unneeded. |
getA11yValueMessageOnChange | "getA11yValueMessageOnChange" function | This is for specify "aria-valuetext", when value cannot be meaningfully represented by a number. https://www.w3.org/TR/wai-aria-1.1/#aria-valuetext |
input | ShorthandValue | Prop polution. |
inputRef | Ref | When you can't use an input what then? |
max | string | number | |
min | string | number | |
onChange | ComponentEventHandler | Great, this is what we want :) |
step | string | number | |
styles | ComponentSlotStyle | Consider only recomposition |
value | string | number | |
variables | any | Consider only recomposition |
vertical | boolean |
Name | Fx | Recommendation |
---|---|---|
accessibility | S | Discuss: In its current state it's unclear why a user would ever pass an accessibility behavior in. This is different from every framework. How does this help developers? |
animation | S | Remove: Why would a user need this as a prop on Slider? Also ambiguous; animation for slider movement or just css on the root? |
ariaLabel | F | Discuss: Without having the user read aria specs, can we abstract this into screenReaderDescription or accessibility.description ? At minimum we need an example of what should go here. |
ariaValueText | F | ariaLabel set label for the slider, the ariaValueText set string instead of slider number value |
buttonProps | F | Replace: Use slotProps/slots. |
componentRef | F | Discuss: Slider imperative API to set focus and read value. Do we need these? |
design | S | Remove: What is the use case for this? |
fluid | S | Replace: Stretch/fill continer should be the default no? |
getA11yValueMessageOnChange | S | Discuss: Unclear what this specifically does; aria-live polite? |
input | S | Replace with slotProps/slots and/or specific props. |
inputRef | S | Use case unclear. Can we remove this? |
label | F | Potentially we could remove this if there is a viable alternative; Form, for example. |
originFromZero | F | Add: valuable use case. |
showValue | F | Note it defaults to true, which should violate a naming convention. valueHidden might be a better name |
styles | F | Remove: this causes perf problems. Replace with recomposition. |
theme | F | Remove: makes API surface muddy. |
valueFormat | F | Discuss: Could be replaced with slot/slot props. |
variables | S | Remove: this causes perf problems. Replace with recomposition. |
Props being changed:
TODO
Props being removed:
TODO
Name | Considerations |
---|---|
root | |
thumb | |
rail | The line behind the slider thumb and rail. |
track | The selected area of the slider. |
mark | Optional mark. |
markLabel | |
tooltip | The tooltip rendered above a thumb. |
General considerations:
- Default should fill container horizontally; the track should touch the edges.
- The thumb may extend beyond the boundaries, but should be compensated for in the container to avoid clipping.
- Focus is set on the thumb, allows for mult range slider selection.
<div class="root">
<div class="rail"></div>
<div class="mark">
<label class="markLabel"></label>
</div>
<div class="track"></div>
<div class="thumb" tabindex="0" role="slider" aria-valuenow="0" aria-valuemin="0" aria-valuemax="10"></div>
<input name="{name}" type="hidden" value="0" />
</div>
- Focus is on the container
- Unneeded extra
div
wrapping the rail - Current impl missing "marks"
- No tooltip support
- no input
<div
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="10"
aria-label="Controlled example"
aria-disabled="false"
class="ms-Slider-slideBox ms-Slider-showValue ms-Slider-showTransitions"
id="Slider187"
role="slider"
tabindex="0"
data-is-focusable="true"
>
<div class="ms-Slider-line">
<span class="ms-Slider-thumb" style="left: 0%;"></span>
<span class="ms-Slider-active activeSection-243" style="width: 0%;"></span>
<span class="ms-Slider-inactive inactiveSection-244" style="width: 100%;"></span>
</div>
</div>
- Extra wrapper div
- input element to receive focus
- No tooltip on thumb
<div class="ui-slider" aria-disabled="false">
<div class="ui-slider__input-wrapper">
<span class="ui-slider__rail"></span>
<span class="ui-slider__track" style="width: 50%;"></span>
<input
aria-orientation="horizontal"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50"
aria-valuetext="50"
min="0"
max="100"
step="1"
type="range"
class="ui-box ui-slider__input"
value="50"
/>
<span class="ui-slider__thumb" style="left: 50%;"></span>
</div>
</div>
- focus on thumb, allows for multi range slider
- input element purpose unclear; (type=hidden?) could be for form support
<span class="MuiSlider-root MuiSlider-colorPrimary">
<span class="MuiSlider-rail"></span>
<span class="MuiSlider-track" style="left: 0%; width: 20%;"></span>
<input type="hidden" value="20" />
<span
class="MuiSlider-thumb MuiSlider-thumbColorPrimary"
tabindex="0"
role="slider"
data-index="0"
aria-label="custom thumb label"
aria-orientation="horizontal"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="20"
style="left: 20%;"
></span>
</span>
Aria spec: https://www.w3.org/TR/wai-aria-1.1/#slider
Fluent UI HIG: https://microsoft.sharepoint-df.com/:w:/t/OPGUXLeads/EWgxpDSGgEVInkHf9qIT8tEB9ukfQaYzXbLqc97M_3wDqw?e=d8rC23
A disabled slider does not allow the user to change the value. All events will be ignored.
Typically disabled browser elements do now allow focus. This makes the control difficult for a blind user to know about it, or why it's disabled, without scanning the entire page. Therefore it is recommended to allow focus on disabled components and to make them readonly. This means we use aria-disabled
attributes, and not disabled
attributes, for defining a disabled state. This may sometimes require special attention to ignoring input events in the case a browser element might do something.
Focus indicators should not show in mouse or touch interaction; they should only appear when keyboard tabbing/directional keystrokes are pressed, and should disappear when mouse/touch interactions occur.
Assume left/right keyboard handling flips in RTL, unless specified.
Key | Description |
---|---|
Up/Right | Increments the value of the slider by the amount specified by the step prop. If the shift modifier is pressed, increases by 10x the step value. |
Down/Left | Decrements the value of the slider by the amount specified by the step prop. If the shift modifier is pressed, increases by 10x the step value. |
PageUp | Increments by 10x step . Behaves as shift up/right. |
PageDown | Decrements by 10x step . Behaves as shift down/left. |
Home | Reduces the value to the amount specified by the min prop. |
End | Increases the value to the amount specified in the max prop. |
Test: Possible to use this to capture mouse, though Safari does not have compatibility: https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
mousedown
should immediately attempt to set value to the appropriate value.mousemove
should be attached to the window during mousedown to track window-wise move events. Value should be updated appropriately. Note; if amousemove
occurs without the primary button pressed, tracking should cancel and treat the even as amouseup
. (Edge case: mouse down, move cursor out of window, release mouse)mouseup
should remove themousemove
event.
The same behavior as above, except that the events should concern
From accessibility point of view the main difference is in type of element used for Slider itself:
- Fluent UI has
<div>
element with role="slider" - Fluent UI Northstar has
<input>
element
The difference above high probably causing different results during screen reader verification:
- Fluent UI slider behaves sometimes not predictable with JAWS in virtual cursor mode
- Fluent UI slider doesn't interact with VoiceOver and Safari browser. Neither common keyboard keys, nor VoiceOver keys.
- Fluent UI slider doesn't interact with VoiceOver and Chrome while using VoiceOver keys, but this difference is not huge with comparing Fluent UI Northstar
- horizontal
- vertical
- no numeric slider
- disabled
- should render the native element using the
as
prop, defaulting todiv
- should mix in native props expected for the element type defined in
as
.
The thumb
slot:
- should be focusable via
tabindex=0
- role set to
slider
- receives
aria-valuemin
andaria-valuemax
representing min/max - receives
aria-valuenow
representing the current value - receives
aria-orientation="vertical"
representing vertical slider - receives
aria-disabled="true"
representing disabled slider - receives callback function which sets
aria-valuetext=STRING
The Slider
uses react-texture
to provide a recomposable implementation that has no runtime performance penalties. The BaseSlider
implementation can be used to provide new slots
and default props
:
const FooSlider = BaseSlider.compose({
tokens: {},
styles: {},
slots: {}
});
render() {
<FooSlider min={-100} max={100} centerValue={0}>
}
1 per slot 1 per state, tagged on root
Tokens represent the general look and feel of the various visual slots. Tokens feed into the styling at the right times in the right slot.
Regarding naming conventions, use a camelCased name following this format:
{slot}{property}{state (or none for default)}
. For example:thumbSizeHovered
.Common property names:
size
,background
,color
,borderRadius
Common states:
hovered
,pressed
,focused
,checked
,checkedHovered
,disabled
Name | Considerations |
---|---|
railBorderColor | |
railBorderRadius | |
railBorderWidth | |
railColor | |
railColorDisabled | |
railColorFocused | |
railColorHovered | |
railColorPressed | |
railSize | |
thumbBorderColor | |
thumbBorderRadius | |
thumbBorderWidth | |
thumbColor | |
thumbColorDisabled | |
thumbColorFocused | |
thumbColorHovered | |
thumbColorPressed | |
thumbSize | |
trackBorderColor | |
trackBorderRadius | |
trackBorderWidth | |
trackColor | |
trackColorDisabled | |
trackColorFocused | |
trackColorHovered | |
trackColorPressed | |
trackSize |
NOTE! Stardust does not follow this convention. Slider currently uses these tokens:
activeThumbColor: string
activeThumbHeight: string
activeThumbWidth: string
disabledRailColor: string
disabledThumbColor: string
disabledTrackColor: string
height: string
length: string
railColor: string
railHeight: string
thumbBorderPadding: string
thumbColor: string
thumbHeight: string
thumbWidth: string
trackColor: string