PurpleSchool — курсы программирования онлайн
  • Пути
    • Frontend React разработчик
    • Frontend Vue разработчик
    • Backend разработчик Node.js
    • Fullstack разработчик React / Node.js
    • Mobile разработчик React Native
    • Backend разработчик Golang
    • Devops инженер
    • Backend разработчик Python
  • Подписка
  • AI для кодаНовое
  • О нас
    • Отзывы
    • Реферальная программа
    • О компании
    • Контакты
  • Иконка открытия меню
    • Сообщество
    • PurpleПлюс
    • AI Собеседование
    • AI тренажёр
    • Проекты
PurpleSchool — платформа бесплатных roadmap и курсов для разработчиков
ютуб иконка
Telegram иконка
VK иконка
VK иконка
Курсы
ГлавнаяКаталог курсовFrontendBackendFullstack
Практика
КарьераПроектыPurpleПлюсPurpleMax
Материалы
БлогБаза знаний
Документы
Договор офертаПолитика конфиденциальностиПроверка сертификатаМиграция курсовРеферальная программа
Реквизиты
ИП Ларичев Антон АндреевичИНН 773373765379contact@purpleschool.ru

PurpleSchool © 2020 -2026 Все права защищены

  • Курсы
    • FrontendИконка стрелки
    • AI разработкаИконка стрелки
    • BackendИконка стрелки
    • DevOpsИконка стрелки
    • MobileИконка стрелки
    • ТестированиеИконка стрелки
    • Soft-skillsИконка стрелки
    • ДизайнИконка стрелки
    Иконка слояПерейти в каталог курсов
  • Бесплатно
    • Курсы
    • JavaScript Основы разработкиPython Основы PythonCSS CSS FlexboxКарта развитияВопросы для собеседований
    • База знанийИконка стрелки
    • Новостные рассылкиИконка стрелки
  • PurpleSchool — курсы программирования онлайн
    • Подписка
    • AI для кодаНовое
    • Сообщество
    Главная
    Сообщество
    Дизайн-система в React с нуля: токены, компоненты и Storybook

    Дизайн-система в React с нуля: токены, компоненты и Storybook

    Аватар автора Дизайн-система в React с нуля: токены, компоненты и Storybook

    Антон Ларичев

    Иконка календаря20 марта 2026
    reacttypescriptcssmiddleИконка уровня middle
    Картинка поста Дизайн-система в React с нуля: токены, компоненты и Storybook

    Введение

    Дизайн-система в React — это набор переиспользуемых компонентов, дизайн-токенов и правил, которые обеспечивают единообразие интерфейса во всём приложении. Без неё каждый разработчик в команде изобретает свои отступы, цвета и размеры кнопок, а продукт превращается в лоскутное одеяло из несогласованных стилей.

    В этой статье разберём, как построить дизайн-систему в React с нуля: от определения дизайн-токенов до создания компонентов и их документирования в Storybook. Рассмотрим практический пример с TypeScript и CSS-переменными.

    Что такое дизайн-токены и зачем они нужны

    Дизайн-токены — это базовые значения визуального языка: цвета, шрифты, отступы, радиусы скруглений, тени. Вместо того чтобы разбрасывать магические числа по коду, токены хранят все значения в одном месте.

    Трёхуровневая структура токенов помогает масштабировать систему:

    // tokens/colors.ts — базовые (core) токены
    export const coreColors = {
      purple100: '#f3e8ff',
      purple500: '#8b5cf6',
      purple900: '#4c1d95',
      gray100: '#f3f4f6',
      gray900: '#111827',
      white: '#ffffff',
    } as const;
    
    // tokens/semantic.ts — семантические токены
    export const semanticColors = {
      textPrimary: coreColors.gray900,
      textSecondary: coreColors.purple900,
      backgroundPrimary: coreColors.white,
      backgroundAccent: coreColors.purple100,
      borderDefault: coreColors.gray100,
      brandPrimary: coreColors.purple500,
    } as const;
    

    Семантические токены не привязаны к конкретному цвету — они описывают назначение. Это позволяет легко добавить тёмную тему, поменяв только маппинг семантических токенов на базовые.

    Как превратить токены в CSS-переменные

    Чтобы дизайн-токены работали в стилях, преобразуем их в CSS-переменные:

    // tokens/cssVariables.ts
    import { semanticColors } from './semantic';
    
    export function applyTokens(element: HTMLElement = document.documentElement) {
      const tokenMap: Record<string, string> = {
        '--color-text-primary': semanticColors.textPrimary,
        '--color-text-secondary': semanticColors.textSecondary,
        '--color-bg-primary': semanticColors.backgroundPrimary,
        '--color-bg-accent': semanticColors.backgroundAccent,
        '--color-border': semanticColors.borderDefault,
        '--color-brand': semanticColors.brandPrimary,
      };
    
      Object.entries(tokenMap).forEach(([key, value]) => {
        element.style.setProperty(key, value);
      });
    }
    

    Теперь в CSS компонентов используем переменные вместо жёстких значений:

    .button-primary {
      background-color: var(--color-brand);
      color: var(--color-bg-primary);
      border-radius: var(--radius-md);
      padding: var(--spacing-sm) var(--spacing-md);
    }
    

    Как создать компоненты дизайн-системы в React

    Компоненты дизайн-системы строятся по принципу Atomic Design: от простых атомов (кнопка, инпут) к молекулам (поле ввода с лейблом) и организмам (форма). Начнём с базового компонента Button.

    // components/Button/Button.tsx
    import React from 'react';
    import styles from './Button.module.css';
    
    type ButtonVariant = 'primary' | 'secondary' | 'ghost';
    type ButtonSize = 'sm' | 'md' | 'lg';
    
    interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
      variant?: ButtonVariant;
      size?: ButtonSize;
      isLoading?: boolean;
    }
    
    export const Button: React.FC<ButtonProps> = ({
      variant = 'primary',
      size = 'md',
      isLoading = false,
      children,
      disabled,
      className,
      ...props
    }) => {
      return (
        <button
          className={`${styles.button} ${styles[variant]} ${styles[size]} ${className ?? ''}`}
          disabled={disabled || isLoading}
          {...props}
        >
          {isLoading ? <span className={styles.spinner} /> : children}
        </button>
      );
    };
    
    /* components/Button/Button.module.css */
    .button {
      font-family: var(--font-family);
      font-weight: 500;
      border: none;
      cursor: pointer;
      transition: background-color 0.2s, opacity 0.2s;
    }
    
    .primary {
      background-color: var(--color-brand);
      color: var(--color-bg-primary);
    }
    
    .secondary {
      background-color: var(--color-bg-accent);
      color: var(--color-text-secondary);
    }
    
    .ghost {
      background-color: transparent;
      color: var(--color-text-primary);
    }
    
    .sm { padding: var(--spacing-xs) var(--spacing-sm); font-size: 14px; }
    .md { padding: var(--spacing-sm) var(--spacing-md); font-size: 16px; }
    .lg { padding: var(--spacing-md) var(--spacing-lg); font-size: 18px; }
    
    .button:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
    

    Каждый компонент дизайн-системы использует токены через CSS-переменные. Если дизайнер изменит основной цвет бренда — достаточно обновить один токен, и все компоненты подхватят изменение.

    Как настроить Storybook для React компонентов

    Storybook позволяет разрабатывать и документировать компоненты UI kit изолированно от основного приложения. Установка:

    npx storybook@latest init
    

    После инициализации создаём stories для компонентов:

    // components/Button/Button.stories.tsx
    import type { Meta, StoryObj } from '@storybook/react';
    import { Button } from './Button';
    
    const meta: Meta<typeof Button> = {
      title: 'Components/Button',
      component: Button,
      argTypes: {
        variant: {
          control: 'select',
          options: ['primary', 'secondary', 'ghost'],
        },
        size: {
          control: 'select',
          options: ['sm', 'md', 'lg'],
        },
      },
    };
    
    export default meta;
    type Story = StoryObj<typeof Button>;
    
    // Основной вариант кнопки
    export const Primary: Story = {
      args: {
        children: 'Отправить',
        variant: 'primary',
        size: 'md',
      },
    };
    
    // Вторичный вариант
    export const Secondary: Story = {
      args: {
        children: 'Отмена',
        variant: 'secondary',
      },
    };
    
    // Состояние загрузки
    export const Loading: Story = {
      args: {
        children: 'Сохранение...',
        isLoading: true,
      },
    };
    

    Для документирования токенов используйте аддон storybook-design-token, который автоматически генерирует таблицы цветов, шрифтов и отступов из CSS-переменных:

    npm install storybook-design-token
    

    Частые ошибки при создании дизайн-системы

    Жёсткие значения вместо токенов. Если в компоненте написано color: #8b5cf6 вместо var(--color-brand), при смене темы этот компонент останется прежним. Все визуальные значения должны идти через токены.

    Слишком много вариантов у компонента. Кнопка с 15 пропсами сложнее в поддержке, чем три отдельных компонента. Начинайте с минимального API и расширяйте по мере реальных потребностей.

    Отсутствие документации. Компонент без Story в Storybook — это компонент, который никто не будет переиспользовать. Документируйте каждый компонент сразу при создании.

    Пропуск семантического уровня токенов. Если компоненты ссылаются напрямую на purple500, добавление тёмной темы потребует правок в каждом файле. Семантический слой (brandPrimary) решает эту проблему.

    Заключение

    Дизайн-система в React — это инвестиция, которая окупается с ростом проекта. Начните с определения дизайн-токенов на трёх уровнях (базовые, семантические, компонентные), создайте переиспользуемые компоненты с минимальным API и задокументируйте их в Storybook. Такой подход обеспечит консистентность интерфейса, ускорит разработку и упростит поддержку тем оформления.

    Иконка глаза1 079

    Комментарии

    0

    Постройте личный план изучения Next.js 15 - с нуля, React TypeScript, Hooks, SSR и CSS Grid до уровня Middle — бесплатно!

    Next.js 15 - с нуля, React TypeScript, Hooks, SSR и CSS Grid — часть карты развития Frontend

    • step100+ шагов развития
    • lessons30 бесплатных лекций
    • lessons300 бонусных рублей на счет

    Бесплатные лекции

    Лучшие курсы по теме

    изображение курса

    Vue 3 и Pinia

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.8
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Nuxt

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга5.0
    3 999 ₽ 6 990 ₽
    Подробнее
    изображение курса

    Feature-Sliced Design

    Антон Ларичев
    AI-тренажерыAI-тренажеры
    Практика в студииПрактика в студии
    Гарантия
    Бонусы
    иконка звёздочки рейтинга4.6
    3 999 ₽ 6 990 ₽
    Подробнее

    Похожие статьи

    Картинка поста Профессиональное выгорание программиста: признаки и как с ним бороться
    Иконка аватараАнтон
    Иконка календаря28 октября 2026
    выгораниепродуктивностьsoft skills+ 2middleИконка уровня middle

    Профессиональное выгорание программиста: признаки и как с ним бороться

    Выгорание программиста — не абстракция, а измеримое явление: падение скорости коммитов, рост багов, ночные дедлайны. Разбираем признаки, причины и рабочие способы восстановиться.

    Иконка чипа0
    Иконка глаза194
    Иконка комментариев0
    Картинка поста Neovim + avante.nvim с Claude из России: настройка
    Иконка аватараАнтон
    Иконка календаря27 октября 2026
    AI-инструментыAnthropicразработкаmiddleИконка уровня middle

    Neovim + avante.nvim с Claude из России: настройка

    avante.nvim — плагин для Neovim, который добавляет ИИ-ассистента в стиле Cursor: боковую панель с чатом по коду, правки выделенного фрагмента и применение изменений прямо в буфер. С Claude он работает через провайдер `claude`, но из России напрямую не подключится: API Anthropic отклоняет российские адреса, а ключ оплачивается зарубежной картой. Решение — указать в конфиге плагина свой адрес API (`endpoint`) и ключ AI для кода от PurpleSchool с оплатой в рублях. Ниже — установка через lazy.nvim, актуальный конфиг, горячие клавиши и частые ошибки.

    Иконка чипа0
    Иконка глаза249
    Иконка комментариев0
    Картинка поста OpenCode: пользовательские провайдеры недоступны — что делать
    Иконка аватараАнтон
    Иконка календаря26 октября 2026
    AI-инструментыapiразработкаjuniorИконка уровня junior

    OpenCode: пользовательские провайдеры недоступны — что делать

    Ошибка «Пользовательские провайдеры недоступны на этом сервере» (в английском интерфейсе — «Custom providers are unavailable on this server») появляется в OpenCode Desktop и веб-интерфейсе OpenCode, когда вы сохраняете своего провайдера через «Настройки → Провайдеры → Пользовательский провайдер». Это ошибка самого OpenCode, а не вашего ключа, сервера или региона: в версиях 2.0.x форма сохранения не подключена к серверу и выдаёт эту ошибку при любых введённых данных. VPN и смена ключа не помогут. Решение — описать провайдера в файле `opencode.json`: так он работает и в Desktop, и в терминальной версии. Ниже — что именно сломано, готовые конфиги для Claude, GPT и Qwen и что делать, если модели не появились.

    Иконка чипа0
    Иконка глаза1 287
    Иконка комментариев0
    Иконка чипа0