Стандарты

Адаптивный дизайн

Стандарт определяет правила создания адаптивного интерфейса с использованием 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>
<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>

Тестирование адаптивности

В браузере

  1. DevTools → Toggle device toolbar (Ctrl+Shift+M)
  2. Проверить на 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

Связанные документы