photoplaces_twilight-design-system.md 40 KB

PhotoPlaces Twilight — Дизайн-система

photoplaces_twilight — концепция дизайна, вдохновлённая магией сумерек: когда golden hour переходит в blue hour, и каждый кадр наполнен тёплым светом и креативностью.

1. АНАЛИЗ ПРОЕКТА

Что это

PhotoPlaces Twilight — маркетплейс фотолокаций. Пользователи находят места и студии для фотосъёмок, бронируют, общаются с фотографами.

Целевая аудитория

  • Фотографы (25-40 лет): ищут новые локации, хотят впечатлить клиентов
  • Владельцы студий/локаций: хотят привлечь клиентов, показать пространство
  • Заказчики съёмок (20-35 лет): свадьбы, любительские, коммерческие съёмки

Эмоциональный посыл

  1. Вдохновение — хочется снимать, открывать новые места
  2. Надёжность — можно доверять бронирование
  3. Профессионализм — серьёзный подход к фотосъёмке
  4. Открытость — дружелюбное сообщество

Ключевые ценности

Вдохновение • Надёжность • Красота • Сообщество

Визуальный характер

Современный минимализм с акцентом на визуальный контент. Фотографии — главные герои. Интерфейс — рамка, которая не отвлекает.

Философия Twilight

  • Golden Hour — тёплые янтарные акценты (secondary #F59E0B)
  • Blue Hour — глубокий индиго как основной цвет (primary #6366F1)
  • Ночной контраст — чистый минимализм на тёмном фоне
  • Магия момента — каждое фото как произведение искусства

2. ЦВЕТОВАЯ ПАЛИТРА

Философия Twilight

Тёмная тема — идеальный фон для фотографий (как ночное небо). Акцентный цвет — индиго-фиолетовый, который ассоциируется с креативностью и вечерним blue hour. Тёплый янтарный secondary — отсылка к golden hour.

Primary: Индиго-фиолетовый

Формат Значение
Hex #6366F1
RGB rgb(99, 102, 241)
HSL hsl(239, 84%, 67%)

Почему именно этот:

  • Ассоциируется с креативностью и вечерним светом (golden hour → blue hour)
  • Хорошо читается на тёмном фоне
  • Отличается от типичного зелёного/синего в конкурентах
  • Работает как для CTA, так и для акцентов

Secondary: Тёплый янтарный

Формат Значение
Hex #F59E0B
RGB rgb(245, 158, 11)
HSL hsl(38, 92%, 50%)

Назначение: бейджи, рейтинг, избранные, премиум-метки

Accent: Бирюзовый

Формат Значение
Hex #06B6D4
RGB rgb(6, 182, 212)
HSL hsl(189, 94%, 43%)

Назначение: ссылки, интерактивные элементы, ховер-состояния

Neutrals (тёмная тема)

Токен Hex Назначение
--bg #09090B Основной фон
--bg-elevated #18181B Поверхности (карточки, модалки)
--bg-hover #27272A Ховер-состояния
--border #3F3F46 Границы, разделители
--border-subtle #27272A Мягкие границы
--text-primary #FAFAFA Основной текст
--text-secondary #A1A1AA Вторичный текст
--text-muted #71717A Приглушённый текст

Neutrals (светлая тема)

Токен Hex Назначение
--bg #FAFAFA Основной фон
--bg-elevated #FFFFFF Поверхности
--bg-hover #F4F4F5 Ховер-состояния
--border #E4E4E7 Границы
--border-subtle #F4F4F5 Мягкие границы
--text-primary #18181B Основной текст
--text-secondary #52525B Вторичный текст
--text-muted #A1A1AA Приглушённый текст

Functional Colors

Состояние Hex Назначение
--success #22C55E Успешные действия, подтверждения
--warning #F59E0B Предупреждения, ожидание
--error #EF4444 Ошибки, удаление
--info #3B82F6 Информационные сообщения

Accessibility (контрастные пары)

Комбинация Ratio WCAG
#FAFAFA на #09090B 19.3:1 AAA ✓
#6366F1 на #09090B 6.8:1 AA ✓
#A1A1AA на #09090B 7.2:1 AA ✓
#18181B на #FAFAFA 19.3:1 AAA ✓
#6366F1 на #FFFFFF 4.7:1 AA ✓

3. ТИПОГРАФИКА

Шрифты

Роль Шрифт Обоснование
Заголовки Inter Геометрический, чистый, отлично читается на экране
Основной текст Inter Единообразие, хорошая читаемость
Mono JetBrains Mono Для кода, ID, технической информации

Почему Inter:

  • Бесплатный (Google Fonts)
  • Оптимизирован для экранов
  • Отличная поддержка кириллицы
  • Множество начертаний (от Thin до Black)

Масштабная шкала

Токен Размер Line-height Letter-spacing Использование
--text-xs 12px 16px 0.01em Метки, капшены
--text-sm 14px 20px 0.01em Small text, описания
--text-base 16px 24px 0em Основной текст
--text-lg 18px 28px -0.01em Крупный body text
--text-xl 20px 28px -0.01em h5, h4
--text-2xl 24px 32px -0.02em h3
--text-3xl 30px 36px -0.02em h2
--text-4xl 36px 40px -0.03em h1
--text-5xl 48px 48px -0.03em Hero заголовки

Иерархия текста

Hero (text-5xl, font-bold)
  └── Подзаголовок (text-lg, text-secondary)

Раздел (text-3xl, font-semibold)
  └── Описание (text-base, text-secondary)

Карточка заголовок (text-xl, font-medium)
  └── Карточка описание (text-sm, text-secondary)

Метка (text-xs, font-medium, uppercase, tracking-wider)
  └── Значение (text-base, font-semibold)

4. СЕТКА И ОТСТУПЫ

Базовая единица: 4px

Spacing Scale

Токен Значение Использование
--space-0 0px Нет отступа
--space-0.5 2px Минимальный зазор
--space-1 4px Минимальный отступ
--space-1.5 6px Between иконок
--space-2 8px Внутренний padding кнопок
--space-3 12px Between элементов в строке
--space-4 16px Стандартный padding
--space-5 20px Между секциями
--space-6 24px Карточки, блоки
--space-8 32px Между группами
--space-10 40px Отступы секций
--space-12 48px Крупные разделители
--space-16 64px Hero-секции
--space-20 80px Между страницами

Сетка

Параметр Значение
Колонки (desktop) 12
Колонки (tablet) 8
Колонки (mobile) 4
Gutter 24px (desktop), 16px (mobile)
Max-width 1280px
Padding 32px (desktop), 16px (mobile)

Breakpoints

Имя Минимальная ширина Описание
sm 640px Мобильный (альбом)
md 768px Планшет
lg 1024px Десктоп
xl 1280px Широкий десктоп
2xl 1536px Экстрабольшой

Border Radius

Токен Значение Использование
--radius-sm 6px Кнопки, инпуты
--radius-md 8px Карточки, модалки
--radius-lg 12px Крупные блоки
--radius-xl 16px Hero-элементы
--radius-2xl 24px Аватары, круглые элементы
--radius-full 9999px Пилюли, бейджи

Shadows (тёмная тема)

Токен Значение
--shadow-sm 0 1px 2px rgba(0,0,0,0.4)
--shadow-md 0 4px 6px -1px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.3)
--shadow-lg 0 10px 15px -3px rgba(0,0,0,0.4), 0 4px 6px rgba(0,0,0,0.3)
--shadow-xl 0 20px 25px -5px rgba(0,0,0,0.4), 0 8px 10px rgba(0,0,0,0.3)
--shadow-glow 0 0 20px rgba(99,102,241,0.3)

Shadows (светлая тема)

Токен Значение
--shadow-sm 0 1px 2px rgba(0,0,0,0.05)
--shadow-md 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.06)
--shadow-lg 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05)
--shadow-xl 0 20px 25px -5px rgba(0,0,0,0.1), 0 8px 10px rgba(0,0,0,0.04)

