@hackplan/polaris
Version:
Shopify’s product component library
345 lines (257 loc) • 8.19 kB
Markdown
---
name: Range slider
category: Forms
platforms:
- android
- ios
- web
keywords:
- RangeSlider
- input
- range
- slider
- percent
- number
- range form
---
# Range slider
A range slider is an input field that merchants can use to select a numeric value within a given range (minimum and maximum values).
---
## Best practices
Range sliders should:
- Always be used with a label, even if that label is `hidden`.
- When a label is visible, it should clearly communicate the purpose of the range input and its values (min, max, step, value)
- Be labeled as “Optional” when you need to request input that’s not required
- Validate input as soon as merchants have finished interacting with a field (but not before)
- Always be used with two text field components when range slider has dual thumbs, to provide accessible alternatives to both the lower and upper thumbs
---
## Content guidelines
### Range label
A label is a short description of the requested input. Labels are not instructional text but they should be meaningful and clearly indicate what is expected. Labels should be:
- Placed above the form field
- Short and succinct (1–3 words)
- Written in sentence case (the first word capitalized, the rest lowercase)
<!-- usagelist -->
#### Do
- Saturation percentage
- Banner width
#### Don’t
- What is the saturation value?
- The banner width is:
<!-- end -->
### Designating optional fields
Try to only ask for information that’s required. If you need to ask merchants
to provide optional information, mark the field optional by placing the text “(optional)” at the end of the field’s label. Don’t mark required fields with asterisks.
<!-- usagelist -->
#### Do
- Banner width (optional)
#### Don’t
- Banner width
<!-- end -->
### Help text
Help text provides extra guidance or instruction to people filling out a form field. It can also be used to clarify how the information will be used. As with all form content, help text should be succinct and easy to read.
<!-- usagelist -->
#### Do
- Video duration is calculated in seconds
#### Don’t
- Example: 134 seconds
<!-- end -->
### Validation error messages
Error messages should:
- Clearly explain what went wrong and how to fix it
- Be short and concise, no more than a single sentence
- Use [passive voice](/content/grammar-and-mechanics) so merchants don’t feel like they’re being blamed for the error
<!-- usagelist -->
#### Do
- Video duration is required
#### Don’t
- You didn’t enter a duration
<!-- end -->
---
## Examples
### Default range slider
Use range sliders where merchants may need to select a percentage between `0 — 100`.
```jsx
class RangeSliderExample extends React.Component {
state = {
value: 32,
};
handleChange = (value) => {
this.setState({value});
};
render() {
return (
<Card sectioned>
<RangeSlider
label="Opacity percentage"
value={this.state.value}
onChange={this.handleChange}
output
/>
</Card>
);
}
}
```
<!-- content-for: android -->

<!-- /content-for -->
<!-- content-for: ios -->

<!-- /content-for -->
### More precise range control
<!-- example-for: web -->
For a more precise value, you can define a `min` and `max` range, as well as the amount with which the slider will be incremented.
```jsx
class RangeSliderExample extends React.Component {
state = {
value: 5,
};
handleChange = (value) => {
this.setState({value});
};
render() {
return (
<Card sectioned>
<RangeSlider
label="Logo offset"
min={-10}
max={10}
step={5}
value={this.state.value}
onChange={this.handleChange}
/>
</Card>
);
}
}
```
### Prefix and suffix elements
<!-- example-for: web -->
Because a range slider can also output a `label` and `helpText`, the height of the overall component can vary. `prefix` and `suffix` props allow you to pass in a React element to be placed before or after the rendered `input`, allowing for perfect vertical alignment and easier stylistic control.
```jsx
class RangeSliderExample extends React.Component {
state = {
value: 100,
};
handleChange = (value) => {
this.setState({value});
};
render() {
const suffixStyles = {
minWidth: '24px',
textAlign: 'right',
};
return (
<Card sectioned>
<RangeSlider
label="Hue color mix"
min={0}
max={360}
value={this.state.value}
onChange={this.handleChange}
prefix={<p>Hue</p>}
suffix={<p style={suffixStyles}>{this.state.value}</p>}
/>
</Card>
);
}
}
```
### Dual thumb range slider
Use a dual thumb range slider when merchants need to select a range of values.
```jsx
const initialValue = [900, 1000];
class RangeSliderExample extends React.Component {
state = {
intermediateTextFieldValue: initialValue,
value: initialValue,
};
handleRangeSliderChange = (value) => {
this.setState({value, intermediateTextFieldValue: value});
};
handleLowerTextFieldChange = (value) => {
const upperValue = this.state.value[1];
this.setState({intermediateTextFieldValue: [parseInt(value), upperValue]});
};
handleUpperTextFieldChange = (value) => {
const lowerValue = this.state.value[0];
this.setState({intermediateTextFieldValue: [lowerValue, parseInt(value)]});
};
handleLowerTextFieldBlur = () => {
const upperValue = this.state.value[1];
const value = this.state.intermediateTextFieldValue[0];
this.setState({value: [parseInt(value), upperValue]});
};
handleUpperTextFieldBlur = () => {
const lowerValue = this.state.value[0];
const value = this.state.intermediateTextFieldValue[1];
this.setState({value: [lowerValue, parseInt(value)]});
};
handleEnterKeyPress = (event) => {
const newValue = this.state.intermediateTextFieldValue;
const oldValue = this.state.value;
if (event.keyCode === Key.Enter && newValue !== oldValue) {
this.setState({value: newValue});
}
};
render() {
const {value, intermediateTextFieldValue} = this.state;
const prefix = '$';
const min = 0;
const max = 2000;
const step = 10;
const lowerTextFieldValue =
intermediateTextFieldValue[0] === value[0]
? value[0]
: intermediateTextFieldValue[0];
const upperTextFieldValue =
intermediateTextFieldValue[1] === value[1]
? value[1]
: intermediateTextFieldValue[1];
return (
<Card sectioned>
<div style={{marginTop: '20px'}} onKeyDown={this.handleEnterKeyPress}>
<RangeSlider
label="Money spent is between"
value={value}
prefix={prefix}
output
min={min}
max={max}
step={step}
onChange={this.handleRangeSliderChange}
/>
<Stack distribution="equalSpacing" spacing="extraLoose">
<TextField
label="Min money spent"
type="number"
value={`${lowerTextFieldValue}`}
prefix={prefix}
min={min}
max={max}
step={step}
onChange={this.handleLowerTextFieldChange}
onBlur={this.handleLowerTextFieldBlur}
/>
<TextField
label="Max money spent"
type="number"
value={`${upperTextFieldValue}`}
prefix={prefix}
min={min}
max={max}
step={step}
onChange={this.handleUpperTextFieldChange}
onBlur={this.handleUpperTextFieldBlur}
/>
</Stack>
</div>
</Card>
);
}
}
```
---
## Related components
- To collect a number value as a text input, [use the text field component](/components/forms/text-field)