Tailwind CSS — утилитарная стилизация сайтов
Tailwind CSS — это utility-first фреймворк: вместо готовых компонентов ты собираешь стили из маленьких классов-утилит прямо в разметке. В этом уроке ты научишься подключать Tailwind, настраивать его, строить адаптивные сетки и подключать тёмную тему — без единой строчки обычного CSS.
1. Что такое Tailwind CSS и utility-first
Классический CSS ты пишешь в отдельном файле и придумываешь имена классов. Tailwind работает иначе: в браузер попадает готовый набор мелких утилит — p-4, flex, text-center, — и ты просто комбинируешь их в HTML. Никто не мешает обоим подходам сосуществовать, но Tailwind меняет то, как ты мыслишь о вёрстке.
<!-- Обычный CSS -->
<button class="btn-primary">Войти</button>
/* в файле style.css */
.btn-primary {
background: #2563eb;
padding: 10px 20px;
border-radius: 8px;
color: #fff;
}
<!-- Tailwind CSS -->
<button class="bg-blue-600 px-5 py-2.5 rounded-lg text-white">Войти</button>2. Установка: CDN и npm
Для экспериментов проще всего подключить Play CDN одним тегом script. Так можно проверять утилиты в браузере без сборки.
<head>
<!-- Play CDN: только для прототипов, не для продакшена -->
<script src="https://cdn.tailwindcss.com"></script>
</head>Для настоящего проекта Tailwind ставят через npm и подключают к сборщику. Например, с Vite:
npm install tailwindcss @tailwindcss/vite
// Vite: в конфиге (vite.config.js)
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()]
})3. Настройка через tailwind.config
В Tailwind 4 конфигурация задаётся CSS-директивой @theme, а в версии 3 — файлом tailwind.config.js. Секция content обязательно должна перечислять файлы, в которых Tailwind ищет классы.
// tailwind.config.js (Tailwind 3)
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,vue}'],
theme: {
extend: {
colors: {
brand: {
light: '#60a5fa',
DEFAULT: '#2563eb',
dark: '#1e40af'
}
},
fontFamily: {
sans: ['Inter', 'sans-serif']
}
}
},
darkMode: 'class'
}content, Tailwind просто не увидит твои классы, и кнопки останутся без стилей. Частая ошибка новичков — пропустить новую папку из сканирования.4. Базовые утилиты: цвет и типографика
Около 90% повседневной вёрстки — это несколько десятков утилит для текста и фона. Значения шагов (50, 100, ..., 900) задают оттенок цвета: чем больше число, тем темнее.
<p class="text-blue-700 font-semibold text-lg leading-snug tracking-tight">
Привет, мир!
</p>
<div class="bg-gray-100 text-gray-900 rounded-md">Карточка</div>
<span class="bg-emerald-100 text-emerald-800 px-2 py-0.5 rounded-full text-sm">
В наличии
</span>| Категория | Примеры классов | Что делает |
|---|---|---|
| цвет текста | text-blue-600, text-gray-500 | задаёт цвет шрифта |
| цвет фона | bg-blue-600, bg-white | задаёт фон |
| размер шрифта | text-sm, text-lg, text-4xl | управляет font-size |
| жирность | font-normal, font-bold | управляет font-weight |
| межстрочный | leading-5, leading-relaxed | управляет line-height |
| регистр | uppercase, lowercase | преобразует текст |
text-emerald-800 bg-emerald-100.5. Отступы и пространство
За отступы отвечают три семейства: p-* (внутренние), m-* (внешние) и gap-* (между элементами flex/grid). Шкала продублирована по сторонам: px-4, py-4, ml-auto.
<!-- padding со всех сторон, по горизонтали/вертикали -->
<div class="p-6 px-4 py-8">...</div>
<!-- margin для разнесения блоков по краям -->
<nav class="flex justify-between items-center px-6">
<a href="#">Логотип</a>
<div class="flex gap-3">
<a href="#" class="ml-auto">Каталог</a>
<a href="#">Контакты</a>
</div>
</nav>
<!-- центрирование блока фиксированной ширины -->
<div class="max-w-3xl mx-auto">Контент по центру</div>space-x-4 добавляют отступ между соседними детьми контейнера автоматически — удобно для списков ссылок без ручных ml-* на каждом пункте.6. Flexbox и Grid на утилитах
Flex и Grid включаются двумя классами — flex и grid. Всё остальное — это их свойства в виде утилит: направление, выравнивание, количество колонок.
<!-- ряд кнопок справа -->
<div class="flex gap-2 justify-end items-center">
<button class="px-4 py-2 rounded bg-gray-200">Отмена</button>
<button class="px-4 py-2 rounded bg-blue-600 text-white">Сохранить</button>
</div>
<!-- сетка из трёх одинаковых карточек -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white shadow rounded-lg p-6">Карточка 1</div>
<div class="bg-white shadow rounded-lg p-6">Карточка 2</div>
<div class="bg-white shadow rounded-lg p-6">Карточка 3</div>
</div>| Задача | Классы |
|---|---|
| гибкий ряд | flex, flex-col, flex-wrap |
| выравнивание по главной оси | justify-start / center / between / end |
| выравнивание по поперечной оси | items-start / center / stretch |
| колонки сетки | grid-cols-2, grid-cols-12 |
| занять несколько колонок | col-span-2, col-span-4 |
| расстояние между элементами | gap-4, gap-x-4, gap-y-6 |
grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3. На телефоне — одна колонка, на планшете — две, на десктопе — три. Это и есть адаптивность без медиазапросов.7. Адаптивность через префиксы
Tailwind не заставляет писать медиазапросы: достаточно добавить префикс брейкпоинта. Утилита md: применится только на экранах от 768px и шире, а lg: — от 1024px.
<header class="flex flex-col md:flex-row justify-between p-6">
<h1 class="text-2xl md:text-4xl">Заголовок сайта</h1>
<nav class="flex flex-col md:flex-row gap-2 md:gap-6">
<a href="#">Главная</a>
<a href="#">Блог</a>
<a href="#">Контакты</a>
</nav>
</header>| Префикс | Мин. ширина экрана | Пример |
|---|---|---|
| sm: | 640px | sm:grid-cols-2 |
| md: | 768px | md:flex-row |
| lg: | 1024px | lg:grid-cols-3 |
| xl: | 1280px | xl:max-w-5xl |
| 2xl: | 1536px | 2xl:gap-10 |
8. Тёмная тема через dark:
Для тёмной темы в Tailwind есть встроенный вариант dark:. По умолчанию он реагирует на системную настройку браузера (media-запрос), а через darkMode: 'class' — на класс dark у корневого элемента. С классом легко сделать переключатель темы на сайте.
// tailwind.config.js
module.exports = {
darkMode: 'class'
}
// script.js — переключение темы
document.documentElement.classList.toggle('dark')<body class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
<div class="max-w-xl mx-auto p-6 rounded-xl
bg-gray-50 dark:bg-gray-800">
<h1 class="text-xl font-bold">Карточка</h1>
<p class="text-gray-600 dark:text-gray-300">
Текст спокойно меняет цвет при переключении темы.
</p>
</div>
</body>9. Состояния hover, focus и прочие варианты
Псевдоклассы включаются своими префиксами. Их можно комбинировать с брейкпоинтами и тёмной темой: md:hover:bg-blue-700 — «при наведении на экранах от 768px».
<button class="bg-blue-600 hover:bg-blue-700 active:bg-blue-800
focus-visible:outline focus-visible:outline-2
focus-visible:outline-blue-400 transition-colors
px-5 py-2.5 rounded-lg text-white">
Нажми меня
</button>
<a href="#" class="text-blue-600 hover:text-blue-800 visited:text-purple-600">
Ссылка
</a>| Префикс | Когда срабатывает |
|---|---|
| hover: | при наведении курсора |
| focus: | при фокусе через мышь или клавиатуру |
| focus-visible: | фокус с клавиатуры (для доступности) |
| active: | в момент клика |
| first: | первый из однотипных элементов |
| checked: | выбранный чекбокс или радио |
| group-hover: | при наведении на родителя с классом group |
group на родителе и group-hover:opacity-100 opacity-0 на скрытом элементе — без единой строчки JS.10. @apply, кастомные утилиты и интеграция с React/Vue
Когда одни и те же связки классов встречаются по сто раз, хочется дать им имя. Директива @apply собирает утилиты в обычный класс в твоём CSS — это ещё и способ постепенно переезжать на Tailwind.
/* input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
.btn-primary {
@apply bg-blue-600 hover:bg-blue-700 text-white
font-medium px-5 py-2.5 rounded-lg transition-colors;
}В React и Vue тот же подход: утилиты передаются как строки классов, а условные модификаторы получаются из шаблонного синтаксиса.
// React
export default function Button({ primary, children }) {
const cls = primary
? 'bg-blue-600 hover:bg-blue-700 text-white'
: 'bg-gray-200 hover:bg-gray-300 text-gray-900'
return (
<button className={`px-5 py-2.5 rounded-lg transition-colors ${cls}`}>
{children}
</button>
)
}
// Vue
<template>
<button :class="[base, primary ? 'bg-blue-600' : 'bg-gray-200']">
<slot />
</button>
</template>@apply: смысл Tailwind в том, чтобы классы были маленькими и предсказуемыми. Компонент с десятками вложенных @apply превращается в обычный CSS-монолит, но с худшей читаемостью.11. Плагины и кастомные варианты
Экосистема Tailwind расширяется плагинами: typography даёт красивые стили для статей, forms — аккуратные input и select. Установка плагина — пара команд и одна строка в конфиге.
// tailwind.config.js
import typography from '@tailwindcss/typography'
import forms from '@tailwindcss/forms'
export default {
plugins: [typography, forms],
theme: {
extend: {
colors: {
brand: '#ff6b35'
}
}
}
}Свои утилиты добавляются функцией plugin(function ({ addUtilities }) { ... }) — это удобно для повторяющихся теней или градиентов.
Варианты вроде group-hover и peer-checked тоже расширяются через плагины: они дают состояния без JavaScript. Логика «наведения на родителя» перестаёт требовать отдельного скрипта — меньше строк, меньше шансов на ошибку.
<article class="prose prose-lg">
<h1>Готовый стиль статьи</h1>
</article>12. Производительность сборки
Tailwind v4 не генерирует «весь CSS». В файл попадают только классы, встреченные в файлах, которые указаны в настройках. Чем точнее пути content — тем меньше итоговый бандл.
// tailwind.config.js — какие файлы сканировать
export default {
content: [
'./index.html',
'./src/**/*.{vue,js,ts,jsx,tsx}'
]
}Если класс встречается только в сгенерированном динамически виде (например, bg-${color}), Tailwind его не найдёт. Выход — safelist или фиксированные варианты в разметке.
Для крупных проектов есть интеграция с бандлерами: PostCSS-плагин подключается к Vite и Webpack одной строкой, а минификация убирает лишнее. Конфигурацию тем держите минимальной: неиспользуемые цвета и размеры не генерируются.
bg-${color} не сработает, так как сборщик не подставляет выражения. Перечисли варианты целиком.13. Типовые макеты на практике
Соберём каркас страницы: шапка, основная сетка карточек и футер — только утилитами, без единой строчки «ручного» CSS.
<header class="flex items-center justify-between px-6 py-4 border-b">
<a href="/" class="text-lg font-bold">Logo</a>
<nav class="flex gap-6 text-sm text-gray-600">
<a href="/"><a>Главная</a>
</nav>
</header>
<main class="grid gap-6 p-6 sm:grid-cols-2 lg:grid-cols-3">
<article class="rounded-xl border p-4 shadow-sm">
<h2 class="text-base font-semibold">Карточка</h2>
<p class="mt-2 text-sm text-gray-500">Описание товара</p>
</article>
</main>Такой подход держит вёрстку единообразной: одинаковые отступы, шрифты и сетки без дублирования. При этом каждый блок легко вывернуть через БЭМ-класс при необходимости.
Для реальных проектов этого каркаса достаточно: шапку можно вынести в компонент, карточки — переиспользовать, а адаптив менять одной-двумя утилитами. Когда макет вырастет, добавьте модалки и навигацию сверху сетки — Tailwind не ограничит в масштабе.
grid определяет ряды, flex — то, что внутри ряда. Смешивать их в одном контейнере не стоит.14. Tailwind вместе с Sass или PostCSS
Tailwind не отменяет препроцессоры: во многих проектах он работает «поверх». Sass даёт вложенность и миксины для повторяющихся кусков, а Tailwind — точечные утилиты для внешнего вида.
// app.scss — порядок подключения важен
@use "tailwindcss/base";
@use "tailwindcss/components";
@use "tailwindcss/utilities";
.card {
background: var(--brand); // CSS-переменная из @theme
&:hover { transform: translateY(-2px); }
}Подключай Tailwind-директивы до своих стилей, чтобы каскад не перекрывал утилиты. Миксины Sass удобны для «составных» компонентов, которые повторяются с параметрами.
@theme и используй в произвольном CSS без дублирования.@apply внутрь глубоко вложенных селекторов Sass без необходимости: вложенность усложняет чтение и тормозит сборку на больших проектах.15. Адаптивность и доступность на утилитах
Адаптивность в Tailwind — это префиксы брейкпоинтов, но есть тонкости, которые пропускают: не только ширина, но и область касания, размер шрифта и порядок контента.
- интерактивные элементы не меньше 44 px — руководство тач-точек,
min-h-11на кнопках; - фокус для клавиатуры:
focus-visible:outline focus-visible:outline-2; - тёмная тема вместе с
prefers-reduced-motion:motion-safe:transition-colors; - порядок в DOM не равен визуальному: на мобильных переставляй элементы flex/grid, не трогая семантику.
<!-- Кнопка с доступностью из коробки -->
<button class="min-h-11 px-4 rounded-lg
focus-visible:outline-2 outline-blue-600
motion-safe:transition-colors">
Купить
</button>Проверяй адаптив не только в DevTools, но и на реальных устройствах: браузер эмулирует ширину, но не тач-зоны и реальный рендеринг.
text-gray-400 на белом неприемлем для основного текста. Проверяй контраст в Lighthouse.16. Шрифты и иконки рядом с Tailwind
Типографика и иконки — половина «дорогого вида». Tailwind не включает шрифты и иконки, но отлично с ними дружит через переменные и CSS-классы.
// tailwind.config.js
export default {
theme: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'ui-monospace']
}
}
}Шрифты подключай локально (fontsource) или самодостаточным @font-face: внешние CDN потянут иконки и задержат первую отрисовку текста. Иконки — инлайновые SVG-компоненты, а не картинки: их можно красить через fill-current и масштабировать w-* h-*.
// React-компонент иконки
export const IconStar = ({ className }) => (
<svg className={`w-5 h-5 fill-current ${className}`} viewBox="0 0 24 24">
<path d="M12 2l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z" />
</svg>
)Иконка наследует цвет текста через currentColor и подстраивается под тёмную тему без дублирования кода.
aria-hidden="true" декоративным иконкам — скринридеры не должны озвучивать «звезду» посреди кнопки.17. Частые ошибки верстальщика на Tailwind
Tailwind упрощает вёрстку, но даёт новичкам набор типовых ловушек. Пройдись по списку перед сдачей макета.
- Динамический класс:
bg-${c}не найдётся сборщиком — перечисли варианты целиком; - Конфликт утилит: два
p-*на одном элементе — победит порядок в CSS, а не «последний в списке классов»; - Сломанный адаптив: без мобильного состояния контент «ломается» на узких экранах;
- Забытый flex-wrap: без него ряд вылезает за пределы блока.
// Ошибка: динамический класс не сгенерируется
<div className={`bg-${status}-500`}>...</div>
// Выход: явный словарь
const colors = { ok: 'bg-green-500', error: 'bg-red-500' }
<div className={colors[status]}>...</div>Если класс не применился — ищи «свой» конфликт в DevTools: посмотри computed styles и порядок правила. Чаще всего это дубликат утилиты или пропущенный путь в content.
18. Tailwind v4: CSS-first конфигурация
В v4 конфигурация уезжает из tailwind.config.js в CSS: кастомные токены объявляются директивами @theme, и из них автоматически рождаются утилиты. Меньше JS-обработки, быстрее сборка, а дизайн-система читается прямо в стилях.
@import "tailwindcss";
@theme {
--color-brand: #7c3aed;
--font-display: "Inter", sans-serif;
}Переменная --color-brand создаст классы bg-brand, text-brand, border-brand — так токены связываются с утилитами без ручного расширения конфига.
@sourceуказывает, где искать классы, заменяя массивcontent;- вложенные темы создаются через
@theme inlineи блоки@variant; - миграция с v3 почти автоматическая: есть утилита
npx @tailwindcss/upgrade.
19. UI-кит: Storybook и документация компонентов
Утилиты — быстрые, но «сырые»: в команде их оборачивают в компоненты (кнопки, карточки, поля), а для разработки и ревью заводится витрина — Storybook. Каждый компонент получает «историю» с разными состояниями.
// Button.stories.tsx
export const Primary = {
args: {
label: "Купить",
variant: "primary",
},
};- история = снимок компонента с props, который можно кликать и смотреть;
- controls-панель меняет аргументы вживую — быстрое тестирование без сборки;
- addon-a11y проверяет контраст и семантику прямо в Storybook.
Результат: дизайнер проверяет «живые» компоненты, разработчик видит все состояния, а бота/скриншот-тесты (chromatic) ловят регрессии после правки токенов.
20. Дизайн-токены: подключение бренда
Дизайн-токены — именованные атомы стиля: цвет, типографика, отступы, радиусы. В Tailwind v4 они превращаются в CSS-переменные (раздел 18), и все утилиты начинают говорить на языке бренда, а не на «дефолтном сером».
@theme {
--color-primary: #0f766e;
--color-accent: #f59e0b;
--spacing-section: 6rem;
--radius-card: 1rem;
}- палитра + семантика ролей: primary, neutral, danger отдельно от «сырых» цветов;
- шкалы отступов и радиусов — модульные, а не «придуманные под кнопку»;
- значения токенов обновляются в одном месте — светятся по всему проекту.
Связь с дизайнерами: спросите у них токены (Figma Variables) и перенесите в @theme построчно — расхождений между макетом и кодом станет заметно меньше.
21. Tailwind против BEM, CSS Modules и CSS-in-JS
Tailwind — не единственный подход к стилям, и вопрос «что выбрать» решается контекстом. Сравнение с ближайшими конкурентами помогает принимать это решение осознанно.
| Подход | Сильная сторона | Цена |
|---|---|---|
| Tailwind (utility-first) | скорость, консистентность, средний опыт команды | длинная разметка классов |
| BEM | семантика «блок-элемент-модификатор», старый код | много ручного именования |
| CSS Modules | изоляция стилей и scoped-классы | меньше переиспользования тем |
| CSS-in-JS (styled) | типы и логика рядом с стилями | runtime-затраты, бандл |
Правило выбора: для прототипов и продуктовых команд Tailwind даёт самый короткий путь; для дизайн-систем с жёсткой разметкой может подойти BEM, а для микрофронтендов — CSS Modules из-за изоляции.
Что дальше
Ты прошёл весь путь: от понимания utility-first и установки до адаптивных сеток, тёмной темы и интеграции с фреймворками. Закрепи навык, сверстав карточку товара с сеткой карточек и переключателем темы на чистом Tailwind. Отличным продолжением будет изучение Bootstrap для сравнения подходов, а также курс по Vue 3 или React, чтобы соединить Tailwind с реальным интерфейсом.