5. КОМПОНЕНТЫ


Button

Описание

Основной интерактивный элемент. Визуально — чистый, с чёткими состояниями.

Variants

Primary

background: var(--primary);
color: white;
font-weight: 600;

Secondary

background: transparent;
border: 1px solid var(--border);
color: var(--text-primary);

Ghost

background: transparent;
color: var(--text-secondary);

Danger

background: var(--error);
color: white;

Размеры

Размер Высота Padding Шрифт Border-radius
sm 32px 8px 12px 13px 6px
md 40px 10px 16px 14px 8px
lg 48px 12px 24px 16px 8px
xl 56px 16px 32px 18px 12px

Состояния

Состояние Изменение
Default Базовый вид
Hover brightness(1.1), transform: translateY(-1px)
Active brightness(0.95), transform: translateY(0)
Focus outline: 2px solid var(--primary), outline-offset: 2px
Disabled opacity: 0.5, cursor: not-allowed
Loading Спиннер вместо текста, pointer-events: none

Tailwind-классы

// Primary
<button className="bg-indigo-600 text-white font-semibold rounded-lg
  hover:bg-indigo-500 active:bg-indigo-700
  focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-zinc-900
  disabled:opacity-50 disabled:cursor-not-allowed
  transition-all duration-150
  px-4 py-2 text-sm">

