From 07246c3bcdcdc5b2957cd4bc3af3a7350f4a7d7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Jun 2025 08:54:36 +0200 Subject: [PATCH 1/7] chore(deps): update dependency cypress-axe to v1.6.0 (#5616) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [cypress-axe](https://redirect.github.com/component-driven/cypress-axe) | [`1.5.0` -> `1.6.0`](https://renovatebot.com/diffs/npm/cypress-axe/1.5.0/1.6.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/cypress-axe/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/cypress-axe/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/cypress-axe/1.5.0/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/cypress-axe/1.5.0/1.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- > [!WARNING] > Some dependencies could not be looked up. Check the Dependency Dashboard for more information. --- ### Release Notes
component-driven/cypress-axe (cypress-axe) ### [`v1.6.0`](https://redirect.github.com/component-driven/cypress-axe/releases/tag/v1.6.0) [Compare Source](https://redirect.github.com/component-driven/cypress-axe/compare/v1.5.0...v1.6.0) ##### Features - add Cypress 14 as a peer dependency ([#​180](https://redirect.github.com/component-driven/cypress-axe/issues/180)) ([f47688d](https://redirect.github.com/component-driven/cypress-axe/commit/f47688d11525f248b3fab0765463d4ba8a7a900f))
--- ### Configuration 📅 **Schedule**: Branch creation - "before 4am on the first day of the month" in timezone Europe/Zurich, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/swisspost/design-system). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/components/package.json | 2 +- packages/documentation/package.json | 2 +- pnpm-lock.yaml | 38 ++++++++++++++--------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index fcffb9bc86..f95a659c42 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -74,7 +74,7 @@ "bootstrap": "5.3.3", "copyfiles": "2.4.1", "cypress": "14.3.2", - "cypress-axe": "1.5.0", + "cypress-axe": "1.6.0", "cypress-storybook": "1.0.0", "eslint": "9.18.0", "eslint-plugin-react": "7.37.4", diff --git a/packages/documentation/package.json b/packages/documentation/package.json index 5837da9429..c27c7d9f3e 100644 --- a/packages/documentation/package.json +++ b/packages/documentation/package.json @@ -64,7 +64,7 @@ "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "cypress": "14.3.2", - "cypress-axe": "1.5.0", + "cypress-axe": "1.6.0", "eslint": "9.18.0", "eslint-plugin-markdown": "5.1.0", "eslint-plugin-mdx": "3.1.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6588b3ae7..07d99b3850 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,8 +129,8 @@ importers: specifier: 14.3.2 version: 14.3.2 cypress-axe: - specifier: 1.5.0 - version: 1.5.0(axe-core@4.7.0)(cypress@14.3.2) + specifier: 1.6.0 + version: 1.6.0(axe-core@4.7.0)(cypress@14.3.2) cypress-storybook: specifier: 1.0.0 version: 1.0.0(cypress@14.3.2) @@ -448,8 +448,8 @@ importers: specifier: 14.3.2 version: 14.3.2 cypress-axe: - specifier: 1.5.0 - version: 1.5.0(axe-core@4.7.0)(cypress@14.3.2) + specifier: 1.6.0 + version: 1.6.0(axe-core@4.7.0)(cypress@14.3.2) eslint: specifier: 9.18.0 version: 9.18.0(jiti@2.4.2) @@ -5896,12 +5896,12 @@ packages: custom-event@1.0.1: resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} - cypress-axe@1.5.0: - resolution: {integrity: sha512-Hy/owCjfj+25KMsecvDgo4fC/781ccL+e8p+UUYoadGVM2ogZF9XIKbiM6KI8Y3cEaSreymdD6ZzccbI2bY0lQ==} + cypress-axe@1.6.0: + resolution: {integrity: sha512-C/ij50G8eebBrl/WsGT7E+T/SFyIsRZ3Epx9cRTLrPL9Y1GcxlQGFoAVdtSFWRrHSCWXq9HC6iJQMaI89O9yvQ==} engines: {node: '>=10'} peerDependencies: axe-core: ^3 || ^4 - cypress: ^10 || ^11 || ^12 || ^13 + cypress: ^10 || ^11 || ^12 || ^13 || ^14 cypress-each@1.14.0: resolution: {integrity: sha512-mUpF5KSSBy0kQaxYPLFswHceYoUMpXWRAJJ2LrcuHu1Vp5lgMw96lLtpDsNGID13FQhBfPSW6FmYBBCQqdJRBg==} @@ -11862,7 +11862,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.1) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.1) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.1)(jiti@2.4.2)(karma@6.4.4)(less@4.2.2)(ng-packagr@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(tslib@2.8.1)(typescript@5.8.3))(postcss@8.5.2)(sass-embedded@1.81.0)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3) @@ -11913,8 +11913,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) - webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-server: 5.2.0(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0(esbuild@0.25.0)) optionalDependencies: @@ -11951,7 +11951,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.1) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) '@angular-devkit/core': 19.2.0(chokidar@4.0.1) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.1)(jiti@2.4.2)(karma@6.4.4)(less@4.2.2)(ng-packagr@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(tslib@2.8.1)(typescript@5.8.3))(postcss@8.5.2)(sass-embedded@1.81.0)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.8.3) @@ -12002,8 +12002,8 @@ snapshots: tslib: 2.8.1 typescript: 5.8.3 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) - webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-server: 5.2.0(webpack@5.98.0) webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.98.0(esbuild@0.25.0)) optionalDependencies: @@ -12036,12 +12036,12 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)))(webpack@5.98.0(esbuild@0.25.0))': + '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.1)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0))': dependencies: '@angular-devkit/architect': 0.1902.0(chokidar@4.0.1) rxjs: 7.8.1 webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-server: 5.2.0(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -17383,7 +17383,7 @@ snapshots: custom-event@1.0.1: {} - cypress-axe@1.5.0(axe-core@4.7.0)(cypress@14.3.2): + cypress-axe@1.6.0(axe-core@4.7.0)(cypress@14.3.2): dependencies: axe-core: 4.7.0 cypress: 14.3.2 @@ -24286,7 +24286,7 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.2(webpack@5.98.0(esbuild@0.25.0)): + webpack-dev-middleware@7.4.2(webpack@5.98.0): dependencies: colorette: 2.0.20 memfs: 4.9.3 @@ -24297,7 +24297,7 @@ snapshots: optionalDependencies: webpack: 5.98.0(esbuild@0.25.0) - webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.0)): + webpack-dev-server@5.2.0(webpack@5.98.0): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -24324,7 +24324,7 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.0)) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.0 optionalDependencies: webpack: 5.98.0(esbuild@0.25.0) From 492ec4c251d8eabef87da8c40b44a683cde05d39 Mon Sep 17 00:00:00 2001 From: Myrta Sakellariou Date: Fri, 13 Jun 2025 14:57:49 +0200 Subject: [PATCH 2/7] feat(documentation): add cross-component referencing stories --- .../aria-controls/aria-controls.docs.mdx | 93 +++++++++++ .../aria-controls/aria-controls.stories.ts | 144 ++++++++++++++++++ .../aria-describedby.docs.mdx | 93 +++++++++++ .../aria-describedby.stories.ts | 105 +++++++++++++ .../aria-labelledby/aria-labelledby.docs.mdx | 91 +++++++++++ .../aria-labelledby.stories.ts | 105 +++++++++++++ .../aria-roles/list-role/list-role.docs.mdx | 0 .../aria-roles/list-role/list-role.stories.ts | 81 ++++++++++ .../crossing-the-shadow-dom/for/for.docs.mdx | 106 +++++++++++++ .../for/for.stories.ts | 116 ++++++++++++++ 10 files changed, 934 insertions(+) create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.docs.mdx create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.stories.ts create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.docs.mdx create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.stories.ts create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.docs.mdx create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.stories.ts create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.docs.mdx create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.stories.ts create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.docs.mdx create mode 100644 packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.docs.mdx b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.docs.mdx new file mode 100644 index 0000000000..3d96e96d8f --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.docs.mdx @@ -0,0 +1,93 @@ +import { Meta, Canvas, Source, Controls } from '@storybook/blocks'; +import * as CCR from './aria-controls.stories'; + + + +# Aria-Controls + +
+ In the Light DOM, `aria-controls` is used to indicate that an element controls the visibility or + interaction of another element, by referencing its `id`. +
+
+ This creates an accessibility relationship, helping assistive technologies understand the + interaction between the trigger and the controlled content. +
+ +### I. Referencing Within the Same DOM Tree ✔️ + + + + +### II. Referencing From the LightDOM Into That Shadow DOM ❌ + + + +The `aria-controls` attribute cannot reference elements inside a Shadow DOM from the Light DOM. This is because IDs inside Shadow roots are encapsulated and inaccessible across boundaries, preventing valid accessibility relationships. + +##### Possible Workarounds + +###### 1. `Element:ariaControlsElements` + +Light DOM → Shadow DOM Example + +
+ +
+ +Slotted Content → Shadow DOM Example + +
+ +
+ +###### 2. Set `aria-labelledby`on the host (for SSR Components: to be tested) + +An option is to set the following attributes directly on the host of the target component: + +
    +
  • the `aria-labelledby`
  • +
  • a semantic `role` (e.g. `textbox`, `button`)
  • +
  • `tabindex="0"`
  • +
+ +Light DOM → Shadow DOM Example + + +Slotted Content → Shadow DOM Example + + +### III. Referencing From Inside a Shadow DOM Out to the Light DOM ❌ + +
    +
  • Shadow DOM → Light DOM
  • +
  • Shadow DOM → Slotted Content
  • +
+ +Similarly to the previous case, referencing an element located outside a Shadow DOM from an element inside that Shadow DOM using standard `aria-labelledby` is not possible. The strong encapsulation of the Shadow DOM prevents elements within it from directly accessing and referencing the IDs of elements in the Light DOM. + +##### Possible Workaround + +An option is to set the `id` directly on the host of the referencing element. Please note that, in this case all the visible text inside the referencing component will be assigned as the Accessible Name of the target element. + + + +##### IV. Referencing From One Shadow DOM To Another Shadow DOM + +
    +
  • Shadow DOM → Other Shadow DOM
  • +
+ +what happens + +##### Possible Workaround + +workaround text + + diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.stories.ts new file mode 100644 index 0000000000..08c58c4dc7 --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-controls/aria-controls.stories.ts @@ -0,0 +1,144 @@ +import { StoryObj, Args } from '@storybook/web-components'; +import { MetaExtended } from '@root/types'; +import { html } from 'lit'; + +const meta: MetaExtended = { + id: '76ade552-2c03-4d6d-9dce-28daa346f3g3', + title: + 'Accessibility Practices/Foundational Structure And Semantics/Reference Relationships/Crossing The Shadow Dom/Aria-Controls', + parameters: { + badges: [], + }, +}; + +export default meta; + +type Story = StoryObj; + +// Case: Standard Light DOM to Light DOM +function handleToggleClick(toggleId: string, textId: string): void { + const toggle = document.getElementById(toggleId); + const text = document.getElementById(textId); + if (toggle && text) { + const timestamp = Date.now(); // or use a counter + text.innerHTML = `

Controlled Text shown at ${timestamp}.

`; + } +} + +export const ExampleHTML = () => { + return html` + +
+

Controlled Text shown at xxxxxxxxxxx.

+
+ `; +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Light DOM (Element) + +export const Example2a: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaControlsElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + + Toggle Text + + `, +}; + +export const Example2b: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaControlsElements'], + }, + }, + args: { + workaround: 'none', + }, + render: () => html` + + + `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Slotted Text (Element) +export const Example3: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaControlsElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + + Toggle Text + + `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Light DOM (Element) workaround with aria-controls directly set on host +export const Example4: Story = { + render: () => html` + Control Element + Controlled Element 4 + `, +}; + +// Case: Referencing from Shadow Dom to Slotted Text (Light DOM) workaround with aria-controls directly set on host +export const Example5: Story = { + render: () => html` + + Controlled Element 5 (Slotted) + + `, +}; + +// Case: Standard Light DOM to Shadow DOM +export const Example6: Story = { + render: () => html` +
+ + Control Element +
+ + `, +}; + +// Case: Shadow DOM to other Shadow Dom workaround +export const Example7: Story = { + render: () => html` + + + `, +}; diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.docs.mdx b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.docs.mdx new file mode 100644 index 0000000000..03cb87c88f --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.docs.mdx @@ -0,0 +1,93 @@ +import { Meta, Canvas, Source, Controls } from '@storybook/blocks'; +import * as CCR from './aria-describedby.stories'; + + + +# Aria-Describedby + +
+ In the Light DOM, `aria-describedby` is used to give an element its accessible name by referencing + the id of another element or multiple elements that contain the desired text. +
+
+ This is useful when a simple text label isn't directly associated or when the name needs to be + combined from different pieces of text. +
+ +### I. Referencing Within the Same DOM Tree ✔️ + +
    +
  • Light DOM → Light DOM
  • + +
+ + +### II. Referencing From Outside a Shadow DOM Into That Shadow DOM ❌ + +
    +
  • Light DOM → Shadow DOM
  • +
  • Slotted Content → Shadow DOM
  • +
+ +Standard `aria-describedby` cannot reference elements located inside a Shadow DOM from outside of it. This is because IDs inside a Shadow DOM are encapsulated and not accessible from the Light DOM or other Shadow DOMs. + +##### Possible Workarounds + +###### 1. `Element:ariaDescribedByElements` (Non SSR compatible: to be tested) + +Light DOM → Shadow DOM Example + +
+ +
+ +Slotted Content → Shadow DOM Example + +
+ +
+ +###### 2. Set `aria-describedby`on the host (for SSR Components: to be tested) + +An option is to set the following attributes directly on the host of the target component: + +
    +
  • the `aria-describedby`
  • +
  • a semantic `role` (e.g. `textbox`, `button`)
  • +
  • `tabindex="0"`
  • +
+ +Light DOM → Shadow DOM Example + + +Slotted Content → Shadow DOM Example + + +### III. Referencing From Inside a Shadow DOM Out to the Light DOM ❌ + +
    +
  • Shadow DOM → Light DOM
  • +
  • Shadow DOM → Slotted Content
  • +
+ +Similarly to the previous case, referencing an element located outside a Shadow DOM from an element inside that Shadow DOM using standard `aria-describedby` is not possible. The strong encapsulation of the Shadow DOM prevents elements within it from directly accessing and referencing the IDs of elements in the Light DOM. + +##### Possible Workaround + +An option is to set the `id` directly on the host of the referencing element. Please note that, in this case all the visible text inside the referencing component will be assigned as the Accessible Name of the target element. + + + +##### IV. Referencing From One Shadow DOM To Another Shadow DOM + +
    +
  • Shadow DOM → Other Shadow DOM
  • +
+ +Referencing an element located inside one Shadow DOM from an element located inside a different, separate Shadow DOM using standard `aria-describedby` is also not possible. Similar to the other cross-boundary scenarios, the IDs are scoped within their respective Shadow DOMs. + +##### Possible Workaround + +In this case, in order for the relationship to work, the referencing element must have the `id` directly set on its host (see workaround of case III) and the target element must have the `aria-describedby`, `role` and `tabindex="0"` set on its host (see workaround of case II). + + diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.stories.ts new file mode 100644 index 0000000000..b7a3df5246 --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-describedby/aria-describedby.stories.ts @@ -0,0 +1,105 @@ +import { StoryObj, Args } from '@storybook/web-components'; +import { MetaExtended } from '@root/types'; +import { html } from 'lit'; + +const meta: MetaExtended = { + id: '76ade552-2c03-4d6d-9dce-28daa340f3g6', + title: + 'Accessibility Practices/Foundational Structure And Semantics/Reference Relationships/Crossing The Shadow Dom/Aria-DescribedBy', + parameters: { + badges: [], + }, +}; + +export default meta; + +type Story = StoryObj; + +// Case: Standard Light DOM to Light DOM +export const ExampleHTML: Story = { + render: () => html` +
+ +
+ My Description + `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Light DOM (Element) workaround setting programmatically the relevant Element property +export const Example2: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaDescribedByElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + + My Description + `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Slotted Content (Element) workaround setting programmatically the relevant Element property +export const Example3: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaDescribedByElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + My Description + `, +}; + +// Case: Referencing from Shadow Dom to Light DOM workaround with aria-describedby directly set on host +export const Example4: Story = { + render: () => html` + + My Description + `, +}; + +// Case: Referencing from Shadow Dom to Slotted Content (Light DOM) workaround with aria-describedby directly set on host +export const Example5: Story = { + render: () => html` + My Description + + `, +}; + +// Case: Standard Light DOM to Shadow DOM workaround +export const Example6: Story = { + render: () => html` +
+ +
+ + `, +}; + +// Case: Shadow DOM to other Shadow Dom workaround +export const Example7: Story = { + render: () => html` + + + `, +}; diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.docs.mdx b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.docs.mdx new file mode 100644 index 0000000000..b0635d1cfa --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.docs.mdx @@ -0,0 +1,91 @@ +import { Meta, Canvas, Source, Controls } from '@storybook/blocks'; +import * as CCR from './aria-labelled-by.stories'; + + + +# Aria-Labelledby + +
+ In the Light DOM, `aria-labelledby` is used to give an element its accessible name by referencing + the id of another element or multiple elements that contain the desired text. +
+
+ This is useful when a simple text label isn't directly associated or when the name needs to be + combined from different pieces of text. +
+### I. Referencing Within the Same DOM Tree ✔️ + +
    +
  • Light DOM → Light DOM
  • + +
+ +### II. Referencing From Outside a Shadow DOM Into That Shadow DOM ❌ + +
    +
  • Light DOM → Shadow DOM
  • +
  • Slotted Content → Shadow DOM
  • +
+ +Standard `aria-labelledby` cannot reference elements located inside a Shadow DOM from outside of it. This is because IDs inside a Shadow DOM are encapsulated and not accessible from the Light DOM or other Shadow DOMs. + +##### Possible Workarounds + +###### 1. `Element:ariaLabelledByElements` (Non SSR compatible: to be tested) + +Light DOM → Shadow DOM Example + +
+ +
+ +Slotted Content → Shadow DOM Example + +
+ +
+ +###### 2. Set `aria-labelledby`on the host (for SSR Components: to be tested) + +An option is to set the following attributes directly on the host of the target component: + +
    +
  • the `aria-labelledby`
  • +
  • a semantic `role` (e.g. `textbox`, `button`)
  • +
  • `tabindex="0"`
  • +
+ +Light DOM → Shadow DOM Example + + +Slotted Content → Shadow DOM Example + + +### III. Referencing From Inside a Shadow DOM Out to the Light DOM ❌ + +
    +
  • Shadow DOM → Light DOM
  • +
  • Shadow DOM → Slotted Content
  • +
+ +Similarly to the previous case, referencing an element located outside a Shadow DOM from an element inside that Shadow DOM using standard `aria-labelledby` is not possible. The strong encapsulation of the Shadow DOM prevents elements within it from directly accessing and referencing the IDs of elements in the Light DOM. + +##### Possible Workaround + +An option is to set the `id` directly on the host of the referencing element. Please note that, in this case all the visible text inside the referencing component will be assigned as the Accessible Name of the target element. + + + +##### IV. Referencing From One Shadow DOM To Another Shadow DOM + +
    +
  • Shadow DOM → Other Shadow DOM
  • +
+ +Referencing an element located inside one Shadow DOM from an element located inside a different, separate Shadow DOM using standard `aria-labelledby` is also not possible. Similar to the other cross-boundary scenarios, the IDs are scoped within their respective Shadow DOMs. + +##### Possible Workaround + +In this case, in order for the relationship to work, the referencing element must have the `id` directly set on its host (see workaround of case III) and the target element must have the `aria-labelledby`, `role` and `tabindex="0"` set on its host (see workaround of case II). + + diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.stories.ts new file mode 100644 index 0000000000..5af88a2f35 --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-labelledby/aria-labelledby.stories.ts @@ -0,0 +1,105 @@ +import { StoryObj, Args } from '@storybook/web-components'; +import { MetaExtended } from '@root/types'; +import { html } from 'lit'; + +const meta: MetaExtended = { + id: '76ade552-2c03-4d6d-9dce-28daa340f7d5', + title: + 'Accessibility Practices/Foundational Structure And Semantics/Reference Relationships/Crossing The Shadow Dom/Aria-LabelledBy', + parameters: { + badges: [], + }, +}; + +export default meta; + +type Story = StoryObj; + +// Case: Standard Light DOM to Light DOM +export const ExampleHTML: Story = { + render: () => html` + My Text +
+ +
+ `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Light DOM (Element) workaround setting programmatically the relevant Element property +export const Example2: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaLabelledByElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + My Text + + `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Slotted Content (Element) workaround setting programmatically the relevant Element property +export const Example3: Story = { + argTypes: { + workaround: { + name: 'Workaround', + control: { + type: 'radio', + }, + options: ['none', 'ariaLabelledByElements'], + }, + }, + args: { + workaround: 'none', + }, + render: (args: Args) => html` + My Text + `, +}; + +// Case: Referencing from Shadow Dom to the Light DOM workaround with aria-labelledby directly set on host +export const Example4: Story = { + render: () => html` + My Text + + `, +}; + +// Case: Referencing from Shadow Dom to Slotted Content (Light) DOM workaround with aria-labelledby directly set on host +export const Example5: Story = { + render: () => html` + My Text + `, +}; + +// Case: Standard Light DOM to Shadow DOM workaround +export const Example6: Story = { + render: () => html` + +
+ +
+ `, +}; + +// Case: Shadow DOM to other Shadow Dom workaround +export const Example7: Story = { + render: () => html` + + + `, +}; diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.docs.mdx b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.docs.mdx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.stories.ts new file mode 100644 index 0000000000..f3f60e905e --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/aria-roles/list-role/list-role.stories.ts @@ -0,0 +1,81 @@ +import { StoryObj } from '@storybook/web-components'; +import { MetaExtended } from '@root/types'; +import { html } from 'lit'; + +const meta: MetaExtended = { + id: '76ade552-2c03-4d6d-9dce-28daa346f456', + title: + 'Accessibility Practices/Foundational Structure And Semantics/Reference Relationships/Crossing The Shadow Dom/Aria-Roles/List Role', + parameters: { + badges: [], + }, +}; + +export default meta; + +type Story = StoryObj; + +// Case: Standard Light DOM to Light DOM +export const ExampleHTML: Story = { + render: () => html` +
+
item 1
+
item 2
+
item 3
+
+ `, +}; + +// Case: Light Dom to child components with Slotted content +export const Example1a: Story = { + render: () => html` +
+ +
item 1
+
item 2
+
item 3
+
+
+ `, +}; + +// Case: Parent in the Light - children with Slotted content same component +export const Example1b: Story = { + render: () => html` + +
item 1
+
item 2
+
item 3
+
+ `, +}; + +// Case: Parent in the Light (Component host) -> Children in the ShadowDOM +export const Example2a: Story = { + render: () => + html` `, +}; + +// Case: Parent in Light (slotted content) - Children in the ShadowDOM +export const Example2b: Story = { + render: () => + html`
+
`, +}; + +// Case: Parent in the Shadow - Children in the Light +export const Example3: Story = { + render: () => html` + +
item 1
+
item 2
+
item 3
+
+ `, +}; + +// Case: Referencing from Shadow DOM (Host Attribute) to Slotted Content (Element) +export const Example4: Story = { + render: () => html` `, +}; diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.docs.mdx b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.docs.mdx new file mode 100644 index 0000000000..ff70e5dd22 --- /dev/null +++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.docs.mdx @@ -0,0 +1,106 @@ +import { Meta, Canvas, Source, Controls } from '@storybook/blocks'; +import * as CCR from './for.stories'; + + + +# for + +## + +
The native HTML `for`/`id` relationship is a fundamental way to connect a `