Паттерны CSS и Tailwind: вёрстка, типографика, эффекты
Двенадцать рецептов CSS и Tailwind: плавная типографика на clamp(), адаптивные сетки без media-запросов, обрезка по строкам, скелетоны загрузки, кастомные скроллбары, градиентный текст, тёмная тема классом и переменные темы.
Большинство этих приёмов заменяют то, что раньше требовало media-запроса, обёртки или JavaScript. В этом и сквозная мысль: современный CSS вобрал в себя изрядную часть вёрстки, и оставшаяся работа — знать, какой примитив взять.
Размеры без брейкпоинтов
clamp(min, preferred, max) плавно масштабирует значение между двумя границами, убирая ступенчатость типографики на брейкпоинтах. Важная деталь — средний аргумент: чистое значение в vw выглядит правильно ровно до момента, когда кто-то включит зум или увеличит базовый размер шрифта, — после этого текст перестаёт реагировать и на то, и на другое. Смешивание составляющей в rem с составляющей в vw сохраняет привязку и к настройкам пользователя, и к ширине вьюпорта — поэтому шкала здесь записана как rem + vw, а не как один vw.
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)) делает то же самое для вёрстки. Сетка перестраивается сама на той ширине, которая нужна содержимому: ни брейкпоинтов для поддержки, ни чисел, которые придётся править при смене дизайна карточки.
У обрезки текста два разных ответа
Однострочная обрезка — это text-overflow: ellipsis вместе с white-space: nowrap и overflow: hidden; нужны все три, иначе не работает ничего. Многострочная — это -webkit-line-clamp, который вопреки префиксу давно специфицирован и поддерживается актуальными браузерами. Это не варианты одного приёма: однострочный не умеет обрезать по трём строкам, а clamp вообще не работает без display: -webkit-box.
Скелетоны и скроллбары — про воспринимаемую скорость
Скелетон ничего не ускоряет, он делает ожидание понятным: вёрстка перестаёт прыгать при появлении контента, потому что место уже зарезервировано. Стоит сочетать с prefers-reduced-motion — мерцающая анимация ровно то, что эта настройка призвана гасить.
У кастомных скроллбаров теперь два синтаксиса: старые псевдоэлементы ::-webkit-scrollbar и стандартные свойства scrollbar-width / scrollbar-color. Писать оба — не избыточность: покрытие браузеров различается, а будущее у стандартных.
Tailwind: переменные темы и стратегия тёмного режима
Стратегия тёмной темы через класс означает, что всем управляет класс .dark на <html>, — именно это делает возможным ручной переключатель: стратегия через media-запрос следует за системой и не может быть переопределена пользователем. В связке с CSS-переменными для палитры обе темы остаются в одном месте, вместо того чтобы рассыпаться вариантами dark: по всем компонентам.
Центрирование чего угодно через flexbox
Переиспользуемый класс, который идеально центрирует единственного потомка по горизонтали и вертикали.
Как это работает: display: flex вместе с align-items: center и justify-content: center центрирует flex-элемент по обеим осям независимо от его размера.
.center {
display: flex;
align-items: center;
justify-content: center;
min-height: 100%;
}
<div class="center">
<div class="card">I'm centered</div>
</div>
Кастомный scrollbar через CSS
Стилизует полосу прокрутки под дизайн-систему без JavaScript — тонкий трек, скруглённый ползунок, hover-состояние. Включает вариант для скрытия scrollbar с сохранением прокрутки.
Как это работает: ::-webkit-scrollbar-* псевдоэлементы работают в Chrome, Safari, Edge и Opera. Firefox использует стандартный scrollbar-color / scrollbar-width. Для полного покрытия нужны оба подхода.
/* Кастомный скроллбар (Chromium + Safari + Edge) */
.custom-scrollbar {
scrollbar-width: thin; /* Firefox */
scrollbar-color: #9ca3af transparent; /* Firefox: thumb track */
}
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: #9ca3af;
border-radius: 999px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: #6b7280;
}
/* Скрыть скроллбар, но оставить прокрутку */
.hide-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE / Edge legacy */
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
<!-- Example usage -->
<div class="custom-scrollbar" style="height: 300px; overflow-y: auto;">
<!-- длинный контент -->
</div>
Fluid Typography с clamp()
Масштабирует размер шрифта плавно между минимальным и максимальным значением в зависимости от ширины viewport — без media queries, только clamp().
Как это работает: clamp(min, preferred, max) зажимает значение между минимумом и максимумом. Средний параметр — линейная функция от ширины viewport (vw). Формула: preferred = minFont + (maxFont - minFont) * (vw - minVw) / (maxVw - minVw).
/* Утилиты fluid typography */
:root {
/* clamp(min-size, fluid-value, max-size) */
--text-sm: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--text-base: clamp(0.875rem, 0.8rem + 0.4vw, 1rem);
--text-lg: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--text-xl: clamp(1.125rem, 1rem + 0.6vw, 1.25rem);
--text-2xl: clamp(1.25rem, 1rem + 1.25vw, 1.5rem);
--text-3xl: clamp(1.5rem, 1rem + 2.5vw, 2.25rem);
--text-4xl: clamp(2rem, 1.5rem + 2.5vw, 3rem);
--text-5xl: clamp(2.5rem, 2rem + 2.5vw, 4rem);
}
h1 { font-size: var(--text-5xl); }
h2 { font-size: var(--text-4xl); }
h3 { font-size: var(--text-3xl); }
p { font-size: var(--text-base); }
/* Или напрямую */
.hero-title {
font-size: clamp(2rem, 5vw + 1rem, 5rem);
line-height: 1.1;
}
.body-text {
font-size: clamp(1rem, 0.5vw + 0.875rem, 1.125rem);
line-height: 1.6;
}
Градиентный текст (CSS)
Применяет линейный или радиальный градиент к тексту через background-clip: text — без SVG, без JavaScript. Поддерживается всеми современными браузерами.
Как это работает: градиент устанавливается как background на элемент, затем background-clip: text обрезает фон по контуру символов, а color: transparent делает сам текст прозрачным — сквозь него виден градиентный фон.
/* Базовый горизонтальный градиент */
.gradient-text {
background: linear-gradient(90deg, #f97316, #ec4899);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* Диагональный с тремя остановками */
.gradient-text--diagonal {
background: linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* Анимированный (двигается) */
.gradient-text--animated {
background: linear-gradient(90deg, #f97316, #ec4899, #6366f1, #f97316);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: gradient-shift 4s linear infinite;
}
@keyframes gradient-shift {
0% { background-position: 0% center; }
100% { background-position: 300% center; }
}
<!-- Example usage -->
<h1 class="gradient-text">Привет, мир</h1>
<h2 class="gradient-text--animated">Animated Gradient</h2>
CSS Grid auto-fit — адаптивная сетка без media queries
Создаёт автоматически адаптируемую сетку колонок через auto-fit и minmax — карточки сами перестраиваются при изменении ширины контейнера без единого breakpoint.
Как это работает: repeat(auto-fit, minmax(min, 1fr)) заставляет CSS Grid автоматически добавлять и убирать колонки. auto-fit схлопывает пустые треки, auto-fill оставляет их. minmax(240px, 1fr) — каждая колонка минимум 240px, максимум — равная доля свободного места.
/* Базовая адаптивная сетка */
.auto-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
}
/* Вариант с минимумом 3 колонок на больших экранах */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
gap: 1.5rem;
}
/* Фотогалерея: равные квадратные ячейки */
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
grid-auto-rows: 160px;
gap: 4px;
}
.photo-grid img {
width: 100%;
height: 100%;
object-fit: cover;
}
<!-- Example usage -->
<div class="auto-grid">
<div class="card">Карточка 1</div>
<div class="card">Карточка 2</div>
<div class="card">Карточка 3</div>
<!-- автоматически переносится на следующую строку -->
</div>
Многострочный text-overflow с ellipsis
Обрезает текст после N строк с многоточием через -webkit-line-clamp — работает во всех современных браузерах без JavaScript.
Как это работает: -webkit-line-clamp в связке с display: -webkit-box и -webkit-box-orient: vertical ограничивает блок заданным числом строк. Несмотря на префикс -webkit-, поддерживается всеми современными браузерами включая Firefox.
/* Одна строка */
.truncate-1 {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* N строк (меняй --lines) */
.line-clamp {
--lines: 3;
display: -webkit-box;
-webkit-line-clamp: var(--lines);
-webkit-box-orient: vertical;
overflow: hidden;
}
/* Готовые утилиты */
.line-clamp-2 { --lines: 2; }
.line-clamp-3 { --lines: 3; }
.line-clamp-4 { --lines: 4; }
<!-- Example usage -->
<p class="line-clamp" style="--lines: 2">
Очень длинный текст, который будет обрезан после второй строки
с многоточием в конце, без какого-либо JavaScript...
</p>
Skeleton Loading анимация (CSS)
Shimmer-эффект для скелетонов загрузки — пульсирующий градиент имитирует загрузку контента. Без JavaScript, только CSS-анимация на кастомных свойствах.
Как это работает: @keyframes shimmer двигает градиент слева направо через элемент. Использование background-size: 200% позволяет градиенту выходить за края — это создаёт эффект бегущего света. will-change: background-position помогает GPU-ускорению.
/* Базовый skeleton-блок */
.skeleton {
background: linear-gradient(
90deg,
var(--skeleton-base, #e5e7eb) 25%,
var(--skeleton-shine, #f3f4f6) 50%,
var(--skeleton-base, #e5e7eb) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
@keyframes shimmer {
0% { background-position: 200% center; }
100% { background-position: -200% center; }
}
/* Тёмная тема */
@media (prefers-color-scheme: dark) {
.skeleton {
--skeleton-base: #374151;
--skeleton-shine: #4b5563;
}
}
/* Готовые фигуры */
.skeleton-text { height: 0.875rem; border-radius: 2px; }
.skeleton-title { height: 1.25rem; border-radius: 2px; }
.skeleton-avatar { width: 40px; height: 40px; border-radius: 50%; }
.skeleton-card { height: 120px; border-radius: 8px; }
<!-- Example usage — карточка в состоянии загрузки -->
<div style="display: flex; gap: 12px; align-items: center">
<div class="skeleton skeleton-avatar"></div>
<div style="flex: 1; display: flex; flex-direction: column; gap: 8px">
<div class="skeleton skeleton-title" style="width: 60%"></div>
<div class="skeleton skeleton-text" style="width: 90%"></div>
<div class="skeleton skeleton-text" style="width: 75%"></div>
</div>
</div>
Обрезание однострочного текста с многоточием
Обрезает текст, не помещающийся в одну строку, и добавляет "…" в конце — классический способ обрезки текста только средствами CSS.
Как это работает: работает только для ОДНОЙ строки — white-space: nowrap запрещает перенос, overflow: hidden обрезает лишнее, а text-overflow: ellipsis заменяет обрезанную часть на "…". Для нескольких строк используйте line-clamp.
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
<p class="truncate" style="max-width: 200px;">
This sentence is way too long to fit on one line and will be truncated
</p>
Tailwind CSS анимации — keyframes и transition
Создание кастомных анимаций через `animate-*` и `@keyframes` в Tailwind — от микро-взаимодействий до переходов между страницами без выхода из конфига.
Как это работает: определите keyframes и animation в tailwind.config.js. Каждый ключ получает класс animate-{name}. Комбинируйте с transition-*, hover: и group-hover: для интерактивных микро-анимаций.
// tailwind.config.js
export default {
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
'scale-in': {
'0%': { transform: 'scale(0.95)', opacity: '0' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
'slide-up': {
'0%': { transform: 'translateY(100%)' },
'100%': { transform: 'translateY(0)' },
},
'pulse-soft': {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0.5' },
},
},
animation: {
'fade-in': 'fade-in 0.3s ease-out',
'scale-in': 'scale-in 0.2s ease-out',
'slide-up': 'slide-up 0.4s ease-out',
'pulse-soft': 'pulse-soft 2s ease-in-out infinite',
},
},
},
}
<!-- Использование классов анимации -->
<div class="animate-fade-in">
<h1>Появляется со слайдом</h1>
</div>
<div class="animate-scale-in">Фон модалки</div>
<!-- Ступенчатая анимация детей -->
<div class="space-y-4">
<div class="animate-fade-in" style="animation-delay: 0ms">Элемент 1</div>
<div class="animate-fade-in" style="animation-delay: 100ms">Элемент 2</div>
<div class="animate-fade-in" style="animation-delay: 200ms">Элемент 3</div>
</div>
<!-- Hover микро-взаимодействия -->
<button class="transition-all duration-200 hover:scale-105 hover:shadow-lg active:scale-95">
Наведи
</button>
<!-- Group hover раскрытие -->
<div class="group relative">
<p>Наведите сюда</p>
<div class="opacity-0 group-hover:opacity-100 transition-opacity duration-200">
Скрытый контент
</div>
</div>
Tailwind CSS кастомная тема через CSS переменные
Расширение стандартной темы Tailwind кастомными цветами, шрифтами, отступами и брейкпоинтами — все значения через CSS-переменные для смены темы в рантайме.
Как это работает: определите CSS-переменные в @layer base и укажите их в tailwind.config.js через theme.extend. Это держит токены дизайна в одном месте и позволяет менять тему в рантайме (просто переопределите значения переменных).
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--color-primary: #2563eb;
--color-primary-light: #60a5fa;
--color-secondary: #7c3aed;
--color-accent: #f59e0b;
--color-surface: #ffffff;
--color-surface-muted: #f8fafc;
--color-border: #e2e8f0;
--font-sans: 'Inter', system-ui, sans-serif;
--font-display: 'Playfair Display', serif;
--spacing-page: 2rem;
}
.dark {
--color-primary: #60a5fa;
--color-primary-light: #93c5fd;
--color-surface: #0f172a;
--color-surface-muted: #1e293b;
--color-border: #334155;
}
}
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
primary: {
DEFAULT: 'var(--color-primary)',
light: 'var(--color-primary-light)',
},
secondary: 'var(--color-secondary)',
accent: 'var(--color-accent)',
surface: {
DEFAULT: 'var(--color-surface)',
muted: 'var(--color-surface-muted)',
},
},
fontFamily: {
sans: ['var(--font-sans)'],
display: ['var(--font-display)'],
},
spacing: {
page: 'var(--spacing-page)',
},
},
},
}
<!-- Использование в шаблонах -->
<div class="bg-surface text-primary font-sans">
<h1 class="font-display text-3xl">Кастомная тема</h1>
</div>
Tailwind CSS dark mode со стратегией class
Включение тёмной темы по кнопке вместо системных настроек — стратегия class даёт полный контроль через переключатель с сохранением в localStorage.
Как это работает: установите darkMode: "class" в tailwind.config.js и переключайте класс dark на <html>. Все варианты dark: активируются, когда родитель имеет этот класс. Это даёт полный контроль — идеально для переключателя темы.
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class',
content: ['./src/**/*.{js,ts,jsx,tsx}'],
}
<!-- Классы тёмной темы -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<h1 class="text-gray-900 dark:text-white">Заголовок</h1>
<p class="text-gray-600 dark:text-gray-400">Контент с адаптивными цветами</p>
<button class="bg-blue-600 dark:bg-blue-500 text-white px-4 py-2 rounded-lg
hover:bg-blue-700 dark:hover:bg-blue-400 transition-colors">
Отправить
</button>
</div>
<!-- Кнопка переключения -->
<button onclick="toggleDarkMode()">
<span class="dark:hidden">☀️ Светлая</span>
<span class="hidden dark:inline">🌙 Тёмная</span>
</button>
// Переключение темы с сохранением в localStorage
function toggleDarkMode() {
const html = document.documentElement
const isDark = html.classList.toggle('dark')
localStorage.setItem('theme', isDark ? 'dark' : 'light')
}
// При загрузке страницы
const saved = localStorage.getItem('theme')
if (saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
}
Tailwind CSS адаптивная сетка карточек
Адаптивная сетка карточек на Tailwind CSS — от 1 колонки на мобильных до 4 на десктопе — без медиа-запросов.
Как это работает: responsive grid-утилиты Tailwind (grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4) декларативно задают брейкпоинты. Карточки используют gap для отступов, aspect-video для единообразных медиа и truncate для переполнения текста.
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 p-6">
<div v-for="item in items" :key="item.id"
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden hover:shadow-md transition-shadow">
<div class="aspect-video bg-gray-100 dark:bg-gray-700">
<img :src="item.image" :alt="item.title" class="w-full h-full object-cover">
</div>
<div class="p-4 space-y-2">
<span class="text-xs font-medium text-blue-600 dark:text-blue-400 uppercase tracking-wide">
{{ item.category }}
</span>
<h3 class="font-semibold text-gray-900 dark:text-white truncate">
{{ item.title }}
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
{{ item.description }}
</p>
</div>
</div>
</div>
/* Опционально: утилита line-clamp если Tailwind v3 */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
# В Tailwind v4 line-clamp через @utility или
# используйте нативный CSS выше
Другие подборки сниппетов
Контактная информация
Оставьте заявку или напишите напрямую в телеграм. Я отвечу в течение 24 часов.