// Secondary
<button className="bg-transparent border border-zinc-700 text-zinc-100 font-medium rounded-lg
  hover:bg-zinc-800 active:bg-zinc-700
  focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:ring-offset-2 focus:ring-offset-zinc-900
  disabled:opacity-50 disabled:cursor-not-allowed
  transition-all duration-150
  px-4 py-2 text-sm">

// Ghost
<button className="bg-transparent text-zinc-400 font-medium rounded-lg
  hover:bg-zinc-800 hover:text-zinc-100 active:bg-zinc-700
  focus:outline-none focus:ring-2 focus:ring-zinc-500 focus:ring-offset-2 focus:ring-offset-zinc-900
  disabled:opacity-50 disabled:cursor-not-allowed
  transition-all duration-150
  px-4 py-2 text-sm">

Доступность

  • aria-label для кнопок с иконками
  • aria-disabled="true" вместо disabled для семантики
  • role="button" для div-кнопок (но лучше использовать <button>)

Input / TextField

Описание

Поле ввода с лейблом, плейсхолдером и состояниями ошибки.

Визуал

┌─────────────────────────────────┐
│ Лейбл (обязательный)            │
├─────────────────────────────────┤
│ Иконка │ Placeholder/Value      │
└─────────────────────────────────┘
│ Текст ошибки (если есть)        │

Размеры

Размер Высота Padding Шрифт
sm 32px 8px 12px 13px
md 40px 10px 14px 14px
lg 48px 12px 16px 16px

Состояния

Состояние Визуал
Default border-zinc-700, bg-zinc-900
Focus border-indigo-500, ring-2 ring-indigo-500/20
Error border-red-500, ring-2 ring-red-500/20
Disabled opacity-50, cursor-not-allowed

Tailwind-классы

<div className="flex flex-col gap-1.5">
  <label className="text-sm font-medium text-zinc-300">
    Лейбл
  </label>
  <input
    className="h-10 px-3 rounded-lg
      bg-zinc-900 border border-zinc-700 text-zinc-100
      placeholder:text-zinc-500
      focus:outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20
      disabled:opacity-50 disabled:cursor-not-allowed
      transition-colors duration-150
      text-sm"
    placeholder="Введите значение"
  />
  <span className="text-xs text-red-400">
    Текст ошибки
  </span>
</div>

Доступность

  • aria-label или aria-labelledby для лейбла
  • aria-invalid="true" при ошибке
  • aria-describedby для связи с текстом ошибки
  • aria-required="true" для обязательных полей

Card

Описание

Карточка для отображения локаций, студий, профилей. Главная фича — крупное фото.

Визуал

┌─────────────────────────────────┐
│                                 │
│         [ФОТО / IMAGE]          │
│                                 │
├─────────────────────────────────┤
│ Заголовок карточки              │
│ Описание (2 строки)             │
│                                 │
│ ★ 4.8  ·  Москва  ·  Студия    │
├─────────────────────────────────┤
│ [Забронировать]     [Избранное ♡]│
└─────────────────────────────────┘

Варианты

Вариант Использование
Default Карточка локации в каталоге
Compact Карточка в боковой панели
Featured Крупная карточка в hero-секции
Minimal Карточка в списке результатов

