Mobile-first: почему база — это мобильный
Класс без приставки работает на всех экранах. Класс с приставкой включается начиная с указанной ширины и продолжает работать выше. Значит, порядок такой: сначала описываем узкий экран, потом добавляем «улучшения» для широких.
<!-- одна колонка везде; от 640px — две; от 1024px — четыре -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">…</div>
Mobile-first — как собирать рюкзак в поход: сначала кладёшь то, без чего нельзя, потом, если место осталось, добавляешь удобства. Desktop-first — это набить чемодан дома и выбрасывать вещи по дороге: выбрасывать всегда жальче, и в итоге тащишь лишнее.
Шкала брейкпоинтов
| Приставка | Ширина от | Обычно это |
|---|---|---|
| — (база) | 0 | телефон |
sm: | 40rem / 640px | крупный телефон, узкое окно |
md: | 48rem / 768px | планшет |
lg: | 64rem / 1024px | ноутбук |
xl: | 80rem / 1280px | десктоп |
2xl: | 96rem / 1536px | большой монитор |
Живой пример — растяните окно браузера и следите за подписью:
Текущий диапазон:
база — до 640px
<div class="rounded-lg border border-slate-200 bg-white p-4 text-sm">
<p class="text-slate-700">Текущий диапазон:</p>
<p class="mt-1 font-semibold text-rose-600 sm:hidden">база — до 640px</p>
<p class="mt-1 hidden font-semibold text-amber-600 sm:block md:hidden">sm — 640–768px</p>
<p class="mt-1 hidden font-semibold text-emerald-600 md:block lg:hidden">md — 768–1024px</p>
<p class="mt-1 hidden font-semibold text-sky-600 lg:block xl:hidden">lg — 1024–1280px</p>
<p class="mt-1 hidden font-semibold text-indigo-600 xl:block">xl и выше — от 1280px</p>
</div>
Диапазоны и «только до»
Иногда правило нужно ограничить сверху. Для этого есть приставка
max-* и их сочетания:
| Запись | Смысл |
|---|---|
md:flex | от 768px и выше |
max-md:hidden | до 768px (не включая) |
sm:max-lg:grid-cols-2 | от 640 до 1024 |
min-[900px]:block | от произвольной ширины |
max-[420px]:text-xs | до произвольной ширины |
max-sm: подсвечиваюсь только на узком экране
sm:max-lg: подсвечиваюсь только между 640 и 1024
<div class="space-y-2 text-sm">
<p class="rounded bg-slate-100 px-3 py-2 text-slate-700 max-sm:bg-rose-100 max-sm:text-rose-900">max-sm: подсвечиваюсь только на узком экране</p>
<p class="rounded bg-slate-100 px-3 py-2 text-slate-700 sm:max-lg:bg-emerald-100 sm:max-lg:text-emerald-900">sm:max-lg: подсвечиваюсь только между 640 и 1024</p>
</div>
Container queries: реакция на родителя
Медиазапрос знает только ширину окна. Но карточка может стоять и в широкой колонке, и в узком сайдбаре — а выглядеть должна по-разному. Именно это решают container queries.
Схема простая: родителю — @container, детям — приставки
@sm:, @md:, @lg:, которые смотрят на
ширину этого родителя.
<div class="@container">
<div class="flex flex-col @md:flex-row @md:items-center">
…
</div>
</div>
Широкий контейнер
Карточка
В широком контейнере — в строку.
Узкий контейнер
Карточка
В узком — в столбик.
<div class="grid gap-4 lg:grid-cols-[1fr_260px]">
<div class="@container rounded-lg border border-slate-200 bg-white p-3">
<p class="mb-2 text-xs text-slate-500">Широкий контейнер</p>
<div class="flex flex-col gap-3 @md:flex-row @md:items-center">
<div class="size-12 shrink-0 rounded-lg bg-sky-200"></div>
<div>
<p class="text-sm font-semibold text-slate-900">Карточка</p>
<p class="text-xs text-slate-600">В широком контейнере — в строку.</p>
</div>
</div>
</div>
<div class="@container rounded-lg border border-slate-200 bg-white p-3">
<p class="mb-2 text-xs text-slate-500">Узкий контейнер</p>
<div class="flex flex-col gap-3 @md:flex-row @md:items-center">
<div class="size-12 shrink-0 rounded-lg bg-emerald-200"></div>
<div>
<p class="text-sm font-semibold text-slate-900">Карточка</p>
<p class="text-xs text-slate-600">В узком — в столбик.</p>
</div>
</div>
</div>
</div>
Обратите внимание: один и тот же компонент с одинаковыми классами выглядит по-разному, потому что стоит в контейнерах разной ширины. С медиазапросами так не получится: они оба «видят» одно и то же окно.
@container/sidebar
у родителя и @lg/sidebar:flex у потомка. Это нужно, когда
контейнеров несколько и важно указать, на какой именно смотреть.Свои брейкпоинты
@theme {
--breakpoint-tablet: 60rem; /* появится приставка tablet: */
--breakpoint-2xl: initial; /* убрать ненужный брейкпоинт */
}
Полезно, когда дизайн-система оперирует своими размерами. Но помните: каждый новый брейкпоинт — это ещё одна комбинация, которую придётся проверять.
Что адаптируют чаще всего
| Что | Мобильный | Десктоп |
|---|---|---|
| Сетка | grid-cols-1 | lg:grid-cols-3 |
| Направление | flex-col | md:flex-row |
| Отступы | px-4 py-6 | md:px-8 md:py-12 |
| Размер заголовка | text-2xl | md:text-4xl |
| Видимость | hidden | md:block |
| Ширина сайдбара | во всю ширину | lg:w-64 |