New Jan 19, 2026

How to scope Tailwind CSS build in a Micro Frontend to prevent CSS bleeding (Host vs. MFE) without Shadow DOM?

Libraries, Frameworks, etc. All from Newest questions tagged reactjs - Stack Overflow View How to scope Tailwind CSS build in a Micro Frontend to prevent CSS bleeding (Host vs. MFE) without Shadow DOM? on stackoverflow.com

I am building a Micro Frontend (MFE) using React + Vite + Tailwind CSS + Shadcn + (dndKit , sonner). This MFE is being integrated into a legacy Laravel host application.

The project is large (500+ files), and we cannot rewrite the class names manually.

The Problem

I am facing a "two-way CSS conflict" that I cannot seem to solve at build time:

  1. Outgoing Leak (MFE breaking Host)
    My MFE's Tailwind Preflight (reset) is leaking out. For example, Tailwind resets button styles globally, which strips the styling from the Laravel host's buttons.
  2. Incoming Bleed (Host breaking MFE)
    The Laravel host has aggressive global styles (e.g., div { border: none; } or specific h1 styles). These are bleeding into my MFE, causing borders to disappear and layouts to break, even when I use Tailwind utility classes.

Constraints

What I have tried

  1. unplugin-tailwindcss-mangle: It renamed the classes, but didn't stop the Host's global styles from breaking my UI.

  2. postcss-prefix-selector: I tried wrapping everything in a .mfe-root class.

If anyone knows the solution for this please provide for you reference check code snippet below:

postcss.config.cjs

module.exports = {
  plugins: {
    'tailwindcss/nesting': {},
    tailwindcss: {},
    autoprefixer: {},
    'postcss-prefix-selector': {
      prefix: '.school-module-app',
      exclude: [],
      transform(prefix, selector, prefixedSelector, filePath, rule) {
        // 1. Transform :root to use our wrapper class
        if (selector === ':root') {
          return prefix;
        }

// 2. Don't double-prefix if (selector.includes('.school-module-app')) { return selector; }

// 3. Handle body/html - scope them under our wrapper if (selector === 'body' || selector === 'html') { return prefix; }

// 4. Handle ::backdrop and other pseudo-elements if (selector.startsWith('::')) { return ${prefix} ${selector}; }

// 5. Default: return the prefixed selector return prefixedSelector; } } } }

tailwind.config.ts

import type { Config } from "tailwindcss";

export default { darkMode: ["class"], important:".school-module-app", content: ["./pages//*.{ts,tsx}", "./components//.{ts,tsx}", "./app/**/.{ts,tsx}", "./src/**/*.{ts,tsx}"], prefix: "", theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, // etc }, plugins: [require("tailwindcss-animate")], } satisfies Config;

main.tsx

<div className="school-module-app">
  <App />
</div>

index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Definition of the design system. All colors, gradients, fonts, etc should be defined here. All colors MUST be HSL. Soft & Friendly Theme. */

@layer base { :root { /* Soft & Friendly Color Palette */ --background: 40 33% 98%; --foreground: 240 10% 20%;

--card: 0 0% 100%; --card-foreground: 240 10% 20%;

--popover: 0 0% 100%; --popover-foreground: 240 10% 20%;

/* Warm indigo primary */ --primary: 252 56% 57%; --primary-foreground: 0 0% 100%;

/* Soft neutral secondary */ --secondary: 240 5% 96%; --secondary-foreground: 240 10% 30%;

/* Warm muted tones */ --muted: 40 20% 96%; --muted-foreground: 240 5% 46%;

/* Soft coral accent */ --accent: 15 85% 94%; --accent-foreground: 15 70% 40%;

--destructive: 0 72% 51%; --destructive-foreground: 0 0% 100%;

/* Soft borders */ --border: 240 6% 90%; --input: 240 6% 90%; --ring: 252 56% 57%;

--radius: 0.75rem;

/* Success, Warning, Info colors */ --success: 152 60% 45%; --success-foreground: 0 0% 100%; --warning: 38 92% 50%; --warning-foreground: 0 0% 100%; --info: 199 89% 48%; --info-foreground: 0 0% 100%;

/* Sidebar - Warm neutral */ --sidebar-background: 40 25% 97%; --sidebar-foreground: 240 10% 30%; --sidebar-primary: 252 56% 57%; --sidebar-primary-foreground: 0 0% 100%; --sidebar-accent: 252 40% 95%; --sidebar-accent-foreground: 252 56% 45%; --sidebar-border: 240 6% 92%; --sidebar-ring: 252 56% 57%; }

.dark { --background: 240 10% 8%; --foreground: 40 20% 95%;

--card: 240 10% 10%; --card-foreground: 40 20% 95%;

--popover: 240 10% 10%; --popover-foreground: 40 20% 95%;

--primary: 252 56% 65%; --primary-foreground: 240 10% 8%;

--secondary: 240 8% 18%; --secondary-foreground: 40 20% 95%;

--muted: 240 8% 18%; --muted-foreground: 240 5% 60%;

--accent: 15 40% 20%; --accent-foreground: 15 70% 80%;

--destructive: 0 62% 45%; --destructive-foreground: 0 0% 100%;

--border: 240 8% 20%; --input: 240 8% 20%; --ring: 252 56% 65%;

--success: 152 50% 40%; --success-foreground: 0 0% 100%; --warning: 38 80% 45%; --warning-foreground: 0 0% 100%; --info: 199 75% 45%; --info-foreground: 0 0% 100%;

--sidebar-background: 240 10% 6%; --sidebar-foreground: 40 20% 90%; --sidebar-primary: 252 56% 65%; --sidebar-primary-foreground: 240 10% 8%; --sidebar-accent: 252 30% 18%; --sidebar-accent-foreground: 252 50% 80%; --sidebar-border: 240 8% 18%; --sidebar-ring: 252 56% 65%; } }

@layer base {

  • { @apply border-border; }

body { @apply bg-background text-foreground antialiased; }

/* Defensive CSS Reset (Scoped) Shields the app from Host Site's global styles / .school-module-app { / Enforce app-specific font stack and defaults */ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; line-height: 1.5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }

/* Reset Buttons: Override potential host styles like background: red or border: 5px solid black / / Reset Buttons: Override potential host styles like background: red or border: 5px solid black */ :where(.school-module-app) :where(button, [type='button'], [type='reset'], [type='submit']) { -webkit-appearance: explicit; appearance: none; background-color: transparent; background-image: none; border-width: 0; border-style: none; border-color: transparent; margin: 0; padding: 0; }

/* Reset Inputs which might inherit ugly borders */ :where(.school-module-app) :where(input, optgroup, select, textarea) { font-family: inherit; font-size: 100%; font-weight: inherit; line-height: inherit; color: inherit; margin: 0; border-width: 0; border-style: none; outline: none; background-color: transparent; box-shadow: none; }

/* Reset Headings if host adds bottom borders etc */ :where(.school-module-app) :where(h1, h2, h3, h4, h5, h6) { margin: 0; font-weight: inherit; border: none; } }

Scroll to top