588 lines
13 KiB
Markdown
588 lines
13 KiB
Markdown
|
|
---
|
||
|
|
name: tailwindcss
|
||
|
|
description: |
|
||
|
|
TailwindCSS v4 patterns with CSS-first configuration using @theme, @source, and modern CSS features.
|
||
|
|
Covers design tokens, CSS variables, container queries, dark mode, and Vite integration.
|
||
|
|
Use when configuring Tailwind, defining design tokens, or leveraging modern CSS with Tailwind utilities.
|
||
|
|
---
|
||
|
|
|
||
|
|
# TailwindCSS v4 Patterns
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
TailwindCSS v4 introduces a CSS-first approach, eliminating the need for JavaScript configuration files. All customization happens directly in CSS using new directives.
|
||
|
|
|
||
|
|
## Key Changes from v3 to v4
|
||
|
|
|
||
|
|
| Feature | v3 | v4 |
|
||
|
|
|---------|-----|-----|
|
||
|
|
| Configuration | `tailwind.config.js` | CSS `@theme` directive |
|
||
|
|
| Content detection | JS array | `@source` directive |
|
||
|
|
| Plugin loading | `require()` in JS | `@plugin` directive |
|
||
|
|
| Custom variants | JS API | `@custom-variant` directive |
|
||
|
|
| Custom utilities | JS API | `@utility` directive |
|
||
|
|
|
||
|
|
## Browser Support
|
||
|
|
|
||
|
|
TailwindCSS v4 requires modern browsers:
|
||
|
|
- Safari 16.4+
|
||
|
|
- Chrome 111+
|
||
|
|
- Firefox 128+
|
||
|
|
|
||
|
|
**Important**: No CSS preprocessors (Sass/Less) needed - Tailwind IS the preprocessor.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Documentation Index
|
||
|
|
|
||
|
|
### Core Documentation
|
||
|
|
|
||
|
|
| Topic | URL | Description |
|
||
|
|
|-------|-----|-------------|
|
||
|
|
| Installation | https://tailwindcss.com/docs/installation | Setup guides by framework |
|
||
|
|
| Using Vite | https://tailwindcss.com/docs/installation/vite | Vite integration (recommended) |
|
||
|
|
| Editor Setup | https://tailwindcss.com/docs/editor-setup | VS Code IntelliSense |
|
||
|
|
| Upgrade Guide | https://tailwindcss.com/docs/upgrade-guide | v3 to v4 migration |
|
||
|
|
| Browser Support | https://tailwindcss.com/docs/browser-support | Compatibility requirements |
|
||
|
|
|
||
|
|
### Configuration Reference
|
||
|
|
|
||
|
|
| Directive | URL | Description |
|
||
|
|
|-----------|-----|-------------|
|
||
|
|
| @theme | https://tailwindcss.com/docs/theme | Define design tokens |
|
||
|
|
| @source | https://tailwindcss.com/docs/content-configuration | Content detection |
|
||
|
|
| @import | https://tailwindcss.com/docs/import | Import Tailwind layers |
|
||
|
|
| @config | https://tailwindcss.com/docs/configuration | Legacy JS config |
|
||
|
|
|
||
|
|
### CSS Features
|
||
|
|
|
||
|
|
| Feature | URL | Description |
|
||
|
|
|---------|-----|-------------|
|
||
|
|
| Dark Mode | https://tailwindcss.com/docs/dark-mode | Dark mode strategies |
|
||
|
|
| Responsive Design | https://tailwindcss.com/docs/responsive-design | Breakpoint utilities |
|
||
|
|
| Hover & Focus | https://tailwindcss.com/docs/hover-focus-and-other-states | State variants |
|
||
|
|
| Container Queries | https://tailwindcss.com/docs/container-queries | Component-responsive design |
|
||
|
|
|
||
|
|
### Customization
|
||
|
|
|
||
|
|
| Topic | URL | Description |
|
||
|
|
|-------|-----|-------------|
|
||
|
|
| Theme Configuration | https://tailwindcss.com/docs/theme | Token customization |
|
||
|
|
| Adding Custom Styles | https://tailwindcss.com/docs/adding-custom-styles | Extending Tailwind |
|
||
|
|
| Functions & Directives | https://tailwindcss.com/docs/functions-and-directives | CSS functions |
|
||
|
|
| Plugins | https://tailwindcss.com/docs/plugins | Plugin system |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## CSS-First Configuration
|
||
|
|
|
||
|
|
### Basic Setup
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* src/index.css */
|
||
|
|
@import "tailwindcss";
|
||
|
|
```
|
||
|
|
|
||
|
|
This single import replaces the v3 directives (`@tailwind base`, `@tailwind components`, `@tailwind utilities`).
|
||
|
|
|
||
|
|
### @theme Directive - Design Tokens
|
||
|
|
|
||
|
|
The `@theme` directive defines design tokens as CSS custom properties:
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
@theme {
|
||
|
|
/* Colors */
|
||
|
|
--color-primary: hsl(221 83% 53%);
|
||
|
|
--color-primary-dark: hsl(224 76% 48%);
|
||
|
|
--color-secondary: hsl(215 14% 34%);
|
||
|
|
--color-accent: hsl(328 85% 70%);
|
||
|
|
|
||
|
|
/* With oklch (modern color space) */
|
||
|
|
--color-success: oklch(0.723 0.191 142.5);
|
||
|
|
--color-warning: oklch(0.828 0.189 84.429);
|
||
|
|
--color-error: oklch(0.637 0.237 25.331);
|
||
|
|
|
||
|
|
/* Typography */
|
||
|
|
--font-display: "Satoshi", "sans-serif";
|
||
|
|
--font-body: "Inter", "sans-serif";
|
||
|
|
--font-mono: "JetBrains Mono", "monospace";
|
||
|
|
|
||
|
|
/* Spacing */
|
||
|
|
--spacing-page: 2rem;
|
||
|
|
--spacing-section: 4rem;
|
||
|
|
|
||
|
|
/* Custom breakpoints */
|
||
|
|
--breakpoint-xs: 480px;
|
||
|
|
--breakpoint-3xl: 1920px;
|
||
|
|
|
||
|
|
/* Animation timing */
|
||
|
|
--ease-spring: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||
|
|
--duration-fast: 150ms;
|
||
|
|
--duration-normal: 300ms;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Generated utilities from above:**
|
||
|
|
- Colors: `bg-primary`, `text-primary-dark`, `border-accent`
|
||
|
|
- Fonts: `font-display`, `font-body`, `font-mono`
|
||
|
|
- Animations: `ease-spring`, `duration-fast`
|
||
|
|
|
||
|
|
### @theme inline Pattern
|
||
|
|
|
||
|
|
Use `@theme inline` to reference existing CSS variables without generating new utilities:
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* Define CSS variables normally */
|
||
|
|
:root {
|
||
|
|
--background: oklch(1 0 0);
|
||
|
|
--foreground: oklch(0.145 0 0);
|
||
|
|
--card: oklch(1 0 0);
|
||
|
|
--primary: oklch(0.205 0 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
.dark {
|
||
|
|
--background: oklch(0.145 0 0);
|
||
|
|
--foreground: oklch(0.985 0 0);
|
||
|
|
--primary: oklch(0.985 0 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Map to Tailwind utilities */
|
||
|
|
@theme inline {
|
||
|
|
--color-background: var(--background);
|
||
|
|
--color-foreground: var(--foreground);
|
||
|
|
--color-card: var(--card);
|
||
|
|
--color-primary: var(--primary);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**When to use `@theme inline`:**
|
||
|
|
- Theming with CSS variables (light/dark mode)
|
||
|
|
- Shadcn/ui integration
|
||
|
|
- Dynamic theme switching
|
||
|
|
|
||
|
|
### @source Directive - Content Detection
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
/* Default: Tailwind scans all git-tracked files */
|
||
|
|
|
||
|
|
/* Add additional sources */
|
||
|
|
@source "../node_modules/my-ui-library/src/**/*.{html,js}";
|
||
|
|
@source "../shared-components/**/*.tsx";
|
||
|
|
|
||
|
|
/* Safelist specific utilities */
|
||
|
|
@source inline("bg-red-500 text-white p-4");
|
||
|
|
```
|
||
|
|
|
||
|
|
### @custom-variant - Custom Variants
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
/* Dark mode variant (class-based) */
|
||
|
|
@custom-variant dark (&:is(.dark *));
|
||
|
|
|
||
|
|
/* RTL variant */
|
||
|
|
@custom-variant rtl ([dir="rtl"] &);
|
||
|
|
|
||
|
|
/* Print variant */
|
||
|
|
@custom-variant print (@media print { & });
|
||
|
|
|
||
|
|
/* Hover on desktop only */
|
||
|
|
@custom-variant hover-desktop (@media (hover: hover) { &:hover });
|
||
|
|
```
|
||
|
|
|
||
|
|
### @utility - Custom Utilities
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
/* Text balance utility */
|
||
|
|
@utility text-balance {
|
||
|
|
text-wrap: balance;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Scrollbar hide */
|
||
|
|
@utility scrollbar-hide {
|
||
|
|
-ms-overflow-style: none;
|
||
|
|
scrollbar-width: none;
|
||
|
|
&::-webkit-scrollbar {
|
||
|
|
display: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Flex center shorthand */
|
||
|
|
@utility flex-center {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### @plugin - Plugin Configuration
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
/* Load a plugin */
|
||
|
|
@plugin "@tailwindcss/typography";
|
||
|
|
@plugin "@tailwindcss/forms";
|
||
|
|
@plugin "@tailwindcss/container-queries";
|
||
|
|
|
||
|
|
/* Plugin with options */
|
||
|
|
@plugin "@tailwindcss/typography" {
|
||
|
|
className: prose;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### @config - Legacy JS Configuration
|
||
|
|
|
||
|
|
When you need JS configuration (rare in v4):
|
||
|
|
|
||
|
|
```css
|
||
|
|
@import "tailwindcss";
|
||
|
|
@config "./tailwind.config.ts";
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Vite Integration
|
||
|
|
|
||
|
|
### Installation
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm install tailwindcss @tailwindcss/vite
|
||
|
|
```
|
||
|
|
|
||
|
|
### Vite Configuration
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// vite.config.ts
|
||
|
|
import tailwindcss from "@tailwindcss/vite"
|
||
|
|
import react from "@vitejs/plugin-react"
|
||
|
|
import path from "path"
|
||
|
|
import { defineConfig } from "vite"
|
||
|
|
|
||
|
|
export default defineConfig({
|
||
|
|
plugins: [react(), tailwindcss()],
|
||
|
|
resolve: {
|
||
|
|
alias: {
|
||
|
|
"@": path.resolve(__dirname, "./src"),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### CSS Entry Point
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* src/index.css */
|
||
|
|
@import "tailwindcss";
|
||
|
|
|
||
|
|
@theme {
|
||
|
|
/* Your design tokens */
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### TypeScript Path Aliases
|
||
|
|
|
||
|
|
```json
|
||
|
|
// tsconfig.json
|
||
|
|
{
|
||
|
|
"compilerOptions": {
|
||
|
|
"baseUrl": ".",
|
||
|
|
"paths": {
|
||
|
|
"@/*": ["./src/*"]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Modern CSS Features with Tailwind v4
|
||
|
|
|
||
|
|
### Native CSS Variables
|
||
|
|
|
||
|
|
Tailwind v4 uses native CSS variables without wrapper functions:
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* v3 - Required hsl wrapper */
|
||
|
|
--primary: 221 83% 53%;
|
||
|
|
background-color: hsl(var(--primary));
|
||
|
|
|
||
|
|
/* v4 - Direct CSS value */
|
||
|
|
--color-primary: hsl(221 83% 53%);
|
||
|
|
/* Used as: bg-primary */
|
||
|
|
```
|
||
|
|
|
||
|
|
### oklch Color Format
|
||
|
|
|
||
|
|
```css
|
||
|
|
@theme {
|
||
|
|
/* oklch: lightness, chroma, hue */
|
||
|
|
--color-brand: oklch(0.65 0.2 250);
|
||
|
|
--color-brand-light: oklch(0.85 0.15 250);
|
||
|
|
--color-brand-dark: oklch(0.45 0.25 250);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Benefits of oklch:**
|
||
|
|
- Perceptually uniform
|
||
|
|
- Consistent lightness across hues
|
||
|
|
- Better for generating color scales
|
||
|
|
- Native browser support
|
||
|
|
|
||
|
|
### Container Queries
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Parent needs @container -->
|
||
|
|
<div class="@container">
|
||
|
|
<!-- Child responds to container width -->
|
||
|
|
<div class="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3">
|
||
|
|
<!-- Content -->
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* Named containers */
|
||
|
|
.sidebar {
|
||
|
|
container-name: sidebar;
|
||
|
|
container-type: inline-size;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Target named container */
|
||
|
|
@container sidebar (min-width: 300px) {
|
||
|
|
.nav-item { /* expanded styles */ }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### :has() Pseudo-Class
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Style parent based on child state -->
|
||
|
|
<label class="group has-[:invalid]:border-red-500 has-[:focus]:ring-2">
|
||
|
|
<input type="email" class="peer" />
|
||
|
|
</label>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* Card with image gets different padding */
|
||
|
|
.card:has(> img) {
|
||
|
|
@apply p-0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Form with invalid fields */
|
||
|
|
.form:has(:invalid) {
|
||
|
|
@apply border-red-500;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Native CSS Nesting
|
||
|
|
|
||
|
|
```css
|
||
|
|
.card {
|
||
|
|
@apply rounded-lg bg-white shadow-md;
|
||
|
|
|
||
|
|
.header {
|
||
|
|
@apply border-b p-4;
|
||
|
|
}
|
||
|
|
|
||
|
|
.content {
|
||
|
|
@apply p-6;
|
||
|
|
}
|
||
|
|
|
||
|
|
&:hover {
|
||
|
|
@apply shadow-lg;
|
||
|
|
}
|
||
|
|
|
||
|
|
&.featured {
|
||
|
|
@apply border-2 border-primary;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Utility Patterns
|
||
|
|
|
||
|
|
### Responsive Design (Mobile-First)
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Breakpoints: sm(640px), md(768px), lg(1024px), xl(1280px), 2xl(1536px) -->
|
||
|
|
<div class="
|
||
|
|
p-4 text-sm /* Mobile */
|
||
|
|
sm:p-6 sm:text-base /* Tablet */
|
||
|
|
lg:p-8 lg:text-lg /* Desktop */
|
||
|
|
2xl:p-12 2xl:text-xl /* Large screens */
|
||
|
|
">
|
||
|
|
```
|
||
|
|
|
||
|
|
### State Variants
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Hover, focus, active -->
|
||
|
|
<button class="
|
||
|
|
bg-primary text-white
|
||
|
|
hover:bg-primary-dark
|
||
|
|
focus:ring-2 focus:ring-primary focus:ring-offset-2
|
||
|
|
active:scale-95
|
||
|
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||
|
|
">
|
||
|
|
|
||
|
|
<!-- Group hover -->
|
||
|
|
<div class="group">
|
||
|
|
<span class="group-hover:underline">Label</span>
|
||
|
|
<span class="opacity-0 group-hover:opacity-100">Icon</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Peer focus -->
|
||
|
|
<input class="peer" />
|
||
|
|
<span class="invisible peer-focus:visible">Hint text</span>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dark Mode
|
||
|
|
|
||
|
|
**Strategy 1: Class-based (recommended)**
|
||
|
|
|
||
|
|
```css
|
||
|
|
@custom-variant dark (&:is(.dark *));
|
||
|
|
```
|
||
|
|
|
||
|
|
```html
|
||
|
|
<html class="dark">
|
||
|
|
<body class="bg-white dark:bg-gray-900 text-black dark:text-white">
|
||
|
|
```
|
||
|
|
|
||
|
|
**Strategy 2: Media query**
|
||
|
|
|
||
|
|
```css
|
||
|
|
@custom-variant dark (@media (prefers-color-scheme: dark) { & });
|
||
|
|
```
|
||
|
|
|
||
|
|
### Animation Utilities
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Built-in animations -->
|
||
|
|
<div class="animate-spin" />
|
||
|
|
<div class="animate-pulse" />
|
||
|
|
<div class="animate-bounce" />
|
||
|
|
|
||
|
|
<!-- Custom animation -->
|
||
|
|
<div class="animate-[fadeIn_0.5s_ease-out]" />
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
@theme {
|
||
|
|
--animate-fade-in: fadeIn 0.5s ease-out;
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes fadeIn {
|
||
|
|
from { opacity: 0; transform: translateY(-10px); }
|
||
|
|
to { opacity: 1; transform: translateY(0); }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Size Utility (v4)
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- v3: Two classes -->
|
||
|
|
<div class="w-10 h-10">
|
||
|
|
|
||
|
|
<!-- v4: Single class -->
|
||
|
|
<div class="size-10">
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
### When to Use @apply
|
||
|
|
|
||
|
|
Use `@apply` sparingly for true component abstraction:
|
||
|
|
|
||
|
|
```css
|
||
|
|
/* Good: Repeated pattern across many components */
|
||
|
|
@layer components {
|
||
|
|
.btn-primary {
|
||
|
|
@apply px-4 py-2 bg-primary text-white rounded-md;
|
||
|
|
@apply hover:bg-primary-dark focus:ring-2 focus:ring-primary;
|
||
|
|
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
||
|
|
@apply transition-colors duration-fast;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Bad: One-off styling (just use utilities in HTML) */
|
||
|
|
.my-special-div {
|
||
|
|
@apply mt-4 p-6 bg-gray-100; /* Just put these in className */
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Rule**: Only extract patterns when reused 3+ times.
|
||
|
|
|
||
|
|
### Design Token Naming
|
||
|
|
|
||
|
|
```css
|
||
|
|
@theme {
|
||
|
|
/* Semantic naming (preferred) */
|
||
|
|
--color-primary: hsl(221 83% 53%);
|
||
|
|
--color-primary-foreground: hsl(0 0% 100%);
|
||
|
|
|
||
|
|
/* Not: --color-blue-600: ... */
|
||
|
|
|
||
|
|
/* Scale naming when needed */
|
||
|
|
--color-gray-50: oklch(0.985 0 0);
|
||
|
|
--color-gray-100: oklch(0.970 0 0);
|
||
|
|
--color-gray-900: oklch(0.145 0 0);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Performance
|
||
|
|
|
||
|
|
1. **Use Vite plugin** - Automatic dead code elimination
|
||
|
|
2. **Avoid dynamic class names** - Static analysis can't optimize them
|
||
|
|
3. **Purge unused styles** - Automatic with proper @source config
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Good: Static class names -->
|
||
|
|
<div class={isActive ? "bg-primary" : "bg-gray-100"}>
|
||
|
|
|
||
|
|
<!-- Bad: Dynamic class construction -->
|
||
|
|
<div class={`bg-${color}-500`}> <!-- Can't be purged -->
|
||
|
|
```
|
||
|
|
|
||
|
|
### CSS Layers Order
|
||
|
|
|
||
|
|
Tailwind v4 uses CSS cascade layers:
|
||
|
|
|
||
|
|
```
|
||
|
|
1. @layer base - Reset, typography defaults
|
||
|
|
2. @layer components - Reusable components
|
||
|
|
3. @layer utilities - Utility classes (highest priority)
|
||
|
|
```
|
||
|
|
|
||
|
|
Custom styles should go in appropriate layers:
|
||
|
|
|
||
|
|
```css
|
||
|
|
@layer components {
|
||
|
|
.card { /* component styles */ }
|
||
|
|
}
|
||
|
|
|
||
|
|
@layer utilities {
|
||
|
|
.text-shadow { /* utility styles */ }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Related Skills
|
||
|
|
|
||
|
|
- **shadcn-ui** - Component library using Tailwind (CSS variables, theming)
|
||
|
|
- **css-modules** - Alternative: scoped CSS for complex components
|
||
|
|
- **react-typescript** - React patterns with Tailwind className
|
||
|
|
- **design-references** - Design system guidelines (Tailwind UI reference)
|