Controls
Big, touch-friendly controls for operating a device, like the toggles and sliders in the more-info dialogs.
The control family is made for operating a device by hand, with a finger as much as with a mouse. The controls are large, have big hit areas, and react to dragging and swiping, not just tapping. You find them in two places:
- In the more-info dialogs, where one big control takes center stage: the vertical brightness slider of a light, the round thermostat dial, the toggle of a switch.
- In the features of the tile card, where a smaller, horizontal version sits below the entity, like a brightness slider or a row of mode buttons.
Use a control for:
- The main job of a screen being to change the state of a device
- A feature of a tile card
Don't use a control for:
- Forms and settings. Use the regular form controls instead, like a button, a switch, or a slider. A control from this family in a settings form looks out of place, and takes far more room than it needs.
- Secondary settings of a device with many options. Use a select menu.
All controls share the same look:
- Track: the rounded shape, with a light tint of the color
- Active part: the part in full color, like the fill of a slider, the knob of a switch, or the selected option
- Content: an icon, a value, or a label, depending on the control
Their color can be set, so it matches the state of the entity, like the color of a light.
A square button with an icon, for a single action like Open, Stop, or Lock. It is the building block for rows of actions, like the open, stop, and close buttons of a cover.
The frontend has a separate group component to put control buttons in a row or a column. It is not available in these demos.
A big on and off switch. Use it for the main toggle of a device. In the more-info dialog it stands upright and is reversed, so on is at the top. In a tile card feature it lies flat.
A thick slider for a value in a range, like brightness, volume, or the position of a cover. While someone drags it, a tooltip shows the value.
The mode sets how the value is drawn:
- Start (default): fills from the start. Use it for an amount, like brightness.
- End: fills from the end. The cover position slider uses it, so the bar comes down from the top, like the cover itself.
- Cursor: shows only a marker, no fill. Use it when there is no "more" or "less", like a color temperature or a tilt position.
The horizontal version is what tile card features use:
A round dial, open at the bottom, for a target value like a temperature or humidity. It can also show the current value as a small dot on the track, so people see where they are and where they are going.
The dial itself shows no text. In the more-info dialogs, the target and current value are printed in the middle of it by the surrounding component.
The mode sets which part of the track is filled, just like the slider. The thermostat fills from the start when heating, from the end when cooling, and the whole track when the direction does not matter. In the dual version, the dial has two handles, a low and a high one, for a range like heating and cooling at once.
A value with a minus and a plus button, for small, precise steps. The target temperature feature of the tile card uses it. Use it when people adjust a value by a step or two, rather than jumping across the range.
A row, or a column, of options where one is selected, like the speed of a fan or the mode of an alarm panel. Each option shows an icon, a label, or both. The selected option is filled with the color.
Use it for a handful of options that people switch between often. In the more-info dialog it stands upright, with one option per step. For secondary settings with more options, like the preset or swing mode of a thermostat, the more-info dialogs use a select menu instead.
The options can only be set from code, so there is no live demo here.
- Hover: the tint of the control switch track gets stronger, from 20% to 40%. An option of a control select gets a 20% tint of the color.
- Focus: keyboard focus shows a 2px ring around the control. The control switch, slider, and select use their own color for the ring. The control button and number buttons use the secondary text color. The circular slider shows focus by making the focused arc thicker, from 24px to 28px.
- Pressed: while someone drags a slider, the tooltip with the value shows. It hides when they let go. With the keyboard, it shows while a key is pressed, and hides half a second after the key is released.
- Swiping: the control switch turns on or off with a swipe in its direction, and toggles with a tap.
- Disabled: each control handles it in its own way:
- The control button turns its icon to the disabled text color, on a 20% tint of the disabled color.
- The control switch is dimmed to 50% opacity.
- The control select turns its text and selected option to the disabled color.
- The number buttons turn their text to the disabled color. The minus and plus buttons are dimmed to 40%.
- The circular slider only shows its track and the dot of the current value, without the colored arcs and the handles.
- The control slider ignores dragging and tapping, but has no separate look.
- Limits: the minus and plus buttons disable themselves at the minimum and the maximum. When the number buttons get narrower than 100 pixels, the unit is hidden.
- Inactive: for a device that is off, the circular slider can hide the colored arc.
- Read only: a read only circular slider shows its value, but ignores dragging and tapping. The keyboard still changes the value.
Most transitions take 180ms, ease-in-out. The arcs of the circular slider move in 300ms.
All controls fill their container. The defaults below are what you get without setting a size.
| Control | Default size | Corner radius | Token |
|---|---|---|---|
| Control button | 40px by 40px, 8px padding | 8px | --ha-border-radius-md, through --control-button-border-radius |
| Control switch | 40px thick | 12px | --ha-border-radius-lg, through --control-switch-border-radius |
| Control slider | 40px thick | 8px | --ha-border-radius-md, through --control-slider-border-radius |
| Control circular slider | 320px wide | Round track ends | None |
| Control number buttons | The height of a card feature, buttons 35px wide | 8px | --ha-border-radius-md, through --control-number-buttons-border-radius |
| Control select | 40px thick, 4px padding | 10px | --control-select-border-radius |
In the more-info dialogs, the vertical controls are 130 pixels thick, up to 320 pixels tall, with 36px corners (--ha-border-radius-6xl). In tile card features, the horizontal controls are 42 pixels thick. A feature inline next to the icon of a tile card keeps the height of the icon, 36 pixels, unless the card reserves a row for it.
| Part | Color | Token |
|---|---|---|
| Track tint | The disabled color at 20% opacity (30% for the circular slider) | --disabled-color |
| Active part | The primary color | --primary-color, through the color property of each control |
| Icon of a control button | The primary text color | --primary-text-color, through --control-button-icon-color |
| Knob icon of the control switch | White | None |
| Text of the selected option of a control select | White | None |
| Handle of the slider and the circular slider | White | None |
| Current value dot of the circular slider | The primary text color at 50% opacity | --primary-text-color |
| Slider tooltip | Clear background, primary text color, 12px corners | --clear-background-color, --ha-border-radius-lg |
| Property | Value | Token |
|---|---|---|
| Icon size | 20px, 16px for the number buttons | --mdc-icon-size |
| Switch knob | Half of the track, inset by 4px | --control-switch-padding |
| Minimum touch area of the switch | 40px | --control-switch-min-touch-size |
| Circular slider track | 24px wide, 270 degrees, open at the bottom | None |
| Extra hit area around the circular slider | 12px | --control-circular-slider-interaction-margin |
| Slider handle | 4px wide, with show-handle |
None |
Give every control a label. Most of them show only an icon or a value, and the label is what screen readers announce.
| Control | Keys |
|---|---|
| Control button | Tab to focus, Enter or Space to select. It is a native button. |
| Control switch | Enter or Space toggles. The arrow keys in its direction turn it on or off, following which way the switch is turned and the text direction. |
| Control slider | Arrow keys move one step. Page up and Page down move 10% of the range. Home and End go to the minimum and the maximum. In a right to left language, left and right are swapped. |
| Control circular slider | The same keys as the control slider, on each handle. |
| Control number buttons | The same keys as the control slider. The minus and plus buttons are not in the tab order: the keyboard works on the value. |
| Control select | Tab focuses the selected option, or the first one when none is selected. The arrow keys move focus between options, and Enter or Space selects the focused one. |
Only the control slider swaps the left and right arrow keys in a right to left language. The circular slider and the number buttons do not.
Keyboard focus shows a 2px ring around the control, or a thicker arc on the circular slider. See behavior for the colors.
A disabled control switch or number buttons leaves the tab order. A disabled control select keeps one option in the tab order, but ignores keys and selection. The control slider stays focusable when disabled, and keeps reacting to the keyboard.
The control button, switch, slider, and select are 40px in their smallest direction by default. The control switch keeps a touch area of at least 40px by 40px. The circular slider adds 12px of hit area around its track.
Every control is announced by its label, with its kind and its value: a button, a switch that is on or off, a slider with its formatted value, a spin button with the value and its unit, or a group of radio buttons with the selected option. The minus and plus buttons of the number buttons are announced in English, as "decrement" and "increment". On the dual circular slider, both handles are announced with the low label.
| Control | Role | Name and state |
|---|---|---|
| Control button | button |
label as aria-label, and as a native tooltip |
| Control switch | switch |
label as aria-label, aria-checked. The knob and its icon are hidden from screen readers. |
| Control slider | slider |
label as aria-label, aria-valuenow, aria-valuemin, aria-valuemax, aria-orientation, and aria-valuetext with the formatted value |
| Control circular slider | slider on each handle |
low-label, or label when there is no low-label, as aria-label on both handles (high-label is not used), aria-valuenow from value (with dual, there is none), aria-valuemin, aria-valuemax, aria-disabled, and aria-readonly |
| Control number buttons | spinbutton |
label as aria-label, aria-valuenow, aria-valuemin, aria-valuemax, and aria-valuetext with the value and the unit. The minus and plus buttons are named "decrement" and "increment". |
| Control select | radiogroup, with a radio per option |
label names the group. Each option is named by its ariaLabel or label, and has aria-checked. |
- Select menu: the dropdown of the control family
- Control scrubber: the member of the family for ranges that go round, like hue
- Slider, switch, and button: the regular form controls
- Tile card: where the smaller controls show up as features
All controls fill their container by default. Size them with CSS: set height and width on the element, and the thickness custom property where there is one. A vertical control gets its width from the thickness, and needs a height.
In the more-info dialogs, the vertical controls are 130 pixels thick, up to 320 pixels tall, with --ha-border-radius-6xl corners. In tile card features, the horizontal controls are 42 pixels thick.
<ha-control-button label="Open">
<ha-svg-icon path="..."></ha-svg-icon>
</ha-control-button>Attributes:
label: the accessible name, also shown as a native tooltip.disabled: disables the button.
Slots:
- Default: the content, usually an
ha-svg-icon.
Events: listen for the regular click event.
Styling:
--control-button-icon-color: defaults tovar(--primary-text-color).--control-button-background-color: defaults tovar(--disabled-color).--control-button-background-opacity: defaults to0.2.--control-button-border-radius: defaults tovar(--ha-border-radius-md).--control-button-focus-color: the focus ring. Defaults tovar(--secondary-text-color).--control-button-padding: defaults to8px.--control-button-font-weight: for a text label. Defaults tovar(--ha-font-weight-medium).--mdc-icon-size: the icon size. Defaults to20px.
The default size is 40 by 40 pixels.
<ha-control-switch vertical reversed checked label="Light">
<ha-svg-icon slot="icon-on" path="..."></ha-svg-icon>
<ha-svg-icon slot="icon-off" path="..."></ha-svg-icon>
</ha-control-switch>Attributes:
checked: the switch is on.label: the accessible name.vertical: stands the switch upright.reversed: flips the direction. Upright and reversed puts on at the top.disabled: disables the switch.touch-action: the CSStouch-actionfor swiping. Defaults topan-y, orpan-xwhen vertical.
These can only be set as properties:
pathOnandpathOff: SVG path data for the icons. Use these, or the slots.
Slots:
icon-on: the icon when on.icon-off: the icon when off.
Events:
change: the switch was toggled, by selecting, swiping, or the keyboard. Read the new state fromchecked.
Styling:
--control-switch-on-color: defaults tovar(--primary-color).--control-switch-off-color: defaults tovar(--disabled-color).--control-switch-background-opacity: the tint of the track. Defaults to0.2.--control-switch-hover-background-opacity: the tint on hover. Defaults to0.4.--control-switch-thickness: defaults to40px.--control-switch-min-touch-size: defaults to40px.--control-switch-border-radius: defaults tovar(--ha-border-radius-lg).--control-switch-padding: the space around the knob. Defaults to4px.--mdc-icon-size: the icon size. Defaults to20px.
<ha-control-slider vertical value="60" unit="%" label="Brightness"></ha-control-slider>Attributes:
value: the current value.min: defaults to0.max: defaults to100.step: defaults to1.label: the accessible name.unit: shown after the value in the tooltip.mode:start(default),end, orcursor.vertical: stands the slider upright.inverted: flips the direction of the values.show-handle: shows a handle at the end of the fill.tooltip-mode:interaction(default) shows the value while dragging,alwayskeeps it visible,neverhides it.tooltip-position:top,bottom,left, orright. Defaults totop, orleftwhen vertical.round-value: rounds the shown and announced value to a whole number, while the handle still snaps tostep. Use it whenstepis fractional, but the value is a whole number in practice.touch-action: the CSStouch-action. Defaults topan-y, orpan-xwhen vertical.disabled: disables the slider.
The locale property, used to format the value, can only be set from code.
Slots:
background: content drawn on the track, behind the fill. The color temperature slider uses it for its gradient.
Events:
value-changed: the value was set, on release.detail.valueis the new value.slider-moved: the handle moves while dragging.detail.valueis the value under the handle, andundefinedon release.
Styling:
--control-slider-color: the fill. Defaults tovar(--primary-color).--control-slider-background: the track. Defaults tovar(--disabled-color).--control-slider-background-opacity: defaults to0.2.--control-slider-thickness: defaults to40px.--control-slider-border-radius: defaults tovar(--ha-border-radius-md).--control-slider-inset-shadow: defaults tonone.--control-slider-tooltip-font-size: defaults tovar(--ha-font-size-m).
<ha-control-circular-slider value="21" current="19" min="10" max="30" step="0.5" label="Target temperature"></ha-control-circular-slider>
<ha-control-circular-slider dual low="19" high="24" min="10" max="30" low-label="Heat to" high-label="Cool to"></ha-control-circular-slider>Attributes:
value: the target value, for a single handle.lowandhigh: the two values, withdual.current: the current value, shown as a dot on the track.min: defaults to0.max: defaults to100.step: defaults to1.mode:start,end, orfull. Sets which part of the track is filled.dual: shows two handles, forlowandhigh.label: the accessible name of the handles.low-label: when set, used as the accessible name of both handles instead oflabel.high-label: accepted, but not used.inactive: hides the colored arcs, for a device that is off.readonly: shows the value, but people can't change it with a pointer. The keyboard still works.disabled: disables the dial.prevent-interaction-on-scroll: on touch screens, only the handle reacts, and only after a short press, so swiping past the dial scrolls the page instead of changing the value.
Events, each with the new value in detail.value:
value-changingandvalue-changed: while dragging, and on release.low-changingandlow-changed: the same for the low handle.high-changingandhigh-changed: the same for the high handle.
Styling:
--control-circular-slider-color: the fill. Defaults tovar(--primary-color).--control-circular-slider-low-colorand--control-circular-slider-high-color: the two fills withdual. Default to the color above.--control-circular-slider-background: the track. Defaults tovar(--disabled-color).--control-circular-slider-background-opacity: defaults to0.3.--control-circular-slider-interaction-margin: the extra hit area around the track. Defaults to12px.
The default width is 320 pixels.
<ha-control-number-buttons value="21" min="10" max="30" step="0.5" unit="°C" label="Target temperature"></ha-control-number-buttons>Attributes:
value: the current value.minandmax: the range. Both are optional.step: defaults to1.unit: shown after the value.label: the accessible name.disabled: disables the control.
These can only be set as properties:
locale: the locale data used to format the value.formatOptions:Intl.NumberFormatOptions, for example to always show one decimal.
Events:
value-changed: the value changed, from a button or the keyboard.detail.valueis the new value.
Styling:
--control-number-buttons-background-color: defaults tovar(--disabled-color).--control-number-buttons-background-opacity: defaults to0.2.--control-number-buttons-border-radius: defaults tovar(--ha-border-radius-md).--control-number-buttons-focus-color: the focus ring. Defaults tovar(--secondary-text-color).--mdc-icon-size: the size of the minus and plus icons. Defaults to16px.
The height comes from --feature-height, the height of a tile card feature. Outside a card feature, set a height.
html`<ha-control-select
.options=${[
{ value: "low", label: "Low", path: mdiFanSpeed1 },
{ value: "high", label: "High", path: mdiFanSpeed3 },
]}
.value=${"low"}
.label=${"Fan speed"}
@value-changed=${this._speedChanged}
></ha-control-select>`Attributes:
value: thevalueof the selected option.label: the accessible name of the group.vertical: shows the options in a column.hide-option-label: shows only the icons. The labels are still used as accessible names.disabled: disables the control.
These can only be set as properties:
options: an array of options. Each has avalue, and optionally alabel, anariaLabel, an icon as SVGpath, or anicontemplate.
Events:
value-changed: an option was selected.detail.valueis itsvalue.
Styling:
--control-select-color: the selected option. Defaults tovar(--primary-color).--control-select-background: the track. Defaults tovar(--disabled-color).--control-select-background-opacity: defaults to0.2.--control-select-selected-opacity: defaults to1.--control-select-focused-opacity: defaults to0.2.--control-select-thickness: defaults to40px.--control-select-border-radius: defaults to10px.--control-select-padding: the space around the options. Defaults to4px.--control-select-button-border-radius: the corners of each option. Follows the border radius and padding.--mdc-icon-size: defaults to20px.
Sources: ha-control-button.ts, ha-control-switch.ts, ha-control-slider.ts, ha-control-circular-slider.ts, ha-control-number-buttons.ts, and ha-control-select.ts, all in src/components/.