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

View File

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

View File

@ -63,7 +63,7 @@ export class DemoHaControlSwitch extends LitElement {
@change=${this.handleValueChanged} @change=${this.handleValueChanged}
.pathOn=${mdiLightbulb} .pathOn=${mdiLightbulb}
.pathOff=${mdiLightbulbOff} .pathOff=${mdiLightbulbOff}
aria-labelledby=${id} .label=${label}
?disabled=${config.disabled} ?disabled=${config.disabled}
?reversed=${config.reversed} ?reversed=${config.reversed}
> >
@ -84,7 +84,7 @@ export class DemoHaControlSwitch extends LitElement {
vertical vertical
class=${ifDefined(config.class)} class=${ifDefined(config.class)}
@change=${this.handleValueChanged} @change=${this.handleValueChanged}
aria-label=${label} .label=${label}
.pathOn=${mdiGarageOpen} .pathOn=${mdiGarageOpen}
.pathOff=${mdiGarage} .pathOff=${mdiGarage}
?disabled=${config.disabled} ?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 { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
@ -17,7 +17,7 @@ export interface ControlSelectOption {
@customElement("ha-control-select") @customElement("ha-control-select")
export class HaControlSelect extends LitElement { export class HaControlSelect extends LitElement {
@property({ type: Boolean, reflect: true }) disabled = false; @property({ type: Boolean }) disabled = false;
@property({ attribute: false }) public options?: ControlSelectOption[]; @property({ attribute: false }) public options?: ControlSelectOption[];
@ -26,58 +26,14 @@ export class HaControlSelect extends LitElement {
@property({ type: Boolean, reflect: true }) @property({ type: Boolean, reflect: true })
public vertical = false; public vertical = false;
@property({ type: Boolean, attribute: "hide-label" }) @property({ type: Boolean, attribute: "hide-option-label" })
public hideLabel = false; public hideOptionLabel = false;
@property({ type: String })
public label?: string;
@state() private _activeIndex?: number; @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() { private _handleFocus() {
if (this.disabled) return; if (this.disabled) return;
this._activeIndex = this._activeIndex =
@ -95,6 +51,7 @@ export class HaControlSelect extends LitElement {
const value = this.options[this._activeIndex].value; const value = this.options[this._activeIndex].value;
switch (ev.key) { switch (ev.key) {
case " ": case " ":
case "Enter":
this.value = value; this.value = value;
fireEvent(this, "value-changed", { value }); fireEvent(this, "value-changed", { value });
break; break;
@ -143,8 +100,26 @@ export class HaControlSelect extends LitElement {
} }
protected render() { protected render() {
const activeValue =
this._activeIndex != null
? this.options?.[this._activeIndex]?.value
: undefined;
const activedescendant =
activeValue != null ? `option-${activeValue}` : undefined;
return html` 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 ${this.options
? repeat( ? repeat(
this.options, this.options,
@ -167,7 +142,7 @@ export class HaControlSelect extends LitElement {
})} })}
role="option" role="option"
.value=${option.value} .value=${option.value}
aria-selected=${this.value === option.value} aria-selected=${this.value === option.value ? "true" : "false"}
aria-label=${ifDefined(option.label)} aria-label=${ifDefined(option.label)}
title=${ifDefined(option.label)} title=${ifDefined(option.label)}
@click=${this._handleOptionClick} @click=${this._handleOptionClick}
@ -178,7 +153,7 @@ export class HaControlSelect extends LitElement {
${option.path ${option.path
? html`<ha-svg-icon .path=${option.path}></ha-svg-icon>` ? html`<ha-svg-icon .path=${option.path}></ha-svg-icon>`
: option.icon || nothing} : option.icon || nothing}
${option.label && !this.hideLabel ${option.label && !this.hideOptionLabel
? html`<span>${option.label}</span>` ? html`<span>${option.label}</span>`
: nothing} : nothing}
</div> </div>
@ -203,18 +178,12 @@ export class HaControlSelect extends LitElement {
--mdc-icon-size: 20px; --mdc-icon-size: 20px;
height: var(--control-select-thickness); height: var(--control-select-thickness);
width: 100%; width: 100%;
border-radius: var(--control-select-border-radius);
outline: none;
transition: box-shadow 180ms ease-in-out;
font-style: normal; font-style: normal;
font-weight: var(--ha-font-weight-medium); font-weight: var(--ha-font-weight-medium);
color: var(--primary-text-color); color: var(--primary-text-color);
user-select: none; user-select: none;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
:host(:focus-visible) {
box-shadow: 0 0 0 2px var(--control-select-color);
}
:host([vertical]) { :host([vertical]) {
width: var(--control-select-thickness); width: var(--control-select-thickness);
height: 100%; height: 100%;
@ -230,6 +199,8 @@ export class HaControlSelect extends LitElement {
flex-direction: row; flex-direction: row;
padding: var(--control-select-padding); padding: var(--control-select-padding);
box-sizing: border-box; box-sizing: border-box;
outline: none;
transition: box-shadow 180ms ease-in-out;
} }
.container::before { .container::before {
position: absolute; position: absolute;
@ -248,6 +219,20 @@ export class HaControlSelect extends LitElement {
margin-inline-start: initial; margin-inline-start: initial;
direction: var(--direction); 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 { .option {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
@ -319,14 +304,6 @@ export class HaControlSelect extends LitElement {
margin-inline-end: initial; margin-inline-end: initial;
margin-bottom: var(--control-select-padding); 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 { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import type { FrontendLocaleData } from "../data/translation";
import { formatNumber } from "../common/number/format_number"; import { formatNumber } from "../common/number/format_number";
import { blankBeforeUnit } from "../common/translations/blank_before_unit"; import { blankBeforeUnit } from "../common/translations/blank_before_unit";
import type { FrontendLocaleData } from "../data/translation";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -75,6 +76,9 @@ export class HaControlSlider extends LitElement {
@property({ type: Number }) @property({ type: Number })
public max = 100; public max = 100;
@property({ type: String })
public label?: string;
@state() @state()
public pressed = false; public pressed = false;
@ -107,10 +111,6 @@ export class HaControlSlider extends LitElement {
protected firstUpdated(changedProperties: PropertyValues): void { protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties); super.firstUpdated(changedProperties);
this.setupListeners(); this.setupListeners();
this.setAttribute("role", "slider");
if (!this.hasAttribute("tabindex")) {
this.setAttribute("tabindex", "0");
}
} }
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
@ -197,9 +197,6 @@ export class HaControlSlider extends LitElement {
this.value = this.steppedValue(this.percentageToValue(percentage)); this.value = this.steppedValue(this.percentageToValue(percentage));
fireEvent(this, "value-changed", { value: this.value }); 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.destroy();
this._mc = undefined; this._mc = undefined;
} }
this.removeEventListener("keydown", this._handleKeyDown);
this.removeEventListener("keyup", this._handleKeyUp);
} }
private get _tenPercentStep() { private get _tenPercentStep() {
@ -323,6 +318,7 @@ export class HaControlSlider extends LitElement {
} }
protected render(): TemplateResult { protected render(): TemplateResult {
const valuenow = this.steppedValue(this.value ?? 0);
return html` return html`
<div <div
class="container${classMap({ class="container${classMap({
@ -332,7 +328,24 @@ export class HaControlSlider extends LitElement {
"--value": `${this.valueToPercentage(this.value ?? 0)}`, "--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> <div class="slider-track-background"></div>
<slot name="background"></slot> <slot name="background"></slot>
${this.mode === "cursor" ${this.mode === "cursor"
@ -371,12 +384,6 @@ export class HaControlSlider extends LitElement {
--control-slider-tooltip-font-size: var(--ha-font-size-m); --control-slider-tooltip-font-size: var(--ha-font-size-m);
height: var(--control-slider-thickness); height: var(--control-slider-thickness);
width: 100%; 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]) { :host([vertical]) {
width: var(--control-slider-thickness); width: var(--control-slider-thickness);
@ -471,9 +478,14 @@ export class HaControlSlider extends LitElement {
width: 100%; width: 100%;
border-radius: var(--control-slider-border-radius); border-radius: var(--control-slider-border-radius);
transform: translateZ(0); transform: translateZ(0);
transition: box-shadow 180ms ease-in-out;
outline: none;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
} }
.slider:focus-visible {
box-shadow: 0 0 0 2px var(--control-slider-color);
}
.slider * { .slider * {
pointer-events: none; pointer-events: none;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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