Initial commit
This commit is contained in:
334
.claude/skills/nuxt-ui/SKILL.md
Normal file
334
.claude/skills/nuxt-ui/SKILL.md
Normal file
@@ -0,0 +1,334 @@
|
||||
---
|
||||
name: nuxt-ui
|
||||
description: Build UIs with @nuxt/ui v4 — 125+ accessible Vue components with Tailwind CSS theming. Use when creating interfaces, customizing themes to match a brand, building forms, or composing layouts like dashboards, docs sites, and chat interfaces.
|
||||
---
|
||||
|
||||
# Nuxt UI
|
||||
|
||||
Vue component library built on [Reka UI](https://reka-ui.com/) + [Tailwind CSS](https://tailwindcss.com/) + [Tailwind Variants](https://www.tailwind-variants.org/). Works with Nuxt, Vue (Vite), Laravel (Inertia), and AdonisJS (Inertia).
|
||||
|
||||
## Installation
|
||||
|
||||
### Nuxt
|
||||
|
||||
```bash
|
||||
pnpm add @nuxt/ui tailwindcss
|
||||
```
|
||||
|
||||
```ts
|
||||
// nuxt.config.ts
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css']
|
||||
})
|
||||
```
|
||||
|
||||
```css
|
||||
/* app/assets/css/main.css */
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- app.vue -->
|
||||
<template>
|
||||
<UApp>
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Vue (Vite)
|
||||
|
||||
```bash
|
||||
pnpm add @nuxt/ui tailwindcss
|
||||
```
|
||||
|
||||
```ts
|
||||
// vite.config.ts
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui()
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/main.ts
|
||||
import './assets/main.css'
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import ui from '@nuxt/ui/vue-plugin'
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
const router = createRouter({
|
||||
routes: [],
|
||||
history: createWebHistory()
|
||||
})
|
||||
|
||||
app.use(router)
|
||||
app.use(ui)
|
||||
app.mount('#app')
|
||||
```
|
||||
|
||||
```css
|
||||
/* assets/main.css */
|
||||
@import "tailwindcss";
|
||||
@import "@nuxt/ui";
|
||||
```
|
||||
|
||||
```vue
|
||||
<!-- src/App.vue -->
|
||||
<template>
|
||||
<UApp>
|
||||
<RouterView />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
> **Vue**: Add `class="isolate"` to your root `<div id="app">` in `index.html`.
|
||||
|
||||
> **Vue + Inertia**: Use `ui({ router: 'inertia' })` in `vite.config.ts`.
|
||||
|
||||
### UApp
|
||||
|
||||
Wrapping your app in `UApp` is **required** — it provides global config for toasts, tooltips, and programmatic overlays. It also accepts a `locale` prop for i18n (see [composables reference](references/composables.md)).
|
||||
|
||||
## Icons
|
||||
|
||||
Nuxt UI uses [Iconify](https://iconify.design/) for 200,000+ icons. In Nuxt, `@nuxt/icon` is auto-registered. In Vue, icons work out of the box via the Vite plugin.
|
||||
|
||||
### Naming convention
|
||||
|
||||
Icons use the format `i-{collection}-{name}`:
|
||||
|
||||
```vue
|
||||
<UIcon name="i-lucide-sun" class="size-5" />
|
||||
<UButton icon="i-lucide-plus" label="Add" />
|
||||
<UAlert icon="i-lucide-info" title="Heads up" />
|
||||
```
|
||||
|
||||
> Browse all icons at [icones.js.org](https://icones.js.org). The `lucide` collection is used throughout Nuxt UI defaults.
|
||||
|
||||
### Install icon collections locally
|
||||
|
||||
```bash
|
||||
pnpm i @iconify-json/lucide
|
||||
pnpm i @iconify-json/simple-icons
|
||||
```
|
||||
|
||||
### Custom local collections (Nuxt)
|
||||
|
||||
```ts
|
||||
// nuxt.config.ts
|
||||
export default defineNuxtConfig({
|
||||
icon: {
|
||||
customCollections: [{
|
||||
prefix: 'custom',
|
||||
dir: './app/assets/icons'
|
||||
}]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```vue
|
||||
<UIcon name="i-custom-my-icon" />
|
||||
```
|
||||
|
||||
## Theming & Branding
|
||||
|
||||
Nuxt UI ships with a default look. The goal is to adapt it to your brand so every app looks unique.
|
||||
|
||||
**Always use semantic utilities** (`text-default`, `bg-elevated`, `border-muted`), never raw Tailwind palette colors. See [references/theming.md](references/theming.md) for the full list.
|
||||
|
||||
### Colors
|
||||
|
||||
7 semantic colors (`primary`, `secondary`, `success`, `info`, `warning`, `error`, `neutral`) configurable at runtime:
|
||||
|
||||
```ts
|
||||
// Nuxt — app.config.ts
|
||||
export default defineAppConfig({
|
||||
ui: { colors: { primary: 'indigo', neutral: 'zinc' } }
|
||||
})
|
||||
```
|
||||
|
||||
```ts
|
||||
// Vue — vite.config.ts
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import ui from '@nuxt/ui/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
ui({
|
||||
ui: { colors: { primary: 'indigo', neutral: 'zinc' } }
|
||||
})
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Customizing components
|
||||
|
||||
**Override priority** (highest wins): `ui` prop / `class` prop > global config > theme defaults.
|
||||
|
||||
The `ui` prop overrides a component's **slots** after variants are computed — it wins over everything:
|
||||
|
||||
```vue
|
||||
<UButton :ui="{ base: 'rounded-none', trailingIcon: 'size-3 rotate-90' }" />
|
||||
<UCard :ui="{ header: 'bg-muted', body: 'p-8' }" />
|
||||
```
|
||||
|
||||
**Read the generated theme file** to find slot names for any component:
|
||||
|
||||
- **Nuxt**: `.nuxt/ui/<component>.ts`
|
||||
- **Vue**: `node_modules/.nuxt-ui/ui/<component>.ts`
|
||||
|
||||
> For CSS variables, custom colors, global config, compound variants, and a **full brand customization playbook**, see [references/theming.md](references/theming.md)
|
||||
|
||||
## Composables
|
||||
|
||||
```ts
|
||||
// Notifications
|
||||
const toast = useToast()
|
||||
toast.add({ title: 'Saved', color: 'success', icon: 'i-lucide-check' })
|
||||
|
||||
// Programmatic overlays
|
||||
const overlay = useOverlay()
|
||||
const modal = overlay.create(MyModal)
|
||||
const { result } = modal.open({ title: 'Confirm' })
|
||||
await result
|
||||
|
||||
// Keyboard shortcuts
|
||||
defineShortcuts({
|
||||
meta_k: () => openSearch(),
|
||||
escape: () => close()
|
||||
})
|
||||
```
|
||||
|
||||
> For full composable reference, see [references/composables.md](references/composables.md)
|
||||
|
||||
## Form validation
|
||||
|
||||
Uses Standard Schema — works with Zod, Valibot, Yup, or Joi.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().email('Invalid email'),
|
||||
password: z.string().min(8, 'Min 8 characters')
|
||||
})
|
||||
|
||||
type Schema = z.output<typeof schema>
|
||||
const state = reactive<Partial<Schema>>({ email: '', password: '' })
|
||||
|
||||
function onSubmit() {
|
||||
// UForm validates before emitting @submit — state is valid here
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UForm :schema="schema" :state="state" @submit="onSubmit">
|
||||
<UFormField name="email" label="Email" required>
|
||||
<UInput v-model="state.email" type="email" />
|
||||
</UFormField>
|
||||
|
||||
<UFormField name="password" label="Password" required>
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormField>
|
||||
|
||||
<UButton type="submit">Sign in</UButton>
|
||||
</UForm>
|
||||
</template>
|
||||
```
|
||||
|
||||
> For all form components and validation patterns, see [references/components.md](references/components.md#form)
|
||||
|
||||
## Overlays
|
||||
|
||||
```vue
|
||||
<!-- Modal -->
|
||||
<UModal v-model:open="isOpen" title="Edit" description="Edit your profile">
|
||||
<template #body>Content</template>
|
||||
<template #footer>
|
||||
<UButton variant="ghost" @click="isOpen = false">Cancel</UButton>
|
||||
<UButton @click="save">Save</UButton>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<!-- Slideover (side panel) -->
|
||||
<USlideover v-model:open="isOpen" title="Settings" side="right">
|
||||
<template #body>Content</template>
|
||||
</USlideover>
|
||||
|
||||
<!-- Dropdown menu (flat array) -->
|
||||
<UDropdownMenu :items="[
|
||||
{ label: 'Edit', icon: 'i-lucide-pencil' },
|
||||
{ type: 'separator' },
|
||||
{ label: 'Delete', icon: 'i-lucide-trash', color: 'error' }
|
||||
]">
|
||||
<UButton icon="i-lucide-ellipsis-vertical" variant="ghost" />
|
||||
</UDropdownMenu>
|
||||
|
||||
<!-- Dropdown menu (nested array — groups with automatic separators) -->
|
||||
<UDropdownMenu :items="[
|
||||
[{ label: 'Edit', icon: 'i-lucide-pencil' }, { label: 'Duplicate', icon: 'i-lucide-copy' }],
|
||||
[{ label: 'Delete', icon: 'i-lucide-trash', color: 'error' }]
|
||||
]">
|
||||
<UButton icon="i-lucide-ellipsis-vertical" variant="ghost" />
|
||||
</UDropdownMenu>
|
||||
```
|
||||
|
||||
> For all overlay components, see [references/components.md](references/components.md#overlay)
|
||||
|
||||
## Layouts
|
||||
|
||||
Nuxt UI provides components to compose full page layouts. Load the reference matching your use case:
|
||||
|
||||
| Layout | Description | Reference |
|
||||
|---|---|---|
|
||||
| Page | Landing, blog, changelog, pricing — public-facing pages | [layouts/page.md](references/layouts/page.md) |
|
||||
| Dashboard | Admin UI with resizable sidebar and panels | [layouts/dashboard.md](references/layouts/dashboard.md) |
|
||||
| Docs | Documentation with sidebar nav and TOC | [layouts/docs.md](references/layouts/docs.md) |
|
||||
| Chat | AI chat with messages and prompt | [layouts/chat.md](references/layouts/chat.md) |
|
||||
| Editor | Rich text editor with toolbars | [layouts/editor.md](references/layouts/editor.md) |
|
||||
|
||||
## Templates
|
||||
|
||||
Official starter templates at [github.com/nuxt-ui-templates](https://github.com/nuxt-ui-templates):
|
||||
|
||||
| Template | Framework | GitHub |
|
||||
|---|---|---|
|
||||
| Starter | Nuxt | [nuxt-ui-templates/starter](https://github.com/nuxt-ui-templates/starter) |
|
||||
| Starter | Vue | [nuxt-ui-templates/starter-vue](https://github.com/nuxt-ui-templates/starter-vue) |
|
||||
| Dashboard | Nuxt | [nuxt-ui-templates/dashboard](https://github.com/nuxt-ui-templates/dashboard) |
|
||||
| Dashboard | Vue | [nuxt-ui-templates/dashboard-vue](https://github.com/nuxt-ui-templates/dashboard-vue) |
|
||||
| SaaS | Nuxt | [nuxt-ui-templates/saas](https://github.com/nuxt-ui-templates/saas) |
|
||||
| Landing | Nuxt | [nuxt-ui-templates/landing](https://github.com/nuxt-ui-templates/landing) |
|
||||
| Docs | Nuxt | [nuxt-ui-templates/docs](https://github.com/nuxt-ui-templates/docs) |
|
||||
| Portfolio | Nuxt | [nuxt-ui-templates/portfolio](https://github.com/nuxt-ui-templates/portfolio) |
|
||||
| Chat | Nuxt | [nuxt-ui-templates/chat](https://github.com/nuxt-ui-templates/chat) |
|
||||
| Editor | Nuxt | [nuxt-ui-templates/editor](https://github.com/nuxt-ui-templates/editor) |
|
||||
| Changelog | Nuxt | [nuxt-ui-templates/changelog](https://github.com/nuxt-ui-templates/changelog) |
|
||||
| Starter | Laravel | [nuxt-ui-templates/starter-laravel](https://github.com/nuxt-ui-templates/starter-laravel) |
|
||||
| Starter | AdonisJS | [nuxt-ui-templates/starter-adonis](https://github.com/nuxt-ui-templates/starter-adonis) |
|
||||
|
||||
> When starting a new project, clone the matching template instead of setting up from scratch.
|
||||
|
||||
## Additional references
|
||||
|
||||
Load based on your task — **do not load all at once**:
|
||||
|
||||
- [references/theming.md](references/theming.md) — CSS variables, custom colors, component theme overrides
|
||||
- [references/components.md](references/components.md) — all 125+ components by category with props and usage
|
||||
- [references/composables.md](references/composables.md) — useToast, useOverlay, defineShortcuts
|
||||
- Generated theme files — all slots, variants, and default classes for any component (Nuxt: `.nuxt/ui/<component>.ts`, Vue: `node_modules/.nuxt-ui/ui/<component>.ts`)
|
||||
Reference in New Issue
Block a user