Tailwind-классы

<div className="group rounded-xl bg-zinc-900 border border-zinc-800
  overflow-hidden hover:border-zinc-700
  transition-all duration-300 hover:shadow-xl hover:shadow-black/20
  hover:-translate-y-1">

  {/* Изображение */}
  <div className="aspect-[4/3] overflow-hidden">
    <img
      className="w-full h-full object-cover
        group-hover:scale-105 transition-transform duration-500"
      src="..."
      alt="..."
    />
  </div>

  {/* Контент */}
  <div className="p-4">
    <h3 className="text-lg font-semibold text-zinc-100 mb-1">
      Название
    </h3>
    <p className="text-sm text-zinc-400 line-clamp-2 mb-3">
      Описание локации
    </p>

    {/* Мета-информация */}
    <div className="flex items-center gap-2 text-xs text-zinc-500">
      <span className="flex items-center gap-1">
        <StarIcon className="w-3.5 h-3.5 text-amber-400" />
        4.8
      </span>
      <span>·</span>
      <span>Москва</span>
      <span>·</span>
      <span>Студия</span>
    </div>
  </div>

  {/* Действия */}
  <div className="px-4 pb-4 flex items-center justify-between">
    <button className="text-sm font-medium text-indigo-400 hover:text-indigo-300">
      Забронировать
    </button>
    <button className="text-zinc-500 hover:text-rose-400 transition-colors">
      <HeartIcon className="w-5 h-5" />
    </button>
  </div>
</div>

Доступность

  • role="article" или <article>
  • alt для изображений
  • aria-label для кнопок действий

Badge / Tag

Описание

Метки для категорий, статусов, тегов.

Варианты

Вариант Цвет Использование
Default bg-zinc-800 text-zinc-300 Обычные теги
Primary bg-indigo-500/20 text-indigo-400 Основные категории
Success bg-emerald-500/20 text-emerald-400 Доступно, подтверждено
Warning bg-amber-500/20 text-amber-400 Ожидание, на модерации
Error bg-red-500/20 text-red-400 Ошибка, отменено

Tailwind-классы

<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full
  bg-indigo-500/20 text-indigo-400 text-xs font-medium">
  Студия
</span>

Modal / Dialog

Описание

Модальное окно для форм, подтверждений, детальной информации.

Размеры

Размер Ширина Описание
sm 400px Подтверждения, простые формы
md 500px Формы входа/регистрации
lg 640px Сложные формы, детали
xl 800px Полноэкранный просмотр

Визуал

┌─────────────────────────────────────────┐
│ Заголовок                    [X закрыть]│
├─────────────────────────────────────────┤
│                                         │
│              Содержимое                 │
│                                         │
├─────────────────────────────────────────┤
│ [Отмена]                    [Действие]  │
└─────────────────────────────────────────┘

Tailwind-классы

// Backdrop
<div className="fixed inset-0 z-50 bg-black/60 backdrop-blur-sm
  animate-in fade-in duration-200" />

// Modal
<div className="fixed left-1/2 top-1/2 z-50
  -translate-x-1/2 -translate-y-1/2
  w-full max-w-md p-6
  bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl
  animate-in fade-in zoom-in-95 duration-200">

  {/* Header */}
  <div className="flex items-center justify-between mb-4">
    <h2 className="text-lg font-semibold text-zinc-100">
      Заголовок
    </h2>
    <button className="text-zinc-500 hover:text-zinc-300">
      <XIcon className="w-5 h-5" />
    </button>
  </div>

  {/* Content */}
  <div className="text-zinc-400">
    Содержимое
  </div>

  {/* Footer */}
  <div className="flex justify-end gap-3 mt-6">
    <button className="secondary">Отмена</button>
    <button className="primary">Подтвердить</button>
  </div>
</div>

Доступность

  • role="dialog"
  • aria-modal="true"
  • aria-labelledby для заголовка
  • Фокус внутри модалки (focus trap)
  • Закрытие по Escape

Navigation (Header)

Описание

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

Визуал

┌─────────────────────────────────────────────────────────────┐
│ PhotoPlaces Twilight    Локации  Студии  Фотографы    [Войти] │
└─────────────────────────────────────────────────────────────┘

