Lumos ShadowDOM 🪄
Automate Shadow DOM elements in Selenium with zero headache.
Shadow DOMs are tricky. They hide elements from standard Selenium commands. lumos-shadowdom solves this by letting you traverse nested shadow roots automatically using simple paths or even just text.
🚀 Quick Start
1. Install
pip install lumos-shadowdom
2. Use it in your code
Lumos adds new superpowers to your Selenium driver automatically!
from selenium import webdriver
import lumos # <--- Import this to activate magic!
driver = webdriver.Chrome()
driver.get("https://example.com")
# 🟢 Scenario 1: You know the path (or found it using our tool below)
# Syntax: "host-element > nested-host > target-element"
driver.find_shadow("my-app > settings-panel > button#save").click()
# 🟢 Scenario 2: Find ALL matching elements in shadow DOM
buttons = driver.find_all_shadow("nav-menu > button.action")
for btn in buttons:
print(btn.text)
# 🟢 Scenario 3: You just want to find a button with specific text
# This scans the ENTIRE page (including all shadow roots) for you.
driver.find_shadow_text("Confirm Purchase").click()
🕵️ How to Find the Path? (The Magic Tool)
Don't waste time inspecting elements manually. Use our Discovery Tool to generate the exact path instantly.
- Open your website in Chrome.
- Open DevTools (Press
F12or Right Click -> Inspect). - Go to the Console tab.
- Paste the script below and hit Enter.
- Hold
Altand Click on any element in the page. - The Console will show you the Short Path and Long Path. Copy the "Short Path" and use it in
driver.find_shadow().
Copy this Script:
// Lumos Discovery Tool v2.0
// Paste this into Chrome Console, then Alt + Click any element.
document.addEventListener('click', function(e) {
if (!e.altKey) return;
e.preventDefault();
e.stopImmediatePropagation();
// 1. Get the real target (piercing Shadow DOM)
let composed = e.composedPath();
let target = composed[0];
let fullPath = [];
let shortPath = [];
let currentScope = [];
// 2. Traverse up the tree
for (let i = 0; i < composed.length; i++) {
let node = composed[i];
// Stop at document root
if (node.nodeType !== Node.ELEMENT_NODE && !(node instanceof ShadowRoot)) {
if (currentScope.length > 0) {
fullPath.unshift(currentScope.join(' '));
shortPath.unshift(getShortSelector(currentScope));
}
break;
}
// Handle Shadow Root Boundary
if (node instanceof ShadowRoot) {
fullPath.unshift(currentScope.join(' '));
shortPath.unshift(getShortSelector(currentScope));
currentScope = [];
continue;
}
// Build Selector for current node
let selector = node.tagName.toLowerCase();
if (node.id) {
selector += '#' + node.id;
} else if (node.className && typeof node.className === 'string') {
selector += '.' + node.className.trim().split(/\s+/).join('.');
}
currentScope.unshift(selector);
}
function getShortSelector(scope) {
let short = [];
for (let j = scope.length - 1; j >= 0; j--) {
short.unshift(scope[j]);
if (scope[j].includes('#')) break; // Stop at ID for brevity
}
return short.join(' ');
}
console.log(`%c 🪄 Lumos Path Discovered!`, 'background: #222; color: #bada55; font-size: 14px; padding: 4px;');
console.log(`%c Short Path:`, 'font-weight: bold; color: #007bff;', shortPath.join(' > '));
console.log(`%c Long Path: `, 'color: #666;', fullPath.join(' > '));
}, true);
📚 API Reference
driver.find_shadow(path: str, timeout: int = 10) -> WebElement
Finds a single element inside nested shadow DOMs.
- path: A string describing the path to the element. Use
>to separate shadow boundaries.- Example:
user-profile > #settings > input.name - Means: Find
user-profile, enter its shadow root, find#settings, enter its shadow root, findinput.name.
- Example:
- timeout: Seconds to wait for element (default: 10).
- Returns: The matching WebElement.
driver.find_all_shadow(path: str, timeout: int = 10) -> List[WebElement]
Finds ALL elements matching the path inside nested shadow DOMs.
- path: Same syntax as
find_shadow. The last selector matches multiple elements.- Example:
app-root > nav-menu > buttonreturns all buttons in the nav.
- Example:
- Returns: List of matching WebElements (may be empty).
driver.find_shadow_text(text: str, timeout: int = 10) -> WebElement
Recursively searches every shadow root in the DOM for an element containing the specified text.
- text: The text to look for (case-sensitive).
- Returns: The first matching WebElement found.
❓ FAQ
Q: Why do I need this?
A: Standard driver.find_element() cannot "see" inside a Shadow DOM. You usually have to write complex code to get the shadow root, then find the element, then repeat for nested roots. Lumos does this in one line.
Q: Does it work with Selenium 3? A: Yes! It is fully compatible with Selenium 3.141.0 and Selenium 4+.
Q: The Discovery Tool shows a path, but Lumos can't find it. A:
- Ensure the element is visible on the screen.
- If the path is very long, try using the "Short Path" provided by the tool.
- Check if the element is inside an
iframe. Lumos currently supports Shadow DOM, butiframesupport is separate (you must switch to the iframe first).
⚠️ Limitations & Edge Cases
1. Shadow DOM inside <iframe>
Lumos cannot automatically cross <iframe> boundaries because they are separate documents.
Solution: Switch to the iframe first using Selenium, then use Lumos.
# 1. Switch to the iframe
iframe = driver.find_element(By.ID, "my-frame")
driver.switch_to.frame(iframe)
# 2. Now use Lumos inside the iframe
driver.find_shadow("host-inside-frame > button").click()
2. Closed Shadow Roots
Lumos cannot access Shadow DOMs created with mode='closed'. This is a browser security limitation that no tool can bypass standardly.
Author: Dhiraj License: MIT
Metadata
Release files for lumos-shadowdom 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lumos_shadowdom-0.2.0.tar.gz | 12.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lumos_shadowdom-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.4 kB
Release files / lumos_shadowdom-0.2.0.tar.gz
| Download URL | lumos_shadowdom-0.2.0.tar.gz |
|---|---|
| Size | 12.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6eb424032019133c21b5315c8652c3b8437a025abca04128a06e553557552187
|
|
BLAKE2b-256 checksum How to use checksums |
1fee34e2229b865290547ecc16bc013044cd66686a1bfd366e6ae9859bc6186a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|
Release files / lumos_shadowdom-0.2.0-py3-none-any.whl
| Download URL | lumos_shadowdom-0.2.0-py3-none-any.whl |
|---|---|
| Size | 8.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
000f1328f1c0432236f617ba0edfb61e4ebf7784b545251027a20d19f78d5956
|
|
BLAKE2b-256 checksum How to use checksums |
42ab4f6baacf85cd715fbc70cf41f1a9059f5c1ac53259ff747ca3ac65f5635e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.0
|