Верстка сайту з використанням CSS Modules
CSS Modules вирішують одну конкретну проблему — глобальне простір імен CSS. У будь-якому проекті більше трьох розробників або більше ста компонентів класи починають конфліктувати, переопределяти друг друга, та !important розповсюджується по кодовій базі. CSS Modules дають локальну область видимості на рівні збирання — без runtime-overhead, без shadow DOM.
Принцип роботи
Vite, Webpack, Parcel та інші бандлери трансформують CSS Modules на етапі збирання. Ім'я класу у .module.css файлі хешується:
.button → .button_a3f7k2x
.title → .title_9cxm1pq
Результативний HTML містить хешовані імена, конфлікт неможливий за визначенням.
/* Button.module.css */
.root {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background-color: var(--color-accent);
color: #fff;
border-radius: 0.5rem;
font-weight: 500;
border: none;
cursor: pointer;
transition: background-color 150ms ease;
}
.root:hover {
background-color: var(--color-accent-hover);
}
.root:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Варіанти */
.ghost {
background-color: transparent;
color: var(--color-accent);
border: 1px solid var(--color-accent);
}
/* Розміри */
.sm {
padding: 0.25rem 0.75rem;
font-size: 0.875rem;
}
.lg {
padding: 0.75rem 1.5rem;
font-size: 1.125rem;
}
// Button.tsx
import { FC, ButtonHTMLAttributes } from 'react';
import styles from './Button.module.css';
import clsx from 'clsx';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'default' | 'ghost';
size?: 'sm' | 'md' | 'lg';
}
const Button: FC<ButtonProps> = ({
variant = 'default',
size = 'md',
className,
children,
...props
}) => {
return (
<button
className={clsx(
styles.root,
variant === 'ghost' && styles.ghost,
size === 'sm' && styles.sm,
size === 'lg' && styles.lg,
className
)}
{...props}
>
{children}
</button>
);
};
Налаштування у Vite
CSS Modules працюють з коробки — будь-який файл *.module.css обробляється автоматично:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
css: {
modules: {
// camelCase для доступу з JS: styles.myClass замість styles['my-class']
localsConvention: 'camelCase',
// Формат генеруємих імен класів
generateScopedName:
process.env.NODE_ENV === 'production'
? '[hash:base64:8]'
: '[name]__[local]__[hash:base64:4]',
},
},
});
Паттерн: composes для переиспользування
CSS Modules підтримують composes — наслідування стилів без JavaScript:
/* base.module.css */
.flexCenter {
display: flex;
align-items: center;
justify-content: center;
}
.card {
background: var(--color-bg-surface);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
}
/* Hero.module.css */
.container {
composes: flexCenter from './base.module.css';
padding: 3rem 1rem;
min-height: 100vh;
}
.title {
composes: card from './base.module.css';
font-size: 2.5rem;
font-weight: bold;
}
Переваги
- Нульова runtime вартість — скомпільовано на етапі збирання
- Локальна область — немає потреби в BEM чи конвенціях імен
- Видалення мертвого коду — невикористані стилі видаляються у production
- Динамічна темізація — комбінування з CSS Custom Properties
Терміни
Інтеграція CSS Modules у існуючий проект: 2–3 дні. Новий проект з повною бібліотекою компонентів: включено в графік розробки компонентів.