Tailwind-классы

<header className="sticky top-0 z-40 w-full
  bg-zinc-950/80 backdrop-blur-xl
  border-b border-zinc-800">

  <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
    <div className="flex h-16 items-center justify-between">

      {/* Logo */}
      <Link href="/" className="flex items-center gap-2">
        <CameraIcon className="h-8 w-8 text-indigo-500" />
        <span className="text-xl font-bold text-zinc-100">
          PhotoPlaces Twilight
        </span>
      </Link>

      {/* Nav Links */}
      <nav className="hidden md:flex items-center gap-6">
        <Link href="/places" className="text-sm text-zinc-400 hover:text-zinc-100 transition-colors">
          Локации
        </Link>
        <Link href="/studios" className="text-sm text-zinc-400 hover:text-zinc-100 transition-colors">
          Студии
        </Link>
        <Link href="/photographers" className="text-sm text-zinc-400 hover:text-zinc-100 transition-colors">
          Фотографы
        </Link>
      </nav>

      {/* Actions */}
      <div className="flex items-center gap-3">
        <button className="text-sm text-zinc-400 hover:text-zinc-100">
          Войти
        </button>
        <button className="bg-indigo-600 text-white text-sm font-medium px-4 py-2 rounded-lg hover:bg-indigo-500">
          Регистрация
        </button>
      </div>

    </div>
  </div>
</header>

Footer

Описание

Подвал сайта с ссылками, копирайтом, соцсетями.

Tailwind-классы

<footer className="border-t border-zinc-800 bg-zinc-950">
  <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12">

    <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
      {/* Колонка 1: О проекте */}
      <div>
        <h3 className="text-sm font-semibold text-zinc-100 mb-4">
          PhotoPlaces Twilight
        </h3>
        <p className="text-sm text-zinc-500">
          Маркетплейс фотолокаций
        </p>
      </div>

      {/* Колонка 2: Навигация */}
      <div>
        <h4 className="text-sm font-medium text-zinc-300 mb-3">Навигация</h4>
        <ul className="space-y-2">
          <li><a href="/places" className="text-sm text-zinc-500 hover:text-zinc-300">Локации</a></li>
          <li><a href="/studios" className="text-sm text-zinc-500 hover:text-zinc-300">Студии</a></li>
        </ul>
      </div>

      {/* Колонка 3: Контакты */}
      <div>
        <h4 className="text-sm font-medium text-zinc-300 mb-3">Контакты</h4>
        <ul className="space-y-2">
          <li><a href="mailto:info@photoplaces.ru" className="text-sm text-zinc-500 hover:text-zinc-300">info@photoplaces.ru</a></li>
        </ul>
      </div>

      {/* Колонка 4: Соцсети */}
      <div>
        <h4 className="text-sm font-medium text-zinc-300 mb-3">Соцсети</h4>
        <div className="flex gap-3">
          <a href="#" className="text-zinc-500 hover:text-zinc-300">
            <TelegramIcon className="w-5 h-5" />
          </a>
          <a href="#" className="text-zinc-500 hover:text-zinc-300">
            <InstagramIcon className="w-5 h-5" />
          </a>
          <a href="#" className="text-zinc-500 hover:text-zinc-300">
            <PinterestIcon className="w-5 h-5" />
          </a>
        </div>
      </div>
    </div>

    <div className="mt-8 pt-8 border-t border-zinc-800">
      <p className="text-xs text-zinc-600 text-center">
        © 2024 PhotoPlaces Twilight. Все права защищены.
      </p>
    </div>

  </div>
</footer>

Toast / Notification

Описание

Всплывающие уведомления о результатах действий.

Варианты

Тип Иконка Цвет Пример
Success CheckCircle text-emerald-400 "Локация добавлена"
Error XCircle text-red-400 "Ошибка сервера"
Warning AlertTriangle text-amber-400 "Проверьте данные"
Info Info text-blue-400 "Отправлена заявка"

Tailwind-классы

