photoplaces_twilight — концепция дизайна, вдохновлённая магией сумерек: когда golden hour переходит в blue hour, и каждый кадр наполнен тёплым светом и креативностью.
PhotoPlaces Twilight — маркетплейс фотолокаций. Пользователи находят места и студии для фотосъёмок, бронируют, общаются с фотографами.
Вдохновение • Надёжность • Красота • Сообщество
Современный минимализм с акцентом на визуальный контент. Фотографии — главные герои. Интерфейс — рамка, которая не отвлекает.
#F59E0B)#6366F1)Тёмная тема — идеальный фон для фотографий (как ночное небо). Акцентный цвет — индиго-фиолетовый, который ассоциируется с креативностью и вечерним blue hour. Тёплый янтарный secondary — отсылка к golden hour.
| Формат | Значение |
|---|---|
| Hex | #6366F1 |
| RGB | rgb(99, 102, 241) |
| HSL | hsl(239, 84%, 67%) |
Почему именно этот:
| Формат | Значение |
|---|---|
| Hex | #F59E0B |
| RGB | rgb(245, 158, 11) |
| HSL | hsl(38, 92%, 50%) |
Назначение: бейджи, рейтинг, избранные, премиум-метки
| Формат | Значение |
|---|---|
| Hex | #06B6D4 |
| RGB | rgb(6, 182, 212) |
| HSL | hsl(189, 94%, 43%) |
Назначение: ссылки, интерактивные элементы, ховер-состояния
| Токен | Hex | Назначение |
|---|---|---|
--bg |
#09090B |
Основной фон |
--bg-elevated |
#18181B |
Поверхности (карточки, модалки) |
--bg-hover |
#27272A |
Ховер-состояния |
--border |
#3F3F46 |
Границы, разделители |
--border-subtle |
#27272A |
Мягкие границы |
--text-primary |
#FAFAFA |
Основной текст |
--text-secondary |
#A1A1AA |
Вторичный текст |
--text-muted |
#71717A |
Приглушённый текст |
| Токен | Hex | Назначение |
|---|---|---|
--bg |
#FAFAFA |
Основной фон |
--bg-elevated |
#FFFFFF |
Поверхности |
--bg-hover |
#F4F4F5 |
Ховер-состояния |
--border |
#E4E4E7 |
Границы |
--border-subtle |
#F4F4F5 |
Мягкие границы |
--text-primary |
#18181B |
Основной текст |
--text-secondary |
#52525B |
Вторичный текст |
--text-muted |
#A1A1AA |
Приглушённый текст |
| Состояние | Hex | Назначение |
|---|---|---|
--success |
#22C55E |
Успешные действия, подтверждения |
--warning |
#F59E0B |
Предупреждения, ожидание |
--error |
#EF4444 |
Ошибки, удаление |
--info |
#3B82F6 |
Информационные сообщения |
| Комбинация | 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 ✓ |
| Роль | Шрифт | Обоснование |
|---|---|---|
| Заголовки | Inter | Геометрический, чистый, отлично читается на экране |
| Основной текст | Inter | Единообразие, хорошая читаемость |
| Mono | JetBrains Mono | Для кода, ID, технической информации |
Почему Inter:
| Токен | Размер | 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)
| Токен | Значение | Использование |
|---|---|---|
--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) |
| Имя | Минимальная ширина | Описание |
|---|---|---|
sm |
640px | Мобильный (альбом) |
md |
768px | Планшет |
lg |
1024px | Десктоп |
xl |
1280px | Широкий десктоп |
2xl |
1536px | Экстрабольшой |
| Токен | Значение | Использование |
|---|---|---|
--radius-sm |
6px | Кнопки, инпуты |
--radius-md |
8px | Карточки, модалки |
--radius-lg |
12px | Крупные блоки |
--radius-xl |
16px | Hero-элементы |
--radius-2xl |
24px | Аватары, круглые элементы |
--radius-full |
9999px | Пилюли, бейджи |
| Токен | Значение |
|---|---|
--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) |
| Токен | Значение |
|---|---|
--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) |
Основной интерактивный элемент. Визуально — чистый, с чёткими состояниями.
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 |
// 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>)Поле ввода с лейблом, плейсхолдером и состояниями ошибки.
┌─────────────────────────────────┐
│ Лейбл (обязательный) │
├─────────────────────────────────┤
│ Иконка │ 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 |
<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" для обязательных полейКарточка для отображения локаций, студий, профилей. Главная фича — крупное фото.
┌─────────────────────────────────┐
│ │
│ [ФОТО / IMAGE] │
│ │
├─────────────────────────────────┤
│ Заголовок карточки │
│ Описание (2 строки) │
│ │
│ ★ 4.8 · Москва · Студия │
├─────────────────────────────────┤
│ [Забронировать] [Избранное ♡]│
└─────────────────────────────────┘
| Вариант | Использование |
|---|---|
| Default | Карточка локации в каталоге |
| Compact | Карточка в боковой панели |
| Featured | Крупная карточка в hero-секции |
| Minimal | Карточка в списке результатов |
<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 для кнопок действийМетки для категорий, статусов, тегов.
| Вариант | Цвет | Использование |
|---|---|---|
| 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 |
Ошибка, отменено |
<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>
Модальное окно для форм, подтверждений, детальной информации.
| Размер | Ширина | Описание |
|---|---|---|
sm |
400px | Подтверждения, простые формы |
md |
500px | Формы входа/регистрации |
lg |
640px | Сложные формы, детали |
xl |
800px | Полноэкранный просмотр |
┌─────────────────────────────────────────┐
│ Заголовок [X закрыть]│
├─────────────────────────────────────────┤
│ │
│ Содержимое │
│ │
├─────────────────────────────────────────┤
│ [Отмена] [Действие] │
└─────────────────────────────────────────┘
// 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 для заголовкаВерхняя навигация с лого, ссылками и действиями пользователя.
┌─────────────────────────────────────────────────────────────┐
│ PhotoPlaces Twilight Локации Студии Фотографы [Войти] │
└─────────────────────────────────────────────────────────────┘
<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 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>
Всплывающие уведомления о результатах действий.
| Тип | Иконка | Цвет | Пример |
|---|---|---|---|
| Success | CheckCircle | text-emerald-400 |
"Локация добавлена" |
| Error | XCircle | text-red-400 |
"Ошибка сервера" |
| Warning | AlertTriangle | text-amber-400 |
"Проверьте данные" |
| Info | Info | text-blue-400 |
"Отправлена заявка" |
<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>
Переключение между секциями или вкладками.
<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>
Вспомогательная информация при наведении.
<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>
Таблица данных для админки, списков бронирований.
<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>
| Токен | Значение | Использование |
|---|---|---|
--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;
}
Фотографии — главные герои. Интерфейс обрамляет, но не перетягивает внимание.
Плохо: Яркие рамки вокруг фото, мигающие кнопки, слишком много цвета Хорошо: Фото занимает 70% карточки, UI — тихий фон
Пользователь должен понимать, что делать, без инструкций.
Плохо: Кнопка-иконка без подписи,神秘ные жесты Хорошо: "Забронировать" вместо просто иконки корзины
Каждый элемент должен работать для всех пользователей.
Плохо: Серый текст на сером фоне, нет фокуса, нет aria Хорошо: Контраст 4.5:1, видимый фокус, семантический HTML
Отступы, размеры, анимации следуют единой системе.
Плохо: Разные отступы в карточках, рандомные размеры шрифтов Хорошо: Везде spacing-4, text-sm для описаний, radius-lg для карточек
Не перегружай интерфейс. Показывай детали по мере необходимости.
Плохо: Вся форма бронирования на главной Хорошо: Сначала карточка → клик → модалка с формой
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"
}
}
/* 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);
}
// 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'),
],
}