TailwindCSS 🎨
TailwindCSS je utility-first CSS framework, který umožňuje rychlé stylování pomocí předdefinovaných tříd. Je ideální (nejen) pro React aplikace díky své flexibilitě a rychlosti vývoje.
Pokud se nechcete patlat v CSS, jako já, tak TailwindCSS je pro vás skvělý nástroj 😊.
🎯 Co je TailwindCSS?
TailwindCSS je utility-first CSS framework, který poskytuje tisíce předdefinovaných CSS tříd pro rychlé stylování bez psaní vlastního CSS.
Výhody:
✅ Rychlý vývoj - žádné přepínání mezi soubory ✅ Konzistentní design - předdefinované hodnoty ✅ Malá velikost - pouze použité styly se zahrnou ✅ Responsive design - vestavěné breakpointy ✅ Dark mode - jednoduché přepínání témat
🚀 Instalace a konfigurace
Instalace:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Konfigurace tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
}
},
spacing: {
'18': '4.5rem',
}
},
},
plugins: [],
}
CSS soubor (src/index.css):
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
}
🎨 Základní použití
EmployeeCard s TailwindCSS:
// EmployeeCard.jsx
import React from 'react';
const EmployeeCard = ({ employee, onClick, isSelected }) => {
return (
<div
className={`
bg-white rounded-lg p-4 shadow-md cursor-pointer
transition-all duration-200 flex items-center mb-3
hover:shadow-lg hover:-translate-y-0.5
${isSelected ? 'ring-2 ring-blue-500 bg-blue-50' : ''}
`}
onClick={onClick}
>
<div className={`
w-12 h-12 rounded-full text-white font-bold text-lg
flex items-center justify-center mr-3
${employee.department === 'IT' ? 'bg-blue-500' :
employee.department === 'Design' ? 'bg-green-500' :
employee.department === 'Marketing' ? 'bg-yellow-500' :
'bg-gray-500'}
`}>
{employee.name.charAt(0)}
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 mb-1">
{employee.name}
</h3>
<p className="text-sm text-gray-600 mb-2">
{employee.position}
</p>
<span className="
inline-block bg-gray-100 text-gray-700 text-xs font-medium
px-2 py-1 rounded-full uppercase tracking-wide
">
{employee.department}
</span>
</div>
</div>
);
};
export default EmployeeCard;
🎨 Pokročilé funkce
Responsive design:
// EmployeeList.jsx
import React from 'react';
const EmployeeList = ({ employees, filter, onFilterChange, onEmployeeSelect }) => {
const filteredEmployees = filter === 'all'
? employees
: employees.filter(emp => emp.department === filter);
return (
<div className="w-full max-w-6xl mx-auto p-4">
{/* Filter Bar */}
<div className="mb-6 flex flex-col sm:flex-row gap-4">
<select
value={filter}
onChange={(e) => onFilterChange(e.target.value)}
className="
px-4 py-2 border border-gray-300 rounded-lg
focus:ring-2 focus:ring-blue-500 focus:border-transparent
bg-white text-gray-700
"
>
<option value="all">Všechna oddělení</option>
<option value="IT">IT</option>
<option value="Design">Design</option>
<option value="Marketing">Marketing</option>
</select>
</div>
{/* Employee Grid */}
<div className="
grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4
gap-4 md:gap-6
">
{filteredEmployees.map(employee => (
<EmployeeCard
key={employee.id}
employee={employee}
onClick={() => onEmployeeSelect(employee)}
/>
))}
</div>
</div>
);
};
export default EmployeeList;
Dark mode:
// App.jsx
import React, { useState } from 'react';
const App = () => {
const [darkMode, setDarkMode] = useState(false);
return (
<div className={`min-h-screen transition-colors duration-200 ${
darkMode ? 'bg-gray-900 text-white' : 'bg-gray-50 text-gray-900'
}`}>
{/* Header */}
<header className="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
<div className="max-w-6xl mx-auto px-4 py-4 flex justify-between items-center">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">
Seznam zaměstnanců
</h1>
<button
onClick={() => setDarkMode(!darkMode)}
className="
px-4 py-2 rounded-lg font-medium transition-colors
bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600
text-gray-700 dark:text-gray-200
"
>
{darkMode ? '☀️' : '🌙'}
</button>
</div>
</header>
{/* Main Content */}
<main className="max-w-6xl mx-auto px-4 py-8">
<EmployeeList
employees={employees}
filter={filter}
onFilterChange={setFilter}
onEmployeeSelect={setSelectedEmployee}
/>
</main>
</div>
);
};
Animace a přechody:
// EmployeeDetail.jsx
import React from 'react';
const EmployeeDetail = ({ employee, onClose }) => {
return (
<div className="
fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center
z-50 p-4
">
<div className="
bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full
transform transition-all duration-300 ease-out
animate-in slide-in-from-bottom-4 fade-in
">
<div className="p-6">
<div className="flex justify-between items-start mb-4">
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">
{employee.name}
</h2>
<button
onClick={onClose}
className="
text-gray-400 hover:text-gray-600 dark:hover:text-gray-200
transition-colors duration-200
"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="space-y-4">
<div>
<label className="text-sm font-medium text-gray-500 dark:text-gray-400">
Pozice
</label>
<p className="text-lg text-gray-900 dark:text-white">
{employee.position}
</p>
</div>
<div>
<label className="text-sm font-medium text-gray-500 dark:text-gray-400">
Oddělení
</label>
<p className="text-lg text-gray-900 dark:text-white">
{employee.department}
</p>
</div>
<div>
<label className="text-sm font-medium text-gray-500 dark:text-gray-400">
Email
</label>
<p className="text-lg text-blue-600 dark:text-blue-400">
{employee.email}
</p>
</div>
</div>
</div>
</div>
</div>
);
};
export default EmployeeDetail;
🎨 Custom komponenty
Vytvoření vlastních komponent:
// components/Button.jsx
import React from 'react';
const Button = ({
children,
variant = 'primary',
size = 'md',
disabled = false,
className = '',
...props
}) => {
const baseClasses = `
font-medium rounded-lg transition-colors duration-200
focus:outline-none focus:ring-2 focus:ring-offset-2
disabled:opacity-50 disabled:cursor-not-allowed
`;
const variantClasses = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white focus:ring-blue-500',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900 focus:ring-gray-500',
danger: 'bg-red-500 hover:bg-red-600 text-white focus:ring-red-500',
};
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return (
<button
className={`
${baseClasses}
${variantClasses[variant]}
${sizeClasses[size]}
${className}
`}
disabled={disabled}
{...props}
>
{children}
</button>
);
};
export default Button;
Použití custom komponent:
// EmployeeCard.jsx
import React from 'react';
import Button from './Button';
const EmployeeCard = ({ employee, onClick, onEdit, onDelete }) => {
return (
<div className="bg-white rounded-lg p-4 shadow-md hover:shadow-lg transition-shadow">
<div className="flex items-center mb-4">
<div className="w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold mr-3">
{employee.name.charAt(0)}
</div>
<div>
<h3 className="text-lg font-semibold">{employee.name}</h3>
<p className="text-gray-600">{employee.position}</p>
</div>
</div>
<div className="flex gap-2">
<Button
variant="primary"
size="sm"
onClick={() => onClick(employee)}
className="flex-1"
>
Zobrazit detail
</Button>
<Button
variant="secondary"
size="sm"
onClick={() => onEdit(employee)}
>
Upravit
</Button>
<Button
variant="danger"
size="sm"
onClick={() => onDelete(employee.id)}
>
Smazat
</Button>
</div>
</div>
);
};
export default EmployeeCard;
🔧 TailwindCSS pluginy
Instalace užitečných pluginů:
npm install @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio
Konfigurace s pluginy:
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}
📋 Praktické cvičení
- Převeďte EmployeeCard na TailwindCSS
- Implementujte responsive design pro různé velikosti obrazovek
- Přidejte dark mode podporu
- Vytvořte custom komponenty s TailwindCSS
- Přidejte animace a přechody
🚀 Tipy a triky
Optimalizace velikosti:
// tailwind.config.js - pouze použité třídy
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
// TailwindCSS automaticky odstraní nepoužité styly
}
IntelliSense podpora:
// .vscode/settings.json
{
"tailwindCSS.includeLanguages": {
"javascript": "javascript",
"html": "HTML"
},
"editor.quickSuggestions": {
"strings": true
}
}
Conditional classes:
const Button = ({ isActive, children }) => (
<button className={`
px-4 py-2 rounded
${isActive ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'}
`}>
{children}
</button>
);
TailwindCSS je výkonný nástroj pro rychlé stylování React aplikací. V příští části se podíváme na práci s daty a mock API! 🚀