651 lines
14 KiB
Markdown
651 lines
14 KiB
Markdown
# Tailwind CSS in Nuxt
|
|
|
|
**Last Updated:** 2025-11 (Tailwind v4.1.16)
|
|
|
|
**Check:** `@nuxtjs/tailwindcss` in package.json
|
|
|
|
Tailwind CSS is automatically integrated into Nuxt through the `@nuxtjs/tailwindcss` module, providing utility-first CSS framework support.
|
|
|
|
## When to Use Tailwind
|
|
|
|
**Detection:** Always check `package.json` for `@nuxtjs/tailwindcss` dependency before suggesting Tailwind patterns.
|
|
|
|
**If Tailwind is installed:**
|
|
|
|
- Prefer Tailwind utility classes in component templates
|
|
- Use utilities for layout, spacing, colors, typography, responsive design
|
|
- Combine utilities for common patterns (flex, grid, etc.)
|
|
|
|
**If Tailwind is NOT installed:**
|
|
|
|
- Use `<style scoped>` for component-specific styles
|
|
- Write traditional CSS/SCSS for styling
|
|
|
|
**Tailwind v4 Capabilities (No `<style>` needed):**
|
|
|
|
- Custom animations via `@theme` directive with `@keyframes`
|
|
- CSS variables for theming via `@theme` with `--color-*`, `--font-*`, etc.
|
|
- Scrollbar styling via arbitrary variants: `[&::-webkit-scrollbar]:w-1.5`
|
|
- Pseudo-elements via arbitrary variants: `before:content-['★']`
|
|
|
|
**When to Still Use `<style>` (even with Tailwind v4):**
|
|
|
|
- Very complex multi-step keyframes that are verbose in `@theme`
|
|
- Cross-browser scrollbar styling (Firefox requires different syntax than WebKit)
|
|
- Complex pseudo-element content with difficult escaping
|
|
- Styles that become unreadable as utility classes (use your judgment)
|
|
|
|
## Installation & Setup
|
|
|
|
### Tailwind v4 Setup (Current)
|
|
|
|
```bash
|
|
pnpm add -D tailwindcss @nuxtjs/tailwindcss
|
|
```
|
|
|
|
**nuxt.config.ts:**
|
|
|
|
```typescript
|
|
export default defineNuxtConfig({
|
|
modules: ["@nuxtjs/tailwindcss"],
|
|
|
|
tailwindcss: {
|
|
// Optional configuration
|
|
exposeConfig: true,
|
|
viewer: true, // Enable /_tailwind in dev mode
|
|
},
|
|
})
|
|
```
|
|
|
|
**assets/css/main.css:**
|
|
|
|
```css
|
|
@import "tailwindcss";
|
|
```
|
|
|
|
**Alternative (explicit layers):**
|
|
|
|
```css
|
|
@import "tailwindcss/base";
|
|
@import "tailwindcss/components";
|
|
@import "tailwindcss/utilities";
|
|
```
|
|
|
|
### Tailwind v3 Setup (Previous Version)
|
|
|
|
```bash
|
|
pnpm add -D tailwindcss @nuxtjs/tailwindcss
|
|
```
|
|
|
|
**assets/css/main.css:**
|
|
|
|
```css
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### tailwind.config.ts
|
|
|
|
Tailwind v4 uses CSS-based configuration by default, but you can still use TypeScript config:
|
|
|
|
```typescript
|
|
import type { Config } from "tailwindcss"
|
|
|
|
export default {
|
|
content: [
|
|
"./components/**/*.{vue,js,ts}",
|
|
"./layouts/**/*.vue",
|
|
"./pages/**/*.vue",
|
|
"./composables/**/*.{js,ts}",
|
|
"./plugins/**/*.{js,ts}",
|
|
"./app.vue",
|
|
],
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: "#f0f9ff",
|
|
100: "#e0f2fe",
|
|
200: "#bae6fd",
|
|
300: "#7dd3fc",
|
|
400: "#38bdf8",
|
|
500: "#0ea5e9",
|
|
600: "#0284c7",
|
|
700: "#0369a1",
|
|
800: "#075985",
|
|
900: "#0c4a6e",
|
|
950: "#082f49",
|
|
},
|
|
},
|
|
fontFamily: {
|
|
sans: ["Inter", "system-ui", "sans-serif"],
|
|
},
|
|
spacing: {
|
|
"128": "32rem",
|
|
"144": "36rem",
|
|
},
|
|
},
|
|
},
|
|
plugins: [],
|
|
} satisfies Config
|
|
```
|
|
|
|
### CSS-Based Configuration (v4)
|
|
|
|
Tailwind v4 supports CSS variables for theming:
|
|
|
|
```css
|
|
/* assets/css/main.css */
|
|
@import "tailwindcss";
|
|
|
|
@layer base {
|
|
:root {
|
|
--color-primary-50: 240 249 255;
|
|
--color-primary-500: 14 165 233;
|
|
--color-primary-900: 12 74 110;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Responsive Design
|
|
|
|
```vue
|
|
<template>
|
|
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
|
|
<h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold">
|
|
Responsive Heading
|
|
</h1>
|
|
|
|
<!-- Hide on mobile, show on desktop -->
|
|
<div class="hidden lg:block">Desktop only content</div>
|
|
|
|
<!-- Show on mobile, hide on desktop -->
|
|
<div class="block lg:hidden">Mobile only content</div>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
### Dark Mode
|
|
|
|
Nuxt integrates with `@nuxtjs/color-mode` for automatic dark mode support:
|
|
|
|
```vue
|
|
<template>
|
|
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
|
<h1 class="text-gray-900 dark:text-gray-100">Auto Dark Mode</h1>
|
|
|
|
<button
|
|
class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-700 dark:hover:bg-blue-800"
|
|
@click="toggleDarkMode"
|
|
>
|
|
Toggle Dark Mode
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const colorMode = useColorMode()
|
|
|
|
function toggleDarkMode() {
|
|
colorMode.preference = colorMode.value === "dark" ? "light" : "dark"
|
|
}
|
|
</script>
|
|
```
|
|
|
|
### Custom Components with @apply
|
|
|
|
```vue
|
|
<template>
|
|
<button class="btn-primary">Primary Button</button>
|
|
</template>
|
|
|
|
<style>
|
|
.btn-primary {
|
|
@apply px-4 py-2 bg-blue-500 text-white font-medium rounded-lg hover:bg-blue-600 transition-colors;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
### Layout Utilities
|
|
|
|
```vue
|
|
<template>
|
|
<!-- Flexbox -->
|
|
<div class="flex items-center justify-between gap-4">
|
|
<div>Left</div>
|
|
<div>Right</div>
|
|
</div>
|
|
|
|
<!-- Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<div>Card 1</div>
|
|
<div>Card 2</div>
|
|
<div>Card 3</div>
|
|
</div>
|
|
|
|
<!-- Container -->
|
|
<div class="container mx-auto max-w-7xl px-4">Content with max width</div>
|
|
</template>
|
|
```
|
|
|
|
### Transitions & Animations
|
|
|
|
```vue
|
|
<template>
|
|
<div class="transition-all duration-300 hover:scale-105 hover:shadow-lg">
|
|
Hover to animate
|
|
</div>
|
|
|
|
<button class="animate-pulse bg-blue-500 text-white px-4 py-2 rounded">
|
|
Pulsing Button
|
|
</button>
|
|
</template>
|
|
```
|
|
|
|
## Custom Layers
|
|
|
|
### Adding Custom Utilities
|
|
|
|
```css
|
|
/* assets/css/main.css */
|
|
@import "tailwindcss";
|
|
|
|
@layer utilities {
|
|
.text-gradient {
|
|
@apply bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent;
|
|
}
|
|
|
|
.scrollbar-hide {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.scrollbar-hide::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
}
|
|
```
|
|
|
|
Usage:
|
|
|
|
```vue
|
|
<template>
|
|
<h1 class="text-gradient text-4xl font-bold">Gradient Text</h1>
|
|
|
|
<div class="scrollbar-hide overflow-auto">Scrollable without scrollbar</div>
|
|
</template>
|
|
```
|
|
|
|
### Custom Components Layer
|
|
|
|
```css
|
|
/* assets/css/main.css */
|
|
@import "tailwindcss";
|
|
|
|
@layer components {
|
|
.card {
|
|
@apply bg-white dark:bg-gray-800 rounded-lg shadow-md p-6;
|
|
}
|
|
|
|
.card-title {
|
|
@apply text-xl font-semibold mb-2 text-gray-900 dark:text-white;
|
|
}
|
|
|
|
.card-body {
|
|
@apply text-gray-600 dark:text-gray-300;
|
|
}
|
|
}
|
|
```
|
|
|
|
Usage:
|
|
|
|
```vue
|
|
<template>
|
|
<div class="card">
|
|
<h2 class="card-title">Card Title</h2>
|
|
<p class="card-body">Card content goes here.</p>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Tailwind Plugins
|
|
|
|
### Using Official Plugins
|
|
|
|
```bash
|
|
pnpm add -D @tailwindcss/forms @tailwindcss/typography @tailwindcss/container-queries
|
|
```
|
|
|
|
```typescript
|
|
// tailwind.config.ts
|
|
import type { Config } from "tailwindcss"
|
|
|
|
export default {
|
|
plugins: [
|
|
require("@tailwindcss/forms"),
|
|
require("@tailwindcss/typography"),
|
|
require("@tailwindcss/container-queries"),
|
|
],
|
|
} satisfies Config
|
|
```
|
|
|
|
### @tailwindcss/typography
|
|
|
|
For rich text content:
|
|
|
|
```vue
|
|
<template>
|
|
<article class="prose dark:prose-invert lg:prose-xl mx-auto">
|
|
<h1>Article Title</h1>
|
|
<p>
|
|
Automatically styled rich text content with proper spacing, typography,
|
|
and dark mode support.
|
|
</p>
|
|
<ul>
|
|
<li>List item 1</li>
|
|
<li>List item 2</li>
|
|
</ul>
|
|
</article>
|
|
</template>
|
|
```
|
|
|
|
### @tailwindcss/forms
|
|
|
|
Automatically styles form elements:
|
|
|
|
```vue
|
|
<template>
|
|
<form class="space-y-4">
|
|
<!-- Forms plugin provides base styling -->
|
|
<input
|
|
type="text"
|
|
class="form-input rounded-md"
|
|
placeholder="Auto-styled input"
|
|
/>
|
|
|
|
<select class="form-select rounded-md">
|
|
<option>Option 1</option>
|
|
<option>Option 2</option>
|
|
</select>
|
|
|
|
<textarea class="form-textarea rounded-md" rows="4" />
|
|
</form>
|
|
</template>
|
|
```
|
|
|
|
## Nuxt-Specific Features
|
|
|
|
### Tailwind Viewer
|
|
|
|
Enable Tailwind config viewer in development:
|
|
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
tailwindcss: {
|
|
viewer: true,
|
|
},
|
|
})
|
|
```
|
|
|
|
Access at: `http://localhost:3000/_tailwind`
|
|
|
|
### IntelliSense Configuration
|
|
|
|
For VSCode autocomplete, create `.vscode/settings.json`:
|
|
|
|
```json
|
|
{
|
|
"tailwindCSS.experimental.classRegex": [
|
|
["class:\\s*['\"`]([^'\"`]*)['\"`]", "([^'\"`]*)"],
|
|
["class:\\s*{([^}]*)", "['\"`]([^'\"`]*)['\"`]"]
|
|
],
|
|
"tailwindCSS.includeLanguages": {
|
|
"vue": "html",
|
|
"typescript": "javascript"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Scoped Styles with Tailwind
|
|
|
|
```vue
|
|
<template>
|
|
<div class="custom-component">Content</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.custom-component {
|
|
@apply bg-blue-500 text-white p-4 rounded-lg;
|
|
}
|
|
|
|
/* Scoped styles work with Tailwind utilities */
|
|
.custom-component:hover {
|
|
@apply bg-blue-600;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
## JIT Mode (Always On in v3+)
|
|
|
|
Just-In-Time compilation is default in Tailwind v3+. Benefits:
|
|
|
|
- **Arbitrary values:** `w-[137px]`, `top-[117px]`
|
|
- **Arbitrary variants:** `[&>*]:text-red-500`
|
|
- **Dynamic values:** `bg-[#1da1f2]`
|
|
|
|
```vue
|
|
<template>
|
|
<div>
|
|
<!-- Arbitrary values -->
|
|
<div class="w-[137px] h-[91px] bg-[#1da1f2]">Custom size and color</div>
|
|
|
|
<!-- Arbitrary variants -->
|
|
<ul class="[&>li]:text-blue-500 [&>li]:font-bold">
|
|
<li>Item 1</li>
|
|
<li>Item 2</li>
|
|
</ul>
|
|
|
|
<!-- Dynamic spacing -->
|
|
<div class="m-[calc(100%-3rem)]">Calculated margin</div>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Purging Unused Styles
|
|
|
|
Automatically handled by Tailwind. Ensure `content` paths in config are correct:
|
|
|
|
```typescript
|
|
// tailwind.config.ts
|
|
export default {
|
|
content: [
|
|
"./components/**/*.{vue,js,ts}",
|
|
"./layouts/**/*.vue",
|
|
"./pages/**/*.vue",
|
|
"./plugins/**/*.{js,ts}",
|
|
"./app.vue",
|
|
],
|
|
} satisfies Config
|
|
```
|
|
|
|
### Safelist Dynamic Classes
|
|
|
|
For dynamically generated classes:
|
|
|
|
```typescript
|
|
// tailwind.config.ts
|
|
export default {
|
|
safelist: [
|
|
"bg-red-500",
|
|
"bg-blue-500",
|
|
"bg-green-500",
|
|
{
|
|
pattern: /bg-(red|blue|green)-(400|500|600)/,
|
|
},
|
|
],
|
|
} satisfies Config
|
|
```
|
|
|
|
Or use string interpolation with full class names:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
// Bad: Classes may be purged
|
|
const color = ref("blue")
|
|
const bgClass = computed(() => `bg-${color.value}-500`)
|
|
|
|
// Good: Full class names are detected
|
|
const bgClass = computed(() => {
|
|
if (color.value === "blue") return "bg-blue-500"
|
|
if (color.value === "red") return "bg-red-500"
|
|
return "bg-green-500"
|
|
})
|
|
</script>
|
|
```
|
|
|
|
## Common Utilities Reference
|
|
|
|
### Spacing
|
|
|
|
- `p-{size}` - padding
|
|
- `m-{size}` - margin
|
|
- `space-x-{size}` - horizontal gap between children
|
|
- `space-y-{size}` - vertical gap between children
|
|
- `gap-{size}` - gap in flex/grid
|
|
|
|
### Sizing
|
|
|
|
- `w-{size}` - width
|
|
- `h-{size}` - height
|
|
- `max-w-{size}` - max width
|
|
- `min-h-{size}` - min height
|
|
|
|
### Typography
|
|
|
|
- `text-{size}` - font size
|
|
- `font-{weight}` - font weight
|
|
- `leading-{size}` - line height
|
|
- `tracking-{size}` - letter spacing
|
|
- `text-{align}` - text alignment
|
|
|
|
### Colors
|
|
|
|
- `text-{color}-{shade}` - text color
|
|
- `bg-{color}-{shade}` - background color
|
|
- `border-{color}-{shade}` - border color
|
|
|
|
### Layout
|
|
|
|
- `flex`, `inline-flex` - flexbox
|
|
- `grid`, `inline-grid` - grid
|
|
- `block`, `inline-block`, `hidden` - display
|
|
- `relative`, `absolute`, `fixed`, `sticky` - positioning
|
|
|
|
### Borders
|
|
|
|
- `border`, `border-{width}` - border width
|
|
- `rounded-{size}` - border radius
|
|
- `border-{side}` - specific side border
|
|
|
|
### Effects
|
|
|
|
- `shadow-{size}` - box shadow
|
|
- `opacity-{amount}` - opacity
|
|
- `blur-{amount}` - blur effect
|
|
|
|
## Best Practices
|
|
|
|
1. **Use Tailwind viewer in dev** - Enable `viewer: true` to explore your config
|
|
2. **Avoid @apply overuse** - Prefer composition over extraction for most cases
|
|
3. **Use arbitrary values sparingly** - Stick to design system values when possible
|
|
4. **Leverage dark mode utilities** - Always test with `dark:` variants
|
|
5. **Keep config minimal** - Only extend when design system requires it
|
|
6. **Use safelist for dynamic classes** - Or ensure full class names in templates
|
|
7. **Enable IntelliSense** - Configure VSCode for autocomplete
|
|
8. **Responsive-first** - Use mobile-first breakpoints (`sm:`, `md:`, `lg:`)
|
|
9. **Use Tailwind plugins** - Leverage official plugins for common needs
|
|
10. **Avoid inline styles** - Prefer Tailwind utilities over style attributes
|
|
|
|
## Troubleshooting
|
|
|
|
### Styles Not Applied
|
|
|
|
Check:
|
|
|
|
1. `@import "tailwindcss"` in `main.css` (v4) or `@tailwind` directives (v3)
|
|
2. `main.css` imported in `nuxt.config.ts` or `app.vue`
|
|
3. `content` paths in `tailwind.config.ts` include all component files
|
|
4. Class names are complete strings (not dynamically concatenated)
|
|
|
|
### Dark Mode Not Working
|
|
|
|
Ensure `@nuxtjs/color-mode` is installed:
|
|
|
|
```bash
|
|
pnpm add @nuxtjs/color-mode
|
|
```
|
|
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
modules: ["@nuxtjs/tailwindcss", "@nuxtjs/color-mode"],
|
|
})
|
|
```
|
|
|
|
### IntelliSense Not Working
|
|
|
|
1. Install "Tailwind CSS IntelliSense" VSCode extension
|
|
2. Create `.vscode/settings.json` with Tailwind config
|
|
3. Restart VSCode
|
|
|
|
### Build Size Too Large
|
|
|
|
1. Verify `content` paths only include necessary files
|
|
2. Remove unused plugins
|
|
3. Avoid safelisting large pattern sets
|
|
4. Use CDN for development, bundled for production
|
|
|
|
## Migration from v3 to v4
|
|
|
|
Key changes when upgrading to Tailwind v4:
|
|
|
|
1. **Import syntax:**
|
|
|
|
```css
|
|
/* v3 */
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* v4 */
|
|
@import "tailwindcss";
|
|
```
|
|
|
|
2. **CSS-based configuration** - Use CSS variables alongside TypeScript config
|
|
|
|
3. **Removed PostCSS plugins** - Many features now built-in
|
|
|
|
4. **New color system** - Improved color utilities with better composability
|
|
|
|
Always consult official Tailwind v4 migration guide for comprehensive changes.
|
|
|
|
## Official Resources
|
|
|
|
- **Tailwind CSS Docs:** https://tailwindcss.com
|
|
- **Nuxt Tailwind Module:** https://tailwindcss.nuxtjs.org
|
|
- **Cheat Sheet:** https://nerdcave.com/tailwind-cheat-sheet
|
|
- **Component Examples:** https://tailwindui.com (official, paid)
|
|
- **Community Components:** https://tailwindcomponents.com
|
|
|
|
---
|
|
|
|
**Note:** Always verify version-specific features with official documentation. This guide covers both Tailwind v3 and v4, focusing on Nuxt integration.
|