<div className="fixed bottom-4 right-4 z-50
  flex items-center gap-3 px-4 py-3
  bg-zinc-900 border border-zinc-800 rounded-lg shadow-xl
  animate-in slide-in-from-bottom-5 fade-in duration-300">

  <CheckCircleIcon className="w-5 h-5 text-emerald-400 shrink-0" />
  <p className="text-sm text-zinc-100">
    Локация успешно добавлена!
  </p>
  <button className="text-zinc-500 hover:text-zinc-300 ml-2">
    <XIcon className="w-4 h-4" />
  </button>
</div>

Tabs

Описание

Переключение между секциями или вкладками.

Tailwind-классы

<div className="border-b border-zinc-800">
  <nav className="flex gap-6" role="tablist">
    <button
      role="tab"
      aria-selected="true"
      className="py-3 text-sm font-medium text-indigo-400 border-b-2 border-indigo-400"
    >
      Вкладка 1
    </button>
    <button
      role="tab"
      aria-selected="false"
      className="py-3 text-sm font-medium text-zinc-500 hover:text-zinc-300 border-b-2 border-transparent"
    >
      Вкладка 2
    </button>
  </nav>
</div>

Tooltip / Popover

Описание

Вспомогательная информация при наведении.

Tailwind-классы

<div className="relative group">
  <button>Элемент</button>
  <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2
    px-3 py-1.5 rounded-lg
    bg-zinc-800 text-zinc-200 text-xs
    opacity-0 group-hover:opacity-100
    transition-opacity duration-200
    pointer-events-none
    whitespace-nowrap">
    Подсказка
    <div className="absolute top-full left-1/2 -translate-x-1/2
      border-4 border-transparent border-t-zinc-800" />
  </div>
</div>

Table

Описание

Таблица данных для админки, списков бронирований.

Tailwind-классы

<div className="overflow-x-auto rounded-lg border border-zinc-800">
  <table className="w-full text-sm">
    <thead>
      <tr className="border-b border-zinc-800 bg-zinc-900/50">
        <th className="text-left py-3 px-4 font-medium text-zinc-400">Название</th>
        <th className="text-left py-3 px-4 font-medium text-zinc-400">Статус</th>
        <th className="text-left py-3 px-4 font-medium text-zinc-400">Дата</th>
      </tr>
    </thead>
    <tbody>
      <tr className="border-b border-zinc-800/50 hover:bg-zinc-900/50 transition-colors">
        <td className="py-3 px-4 text-zinc-100">Название</td>
        <td className="py-3 px-4">
          <span className="inline-flex px-2 py-1 rounded-full bg-emerald-500/20 text-emerald-400 text-xs">
            Активно
          </span>
        </td>
        <td className="py-3 px-4 text-zinc-400">01.01.2024</td>
      </tr>
    </tbody>
  </table>
</div>

6. АНИМАЦИИ И ПЕРЕХОДЫ

Длительность

Токен Значение Использование
--duration-fast 100ms Hover-эффекты, color transitions
--duration-normal 200ms Toggle, appearance
--duration-slow 300ms Modal, dropdown
--duration-slower 500ms Page transitions, complex

Кривые

Токен Значение Использование
--ease-default cubic-bezier(0.4, 0, 0.2, 1) Стандартные переходы
--ease-in cubic-bezier(0.4, 0, 1, 1) Уход элементов
--ease-out cubic-bezier(0, 0, 0.2, 1) Появление элементов
--ease-spring cubic-bezier(0.34, 1.56, 0.64, 1) Bounce-эффекты

Примеры анимаций

/* Появление модалки */
@keyframes modal-in {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
}

/* Slide-in toast */
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(100%);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Shimmer для skeleton */
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-elevated) 25%,
    var(--bg-hover) 50%,
    var(--bg-elevated) 75%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

7. ПРАВИЛА И ПРИНЦИПЫ

1. Content First (Контент важнее декора)

Фотографии — главные герои. Интерфейс обрамляет, но не перетягивает внимание.

Плохо: Яркие рамки вокруг фото, мигающие кнопки, слишком много цвета Хорошо: Фото занимает 70% карточки, UI — тихий фон


2. Clarity Over Cleverness (Понятность важнее красивости)

Пользователь должен понимать, что делать, без инструкций.

Плохо: Кнопка-иконка без подписи,神秘ные жесты Хорошо: "Забронировать" вместо просто иконки корзины


