← Назад к вопросам

Как работают Scope стили?

2.0 Middle🔥 251 комментариев
#HTML и CSS

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работают Scoped стили

Что такое scoped стили

Scoped стили (область видимости стилей) — это механизм, который ограничивает применение CSS правил только конкретным компонентам, предотвращая конфликты имён и случайное переопределение стилей. Это критически важно в больших приложениях, где множество компонентов используют похожие имена классов.

Способ 1: CSS Modules (рекомендуется)

CSS Modules — это встроенная механика в Next.js и других бандлерах, которая автоматически преобразует имена классов в уникальные:

// components/Button.module.css
.button {
  padding: 8px 16px;
  background-color: blue;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.button:hover {
  background-color: darkblue;
}

.primary {
  background-color: #0066ff;
}

.secondary {
  background-color: #666666;
}

Использование в компоненте:

// components/Button.tsx
import styles from './Button.module.css';

interface ButtonProps {
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
  onClick?: () => void;
}

export function Button({
  variant = 'primary',
  children,
  onClick,
}: ButtonProps) {
  const buttonClass = variant === 'secondary'
    ? styles.secondary
    : styles.primary;

  return (
    <button
      className={`${styles.button} ${buttonClass}`}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

Как это работает под капотом:

// Исходный CSS
.button { ... }
.primary { ... }

// Скомпилированный CSS (автоматически)
.Button_button__a1b2c {
  padding: 8px 16px;
  /* ... */
}

.Button_primary__d3e4f {
  background-color: #0066ff;
}

// JavaScript получает объект
const styles = {
  button: 'Button_button__a1b2c',
  primary: 'Button_primary__d3e4f',
  secondary: 'Button_secondary__g5h6i',
};

Способ 2: Tailwind CSS (современный подход)

Tailwind CSS решает проблему scope стилей через утилитарные классы, которые можно комбинировать:

// components/Button.tsx
import { cn } from '@/lib/utils';

interface ButtonProps {
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
  className?: string;
}

export function Button({
  variant = 'primary',
  children,
  className,
}: ButtonProps) {
  const baseStyles = 'px-4 py-2 rounded-lg font-medium transition-colors';
  
  const variantStyles =
    variant === 'secondary'
      ? 'bg-gray-200 text-gray-900 hover:bg-gray-300'
      : 'bg-blue-600 text-white hover:bg-blue-700';

  return (
    <button className={cn(baseStyles, variantStyles, className)}>
      {children}
    </button>
  );
}

У Tailwind нет конфликтов имён, потому что не используются обычные классы CSS:

// Каждый класс сгенерирован автоматически
// .px-4 { padding-left: 1rem; padding-right: 1rem; }
// .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
// .bg-blue-600 { background-color: rgb(37, 99, 235); }
// и т.д.

Способ 3: BEM методология (Manual Scoping)

БЕМ (Block Element Modifier) — это соглашение по именованию, которое вручную создаёт scope:

/* styles/button.css */
.button {
  padding: 8px 16px;
  background-color: blue;
  color: white;
}

/* Элемент кнопки */
.button__text {
  font-weight: bold;
}

/* Модификатор */
.button--secondary {
  background-color: gray;
}

.button--secondary:hover {
  background-color: darkgray;
}

Использование:

// components/Button.tsx
export function Button({ variant = 'primary', children }) {
  const className = [
    'button',
    variant === 'secondary' && 'button--secondary',
  ]
    .filter(Boolean)
    .join(' ');

  return <button className={className}>{children}</button>;
}

Способ 4: CSS-in-JS (Styled Components)

CSS-in-JS создаёт уникальные className автоматически:

import styled from 'styled-components';

const StyledButton = styled.button`
  padding: 8px 16px;
  background-color: ${(props) => (props.primary ? 'blue' : 'gray')};
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: ${(props) => (props.primary ? 'darkblue' : 'darkgray')};
  }
`;

export function Button({ primary = true, children }) {
  return <StyledButton primary={primary}>{children}</StyledButton>;
}

// Скомпилированный результат:
// <button class="sc-Button__StyledButton-a1b2c__0">Click me</button>

Сравнение всех методов

// ПРОБЛЕМА: глобальный CSS без scope
// styles/global.css
.button { padding: 8px; }

// components/MyButton.tsx
export function MyButton() {
  return <button className="button">Click</button>;
}

// components/ThirdPartyButton.tsx (из библиотеки)
// Там ТОЖЕ есть .button, и они конфликтуют!
export function ThirdPartyButton() {
  return <button className="button">Also click</button>;
}

// РЕШЕНИЕ 1: CSS Modules (автоматический scope)
// Каждый файл получает уникальные классы

// РЕШЕНИЕ 2: Tailwind (утилитарные классы, нет конфликтов)
// Нет обычных классов, только утилиты

// РЕШЕНИЕ 3: BEM (ручной scope через именование)
// Соглашение о том, как называть классы

// РЕШЕНИЕ 4: CSS-in-JS (динамический scope)
// Классы генерируются в runtime

Практический пример: Component Library

// lib/ui/Card.module.css
.card {
  background-color: white;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.cardTitle {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 12px;
}

.cardContent {
  font-size: 14px;
  line-height: 1.5;
}

// lib/ui/Card.tsx
import styles from './Card.module.css';

interface CardProps {
  title?: string;
  children: React.ReactNode;
}

export function Card({ title, children }: CardProps) {
  return (
    <div className={styles.card}>
      {title && <h3 className={styles.cardTitle}>{title}</h3>}
      <div className={styles.cardContent}>{children}</div>
    </div>
  );
}

// pages/index.tsx
import { Card } from '@/lib/ui/Card';

export default function Home() {
  return (
    <main>
      <Card title="Welcome">
        <p>This is a scoped component</p>
      </Card>
    </main>
  );
}

// Каждый компонент имеет свой scope
// styles.card = Card_card__a1b2c (уникален)
// styles.cardTitle = Card_cardTitle__d3e4f (уникален)
// styles.cardContent = Card_cardContent__g5h6i (уникален)

Преимущества scoped стилей

  1. Изоляция: стили одного компонента не влияют на другие
  2. Переиспользование: можно использовать одинаковые имена в разных компонентах
  3. Безопасность рефакторинга: можно переименовать класс и знать, что повлияет только этот компонент
  4. Предсказуемость: нет неожиданных конфликтов стилей

Итоговый совет

Для современных приложений используй либо Tailwind CSS (простая и быстрая), либо CSS Modules (если нужны traditionalные CSS файлы). Избегай глобального CSS для компонентов — это приводит к проблемам в больших приложениях. CSS-in-JS (Styled Components) полезен для очень динамичных стилей, но добавляет overhead.

Как работают Scope стили? | PrepBro