Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: keep action elements focusable when disabled #3040

Merged
merged 18 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ snapshots["sbb-button renders a sbb-button without icon DOM"] =
negative=""
role="button"
size="m"
tabindex="0"
type="button"
value="value"
>
Expand Down
4 changes: 2 additions & 2 deletions src/elements/button/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit';
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { buttonCommonStyle, buttonPrimaryStyle, SbbButtonCommonElementMixin } from '../common.js';

/**
Expand All @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonPrimaryStyle, SbbButtonCommonElementMixin } fr
*/
@customElement('sbb-button')
export class SbbButtonElement extends SbbButtonCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {
public static override styles: CSSResultGroup = [buttonCommonStyle, buttonPrimaryStyle];
}
Expand Down
6 changes: 6 additions & 0 deletions src/elements/button/button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ sbb-button {

Use the accessibility properties in case of an icon-only button to describe the purpose of the `sbb-button` for screen-reader users.

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
6 changes: 4 additions & 2 deletions src/elements/button/mini-button/mini-button.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CSSResultGroup } from 'lit';
import { customElement } from 'lit/decorators.js';

import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';

import { SbbMiniButtonBaseElement } from './mini-button-base-element.js';
import style from './mini-button.scss?lit&inline';
Expand All @@ -13,7 +13,9 @@ import style from './mini-button.scss?lit&inline';
* @slot icon - Slot used to display the icon, if one is set
*/
@customElement('sbb-mini-button')
export class SbbMiniButtonElement extends SbbDisabledTabIndexActionMixin(SbbMiniButtonBaseElement) {
export class SbbMiniButtonElement extends SbbFocusableDisabledActionMixin(
SbbMiniButtonBaseElement,
) {
public static override styles: CSSResultGroup = style;
}

Expand Down
6 changes: 6 additions & 0 deletions src/elements/button/mini-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ sbb-mini-button {

Use the accessibility properties to describe the purpose of the `sbb-mini-button` for screen-reader users.

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.
jeripeierSBB marked this conversation as resolved.
Show resolved Hide resolved

<!-- Auto Generated Below -->

## Properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ snapshots["sbb-secondary-button renders a sbb-secondary-button without icon DOM"
negative=""
role="button"
size="m"
tabindex="0"
type="button"
value="value"
>
Expand Down
6 changes: 6 additions & 0 deletions src/elements/button/secondary-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ sbb-secondary-button {

Use the accessibility properties in case of an icon-only button to describe the purpose of the `sbb-secondary-button` for screen-reader users.

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
4 changes: 2 additions & 2 deletions src/elements/button/secondary-button/secondary-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit';
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { buttonCommonStyle, buttonSecondaryStyle, SbbButtonCommonElementMixin } from '../common.js';

/**
Expand All @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonSecondaryStyle, SbbButtonCommonElementMixin }
*/
@customElement('sbb-secondary-button')
export class SbbSecondaryButtonElement extends SbbButtonCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {
public static override styles: CSSResultGroup = [buttonCommonStyle, buttonSecondaryStyle];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ snapshots["sbb-tertiary-button renders a sbb-tertiary-button without icon DOM"]
negative=""
role="button"
size="m"
tabindex="0"
type="button"
value="value"
>
Expand Down
6 changes: 6 additions & 0 deletions src/elements/button/tertiary-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ sbb-tertiary-button {

Use the accessibility properties in case of an icon-only button to describe the purpose of the `sbb-tertiary-button` for screen-reader users.

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
4 changes: 2 additions & 2 deletions src/elements/button/tertiary-button/tertiary-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit';
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { buttonCommonStyle, buttonTertiaryStyle, SbbButtonCommonElementMixin } from '../common.js';

/**
Expand All @@ -13,7 +13,7 @@ import { buttonCommonStyle, buttonTertiaryStyle, SbbButtonCommonElementMixin } f
*/
@customElement('sbb-tertiary-button')
export class SbbTertiaryButtonElement extends SbbButtonCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {
public static override styles: CSSResultGroup = [buttonCommonStyle, buttonTertiaryStyle];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ snapshots["sbb-transparent-button renders a sbb-transparent-button without icon
negative=""
role="button"
size="m"
tabindex="0"
type="button"
value="value"
>
Expand Down
6 changes: 6 additions & 0 deletions src/elements/button/transparent-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ sbb-transparent-button {

Use the accessibility properties in case of an icon-only button to describe the purpose of the `sbb-transparent-button` for screen-reader users.

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { CSSResultGroup } from 'lit';
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import {
buttonCommonStyle,
buttonTransparentStyle,
Expand All @@ -17,7 +17,7 @@ import {
*/
@customElement('sbb-transparent-button')
export class SbbTransparentButtonElement extends SbbButtonCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {
public static override styles: CSSResultGroup = [buttonCommonStyle, buttonTransparentStyle];
}
Expand Down
38 changes: 38 additions & 0 deletions src/elements/core/mixins/disabled-mixin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { LitElement, PropertyValues } from 'lit';
import { property } from 'lit/decorators.js';

import { hostAttributes } from '../decorators.js';

import type { AbstractConstructor } from './constructor.js';

export declare class SbbDisabledMixinType {
Expand Down Expand Up @@ -69,3 +71,39 @@ export const SbbDisabledTabIndexActionMixin = <T extends AbstractConstructor<Lit
}
return SbbDisabledTabIndexAction as AbstractConstructor<SbbDisabledMixinType> & T;
};

/**
* Extends `SbbDisabledMixin` with the `aria-disabled` and the `tabindex` handling.
* For a11y purposes, keeps the element focusable even when disabled
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export const SbbFocusableDisabledActionMixin = <T extends AbstractConstructor<LitElement>>(
superClass: T,
): AbstractConstructor<SbbDisabledMixinType> & T => {
@hostAttributes({
tabindex: '0',
})
abstract class SbbFocusableDisabledAction
extends SbbDisabledMixin(superClass)
implements SbbDisabledMixinType
{
public override connectedCallback(): void {
super.connectedCallback();
}

protected override willUpdate(changedProperties: PropertyValues<this>): void {
super.willUpdate(changedProperties);

if (!changedProperties.has('disabled')) {
return;
}

if (this.disabled) {
this.setAttribute('aria-disabled', 'true');
} else {
this.removeAttribute('aria-disabled');
}
}
}
return SbbFocusableDisabledAction as AbstractConstructor<SbbDisabledMixinType> & T;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbConnectedAbortController, SbbSlotStateController } from '../../core/controllers.js';
import { hostAttributes } from '../../core/decorators.js';
import { EventEmitter } from '../../core/eventing.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { SbbIconNameMixin } from '../../icon.js';
import type { SbbExpansionPanelElement } from '../expansion-panel.js';

Expand All @@ -22,7 +22,7 @@ import style from './expansion-panel-header.scss?lit&inline';
@hostAttributes({
slot: 'header',
})
export class SbbExpansionPanelHeaderElement extends SbbDisabledTabIndexActionMixin(
export class SbbExpansionPanelHeaderElement extends SbbFocusableDisabledActionMixin(
SbbIconNameMixin(SbbButtonBaseElement),
) {
public static override styles: CSSResultGroup = style;
Expand Down
6 changes: 6 additions & 0 deletions src/elements/expansion-panel/expansion-panel/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ and the header's `id` is set as `aria-labelledby` attribute on the content.
The `expanded` attribute is used to correctly set the `aria-expanded` attribute on the header
and the `aria-hidden` attribute on the content.

### Disabled elements

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
4 changes: 2 additions & 2 deletions src/elements/link/block-link-button/block-link-button.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { SbbBlockLinkCommonElementMixin } from '../common.js';

/**
Expand All @@ -12,7 +12,7 @@ import { SbbBlockLinkCommonElementMixin } from '../common.js';
*/
@customElement('sbb-block-link-button')
export class SbbBlockLinkButtonElement extends SbbBlockLinkCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {}

declare global {
Expand Down
8 changes: 8 additions & 0 deletions src/elements/link/block-link-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ The component has three sizes (`xs`, `s`, which is the default, and `m`).
<sbb-block-link-button size="m">Refunds</sbb-block-link-button>
```

## Accessibility

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
4 changes: 2 additions & 2 deletions src/elements/link/link-button/link-button.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { SbbInlineLinkCommonElementMixin } from '../common.js';

/**
Expand All @@ -11,7 +11,7 @@ import { SbbInlineLinkCommonElementMixin } from '../common.js';
*/
@customElement('sbb-link-button')
export class SbbLinkButtonElement extends SbbInlineLinkCommonElementMixin(
SbbDisabledTabIndexActionMixin(SbbButtonBaseElement),
SbbFocusableDisabledActionMixin(SbbButtonBaseElement),
) {}

declare global {
Expand Down
8 changes: 8 additions & 0 deletions src/elements/link/link-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ accepting its associated properties (`type`, `name`, `value` and `form`).
</sbb-link-button>
```

## Accessibility

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
4 changes: 2 additions & 2 deletions src/elements/menu/menu-button/menu-button.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { customElement } from 'lit/decorators.js';

import { SbbButtonBaseElement } from '../../core/base-elements.js';
import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js';
import { SbbFocusableDisabledActionMixin } from '../../core/mixins.js';
import { SbbMenuActionCommonElementMixin } from '../common.js';

/**
Expand All @@ -13,7 +13,7 @@ import { SbbMenuActionCommonElementMixin } from '../common.js';
* to modify horizontal padding.
*/
@customElement('sbb-menu-button')
export class SbbMenuButtonElement extends SbbDisabledTabIndexActionMixin(
export class SbbMenuButtonElement extends SbbFocusableDisabledActionMixin(
SbbMenuActionCommonElementMixin(SbbButtonBaseElement),
) {}

Expand Down
8 changes: 8 additions & 0 deletions src/elements/menu/menu-button/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ accepting its associated properties (`type`, `name`, `value` and `form`).
<sbb-menu-button value="menu" name="menu">Button</sbb-menu-button>
```

## Accessibility

### Disabled buttons

Generally speaking, `disabled` elements are considered a bad pattern for a11y. They are invisible to assistive technology and do not provide the reason for which they are disabled.
To partially address the problem, disabled elements are kept focusable (other interactions are still prevented).
Still, the consumer's responsible for providing the reason for a disabled element.

<!-- Auto Generated Below -->

## Properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ snapshots["sbb-menu renders DOM"] =
disabled=""
icon-name="pen-small"
role="button"
tabindex="0"
>
Edit
</sbb-menu-button>
Expand Down Expand Up @@ -111,6 +112,7 @@ snapshots["sbb-menu renders with list DOM"] =
icon-name="pen-small"
role="button"
slot="li-1"
tabindex="0"
>
Edit
</sbb-menu-button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ snapshots["sbb-tag renders disabled with icon and amount DOM"] =
icon-name="circle-information-small"
role="button"
size="m"
tabindex="0"
value="information"
>
Info
Expand Down
Loading
Loading