How to Prevent CSS Conflicts in Micro‑Frontends with Garfish and Shadow DOM
This article explains why class name and global element style conflicts occur in micro‑frontend architectures, and presents two main strategies—style naming with priority rules and host‑environment isolation using Shadow DOM or Garfish runtime plugins—to achieve reliable CSS isolation across parent and child applications.
Problem Examples
When the same title class is used in both the host and a child application, the child’s style (red) overrides the host’s style (yellow) because the child’s stylesheet loads later.
Similar conflicts arise with html and body tags, which are unique elements but their global styles affect the host application.
Solutions
Two main approaches can resolve these style conflicts:
Resolve through style naming and priority.
Achieve isolation via the host environment.
Style Naming & Priority
Ensuring each application’s class names are globally unique prevents direct clashes. Adding selector prefixes or attribute selectors further isolates styles, e.g., prefixing class selectors or using attribute selectors.
Example: prepend a selector before the original class name.
Use tag selector + attribute selector.
Child Application Refactoring
Two sources of global styles need handling:
UI component library styles
Most UI libraries expose a ConfigProvider to customize the global prefixCls (e.g., Ant Design, ArcoDesign).
Custom styles
Apply BEM, CSS Modules, CSS‑in‑JS, or a PostCSS plugin to add a unique prefix to every selector during compilation.
Runtime Conversion in the Host Application
If modifying child builds is not feasible, the host can prepend a unique prefix to all selectors at runtime, raising their specificity.
For example, the selector .title becomes #garfish_app_id_xxx .title, where #garfish_app_id_xxx is the child’s root element ID, ensuring the style applies only within that child.
Garfish provides a plugin to perform this runtime conversion:
import { GarfishCssScope } from '@garfish/css-scope';
Garfish.run({
plugins: [
GarfishCssScope({
fixBodyGetter: true,
excludes: ['appName'],
}),
],
});Advantages: supports most isolation needs, handles both UI library and custom styles.
Disadvantages: incurs runtime performance overhead; !important rules may still break isolation.
Host‑Environment Isolation
Shadow DOM
Shadow DOM creates a hidden subtree whose styles are completely isolated from the rest of the page, similar to an iframe.
Creation: shadowHostElement.attachShadow({ mode: 'open' }) or { mode: 'closed' }. In open mode, the shadow root is accessible via element.shadowRoot; in closed mode it returns null.
let shadowRoot = shadowHostElement.attachShadow({mode: 'open'});
let shadowRoot = shadowHostElement.attachShadow({mode: 'closed'});Styles can be added using a <style> element or a <link> to an external stylesheet:
// Using a style element
var style = document.createElement('style');
style.textContent = `
.title {
color: blue;
}
`;
shadow.appendChild(style);
// Using a link element
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'style.css');
shadow.appendChild(linkElem);Event Model: events that bubble out of a Shadow DOM have their target retargeted to the host element. Some events (e.g., focus, mouse, wheel, input, keyboard, composition, drag) bubble; others may not.
Focus events: blur, focus, focusin, focusout
Mouse events: click, dblclick, mousedown, mouseenter, mousemove, …
Wheel event: wheel
Input events: beforeinput, input
Keyboard events: keydown, keyup
Composition events: compositionstart, compositionupdate, compositionend
Drag events: dragstart, drag, dragend, drop, …
When the Shadow DOM mode is open, event.composedPath() returns the full propagation path.
Garfish Integration
Garfish can enable Shadow‑DOM‑like isolation with a single configuration:
Garfish.run({
sandbox: {
strictIsolation: true,
},
});Advantages: provides complete CSS isolation.
Disadvantages: certain components (e.g., Ant Design Select) may render popups outside the shadow boundary, requiring manual container adjustments; older React versions’ event delegation can conflict with Shadow DOM retargeting.
Conclusion
Style isolation is not overly complex, but each method has trade‑offs. Using CSS Modules or similar tools with agreed‑upon prefixes is currently the most stable solution, while Shadow DOM offers the strongest long‑term isolation despite compatibility considerations.
From a strategic perspective, combining Shadow DOM with framework‑specific adaptations will likely become the optimal path.
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.
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.
