Вёрстка сайта с использованием Tailwind CSS
Tailwind CSS — utility-first фреймворк с генерацией только используемых классов. В production bundle — только те классы, которые реально присутствуют в JSX/HTML. Среднестатистический проект: 8–15 KB CSS после gzip. Это не фреймворк компонентов — это инструмент быстрого прототипирования и масштабируемой вёрстки без переключения контекста между файлами.
Установка и настройка (Tailwind v4)
Tailwind v4 переходит на CSS-first конфигурацию:
npm install tailwindcss @tailwindcss/vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
});
/* src/styles/globals.css */
@import "tailwindcss";
/* Кастомизация через CSS-переменные (v4) */
@theme {
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--font-sans: 'Inter', ui-sans-serif, system-ui;
--font-mono: 'JetBrains Mono', ui-monospace;
--radius-card: 0.75rem;
--breakpoint-xs: 375px;
}
Конфигурация для v3 (если требуется)
// tailwind.config.ts
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{tsx,ts,jsx,js}', './index.html'],
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
},
fontFamily: {
sans: ['Inter', 'ui-sans-serif', 'system-ui'],
},
borderRadius: {
card: '0.75rem',
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
],
} satisfies Config;
Компоненты: clsx + tailwind-merge
Без tailwind-merge при слиянии классов побеждает не последний класс, а порядок в CSS-файле. twMerge решает это:
npm install clsx tailwind-merge
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// components/Button/Button.tsx
import { ButtonHTMLAttributes, FC } from 'react';
import { cn } from '@/lib/utils';
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
// Базовые классы
'inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-brand-600 text-white hover:bg-brand-700',
ghost: 'bg-transparent text-brand-600 border border-brand-600 hover:bg-brand-50',
destructive: 'bg-red-600 text-white hover:bg-red-700',
link: 'text-brand-600 underline-offset-4 hover:underline',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'md',
},
}
);
interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
const Button: FC<ButtonProps> = ({ className, variant, size, ...props }) => (
<button className={cn(buttonVariants({ variant, size }), className)} {...props} />
);
Типовые паттерны вёрстки
Адаптивная сетка карточек
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{items.map((item) => (
<article
key={item.id}
className="flex flex-col rounded-card bg-white p-5 shadow-sm ring-1 ring-black/5 transition-shadow hover:shadow-md"
>
<img
src={item.image}
alt={item.title}
className="mb-4 h-48 w-full rounded-md object-cover"
/>
<h3 className="mb-2 text-lg font-semibold text-gray-900">{item.title}</h3>
<p className="flex-1 text-sm text-gray-500">{item.description}</p>
<Button className="mt-4" size="sm">Подробнее</Button>
</article>
))}
</div>
Навигация с мобильным меню
<header className="sticky top-0 z-50 border-b border-gray-100 bg-white/80 backdrop-blur-md">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 lg:px-8">
<Logo className="h-8" />
<nav className="hidden items-center gap-6 lg:flex">
{navLinks.map((link) => (
<a
key={link.href}
href={link.href}
className="text-sm font-medium text-gray-600 transition-colors hover:text-gray-900"
>
{link.label}
</a>
))}
</nav>
<Button className="hidden lg:inline-flex">Связаться</Button>
<MobileMenuButton className="lg:hidden" />
</div>
</header>
Tailwind Typography для контента
<article className="prose prose-gray max-w-none prose-headings:font-semibold prose-a:text-brand-600 prose-a:no-underline hover:prose-a:underline prose-img:rounded-lg lg:prose-lg">
{/* Markdown/HTML контент */}
</article>
Dark mode
/* globals.css */
@import "tailwindcss";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
// Или через media query (v3: darkMode: 'class')
<div className="bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-50">
Arbitrary values для нестандартных случаев
<div className="top-[72px] h-[calc(100svh-72px)] grid-cols-[280px_1fr] bg-[#1a1a2e]">
Arbitrary values — escape hatch для значений вне design system, не злоупотреблять.
Сроки
Настройка Tailwind + конфигурация токенов: 2–3 часа. Скорость вёрстки страниц с Tailwind примерно в 1.5–2 раза быстрее классического CSS за счёт устранения переключения файлов. Посадочная страница со всеми секциями: 1–2 дня. Полный многостраничный сайт: 3–7 дней.







