PracticeQAAutomation logoPracticeQAAutomation

Shadow DOM

Every shadow-root scenario you'll meet in real apps: open, nested, closed, closed-inside-open, SVG inside a shadow root, an iframe inside a shadow root, and shadow roots inside an iframe. Page-level CSS/XPath can't see inside — you have to pierce each boundary the way your tool supports.

1. Open shadow roots

Two custom elements with open shadow roots — the everyday case.

💡 Playwright pierces open shadow roots automatically; Selenium 4 uses host.getShadowRoot(); Cypress uses .shadow() or includeShadowDom: true. XPath never crosses a shadow boundary — use CSS.

2. Nested shadow DOM

A shadow root inside another shadow root.

💡 Selenium: outer.getShadowRoot().findElement(inner).getShadowRoot() — one hop per level. Playwright and Cypress (with includeShadowDom) walk nested open roots for you.

3. Closed shadow DOM

mode: "closed" — element.shadowRoot returns null, so no tool can query inside.

💡 Click the host (it delegates focus to the input), type with the keyboard, press Tab then Enter; assert the host's data-result attribute. Alternative: before the page loads, patch Element.prototype.attachShadow to force mode: "open" (Playwright addInitScript, Cypress onBeforeLoad, Selenium CDP).

4. Closed shadow DOM inside an open one

You can reach the open level normally, then hit a closed boundary.

5. SVG inside a shadow root

Clickable SVG icons that live inside a shadow root.

💡 SVG elements need *[name()='svg'] in XPath — but inside a shadow root use CSS such as svg[data-icon='star'] after piercing the root.

6. iframe inside a shadow root

Pierce the shadow root first, then switch into the frame.

💡 Selenium: get the iframe element from getShadowRoot(), then switchTo().frame(thatElement). Playwright: page.frameLocator('[data-testid=iframe-in-shadow]') works through open roots.

7. Shadow DOM inside an iframe

Switch into the frame first, then pierce its open, nested and closed shadow roots.