Style isolation are methods to prevent CSS conflicts between components:
/* CSS Modules */
.button { /* becomes .button_abc123 */ }
/* Styled Components */
const Button = styled.button`color: blue;`;
/* BEM */
.block__element--modifier { }Style isolation is like building houses with separate apartments. Each component lives in its own space and doesn’t interfere with neighbors! 🏠
/* Problem: global styles conflict */
.button {
background: red;
padding: 10px;
}
/* In another file */
.button {
background: blue; /* Overwrites the previous one! */
margin: 5px;
}
/* Result: unpredictable styles */CSS Modules automatically generate unique class names:
/* Button.module.css */
.button {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.primary {
background: #28a745;
}
.large {
padding: 16px 32px;
font-size: 18px;
}// Button.jsx
import styles from './Button.module.css';
function Button({ children, primary, large }) {
const className = [
styles.button,
primary && styles.primary,
large && styles.large
].filter(Boolean).join(' ');
return (
<button className={className}>
{children}
</button>
);
}
// Result in DOM:
// <button class="Button_button__2Fk1a Button_primary__3Gh2b">
// Button
// </button>/* Button.module.css */
.base {
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
}
.primary {
composes: base;
background: #007bff;
color: white;
}
.secondary {
composes: base;
background: #6c757d;
color: white;
}
.outline {
composes: base;
background: transparent;
border: 2px solid #007bff;
color: #007bff;
}Styled Components create isolated components with styles:
import styled, { css } from 'styled-components';
// Basic button
const Button = styled.button`
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: all 0.2s ease;
&:hover {
background: #0056b3;
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
`;
// Variants through props
const StyledButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: all 0.2s ease;
${props => props.variant === 'primary' && css`
background: #007bff;
color: white;
&:hover {
background: #0056b3;
}
`}
${props => props.variant === 'secondary' && css`
background: #6c757d;
color: white;
&:hover {
background: #545b62;
}
`}
${props => props.variant === 'outline' && css`
background: transparent;
border: 2px solid #007bff;
color: #007bff;
&:hover {
background: #007bff;
color: white;
}
`}
${props => props.size === 'large' && css`
padding: 16px 32px;
font-size: 18px;
`}
${props => props.size === 'small' && css`
padding: 8px 16px;
font-size: 14px;
`}
${props => props.disabled && css`
opacity: 0.6;
cursor: not-allowed;
&:hover {
background: ${props.variant === 'primary' ? '#007bff' : '#6c757d'};
transform: none;
}
`}
`;
// Usage
function App() {
return (
<div>
<StyledButton variant="primary" size="large">
Primary Button
</StyledButton>
<StyledButton variant="outline" size="small">
Outline Button
</StyledButton>
<StyledButton variant="secondary" disabled>
Disabled Button
</StyledButton>
</div>
);
}import styled, { ThemeProvider } from 'styled-components';
// Theme
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
info: '#17a2b8'
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px'
},
borderRadius: {
sm: '2px',
md: '4px',
lg: '8px',
xl: '12px'
}
};
// Component with theme
const ThemedButton = styled.button`
background: ${props => props.theme.colors[props.color] || props.theme.colors.primary};
color: white;
padding: ${props => props.theme.spacing.md} ${props => props.theme.spacing.lg};
border: none;
border-radius: ${props => props.theme.borderRadius.md};
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;
// Usage with theme
function App() {
return (
<ThemeProvider theme={theme}>
<ThemedButton color="primary">Primary</ThemedButton>
<ThemedButton color="success">Success</ThemedButton>
<ThemedButton color="danger">Danger</ThemedButton>
</ThemeProvider>
);
}Shadow DOM provides complete style isolation at the browser level:
// Creating Web Component with Shadow DOM
class IsolatedButton extends HTMLElement {
constructor() {
super();
// Create Shadow DOM
this.attachShadow({ mode: 'open' });
// Styles are completely isolated
this.shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
}
button {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: all 0.2s ease;
}
button:hover {
background: #0056b3;
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
/* These styles DON'T affect external elements */
.global-class {
color: red;
}
</style>
<button>
<slot></slot>
</button>
`;
}
}
// Register component
customElements.define('isolated-button', IsolatedButton);
// Usage
// <isolated-button>Isolated Button</isolated-button>import { useEffect, useRef } from 'react';
import { createRoot } from 'react-dom/client';
function ShadowWrapper({ children, styles }) {
const shadowRef = useRef();
const rootRef = useRef();
useEffect(() => {
const shadowRoot = shadowRef.current.attachShadow({ mode: 'open' });
// Add styles to Shadow DOM
const styleElement = document.createElement('style');
styleElement.textContent = styles;
shadowRoot.appendChild(styleElement);
// Create container for React
const container = document.createElement('div');
shadowRoot.appendChild(container);
// Render React component in Shadow DOM
rootRef.current = createRoot(container);
rootRef.current.render(children);
return () => {
rootRef.current?.unmount();
};
}, [children, styles]);
return <div ref={shadowRef}></div>;
}
// Usage
function App() {
const isolatedStyles = `
.button {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button:hover {
background: #0056b3;
}
`;
return (
<ShadowWrapper styles={isolatedStyles}>
<button className="button">
Isolated Button
</button>
</ShadowWrapper>
);
}/** @jsxImportSource @emotion/react */
import { css, jsx } from '@emotion/react';
import styled from '@emotion/styled';
// CSS prop
const buttonStyle = css`
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: #0056b3;
}
`;
function Button() {
return (
<button css={buttonStyle}>
Emotion Button
</button>
);
}
// Styled component
const StyledButton = styled.button`
background: ${props => props.primary ? '#007bff' : '#6c757d'};
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
`;import { createUseStyles } from 'react-jss';
const useStyles = createUseStyles({
button: {
background: '#007bff',
color: 'white',
padding: [12, 24],
border: 'none',
borderRadius: 4,
cursor: 'pointer',
transition: 'all 0.2s ease',
'&:hover': {
background: '#0056b3',
transform: 'translateY(-1px)'
},
'&:active': {
transform: 'translateY(0)'
}
},
primary: {
extend: 'button',
background: '#007bff'
},
secondary: {
extend: 'button',
background: '#6c757d'
}
});
function Button({ variant = 'primary', children }) {
const classes = useStyles();
return (
<button className={classes[variant]}>
{children}
</button>
);
}import { styled } from '@stitches/react';
const Button = styled('button', {
// Base styles
padding: '12px 24px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontFamily: 'inherit',
transition: 'all 0.2s ease',
// Variants
variants: {
variant: {
primary: {
background: '#007bff',
color: 'white',
'&:hover': {
background: '#0056b3'
}
},
secondary: {
background: '#6c757d',
color: 'white',
'&:hover': {
background: '#545b62'
}
},
outline: {
background: 'transparent',
border: '2px solid #007bff',
color: '#007bff',
'&:hover': {
background: '#007bff',
color: 'white'
}
}
},
size: {
small: {
padding: '8px 16px',
fontSize: '14px'
},
large: {
padding: '16px 32px',
fontSize: '18px'
}
}
},
// Compound variants
compoundVariants: [
{
variant: 'primary',
size: 'large',
css: {
fontWeight: 'bold'
}
}
],
// Default variants
defaultVariants: {
variant: 'primary',
size: 'medium'
}
});
// Usage
<Button variant="outline" size="large">
Large Outline Button
</Button>/* Block */
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 20px;
}
/* Elements */
.card__header {
border-bottom: 1px solid #eee;
padding-bottom: 15px;
margin-bottom: 15px;
}
.card__title {
font-size: 24px;
font-weight: bold;
margin: 0;
}
.card__subtitle {
font-size: 16px;
color: #666;
margin: 5px 0 0 0;
}
.card__content {
line-height: 1.6;
}
.card__footer {
border-top: 1px solid #eee;
padding-top: 15px;
margin-top: 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.card__button {
background: #007bff;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* Modifiers */
.card--featured {
border: 2px solid #007bff;
box-shadow: 0 4px 8px rgba(0,123,255,0.2);
}
.card--compact {
padding: 12px;
}
.card__button--secondary {
background: #6c757d;
}
.card__button--large {
padding: 12px 24px;
font-size: 16px;
}<!-- BEM Usage -->
<div class="card card--featured">
<div class="card__header">
<h2 class="card__title">Card Title</h2>
<p class="card__subtitle">Subtitle</p>
</div>
<div class="card__content">
Card content
</div>
<div class="card__footer">
<button class="card__button">Primary</button>
<button class="card__button card__button--secondary">
Secondary
</button>
</div>
</div>/* Base - base styles */
body, h1, h2, p { margin: 0; padding: 0; }
body { font-family: Arial, sans-serif; }
/* Layout - layout */
.l-header { /* layout-header */ }
.l-sidebar { /* layout-sidebar */ }
.l-content { /* layout-content */ }
/* Module - modules */
.button { }
.card { }
.navigation { }
/* State - states */
.is-hidden { display: none; }
.is-active { background: #007bff; }
.is-disabled { opacity: 0.5; }
/* Theme - themes */
.theme-dark .button { background: #333; }
.theme-light .button { background: #fff; }Vue.js provides built-in style isolation:
<template>
<div class="card">
<h2 class="title">{{ title }}</h2>
<p class="content">{{ content }}</p>
<button class="button" @click="handleClick">
{{ buttonText }}
</button>
</div>
</template>
<script>
export default {
props: ['title', 'content', 'buttonText'],
methods: {
handleClick() {
this.$emit('click');
}
}
}
</script>
<style scoped>
/* These styles apply only to this component */
.card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.title {
font-size: 24px;
margin-bottom: 10px;
color: #333;
}
.content {
line-height: 1.6;
margin-bottom: 20px;
color: #666;
}
.button {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s ease;
}
.button:hover {
background: #0056b3;
}
/* Deep selectors for child components */
.card :deep(.child-component) {
margin-top: 10px;
}
/* Global styles */
:global(.global-class) {
font-weight: bold;
}
</style>// postcss.config.js
module.exports = {
plugins: [
require('postcss-modules')({
generateScopedName: '[name]__[local]___[hash:base64:5]'
})
]
};// Automatically adds prefix to all selectors
module.exports = {
plugins: [
require('postcss-prefixwrap')('.my-component')
]
};
// Input CSS:
// .button { color: red; }
// Output CSS:
// .my-component .button { color: red; }| Method | Isolation | Performance | Complexity | DX | SSR |
|---|---|---|---|---|---|
| CSS Modules | ✅ High | ✅ Excellent | 🟡 Medium | ✅ Good | ✅ Yes |
| Styled Components | ✅ High | 🟡 Medium | 🟡 Medium | ✅ Excellent | ✅ Yes |
| Shadow DOM | ✅ Complete | ✅ Excellent | 🔴 High | 🟡 Medium | ❌ No |
| Emotion | ✅ High | 🟡 Medium | 🟡 Medium | ✅ Excellent | ✅ Yes |
| BEM | 🟡 Conditional | ✅ Excellent | 🟡 Medium | 🟡 Medium | ✅ Yes |
| CSS Scoped | ✅ High | ✅ Excellent | ✅ Low | ✅ Excellent | ✅ Yes |
// For React applications
const recommendations = {
'small-project': 'CSS Modules or Styled Components',
'medium-project': 'Styled Components + Theme Provider',
'large-project': 'CSS Modules + PostCSS or Stitches',
'component-library': 'Styled Components or Stitches',
'legacy-project': 'BEM + PostCSS plugins'
};
// For Vue.js
const vueRecommendations = {
'any-size': 'CSS Scoped (built-in)'
};
// For Web Components
const webComponentsRecommendations = {
'any-size': 'Shadow DOM (native)'
};// Combining methods for maximum efficiency
// 1. CSS Modules for components + global styles
import styles from './Button.module.css';
import './global.css'; // Global styles
// 2. Styled Components for dynamic styles + CSS for static
const DynamicButton = styled.button`
background: ${props => props.theme.colors[props.variant]};
`;
// 3. BEM for large blocks + CSS Modules for components
<div className="page-layout"> {/* BEM */}
<Button className={styles.button} /> {/* CSS Modules */}
</div>// ✅ Good: one approach in project
// CSS Modules everywhere
import buttonStyles from './Button.module.css';
import cardStyles from './Card.module.css';
// ❌ Bad: mixing approaches without system
import buttonStyles from './Button.module.css';
import styled from 'styled-components';
const Card = styled.div`...`;// ✅ Good: shared styles in variables
const theme = {
colors: { primary: '#007bff' },
spacing: { md: '16px' }
};
// ❌ Bad: duplicating values
const Button1 = styled.button`background: #007bff;`;
const Button2 = styled.button`background: #007bff;`; // Duplication!// ✅ Good: static styles
const StaticButton = styled.button`
padding: 12px 24px;
border-radius: 4px;
`;
// ❌ Bad: dynamic styles without memoization
const DynamicButton = styled.button`
padding: ${() => Math.random() * 20}px; // Recalculation every render!
`;Proper style isolation will make your code predictable and easily maintainable! 🚀
Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site and improve every day 💪