Skip to content

fix(tag tag-list): Improve tag-list accessibility#3214

Draft
jpzwarte wants to merge 33 commits into
mainfrom
fix/2868-tag-list-a11y
Draft

fix(tag tag-list): Improve tag-list accessibility#3214
jpzwarte wants to merge 33 commits into
mainfrom
fix/2868-tag-list-a11y

Conversation

@jpzwarte

@jpzwarte jpzwarte commented Apr 14, 2026

Copy link
Copy Markdown
Member

What changed

This PR improves the accessibility of <sl-tag> and <sl-tag-list>, especially for removable tags, disabled removable tags, keyboard navigation, and screen reader users.

Closes #2868 #2869

<sl-tag>

  • Reworked removable tags so the remove action is handled by a native <button> instead of the host tag.
  • Added a localized accessible name to the remove button: Remove tag '<label>'.
  • Removed aria-hidden="true" from the remove button.
  • Added type="button" to prevent accidental form submissions.
  • Kept the remove button rendered for disabled removable tags and uses aria-disabled="true" instead of native disabled, so the action remains discoverable to assistive technologies.
  • Guarded disabled remove actions so click and keyboard activation do not remove the tag.
  • Moved Backspace/Delete handling to the remove button and prevents default behavior/propagation for those shortcuts.
  • Added a custom focus() override:
    • removable tags delegate focus to the remove button,
    • overflow/static tags delegate focus to the label when applicable,
    • host-focus cases such as stacked counter tags remain focusable.
  • Reworked focus styling:
    • removable tags show the focus ring on the remove button,
    • non-removable/host-focused tags keep a visible host focus ring,
    • mouse focus does not force the custom focus-visible state.
  • Wrapped the default slot in <div part="label">.
  • Added @csspart label and @csspart button documentation.
  • Reflects the removable property to support CSS selectors.
  • Reworked overflow tooltip rendering to use reactive state and aria-describedby on the label part.
  • Derives the tag label from all slotted text content, including nested elements.
  • Updates label and tooltip overflow state when slotted text changes without resize.
  • Ensures clicking the label area does not remove a removable tag.

<sl-tag-list>

  • Sets role="list" on the tag list and role="listitem" on slotted tags.
  • Adds screen reader navigation instructions to removable tag remove buttons.
  • Keeps disabled removable tags in roving tabindex, while their remove buttons remain aria-disabled.
  • Prevents disabled stacked counter tags from becoming tab stops.
  • Excludes disabled stacked counter tags from roving tabindex candidates.
  • Keeps tag properties synchronized on update, including localized navigation instructions after runtime locale changes.
  • Keeps keyboard navigation scoped to visible removable tags in stacked lists.
  • Added @customElement sl-tag-list JSDoc.

<sl-combobox> integration with removable tags

  • Updated the multi-select combobox integration with <sl-tag-list> so keyboard focus uses the real remove buttons from the tag list instead of competing with the combobox's older fake tag focus state.
  • Prevented combobox input key handling from reacting to keyboard events that originate from the tag list remove buttons.
  • Cleared the combobox fake focusedTag state when focus enters the tag list, avoiding two visible focus indicators at once.
  • Improved focus behavior after removing selected tags from a multi-select combobox:
    • when a selected tag is removed, focus moves to the next visible removable tag,
    • if there is no next tag, focus moves to the previous visible removable tag,
    • if the removed tag was the only selected tag, focus returns to the combobox input.
  • Added regression coverage for combobox tag focus behavior and removing multiple selected tags with the keyboard.

Locales

  • Removed the unused sl.tag.removalInstructions key.
  • Added sl.tag.remove.
  • Added sl.tagList.navigationInstructions.
  • Updated locale files and XLF files for es-ES, it, nl, and pl.

Stories and docs

  • Added OverflowRemovable story.
  • Added RemovableDisabled stories.
  • Removed mixed tag list story.
  • Updated tag examples to separate static and removable tag lists.
  • Updated docs to clarify:
    • the remove button is the only interactive part of a removable tag,
    • static and removable tags should not be mixed in one list,
    • disabled tags should only be used for removable tags.

Website accessibility follow-ups

  • Updated website vertical navigation tabs from pseudo-tab semantics to navigation link semantics using aria-current="page".
  • Adjusted website link/tab colors for contrast.
  • Narrowed the website axe exclusion for the known sl-tab page navigation issue so page content remains covered.

Tests

  • Updated and expanded <sl-tag> tests for:

    • remove button focus,
    • accessible labels,
    • slotted element labels,
    • disabled remove behavior,
    • label click behavior,
    • Backspace/Delete isolation,
    • tooltip updates,
    • host focus-visible fallback.
  • Updated and expanded <sl-tag-list> tests for:

    • disabled removable tags in roving tabindex,
    • navigation descriptions,
    • localized description resync,
    • stacked disabled counter behavior,
    • all-regular-tags-hidden stacked edge case.

