Component Lab N · Shadow boundaries

Shadow DOM Lab

Practise open, closed, nested, and slotted Shadow DOM with truthful browser boundaries and accessible, deterministic outcomes.

PRACTICE FIXTURE — intentionally built for automation practice

Open shadow root

A semantic button and live result sit inside an open root.

Nested open shadow roots

An open host contains a custom element with its own open root.

Slotted content

These nodes stay in the light DOM and are assigned to named slots in an open root.

Slotted synthetic summary

Slotted content remains selectable from the light DOM.

Accessible form in an open root

The label, required input, submit button, and live result all exist in the component’s open root.

Closed shadow root boundary

The host is observable, but its internal control is deliberately unavailable to page JavaScript and locators.

The internal closed-shadow button is rendered for browser behavior observation, but it cannot be selected from page JavaScript or Playwright locators. Test the host’s documented attributes instead.

Passing examples

Start with observable behavior

These snippets match executable tests stored inexamples/playwright-ts/.

Open-root accessibility locator
await page.getByRole('button', { name: 'Trigger open shadow action' }).click();
await expect(
  page.getByRole('status', { name: 'Open shadow result' }),
).toHaveText('Open shadow action completed.');
Closed-root boundary assertion
const closedHost = page.getByTestId('closed-shadow-host');
await expect(closedHost).toHaveAttribute('data-shadow-root', 'closed');
await expect(closedHost.locator('button')).toHaveCount(0);
expect(await closedHost.evaluate((element) => element.shadowRoot)).toBeNull();