Отказ от Tailwind: как структурировать CSS без фреймворка
Джулия Эванс восемь лет работала с Tailwind, а потом перевела несколько сайтов на ванильный CSS. Разбираем её систему: компоненты, переменные, Grid без media queries.
Восемь лет назад Джулия Эванс открыла для себя Tailwind и была в восторге. Теперь она провела неделю, переводя несколько сайтов обратно на семантический HTML и ванильный CSS — и делится практической системой из девяти составных частей: reset, компоненты, цвета, размеры шрифтов, утилиты, базовые стили, отступы, адаптивный Grid и сборка через esbuild.
Ключевые выводы
Tailwind обучает системам: reset, цветовая палитра, шкала размеров — всё это переносится в CSS custom properties без зависимостей.
Компонентный подход (один класс — один CSS-файл) резко снижает когнитивную нагрузку: 100 строк компонента — это только 100 строк.
CSS Grid с auto-fit и minmax() создаёт гибкие сетки без единого media query.
В dev-режиме сборщик не нужен: браузер понимает нативные @import и вложенные селекторы. Для production — одна команда esbuild.
Tailwind v2 без сборщика весит 2,8 МБ (270 КБ gzip) в каждом проекте — и это один из аргументов для миграции.
Зачем уходить с Tailwind
Прежде чем разбирать систему, — почему вообще стоит её строить, если Tailwind под рукой.
- Build-система стала обязательной. Tailwind v3+ требует JIT-компилятора. Без сборщика вы застряли на v2 — а это 2,8 МБ CSS (270 КБ gzip) в каждом проекте.
- Смесь стилей неудобна. Когда в одном проекте живут и ванильный CSS, и Tailwind — сопровождение становится болезненным.
- Tailwind ограничивает.
grid-template-areasтехнически работает через arbitrary values, но синтаксис настолько громоздкий, что на практике им никто не пользуется. Container queries, @layer, @scope — туда же. - Накопленный опыт. После нескольких лет работы ванильный CSS уже не пугает.
- Ценность CSS-экспертизы. Джулия упоминает эссе «Tailwind and the Femininity of CSS»: Tailwind косвенно транслирует идею, что CSS — не настоящее программирование. Она больше не хочет поддерживать этот нарратив.
При этом Tailwind честно обучал хорошим системам. Миграция — не отказ от этих систем, а их присвоение в виде нативного CSS.
Что Tailwind успел дать
Приступая к миграции, Джулия поняла: у неё уже есть многое из нужного. Любая CSS-кодовая база состоит из нескольких слоёв — раскладка, шрифты, цвета, переиспользуемые компоненты. Для каждого нужна система, иначе хаос. Tailwind эти системы давал: reset-стили, цветовую палитру, шкалу размеров шрифтов. Переход — это не изобретение колеса заново, а перенос уже понятых систем в нативный CSS.
Девять аспектов структуры CSS
Как Джулия организует свою CSS-кодовую базу сегодня.
1. Reset-стили
Простейшее решение — скопировать первые ~200 строк Tailwind Preflight из tailwind.css. За годы работы привыкаешь к box-sizing: border-box и line-height: 1.5 как к умолчаниям. Перенос preflight сохраняет привычное поведение без npm-зависимости.
2. Компоненты
Это ядро подхода. Идея та же, что в Vue или React-компонентах — но без JavaScript. Три правила:
- У каждого компонента — уникальный CSS-класс.
- CSS одного компонента никогда не перекрывает стили другого.
- Каждый компонент живёт в собственном CSS-файле.
При редактировании ста строк компонента нужно думать только о ста строках. Нативные вложенные CSS-селекторы (поддерживаются всеми браузерами с 2023 года) делают код компактным:
3. Цвета
Одно правило: все цвета сайта объявляются в colours.css через CSS custom properties — никакого хардкода в компонентах.
4. Размеры шрифтов
Вместо классов text-lg — переменные, взятые из Tailwind. Не нужно помнить, в каких единицах задавать размер (em, px или rem):
5. Утилитарные классы
Небольшой файл для вещей, которые встречаются в разных компонентах — кнопки, .sr-only для скринридеров и т.п. Держится маленьким намеренно.
6. Базовые стили
Стили, применяемые ко всему сайту. Джулия намеренно ограничивает этот файл двумя правилами — и планирует пополнять его снизу вверх, когда паттерн из компонентов повторяется несколько раз:
7. Отступы
Наименее завершённая часть системы. Основной принцип: за внешние отступы отвечает родительский контейнер, а не сами компоненты. Паттерн «owl selector» (или «lobotomized owl» — селектор вида * + *) даёт отступ между любыми соседними дочерними элементами, не трогая первый:
Дополняет это принцип «no outer margin»: компоненты не задают собственные внешние отступы — ими управляет исключительно родитель. Это предотвращает коллизии при вставке компонента в разные контексты.
8. Адаптивный дизайн: больше Grid
Вместо Tailwind-классов вида md:text-xl — гибкие сетки CSS Grid, которые сами адаптируются без брейкпоинтов. auto-fit — директива Grid, которая создаёт столько колонок, сколько умещается в контейнер (может быть 4, 3, 2 или 1 — в зависимости от ширины):
Ещё одна возможность, неудобная в Tailwind, — grid-template-areas (именованные зоны сетки): в Tailwind её можно задать через arbitrary values, но синтаксис настолько громоздкий, что в реальных проектах это нечитаемо. В ванильном CSS это просто:
9. Система сборки: esbuild
В dev-режиме сборщик не нужен совсем: современный CSS поддерживает нативные @import и вложенные селекторы в браузере без препроцессоров.
Для production-бандла Джулия использует esbuild — сборщик на Go, распространяемый как нативный бинарник (без Node.js в runtime), который работает в десятки раз быстрее webpack:
Возможности CSS, которые стоит изучить
В процессе миграции Джулия наткнулась на несколько возможностей CSS, которые пока не использовала:
- @layer — каскадные слои для управления приоритетом стилей без повышения специфичности.
- @scope — ограничение области действия CSS-правил конкретным поддеревом DOM.
- Container queries — медиазапросы относительно размера родительского контейнера, а не viewport.
- Subgrid — вложенные сетки, наследующие треки родительской Grid.
Часто задаваемые вопросы
Зачем отказываться от Tailwind, если он так популярен?
Tailwind v3+ требует build-системы, а v2 без неё весит 2,8 МБ (270 КБ gzip). Если вы накопили опыт в CSS и хотите использовать grid-template-areas, @layer или container queries без костылей — ванильный CSS с custom properties даёт ту же структуру без ограничений.
Что заменяет Tailwind reset при переходе?
Скопируйте первые ~200 строк файла tailwind.css (Preflight) напрямую в свой проект. Это те же reset-стили — box-sizing, line-height, нормализация — только теперь без npm-зависимости.
Как реализовать адаптивный дизайн без media queries?
CSS Grid с repeat(auto-fit, minmax(...)) автоматически управляет количеством колонок в зависимости от ширины контейнера. Для большинства сеток media queries не нужны. Для компонентной адаптивности — container queries.
Нужен ли сборщик при переходе на ванильный CSS?
В dev-режиме — нет. Браузер понимает нативные @import и вложенные селекторы без препроцессоров. Для production — esbuild: один статический Go-бинарник, одна команда, никакой конфигурации.
Как управлять цветами и размерами шрифтов без Tailwind-классов?
Объявите переменные (--pink, --size-lg) один раз в :root и используйте через var() в компонентах. Это те же системы, что в Tailwind — только в нативном CSS, без зависимости от фреймворка.
Итог
Главный вывод: Tailwind — это не магия, а набор систем. Reset-стили, цветовая палитра, шкала размеров шрифтов — всё это существует в нативном CSS как custom properties. Компонентный подход решает те же задачи изоляции. CSS Grid делает большинство media queries ненужными. А esbuild заменяет тяжёлый toolchain одним бинарником.
Начать можно с трёх шагов: скопировать Preflight → добавить custom properties для цветов и размеров → разбить стили по файлам-компонентам. Оригинальная статья Джулии Эванс доступна на jvns.ca.