Copilot AI review requested due to automatic review settings April 14, 2026 10:25
@changeset-bot

changeset-bot Bot commented Apr 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f19371c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@sl-design-system/tag Patch
@sl-design-system/combobox Patch
@sl-design-system/locales Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to improve accessibility for the sl-tag component (particularly when tags are removable) by shifting removal interaction to an explicit, labeled remove button and updating related localization, styles, stories, and tests.

Changes:

  • Replaced the previous “press Backspace/Delete” ARIA instruction with an explicit remove button aria-label (localized) and updated NL locale resources accordingly.
  • Updated sl-tag focus handling and styling to support a host-level focus outline via custom states and delegatesFocus.
  • Adjusted Storybook stories and component tests to reflect the new removable/disabled behaviors.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/locales/src/nl.xlf Removes old removal-instructions string; adds new sl.tag.remove translation.
packages/locales/src/nl.ts Removes old key and adds a new remove-label entry (currently mismatched vs XLF/component id).
packages/components/tag/src/tag.ts Implements accessible remove button labeling and new focus handling; removes old ARIA description logic.
packages/components/tag/src/tag.stories.ts Simplifies max-width styling and adds a removable+disabled story.
packages/components/tag/src/tag.spec.ts Updates/extends tests for reflectable removable, focus delegation, and remove button labeling/disabled behavior.
packages/components/tag/src/tag.scss Switches focus styling to :state(focus-visible) and tweaks button outline/disabled interaction behavior.

Comment thread packages/locales/src/nl.ts Outdated
Comment thread packages/components/tag/src/tag.scss Outdated
Comment thread packages/components/tag/src/tag.ts Outdated
@github-actions

github-actions Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

🕸 Website preview

You can view a preview here (commit b6ae79f745014f407ae511c308a54b396c395b88).

@github-actions

github-actions Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

🕸 Storybook preview

You can view a preview here (commit b6ae79f745014f407ae511c308a54b396c395b88).

Copilot AI review requested due to automatic review settings April 21, 2026 12:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

packages/components/tag/src/tag-list.ts:346

  • tag.role = 'listitem' and tag.setAttribute('role', 'listitem') are redundant because the IDL role property reflects to the role attribute. Keeping only one avoids duplication and makes it clearer where the role is set.
      tag.role = 'listitem';
      tag.size = this.size;
      tag.variant = this.variant;
      tag.setAttribute('role', 'listitem');
    });

Comment thread packages/components/tag/src/tag.spec.ts Outdated
Comment thread packages/components/tag/src/tag.ts Outdated
jpzwarte and others added 2 commits April 21, 2026 14:46
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 21, 2026 12:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread packages/components/tag/src/tag.ts Outdated
Comment thread packages/components/tag/src/tag.ts
@jpzwarte jpzwarte marked this pull request as ready for review April 21, 2026 13:12
Copilot AI review requested due to automatic review settings April 21, 2026 13:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

packages/components/tag/src/tag.ts:184

  • Tooltip visibility is only recalculated from the ResizeObserver callback. If the slotted label text changes (slotchange) without affecting the host element's size, ResizeObserver may not fire and this.tooltip can become stale. Consider recalculating overflow (or scheduling #onResize()) after updating this.label in #onSlotChange.
  #onSlotChange(event: Event & { target: HTMLSlotElement }): void {
    this.label = event.target
      .assignedNodes({ flatten: true })
      .filter(node => node.nodeType === Node.TEXT_NODE)
      .map(node => node.textContent?.trim())
      .join('');
  }

Comment thread packages/components/tag/src/tag.ts
Comment thread packages/components/tag/src/tag.ts

@michal-sanoma michal-sanoma left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Small a11y/UX concern: because #onFocus() always adds focus-visible, the host behaves like :focus instead of native :focus-visible. Would it make sense to track input source and apply the ring only for keyboard interaction?

@a11ymiko a11ymiko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Stacked variants keyboard user cannot set focus on tag with tooltip. sl-tag has tabindex=0 but keyboard focus cannot be place on this tag.

@a11ymiko a11ymiko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Stacked Removable variant user cannot place focus on any tag. This happens on Safari, Chrome and Firefox on macOS and Edge and Chrome on Win11. On Firefox on Win11 user can place focus on some removable tag, but it's not the first one in the list.

Screen.Recording.2026-04-22.at.08.48.35.mov

@a11ymiko a11ymiko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor issue but on Chrome on macOS tag with Overflow has it's content announced twice (once from tag text and once from tooltip text). Not adding 'aria-describedby' to div in Overflow sl-tag will fix that issue in Chrome without making an issue for other OSes and browsers.

Screen.Recording.2026-04-22.at.09.38.10.mov

Copilot AI review requested due to automatic review settings June 16, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Comment thread packages/components/tag/src/tag-list.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated no new comments.

@a11ymiko a11ymiko marked this pull request as ready for review June 17, 2026 06:18
@michal-sanoma michal-sanoma marked this pull request as draft June 17, 2026 08:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

5 participants