Tailwind CSSMiddleCoding
Как использовать Tailwind CSS с компонентным фреймворком, например React или Vue?
Tailwind отлично работает с React/Vue: классы добавляются через className/class атрибуты. Для переиспользования стилей используйте компоненты фреймворка, @apply в CSS, или утилиту clsx/cva для условных классов.
Tailwind CSS с React и Vue
Tailwind идеально сочетается с компонентными фреймворками: стили инкапсулируются в компонентах вместо CSS-файлов, а JIT-режим отслеживает классы в JSX/Vue-шаблонах.
Установка в Vite-проект (React)
// 1. Установка (через контейнер или проектный пакетный менеджер)
// npm install -D tailwindcss @tailwindcss/vite
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite'; // v4
export default defineConfig({
plugins: [react(), tailwindcss()],
});
/* src/index.css */
@import "tailwindcss";
Базовое использование в React
// Button.tsx
interface ButtonProps {
variant?: 'primary' | 'secondary';
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
}
export function Button({ variant = 'primary', children, onClick, disabled }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`
px-4 py-2 rounded-lg font-semibold transition-colors
focus:outline-none focus:ring-2 focus:ring-offset-2
disabled:opacity-50 disabled:cursor-not-allowed
${variant === 'primary'
? 'bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500'
: 'bg-gray-100 hover:bg-gray-200 text-gray-900 focus:ring-gray-400'
}
`}
>
{children}
</button>
);
}
clsx + tailwind-merge для условных классов
// npm install clsx tailwind-merge
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
// Вспомогательная функция (cn)
function cn(...inputs: (string | undefined | null | false)[]) {
return twMerge(clsx(inputs));
}
// Использование
function Card({ className, highlighted }: { className?: string; highlighted?: boolean }) {
return (
<div
className={cn(
'rounded-xl border bg-white p-6 shadow-sm',
highlighted && 'border-blue-500 shadow-blue-100',
className // позволяет переопределить снаружи
)}
/>
);
}
cva (Class Variance Authority) для вариантов компонентов
// npm install class-variance-authority
import { cva, type VariantProps } from 'class-variance-authority';
const button = cva(
// Базовые классы
'inline-flex items-center justify-center rounded-lg font-semibold transition-colors focus:outline-none',
{
variants: {
variant: {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
danger: 'bg-red-600 text-white hover:bg-red-700',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>
& VariantProps<typeof button>;
export function Button({ variant, size, className, ...props }: ButtonProps) {
return <button className={button({ variant, size, className })} {...props} />;
}
// Использование
<Button variant="danger" size="lg">Удалить</Button>
Vue 3 SFC
// Card.vue
<template>
<div
:class="[
'rounded-xl border p-6 transition-shadow',
highlighted ? 'border-blue-500 shadow-lg' : 'border-gray-200 shadow-sm'
]"
>
<slot />
</div>
</template>
<script setup lang="ts">
defineProps<{ highlighted?: boolean }>();
</script>
@apply для переиспользуемых паттернов
/* styles/components.css */
@layer components {
.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded-lg
hover:bg-blue-700 transition-colors
focus:outline-none focus:ring-2 focus:ring-blue-500;
}
}
Подводные камни
- Динамически составленные имена классов через интерполяцию строк (
`bg-${color}-500`) не попадают в JIT-сборку — всегда используйте полные имена классов. - Без
tailwind-mergeпереопределение классов работает непредсказуемо:p-4 p-2— выиграет тот, кто стоит позже в CSS, а не в строке className. - Vue: динамические классы через
:classработают, но Tailwind IntelliSense не подсвечивает их — убедитесь, что полные имена присутствуют в коде. - Длинные строки className в JSX ухудшают читаемость — используйте
prettier-plugin-tailwindcssдля автоматической сортировки классов. - При использовании CSS Modules вместе с Tailwind возникает конфликт скоупов — решайте через явный импорт в
@layer. - Server Components в Next.js не поддерживают контекст и хуки — не передавайте className через контекст для серверных компонентов.
- Избыток
@applyвоспроизводит проблемы обычного CSS (специфичность, порядок) — применяйте только для действительно часто используемых паттернов.
Common mistakes
- Смешивать «Tailwind с React или Vue» с похожим механизмом без критерия выбора.
- Игнорировать риск: неверно оценить границы применения темы «Tailwind с React или Vue» и получить хрупкое решение.
- Показывать только синтаксис и не объяснять поведение в runtime или сборке.
What the interviewer is testing
- Объясняет композиция utilities с компонентами и пропсами без динамических class fragments.
- Показывает на примере, как работает: в компонентных фреймворках нужно маппить props на полные строки классов, чтобы Tailwind смог обнаружить все варианты на build time.
- Называет production-нюанс и граничный случай для темы «Tailwind с React или Vue».