Implementing a CSS Sandbox for Qiankun: Shadow DOM and Scoped CSS Isolation
This article explains how to build a CSS sandbox for Qiankun micro‑frontends by using Shadow DOM for strict style isolation and a Scoped CSS approach for experimental isolation, providing step‑by‑step code examples, underlying principles, and a final Web Component implementation.
The article begins with a brief introduction, stating that it will demonstrate how to create a CSS sandbox for Qiankun, complementing a previous tutorial on a JavaScript sandbox.
Preparation
Three files are required: index.html (the entry HTML), shadowDOMIsolation.js (Shadow DOM sandbox implementation), and scopedCSSIsolation.js (Scoped CSS sandbox implementation). The source code is hosted in the mini-css-sandbox repository.
Shadow DOM Sandbox
Principle
Shadow DOM attaches a hidden, independent DOM tree to a regular DOM element (the shadow host), providing hard style isolation.
Implementation
The shadowDOMIsolation function trims the HTML string, creates a container div, extracts the root element, clears its content, attaches a shadow root (using attachShadow({mode:'open'}) or the legacy createShadowRoot), injects the original inner HTML into the shadow root, and returns the element.
function shadowDOMIsolation(contentHtmlString) {
// Clean HTML
contentHtmlString = contentHtmlString.trim();
// Create container div
const containerElement = document.createElement('div');
containerElement.innerHTML = contentHtmlString;
// Get root div element
const appElement = containerElement.firstChild;
const { innerHTML } = appElement;
appElement.innerHTML = '';
let shadow;
if (appElement.attachShadow) {
// Modern API
shadow = appElement.attachShadow({ mode: 'open' });
} else {
// Legacy API
shadow = appElement.createShadowRoot();
}
// Populate shadow DOM
shadow.innerHTML = innerHTML;
return appElement;
}The resulting effect shows that external styles (e.g., a global red p style) do not affect the content inside the shadow DOM.
Scoped CSS Sandbox
Principle
Scoped CSS extracts the <style> text from a micro‑app and rewrites each selector by prefixing it with a unique container selector such as div[data-app-name="MyApp"], ensuring the styles apply only within that container.
Implementation
The core functions are: processCSS(appElement, stylesheetElement, appName) – creates a temporary disabled <style> element to obtain CSS rules, then rewrites them using rewrite. ruleStyle(rule, prefix) – replaces the selector part of a CSS rule with the prefixed selector. rewrite(rules, prefix) – iterates over CSS rules, handling STYLE rules (others like MEDIA and SUPPORTS are omitted for brevity).
function processCSS(appElement, stylesheetElement, appName) {
const prefix = `${appElement.tagName.toLowerCase()}[data-app-name="${appName}"]`;
const tempNode = document.createElement('style');
document.body.appendChild(tempNode);
tempNode.sheet.disabled = true;
if (stylesheetElement.textContent !== '') {
const textNode = document.createTextNode(stylesheetElement.textContent || '');
tempNode.appendChild(textNode);
const sheet = tempNode.sheet;
const rules = [...sheet?.cssRules ?? []];
stylesheetElement.textContent = this.rewrite(rules, prefix);
tempNode.removeChild(textNode);
}
}
function scopedCSSIsolation(appName, contentHtmlString) {
contentHtmlString = contentHtmlString.trim();
const containerElement = document.createElement('div');
containerElement.innerHTML = contentHtmlString;
const appElement = containerElement.firstChild;
appElement.setAttribute('data-app-name', appName);
const styleNodes = appElement.querySelectorAll('style') || [];
[...styleNodes].forEach((stylesheetElement) => {
processCSS(appElement, stylesheetElement, appName);
});
return appElement;
}A test case demonstrates that the outer text remains red while the inner text becomes blue after the scoped CSS transformation.
Turning It Into a Web Component
To avoid repetitive container handling, the article wraps the isolation logic into a custom element isolation-content. The component reads data-app-name and data-isolation-mode attributes, builds the inner HTML, applies either shadowDOMIsolation or scopedCSSIsolation based on the mode, clears its original content, and appends the isolated element.
class Isolation extends HTMLElement {
constructor() {
super();
const name = this.getAttribute('data-app-name');
const mode = this.getAttribute('data-isolation-mode');
const html = `<div class="wrapper">${this.innerHTML.trim()}</div>`;
const appElement = mode === 'shadowDOM' ? shadowDOMIsolation(html) : scopedCSSIsolation(name, html);
this.innerHTML = '';
this.appendChild(appElement);
}
}
customElements.define('isolation-content', Isolation);The final HTML includes the three script files ( scopedCSSIsolation.js, shadowDOMIsolation.js, and Isolation.js) and demonstrates both isolation modes side by side.
Summary
Qiankun style isolation offers two methods: Shadow DOM isolation (hard isolation) and Scoped CSS isolation (selector prefixing).
Shadow DOM leverages the browser's native shadow tree to completely separate styles.
Scoped CSS rewrites <style> rules by adding a unique container selector, achieving isolation without a shadow tree.
Both techniques can be encapsulated into a reusable Web Component for cleaner micro‑frontend integration.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
