+ 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 ✔️
+
+
+
Light DOM → Light DOM
+
+
+
+
+### II. Referencing From the LightDOM Into That Shadow DOM ❌
+
+
+
Light DOM → Shadow DOM
+
Slotted Content → 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 = `
+
+ `,
+};
+
+// 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 `
+
This connection is established by setting the for attribute on the `
+
This relationship is crucial for accessibility, as it allows assistive technologies like screen readers to correctly associate and announce the label text when focus is placed on the associated input field, thereby making forms understandable and navigable.
+
+### I. Referencing Within the Same DOM Tree ✔️
+
+
+
Light DOM → Light DOM
+
Shadow DOM → Same Shadow DOM
+
+
+
+
+### II. Referencing From Outside a Shadow DOM Into That Shadow DOM ❌
+
+
+
Light DOM → Shadow DOM
+
Slotted Content → Shadow DOM
+
+
+
When the referencing element (e.g. `
+
+##### Possible Workarounds
+
+###### 1. `Element:ariaLabelledByElements` (Non SSR compatible: to be tested)
+
+Define programmatically the `ariaLabelledByElements` property of the element. This solution creates the relationship without the use of an `id`.
+
+Light DOM → Shadow DOM Example
+
+
+
+
+
+Slotted Content → Shadow DOM Example
+
+
+
+
+
+###### 2. Set `aria-labelledby`on the host (for SSR Components : to be tested)
+
+This solution involves setting the `aria-labelledby` directly on the host. In order for this solution to work, the host must have a set `role=""` along with `tabindex="0"`.
+This defines the label text as the Accessible Name of the component, but NVDA does not announce it when an interactive element (e.g. an ``) exists inside the component.
+
+Directly setting `aria-labelledby` on the host.
+
+
+
+
+
+
+Setting `aria-labelledby` via `ElementInternals`.
+
+
+
+
+
+
+### III. Referencing From Inside a Shadow DOM Out to the Light DOM ❌
+
+
+
Shadow DOM → Light DOM
+
Shadow DOM → Slotted Content
+
+
+ A `for` attribute on an element inside a Shadow DOM{' '}
+ cannot be used to reference the id of an element outside that Shadow DOM (in the Light DOM
+ or an ancestor Shadow DOM). The way the HTML for attribute resolves an id is strictly limited to
+ the element's shadow-including tree.
+
+
+
+
+##### Possible Workaround
+
+The `Element:ariaLabelledByElements` does not work in this case.
+
+An alternative is to directly add an `id` on the host of the referencing element and an `aria-labelledby`on the target element.
+
+### IV.Referencing From a Shadow DOM Out to another Shadow DOM ❌
+
+
+
Shadow DOM → Other Shadow DOM
+
+
+##### Possible Workaround
+
+The `Element:ariaLabelledByElements` does not work in this case.
+
+An alternative would be to combine workarounds for cases II and III.
+This involves setting an `id` on the host of the referencing element and an `aria-labelledby` directly on the host of the target element along with `role=""` and `tabindex="0"`. Same limitation applies as in case II: the label text is defined as the Accessible Name of the component, but NVDA does not announce if an interactive element (e.g. an ``) exists inside the target component.
+
+
+
+
+
diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts
new file mode 100644
index 0000000000..294ea426f3
--- /dev/null
+++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts
@@ -0,0 +1,116 @@
+import { StoryObj, Args } from '@storybook/web-components';
+import { MetaExtended } from '@root/types';
+import { html } from 'lit';
+
+const meta: MetaExtended = {
+ id: '76ade552-2c03-4d6d-9dce-28daa340f7d3',
+ title:
+ 'Accessibility Practices/Foundational Structure And Semantics/Reference Relationships/Crossing The Shadow Dom/For',
+ parameters: {
+ badges: [],
+ },
+
+ decorators: [story => html`
${story()}
`],
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+// Case: Standard Light DOM to Light DOM
+export const ExampleHTML: Story = {
+ render: () => html`
+
+
+
+ `,
+};
+
+// 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`
+
+
+ `,
+};
+
+// 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`
+
+
+ `,
+};
+
+// Case: Referencing from Shadow Dom to Light DOM workaround with aria-labelledby directly set on host
+export const Example5: Story = {
+ render: () => html`
+
+
+
+ `,
+};
+
+// Case: Referencing from Shadow Dom to Slotted Content (Light) DOM workaround with aria-labelledby directly set on host
+export const Example7: Story = {
+ render: () => html`
+
+
+
+ `,
+};
+
+// Case: Standard Light DOM to Shadow DOM workaround
+export const Example4: Story = {
+ argTypes: {
+ workaround: {
+ name: 'Workaround',
+ control: {
+ type: 'radio',
+ },
+ options: ['none', 'ariaLabelledByElements (not working)'],
+ },
+ },
+ args: {
+ workaround: 'none',
+ },
+ render: (args: Args) => html`
+
+
+ `,
+};
+
+// Case: Shadow DOM to other Shadow Dom workaround
+
+export const Example6: Story = {
+ render: () => html`
+
+
+ `,
+};
From 965c742e7999aff0e752423b78218f4b3cb25dd6 Mon Sep 17 00:00:00 2001
From: Myrta Sakellariou
Date: Mon, 16 Jun 2025 13:20:44 +0200
Subject: [PATCH 3/7] add file content
---
.../aria-labelledby/aria-labelledby.docs.mdx | 2 +-
.../aria-roles/list-role/list-role.docs.mdx | 61 +++++++++++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)
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
index b0635d1cfa..2e9e7ff6c6 100644
--- 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
@@ -1,5 +1,5 @@
import { Meta, Canvas, Source, Controls } from '@storybook/blocks';
-import * as CCR from './aria-labelled-by.stories';
+import * as CCR from './aria-labelledby.stories';
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
index e69de29bb2..8cca546c29 100644
--- 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
@@ -0,0 +1,61 @@
+import { Meta, Canvas, Source, Controls } from '@storybook/blocks';
+import * as CCR from './list-role.stories';
+
+
+
+# role="list"
+
+
+ The `role` attribute defines the purpose of a web element (e.g. `role="menu"`, `role="tab"`,
+ `role="dialog"`, `role="accordion"`, etc.) for assistive technologies like screen readers.
+
+
+
+ While native HTML elements have implicit roles, the role attribute adds meaning to non-semantic
+ elements like `div` or `span` when used for custom interactive components.
+
+
+### I. All Within the Same DOM Tree ✔️
+
+
+
Basic HTML Example
+
+
+
+
Light DOM → Component with Slotted Children
+
+
+
+
+
Component host → Same Component Slotted Children
+
+
+
+### II. Parent in the Light DOM - Children Into That Shadow DOM
+
+
+
Light DOM → Shadow DOM ❓
+
+
+
+
Slotted Parent → Shadow DOM ❌
+
+
+Light DOM elements with an ARIA parent role cannot be associated to children elements with the
+respective ARIA child roles across the Shadow DOM.
+
+### III. Parent in the Shadow DOM - Children Out to the Light DOM ✔️
+
+
+
Shadow DOM → Light DOM
+
+
+
+
+### IV. Referencing From One Shadow DOM To Another Shadow DOM ✔️
+
+
-
+
### II. Referencing From Outside a Shadow DOM Into That Shadow DOM ❌
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
index 2e9e7ff6c6..b420befa0c 100644
--- 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
@@ -15,11 +15,8 @@ import * as CCR from './aria-labelledby.stories';
### I. Referencing Within the Same DOM Tree ✔️
-
-
Light DOM → Light DOM
-
-
-
+{' '}
+
### II. Referencing From Outside a Shadow DOM Into That Shadow DOM ❌
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
index ff70e5dd22..cc38d5ce17 100644
--- 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
@@ -7,16 +7,11 @@ import * as CCR from './for.stories';
##
-
The native HTML `for`/`id` relationship is a fundamental way to connect a `
-
This connection is established by setting the for attribute on the `
-
This relationship is crucial for accessibility, as it allows assistive technologies like screen readers to correctly associate and announce the label text when focus is placed on the associated input field, thereby making forms understandable and navigable.
+
The native HTML `for`/`id` relationship is a fundamental way to connect a `
-### I. Referencing Within the Same DOM Tree ✔️
+
This connection is established by setting the for attribute on the `
-
-
Light DOM → Light DOM
-
Shadow DOM → Same Shadow DOM
-
+### I. Referencing Within the Same DOM Tree ✔️
@@ -33,7 +28,7 @@ import * as CCR from './for.stories';
###### 1. `Element:ariaLabelledByElements` (Non SSR compatible: to be tested)
-Define programmatically the `ariaLabelledByElements` property of the element. This solution creates the relationship without the use of an `id`.
+Defines programmatically the `ariaLabelledByElements` property of the element: `this.internalEl.ariaLabelledByElements = [labelEl]`. This solution creates the relationship without the use of an `id`.
Light DOM → Shadow DOM Example
@@ -79,14 +74,14 @@ Setting `aria-labelledby` via `ElementInternals`.
the element's shadow-including tree.
-
-
##### Possible Workaround
The `Element:ariaLabelledByElements` does not work in this case.
An alternative is to directly add an `id` on the host of the referencing element and an `aria-labelledby`on the target element.
+
+
### IV.Referencing From a Shadow DOM Out to another Shadow DOM ❌
@@ -95,7 +90,7 @@ An alternative is to directly add an `id` on the host of the referencing element
##### Possible Workaround
-The `Element:ariaLabelledByElements` does not work in this case.
+Same as above, the `Element:ariaLabelledByElements` does not work in this case.
An alternative would be to combine workarounds for cases II and III.
This involves setting an `id` on the host of the referencing element and an `aria-labelledby` directly on the host of the target element along with `role=""` and `tabindex="0"`. Same limitation applies as in case II: the label text is defined as the Accessible Name of the component, but NVDA does not announce if an interactive element (e.g. an ``) exists inside the target component.
diff --git a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts
index 294ea426f3..d06bd1c508 100644
--- a/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts
+++ b/packages/documentation/src/stories/accessibility-practices/functional-structure-and-semantics/reference-relationships/crossing-the-shadow-dom/for/for.stories.ts
@@ -101,7 +101,7 @@ export const Example4: Story = {
workaround: 'none',
},
render: (args: Args) => html`
-
+
`,
};
From fa22dc7a180d4de3661172d0e31630b318223312 Mon Sep 17 00:00:00 2001
From: Myrta Sakellariou
Date: Tue, 17 Jun 2025 13:35:50 +0200
Subject: [PATCH 6/7] minor
---
.../post-test-button-control.tsx | 6 +++---
.../post-test-button-control2.tsx | 6 +++---
.../aria-controls/aria-controls.stories.ts | 12 ++++++------
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/packages/components/src/components/post-test-button-control/post-test-button-control.tsx b/packages/components/src/components/post-test-button-control/post-test-button-control.tsx
index 2789e17166..9731dd588a 100644
--- a/packages/components/src/components/post-test-button-control/post-test-button-control.tsx
+++ b/packages/components/src/components/post-test-button-control/post-test-button-control.tsx
@@ -19,7 +19,7 @@ export class PostTestButtonControl {
private readonly handleToggleClick = () => {
if (this.host && this.internalEl) {
const timestamp = Date.now();
- this.internalEl.innerHTML = `
Controlled Text shown at ${timestamp}.
`;
+ this.internalEl.innerHTML = `
Text shown at ${timestamp}.
`;
}
};
@@ -56,7 +56,7 @@ export class PostTestButtonControl {
render() {
return (
- Toggle Text
+ Button