3. Accessible by Default (Доступность не опциональность)

Каждый элемент должен работать для всех пользователей.

Плохо: Серый текст на сером фоне, нет фокуса, нет aria Хорошо: Контраст 4.5:1, видимый фокус, семантический HTML


4. Consistent Rhythm (Единый ритм)

Отступы, размеры, анимации следуют единой системе.

Плохо: Разные отступы в карточках, рандомные размеры шрифтов Хорошо: Везде spacing-4, text-sm для описаний, radius-lg для карточек


5. Progressive Disclosure (Показывай по необходимости)

Не перегружай интерфейс. Показывай детали по мере необходимости.

Плохо: Вся форма бронирования на главной Хорошо: Сначала карточка → клик → модалка с формой


8. ТОКЕНЫ ДЛЯ FIGMA

JSON-структура (Tokens Studio)

PhotoPlaces Twilight Design Tokens

{
  "colors": {
    "primary": {
      "50": "#EEF2FF",
      "100": "#E0E7FF",
      "200": "#C7D2FE",
      "300": "#A5B4FC",
      "400": "#818CF8",
      "500": "#6366F1",
      "600": "#4F46E5",
      "700": "#4338CA",
      "800": "#3730A3",
      "900": "#312E81"
    },
    "secondary": {
      "500": "#F59E0B",
      "600": "#D97706"
    },
    "accent": {
      "500": "#06B6D4",
      "600": "#0891B2"
    },
    "neutral": {
      "50": "#FAFAFA",
      "100": "#F4F4F5",
      "200": "#E4E4E7",
      "300": "#D4D4D8",
      "400": "#A1A1AA",
      "500": "#71717A",
      "600": "#52525B",
      "700": "#3F3F46",
      "800": "#27272A",
      "900": "#18181B",
      "950": "#09090B"
    },
    "success": { "500": "#22C55E" },
    "warning": { "500": "#F59E0B" },
    "error": { "500": "#EF4444" },
    "info": { "500": "#3B82F6" }
  },
  "typography": {
    "fontFamily": {
      "heading": "Inter, sans-serif",
      "body": "Inter, sans-serif",
      "mono": "JetBrains Mono, monospace"
    },
    "fontSize": {
      "xs": { "value": "12px", "lineHeight": "16px" },
      "sm": { "value": "14px", "lineHeight": "20px" },
      "base": { "value": "16px", "lineHeight": "24px" },
      "lg": { "value": "18px", "lineHeight": "28px" },
      "xl": { "value": "20px", "lineHeight": "28px" },
      "2xl": { "value": "24px", "lineHeight": "32px" },
      "3xl": { "value": "30px", "lineHeight": "36px" },
      "4xl": { "value": "36px", "lineHeight": "40px" },
      "5xl": { "value": "48px", "lineHeight": "48px" }
    },
    "fontWeight": {
      "normal": "400",
      "medium": "500",
      "semibold": "600",
      "bold": "700"
    }
  },
  "spacing": {
    "0": "0px",
    "0.5": "2px",
    "1": "4px",
    "1.5": "6px",
    "2": "8px",
    "3": "12px",
    "4": "16px",
    "5": "20px",
    "6": "24px",
    "8": "32px",
    "10": "40px",
    "12": "48px",
    "16": "64px",
    "20": "80px"
  },
  "borderRadius": {
    "sm": "6px",
    "md": "8px",
    "lg": "12px",
    "xl": "16px",
    "2xl": "24px",
    "full": "9999px"
  },
  "boxShadow": {
    "sm": "0 1px 2px rgba(0,0,0,0.4)",
    "md": "0 4px 6px -1px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.3)",
    "lg": "0 10px 15px -3px rgba(0,0,0,0.4), 0 4px 6px rgba(0,0,0,0.3)",
    "xl": "0 20px 25px -5px rgba(0,0,0,0.4), 0 8px 10px rgba(0,0,0,0.3)",
    "glow": "0 0 20px rgba(99,102,241,0.3)"
  },
  "duration": {
    "fast": "100ms",
    "normal": "200ms",
    "slow": "300ms",
    "slower": "500ms"
  }
}

9. CSS-ПЕРЕМЕННЫЕ

