Page under test
CSS

    Challenges

      How the page is built

      Host elementShadow rootInside
      user-card#card-hostopen.card → h2.name, button.edit
      settings-panel#panel-hostopenselect.theme, and a nested toggle-switch
      toggle-switch#notify-hostopen (nested)button.toggle
      payment-widget#payment-hostclosedinput.card-number — unreachable

      Try input.card-number: it really is on the page, but a closed root hides it from scripts, so neither Selenium nor Playwright can locate it. In real projects, ask developers for an open root or a test hook.

      Selenium vs Playwright

      PlaywrightSelenium 4
      Open rootsCSS and getBy* pierce them automaticallyGet the host, then getShadowRoot()
      Nested rootsOne selector, e.g. toggle-switch buttonChain getShadowRoot() once per level
      XPathDoes not pierce shadow rootsNot supported inside a shadow root
      Closed rootsUnreachableUnreachable
      // Selenium 4 (Java): one getShadowRoot() per level, CSS only inside a root
      SearchContext panel = driver.findElement(By.cssSelector("settings-panel")).getShadowRoot();
      SearchContext toggle = panel.findElement(By.cssSelector("toggle-switch")).getShadowRoot();
      toggle.findElement(By.cssSelector("button.toggle")).click();
      
      // Playwright (TypeScript): one selector pierces every open root
      await page.locator('toggle-switch button').click();

      Want to automate a real shadow DOM component end to end? Head to the Shadow DOM practice page.