Стандарты
Адаптивный дизайн
Стандарт определяет правила создания адаптивного интерфейса с использованием Tailwind CSS.
Стандарт определяет правила создания адаптивного интерфейса с использованием Tailwind CSS.
Принцип Mobile-First
Правило: Сначала пишем стили для мобильных устройств, затем расширяем для больших экранов.
<!-- ✅ Хорошо: Mobile-First -->
<div class="flex flex-col md:flex-row lg:gap-8">
<!-- По умолчанию (mobile): flex-col -->
<!-- md и выше: flex-row -->
<!-- lg и выше: gap-8 -->
</div>
<!-- ❌ Плохо: Desktop-First мышление -->
<div class="flex flex-row max-md:flex-col">
<!-- Сложнее читать и поддерживать -->
</div>
Breakpoints Tailwind
| Breakpoint | Минимальная ширина | Устройства |
|---|---|---|
| (default) | 0px | Мобильные |
sm: | 640px | Большие мобильные |
md: | 768px | Планшеты |
lg: | 1024px | Ноутбуки |
xl: | 1280px | Десктопы |
2xl: | 1536px | Большие мониторы |
Запомни: Без префикса = мобильная версия. Префикс = "от этого размера и выше".
Типовые паттерны
Сетка колонок
<template>
<!-- 1 колонка → 2 колонки → 3 колонки → 4 колонки -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
<Card v-for="item in items" :key="item.id" />
</div>
</template>
Sidebar + Content
<template>
<div class="flex flex-col lg:flex-row">
<!-- Sidebar: скрыт на мобильных или в drawer -->
<aside class="hidden lg:block lg:w-64 lg:shrink-0">
<Navigation />
</aside>
<!-- Content: полная ширина → с отступом под sidebar -->
<main class="flex-1 p-4 lg:p-6">
<slot />
</main>
</div>
</template>
Адаптивная типографика
<template>
<!-- Размер текста увеличивается с размером экрана -->
<h1 class="text-2xl md:text-3xl lg:text-4xl font-bold">
Заголовок
</h1>
<p class="text-sm md:text-base lg:text-lg">
Описание
</p>
</template>
Адаптивные отступы
<template>
<!-- Padding увеличивается с размером экрана -->
<section class="p-4 md:p-6 lg:p-8">
<div class="space-y-4 md:space-y-6">
<!-- Вертикальные отступы между элементами -->
</div>
</section>
</template>
Скрытие/показ элементов
<template>
<!-- Только на мобильных -->
<MobileMenu class="lg:hidden" />
<!-- Только на десктопе -->
<DesktopNavigation class="hidden lg:flex" />
<!-- Показать на планшетах и выше -->
<Sidebar class="hidden md:block" />
</template>
Адаптивный flex
<template>
<!-- Вертикально на мобильных, горизонтально на десктопе -->
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>Левая часть</div>
<div>Правая часть</div>
</div>
</template>
Таблицы
Таблицы сложно адаптировать. Варианты решения:
1. Горизонтальный скролл
<template>
<div class="overflow-x-auto">
<UTable :columns="columns" :rows="rows" />
</div>
</template>
2. Карточки на мобильных
<template>
<!-- Таблица на десктопе -->
<UTable :columns="columns" :rows="rows" class="hidden md:table" />
<!-- Карточки на мобильных -->
<div class="md:hidden space-y-4">
<OrderCard v-for="order in rows" :key="order.id" :order="order" />
</div>
</template>
3. Скрытие колонок
<script setup>
const columns = [
{ key: 'id', label: 'ID' },
{ key: 'name', label: 'Имя' },
{ key: 'email', label: 'Email', class: 'hidden md:table-cell' },
{ key: 'phone', label: 'Телефон', class: 'hidden lg:table-cell' },
{ key: 'actions', label: '' },
]
</script>
Формы
<template>
<UForm :state="state" class="space-y-4">
<!-- Поля в колонку на мобильных, в ряд на десктопе -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<UFormGroup label="Имя" name="firstName">
<UInput v-model="state.firstName" />
</UFormGroup>
<UFormGroup label="Фамилия" name="lastName">
<UInput v-model="state.lastName" />
</UFormGroup>
</div>
<!-- Полная ширина на всех экранах -->
<UFormGroup label="Email" name="email">
<UInput v-model="state.email" />
</UFormGroup>
<!-- Кнопки: полная ширина на мобильных -->
<div class="flex flex-col sm:flex-row sm:justify-end gap-2">
<UButton variant="ghost" class="w-full sm:w-auto">Отмена</UButton>
<UButton type="submit" class="w-full sm:w-auto">Сохранить</UButton>
</div>
</UForm>
</template>
Модальные окна
<template>
<UModal v-model="isOpen">
<UCard class="w-full sm:w-96 md:w-[500px] lg:w-[600px]">
<!-- Контент -->
</UCard>
</UModal>
</template>
Изображения
<template>
<!-- Адаптивные размеры -->
<img
src="/image.jpg"
class="w-full md:w-1/2 lg:w-1/3"
alt="..."
/>
<!-- Разные изображения для разных экранов (через @nuxt/image) -->
<NuxtImg
src="/hero.jpg"
sizes="100vw sm:50vw md:400px"
alt="..."
/>
</template>
Тестирование адаптивности
В браузере
- DevTools → Toggle device toolbar (Ctrl+Shift+M)
- Проверить на breakpoints: 375px, 768px, 1024px, 1440px
Чек-лист проверки
- Мобильный (375px) — всё читаемо, нет горизонтального скролла
- Планшет (768px) — оптимальное использование пространства
- Десктоп (1024px+) — контент не растянут, sidebar виден
Частые ошибки
1. Фиксированные размеры
<!-- ❌ Плохо: фиксированная ширина -->
<div class="w-[500px]">...</div>
<!-- ✅ Хорошо: адаптивная ширина -->
<div class="w-full max-w-lg">...</div>
2. Desktop-First
<!-- ❌ Плохо: начинаем с десктопа -->
<div class="flex-row max-md:flex-col">...</div>
<!-- ✅ Хорошо: начинаем с мобильных -->
<div class="flex-col md:flex-row">...</div>
3. Забытый overflow
<!-- ❌ Плохо: контент выходит за границы на мобильных -->
<div class="flex gap-4">
<div class="w-64">Sidebar</div>
<div>Content</div>
</div>
<!-- ✅ Хорошо: адаптивный layout -->
<div class="flex flex-col lg:flex-row gap-4">
<div class="lg:w-64">Sidebar</div>
<div class="flex-1 min-w-0">Content</div>
</div>
4. Мелкие touch-targets
<!-- ❌ Плохо: слишком маленькая область нажатия -->
<button class="p-1 text-xs">X</button>
<!-- ✅ Хорошо: минимум 44x44px для touch -->
<button class="p-2 min-w-[44px] min-h-[44px]">
<UIcon name="i-lucide-x" />
</button>
5. Margin вместо padding для touch-targets
<!-- ❌ Плохо: margin не увеличивает область нажатия -->
<button class="m-4">
<UIcon name="i-lucide-menu" class="w-5 h-5" />
</button>
<!-- ✅ Хорошо: padding увеличивает кликабельную область -->
<button class="p-4">
<UIcon name="i-lucide-menu" class="w-5 h-5" />
</button>
<!-- ❌ Плохо: иконка с margin, область нажатия маленькая -->
<a href="/profile" class="m-2">
<UAvatar size="sm" />
</a>
<!-- ✅ Хорошо: padding делает всю область кликабельной -->
<a href="/profile" class="p-2 -m-2">
<UAvatar size="sm" />
</a>
Правило: Для интерактивных элементов (кнопки, ссылки) используй padding для увеличения touch-target. margin не входит в кликабельную область.
Чек-лист
- Используется Mobile-First подход
- Нет фиксированных размеров (кроме max-width)
- Таблицы имеют мобильную версию или скролл
- Touch-targets минимум 44x44px (используй padding, не margin)
- Протестировано на 375px, 768px, 1024px