/* PhotoPlaces Twilight Design System */
:root {
  /* ========== LIGHT THEME ========== */
  --bg: #FAFAFA;
  --bg-elevated: #FFFFFF;
  --bg-hover: #F4F4F5;
  --border: #E4E4E7;
  --border-subtle: #F4F4F5;
  --text-primary: #18181B;
  --text-secondary: #52525B;
  --text-muted: #A1A1AA;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.06);
  --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);
}

[data-theme="dark"] {
  /* ========== DARK THEME ========== */
  --bg: #09090B;
  --bg-elevated: #18181B;
  --bg-hover: #27272A;
  --border: #3F3F46;
  --border-subtle: #27272A;
  --text-primary: #FAFAFA;
  --text-secondary: #A1A1AA;
  --text-muted: #71717A;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
  --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.4), 0 2px 4px rgba(0,0,0,0.3);
  --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.4), 0 4px 6px rgba(0,0,0,0.3);
}

:root {
  /* ========== COLORS ========== */
  --primary: #6366F1;
  --primary-hover: #4F46E5;
  --secondary: #F59E0B;
  --accent: #06B6D4;

  /* Functional */
  --success: #22C55E;
  --warning: #F59E0B;
  --error: #EF4444;
  --info: #3B82F6;

  /* ========== TYPOGRAPHY ========== */
  --font-heading: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;

  /* ========== SPACING ========== */
  --space-0: 0px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;

  /* ========== BORDER RADIUS ========== */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-2xl: 24px;
  --radius-full: 9999px;

  /* ========== TRANSITIONS ========== */
  --duration-fast: 100ms;
  --duration-normal: 200ms;
  --duration-slow: 300ms;
  --duration-slower: 500ms;

  --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

10. TAILWIND CONFIG

// tailwind.config.js
// PhotoPlaces Twilight Design System
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{ts,tsx}'],
  darkMode: ['class', '[data-theme="dark"]'],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#EEF2FF',
          100: '#E0E7FF',
          200: '#C7D2FE',
          300: '#A5B4FC',
          400: '#818CF8',
          500: '#6366F1',
          600: '#4F46E5',
          700: '#4338CA',
          800: '#3730A3',
          900: '#312E81',
        },
        secondary: {
          500: '#F59E0B',
          600: '#D97706',
        },
        accent: {
          500: '#06B6D4',
          600: '#0891B2',
        },
      },
      fontFamily: {
        heading: ['Inter', 'sans-serif'],
        body: ['Inter', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
      fontSize: {
        'xs': ['12px', { lineHeight: '16px' }],
        'sm': ['14px', { lineHeight: '20px' }],
        'base': ['16px', { lineHeight: '24px' }],
        'lg': ['18px', { lineHeight: '28px' }],
        'xl': ['20px', { lineHeight: '28px' }],
        '2xl': ['24px', { lineHeight: '32px' }],
        '3xl': ['30px', { lineHeight: '36px' }],
        '4xl': ['36px', { lineHeight: '40px' }],
        '5xl': ['48px', { lineHeight: '48px' }],
      },
      spacing: {
        '0.5': '2px',
        '1.5': '6px',
        '18': '72px',
        '88': '352px',
      },
      borderRadius: {
        'sm': '6px',
        'md': '8px',
        'lg': '12px',
        'xl': '16px',
        '2xl': '24px',
      },
      boxShadow: {
        'glow': '0 0 20px rgba(99,102,241,0.3)',
        'glow-lg': '0 0 40px rgba(99,102,241,0.4)',
      },
      animation: {
        'fade-in': 'fade-in 0.2s ease-out',
        'slide-up': 'slide-up 0.3s ease-out',
        'slide-down': 'slide-down 0.3s ease-out',
        'scale-in': 'scale-in 0.2s ease-out',
      },
      keyframes: {
        'fade-in': {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        'slide-up': {
          '0%': { transform: 'translateY(10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
        'slide-down': {
          '0%': { transform: 'translateY(-10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
        'scale-in': {
          '0%': { transform: 'scale(0.95)', opacity: '0' },
          '100%': { transform: 'scale(1)', opacity: '1' },
        },
      },
    },
  },
  plugins: [
    require('tailwindcss-animate'),
  ],
}