commit 8bd861b848c0a9cb972f6dc33d29a113180ca2a7 Author: Zhongwei Li Date: Sun Nov 30 08:38:06 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..d5f12b6 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "nuxt", + "description": "Nuxt development guidance with Vue best practices, auto-imports awareness, and library-specific patterns for Pinia, VueUse, and more", + "version": "2.3.0", + "author": { + "name": "Lukas Trumm" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bcd1934 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# nuxt + +Nuxt development guidance with Vue best practices, auto-imports awareness, and library-specific patterns for Pinia, VueUse, and more diff --git a/commands/prime/components.md b/commands/prime/components.md new file mode 100644 index 0000000..bdf6a57 --- /dev/null +++ b/commands/prime/components.md @@ -0,0 +1,488 @@ +--- +description: Load Vue component patterns and best practices +--- + +# Vue Component Priming + +> **Note:** This command references the `nuxt:nuxt` skill for progressive disclosure of additional Vue patterns and library-specific documentation. + +## Script Setup Syntax + +ALWAYS use ` + + +``` + +### Component Naming + +- ALWAYS use multi-word component names except for Nuxt pages and layouts +- Examples: `UserProfile.vue`, `SearchBar.vue` (not `User.vue`, `Search.vue`) +- Exception: `pages/index.vue`, `pages/about.vue`, `layouts/default.vue` + +## Template Directives + +### v-for Loops + +ALWAYS use `key` in v-for loops and prefer `of` over `in`: + +```vue + +
  • {{ user.name }}
  • + + +
  • {{ user.name }}
  • + + +
  • {{ user.name }}
  • +``` + +### Prop Binding Shorthand + +ALWAYS use shorthand syntax when passing props with same name as variable: + +```vue + + + + + +``` + +## Reactivity and State + +### Reactive References + +PREFER `ref()` for reactive state instead of `reactive()`: + +```typescript +// ✅ Preferred: Using ref +const count = ref(0) +const user = ref({ name: "Alice", age: 30 }) + +// ❌ Less preferred: Using reactive (loses reactivity on destructure) +const state = reactive({ count: 0 }) +``` + +### Auto-Imported Vue APIs + +Never manually import these in Nuxt projects - they're auto-imported: + +**Reactivity:** + +- `ref` - Reactive primitive values +- `reactive` - Reactive objects +- `computed` - Computed values +- `watch` - Watch reactive values + +**Lifecycle:** + +- `onMounted` - Component mounted +- `onUnmounted` - Component unmounted +- `onBeforeMount`, `onBeforeUnmount`, etc. + +**Component APIs:** + +- `defineProps` - Define props (type-based) +- `defineEmits` - Define emits (type-based) +- `defineModel` - Define v-model (type-based) + +**Utilities:** + +- `useId` - Generate unique IDs for accessibility/form elements (SSR-safe) + +## Component Organization + +### Logical Grouping + +PREFER to group by logical concerns rather than by type: + +```typescript +// ✅ Preferred: Grouped by feature/concern + + +// ❌ Less preferred: Grouped by type + +``` + +## Styling Strategy + +**Check `package.json` for `@nuxtjs/tailwindcss`:** + +### If Tailwind Installed + +```vue + +``` + +### If NO Tailwind + +ALWAYS use ` + + +
    +
    Title
    +
    +``` + +## VueUse Composables (If Installed) + +Check `package.json` for `@vueuse/core` or `@vueuse/nuxt`: + +PREFER VueUse composables over custom implementations for common tasks: + +```typescript +// ✅ Preferred: Using VueUse (if installed) +import { useLocalStorage, useMouse, useWindowSize } from "@vueuse/core" +const token = useLocalStorage("auth-token", "") + +// ❌ Avoid: Custom implementation when VueUse exists +const token = ref(localStorage.getItem("auth-token") || "") +watch(token, (val) => localStorage.setItem("auth-token", val)) +``` + +### Common VueUse Patterns + +**State:** + +- `useToggle`, `useCounter`, `useLocalStorage`, `useSessionStorage` + +**DOM:** + +- `useMouse`, `useScroll`, `useElementVisibility`, `useIntersectionObserver`, `useResizeObserver` + +**Browser:** + +- `useClipboard`, `useMediaQuery`, `useDark`, `usePreferredDark`, `useGeolocation` + +**Utilities:** + +- `refDebounced`, `useDebounceFn`, `refThrottled`, `useThrottleFn`, `useInterval`, `useTimeout` + +The `nuxt:nuxt` skill provides detailed VueUse reference when installed. + +## Accessibility + +- Use semantic HTML: ` + + + + + + + +``` + +### Slots + +```vue + + + + + + + + +``` + +## Provide/Inject + +For dependency injection: + +```typescript +// Parent component +provide("theme", "dark") +provide("api", apiClient) + +// Child component (any depth) +const theme = inject("theme") +const api = inject("api") + +// With TypeScript +import type { InjectionKey } from "vue" + +interface Theme { + mode: "light" | "dark" +} + +const themeKey: InjectionKey = Symbol("theme") + +// Provide +provide(themeKey, { mode: "dark" }) + +// Inject +const theme = inject(themeKey) +``` + +## Lifecycle Hooks + +```typescript +// Setup (reactive state initialization) +const count = ref(0) + +// Mounted (DOM available) +onMounted(() => { + console.log("Component mounted") +}) + +// Before unmount (cleanup) +onBeforeUnmount(() => { + // Remove event listeners, clear timers, etc. +}) + +// Unmounted +onUnmounted(() => { + console.log("Component unmounted") +}) + +// Watch effect (runs immediately and on dependencies change) +watchEffect(() => { + console.log(`Count is ${count.value}`) +}) + +// Watch specific value +watch(count, (newValue, oldValue) => { + console.log(`Count changed from ${oldValue} to ${newValue}`) +}) +``` + +## Template Refs + +```vue + + + +``` diff --git a/commands/prime/framework.md b/commands/prime/framework.md new file mode 100644 index 0000000..598426a --- /dev/null +++ b/commands/prime/framework.md @@ -0,0 +1,552 @@ +--- +description: Load Nuxt framework patterns and conventions +--- + +# Nuxt Framework Priming + +> **Note:** This command references the `nuxt:nuxt` skill for progressive disclosure of additional patterns and module-specific documentation. + +## Data Fetching + +### useFetch for API Calls + +Use `useFetch` for API endpoints. It runs on both server and client, with automatic hydration. + +```typescript +const { data, status, error, refresh } = await useFetch("/api/users") + +// With query parameters +const { data, status } = await useFetch("/api/users", { + query: { limit: 10, page: 1 }, +}) + +// With type safety +interface User { + id: number + name: string +} +const { data, status, error } = await useFetch("/api/users") +``` + +**Always handle all states in templates:** + +```vue + +``` + +### useAsyncData for Complex Data + +Use `useAsyncData` when you need more control or complex transformations. + +```typescript +const { data, status, error } = await useAsyncData("users", async () => { + const users = await $fetch("/api/users") + const stats = await $fetch("/api/stats") + return { users, stats } +}) + +// With caching key +const { data, status, error } = await useAsyncData(`user-${id}`, () => + $fetch(`/api/users/${id}`), +) +``` + +### Lazy Fetching + +Use lazy variants when you don't want to block navigation: + +```typescript +// Non-blocking +const { status, data } = await useLazyFetch('/api/users') + +// Show loading state +
    Loading...
    +
    {{ data }}
    +``` + +### Client-Only Fetching + +```typescript +const { data } = await useFetch("/api/users", { + server: false, // Only fetch on client +}) +``` + +### Refresh and Refetch + +```typescript +const { data, status, refresh } = await useFetch("/api/users") + +// Manually refetch +await refresh() + +// Refetch on event +watch(searchQuery, () => refresh()) +``` + +## SEO and Meta Tags + +### useHead + +```typescript +useHead({ + title: "My Page", + meta: [ + { name: "description", content: "Page description" }, + { property: "og:title", content: "My Page" }, + ], + link: [{ rel: "canonical", href: "https://example.com/page" }], +}) +``` + +### useSeoMeta (Type-Safe) + +```typescript +useSeoMeta({ + title: "My Page", + description: "Page description", + ogTitle: "My Page", + ogDescription: "Page description", + ogImage: "https://example.com/image.jpg", + twitterCard: "summary_large_image", +}) +``` + +### definePageMeta + +```typescript +definePageMeta({ + title: "User Profile", + description: "View user profile", + middleware: ["auth"], +}) +``` + +## Error Handling + +### Show Error Page + +```typescript +showError({ + statusCode: 404, + message: "Page not found", +}) + +// With custom error +showError({ + statusCode: 403, + message: "Access denied", + fatal: true, +}) +``` + +### Clear Error + +```typescript +clearError({ redirect: "/" }) +``` + +### Handle Errors in Data Fetching + +```typescript +const { data, status, error } = await useFetch("/api/users") + +if (error.value) { + showError({ + statusCode: error.value.statusCode, + message: error.value.message, + }) +} +``` + +### Error Component + +```vue + + + + +``` + +## Environment Variables and Config + +### Runtime Config + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + runtimeConfig: { + // Private (server-only) + apiSecret: process.env.API_SECRET, + databaseUrl: process.env.DATABASE_URL, + + // Public (exposed to client) + public: { + apiBase: process.env.API_BASE_URL || "http://localhost:3000", + environment: process.env.NODE_ENV, + }, + }, +}) + +// Usage +const config = useRuntimeConfig() +console.log(config.public.apiBase) // Available everywhere +console.log(config.apiSecret) // Server-only +``` + +### App Config + +For non-sensitive configuration that can be updated at runtime: + +```typescript +// app.config.ts +export default defineAppConfig({ + theme: { + primaryColor: "#3b82f6", + }, +}) + +// Usage +const appConfig = useAppConfig() +console.log(appConfig.theme.primaryColor) +``` + +## Server API Routes + +### GET Request + +```typescript +// server/api/users.get.ts +export default defineEventHandler(async (event) => { + const query = getQuery(event) + + return { + users: [ + { id: 1, name: "Alice" }, + { id: 2, name: "Bob" }, + ], + } +}) +``` + +### POST Request + +```typescript +// server/api/users.post.ts +export default defineEventHandler(async (event) => { + const body = await readBody(event) + + // Validate and save user + return { success: true, user: body } +}) +``` + +### Dynamic Routes + +```typescript +// server/api/users/[id].get.ts +export default defineEventHandler(async (event) => { + const id = getRouterParam(event, "id") + + // Fetch user by id + return { id, name: "User" } +}) +``` + +### Error Handling in API Routes + +```typescript +export default defineEventHandler(async (event) => { + try { + const data = await fetchData() + return data + } catch (error) { + throw createError({ + statusCode: 500, + message: "Internal server error", + }) + } +}) +``` + +### Protected API Routes + +```typescript +// server/api/admin/users.get.ts +export default defineEventHandler(async (event) => { + const session = await requireUserSession(event) + + if (!session.user.isAdmin) { + throw createError({ + statusCode: 403, + message: "Forbidden", + }) + } + + return { users: [] } +}) +``` + +## Middleware + +### Route Middleware + +```typescript +// middleware/auth.ts +export default defineNuxtRouteMiddleware((to, from) => { + const user = useState("user") + + if (!user.value) { + return navigateTo("/login") + } +}) + +// Usage in page +definePageMeta({ + middleware: "auth", +}) +``` + +### Global Middleware + +```typescript +// middleware/analytics.global.ts +export default defineNuxtRouteMiddleware((to, from) => { + // Track page view + console.log("Navigating to:", to.path) +}) +``` + +## State Management + +### useState + +For shared state across components: + +```typescript +// composables/useAuth.ts +export const useAuth = () => { + const user = useState("user", () => null) + const isAuthenticated = computed(() => !!user.value) + + async function login(credentials: LoginCredentials) { + const response = await $fetch("/api/auth/login", { + method: "POST", + body: credentials, + }) + user.value = response.user + } + + function logout() { + user.value = null + } + + return { + user, + isAuthenticated, + login, + logout, + } +} + +// Usage in component +const { user, login, logout } = useAuth() +``` + +## Composables + +### Auto-Import from composables/ + +```typescript +// composables/useCounter.ts +export const useCounter = () => { + const count = ref(0) + + function increment() { + count.value++ + } + + function decrement() { + count.value-- + } + + return { + count, + increment, + decrement, + } +} + +// Usage (auto-imported) +const { count, increment } = useCounter() +``` + +## Layouts + +### Default Layout + +```vue + + +``` + +### Custom Layout + +```vue + + + + + +``` + +### Dynamic Layout + +```typescript +setPageLayout("admin") +``` + +## Plugins + +### Client-Only Plugin + +```typescript +// plugins/analytics.client.ts +export default defineNuxtPlugin(() => { + // Only runs on client + console.log("Client-side analytics initialized") +}) +``` + +### Server-Only Plugin + +```typescript +// plugins/database.server.ts +export default defineNuxtPlugin(() => { + // Only runs on server + return { + provide: { + db: createDatabaseConnection(), + }, + } +}) +``` + +### Universal Plugin + +```typescript +// plugins/api.ts +export default defineNuxtPlugin(() => { + const api = $fetch.create({ + baseURL: "/api", + onResponseError({ response }) { + if (response.status === 401) { + navigateTo("/login") + } + }, + }) + + return { + provide: { + api, + }, + } +}) + +// Usage +const { $api } = useNuxtApp() +const data = await $api("/users") +``` + +## Auto-Imported APIs + +Never manually import these - Nuxt auto-imports them: + +**Vue APIs:** `ref`, `reactive`, `computed`, `watch`, `onMounted`, `defineProps`, `defineEmits`, `defineModel` + +**Nuxt Composables:** `useState`, `useFetch`, `useAsyncData`, `useRoute`, `useRouter`, `navigateTo`, `useCookie`, `useHead`, `useSeoMeta`, `useRuntimeConfig`, `showError`, `clearError` + +## File-Based Conventions + +**Routing:** + +- `pages/index.vue` → `/` +- `pages/about.vue` → `/about` +- `pages/users/[id].vue` → `/users/:id` + +**Server API:** + +- `server/api/users.get.ts` → `/api/users` (GET) +- `server/api/users.post.ts` → `/api/users` (POST) +- `server/routes/healthz.ts` → `/healthz` + +**Layouts & Middleware:** + +- `layouts/default.vue` - Default layout +- `middleware/auth.ts` - Named middleware +- `middleware/analytics.global.ts` - Global middleware + +## Nuxt CLI Commands + +**Development:** + +- `nuxt dev` - Start dev server +- `nuxt dev --host` - Expose to network + +**Building:** + +- `nuxt build` - Production build +- `nuxt generate` - Static site generation +- `nuxt preview` - Preview production build + +**Analysis:** + +- `nuxt analyze` - Bundle size analysis +- `nuxt typecheck` - Type checking +- `nuxt info` - Environment info diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..52ce333 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,97 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:lttr/claude-marketplace:plugins/nuxt", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "626e443911e81c6bdccf5b6bd0ecadcd626d3733", + "treeHash": "d1094c64598ae325003608e536199af804cad0457de4d4bd64963d1ee8becbaf", + "generatedAt": "2025-11-28T10:20:21.411033Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "nuxt", + "description": "Nuxt development guidance with Vue best practices, auto-imports awareness, and library-specific patterns for Pinia, VueUse, and more", + "version": "2.3.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "ce260d90f9531dd5cb3b65c3adb1f4050ec5be3a3282429698ab86cb84ceb806" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "0621531f83fc2be0ff2a79595811dee13f9a50a30ddbd4039fe566172e3a2c0a" + }, + { + "path": "commands/prime/components.md", + "sha256": "b64abab9be3a60502fa7ce9e9c9fa586cd9ceef725c49dd59fd3df3324f1c930" + }, + { + "path": "commands/prime/framework.md", + "sha256": "18850a293a59d872751df102f08362ea3bc9d97146a4b8a38495e24301537ac4" + }, + { + "path": "skills/nuxt/SKILL.md", + "sha256": "d5c80930f725ce0ea65bdabb4ffe297fee73fd314be87b8aec8fce1fa56b521e" + }, + { + "path": "skills/nuxt/references/tailwind.md", + "sha256": "4fa934006a2c52fc99326c4639cf8fdd585e9a7503dc86c350b064421badf044" + }, + { + "path": "skills/nuxt/references/nuxt-i18n.md", + "sha256": "f6d8a1c722b11932ff7d533586432dd734f8bc6088009df9770e6f2e170acd0e" + }, + { + "path": "skills/nuxt/references/vueuse.md", + "sha256": "e571d8079e0e328cf43323414e1ed96d7118bf157068f2b64a1afa55d235f256" + }, + { + "path": "skills/nuxt/references/nuxt-ui.md", + "sha256": "0e9c12178a1c27825d261187dfbf3941b02a94bff5dcee8b099aa653aecb1ebd" + }, + { + "path": "skills/nuxt/references/nuxt-patterns.md", + "sha256": "a54b6ae1da4f4c538b5ec618b34dfa035a5fc616f2b633095708872fc618a0c0" + }, + { + "path": "skills/nuxt/references/drizzle-db0.md", + "sha256": "ade751a4f2fa0be17f969dbafb8675b727e3b34c2f69b1c1c715bcfa9d81ec75" + }, + { + "path": "skills/nuxt/references/nuxt-modules.md", + "sha256": "60bdc911bc49a56f8abdc50f2ed60107a4a597ccc07aeca149624c15c5dbf2d0" + }, + { + "path": "skills/nuxt/references/nuxt-image.md", + "sha256": "b5260c00c65c3109813dcf16991b7744f1e216cc26a6213fab05cec00987c404" + }, + { + "path": "skills/nuxt/references/vue-best-practices.md", + "sha256": "fb076419a4e5dd9de6c969dedd6968371043a8fa518138ae993aa9dc8a1a02ef" + }, + { + "path": "skills/nuxt/references/nuxt-content.md", + "sha256": "2edfeb5744ce32ef392b652914e55dc275fb279e46d71458c85c9392a4228b43" + }, + { + "path": "skills/nuxt/references/pinia.md", + "sha256": "4dfd9a2088b1514060dc253becd026003ec745039b1a23687f90a9190ca1cb6e" + } + ], + "dirSha256": "d1094c64598ae325003608e536199af804cad0457de4d4bd64963d1ee8becbaf" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/nuxt/SKILL.md b/skills/nuxt/SKILL.md new file mode 100644 index 0000000..792f1b8 --- /dev/null +++ b/skills/nuxt/SKILL.md @@ -0,0 +1,313 @@ +--- +name: nuxt +description: This skill should be used when working on Nuxt projects (v3+). Use it for building Vue applications with Nuxt's file-based routing, server-side rendering, and auto-import features. Trigger when working with .vue or .ts files in Nuxt directories (pages/, server/, composables/, middleware/), nuxt.config.ts, or when the project contains a nuxt dependency in package.json. Also trigger for questions about Nuxt concepts like composables, auto-imports, server routes, SSR/SSG patterns, or file-based routing. +--- + +# Nuxt Development + +## Overview + +This skill provides specialized guidance for developing Nuxt applications (v3+), including Vue best practices, Nuxt-specific conventions, ecosystem library knowledge, and access to up-to-date documentation. + +## When to Use This Skill + +Trigger this skill when: + +- Working in a project with `nuxt` as a dependency in package.json +- Creating or editing `.vue` single-file components +- Working with `.ts` or `.tsx` files in Nuxt directories: `pages/`, `server/`, `composables/`, `middleware/`, `layouts/`, or `utils/` +- Working with Nuxt-specific files: `nuxt.config.ts`, `app.vue`, or any file in Nuxt convention directories +- Questions about Nuxt architecture, routing, or SSR/SSG patterns +- User mentions Nuxt-specific concepts: composables, auto-imports, server routes, server API, middleware, file-based routing +- Debugging Nuxt-specific issues or errors + +## Documentation Access + +### Official Nuxt Documentation + +Fetch up-to-date Nuxt documentation when needed: + +``` +https://nuxt.com/llms.txt +``` + +Fetch when: + +- Uncertain about current Nuxt API syntax or conventions +- User asks about specific Nuxt features or modules +- Working with recently released Nuxt features +- Encountering Nuxt-specific errors or configuration issues +- Need to verify patterns work with the specific Nuxt version in use + +## Quick Reference + +### Auto-Imported APIs (No Import Needed) + +Nuxt automatically imports these without explicit import statements: + +**Vue APIs:** `ref`, `reactive`, `computed`, `watch`, `onMounted`, `defineProps`, `defineEmits`, `defineModel` + +**Nuxt Composables:** `useState`, `useFetch`, `useAsyncData`, `useRoute`, `useRouter`, `navigateTo`, `useCookie`, `useHead`, `useSeoMeta`, `useRuntimeConfig`, `showError`, `clearError` + +**Auto-imports:** + +- Components from `components/` directory +- Composables from `composables/` directory +- Server utilities from `server/utils/` directory + +### File-Based Conventions + +**Routing:** + +- `pages/index.vue` → `/` +- `pages/about.vue` → `/about` +- `pages/users/[id].vue` → `/users/:id` (dynamic route) + +**Server API:** + +- `server/api/users.get.ts` → `/api/users` (GET endpoint) +- `server/api/users.post.ts` → `/api/users` (POST endpoint) +- `server/routes/healthz.ts` → `/healthz` (custom route) + +**Layouts & Middleware:** + +- `layouts/default.vue` - Default layout +- `middleware/auth.ts` - Named middleware (use via `definePageMeta({ middleware: 'auth' })`) +- `middleware/analytics.global.ts` - Global middleware (runs on every route) + +### Nuxt CLI Commands + +**Development:** + +- `nuxt dev` - Start development server +- `nuxt dev --host` - Expose to network + +**Building:** + +- `nuxt build` - Production build +- `nuxt generate` - Static site generation +- `nuxt preview` - Preview production build + +**Analysis:** + +- `nuxt analyze` - Bundle size analysis +- `nuxt typecheck` - Type checking +- `nuxt info` - Environment info for bug reports + +## Project Dependency Detection + +**Important:** Before providing library-specific guidance, check if the library is installed by examining `package.json`. Only include library-specific advice for dependencies that exist in the project. + +### Core Libraries (Always Available) + +- **Vue** - Component framework (auto-imported) +- **Vue Router** - Routing (file-based, managed by Nuxt) +- **Nitro** - Server engine (built into Nuxt) + +### Optional Libraries to Check + +Check `package.json` for these before suggesting their features: + +**State & Utilities:** + +- `pinia` or `@pinia/nuxt` - State management → See `references/pinia.md` +- `@vueuse/core` or `@vueuse/nuxt` - Composition utilities → See `references/vueuse.md` +- `drizzle-orm` - Database ORM → See `references/drizzle-db0.md` + +**Core Nuxt Modules (Dedicated References):** + +- `@nuxt/ui` - UI component library → See `references/nuxt-ui.md` +- `@nuxt/image` - Image optimization → See `references/nuxt-image.md` +- `@nuxt/content` - File-based CMS → See `references/nuxt-content.md` +- `@nuxtjs/i18n` - Internationalization → See `references/nuxt-i18n.md` +- `@nuxtjs/tailwindcss` - Tailwind CSS → See `references/tailwind.md` + +**Other Nuxt Modules:** + +- `@nuxt/icon`, `@nuxtjs/seo`, `@nuxtjs/color-mode` → See `references/nuxt-modules.md` +- `@nuxt/eslint`, `@nuxt/fonts`, `@nuxt/scripts`, `nuxt-security` → See `references/nuxt-modules.md` + +## References + +This skill includes detailed reference documentation for specific topics. Load these files as needed when working with specific features: + +### Core Best Practices + +**`references/vue-best-practices.md`** - Vue component patterns and conventions + +- Use when writing or reviewing Vue components +- Covers: Script setup syntax, props/emits/v-model, component structure, template directives, reactivity patterns + +**`references/nuxt-patterns.md`** - Common Nuxt patterns and recipes + +- Use when implementing features or solving common tasks +- Covers: Data fetching, SEO/meta tags, error handling, environment config, server API routes, middleware, state management, composables, layouts, plugins + +### Core Nuxt Modules (Comprehensive Documentation) + +**`references/nuxt-ui.md`** - Nuxt UI component library (Last updated: 2025-01) + +- Only use if `@nuxt/ui` is installed +- Covers: v3/v4 setup and migration, components (forms, buttons, modals, tables), Tailwind v4 integration, theming, validation, troubleshooting +- **Important:** Includes version-specific breaking changes and setup requirements + +**`references/tailwind.md`** - Tailwind CSS in Nuxt (Last updated: 2025-01) + +- Only use if `@nuxtjs/tailwindcss` is installed +- Covers: v3/v4 setup, configuration, responsive design, dark mode, custom utilities, plugins, JIT mode, performance optimization + +**`references/nuxt-image.md`** - Image optimization (Last updated: 2025-01) + +- Only use if `@nuxt/image` is installed +- Covers: NuxtImg/NuxtPicture components, image providers, lazy loading, responsive images, performance optimization + +**`references/nuxt-content.md`** - File-based CMS (Last updated: 2025-01) + +- Only use if `@nuxt/content` is installed +- Covers: Markdown/YAML content, queryContent API, components (ContentDoc, ContentList), navigation, search, pagination, syntax highlighting + +**`references/nuxt-i18n.md`** - Internationalization (Last updated: 2025-01) + +- Only use if `@nuxtjs/i18n` is installed +- Covers: Multi-language routing, translations, locale switching, SEO, number/date formatting, pluralization, composables + +### State Management & Utilities + +**`references/pinia.md`** - Pinia state management + +- Only use if `pinia` or `@pinia/nuxt` is installed +- Covers: Store definition, component usage, SSR, persistence, testing + +**`references/vueuse.md`** - VueUse composables + +- Only use if `@vueuse/core` or `@vueuse/nuxt` is installed +- Covers: State management composables, browser APIs, element interaction, utilities, common patterns + +**`references/drizzle-db0.md`** - Database with Drizzle ORM + +- Only use if `drizzle-orm` is installed +- Covers: Setup, schema definition, CRUD operations, queries, joins, filtering, transactions, migrations, type safety + +### Other Modules + +**`references/nuxt-modules.md`** - Other official Nuxt modules + +- Brief overview of: @nuxt/icon, @nuxtjs/seo, @nuxtjs/color-mode, @nuxt/eslint, @nuxt/fonts, @nuxt/scripts, nuxt-security +- For detailed guidance on @nuxt/ui, @nuxt/image, @nuxt/content, @nuxtjs/i18n, or @nuxtjs/tailwindcss, use their dedicated reference files instead + +## How to Use This Skill + +1. **Check dependencies** - Always examine `package.json` first to know what libraries are available +2. **Follow Vue best practices** - Apply patterns from `vue-best-practices.md` to all component code +3. **Leverage auto-imports** - Never manually import Nuxt/Vue composables that are auto-imported +4. **Use file-based conventions** - Follow Nuxt's directory structure for routing, APIs, and middleware +5. **Reference library docs** - When a library is installed, consult its reference file for specific patterns +6. **Verify version-specific features** - Reference files include "Last updated" dates; always verify with official docs for version-specific details +7. **Fetch official docs** - For recent features or uncertainty, fetch from https://nuxt.com/llms.txt or module-specific documentation URLs + +### Version-Specific Information + +- **Reference files with dates** (marked "Last updated: YYYY-MM") contain version-specific info +- **Always verify with official docs** when: + - Working with modules not documented in references + - Module version differs significantly from documentation date + - Encountering breaking changes or migration scenarios + - Uncertain about syntax or API for current version +- **Fallback principle:** If reference documentation doesn't match project needs, fetch official docs rather than guessing + +## Important Conventions + +### Component Files Must: + +- Use ` + + +``` + +### Query Methods + +#### all() + +Get all matching documents: + +```typescript +const posts = await queryCollection("blog").all() +``` + +#### first() + +Get first matching document: + +```typescript +const post = await queryCollection("blog").path("/blog/my-post").first() +``` + +#### where() + +Filter by field with SQL operators: + +```typescript +// Single condition +const published = await queryCollection("blog").where("draft", "=", false).all() + +// Multiple conditions (AND) +const filtered = await queryCollection("blog") + .where("draft", "=", false) + .where("category", "=", "tech") + .all() +``` + +#### andWhere() + +Complex AND conditions: + +```typescript +const posts = await queryCollection("blog") + .where("published", "=", true) + .andWhere((query) => + query.where("date", ">", "2024-01-01").where("category", "=", "news"), + ) + .all() +``` + +#### orWhere() + +OR conditions: + +```typescript +const posts = await queryCollection("blog") + .where("published", "=", true) + .orWhere((query) => + query.where("featured", "=", true).where("priority", ">", 5), + ) + .all() +``` + +#### order() + +Sort results: + +```typescript +// Descending +const posts = await queryCollection("blog").order("date", "DESC").all() + +// Ascending +const posts = await queryCollection("blog").order("title", "ASC").all() +``` + +#### limit() + +Limit results: + +```typescript +const latest = await queryCollection("blog") + .order("date", "DESC") + .limit(5) + .all() +``` + +#### skip() + +Skip results (for pagination): + +```typescript +const page = 2 +const perPage = 10 + +const posts = await queryCollection("blog") + .order("date", "DESC") + .skip((page - 1) * perPage) + .limit(perPage) + .all() +``` + +#### select() + +Select specific fields: + +```typescript +const posts = await queryCollection("blog") + .select(["title", "description", "date", "path"]) + .all() +``` + +#### path() + +Filter by path: + +```typescript +const post = await queryCollection("blog").path("/blog/my-post").first() +``` + +## ContentRenderer Component + +Renders parsed markdown content (the main component for v3): + +```vue + + + +``` + +With error handling: + +```vue + + + +``` + +## Common Patterns + +### Blog List Page + +```vue + + + +``` + +### Single Blog Post with Related Articles + +```vue + + + +``` + +### Documentation Site + +```vue + + + + +``` + +### Search + +```vue + + + +``` + +### Pagination + +```vue + + + +``` + +### Filter by Tags/Categories + +```vue + + + +``` + +## Server-Side Queries + +Use `queryCollection` in API routes with event parameter: + +```typescript +// server/api/posts.get.ts +export default defineEventHandler(async (event) => { + const posts = await queryCollection(event, "blog") + .where("draft", "=", false) + .order("date", "DESC") + .limit(10) + .all() + + return posts +}) +``` + +With query parameters: + +```typescript +// server/api/posts/[slug].get.ts +export default defineEventHandler(async (event) => { + const slug = getRouterParam(event, "slug") + + const post = await queryCollection(event, "blog") + .path(`/blog/${slug}`) + .first() + + if (!post) { + throw createError({ + statusCode: 404, + message: "Post not found", + }) + } + + return post +}) +``` + +## Navigation + +Use `queryCollectionNavigation` for generating navigation: + +```vue + + + +``` + +## Markdown Features + +### Syntax Highlighting + +Configure in `nuxt.config.ts`: + +```typescript +export default defineNuxtConfig({ + content: { + highlight: { + theme: { + default: "github-light", + dark: "github-dark", + }, + preload: ["typescript", "vue", "bash", "json"], + }, + }, +}) +``` + +Use in markdown: + +````markdown +```typescript +interface User { + name: string + age: number +} +``` +```` + +### Custom Vue Components in Markdown + +Use Vue components directly in markdown (MDC syntax): + +```markdown +# My Article + +::Alert{type="info"} +This is an informational alert! +:: + +::CallToAction{title="Get Started" url="/docs"} +Learn more about Nuxt Content +:: +``` + +Register components in `components/content/` directory: + +```vue + + + + +``` + +## TypeScript Support + +Type your collections: + +```typescript +// types/content.ts +export interface BlogPost { + title: string + description: string + date: string + tags?: string[] + author?: string + image?: string + draft: boolean + path: string + body: any +} +``` + +Use in components: + +```vue + +``` + +## Migration from v2 + +### Key Changes + +1. **`queryContent()` → `queryCollection(name)`** + + ```typescript + // v2 + queryContent("/blog").find() + + // v3 + queryCollection("blog").all() + ``` + +2. **Collections must be defined in `content.config.ts`** + +3. **Components removed:** + - ❌ `` - use ContentRenderer + - ❌ `` - query manually with queryCollection + - ❌ `` - use queryCollectionNavigation + - ❌ `` - use queryCollection + +4. **New query methods:** + - `.all()` instead of `.find()` + - `.first()` instead of `.findOne()` + - `.where()` with SQL operators + - `.order()` instead of `.sort()` + +5. **SQL-backed storage** - faster queries for large datasets + +## Best Practices + +1. **Define collections** - Always create `content.config.ts` with schemas +2. **Use TypeScript** - Type your collections for better DX +3. **Cache queries** - Use `useAsyncData` with proper keys +4. **Server-side queries** - Query on server for API routes +5. **Index for performance** - Consider indexing frequently queried fields +6. **Validate frontmatter** - Use Zod schemas in collection definitions +7. **Handle 404s** - Always check if content exists and throw errors +8. **Use path()** - More efficient than where() for path filtering +9. **Select fields** - Use `.select()` to reduce payload size +10. **Pagination** - Implement for large collections + +## Official Resources + +- **Documentation:** https://content.nuxt.com +- **Collections:** https://content.nuxt.com/docs/collections +- **Query API:** https://content.nuxt.com/docs/utils/query-collection +- **Migration Guide:** https://content.nuxt.com/docs/getting-started/migration +- **GitHub:** https://github.com/nuxt/content + +--- + +**Note:** This reference covers Nuxt Content v3. The v2 API (`queryContent`, `ContentDoc`, `ContentList`) is deprecated and not compatible with v3. diff --git a/skills/nuxt/references/nuxt-i18n.md b/skills/nuxt/references/nuxt-i18n.md new file mode 100644 index 0000000..a888479 --- /dev/null +++ b/skills/nuxt/references/nuxt-i18n.md @@ -0,0 +1,757 @@ +# Nuxt I18n Reference + +**Last Updated:** 2025-11 (v10.2.0) + +**Check:** `@nuxtjs/i18n` in package.json + +Nuxt I18n v10 provides internationalization (i18n) for Nuxt applications with auto-imports, locale routing, SEO support, and integration with Vue I18n v11. + +## Installation & Setup + +```bash +pnpm add @nuxtjs/i18n +``` + +**nuxt.config.ts:** + +```typescript +export default defineNuxtConfig({ + modules: ["@nuxtjs/i18n"], + + i18n: { + locales: [ + { code: "en", iso: "en-US", name: "English", file: "en.json" }, + { code: "fr", iso: "fr-FR", name: "Français", file: "fr.json" }, + { code: "es", iso: "es-ES", name: "Español", file: "es.json" }, + ], + defaultLocale: "en", + langDir: "locales/", + strategy: "prefix_except_default", // or 'prefix', 'no_prefix' + detectBrowserLanguage: { + useCookie: true, + cookieKey: "i18n_redirected", + redirectOn: "root", + }, + }, +}) +``` + +## Directory Structure + +``` +locales/ +├── en.json +├── fr.json +└── es.json +``` + +**Example locale file (en.json):** + +```json +{ + "welcome": "Welcome", + "hello": "Hello {name}", + "nav": { + "home": "Home", + "about": "About", + "contact": "Contact" + }, + "products": { + "title": "Our Products", + "description": "Browse our collection" + } +} +``` + +## Basic Usage + +### Translation in Templates + +```vue + +``` + +### Translation in Script + +```vue + +``` + +## Routing + +### Route Strategies + +**prefix_except_default** (recommended): + +- Default locale: `/about` +- Other locales: `/fr/about`, `/es/about` + +**prefix**: + +- All locales: `/en/about`, `/fr/about`, `/es/about` + +**no_prefix**: + +- All locales use same path: `/about` +- Locale detected from cookie/browser + +### Locale Links + +```vue + + + +``` + +### Programmatic Navigation + +```vue + +``` + +## Composables + +### useI18n + +Main composable for translations: + +```vue + +``` + +### useLocalePath + +Generate localized paths: + +```vue + +``` + +### useSwitchLocalePath + +Generate paths for locale switching: + +```vue + +``` + +### useRouteBaseName + +Get route name without locale prefix: + +```vue + +``` + +### useBrowserLocale + +Detect browser locale: + +```vue + +``` + +## Common Patterns + +### Language Switcher + +```vue + + + +``` + +### Dropdown Language Switcher + +```vue + + + +``` + +### SEO with I18n + +```vue + +``` + +### Per-Page Translations + +```vue + + + + + +{ + "en": { + "title": "About Us", + "description": "Learn more about our company" + }, + "fr": { + "title": "À propos", + "description": "En savoir plus sur notre entreprise" + } +} + +``` + +### Dynamic Content Translation + +```vue + + + +``` + +### Lazy Loading Translations + +For large translation files: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + i18n: { + lazy: true, + langDir: "locales/", + locales: [ + { code: "en", file: "en.json" }, + { code: "fr", file: "fr.json" }, + { code: "es", file: "es.json" }, + ], + }, +}) +``` + +## Number & Date Formatting + +### Number Formatting + +Define formats in config: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + i18n: { + numberFormats: { + en: { + currency: { + style: "currency", + currency: "USD", + }, + decimal: { + style: "decimal", + minimumFractionDigits: 2, + }, + }, + fr: { + currency: { + style: "currency", + currency: "EUR", + }, + decimal: { + style: "decimal", + minimumFractionDigits: 2, + }, + }, + }, + }, +}) +``` + +Usage: + +```vue + +``` + +### Date Formatting + +Define formats in config: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + i18n: { + datetimeFormats: { + en: { + short: { + year: "numeric", + month: "short", + day: "numeric", + }, + long: { + year: "numeric", + month: "long", + day: "numeric", + weekday: "long", + }, + }, + fr: { + short: { + year: "numeric", + month: "short", + day: "numeric", + }, + long: { + year: "numeric", + month: "long", + day: "numeric", + weekday: "long", + }, + }, + }, + }, +}) +``` + +Usage: + +```vue + + + +``` + +## Pluralization + +**Translation file:** + +```json +{ + "items": "no items | one item | {count} items", + "cart": "You have {n} item in your cart | You have {n} items in your cart" +} +``` + +**Usage:** + +```vue + +``` + +## TypeScript Support + +### Typed Translations + +```typescript +// types/i18n.ts +export interface LocaleMessages { + welcome: string + hello: (params: { name: string }) => string + nav: { + home: string + about: string + contact: string + } +} +``` + +Usage: + +```vue + +``` + +## API Routes with I18n + +```typescript +// server/api/products.get.ts +export default defineEventHandler(async (event) => { + const locale = getCookie(event, "i18n_redirected") || "en" + + const products = await db.products.findMany({ + include: { + translations: { + where: { locale }, + }, + }, + }) + + return products.map((product) => ({ + ...product, + name: product.translations[0]?.name || product.name, + description: product.translations[0]?.description || product.description, + })) +}) +``` + +## Best Practices + +1. **Use prefix_except_default strategy** - Better UX for default locale users +2. **Enable browser detection** - Auto-redirect to user's preferred language +3. **Provide language switcher** - Always visible on every page +4. **Use lazy loading for large apps** - Load translations on demand +5. **Organize translations by feature** - Use nested keys (`nav.home`, `products.title`) +6. **Include locale in SEO** - Use `hreflang` links and `og:locale` +7. **Handle missing translations** - Provide fallback locale +8. **Use placeholders consistently** - `{name}` for simple, `{n}` for pluralization +9. **Test all locales** - Verify layout with longer translations (German, French) +10. **Keep keys consistent** - Same structure across all locale files + +## Troubleshooting + +### Translations Not Loading + +Check: + +1. `@nuxtjs/i18n` in `modules` array +2. Locale files in correct `langDir` path +3. File names match `file` property in config +4. JSON is valid (no trailing commas) + +### Routes Not Localized + +1. Verify `strategy` is set correctly +2. Check `defaultLocale` matches one of your locales +3. Ensure you're using `localePath()` for links + +### Browser Detection Not Working + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + i18n: { + detectBrowserLanguage: { + useCookie: true, + cookieKey: "i18n_redirected", + redirectOn: "root", + alwaysRedirect: true, + fallbackLocale: "en", + }, + }, +}) +``` + +### Missing Translations + +Enable warnings in development: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + i18n: { + compilation: { + strictMessage: false, + }, + vueI18n: "./i18n.config.ts", + }, +}) +``` + +```typescript +// i18n.config.ts +export default { + legacy: false, + locale: "en", + missingWarn: true, + fallbackWarn: true, +} +``` + +## v10 Changes from v9 + +### Key Updates + +1. **Vue I18n v11** - Upgraded from v10 with JIT compilation as default +2. **Improved Nuxt 4 Support** - Better compatibility with Nuxt 4 +3. **Custom Routes** - Use `definePageMeta` for per-page locale configuration +4. **Server-Side Redirects** - Improved server-side redirection behavior +5. **Strict SEO** - Experimental strict SEO head management +6. **Fixed Behaviors** - `strategy` and `redirectOn` combinations now work as expected + +### Migration Notes + +- `$tc()` API integrated into `$t()` (from Vue I18n v10→v11 upgrade) +- JIT compilation now default (no need for `jit` option) +- New directory structure: i18n files resolved from `/i18n` (configurable with `restructureDir`) +- Context functions require `$` prefix: use `$localePath()` not `localePath()` in templates + +## Official Resources + +- **Documentation:** https://i18n.nuxtjs.org +- **Migration Guide:** https://i18n.nuxtjs.org/docs/guide/migrating +- **API Reference:** https://i18n.nuxtjs.org/api +- **Vue I18n Docs:** https://vue-i18n.intlify.dev +- **Examples:** https://i18n.nuxtjs.org/examples +- **GitHub:** https://github.com/nuxt-modules/i18n + +--- + +**Note:** This reference covers Nuxt I18n v10 (latest as of 2025-11) with Vue I18n v11. For v9 projects, consult the migration guide. diff --git a/skills/nuxt/references/nuxt-image.md b/skills/nuxt/references/nuxt-image.md new file mode 100644 index 0000000..121bd57 --- /dev/null +++ b/skills/nuxt/references/nuxt-image.md @@ -0,0 +1,595 @@ +# Nuxt Image Reference + +**Last Updated:** 2025-11 + +**Check:** `@nuxt/image` in package.json + +Nuxt Image is an image optimization module that provides automatic image optimization, lazy loading, responsive images, and support for multiple image providers. + +## Installation & Setup + +```bash +pnpm add @nuxt/image +``` + +**nuxt.config.ts:** + +```typescript +export default defineNuxtConfig({ + modules: ["@nuxt/image"], + + image: { + // Optional configuration + quality: 80, + formats: ["webp", "avif"], + + // Image providers + providers: { + cloudinary: { + baseURL: "https://res.cloudinary.com/{your-cloud-name}/image/upload/", + }, + }, + }, +}) +``` + +## Core Components + +### NuxtImg + +Basic optimized image component: + +```vue + +``` + +**Common Props:** + +- `src` - Image source (path or URL) +- `alt` - Alt text for accessibility +- `width` / `height` - Dimensions +- `loading` - `"lazy"` (default) or `"eager"` +- `fit` - `"cover"`, `"contain"`, `"fill"`, `"inside"`, `"outside"` +- `format` - `"webp"`, `"avif"`, `"jpg"`, `"png"` +- `quality` - Image quality (0-100) +- `provider` - Image provider to use + +### NuxtPicture + +Responsive image with multiple formats: + +```vue + +``` + +**Benefits:** + +- Automatically generates multiple formats (WebP, AVIF) +- Creates responsive srcset for different screen sizes +- Better browser compatibility with fallbacks + +## Common Patterns + +### Responsive Images + +```vue + +``` + +### Image with Loading States + +```vue + + + +``` + +### Background Images + +```vue + +``` + +### Image with Different Formats + +```vue + +``` + +### Fit Options + +```vue + +``` + +## Image Providers + +### Local Provider (Default) + +For images in `public/` directory: + +```vue + +``` + +### External URLs + +```vue + +``` + +### Cloudinary + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + image: { + cloudinary: { + baseURL: "https://res.cloudinary.com/{your-cloud-name}/image/upload/", + }, + }, +}) +``` + +```vue + +``` + +### Vercel / Netlify + +Automatically detected and configured when deployed: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + image: { + provider: "vercel", // or 'netlify' + }, +}) +``` + +### Custom Provider + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + image: { + providers: { + custom: { + provider: "~/providers/custom-provider.ts", + options: { + baseURL: "https://cdn.example.com", + }, + }, + }, + }, +}) +``` + +```typescript +// providers/custom-provider.ts +import { joinURL } from "ufo" +import type { ProviderGetImage } from "@nuxt/image" + +export const getImage: ProviderGetImage = (src, { modifiers, baseURL }) => { + const { width, height, format, quality } = modifiers + const url = joinURL(baseURL, src) + + return { + url: `${url}?w=${width}&h=${height}&fm=${format}&q=${quality}`, + } +} +``` + +## Composables + +### $img Helper + +Generate image URLs programmatically: + +```vue + + + +``` + +## Performance Optimization + +### Lazy Loading (Default) + +Images are lazy-loaded by default: + +```vue + +``` + +### Preload Critical Images + +```vue + + + +``` + +### Placeholder / Blur + +```vue + +``` + +### Image Sizes + +Specify responsive sizes for optimal loading: + +```vue + +``` + +## Advanced Usage + +### Modifiers Object + +Pass multiple modifiers: + +```vue + + + +``` + +### Dynamic Sources + +```vue + + + +``` + +### Image Gallery + +```vue + + + +``` + +### Art Direction + +Different images for different screen sizes: + +```vue + +``` + +## Configuration Reference + +### Global Configuration + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + image: { + // Default quality + quality: 80, + + // Default formats + formats: ["webp", "avif", "jpg"], + + // Image sizes for responsive images + screens: { + xs: 320, + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + xxl: 1536, + "2xl": 1536, + }, + + // Provider configuration + provider: "cloudinary", + providers: { + cloudinary: { + baseURL: "https://res.cloudinary.com/{cloud-name}/image/upload/", + }, + }, + + // IPX options (local provider) + ipx: { + maxAge: 60 * 60 * 24 * 365, // 1 year cache + }, + + // Presets + presets: { + avatar: { + modifiers: { + format: "webp", + width: 100, + height: 100, + fit: "cover", + }, + }, + thumbnail: { + modifiers: { + format: "webp", + width: 300, + height: 200, + fit: "cover", + }, + }, + }, + }, +}) +``` + +### Using Presets + +```vue + +``` + +## Best Practices + +1. **Always include alt text** - Essential for accessibility +2. **Use NuxtPicture for hero images** - Better format support and responsiveness +3. **Specify dimensions** - Prevents layout shift +4. **Lazy load by default** - Except above-the-fold images +5. **Use appropriate fit** - `cover` for thumbnails, `contain` for products +6. **Optimize quality** - 80-85 is usually sufficient +7. **Leverage providers** - Use CDN providers for external images +8. **Use presets** - Define common image styles once +9. **Test on slow networks** - Verify lazy loading and placeholders work +10. **Prefer WebP/AVIF** - Modern formats for better compression + +## Troubleshooting + +### Images Not Optimizing + +Check: + +1. `@nuxt/image` is in `nuxt.config.ts` modules +2. Images are in `public/` directory for local provider +3. Provider is correctly configured +4. Development server was restarted after config changes + +### Images Not Loading + +1. Verify src path is correct +2. Check provider baseURL configuration +3. Ensure CORS is configured for external images +4. Check network tab for 404/403 errors + +### Poor Performance + +1. Enable lazy loading (default) +2. Use appropriate image sizes +3. Implement placeholders +4. Use WebP/AVIF formats +5. Configure CDN caching + +### Layout Shift + +Always specify width and height: + +```vue + +``` + +## Official Resources + +- **Documentation:** https://image.nuxt.com +- **Providers:** https://image.nuxt.com/providers +- **API Reference:** https://image.nuxt.com/api +- **GitHub:** https://github.com/nuxt/image + +--- + +**Note:** Always verify provider-specific features with official documentation. Image optimization strategies may vary by provider. diff --git a/skills/nuxt/references/nuxt-modules.md b/skills/nuxt/references/nuxt-modules.md new file mode 100644 index 0000000..383993f --- /dev/null +++ b/skills/nuxt/references/nuxt-modules.md @@ -0,0 +1,161 @@ +# Nuxt Modules Reference + +Check package.json for installed modules before suggesting module-specific features. + +## Image Optimization (@nuxt/image) + +**Check:** `@nuxt/image` in package.json + +Provides optimized image components with automatic format conversion and responsive images. For comprehensive guidance, see `references/nuxt-image.md`. + +## Content Management (@nuxt/content) + +**Check:** `@nuxt/content` in package.json + +File-based CMS for Nuxt with markdown, YAML, and JSON support. For comprehensive guidance including queries, components, and content navigation, see `references/nuxt-content.md`. + +## Icon Management (@nuxt/icon) + +**Check:** `@nuxt/icon` in package.json + +### Icon Component + +```vue + + +``` + +## UI Components (@nuxt/ui) + +**Check:** `@nuxt/ui` in package.json + +Provides pre-built components with Tailwind CSS. For comprehensive Nuxt UI guidance including setup, v3/v4 differences, and migration, see `references/nuxt-ui.md`. + +## SEO (@nuxtjs/seo) + +**Check:** `@nuxtjs/seo` in package.json + +Provides SEO utilities including sitemap, robots.txt, and OG images. + +```typescript +// Auto-generates sitemap +// Configure in nuxt.config.ts +export default defineNuxtConfig({ + site: { + url: "https://example.com", + name: "My Site", + }, +}) +``` + +## Internationalization (@nuxtjs/i18n) + +**Check:** `@nuxtjs/i18n` in package.json + +Provides internationalization with auto-imports, locale routing, and SEO support. For comprehensive guidance including setup, translations, and routing, see `references/nuxt-i18n.md`. + +## Tailwind CSS (@nuxtjs/tailwindcss) + +**Check:** `@nuxtjs/tailwindcss` in package.json + +Auto-configured Tailwind CSS integration. For comprehensive guidance including setup, configuration, dark mode, and best practices, see `references/tailwind.md`. + +## Color Mode (@nuxtjs/color-mode) + +**Check:** `@nuxtjs/color-mode` in package.json + +### Toggle Dark Mode + +```vue + + + +``` + +## ESLint (@nuxt/eslint) + +**Check:** `@nuxt/eslint` in package.json + +Provides auto-configured ESLint with Nuxt-specific rules. Configuration in `eslint.config.mjs`: + +```javascript +import { createConfigForNuxt } from "@nuxt/eslint-config/flat" + +export default createConfigForNuxt({ + features: { + stylistic: true, + }, +}) +``` + +## Security (nuxt-security) + +**Check:** `nuxt-security` in package.json + +### Security Headers + +Automatically applies security headers. Configure in nuxt.config.ts: + +```typescript +export default defineNuxtConfig({ + security: { + headers: { + contentSecurityPolicy: { + "img-src": ["'self'", "https:", "data:"], + }, + }, + rateLimiter: { + tokensPerInterval: 150, + interval: "hour", + }, + }, +}) +``` + +## Fonts (@nuxt/fonts) + +**Check:** `@nuxt/fonts` in package.json + +Automatically optimizes and loads fonts: + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + fonts: { + families: [{ name: "Inter", provider: "google" }], + }, +}) +``` + +```vue + +``` + +## Scripts (@nuxt/scripts) + +**Check:** `@nuxt/scripts` in package.json + +Load third-party scripts efficiently: + +```vue + +``` diff --git a/skills/nuxt/references/nuxt-patterns.md b/skills/nuxt/references/nuxt-patterns.md new file mode 100644 index 0000000..b3a9683 --- /dev/null +++ b/skills/nuxt/references/nuxt-patterns.md @@ -0,0 +1,499 @@ +# Nuxt Common Patterns + +## Data Fetching + +### useFetch for API Calls + +Use `useFetch` for API endpoints. It runs on both server and client, with automatic hydration. + +```typescript +const { data, status, error, refresh } = await useFetch("/api/users") + +// With query parameters +const { data, status } = await useFetch("/api/users", { + query: { limit: 10, page: 1 }, +}) + +// With type safety +interface User { + id: number + name: string +} +const { data, status, error } = await useFetch("/api/users") +``` + +**Always handle all states in templates:** + +```vue + +``` + +### useAsyncData for Complex Data + +Use `useAsyncData` when you need more control or complex transformations. + +```typescript +const { data, status, error } = await useAsyncData("users", async () => { + const users = await $fetch("/api/users") + const stats = await $fetch("/api/stats") + return { users, stats } +}) + +// With caching key +const { data, status, error } = await useAsyncData(`user-${id}`, () => + $fetch(`/api/users/${id}`), +) +``` + +### Lazy Fetching + +Use lazy variants when you don't want to block navigation: + +```typescript +// Non-blocking +const { status, data } = await useLazyFetch('/api/users') + +// Show loading state +
    Loading...
    +
    {{ data }}
    +``` + +### Client-Only Fetching + +```typescript +const { data } = await useFetch("/api/users", { + server: false, // Only fetch on client +}) +``` + +### Refresh and Refetch + +```typescript +const { data, status, refresh } = await useFetch("/api/users") + +// Manually refetch +await refresh() + +// Refetch on event +watch(searchQuery, () => refresh()) +``` + +## SEO and Meta Tags + +### useHead + +```typescript +useHead({ + title: "My Page", + meta: [ + { name: "description", content: "Page description" }, + { property: "og:title", content: "My Page" }, + ], + link: [{ rel: "canonical", href: "https://example.com/page" }], +}) +``` + +### useSeoMeta (Type-Safe) + +```typescript +useSeoMeta({ + title: "My Page", + description: "Page description", + ogTitle: "My Page", + ogDescription: "Page description", + ogImage: "https://example.com/image.jpg", + twitterCard: "summary_large_image", +}) +``` + +### definePageMeta + +```typescript +definePageMeta({ + title: "User Profile", + description: "View user profile", + middleware: ["auth"], +}) +``` + +## Error Handling + +### Show Error Page + +```typescript +showError({ + statusCode: 404, + message: "Page not found", +}) + +// With custom error +showError({ + statusCode: 403, + message: "Access denied", + fatal: true, +}) +``` + +### Clear Error + +```typescript +clearError({ redirect: "/" }) +``` + +### Handle Errors in Data Fetching + +```typescript +const { data, status, error } = await useFetch("/api/users") + +if (error.value) { + showError({ + statusCode: error.value.statusCode, + message: error.value.message, + }) +} +``` + +### Error Component + +```vue + + + + +``` + +## Environment Variables and Config + +### Runtime Config + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + runtimeConfig: { + // Private (server-only) + apiSecret: process.env.API_SECRET, + databaseUrl: process.env.DATABASE_URL, + + // Public (exposed to client) + public: { + apiBase: process.env.API_BASE_URL || "http://localhost:3000", + environment: process.env.NODE_ENV, + }, + }, +}) + +// Usage +const config = useRuntimeConfig() +console.log(config.public.apiBase) // Available everywhere +console.log(config.apiSecret) // Server-only +``` + +### App Config + +For non-sensitive configuration that can be updated at runtime: + +```typescript +// app.config.ts +export default defineAppConfig({ + theme: { + primaryColor: "#3b82f6", + }, +}) + +// Usage +const appConfig = useAppConfig() +console.log(appConfig.theme.primaryColor) +``` + +## Server API Routes + +### GET Request + +```typescript +// server/api/users.get.ts +export default defineEventHandler(async (event) => { + const query = getQuery(event) + + return { + users: [ + { id: 1, name: "Alice" }, + { id: 2, name: "Bob" }, + ], + } +}) +``` + +### POST Request + +```typescript +// server/api/users.post.ts +export default defineEventHandler(async (event) => { + const body = await readBody(event) + + // Validate and save user + return { success: true, user: body } +}) +``` + +### Dynamic Routes + +```typescript +// server/api/users/[id].get.ts +export default defineEventHandler(async (event) => { + const id = getRouterParam(event, "id") + + // Fetch user by id + return { id, name: "User" } +}) +``` + +### Error Handling in API Routes + +```typescript +export default defineEventHandler(async (event) => { + try { + const data = await fetchData() + return data + } catch (error) { + throw createError({ + statusCode: 500, + message: "Internal server error", + }) + } +}) +``` + +### Protected API Routes + +```typescript +// server/api/admin/users.get.ts +export default defineEventHandler(async (event) => { + const session = await requireUserSession(event) + + if (!session.user.isAdmin) { + throw createError({ + statusCode: 403, + message: "Forbidden", + }) + } + + return { users: [] } +}) +``` + +## Middleware + +### Route Middleware + +```typescript +// middleware/auth.ts +export default defineNuxtRouteMiddleware((to, from) => { + const user = useState("user") + + if (!user.value) { + return navigateTo("/login") + } +}) + +// Usage in page +definePageMeta({ + middleware: "auth", +}) +``` + +### Global Middleware + +```typescript +// middleware/analytics.global.ts +export default defineNuxtRouteMiddleware((to, from) => { + // Track page view + console.log("Navigating to:", to.path) +}) +``` + +## State Management + +### useState + +For shared state across components: + +```typescript +// composables/useAuth.ts +export const useAuth = () => { + const user = useState("user", () => null) + const isAuthenticated = computed(() => !!user.value) + + async function login(credentials: LoginCredentials) { + const response = await $fetch("/api/auth/login", { + method: "POST", + body: credentials, + }) + user.value = response.user + } + + function logout() { + user.value = null + } + + return { + user, + isAuthenticated, + login, + logout, + } +} + +// Usage in component +const { user, login, logout } = useAuth() +``` + +## Composables + +### Auto-Import from composables/ + +```typescript +// composables/useCounter.ts +export const useCounter = () => { + const count = ref(0) + + function increment() { + count.value++ + } + + function decrement() { + count.value-- + } + + return { + count, + increment, + decrement, + } +} + +// Usage (auto-imported) +const { count, increment } = useCounter() +``` + +## Layouts + +### Default Layout + +```vue + + +``` + +### Custom Layout + +```vue + + + + + +``` + +### Dynamic Layout + +```typescript +setPageLayout("admin") +``` + +## Plugins + +### Client-Only Plugin + +```typescript +// plugins/analytics.client.ts +export default defineNuxtPlugin(() => { + // Only runs on client + console.log("Client-side analytics initialized") +}) +``` + +### Server-Only Plugin + +```typescript +// plugins/database.server.ts +export default defineNuxtPlugin(() => { + // Only runs on server + return { + provide: { + db: createDatabaseConnection(), + }, + } +}) +``` + +### Universal Plugin + +```typescript +// plugins/api.ts +export default defineNuxtPlugin(() => { + const api = $fetch.create({ + baseURL: "/api", + onResponseError({ response }) { + if (response.status === 401) { + navigateTo("/login") + } + }, + }) + + return { + provide: { + api, + }, + } +}) + +// Usage +const { $api } = useNuxtApp() +const data = await $api("/users") +``` diff --git a/skills/nuxt/references/nuxt-ui.md b/skills/nuxt/references/nuxt-ui.md new file mode 100644 index 0000000..dc37816 --- /dev/null +++ b/skills/nuxt/references/nuxt-ui.md @@ -0,0 +1,944 @@ +# Nuxt UI Reference + +**Last Updated:** 2025-11 (v4.1.0) + +**Check:** `@nuxt/ui` in package.json + +Nuxt UI is a component library built on Tailwind CSS that provides pre-built, accessible UI components for Nuxt applications. + +## Version Detection + +Check `package.json` to determine which version is installed: + +- **v4.x** - Latest version (Nuxt 4 required, Tailwind v4, breaking changes from v3) +- **v3.x** - Previous stable (Nuxt 3, Tailwind v3) +- **v2.x** - Legacy version (deprecated) + +For v4-specific features and migration, always verify with official docs: https://ui.nuxt.com + +## Installation & Setup + +### v4 Setup (Current) + +**Prerequisites:** + +- Nuxt 4+ +- Tailwind CSS v4 + +```bash +pnpm add @nuxt/ui +``` + +**Required Configuration:** + +1. **nuxt.config.ts:** + +```typescript +export default defineNuxtConfig({ + modules: ["@nuxt/ui"], + + // Optional: Configure color theme + colorMode: { + preference: "system", // 'light' | 'dark' | 'system' + }, +}) +``` + +2. **app.vue - UApp Wrapper (Required):** + +```vue + +``` + +3. **assets/css/main.css - Tailwind v4 Import:** + +```css +@import "tailwindcss"; +``` + +4. **app.config.ts - Color Configuration:** + +```typescript +export default defineAppConfig({ + ui: { + colors: { + primary: "blue", + neutral: "slate", + }, + }, +}) +``` + +### v3 Setup (Previous Version) + +```bash +pnpm add @nuxt/ui +``` + +```typescript +// nuxt.config.ts +export default defineNuxtConfig({ + modules: ["@nuxt/ui"], +}) +``` + +```css +/* assets/css/main.css */ +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +## Breaking Changes + +### v3 → v4 Migration + +**Component Renames:** + +- `UButtonGroup` → `UFieldGroup` +- `UFormGroup` → `UFieldGroup` +- `UVerticalNavigation` → `UNavigationTree` + +**Modal/Popover/Slideover Structure Changes:** +Major structural changes - trigger button now goes inside component, content uses `#content` slot: + +```vue + +Open + +
    Content
    +
    + + + + Open + + +``` + +**Composable Changes:** + +- `useModal()` → `useOverlay()` +- Overlays now close automatically on close events (no manual `.close()` needed) + +**Color System:** +Configure colors in `app.config.ts` instead of component props: + +```typescript +// app.config.ts (v4) +export default defineAppConfig({ + ui: { + colors: { + primary: "blue", // Changed from 'primary' prop on components + neutral: "slate", + }, + }, +}) +``` + +**Tailwind v4 Requirement:** + +- Must use `@import "tailwindcss"` in main.css +- Tailwind config now uses CSS-based configuration + +For complete migration details, always check: https://ui.nuxt.com/getting-started/migration + +### v2 → v3 Migration + +Major overhaul with many breaking changes. Recommend checking official migration guide for projects still on v2. + +## Common Components + +### Forms + +#### UInput + +```vue + + + +``` + +#### UTextarea + +```vue + + + +``` + +#### USelect + +```vue + + + +``` + +#### UCheckbox & URadio + +```vue + + + +``` + +#### UFieldGroup (v4) / UFormGroup (v3) + +```vue + + + +``` + +### Buttons + +#### UButton + +```vue + +``` + +#### UButtonGroup (v3) / UFieldGroup (v4) + +```vue + +``` + +### Overlays + +#### UModal + +```vue + + + +``` + +#### UPopover + +```vue + + + +``` + +#### UTooltip + +```vue + +``` + +### Navigation + +#### ULink + +```vue + +``` + +#### UTabs + +```vue + + + +``` + +#### UBreadcrumb + +```vue + + + +``` + +### Data Display + +#### UCard + +```vue + +``` + +#### UTable + +```vue + + + +``` + +#### UBadge + +```vue + +``` + +#### UAlert + +```vue + +``` + +### Feedback + +#### UNotification (Toast) + +```vue + + + +``` + +#### UProgress + +```vue + + + +``` + +## Form Validation + +### Using with Zod (Recommended) + +```vue + + + +``` + +## Theming & Customization + +### Color Configuration (v4) + +```typescript +// app.config.ts +export default defineAppConfig({ + ui: { + colors: { + primary: "blue", + secondary: "purple", + success: "green", + warning: "yellow", + error: "red", + neutral: "slate", + }, + }, +}) +``` + +### Component Styling + +Override component styles using Tailwind classes: + +```vue + +``` + +### Dark Mode + +Nuxt UI automatically supports dark mode when `@nuxtjs/color-mode` is configured: + +```vue + + + +``` + +## Nuxt UI-Specific Tailwind Configuration + +When using Nuxt UI v4 with Tailwind v4, the Tailwind setup is automatically configured by the module. However, you may need custom configuration: + +### Custom Tailwind Classes with Nuxt UI + +```css +/* assets/css/main.css */ +@import "tailwindcss"; + +/* Custom utilities that work with Nuxt UI */ +@layer utilities { + .text-gradient { + @apply bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent; + } +} +``` + +### Extending Nuxt UI Colors + +```typescript +// tailwind.config.ts +import type { Config } from "tailwindcss" + +export default { + theme: { + extend: { + colors: { + // Add custom colors that integrate with Nuxt UI + brand: { + 50: "#f0f9ff", + 100: "#e0f2fe", + // ... more shades + 900: "#0c4a6e", + }, + }, + }, + }, +} satisfies Config +``` + +Reference `app.config.ts` to use custom color in Nuxt UI: + +```typescript +// app.config.ts +export default defineAppConfig({ + ui: { + colors: { + primary: "brand", // Use custom Tailwind color + }, + }, +}) +``` + +## Common Patterns + +### Loading States + +```vue + + + +``` + +### Confirmation Dialogs + +```vue + + + +``` + +### Data Tables with Actions + +```vue + + + +``` + +## Icons + +Nuxt UI v4 uses Iconify with the `i-` prefix: + +```vue + +``` + +Browse icons at: https://icones.js.org + +## Best Practices + +1. **Always wrap app with UApp (v4)** - Required for proper functioning +2. **Use v-model:open for modals** (v4) - Breaking change from v3 +3. **Configure colors in app.config.ts** (v4) - Centralized theming +4. **Check package.json version** - Use version-appropriate syntax +5. **Leverage auto-completion** - TypeScript types are fully supported +6. **Use built-in validation** - Integrate with Zod for form validation +7. **Prefer composition** - Use slots and composables over prop overload +8. **Follow Tailwind patterns** - Nuxt UI components accept Tailwind classes +9. **Test dark mode** - Always verify components in both light and dark modes + +## Troubleshooting + +### Common Issues (v4) + +**Missing UApp wrapper:** + +``` +Error: Nuxt UI components require wrapper +``` + +Solution: Wrap your app in `app.vue`: + +```vue + +``` + +**Tailwind not working:** +Ensure `main.css` has: + +```css +@import "tailwindcss"; +``` + +**v-model not working on Modal:** +Use `v-model:open` instead of `v-model` in v4. + +## Migration Checklist + +When upgrading from v3 to v4: + +- [ ] Upgrade to Nuxt 4 +- [ ] Install Tailwind CSS v4 +- [ ] Update `main.css` to use `@import "tailwindcss"` +- [ ] Wrap app with `` in `app.vue` +- [ ] Move color config to `app.config.ts` +- [ ] Update `v-model` to `v-model:open` on Modal/Popover/Dialog +- [ ] Rename `UButtonGroup` → `UFieldGroup` +- [ ] Rename `UFormGroup` → `UFieldGroup` +- [ ] Rename `UVerticalNavigation` → `UNavigationTree` +- [ ] Test all components in both light and dark mode +- [ ] Review official migration guide for additional breaking changes + +## Official Resources + +- **Documentation:** https://ui.nuxt.com +- **Migration Guide:** https://ui.nuxt.com/getting-started/migration +- **Component Reference:** https://ui.nuxt.com/components +- **GitHub:** https://github.com/nuxt/ui +- **Iconify Icons:** https://icones.js.org + +--- + +**Note:** Always verify version-specific syntax with official documentation at https://ui.nuxt.com. This reference covers v3 and v4, but breaking changes may occur in future releases. diff --git a/skills/nuxt/references/pinia.md b/skills/nuxt/references/pinia.md new file mode 100644 index 0000000..1179267 --- /dev/null +++ b/skills/nuxt/references/pinia.md @@ -0,0 +1,167 @@ +# Pinia State Management + +**Check if installed:** Look for `pinia` or `@pinia/nuxt` in package.json before using. + +## Overview + +Pinia is the official state management library for Vue. In Nuxt, it integrates seamlessly with auto-imports and SSR. + +## Store Definition + +Use the Composition API style with the setup function pattern. + +```typescript +// stores/user.ts +export const useUserStore = defineStore("user", () => { + // State + const user = ref(null) + const token = ref(null) + + // Getters (computed) + const isAuthenticated = computed(() => !!user.value) + const fullName = computed(() => { + if (!user.value) return "" + return `${user.value.firstName} ${user.value.lastName}` + }) + + // Actions (functions) + async function login(credentials: LoginCredentials) { + const response = await $fetch("/api/auth/login", { + method: "POST", + body: credentials, + }) + user.value = response.user + token.value = response.token + } + + function logout() { + user.value = null + token.value = null + } + + return { + // State + user, + token, + // Getters + isAuthenticated, + fullName, + // Actions + login, + logout, + } +}) +``` + +## Usage in Components + +### Accessing Store + +```typescript + +``` + +### Important: storeToRefs + +Use `storeToRefs()` to maintain reactivity when destructuring state and getters: + +```typescript +// ✅ Correct: Reactive +const { user, isAuthenticated } = storeToRefs(userStore) + +// ❌ Wrong: Loses reactivity +const { user, isAuthenticated } = userStore + +// ✅ Correct: Actions don't need storeToRefs +const { login, logout } = userStore +``` + +## SSR Considerations + +Pinia stores are automatically hydrated in Nuxt. No special configuration needed for SSR. + +### Server-side initialization + +```typescript +// stores/config.ts +export const useConfigStore = defineStore('config', () => { + const config = ref(null) + + async function fetchConfig() { + // This runs on server during SSR + config.value = await $fetch('/api/config') + } + + return { config, fetchConfig } +}) + +// Usage in page + +``` + +## Persisting State + +For client-side persistence, use `@pinia-plugin-persistedstate/nuxt`: + +```typescript +// stores/preferences.ts +export const usePreferencesStore = defineStore( + "preferences", + () => { + const theme = ref<"light" | "dark">("light") + const language = ref("en") + + return { theme, language } + }, + { + persist: true, // Persists to localStorage + }, +) +``` + +## Multiple Stores Pattern + +Organize stores by domain: + +``` +stores/ +├── user.ts # Authentication & user data +├── cart.ts # Shopping cart +├── products.ts # Product catalog +└── ui.ts # UI state (modals, sidebars, etc.) +``` + +## Testing Stores + +```typescript +import { setActivePinia, createPinia } from "pinia" +import { useUserStore } from "~/stores/user" + +describe("User Store", () => { + beforeEach(() => { + setActivePinia(createPinia()) + }) + + it("should authenticate user", async () => { + const store = useUserStore() + await store.login({ email: "test@example.com", password: "password" }) + expect(store.isAuthenticated).toBe(true) + }) +}) +``` diff --git a/skills/nuxt/references/tailwind.md b/skills/nuxt/references/tailwind.md new file mode 100644 index 0000000..5b87b33 --- /dev/null +++ b/skills/nuxt/references/tailwind.md @@ -0,0 +1,650 @@ +# 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 ` +``` + +### Layout Utilities + +```vue + +``` + +### Transitions & Animations + +```vue + +``` + +## 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 + +``` + +### 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 + +``` + +## 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 + +``` + +### @tailwindcss/forms + +Automatically styles form elements: + +```vue +