Improve aria support in control elements

This commit is contained in:
Paul Bottein 2025-07-07 19:32:03 +02:00
parent b60f2e3201
commit 0b45924d10
No known key found for this signature in database
31 changed files with 148 additions and 166 deletions

View File

@ -135,7 +135,7 @@ export class DemoHaControlSelect extends LitElement {
.options=${options}
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
aria-labelledby=${id}
.label=${label}
?disabled=${config.disabled}
>
</ha-control-select>
@ -156,7 +156,7 @@ export class DemoHaControlSelect extends LitElement {
vertical
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
aria-labelledby=${id}
.label=${label}
?disabled=${config.disabled}
>
</ha-control-select>

View File

@ -97,7 +97,7 @@ export class DemoHaBarSlider extends LitElement {
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-labelledby=${id}
.label=${label}
.unit=${config.unit}
>
</ha-control-slider>
@ -119,7 +119,7 @@ export class DemoHaBarSlider extends LitElement {
class=${ifDefined(config.class)}
@value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved}
aria-label=${label}
.label=${label}
.unit=${config.unit}
>
</ha-control-slider>

View File

@ -63,7 +63,7 @@ export class DemoHaControlSwitch extends LitElement {
@change=${this.handleValueChanged}
.pathOn=${mdiLightbulb}
.pathOff=${mdiLightbulbOff}
aria-labelledby=${id}
.label=${label}
?disabled=${config.disabled}
?reversed=${config.reversed}
>
@ -84,7 +84,7 @@ export class DemoHaControlSwitch extends LitElement {
vertical
class=${ifDefined(config.class)}
@change=${this.handleValueChanged}
aria-label=${label}
.label=${label}
.pathOn=${mdiGarageOpen}
.pathOff=${mdiGarage}
?disabled=${config.disabled}

View File

@ -1,4 +1,4 @@
import type { PropertyValues, TemplateResult } from "lit";
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
@ -17,7 +17,7 @@ export interface ControlSelectOption {
@customElement("ha-control-select")
export class HaControlSelect extends LitElement {
@property({ type: Boolean, reflect: true }) disabled = false;
@property({ type: Boolean }) disabled = false;
@property({ attribute: false }) public options?: ControlSelectOption[];
@ -26,58 +26,14 @@ export class HaControlSelect extends LitElement {
@property({ type: Boolean, reflect: true })
public vertical = false;
@property({ type: Boolean, attribute: "hide-label" })
public hideLabel = false;
@property({ type: Boolean, attribute: "hide-option-label" })
public hideOptionLabel = false;
@property({ type: String })
public label?: string;
@state() private _activeIndex?: number;
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
this.setAttribute("role", "listbox");
if (!this.hasAttribute("tabindex")) {
this.setAttribute("tabindex", "0");
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("_activeIndex")) {
const activeValue =
this._activeIndex != null
? this.options?.[this._activeIndex]?.value
: undefined;
const activedescendant =
activeValue != null ? `option-${activeValue}` : undefined;
this.setAttribute("aria-activedescendant", activedescendant ?? "");
}
if (changedProps.has("vertical")) {
const orientation = this.vertical ? "vertical" : "horizontal";
this.setAttribute("aria-orientation", orientation);
}
}
public connectedCallback(): void {
super.connectedCallback();
this._setupListeners();
}
public disconnectedCallback(): void {
super.disconnectedCallback();
this._destroyListeners();
}
private _setupListeners() {
this.addEventListener("focus", this._handleFocus);
this.addEventListener("blur", this._handleBlur);
this.addEventListener("keydown", this._handleKeydown);
}
private _destroyListeners() {
this.removeEventListener("focus", this._handleFocus);
this.removeEventListener("blur", this._handleBlur);
this.removeEventListener("keydown", this._handleKeydown);
}
private _handleFocus() {
if (this.disabled) return;
this._activeIndex =
@ -95,6 +51,7 @@ export class HaControlSelect extends LitElement {
const value = this.options[this._activeIndex].value;
switch (ev.key) {
case " ":
case "Enter":
this.value = value;
fireEvent(this, "value-changed", { value });
break;
@ -143,8 +100,26 @@ export class HaControlSelect extends LitElement {
}
protected render() {
const activeValue =
this._activeIndex != null
? this.options?.[this._activeIndex]?.value
: undefined;
const activedescendant =
activeValue != null ? `option-${activeValue}` : undefined;
return html`
<div class="container">
<div
class="container"
role="listbox"
tabindex="0"
aria-label=${ifDefined(this.label)}
aria-orientation=${this.vertical ? "vertical" : "horizontal"}
aria-activedescendant=${ifDefined(activedescendant)}
@focus=${this._handleFocus}
@blur=${this._handleBlur}
@keydown=${this._handleKeydown}
?disabled=${this.disabled}
>
${this.options
? repeat(
this.options,
@ -167,7 +142,7 @@ export class HaControlSelect extends LitElement {
})}
role="option"
.value=${option.value}
aria-selected=${this.value === option.value}
aria-selected=${this.value === option.value ? "true" : "false"}
aria-label=${ifDefined(option.label)}
title=${ifDefined(option.label)}
@click=${this._handleOptionClick}
@ -178,7 +153,7 @@ export class HaControlSelect extends LitElement {
${option.path
? html`<ha-svg-icon .path=${option.path}></ha-svg-icon>`
: option.icon || nothing}
${option.label && !this.hideLabel
${option.label && !this.hideOptionLabel
? html`<span>${option.label}</span>`
: nothing}
</div>
@ -203,18 +178,12 @@ export class HaControlSelect extends LitElement {
--mdc-icon-size: 20px;
height: var(--control-select-thickness);
width: 100%;
border-radius: var(--control-select-border-radius);
outline: none;
transition: box-shadow 180ms ease-in-out;
font-style: normal;
font-weight: var(--ha-font-weight-medium);
color: var(--primary-text-color);
user-select: none;
-webkit-tap-highlight-color: transparent;
}
:host(:focus-visible) {
box-shadow: 0 0 0 2px var(--control-select-color);
}
:host([vertical]) {
width: var(--control-select-thickness);
height: 100%;
@ -230,6 +199,8 @@ export class HaControlSelect extends LitElement {
flex-direction: row;
padding: var(--control-select-padding);
box-sizing: border-box;
outline: none;
transition: box-shadow 180ms ease-in-out;
}
.container::before {
position: absolute;
@ -248,6 +219,20 @@ export class HaControlSelect extends LitElement {
margin-inline-start: initial;
direction: var(--direction);
}
.container[disabled] {
--control-select-color: var(--disabled-color);
--control-select-focused-opacity: 0;
color: var(--disabled-color);
}
.container[disabled] .option {
cursor: not-allowed;
}
.container:focus-visible {
box-shadow: 0 0 0 2px var(--control-select-color);
}
.option {
cursor: pointer;
position: relative;
@ -319,14 +304,6 @@ export class HaControlSelect extends LitElement {
margin-inline-end: initial;
margin-bottom: var(--control-select-padding);
}
:host([disabled]) {
--control-select-color: var(--disabled-color);
--control-select-focused-opacity: 0;
color: var(--disabled-color);
}
:host([disabled]) .option {
cursor: not-allowed;
}
`;
}

View File

@ -3,11 +3,12 @@ import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import type { FrontendLocaleData } from "../data/translation";
import { formatNumber } from "../common/number/format_number";
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
import type { FrontendLocaleData } from "../data/translation";
declare global {
interface HASSDomEvents {
@ -75,6 +76,9 @@ export class HaControlSlider extends LitElement {
@property({ type: Number })
public max = 100;
@property({ type: String })
public label?: string;
@state()
public pressed = false;
@ -107,10 +111,6 @@ export class HaControlSlider extends LitElement {
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
this.setupListeners();
this.setAttribute("role", "slider");
if (!this.hasAttribute("tabindex")) {
this.setAttribute("tabindex", "0");
}
}
protected updated(changedProps: PropertyValues) {
@ -197,9 +197,6 @@ export class HaControlSlider extends LitElement {
this.value = this.steppedValue(this.percentageToValue(percentage));
fireEvent(this, "value-changed", { value: this.value });
});
this.addEventListener("keydown", this._handleKeyDown);
this.addEventListener("keyup", this._handleKeyUp);
}
}
@ -208,8 +205,6 @@ export class HaControlSlider extends LitElement {
this._mc.destroy();
this._mc = undefined;
}
this.removeEventListener("keydown", this._handleKeyDown);
this.removeEventListener("keyup", this._handleKeyUp);
}
private get _tenPercentStep() {
@ -323,6 +318,7 @@ export class HaControlSlider extends LitElement {
}
protected render(): TemplateResult {
const valuenow = this.steppedValue(this.value ?? 0);
return html`
<div
class="container${classMap({
@ -332,7 +328,24 @@ export class HaControlSlider extends LitElement {
"--value": `${this.valueToPercentage(this.value ?? 0)}`,
})}
>
<div id="slider" class="slider">
<div
id="slider"
class="slider"
role="slider"
tabindex="0"
aria-label=${ifDefined(this.label)}
aria-valuenow=${valuenow.toString()}
aria-valuetext=${this._formatValue(valuenow)}
aria-valuemin=${ifDefined(
this.min != null ? this.min.toString() : undefined
)}
aria-valuemax=${ifDefined(
this.max != null ? this.max.toString() : undefined
)}
aria-orientation=${this.vertical ? "vertical" : "horizontal"}
@keydown=${this._handleKeyDown}
@keyup=${this._handleKeyUp}
>
<div class="slider-track-background"></div>
<slot name="background"></slot>
${this.mode === "cursor"
@ -371,12 +384,6 @@ export class HaControlSlider extends LitElement {
--control-slider-tooltip-font-size: var(--ha-font-size-m);
height: var(--control-slider-thickness);
width: 100%;
border-radius: var(--control-slider-border-radius);
outline: none;
transition: box-shadow 180ms ease-in-out;
}
:host(:focus-visible) {
box-shadow: 0 0 0 2px var(--control-slider-color);
}
:host([vertical]) {
width: var(--control-slider-thickness);
@ -471,9 +478,14 @@ export class HaControlSlider extends LitElement {
width: 100%;
border-radius: var(--control-slider-border-radius);
transform: translateZ(0);
transition: box-shadow 180ms ease-in-out;
outline: none;
overflow: hidden;
cursor: pointer;
}
.slider:focus-visible {
box-shadow: 0 0 0 2px var(--control-slider-color);
}
.slider * {
pointer-events: none;
}

View File

@ -9,18 +9,19 @@ import {
import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import "./ha-svg-icon";
@customElement("ha-control-switch")
export class HaControlSwitch extends LitElement {
@property({ type: Boolean, reflect: true }) public disabled = false;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public vertical = false;
@property({ type: Boolean }) public reversed = false;
@property({ type: Boolean, reflect: true }) public checked = false;
@property({ type: Boolean }) public checked = false;
// SVG icon path (if you need a non SVG icon instead, use the provided on icon slot to pass an <ha-icon slot="icon-on"> in)
@property({ attribute: false, type: String }) pathOn?: string;
@ -28,6 +29,9 @@ export class HaControlSwitch extends LitElement {
// SVG icon path (if you need a non SVG icon instead, use the provided off icon slot to pass an <ha-icon slot="icon-off"> in)
@property({ attribute: false, type: String }) pathOff?: string;
@property({ type: String })
public label?: string;
@property({ attribute: "touch-action" })
public touchAction?: string;
@ -36,17 +40,6 @@ export class HaControlSwitch extends LitElement {
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
this.setupListeners();
this.setAttribute("role", "switch");
if (!this.hasAttribute("tabindex")) {
this.setAttribute("tabindex", "0");
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (changedProps.has("checked")) {
this.setAttribute("aria-checked", this.checked ? "true" : "false");
}
}
private _toggle() {
@ -112,8 +105,6 @@ export class HaControlSwitch extends LitElement {
if (this.disabled) return;
this._toggle();
});
this.addEventListener("keydown", this._keydown);
}
}
@ -122,7 +113,6 @@ export class HaControlSwitch extends LitElement {
this._mc.destroy();
this._mc = undefined;
}
this.removeEventListener("keydown", this._keydown);
}
private _keydown(ev: any) {
@ -135,7 +125,18 @@ export class HaControlSwitch extends LitElement {
protected render(): TemplateResult {
return html`
<div id="switch" class="switch">
<div
id="switch"
class="switch"
@keydown=${this._keydown}
aria-checked=${this.checked ? "true" : "false"}
aria-label=${ifDefined(this.label)}
@click=${this._toggle}
role="switch"
tabindex="0"
?checked=${this.checked}
?disabled=${this.disabled}
>
<div class="background"></div>
<div class="button" aria-hidden="true">
${this.checked
@ -164,16 +165,13 @@ export class HaControlSwitch extends LitElement {
width: 100%;
box-sizing: border-box;
user-select: none;
cursor: pointer;
border-radius: var(--control-switch-border-radius);
outline: none;
transition: box-shadow 180ms ease-in-out;
-webkit-tap-highlight-color: transparent;
}
:host(:focus-visible) {
.switch:focus-visible {
box-shadow: 0 0 0 2px var(--control-switch-off-color);
}
:host([checked]:focus-visible) {
.switch[checked]:focus-visible {
box-shadow: 0 0 0 2px var(--control-switch-on-color);
}
.switch {
@ -182,9 +180,15 @@ export class HaControlSwitch extends LitElement {
height: 100%;
width: 100%;
border-radius: var(--control-switch-border-radius);
outline: none;
overflow: hidden;
padding: var(--control-switch-padding);
display: flex;
cursor: pointer;
}
.switch[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.switch .background {
position: absolute;
@ -212,24 +216,24 @@ export class HaControlSwitch extends LitElement {
align-items: center;
justify-content: center;
}
:host([checked]) .switch .background {
.switch[checked] .background {
background-color: var(--control-switch-on-color);
}
:host([checked]) .switch .button {
.switch[checked] .button {
transform: translateX(100%);
background-color: var(--control-switch-on-color);
}
:host([reversed]) .switch {
flex-direction: row-reverse;
}
:host([reversed][checked]) .switch .button {
:host([reversed]) .switch[checked] .button {
transform: translateX(-100%);
}
:host([vertical]) {
width: var(--control-switch-thickness);
height: 100%;
}
:host([vertical][checked]) .switch .button {
:host([vertical]) .switch[checked] .button {
transform: translateY(100%);
}
:host([vertical]) .switch .button {
@ -239,13 +243,9 @@ export class HaControlSwitch extends LitElement {
:host([vertical][reversed]) .switch {
flex-direction: column-reverse;
}
:host([vertical][reversed][checked]) .switch .button {
:host([vertical][reversed]) .switch[checked] .button {
transform: translateY(-100%);
}
:host([disabled]) {
opacity: 0.5;
cursor: not-allowed;
}
`;
}

View File

@ -79,7 +79,7 @@ class LightColorTempPicker extends LitElement {
mode="cursor"
@value-changed=${this._ctColorChanged}
@slider-moved=${this._ctColorCursorMoved}
.ariaLabel=${this.hass.localize(
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.color_temp"
)}
style=${styleMap({

View File

@ -174,10 +174,8 @@ class HuiAlarmModeCardFeature
.options=${options}
.value=${this._currentMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass.localize(
"ui.card.alarm_control_panel.modes_label"
)}
hide-option-label
.label=${this.hass.localize("ui.card.alarm_control_panel.modes_label")}
style=${styleMap({
"--control-select-color": color,
"--modes-count": options.length.toString(),

View File

@ -174,11 +174,8 @@ class HuiClimateFanModesCardFeature
.options=${options}
.value=${this._currentFanMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(
stateObj,
"fan_mode"
)}
hide-option-label
.label=${this.hass!.formatEntityAttributeName(stateObj, "fan_mode")}
.disabled=${this._stateObj!.state === UNAVAILABLE}
>
</ha-control-select>

View File

@ -205,8 +205,8 @@ class HuiClimateHvacModesCardFeature
.options=${options}
.value=${this._currentHvacMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass.localize("ui.card.climate.mode")}
hide-option-label
.label=${this.hass.localize("ui.card.climate.mode")}
style=${styleMap({
"--control-select-color": color,
})}

View File

@ -176,8 +176,8 @@ class HuiClimatePresetModesCardFeature
.options=${options}
.value=${this._currentPresetMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(
hide-option-label
.label=${this.hass!.formatEntityAttributeName(
stateObj,
"preset_mode"
)}

View File

@ -178,8 +178,8 @@ class HuiClimateSwingHorizontalModesCardFeature
.options=${options}
.value=${this._currentSwingHorizontalMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(
hide-option-label
.label=${this.hass!.formatEntityAttributeName(
stateObj,
"swing_horizontal_mode"
)}

View File

@ -176,7 +176,7 @@ class HuiClimateSwingModesCardFeature
.options=${options}
.value=${this._currentSwingMode}
@value-changed=${this._valueChanged}
hide-label
hide-option-label
.ariaLabel=${this.hass!.formatEntityAttributeName(
stateObj,
"swing_mode"

View File

@ -106,7 +106,7 @@ class HuiCoverPositionCardFeature
inverted
show-handle
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj,
this.hass.entities,

View File

@ -105,7 +105,7 @@ class HuiCoverTiltPositionCardFeature
mode="cursor"
inverted
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj,
this.hass.entities,

View File

@ -170,8 +170,8 @@ class HuiFanPresetModesCardFeature
.options=${options}
.value=${this._currentPresetMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(
hide-option-label
.label=${this.hass!.formatEntityAttributeName(
stateObj,
"preset_mode"
)}

View File

@ -109,8 +109,8 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
.options=${options}
.value=${speed}
@value-changed=${this._speedValueChanged}
hide-label
.ariaLabel=${computeAttributeNameDisplay(
hide-option-label
.label=${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj,
this.hass.entities,
@ -131,7 +131,7 @@ class HuiFanSpeedCardFeature extends LitElement implements LovelaceCardFeature {
max="100"
.step=${this._stateObj.attributes.percentage_step ?? 1}
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj,
this.hass.entities,

View File

@ -174,8 +174,8 @@ class HuiHumidifierModesCardFeature
.options=${options}
.value=${this._currentMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
hide-option-label
.label=${this.hass!.formatEntityAttributeName(stateObj, "mode")}
.disabled=${this._stateObj!.state === UNAVAILABLE}
>
</ha-control-select>

View File

@ -130,8 +130,8 @@ class HuiHumidifierToggleCardFeature
.options=${options}
.value=${this._currentState}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass.localize("ui.card.humidifier.state")}
hide-option-label
.label=${this.hass.localize("ui.card.humidifier.state")}
style=${styleMap({
"--control-select-color": color,
})}

View File

@ -185,7 +185,7 @@ class HuiToggleCardFeature extends LitElement implements LovelaceCardFeature {
.pathOff=${offIcon}
.checked=${isOn}
@change=${this._valueChanged}
.ariaLabel=${this.hass.localize("ui.card.common.toggle")}
.label=${this.hass.localize("ui.card.common.toggle")}
.disabled=${this._stateObj.state === UNAVAILABLE}
>
</ha-control-switch>

View File

@ -151,8 +151,8 @@ class HuiWaterHeaterOperationModeCardFeature
.options=${options}
.value=${this._currentOperationMode}
@value-changed=${this._valueChanged}
hide-label
.ariaLabel=${this.hass.localize("ui.card.water_heater.mode")}
hide-option-label
.label=${this.hass.localize("ui.card.water_heater.mode")}
style=${styleMap({
"--control-select-color": color,
})}

View File

@ -87,9 +87,7 @@ export class HaStateControlAlarmControlPanelModes extends LitElement {
.options=${options}
.value=${this._currentMode}
@value-changed=${this._valueChanged}
.ariaLabel=${this.hass.localize(
"ui.card.alarm_control_panel.modes_label"
)}
.label=${this.hass.localize("ui.card.alarm_control_panel.modes_label")}
style=${styleMap({
"--control-select-color": color,
"--modes-count": modes.length.toString(),

View File

@ -50,7 +50,7 @@ export class HaStateControlCoverPosition extends LitElement {
show-handle
mode="end"
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,

View File

@ -79,7 +79,7 @@ export class HaStateControlInfoCoverTiltPosition extends LitElement {
max="100"
mode="cursor"
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,

View File

@ -112,7 +112,7 @@ export class HaStateControlCoverToggle extends LitElement {
reversed
.checked=${isOn}
@change=${this._valueChanged}
.ariaLabel=${isOn
.label=${isOn
? this.hass.localize("ui.card.cover.close_cover")
: this.hass.localize("ui.card.cover.open_cover")}
style=${styleMap({

View File

@ -92,7 +92,7 @@ export class HaStateControlFanSpeed extends LitElement {
.options=${options}
.value=${this.speedValue}
@value-changed=${this._speedValueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,
@ -117,7 +117,7 @@ export class HaStateControlFanSpeed extends LitElement {
.value=${this.sliderValue}
.step=${this.stateObj.attributes.percentage_step ?? 1}
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,

View File

@ -117,7 +117,7 @@ export class HaStateControlToggle extends LitElement {
.checked=${isOn}
.showHandle=${stateActive(this.stateObj)}
@change=${this._valueChanged}
.ariaLabel=${this.hass.localize("ui.card.common.toggle")}
.label=${this.hass.localize("ui.card.common.toggle")}
style=${styleMap({
"--control-switch-on-color": onColor,
"--control-switch-off-color": offColor,

View File

@ -67,7 +67,7 @@ export class HaStateControlLightBrightness extends LitElement {
max="100"
.showHandle=${stateActive(this.stateObj)}
@value-changed=${this._valueChanged}
.ariaLabel=${this.hass.formatEntityAttributeName(
.label=${this.hass.formatEntityAttributeName(
this.stateObj,
"brightness"
)}

View File

@ -118,7 +118,7 @@ export class HaStateControlLockToggle extends LitElement {
reversed
.checked=${this._isOn}
@change=${this._valueChanged}
.ariaLabel=${this._isOn
.label=${this._isOn
? this.hass.localize("ui.card.lock.unlock")
: this.hass.localize("ui.card.lock.lock")}
style=${styleMap({

View File

@ -48,7 +48,7 @@ export class HaStateControlValvePosition extends LitElement {
max="100"
show-handle
@value-changed=${this._valueChanged}
.ariaLabel=${computeAttributeNameDisplay(
.label=${computeAttributeNameDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,

View File

@ -112,7 +112,7 @@ export class HaStateControlValveToggle extends LitElement {
reversed
.checked=${isOn}
@change=${this._valueChanged}
.ariaLabel=${isOn
.label=${isOn
? this.hass.localize("ui.card.valve.close_valve")
: this.hass.localize("ui.card.valve.open_valve")}
style=${styleMap({