Compare commits

..

1 Commits

Author SHA1 Message Date
Romain Hamel
3a56de3be0 fix(form): provide typings for $el 2024-11-08 12:18:30 +01:00
263 changed files with 3372 additions and 21957 deletions

View File

@@ -5,8 +5,8 @@ body:
- type: markdown
attributes:
value: |
> [!IMPORTANT]
> As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library.
> [!IMPORTANT]
> As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library.
- type: markdown
attributes:
value: |
@@ -29,20 +29,11 @@ body:
- Build Modules: `-`
validations:
required: true
- type: dropdown
id: package
attributes:
label: Is this bug related to Nuxt or Vue?
options:
- Nuxt
- Vue
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: v3.0.0-alpha.x
placeholder: v3.0.0-alpha.5
validations:
required: true
- type: textarea

View File

@@ -12,7 +12,7 @@ body:
label: For what version of Nuxt UI are you suggesting this?
options:
- v2.x
- v3.0.0-alpha.x
- v3-alpha
validations:
required: true
- type: textarea

View File

@@ -12,7 +12,7 @@ body:
label: For what version of Nuxt UI are you asking this question?
options:
- v2.x
- v3.0.0-alpha.x
- v3-alpha
validations:
required: true
- type: textarea

View File

@@ -61,8 +61,5 @@ jobs:
- name: Build
run: pnpm run build
- name: Build vue fixture
run: pnpm run test:vue:build
- name: Publish
run: pnpx pkg-pr-new publish --compact --no-template --pnpm

View File

@@ -1,67 +1,5 @@
# Changelog
## [3.0.0-alpha.9](https://github.com/nuxt/ui/compare/v3.0.0-alpha.8...v3.0.0-alpha.9) (2024-11-19)
### Features
* **cli:** add locale command ([#2586](https://github.com/nuxt/ui/issues/2586)) ([824ba56](https://github.com/nuxt/ui/commit/824ba5629183bc4cd59321213558770db211f6e5))
* **css:** add `--ui-bg-muted` / `--ui-border-muted` variables ([7f6db45](https://github.com/nuxt/ui/commit/7f6db45f1e15ef39cda9b732cb601c552f29570a))
* **Form:** apply transformations ([#2550](https://github.com/nuxt/ui/issues/2550)) ([75c5e87](https://github.com/nuxt/ui/commit/75c5e87724e7abdf0a6751d7a1dbbafb947f373f))
* **FormField:** add `error-pattern` prop ([#2601](https://github.com/nuxt/ui/issues/2601)) ([143612e](https://github.com/nuxt/ui/commit/143612ec737d1c7571398601c3222f2eed37996e))
* **InputMenu/SelectMenu:** add `create-item` prop ([#2472](https://github.com/nuxt/ui/issues/2472)) ([f516d7b](https://github.com/nuxt/ui/commit/f516d7b36da51565f4ab05a4c9cfe5e5b4015124))
* **InputNumber:** implement component ([#2577](https://github.com/nuxt/ui/issues/2577)) ([bd2f077](https://github.com/nuxt/ui/commit/bd2f077fe8e645d5fce8b1eb5a6eb1068b3e8f7c))
* **Link:** allow partial query match for its active state ([#2664](https://github.com/nuxt/ui/issues/2664)) ([7329900](https://github.com/nuxt/ui/commit/7329900ae549430b88567a09cbb585d3cf0a6d32))
* **locale:** add Persian language ([#2682](https://github.com/nuxt/ui/issues/2682)) ([14fb21b](https://github.com/nuxt/ui/commit/14fb21be0034ffc0ba5d213734c00f12e0d6bea8))
* **locale:** add Polish language ([#2678](https://github.com/nuxt/ui/issues/2678)) ([2fc36c8](https://github.com/nuxt/ui/commit/2fc36c878c67967ec91e4f6999575bad45521d44))
* **locale:** add support for Arabic ([#2582](https://github.com/nuxt/ui/issues/2582)) ([602a667](https://github.com/nuxt/ui/commit/602a667343be22b72383ab3cf42f36ec9e135082))
* **locale:** add support for Czech translation ([#2593](https://github.com/nuxt/ui/issues/2593)) ([4889d30](https://github.com/nuxt/ui/commit/4889d30b448296de42e146dc5771738837c31f8c))
* **locale:** add support for Italian ([#2583](https://github.com/nuxt/ui/issues/2583)) ([4fbbb25](https://github.com/nuxt/ui/commit/4fbbb25f68b0b5ee76e50f2da776a74d54acc041))
* **locale:** provide `code` ([#2611](https://github.com/nuxt/ui/issues/2611)) ([8a8b1ee](https://github.com/nuxt/ui/commit/8a8b1ee2e1628bc5439ef109d3c68b69bf631f81))
* **locale:** provide `dir` on `defineLocale` ([#2620](https://github.com/nuxt/ui/issues/2620)) ([937585c](https://github.com/nuxt/ui/commit/937585cb3f8bc902d60a4b5904711598204aee2d))
* **locale:** translate chinese ([#2580](https://github.com/nuxt/ui/issues/2580)) ([febda5c](https://github.com/nuxt/ui/commit/febda5c2b67374d1358a66694543b77037d239c6))
* **locale:** translate Spanish ([#2644](https://github.com/nuxt/ui/issues/2644)) ([8ed434c](https://github.com/nuxt/ui/commit/8ed434c105b75ae02aa7493a235cebb64d518d09))
* **locale:** typing `dir` ([#2643](https://github.com/nuxt/ui/issues/2643)) ([e55c0e2](https://github.com/nuxt/ui/commit/e55c0e25947e7bcef931b26dafaad120f488a627))
* **module:** support i18n in components ([#2553](https://github.com/nuxt/ui/issues/2553)) ([2636240](https://github.com/nuxt/ui/commit/26362408b161108487b889ff001bec9166059c79))
* **NavigationMenu:** control items `open` & `defaultOpen` on vertical ([30218f1](https://github.com/nuxt/ui/commit/30218f1b5b0a56207fd4db224ffa0401ac194a04)), closes [#2608](https://github.com/nuxt/ui/issues/2608)
* **PinInput:** implement component ([#2570](https://github.com/nuxt/ui/issues/2570)) ([95aa6f6](https://github.com/nuxt/ui/commit/95aa6f68b316d02c28d1124d9a826bca55cde81f))
* **Popover:** add `prevent-close` prop ([ea97759](https://github.com/nuxt/ui/commit/ea97759c2c219bdf5c48b652b47d293ddf513a00)), closes [#2245](https://github.com/nuxt/ui/issues/2245)
* **SelectMenu:** use `UInput` in search to handle props like icon ([ff1e079](https://github.com/nuxt/ui/commit/ff1e0798d384d40ad82a95fe5faa16acb080efe3)), closes [#2021](https://github.com/nuxt/ui/issues/2021)
* **Table:** add `caption` prop ([446f9c1](https://github.com/nuxt/ui/commit/446f9c1085e96187afdc5c1d7ce3450f8df1a2e1))
### Bug Fixes
* **App:** missing `vue` imports ([ddb4690](https://github.com/nuxt/ui/commit/ddb46905e7e3480ab578bcd8a478f25dff60081a))
* **App:** remove `dir` prop ([#2630](https://github.com/nuxt/ui/issues/2630)) ([7cc26d0](https://github.com/nuxt/ui/commit/7cc26d098dff70923bcd9d414d675018951b1967))
* **Breadcrumb/Carousel/Pagination:** handle icons in RTL mode ([#2633](https://github.com/nuxt/ui/issues/2633)) ([e5119a9](https://github.com/nuxt/ui/commit/e5119a9ca4e217ef769904323c16bd8c0cbc02d9))
* **Breadcrumb:** render as `nav` ([756f791](https://github.com/nuxt/ui/commit/756f791a1a8dd3a4a88c212b4e4f775584decb55)), closes [#2649](https://github.com/nuxt/ui/issues/2649)
* **Button:** improve neutral solid variant hover ([8d85498](https://github.com/nuxt/ui/commit/8d85498ee197ec0b26cdd7c4b08f84fec45ddd8f))
* **Carousel:** use `dir` from locale ([#2647](https://github.com/nuxt/ui/issues/2647)) ([1fbbfe8](https://github.com/nuxt/ui/commit/1fbbfe8df06b3b8b294615ac328d582c5230aa8b))
* **ContextMenu/DropdownMenu:** relative imports with prefix ([47f58f5](https://github.com/nuxt/ui/commit/47f58f52ef2d03176a184a3ca2154f5cea655edb))
* **css:** `--font-family-sans` renamed to `--font-sans` ([#2680](https://github.com/nuxt/ui/issues/2680)) ([b2fa657](https://github.com/nuxt/ui/commit/b2fa65734bb59186520c985f7c73fc34a0cb8b37))
* **css:** remove useless spacing override ([8d00265](https://github.com/nuxt/ui/commit/8d0026558a21efbbca08e9939844f7479a0d6cce))
* **FormField:** missing conditions to apply container classes ([#2631](https://github.com/nuxt/ui/issues/2631)) ([9241ba1](https://github.com/nuxt/ui/commit/9241ba1230b0fde41595634362d83c92c66b7699))
* **Form:** match `error-pattern` on input validation ([#2606](https://github.com/nuxt/ui/issues/2606)) ([3584a33](https://github.com/nuxt/ui/commit/3584a3328b8588f024557c9908242bc374853419))
* **InputMenu/SelectMenu:** init `filter` with `labelKey` ([18931ac](https://github.com/nuxt/ui/commit/18931acdb316bc72a3e5ed6d20985688ad5c8d99))
* **InputMenu/SelectMenu:** look in `items` only with `value-attribute` ([0ceafe1](https://github.com/nuxt/ui/commit/0ceafe1d54000f3eb49562b00c188d82fa23c4ee)), closes [#2464](https://github.com/nuxt/ui/issues/2464)
* **InputMenu/SelectMenu:** multiple not working with generic boolean casting ([503f701](https://github.com/nuxt/ui/commit/503f701c7ecdfe27e9057e5ddebfc7e03889d61b)), closes [#2541](https://github.com/nuxt/ui/issues/2541)
* **InputMenu/SelectMenu:** use `isEqual` from `ohash` ([f943f88](https://github.com/nuxt/ui/commit/f943f88fcc9f4678d8f7bd224799e289a0c57dd8))
* **Link:** missing relative import ([#2588](https://github.com/nuxt/ui/issues/2588)) ([95a0bbc](https://github.com/nuxt/ui/commit/95a0bbc581a40677f620eed3170f9a423976214b))
* **locale:** Improve German translation ([#2676](https://github.com/nuxt/ui/issues/2676)) ([992be91](https://github.com/nuxt/ui/commit/992be91823fe1877254ccd092c71c77dd3ff42f7))
* **locale:** it translation ([#2623](https://github.com/nuxt/ui/issues/2623)) ([73e25ed](https://github.com/nuxt/ui/commit/73e25ed23562f755ea4c66e6c5fb06dd18caac1e))
* **locale:** Italian translation ([#2584](https://github.com/nuxt/ui/issues/2584)) ([d167c9b](https://github.com/nuxt/ui/commit/d167c9b807a82fdf0fd280ce8417a66f86d7ed72))
* **Modal/Slideover:** prevent `esc` with `prevent-close` prop ([9e2cc5b](https://github.com/nuxt/ui/commit/9e2cc5b12567472044726924a3896b4b0e7993a1)), closes [#2501](https://github.com/nuxt/ui/issues/2501)
* **module:** remove `fast-deep-equal` in favor of custom `isEqual` ([37a3597](https://github.com/nuxt/ui/commit/37a359701f4b2ce4a9b0727b64c0e3eea6be00b4))
* **module:** skip devtools renderer page injection if router integration is disabled ([#2571](https://github.com/nuxt/ui/issues/2571)) ([afe4003](https://github.com/nuxt/ui/commit/afe40033b088d8aedb73fa8387a0284ef78444e4))
* **PinInput:** missing `useFormField` import ([601f4b2](https://github.com/nuxt/ui/commit/601f4b2cd2027817b935e02a0b4584dc3dce655f))
* **Textarea:** `autoresize` does not work when initializing `modelValue` ([#2681](https://github.com/nuxt/ui/issues/2681)) ([d3a079a](https://github.com/nuxt/ui/commit/d3a079a644db3dfe2f4e9567973550d74b3ba905))
* **Toaster:** teleport to `body` ([b0be26d](https://github.com/nuxt/ui/commit/b0be26d67feab467ac5862edd82e52df03a5091c)), closes [#2404](https://github.com/nuxt/ui/issues/2404)
* **Toast:** unreachable behind overlays ([#2650](https://github.com/nuxt/ui/issues/2650)) ([0daac5b](https://github.com/nuxt/ui/commit/0daac5bafb756c3a2dfaf2bf166c30c0eb476e08))
* **useLocale:** missing import in various components ([#2603](https://github.com/nuxt/ui/issues/2603)) ([df7a61a](https://github.com/nuxt/ui/commit/df7a61a97a14b3d7943baee6a74686134dfdb10b))
### Reverts
* Revert "docs(ComponentCode/ComponentExample): use relative imports" ([5deadc7](https://github.com/nuxt/ui/commit/5deadc709640bbfd3ec14c1c9363deb55e765d6a))
## [3.0.0-alpha.8](https://github.com/nuxt/ui/compare/v3.0.0-alpha.7...v3.0.0-alpha.8) (2024-11-07)
### ⚠ BREAKING CHANGES

View File

@@ -1,23 +1,18 @@
[![nuxt-ui.png](https://volta.s3.fr-par.scw.cloud/nuxt_ui_social_card_531d133fa2.png)](https://ui.nuxt.com)
[![nuxt-ui.png](https://repository-images.githubusercontent.com/428329515/43fec891-9030-4601-8233-5d45ba5c6013)](https://ui.nuxt.com)
# Nuxt UI
# Nuxt UI v3
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![License][license-src]][license-href]
[![Nuxt][nuxt-src]][nuxt-href]
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/docs/v4-beta), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
> [!NOTE]
> You are on the `v3` development branch, check out the [dev branch](https://github.com/nuxt/ui) for Nuxt UI v2.
## Documentation
Visit https://ui3.nuxt.dev to explore the documentation.
We're thrilled to introduce Nuxt UI v3, a significant upgrade to our UI library that delivers extensive improvements and robust new capabilities. This major update harnesses the combined strengths of [Radix Vue](https://www.radix-vue.com/), [Tailwind CSS v4](https://tailwindcss.com/blog/tailwindcss-v4-alpha), and [Tailwind Variants](https://www.tailwind-variants.org/) to offer developers an unparalleled set of tools for creating sophisticated, accessible, and highly performant user interfaces.
## Installation
1. Install the Nuxt UI v3 alpha package:
```bash [pnpm]
pnpm add @nuxt/ui@next
```
@@ -34,9 +29,10 @@ npm install @nuxt/ui@next
bun add @nuxt/ui@next
```
### Nuxt
> [!WARNING]
> Make sure you have `typescript` installed in your dev dependencies.
1. Add the Nuxt UI module in your `nuxt.config.ts`:
2. Register the Nuxt UI module in your `nuxt.config.ts`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
@@ -44,54 +40,18 @@ export default defineNuxtConfig({
})
```
2. Import Tailwind CSS and Nuxt UI in your CSS:
3. Import Tailwind CSS and Nuxt UI in your `app.vue` or [CSS](https://nuxt.com/docs/getting-started/styling#the-css-property):
```css [assets/css/main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
</style>
```
Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/installation/nuxt).
## Documentation
### Vue
1. Add the Nuxt UI Vite plugin in your `vite.config.ts`:
```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()
]
})
```
2. Use the Nuxt UI Vue plugin in your `main.ts`:
```ts [main.ts]
import { createApp } from 'vue'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
app.use(ui)
app.mount('#app')
```
3. Import Tailwind CSS and Nuxt UI in your CSS:
```css [assets/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
```
Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/installation/vue).
Visit https://ui3.nuxt.dev to explore the documentation.
## Credits
@@ -105,7 +65,7 @@ Learn more in the [installation guide](https://ui3.nuxt.dev/getting-started/inst
## License
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/v3/LICENSE.md).
Licensed under the [MIT license](https://github.com/nuxt/ui/blob/dev/LICENSE.md).
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/@nuxt/ui/next.svg?style=flat&colorA=18181B&colorB=28CF8D
@@ -115,7 +75,7 @@ Licensed under the [MIT license](https://github.com/nuxt/ui/blob/v3/LICENSE.md).
[npm-downloads-href]: https://npm.chart.dev/@nuxt/ui
[license-src]: https://img.shields.io/github/license/nuxt/ui.svg?style=flat&colorA=18181B&colorB=28CF8D
[license-href]: https://github.com/nuxt/ui/blob/v3/LICENSE.md
[license-href]: https://github.com/nuxt/ui/blob/main/LICENSE.md
[nuxt-src]: https://img.shields.io/badge/Nuxt-18181B?logo=nuxt.js
[nuxt-href]: https://nuxt.com

View File

@@ -3,13 +3,13 @@ import { resolve } from 'pathe'
import { defineCommand } from 'citty'
import { consola } from 'consola'
import { splitByCase, upperFirst, camelCase, kebabCase } from 'scule'
import { appendFile, sortFile } from '../../utils.mjs'
import templates from '../../templates.mjs'
import { appendFile, sortFile } from '../utils.mjs'
import templates from '../templates.mjs'
export default defineCommand({
meta: {
name: 'component',
description: 'Make a new component.'
name: 'init',
description: 'Init a new component.'
},
args: {
name: {

View File

@@ -1,14 +0,0 @@
import { defineCommand } from 'citty'
import component from './component.mjs'
import locale from './locale.mjs'
export default defineCommand({
meta: {
name: 'make',
description: 'Commands to create new Nuxt UI entities.'
},
subCommands: {
component,
locale
}
})

View File

@@ -1,64 +0,0 @@
import { existsSync, promises as fsp } from 'node:fs'
import { resolve } from 'pathe'
import { consola } from 'consola'
import { appendFile, sortFile, normalizeLocale } from '../../utils.mjs'
import { defineCommand } from 'citty'
export default defineCommand({
meta: {
name: 'locale',
description: 'Make a new locale.'
},
args: {
code: {
description: 'Locale code to create. For example: en.',
required: true
},
name: {
description: 'Locale name to create. For example: English.',
required: true
},
dir: {
description: 'Locale direction. For example: rtl.',
default: 'ltr'
}
},
async setup({ args }) {
const path = resolve('.')
const localePath = resolve(path, `src/runtime/locale`)
const originLocaleFilePath = resolve(localePath, 'en.ts')
const newLocaleFilePath = resolve(localePath, `${args.code}.ts`)
// Validate locale code
if (existsSync(newLocaleFilePath)) {
consola.error(`🚨 ${args.code} already exists!`)
process.exit(1)
}
if (!['ltr', 'rtl'].includes(args.dir)) {
consola.error(`🚨 Direction ${args.dir} not supported!`)
process.exit(1)
}
if (!args.code.match(/^[a-z]{2}(?:_[a-z]{2,4})?$/)) {
consola.error(`🚨 ${args.code} is not a valid locale code!\nExample: en or en_us`)
process.exit(1)
}
// Create new locale export
const localeExportFile = resolve(localePath, `index.ts`)
await appendFile(localeExportFile, `export { default as ${args.code} } from './${args.code}'`)
await sortFile(localeExportFile)
// Create new locale file
await fsp.copyFile(originLocaleFilePath, newLocaleFilePath)
const localeFile = await fsp.readFile(newLocaleFilePath, 'utf-8')
const rewrittenLocaleFile = localeFile
.replace(/name: '(.*)',/, `name: '${args.name}',`)
.replace(/code: '(.*)',/, `code: '${normalizeLocale(args.code)}',${(args.dir && args.dir !== 'ltr') ? `\n dir: '${args.dir}',` : ''}`)
await fsp.writeFile(newLocaleFilePath, rewrittenLocaleFile)
consola.success(`🪄 Generated ${newLocaleFilePath}`)
}
})

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
import { defineCommand, runMain } from 'citty'
import make from './commands/make/index.mjs'
import init from './commands/init.mjs'
const main = defineCommand({
meta: {
@@ -8,7 +8,7 @@ const main = defineCommand({
description: 'Nuxt UI CLI'
},
subCommands: {
make
init
}
})

View File

@@ -163,54 +163,9 @@ describe('${upperName}', () => {
}
}
const doc = ({ name, pro }) => {
const kebabName = kebabCase(name)
const upperName = splitByCase(name).map(p => upperFirst(p)).join('')
return {
filename: `docs/content/${pro ? 'pro' : '3.components'}/${kebabName}.md`,
contents: `---
description:
links: ${pro
? ''
: `
- label: ${upperName}
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/${kebabName}.html`}
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/${pro ? 'ui-pro' : 'ui'}/tree/v3/src/runtime/components/${upperName}.vue
---
## Usage
## Examples
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
## Theme
:component-theme
`
}
}
export default {
playground,
component,
theme,
test,
doc
test
}

View File

@@ -15,17 +15,3 @@ export async function appendFile(path, contents) {
await fsp.writeFile(path, file.trim() + '\n' + contents + '\n')
}
}
export function normalizeLocale(locale) {
if (!locale) {
return ''
}
if (locale.includes('_')) {
return locale.split('_')
.map((part, index) => index === 0 ? part.toLowerCase() : part.toUpperCase())
.join('-')
}
return locale.toLowerCase()
}

View File

@@ -168,7 +168,7 @@ const isDark = computed({
@import '@nuxt/ui';
@theme {
--font-sans: 'DM Sans', sans-serif;
--font-family-sans: 'DM Sans', sans-serif;
--color-primary-50: var(--ui-color-primary-50);
--color-primary-100: var(--ui-color-primary-100);

View File

@@ -18,7 +18,7 @@ onMounted(() => {
</script>
<template>
<div class="border rounded-[var(--ui-radius)] border-[var(--ui-border)]">
<div class="border rounded border-[var(--ui-border)]">
<div
ref="wrapper"
:class="['overflow-hidden', collapsed && overflow ? 'max-h-48' : 'max-h-none']"

View File

@@ -126,7 +126,7 @@ const previewUrl = computed(() => {
</div>
<div v-if="highlightedCode && formattedCode" v-show="rendererReady" class="relative w-full p-3">
<!-- eslint-disable vue/no-v-html -->
<pre class="p-4 min-h-40 max-h-72 text-sm overflow-y-auto rounded-[calc(var(--ui-radius)*1.5)] border border-[var(--ui-border)] bg-neutral-50 dark:bg-neutral-800" v-html="highlightedCode" />
<pre class="p-4 min-h-40 max-h-72 text-sm overflow-y-auto rounded-lg border border-[var(--ui-border)] bg-neutral-50 dark:bg-neutral-800" v-html="highlightedCode" />
<UButton
color="neutral"
variant="link"

View File

@@ -17,14 +17,14 @@ watchEffect(() => {
})
const description = computed(() => {
return props.meta.description?.replace(/`([^`]+)`/g, '<code class="font-medium bg-[var(--ui-bg-elevated)] px-1 py-0.5 rounded-[var(--ui-radius)]">$1</code>')
return props.meta.description?.replace(/`([^`]+)`/g, '<code class="font-medium bg-[var(--ui-bg-elevated)] px-1 py-0.5 rounded">$1</code>')
})
</script>
<template>
<UFormField :name="meta?.name" class="" :ui="{ wrapper: 'mb-2' }" :class="{ 'opacity-70 cursor-not-allowed': !matchedInput || ignore }">
<template #label>
<p v-if="meta?.name" class="font-mono font-bold px-1.5 py-0.5 border border-[var(--ui-border-accented)] border-dashed rounded-[var(--ui-radius)] bg-[var(--ui-bg-elevated)]">
<p v-if="meta?.name" class="font-mono font-bold px-1.5 py-0.5 border border-[var(--ui-border-accented)] border-dashed rounded bg-[var(--ui-bg-elevated)]">
{{ meta?.name }}
</p>
</template>

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@nuxt/ui": "latest",
"knitwork": "^1.1.0",
"nuxt": "^3.14.1592",
"nuxt": "^3.14.159",
"prettier": "^3.3.3",
"zod": "^3.23.8"
}

View File

@@ -8,9 +8,7 @@ const appConfig = useAppConfig()
const colorMode = useColorMode()
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('content'))
const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('content'), {
server: false
})
const { data: files } = await useAsyncData('files', () => queryCollectionSearchSections('content', { ignoredTags: ['style'] }))
const searchTerm = ref('')
@@ -73,41 +71,19 @@ useServerSeoMeta({
twitterCard: 'summary_large_image'
})
const { framework, frameworks } = useSharedData()
const updatedNavigation = computed(() => navigation.value?.map(item => ({
...item,
children: item.children?.map((child: typeof item) => ({
...child,
...(child.path === '/getting-started/installation' && {
title: 'Installation',
active: route.path.startsWith('/getting-started/installation'),
children: []
})
})) || []
})))
const groups = computed(() => {
return [{
id: 'framework',
label: 'Framework',
items: frameworks.value
}]
})
function filterFrameworkItems(items: any[]) {
return items?.filter(item => !item.framework || item.framework === framework.value)
}
function processNavigationItem(item: any): any {
if (item.shadow) {
const matchingChild = filterFrameworkItems(item.children)?.[0]
return matchingChild
? {
...matchingChild,
title: item.title,
children: matchingChild.children ? processNavigationItem(matchingChild) : undefined
}
: item
}
return {
...item,
children: item.children?.length ? filterFrameworkItems(item.children)?.map(processNavigationItem) : undefined
}
}
const filteredNavigation = computed(() => navigation.value?.map(processNavigationItem))
provide('navigation', filteredNavigation)
provide('navigation', updatedNavigation)
</script>
<template>
@@ -128,13 +104,7 @@ provide('navigation', filteredNavigation)
<Footer />
<ClientOnly>
<LazyUContentSearch
v-model:search-term="searchTerm"
:files="files"
:groups="groups"
:navigation="filteredNavigation"
:fuse="{ resultLimit: 42 }"
/>
<LazyUContentSearch v-model:search-term="searchTerm" :files="files" :navigation="navigation" :fuse="{ resultLimit: 42 }" />
</ClientOnly>
</template>
</UApp>
@@ -144,12 +114,10 @@ provide('navigation', filteredNavigation)
@import "tailwindcss";
@import "@nuxt/ui-pro";
@source "../content";
@source "../content/**/*.md";
@theme {
--container-8xl: 90rem;
--font-sans: 'Public Sans', sans-serif;
--font-family-sans: 'Public Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;
@@ -165,6 +133,6 @@ provide('navigation', filteredNavigation)
}
:root {
--ui-container: var(--container-8xl);
--ui-container-width: 90rem;
}
</style>

View File

@@ -1,10 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 283.46 283.46">
<defs>
<style>
.cls-1{fill:#231815;}
@media (prefers-color-scheme: dark) { .cls-1{fill:#ffffff;} }
</style>
</defs>
<path class="cls-1" d="M144.89,89.86c-33.46,0-54.44,14.56-66.14,26.76a86,86,0,0,0-23.69,58.94c0,22.64,8.81,43.48,24.8,58.67,15.7,14.92,36.9,23.14,59.68,23.14,23.81,0,46-8.49,62.49-23.91,17-15.9,26.37-37.93,26.37-62C228.4,120.37,185.94,89.86,144.89,89.86Zm.49,153.67a61.49,61.49,0,0,1-46.45-20.4c-12.33-13.76-18.85-32.64-18.85-54.62,0-20.7,6.07-37.67,17.57-49.07,10.11-10,24.39-15.62,40.19-15.74,19,0,35.22,6.56,46.76,19,12.6,13.58,19.27,34,19.27,58.95C203.87,224.39,174.49,243.53,145.38,243.53Z"/>
<polygon class="cls-1" points="198.75 74.96 179.45 74.96 142.09 37.83 104.51 74.96 86.14 74.96 138.09 24.25 146.81 24.25 198.75 74.96"/>
</svg>

Before

Width:  |  Height:  |  Size: 855 B

View File

@@ -1,25 +0,0 @@
<script setup lang="ts">
const { framework, frameworks } = useSharedData()
</script>
<template>
<UDropdownMenu
v-slot="{ open }"
:modal="false"
:items="frameworks"
:ui="{ content: 'w-(--radix-dropdown-menu-trigger-width)' }"
>
<UButton
color="neutral"
variant="outline"
v-bind="frameworks.find(f => f.value === framework)"
block
trailing-icon="i-lucide-chevron-down"
:class="[open && 'bg-[var(--ui-bg-elevated)]']"
:ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}"
class="-mx-2 w-[calc(100%+1rem)]"
/>
</UDropdownMenu>
</template>

View File

@@ -42,7 +42,7 @@ defineShortcuts({
<UButton
color="neutral"
variant="ghost"
to="https://github.com/nuxt/ui"
to="https://github.com/nuxt/ui/tree/v3"
target="_blank"
icon="i-simple-icons-github"
aria-label="GitHub"
@@ -55,8 +55,6 @@ defineShortcuts({
<USeparator type="dashed" class="my-4" />
<FrameworkSelect class="mb-4" />
<UContentNavigation :navigation="navigation" highlight />
</template>
</UHeader>

View File

@@ -3,42 +3,9 @@
import json5 from 'json5'
import { upperFirst, camelCase, kebabCase } from 'scule'
import { hash } from 'ohash'
import { CalendarDate } from '@internationalized/date'
import * as theme from '#build/ui'
import { get, set } from '#ui/utils'
interface Cast {
get: (args: any) => any
template: (args: any) => string
}
type CastDateValue = [number, number, number]
const castMap: Record<string, Cast> = {
'DateValue': {
get: (args: CastDateValue) => new CalendarDate(...args),
template: (value: CalendarDate) => {
return value ? `new CalendarDate(${value.year}, ${value.month}, ${value.day})` : 'null'
}
},
'DateValue[]': {
get: (args: CastDateValue[]) => args.map(date => new CalendarDate(...date)),
template: (value: CalendarDate[]) => {
return value ? `[${value.map(date => `new CalendarDate(${date.year}, ${date.month}, ${date.day})`).join(', ')}]` : '[]'
}
},
'DateRange': {
get: (args: { start: CastDateValue, end: CastDateValue }) => ({ start: new CalendarDate(...args.start), end: new CalendarDate(...args.end) }),
template: (value: { start: CalendarDate, end: CalendarDate }) => {
if (!value.start || !value.end) {
return `{ start: null, end: null }`
}
return `{ start: new CalendarDate(${value.start.year}, ${value.start.month}, ${value.start.day}), end: new CalendarDate(${value.end.year}, ${value.end.month}, ${value.end.day}) }`
}
}
}
const props = defineProps<{
/** Override the slug taken from the route */
slug?: string
@@ -51,8 +18,6 @@ const props = defineProps<{
external?: string[]
/** List of props to use with `v-model` */
model?: string[]
/** List of props to cast from code and selection */
cast?: { [key: string]: string }
/** List of items for each prop */
items?: { [key: string]: string[] }
props?: { [key: string]: any }
@@ -80,17 +45,7 @@ const camelName = camelCase(props.slug ?? route.params.slug?.[route.params.slug.
const name = `U${upperFirst(camelName)}`
const component = defineAsyncComponent(() => import(`#ui/components/${upperFirst(camelName)}.vue`))
const componentProps = reactive({
...Object.fromEntries(Object.entries(props.props || {}).map(([key, value]) => {
const cast = props.cast?.[key]
if (cast && !castMap[cast]) {
throw new Error(`Unknown cast: ${cast}`)
}
return [key, cast ? castMap[cast]!.get(value) : value]
}))
})
const componentProps = reactive({ ...(props.props || {}) })
const componentEvents = reactive({
...Object.fromEntries((props.model || []).map(key => [`onUpdate:${key}`, (e: any) => setComponentProp(key, e)])),
...(componentProps.modelValue ? { [`onUpdate:modelValue`]: (e: any) => setComponentProp('modelValue', e) } : {})
@@ -141,7 +96,7 @@ const options = computed(() => {
return {
name: key,
label: key,
type: props?.cast?.[key] ?? prop?.type,
type: prop?.type,
items
}
})
@@ -162,10 +117,7 @@ const code = computed(() => {
<script setup lang="ts">
`
for (const key of props.external) {
const cast = props.cast?.[key]
const value = cast ? castMap[cast]!.template(componentProps[key]) : json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${value})
code += `const ${key === 'modelValue' ? 'value' : key} = ref(${json5.stringify(componentProps[key], null, 2).replace(/,([ |\t\n]+[}|\]])/g, '$1')})
`
}
code += `<\/script>
@@ -212,7 +164,7 @@ const code = computed(() => {
continue
}
code += ` ${typeof value === 'number' ? ':' : ''}${name}="${value}"`
code += ` ${prop?.type.includes('number') ? ':' : ''}${name}="${value}"`
}
}
@@ -268,7 +220,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
<template>
<div class="my-5">
<div>
<div v-if="options.length" class="flex items-center gap-2.5 border border-[var(--ui-border-muted)] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
<div v-if="options.length" class="flex items-center gap-2.5 border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] border-b-0 relative rounded-t-[calc(var(--ui-radius)*1.5)] px-4 py-2.5 overflow-x-auto">
<template v-for="option in options" :key="option.name">
<UFormField
:label="option.label"
@@ -317,7 +269,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
</template>
</div>
<div v-if="component" class="flex justify-center border border-b-0 border-[var(--ui-border-muted)] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
<div v-if="component" class="flex justify-center border border-b-0 border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class]">
<component :is="component" v-bind="{ ...componentProps, ...componentEvents }">
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<MDCSlot :name="slot" unwrap="p">

View File

@@ -117,8 +117,8 @@ const optionsValues = ref(props.options?.reduce((acc, option) => {
<template>
<div class="my-5">
<template v-if="preview">
<div class="border border-[var(--ui-border-muted)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-border-muted)]">
<div class="border border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)] relative z-[1]" :class="[{ 'border-b-0 rounded-t-[calc(var(--ui-radius)*1.5)]': props.source, 'rounded-[calc(var(--ui-radius)*1.5)]': !props.source }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-[var(--ui-color-neutral-200)] dark:border-[var(--ui-color-neutral-700)]">
<slot name="options" />
<UFormField

View File

@@ -35,7 +35,7 @@ const schemaProps = computed(() => {
</script>
<template>
<ProseCollapsible v-if="schemaProps?.length" class="mt-1">
<Collapsible v-if="schemaProps?.length" class="mt-1">
<ProseUl>
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
@@ -43,5 +43,5 @@ const schemaProps = computed(() => {
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-[var(--ui-text-muted)] my-1" />
</ProseLi>
</ProseUl>
</ProseCollapsible>
</Collapsible>
</template>

View File

@@ -4,19 +4,14 @@ import { camelCase } from 'scule'
import * as theme from '#build/ui'
const route = useRoute()
const { framework } = useSharedData()
const name = camelCase(route.params.slug?.[route.params.slug.length - 1] ?? '')
const strippedCompoundVariants = ref(false)
const strippedTheme = computed(() => {
const strippedTheme = {
...(theme as any)[name]
}
if (strippedTheme?.compoundVariants) {
strippedTheme.compoundVariants = strippedTheme.compoundVariants.filter((compoundVariant: any) => {
function stripCompoundVariants(component?: any) {
if (component?.compoundVariants) {
component.compoundVariants = component.compoundVariants.filter((compoundVariant: any) => {
if (compoundVariant.color) {
if (!['primary', 'neutral'].includes(compoundVariant.color)) {
strippedCompoundVariants.value = true
@@ -45,43 +40,24 @@ const strippedTheme = computed(() => {
})
}
return strippedTheme
})
return component
}
const component = computed(() => {
return {
ui: {
[name]: strippedTheme.value
[name]: stripCompoundVariants((theme as any)[name])
}
}
})
const { data: ast } = await useAsyncData(`component-theme-${name}-${framework.value}`, async () => {
const { data: ast } = await useAsyncData(`component-theme-${name}`, async () => {
const md = `
::code-collapse
${framework.value === 'nuxt'
? `
\`\`\`ts [app.config.ts]
export default defineAppConfig(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
\`\`\`\
`
: `
\`\`\`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(${json5.stringify(component.value, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
.split('\n')
.map((line, i) => i === 0 ? line : ` ${line}`)
.join('\n')})
]
})
\`\`\`
`}
::
${strippedCompoundVariants.value
@@ -93,7 +69,7 @@ Some colors in \`compoundVariants\` are omitted for readability. Check out the s
`
return parseMarkdown(md)
}, { watch: [framework] })
})
</script>
<template>

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
const { framework } = useSharedData()
</script>
<template>
<slot :name="framework" />
</template>

View File

@@ -2,13 +2,8 @@
import json5 from 'json5'
import icons from '../../../../src/theme/icons'
const { framework } = useSharedData()
const { data: ast } = await useAsyncData(`icons-theme-${framework.value}`, async () => {
const { data: ast } = await useAsyncData(`icons-theme`, async () => {
const md = `
::code-collapse
${framework.value === 'nuxt'
? `
\`\`\`ts [app.config.ts]
export default defineAppConfig(${json5.stringify({
ui: {
@@ -16,33 +11,10 @@ export default defineAppConfig(${json5.stringify({
}
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')})
\`\`\`\
`
: `
\`\`\`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(${json5.stringify({
ui: {
icons
}
}, null, 2).replace(/,([ |\t\n]+[}|\])])/g, '$1')
.split('\n')
.map((line, i) => i === 0 ? line : ` ${line}`)
.join('\n')})
]
})
\`\`\`
`}
::
`
return parseMarkdown(md)
}, { watch: [framework] })
})
</script>
<template>

View File

@@ -1,53 +0,0 @@
<script setup lang="ts">
import type { Locale } from '@nuxt/ui'
import * as locales from '@nuxt/ui/locale'
type LocaleKey = keyof typeof locales
type LocaleComputed = Locale & { flag: string }
const props = withDefaults(defineProps<{
default?: string
}>(), {
default: 'en'
})
const countries = await $fetch('/api/locales.json')
const getLocaleKeys = Object.keys(locales) as LocaleKey[]
const localesList = getLocaleKeys.map<LocaleComputed>((code) => {
const locale: Locale = locales[code]
return {
...locale,
flag: countries[locale.code] || ''
}
})
</script>
<!-- eslint-disable vue/singleline-html-element-content-newline -->
<template>
<div>
<ProseP>
By default, the <ProseCode>{{ props.default }}</ProseCode> locale is used.
</ProseP>
<div class="grid gap-6 grid-cols-2 md:grid-cols-3">
<div v-for="locale in localesList" :key="locale.code">
<div class="flex gap-3 items-center">
<UAvatar :text="locale.flag" size="xl" />
<div class="text-sm">
<div class="font-semibold">{{ locale.name }}</div>
<div class="mt-1">Code: <ProseCode class="text-xs">{{ locale.code }}</ProseCode></div>
</div>
</div>
</div>
</div>
<ProseNote to="https://github.com/nuxt/ui/tree/v3/src/runtime/locale" target="_blank">
If you need additional languages, you can contribute by creating a PR to add a new locale in <ProseCode>src/runtime/locale/</ProseCode>.
</ProseNote>
<ProseTip>
You can use the <ProseCode>nuxt-ui</ProseCode> CLI to create a new locale:
<ProsePre language="bash">nuxt-ui make locale --code "en" --name "English"</ProsePre>
</ProseTip>
</div>
</template>

View File

@@ -1,21 +0,0 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
</script>
<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
{{ df.format(modelValue.toDate(getLocalTimeZone())) }}
</UButton>
<template #content>
<UCalendar v-model="modelValue" class="p-2" />
</template>
</UPopover>
</template>

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
import { CalendarDate, DateFormatter, getLocalTimeZone } from '@internationalized/date'
const df = new DateFormatter('en-US', {
dateStyle: 'medium'
})
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 2, 10)
})
</script>
<template>
<UPopover>
<UButton color="neutral" variant="subtle" icon="i-lucide-calendar">
<template v-if="modelValue.start">
<template v-if="modelValue.end">
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }} - {{ df.format(modelValue.end.toDate(getLocalTimeZone())) }}
</template>
<template v-else>
{{ df.format(modelValue.start.toDate(getLocalTimeZone())) }}
</template>
</template>
<template v-else>
Pick a date
</template>
</UButton>
<template #content>
<UCalendar v-model="modelValue" class="p-2" :number-of-months="2" range />
</template>
</UPopover>
</template>

View File

@@ -1,17 +0,0 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateDisabled: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>
<template>
<UCalendar v-model="modelValue" :is-date-disabled="isDateDisabled" range />
</template>

View File

@@ -1,30 +0,0 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2022, 1, 10))
function getColorByDate(date: Date) {
const isWeekend = date.getDay() % 6 == 0
const isDayMeeting = date.getDay() % 3 == 0
if (isWeekend) {
return undefined
}
if (isDayMeeting) {
return 'error'
}
return 'success'
}
</script>
<template>
<UCalendar v-model="modelValue">
<template #day="{ day }">
<UChip :show="!!getColorByDate(day.toDate('UTC'))" :color="getColorByDate(day.toDate('UTC'))" size="2xs">
{{ day.day }}
</UChip>
</template>
</UCalendar>
</template>

View File

@@ -1,11 +0,0 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const modelValue = shallowRef(new CalendarDate(2023, 9, 10))
const minDate = new CalendarDate(2023, 9, 1)
const maxDate = new CalendarDate(2023, 9, 30)
</script>
<template>
<UCalendar v-model="modelValue" :min-value="minDate" :max-value="maxDate" />
</template>

View File

@@ -1,17 +0,0 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
import type { Matcher } from 'radix-vue/date'
const modelValue = shallowRef({
start: new CalendarDate(2022, 1, 1),
end: new CalendarDate(2022, 1, 9)
})
const isDateUnavailable: Matcher = (date) => {
return date.day >= 10 && date.day <= 16
}
</script>
<template>
<UCalendar v-model="modelValue" :is-date-unavailable="isDateUnavailable" range />
</template>

View File

@@ -32,7 +32,7 @@ const items = computed(() => [{
</script>
<template>
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<UContextMenu :items="items" class="w-48">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -25,7 +25,7 @@ const items = [
</script>
<template>
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<UContextMenu :items="items" class="w-48">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -12,7 +12,7 @@ const items = [{
</script>
<template>
<UContextMenu :items="items" :ui="{ content: 'w-48' }">
<UContextMenu :items="items" class="w-48">
<div class="flex items-center justify-center rounded-md border border-dashed border-[var(--ui-border-accented)] text-sm aspect-video w-72">
Right click here
</div>

View File

@@ -40,7 +40,7 @@ const items = computed(() => [{
</script>
<template>
<UDropdownMenu :items="items" :content="{ align: 'start' }" :ui="{ content: 'w-48' }">
<UDropdownMenu :items="items" :content="{ align: 'start' }" class="w-48">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
</UDropdownMenu>
</template>

View File

@@ -25,7 +25,7 @@ const items = [
</script>
<template>
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
<UDropdownMenu :items="items" class="w-48">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #profile-trailing>

View File

@@ -13,7 +13,7 @@ const items = [{
</script>
<template>
<UDropdownMenu :items="items" :ui="{ content: 'w-48' }">
<UDropdownMenu :items="items" class="w-48">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #profile-trailing>

View File

@@ -18,7 +18,7 @@ const items = [{
</script>
<template>
<UDropdownMenu v-model:open="open" :items="items" :ui="{ content: 'w-48' }">
<UDropdownMenu v-model:open="open" :items="items" class="w-48">
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
</UDropdownMenu>
</template>

View File

@@ -4,7 +4,6 @@ import type { FormSubmitEvent } from '@nuxt/ui'
const schema = z.object({
input: z.string().min(10),
inputNumber: z.number().min(10),
inputMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select Option 2'
}),
@@ -30,11 +29,10 @@ const schema = z.object({
radioGroup: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
}),
slider: z.number().max(20, { message: 'Must be less than 20' }),
pin: z.string().regex(/^\d$/).array().length(5)
slider: z.number().max(20, { message: 'Must be less than 20' })
})
type Schema = z.input<typeof schema>
type Schema = z.output<typeof schema>
const state = reactive<Partial<Schema>>({})
@@ -54,10 +52,10 @@ async function onSubmit(event: FormSubmitEvent<any>) {
</script>
<template>
<UForm ref="form" :state="state" :schema="schema" class="w-full" @submit="onSubmit">
<UForm ref="form" :state="state" :schema="schema" @submit="onSubmit">
<div class="grid grid-cols-3 gap-4">
<UFormField label="Input" name="input">
<UInput v-model="state.input" placeholder="john@lennon.com" class="w-full" />
<UInput v-model="state.input" placeholder="john@lennon.com" class="w-40" />
</UFormField>
<div class="flex flex-col gap-4">
@@ -75,48 +73,42 @@ async function onSubmit(event: FormSubmitEvent<any>) {
</UFormField>
<UFormField name="select" label="Select">
<USelect v-model="state.select" :items="items" class="w-full" />
<USelect v-model="state.select" :items="items" />
</UFormField>
<UFormField name="selectMenu" label="Select Menu">
<USelectMenu v-model="state.selectMenu" :items="items" class="w-full" />
<USelectMenu v-model="state.selectMenu" :items="items" />
</UFormField>
<UFormField name="selectMenuMultiple" label="Select Menu (Multiple)">
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" class="w-full" />
<USelectMenu v-model="state.selectMenuMultiple" multiple :items="items" />
</UFormField>
<UFormField name="inputMenu" label="Input Menu">
<UInputMenu v-model="state.inputMenu" :items="items" class="w-full" />
<UInputMenu v-model="state.inputMenu" :items="items" />
</UFormField>
<UFormField name="inputMenuMultiple" label="Input Menu (Multiple)">
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" class="w-full" />
<UInputMenu v-model="state.inputMenuMultiple" multiple :items="items" />
</UFormField>
<UFormField name="inputNumber" label="Input Number">
<UInputNumber v-model="state.inputNumber" class="w-full" />
</UFormField>
<span />
<UFormField label="Textarea" name="textarea">
<UTextarea v-model="state.textarea" class="w-full" />
<UTextarea v-model="state.textarea" />
</UFormField>
<UFormField name="radioGroup">
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
</UFormField>
<UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/">
<UPinInput v-model="state.pin" />
</UFormField>
</div>
<div class="flex gap-2 mt-8">
<UButton type="submit">
<UButton color="neutral" type="submit">
Submit
</UButton>
<UButton variant="outline" @click="form?.clear()">
<UButton color="neutral" variant="outline" @click="form?.clear()">
Clear
</UButton>
</div>

View File

@@ -1,39 +0,0 @@
<script setup lang="ts">
const { data: countries, status, execute } = await useLazyFetch<{
name: string
code: string
emoji: string
}[]>('/api/countries.json', {
immediate: false
})
function onOpen() {
if (!countries.value?.length) {
execute()
}
}
</script>
<template>
<UInputMenu
:items="countries || []"
:loading="status === 'pending'"
label-key="name"
:search-input="{ icon: 'i-lucide-search' }"
placeholder="Select country"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<span v-if="modelValue" class="size-5 text-center">
{{ modelValue?.emoji }}
</span>
<UIcon v-else name="i-lucide-earth" :class="ui.leadingIcon()" />
</template>
<template #item-leading="{ item }">
<span class="size-5 text-center">
{{ item.emoji }}
</span>
</template>
</UInputMenu>
</template>

View File

@@ -16,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
<UInputMenu
:items="users || []"
:loading="status === 'pending'"
:filter="['label', 'email']"
:filter="['name', 'email']"
icon="i-lucide-user"
placeholder="Select user"
class="w-80"

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const value = ref(1500)
</script>
<template>
<UInputNumber
v-model="value"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting'
}"
/>
</template>

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<UInputNumber
v-model="value"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1
}"
/>
</template>

View File

@@ -1,9 +0,0 @@
<script setup lang="ts">
const retries = ref(0)
</script>
<template>
<UFormField label="Retries" help="Specify number of attempts" required>
<UInputNumber v-model="retries" placeholder="Enter retries" />
</UFormField>
</template>

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
const value = ref(0.05)
</script>
<template>
<UInputNumber
v-model="value"
:step="0.01"
:format-options="{
style: 'percent'
}"
/>
</template>

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
const value = ref(5)
</script>
<template>
<UInputNumber v-model="value">
<template #decrement>
<UButton size="xs" icon="i-lucide-minus" />
</template>
<template #increment>
<UButton size="xs" icon="i-lucide-plus" />
</template>
</UInputNumber>
</template>

View File

@@ -6,7 +6,7 @@ const value = ref('Click to clear')
<UInput
v-model="value"
placeholder="Type something..."
:ui="{ trailing: 'pe-1' }"
:ui="{ trailing: 'pr-0.5' }"
>
<template v-if="value?.length" #trailing>
<UButton

View File

@@ -1,33 +0,0 @@
<script setup lang="ts">
const value = ref('npx nuxi module add ui')
const copied = ref(false)
function copy() {
navigator.clipboard.writeText(value.value)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
}
</script>
<template>
<UInput
v-model="value"
:ui="{ trailing: 'pr-0.5' }"
>
<template v-if="value?.length" #trailing>
<UTooltip text="Copy to clipboard" :content="{ side: 'right' }">
<UButton
:color="copied ? 'success' : 'neutral'"
variant="link"
size="sm"
:icon="copied ? 'i-lucide-copy-check' : 'i-lucide-copy'"
aria-label="Copy to clipboard"
@click="copy"
/>
</UTooltip>
</template>
</UInput>
</template>

View File

@@ -1,21 +0,0 @@
<script setup lang="ts">
const input = useTemplateRef('input')
defineShortcuts({
'/': () => {
input.value?.inputRef?.focus()
}
})
</script>
<template>
<UInput
ref="input"
icon="i-lucide-search"
placeholder="Search..."
>
<template #trailing>
<UKbd value="/" />
</template>
</UInput>
</template>

View File

@@ -40,7 +40,7 @@ const text = computed(() => {
placeholder="Password"
:color="color"
:type="show ? 'text' : 'password'"
:ui="{ trailing: 'pe-1' }"
:ui="{ trailing: 'pr-0.5' }"
:aria-invalid="score < 4"
aria-describedby="password-strength"
class="w-full"

View File

@@ -8,7 +8,7 @@ const password = ref('password')
v-model="password"
placeholder="Password"
:type="show ? 'text' : 'password'"
:ui="{ trailing: 'pe-1' }"
:ui="{ trailing: 'pr-0.5' }"
>
<template #trailing>
<UButton

View File

@@ -1,40 +0,0 @@
<script setup lang="ts">
const { data: countries, status, execute } = await useLazyFetch<{
name: string
code: string
emoji: string
}[]>('/api/countries.json', {
immediate: false,
default: () => []
})
function onOpen() {
if (!countries.value?.length) {
execute()
}
}
</script>
<template>
<USelectMenu
:items="countries"
:loading="status === 'pending'"
label-key="name"
:search-input="{ icon: 'i-lucide-search' }"
placeholder="Select country"
class="w-48"
@update:open="onOpen"
>
<template #leading="{ modelValue, ui }">
<span v-if="modelValue" class="size-5 text-center">
{{ modelValue?.emoji }}
</span>
<UIcon v-else name="i-lucide-earth" :class="ui.leadingIcon()" />
</template>
<template #item-leading="{ item }">
<span class="size-5 text-center">
{{ item.emoji }}
</span>
</template>
</USelectMenu>
</template>

View File

@@ -16,7 +16,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
<USelectMenu
:items="users || []"
:loading="status === 'pending'"
:filter="['label', 'email']"
:filter="['name', 'email']"
icon="i-lucide-user"
placeholder="Select user"
class="w-80"

View File

@@ -7,9 +7,9 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.duration"
size="sm"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded-[var(--ui-radius)]"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
:ui="{
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l-[var(--ui-radius)] flex border-r border-[var(--ui-border-accented)]',
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
container: 'mt-0'
}"
@@ -18,7 +18,8 @@ const appConfig = useAppConfig()
v-model="appConfig.toaster.duration"
color="neutral"
variant="soft"
:ui="{ base: 'rounded-[var(--ui-radius)] rounded-l-none min-w-12' }"
class="rounded rounded-l-none min-w-12"
:search-input="false"
/>
</UFormField>
</div>

View File

@@ -7,9 +7,9 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.expand"
size="sm"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded-[var(--ui-radius)]"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
:ui="{
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l-[var(--ui-radius)] flex border-r border-[var(--ui-border-accented)]',
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
container: 'mt-0'
}"
@@ -19,7 +19,7 @@ const appConfig = useAppConfig()
:items="[true, false]"
color="neutral"
variant="soft"
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
class="rounded rounded-l-none min-w-12"
:search-input="false"
/>
</UFormField>

View File

@@ -10,9 +10,9 @@ const appConfig = useAppConfig()
<UFormField
label="toaster.position"
size="sm"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded-[var(--ui-radius)]"
class="inline-flex ring ring-[var(--ui-border-accented)] rounded"
:ui="{
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l-[var(--ui-radius)] flex border-r border-[var(--ui-border-accented)]',
wrapper: 'bg-[var(--ui-bg-elevated)]/50 rounded-l flex border-r border-[var(--ui-border-accented)]',
label: 'text-[var(--ui-text-muted)] px-2 py-1.5',
container: 'mt-0'
}"
@@ -22,7 +22,7 @@ const appConfig = useAppConfig()
:items="positions"
color="neutral"
variant="soft"
class="rounded-[var(--ui-radius)] rounded-l-none min-w-12"
class="rounded rounded-l-none min-w-12"
:search-input="false"
/>
</UFormField>

View File

@@ -56,7 +56,7 @@ defineProps({
<h1 class="m-0 text-[75px] font-semibold mb-2 text-white flex items-center">
<span>{{ title }}</span>
</h1>
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight text-balance">
<p v-if="description" class="text-[32px] text-[#94a3b8] leading-tight">
{{ description.slice(0, 200) }}
</p>
</div>

View File

@@ -1,23 +0,0 @@
import { createSharedComposable } from '@vueuse/core'
function _useSharedData() {
const framework = useCookie('nuxt-ui-framework', { default: () => 'nuxt' })
const frameworks = computed(() => [{
label: 'Nuxt',
icon: 'i-logos-nuxt-icon',
value: 'nuxt',
onSelect: () => framework.value = 'nuxt'
}, {
label: 'Vue',
icon: 'i-logos-vue',
value: 'vue',
onSelect: () => framework.value = 'vue'
}].map(f => ({ ...f, active: framework.value === f.value })))
return {
framework,
frameworks
}
}
export const useSharedData = createSharedComposable(_useSharedData)

View File

@@ -10,10 +10,6 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
<UPage>
<template #left>
<UPageAside>
<template #top>
<FrameworkSelect />
</template>
<UContentNavigation :navigation="navigation" highlight />
</UPageAside>
</template>

View File

@@ -21,32 +21,10 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
const breadcrumb = computed(() => mapContentNavigation(findPageBreadcrumb(navigation?.value, page.value)).map(({ icon, ...link }) => link))
const { framework } = useSharedData()
// Redirect to the correct framework version if the page is not the current framework
if (!import.meta.prerender) {
watch(framework, () => {
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
if (route.path.endsWith(`/${page.value?.navigation?.framework}`)) {
navigateTo(`${route.path.split('/').slice(0, -1).join('/')}/${framework.value}`)
} else {
navigateTo(`/getting-started`)
}
}
})
}
// Update the framework if the page has a different framework
watch(page, () => {
if (page.value?.navigation?.framework && page.value?.navigation?.framework !== framework.value) {
framework.value = page.value?.navigation?.framework as string
}
}, { immediate: true })
useSeoMeta({
titleTemplate: '%s - Nuxt UI v3',
title: typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title,
ogTitle: `${typeof page.value.navigation === 'object' && page.value.navigation.title ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
title: typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title,
ogTitle: `${typeof page.value.navigation === 'object' ? page.value.navigation.title : page.value.title} - Nuxt UI v3`,
description: page.value.description,
ogDescription: page.value.description
})
@@ -97,6 +75,21 @@ const communityLinks = computed(() => [{
</template>
<template #links>
<UDropdownMenu v-if="page.select" v-slot="{ open }" :items="page.select.items" :content="{ align: 'end' }">
<UButton
color="neutral"
variant="subtle"
v-bind="page.select.items.find((item: any) => item.to === route.path)"
block
trailing-icon="i-lucide-chevron-down"
:class="[open && 'bg-[var(--ui-bg-accented)]/75']"
:ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}"
class="w-[128px]"
/>
</UDropdownMenu>
<UButton
v-for="link in page.links"
:key="link.label"

View File

@@ -5,10 +5,6 @@ export const collections = {
type: 'page',
source: '**/*',
schema: z.object({
navigation: z.object({
title: z.string().optional(),
framework: z.string().optional()
}),
links: z.array(z.object({
label: z.string(),
icon: z.string(),
@@ -18,7 +14,14 @@ export const collections = {
}).optional(),
to: z.string(),
target: z.string().optional()
}))
})),
select: z.object({
items: z.array(z.object({
label: z.string(),
icon: z.string(),
to: z.string()
}))
})
})
})
}

View File

@@ -21,15 +21,15 @@ This transition empowers Nuxt UI to become a more comprehensive and flexible UI
### Tailwind CSS v4
Nuxt UI v3 integrates the latest Tailwind CSS v4 beta (released Nov 21, 2024), bringing significant improvements:
Nuxt UI v3 integrates the latest Tailwind CSS v4 alpha (announced March 6, 2024), bringing significant improvements:
- **Built for performance**: Full builds in the new engine are up to 5x faster, and incremental builds are over 100x faster — and measured in microseconds.
- **Unified toolchain**: Built-in import handling, vendor prefixing, and syntax transforms, with no additional tooling required.
- **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file.
- **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more.
- **Faster Builds**: Up to 10x faster, especially for larger projects.
- **Unified Toolchain**: Built-in features like vendor prefixing, nesting support, and modern CSS transforms.
- **CSS-First Configuration**: New `@theme` directive for easy customization without JavaScript.
- **Optimized Performance**: Smaller engine footprint and more efficient processing.
::note
For a comprehensive overview of Tailwind CSS v4 beta features, read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta).
For a comprehensive overview of Tailwind CSS v4 alpha features, visit the [official announcement](https://tailwindcss.com/blog/tailwindcss-v4-alpha).
::
### Tailwind Variants
@@ -71,7 +71,7 @@ You can now use Nuxt UI in any Vue project without Nuxt by adding the Vite and V
- **Developer Experience**: Complete TypeScript support with IntelliSense and auto-completion
::tip{to="/getting-started/installation/vue"}
Learn how to install and configure Nuxt UI in a Vue project in the **Vue installation guide**.
Learn how to install and configure Nuxt UI in a Vue project in the [Vue installation guide](/getting-started/installation/vue).
::
## Migration
@@ -90,10 +90,6 @@ Key points to consider:
The transition to v3 involves significant changes, including new component structures, updated theming approaches, and revised TypeScript definitions. We recommend a careful, incremental upgrade process, starting with thorough testing in a development environment.
::
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
Nuxt UI is now compatible with Vue! You can follow the [installation guide](/getting-started/installation/vue) to get started.
::
::accordion-item{label="What about Nuxt UI Pro?"}
We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package but it only contains the components to build this documentation yet. This will be a free update, so the license you buy now will be valid for v3. We're actively working to finish the rewrite of all Nuxt UI Pro components.
::
@@ -102,12 +98,16 @@ Key points to consider:
Nuxt UI v3 is currently designed to work exclusively with Tailwind CSS. While there's interest in UnoCSS support, implementing it would require significant changes to the theme structure due to differences in class naming conventions. As a result, we don't have plans to add UnoCSS support in v3.
::
::accordion-item{label="Is Nuxt UI v3 compatible with standalone Vue projects?"}
We're planning to add Vue support in the near future. For now, Nuxt UI v3 is only available for Nuxt. Track progress on Vue compatibility [in this issue](https://github.com/nuxt/ui/issues/2129).
::
::accordion-item{label="How does Nuxt UI v3 handle accessibility?"}
Nuxt UI v3 enhances accessibility through Radix Vue integration. This provides automatic ARIA attributes, keyboard navigation support, intelligent focus management, and screen reader announcements. While offering a strong foundation, proper implementation and testing in your specific use case remains crucial for full accessibility compliance. For more detailed information, refer to [Radix Vue's accessibility documentation](https://www.radix-vue.com/overview/accessibility.html).
::
::accordion-item{label="What is the testing approach for Nuxt UI v3?"}
Nuxt UI v3 ensures reliability with 1000+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
Nuxt UI v3 ensures reliability with 800+ Vitest tests, covering core functionality and accessibility. This robust testing suite supports the library's stability and serves as a reference for developers.
::
::accordion-item{label="Is this version stable and suitable for production use?"}

View File

@@ -1 +0,0 @@
shadow: true

View File

@@ -1,18 +1,20 @@
---
title: Installation
title: Install in a Nuxt app
navigation.title: Nuxt
description: 'Learn how to install and configure Nuxt UI in your Nuxt application.'
navigation.framework: nuxt
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/installation/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/installation/vue
---
::callout{to="/getting-started/installation/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Setup
::steps{level="4"}
#### Install the Nuxt UI v3 alpha package
1. Install the Nuxt UI v3 alpha package:
::code-group{sync="pm"}
@@ -35,10 +37,10 @@ bun add @nuxt/ui@next
::
::warning
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist) in your `.npmrc` file or install `tailwindcss@next` in your project's root directory.
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist) in your `.npmrc` file or install `tailwindcss@next` directly in your project's root directory.
::
#### Add the Nuxt UI module in your `nuxt.config.ts`{lang="ts-type"}
2. Register the Nuxt UI module in your `nuxt.config.ts`{lang="ts-type"}:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
@@ -46,24 +48,15 @@ export default defineNuxtConfig({
})
```
#### Import Tailwind CSS and Nuxt UI in your CSS
3. Import Tailwind CSS and Nuxt UI in your `app.vue`{lang="ts-type"} or [CSS](https://nuxt.com/docs/getting-started/styling#the-css-property):
```css [assets/css/main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
</style>
```
::note
Use the `css` property in your `nuxt.config.ts` to import your CSS file.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['@nuxt/ui'],
css: ['~/assets/css/main.css']
})
```
::
::tip
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension for VSCode and add the following settings:
```json
@@ -77,6 +70,8 @@ It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.
::
::warning
IntelliSense works better when importing `tailwindcss` in a proper `.css` file which will be automatically detected.
::
## Options

View File

@@ -1,18 +1,20 @@
---
title: Installation
navigation.title: Vue
title: Install in a Vue app
description: 'Learn how to install and configure Nuxt UI in your Vue application.'
navigation.framework: vue
select:
items:
- label: Nuxt
icon: i-logos-nuxt-icon
to: /getting-started/installation/nuxt
- label: Vue
icon: i-logos-vue
to: /getting-started/installation/vue
---
::callout{to="/getting-started/installation/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Setup
::steps{level="4"}
#### Install the Nuxt UI v3 alpha package
1. Install the Nuxt UI v3 alpha package:
::code-group{sync="pm"}
@@ -35,12 +37,12 @@ bun add @nuxt/ui@next
::
::warning
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist) in your `.npmrc` file or install `tailwindcss@next`, `vue-router` and `@unhead/vue` in your project's root directory.
If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist) in your `.npmrc` file or install `tailwindcss@next`, `vue-router` and `@unhead/vue` directly in your project's root directory.
::
#### Add the Nuxt UI Vite plugin in your `vite.config.ts`{lang="ts-type"}
2. Add the Nuxt UI Vite plugin in your `vite.config.ts`{lang="ts-type"}:
```ts [vite.config.ts]{3,8}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
@@ -49,7 +51,7 @@ export default defineConfig({
plugins: [
vue(),
ui()
]
],
})
```
@@ -69,45 +71,28 @@ components.d.ts
```
::
#### Use the Nuxt UI Vue plugin in your `main.ts`
3. Register the Nuxt UI Vue plugin in your app:
```ts [main.ts]{2,7}
```ts [main.ts]
import { createApp } from 'vue'
import ui from '@nuxt/ui/vue-plugin'
import nuxtUI from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
app.use(ui)
// ...
app.use(nuxtUI)
app.mount('#app')
```
#### Import Tailwind CSS and Nuxt UI in your CSS
4. Import Tailwind CSS and Nuxt UI in your `App.vue`{lang="ts-type"} or CSS:
```css [assets/main.css]
```vue [App.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
</style>
```
::note
Import the CSS file in your `main.ts`.
```ts [main.ts]{1}
import './assets/main.css'
import { createApp } from 'vue'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const app = createApp(App)
app.use(ui)
app.mount('#app')
```
::
::tip
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension for VSCode and add the following settings:
```json
@@ -121,6 +106,8 @@ It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.
::
::warning
IntelliSense works better when importing `tailwindcss` in a proper `.css` file which will be automatically detected.
::
## Options
@@ -152,6 +139,10 @@ export default defineConfig({
Use the `ui` option to provide configuration for Nuxt UI.
::warning
In the rest of the docs, there are references to the `app.config.ts` file (which is a Nuxt feature). When using Nuxt UI in a project without Nuxt, this configuration is passed in this `ui` option instead.
::
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

View File

@@ -5,18 +5,19 @@ description: 'Learn how to customize Nuxt UI components using Tailwind CSS v4, C
## Tailwind CSS
Nuxt UI v3 uses Tailwind CSS v4 beta, you can read the [prerelease documentation](https://tailwindcss.com/docs/v4-beta) for more information.
Nuxt UI v3 uses Tailwind CSS v4 alpha which doesn't have a documentation yet, let's have a look on how to use it.
### `@theme`
Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your theme with CSS variables inside a `@theme` directive:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-sans: 'Public Sans', sans-serif;
--font-family-sans: 'Public Sans', sans-serif;
--breakpoint-3xl: 1920px;
@@ -32,12 +33,13 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
--color-green-900: #0A5331;
--color-green-950: #052E16;
}
</style>
```
The `@theme` directive tells Tailwind to make new utilities and variants available based on these variables. It's the equivalent of the `theme.extend` key in Tailwind CSS v3 `tailwind.config.ts` file.
::note{to="https://tailwindcss.com/docs/v4-beta#css-first-configuration" target="_blank"}
Learn more about Tailwind CSS v4 CSS-first configuration approach.
::note
You can learn more about this on [https://tailwindcss.com/blog/tailwindcss-v4-alpha](https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration).
::
### `@source`
@@ -46,30 +48,34 @@ You can use the `@source` directive to add explicit content glob patterns if you
This can be useful when writing Tailwind classes in markdown files with [`@nuxt/content`](https://github.com/nuxt/content):
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@source "../content";
@source "../content/**/*.md";
</style>
```
::note{to="https://tailwindcss.com/docs/v4-beta#adding-content-sources"}
Learn how to add content sources in Tailwind CSS v4.
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14078"}
You can learn more about the `@source` directive in this pull request.
::
### `@plugin`
You can use the `@plugin` directive to import Tailwind CSS plugins.
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@plugin "@tailwindcss/typography";
</style>
```
::note{to="https://tailwindcss.com/docs/v4-beta#using-plugins"}
Learn more about using plugins in Tailwind CSS v4.
::note{to="https://github.com/tailwindlabs/tailwindcss/pull/14264"}
You can learn more about the `@plugin` directive in this pull request.
::
## Design system
@@ -78,14 +84,8 @@ Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design
### Colors
::framework-only
#nuxt
Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
#vue
Nuxt UI leverages Vite config to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
::
| Color | Default | Description |
| --- | --- | --- |
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
@@ -96,8 +96,6 @@ Nuxt UI leverages Vite config to provide customizable color aliases based on [Ta
| `error`{color="error"} | `red` | Used for form error validation states. |
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
::framework-only
#nuxt
You can configure these color aliases at runtime in your `app.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
```ts [app.config.ts]
@@ -111,30 +109,6 @@ export default defineAppConfig({
})
```
#vue
You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key:
```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({
ui: {
colors: {
primary: 'blue',
neutral: 'zinc'
}
}
})
]
})
```
::
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-[var(--ui-primary)]"} theme picker in the header above to change `primary` and `neutral` colors.
::
@@ -150,10 +124,8 @@ slots:
---
::
::framework-only
#nuxt
:::tip
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file.
::tip
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation#themecolors) option in your `nuxt.config.ts` file.
```ts [app.config.ts]
export default defineAppConfig({
@@ -174,34 +146,7 @@ export default defineNuxtConfig({
}
})
```
:::
#vue
:::tip
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin.
```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({
ui: {
colors: {
tertiary: 'indigo'
}
},
theme: {
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
})
]
})
```
:::
::
::warning
@@ -209,7 +154,8 @@ These color aliases are not automatically defined as Tailwind CSS colors, so cla
However, you can generate these classes using Tailwind's `@theme` directive, allowing you to use custom color utility classes while maintaining dynamic color aliases:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@@ -226,6 +172,7 @@ However, you can generate these classes using Tailwind's `@theme` directive, all
--color-primary-900: var(--ui-color-primary-900);
--color-primary-950: var(--ui-color-primary-950);
}
</style>
```
::
@@ -270,7 +217,8 @@ You can use these variables in classes like `text-[var(--ui-primary)]`, it will
::tip
You can change which shade is used for each color on light and dark mode:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@@ -281,6 +229,7 @@ You can change which shade is used for each color on light and dark mode:
.dark {
--ui-primary: var(--ui-color-primary-200);
}
</style>
```
::
@@ -375,7 +324,8 @@ body {
::tip
You can customize these CSS variables to tailor the appearance of your application:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@@ -388,6 +338,7 @@ You can customize these CSS variables to tailor the appearance of your applicati
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
</style>
```
::
@@ -408,40 +359,15 @@ Try the :prose-icon{name="i-lucide-swatch-book" class="text-[var(--ui-primary)]"
::tip
You can customize the default radius value using the default Tailwind CSS variables or a value of your choice:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-radius: var(--radius-sm);
}
```
::
#### Container
Nuxt UI uses a global `--ui-container` CSS variable to define the width of the container:
```css
:root {
--ui-container: var(--container-7xl);
}
```
::tip
You can customize the default container width using the default Tailwind CSS variables or a value of your choice:
```css [main.css]
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--container-8xl: 90rem;
}
:root {
--ui-container: var(--container-8xl);
}
</style>
```
::
@@ -458,7 +384,7 @@ Components in Nuxt UI can have multiple `slots`, each representing a distinct HT
```ts [src/theme/card.ts]
export default {
slots: {
root: 'bg-[var(--ui-bg)] ring ring-[var(--ui-border)] divide-y divide-[var(--ui-border)] rounded-[calc(var(--ui-radius)*2)] shadow-sm',
root: 'bg-[var(--ui-bg)] ring ring-[var(--ui-border)] divide-y divide-[var(--ui-border)] rounded-[calc(var(--ui-radius)*2)] shadow',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
@@ -492,7 +418,7 @@ Some components don't have slots, they are just composed of a single root elemen
```ts [src/theme/container.ts]
export default {
base: 'max-w-[var(--ui-container)] mx-auto px-4 sm:px-6 lg:px-8'
base: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8'
}
```
@@ -551,19 +477,7 @@ props:
---
::
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided.
::framework-only
#nuxt
:::tip
These default values can be customized in your [`app.config.ts`](#config) to adjust the standard appearance of components throughout your application.
:::
#vue
:::tip
These default values can be customized in your [`vite.config.ts`](#config) to adjust the standard appearance of components throughout your application.
:::
::
The `defaultVariants` property specifies the default values for each variant. It determines how a component looks and behaves when no prop is provided. These default values can be customized in your [`app.config.ts`](#appconfigts) to adjust the standard appearance of components throughout your application.
## Customize theme
@@ -580,11 +494,9 @@ You can explore the theme for each component in two ways:
- Browse the source code directly in the GitHub repository at https://github.com/nuxt/ui/tree/v3/src/theme.
::
### Config
### `app.config.ts`
::framework-only
#nuxt
You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object.
You can override the theme of components inside your `app.config.ts` by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
@@ -600,42 +512,13 @@ export default defineAppConfig({
})
```
#vue
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
Let's say you want to change the font weight of all your buttons, you can do it like this:
```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({
ui: {
button: {
slots: {
base: 'font-bold'
}
}
}
})
]
})
```
::
::note
In this example, the `font-bold` class will override the default `font-medium` class on all buttons.
::
### Props
### `ui` prop
#### `ui` prop
You can also override a component's **slots** using the `ui` prop. This has priority over the global configuration and `variants` resolution.
You can also override a component's **slots** using the `ui` prop. This has priority over the `app.config.ts` configuration and `variants` resolution.
::component-code{slug="button"}
---
@@ -664,9 +547,9 @@ slots:
In this example, the `trailingIcon` slot is overwritten with `size-3` even though the `md` size variant would apply a `size-5` class to it.
::
#### `class` prop
### `class` prop
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the global configuration and `variants` resolution.
The `class` prop allows you to override the classes of the `root` or `base` slot. This has priority over the `app.config.ts` configuration and `variants` resolution.
::component-code{slug="button"}
---

View File

@@ -1,7 +1,6 @@
---
title: Icons
description: 'Nuxt UI integrates with Nuxt Icon to access over 200,000+ icons from Iconify.'
navigation.framework: nuxt
links:
- label: 'Iconify'
to: https://iconify.design/
@@ -13,14 +12,14 @@ links:
icon: i-simple-icons-github
---
::callout{to="/getting-started/icons/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxt/icon`](https://github.com/nuxt/icon) module for you, so there's no additional setup required.
::note
You can use any name from the https://icones.js.org collection.
::
### Icon Component
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
@@ -33,10 +32,6 @@ props:
---
::
::note
You can use any name from the https://icones.js.org collection.
::
### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:

View File

@@ -1 +0,0 @@
shadow: true

View File

@@ -1,55 +0,0 @@
---
title: Icons
description: 'Nuxt UI integrates with Iconify to access over 200,000+ icons.'
navigation.framework: vue
links:
- label: 'Iconify'
to: https://iconify.design/
target: _blank
icon: i-simple-icons-iconify
---
::callout{to="/getting-started/icons/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Usage
### Icon Component
You can use the [Icon](/components/icon) component with a `name` prop to display an icon:
::component-code{slug="icon"}
---
props:
name: 'i-lucide-lightbulb'
class: 'size-5'
---
::
::note
You can use any name from the https://icones.js.org collection.
::
### Component Props
Some components also have an `icon` prop to display an icon, like the [Button](/components/button) for example:
::component-code{slug="button"}
---
ignore:
- color
- variant
props:
icon: i-lucide-sun
variant: subtle
slots:
default: Button
---
::
## Theme
You can change the default icons used by Nuxt UI components in your `vite.config.ts`:
:icons-theme

View File

@@ -1,7 +1,6 @@
---
title: Fonts
description: 'Nuxt UI integrates with Nuxt Fonts to provide plug-and-play font optimization.'
navigation.framework: nuxt
links:
- label: 'nuxt/fonts'
to: https://github.com/nuxt/fonts
@@ -11,23 +10,21 @@ links:
## Usage
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS. It will be automatically loaded and optimized for you.
Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/fonts) module for you, so there's no additional setup required. To use a font in your Nuxt UI application, you can simply declare it in your CSS:
```css [main.css]
```vue [app.vue]
<style>
@import "tailwindcss";
@import "@nuxt/ui";
@theme {
--font-sans: 'Public Sans', sans-serif;
--font-family-sans: 'Public Sans', sans-serif;
}
</style>
```
You can disable the `@nuxt/fonts` module with the `ui.fonts` option in your `nuxt.config.ts`:
That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
fonts: false
}
})
```
::note{to="https://fonts.nuxt.com/advanced" target="_blank"}
Read more about how `@nuxt/fonts` work behind the scenes to optimize your fonts.
::

View File

@@ -0,0 +1,27 @@
---
title: Color Mode
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
links:
- label: 'nuxtjs/color-mode'
to: https://github.com/nuxt-modules/color-mode
target: _blank
icon: i-simple-icons-github
---
## Usage
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required.
You can disable dark mode by setting the `preference` to `light` instead of `system` in your `nuxt.config.ts`.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
colorMode: {
preference: 'light'
}
})
```
::tip
If you're stuck in dark mode even after changing this setting, you might need to remove the `nuxt-color-mode` entry from your browser's local storage.
::

View File

@@ -1 +0,0 @@
shadow: true

View File

@@ -1,58 +0,0 @@
---
title: Color Mode
description: 'Nuxt UI integrates with Nuxt Color Mode to allow for easy switching between light and dark themes.'
navigation.framework: nuxt
links:
- label: 'nuxtjs/color-mode'
to: https://github.com/nuxt-modules/color-mode
target: _blank
icon: i-simple-icons-github
---
::callout{to="/getting-started/color-mode/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Usage
Nuxt UI automatically registers the [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode) module for you, so there's no additional setup required. You can simply use the `useColorMode` composable to switch between light and dark modes:
```vue [ColorModeButton.vue]
<script setup>
const colorMode = useColorMode()
const isDark = computed({
get() {
return colorMode.value === 'dark'
},
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>
<template>
<ClientOnly v-if="!colorMode?.forced">
<UButton
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="size-8" />
</template>
</ClientOnly>
</template>
```
You can disable the `@nuxtjs/color-mode` module with the `ui.colorMode` option in your `nuxt.config.ts`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
colorMode: false
}
})
```

View File

@@ -1,47 +0,0 @@
---
title: Color Mode
description: 'Nuxt UI integrates with VueUse to allow for easy switching between light and dark themes.'
navigation.framework: vue
---
::callout{to="/getting-started/color-mode/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Usage
Nuxt UI automatically registers the [useDark](https://vueuse.org/core/useDark) composable as a Vue plugin, so there's no additional setup required. You can simply use it to switch between light and dark modes:
```vue [ColorModeButton.vue]
<script setup>
import { useColorMode } from '@vueuse/core'
const mode = useColorMode()
</script>
<template>
<UButton
:icon="mode === 'dark' ? 'i-lucide-moon' : 'i-lucide-sun'"
color="neutral"
variant="ghost"
@click="mode = mode === 'dark' ? 'light' : 'dark'"
/>
</template>
```
You can disable this plugin with the `colorMode` option in your `vite.config.ts`:
```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({
colorMode: false
})
]
})
```

View File

@@ -1,2 +0,0 @@
badge: New
shadow: true

View File

@@ -1,163 +0,0 @@
---
title: Internationalization (i18n)
description: 'Learn how to internationalize your Nuxt app with multi-directional support (LTR/RTL).'
navigation.framework: nuxt
---
::callout{to="/getting-started/i18n/vue" icon="i-logos-vue" class="hidden"}
Looking for the **Vue** version?
::
## Usage
::note{to="/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [app.vue]
<script setup lang="ts">
import { fr } from '@nuxt/ui/locale'
</script>
<template>
<UApp :locale="fr">
<NuxtPage />
</UApp>
</template>
```
### Custom locale
You also have the option to add your own locale using `defineLocale`:
```vue [app.vue]
<script setup lang="ts">
const locale = defineLocale({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
<template>
<UApp :locale="locale">
<NuxtPage />
</UApp>
</template>
```
::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
* `hi` Hindi (language)
* `de-AT`: German (language) as used in Austria (region)
::
### Dynamic locale
To dynamically switch between languages, you can use the [Nuxt I18n](https://i18n.nuxtjs.org/) module.
::steps{level="4"}
#### Install the Nuxt I18n package
::code-group{sync="pm"}
```bash [pnpm]
pnpm add @nuxtjs/i18n@next
```
```bash [yarn]
yarn add @nuxtjs/i18n@next
```
```bash [npm]
npm install @nuxtjs/i18n@next
```
```bash [bun]
bun add @nuxtjs/i18n@next
```
::
#### Add the Nuxt I18n module in your `nuxt.config.ts`{lang="ts-type"}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: [
'@nuxt/ui',
'@nuxtjs/i18n'
],
i18n: {
locales: [{
code: 'de',
name: 'Deutsch'
}, {
code: 'en',
name: 'English'
}, {
code: 'fr',
name: 'Français'
}]
}
})
```
#### Set the `locale` prop using `useI18n`
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui/locale'
const { locale } = useI18n()
</script>
<template>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</template>
```
::
### Dynamic direction
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://nuxt.com/docs/api/composables/use-head) composable:
```vue [app.vue]
<script setup lang="ts">
import * as locales from '@nuxt/ui/locale'
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</template>
```
## Supported languages
:supported-languages

View File

@@ -1,176 +0,0 @@
---
title: Internationalization (i18n)
description: 'Learn how to internationalize your Vue app with multi-directional support (LTR/RTL).'
navigation.framework: vue
---
::callout{to="/getting-started/i18n/nuxt" icon="i-logos-nuxt-icon" class="hidden"}
Looking for the **Nuxt** version?
::
## Usage
::note{to="/components/app"}
Nuxt UI provides an **App** component that wraps your app to provide global configurations.
::
### Locale
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
```vue [App.vue]
<script setup lang="ts">
import { fr } from '@nuxt/ui/locale'
</script>
<template>
<UApp :locale="fr">
<RouterView />
</UApp>
</template>
```
### Custom locale
You also have the option to add your locale using `defineLocale`:
```vue [App.vue]
<script setup lang="ts">
import { defineLocale } from '@nuxt/ui/runtime/composables/defineLocale'
const locale = defineLocale({
name: 'My custom locale',
code: 'en',
dir: 'ltr',
messages: {
// implement pairs
}
})
</script>
<template>
<UApp :locale="locale">
<RouterView />
</UApp>
</template>
```
::tip
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
* `hi` Hindi (language)
* `de-AT`: German (language) as used in Austria (region)
::
### Dynamic locale
To dynamically switch between languages, you can use the [Vue I18n](https://vue-i18n.intlify.dev/) plugin.
::steps{level="4"}
#### Install the Vue I18n package
::code-group{sync="pm"}
```bash [pnpm]
pnpm add vue-i18n@10
```
```bash [yarn]
yarn add vue-i18n@10
```
```bash [npm]
npm install vue-i18n@10
```
```bash [bun]
bun add vue-i18n@10
```
::
#### Use the Vue I18n plugin in your `main.ts`
```ts [main.ts]{2,6-18,22}
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import ui from '@nuxt/ui/vue-plugin'
import App from './App.vue'
const i18n = createI18n({
legacy: false,
locale: 'en',
availableLocales: ['en', 'de'],
messages: {
en: {
// ...
},
de: {
// ...
}
}
})
const app = createApp(App)
app.use(i18n)
app.use(ui)
app.mount('#app')
```
#### Set the `locale` prop using `useI18n`
```vue [App.vue]
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import * as locales from '@nuxt/ui/locale'
const { locale } = useI18n()
</script>
<template>
<UApp :locale="locales[locale]">
<RouterView />
</UApp>
</template>
```
::
### Dynamic direction
Each locale has a `dir` property which will be used by the `App` component to set the directionality of all components.
In a multilingual application, you might want to set the `lang` and `dir` attributes on the `<html>` element dynamically based on the user's locale, which you can do with the [useHead](https://unhead.unjs.io/usage/composables/use-head) composable:
```vue [App.vue]
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useHead } from '@unhead/vue'
import * as locales from '@nuxt/ui/locale'
const { locale } = useI18n()
const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)
useHead({
htmlAttrs: {
lang,
dir
}
})
</script>
<template>
<UApp :locale="locales[locale]">
<RouterView />
</UApp>
</template>
```
## Supported languages
:supported-languages

View File

@@ -27,18 +27,6 @@ Use it as at the root of your app:
</template>
```
::framework-only
#nuxt
:::tip{to="/getting-started/i18n/nuxt#locale"}
Learn how to use the `locale` prop to change the locale of your app.
:::
#vue
:::tip{to="/getting-started/i18n/vue#locale"}
Learn how to use the `locale` prop to change the locale of your app.
:::
::
## API
### Props

View File

@@ -168,16 +168,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
## Examples

View File

@@ -178,16 +178,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Actions

View File

@@ -67,16 +67,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronRight` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronRight` key.
:::
::
## Examples

View File

@@ -197,16 +197,8 @@ slots:
Button
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled

View File

@@ -1,248 +0,0 @@
---
title: Calendar
description: A calendar component for selecting single dates, multiple dates or date ranges.
links:
- label: Calendar
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/calendar.html
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Calendar.vue
navigation.badge: New
---
::note
This component relies on the [@internationalized/date](https://react-spectrum.adobe.com/internationalized/date/index.html) package which provides objects and functions for representing and manipulating dates and times in a locale-aware manner.
::
## Usage
Use the `v-model` directive to control the selected date.
::component-code
---
cast:
modelValue: DateValue
ignore:
- modelValue
external:
- modelValue
props:
modelValue: [2022, 2, 3]
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
cast:
defaultValue: DateValue
ignore:
- defaultValue
external:
- defaultValue
props:
defaultValue: [2022, 2, 6]
---
::
### Multiple
Use the `multiple` prop to allow multiple selections.
::component-code
---
prettier: true
cast:
modelValue: DateValue[]
ignore:
- multiple
- modelValue
external:
- modelValue
props:
multiple: true
modelValue: [[2022, 2, 4], [2022, 2, 6], [2022, 2, 8]]
---
::
### Range
Use the `range` prop to select a range of dates.
::component-code
---
prettier: true
cast:
modelValue: DateRange
ignore:
- range
- modelValue.start
- modelValue.end
external:
- modelValue
props:
range: true
modelValue:
start: [2022, 2, 3]
end: [2022, 2, 20]
---
::
### Color
Use the `color` prop to change the color of the calendar.
::component-code
---
props:
color: neutral
---
::
### Size
Use the `size` prop to change the size of the calendar.
::component-code
---
props:
size: xl
---
::
### Disabled
Use the `disabled` prop to disable the calendar.
::component-code
---
props:
disabled: true
---
::
### Number Of Months
Use the `numberOfMonths` prop to change the number of months in the calendar.
::component-code
---
props:
numberOfMonths: 3
---
::
### Month Controls
Use the `month-controls` prop to show the month controls. Defaults to `true`.
::component-code
---
props:
monthControls: false
---
::
### Year Controls
Use the `year-controls` prop to show the year controls. Defaults to `true`.
::component-code
---
props:
yearControls: false
---
::
### Fixed Weeks
Use the `fixed-weeks` prop to display the calendar with fixed weeks.
::component-code
---
props:
fixedWeeks: false
---
::
## Examples
### With chip events
Use the [Chip](/components/chip) component to add events to specific days.
::component-example
---
name: 'calendar-events-example'
---
::
### With disabled dates
Use the `is-date-disabled` prop with a function to mark specific dates as disabled.
::component-example
---
name: 'calendar-disabled-dates-example'
---
::
### With unavailable dates
Use the `is-date-unavailable` prop with a function to mark specific dates as unavailable.
::component-example
---
name: 'calendar-unavailable-dates-example'
---
::
### With min/max dates
Use the `min-value` and `max-value` props to limit the dates.
::component-example
---
name: 'calendar-min-max-dates-example'
---
::
### As a DatePicker
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.
::component-example
---
name: 'calendar-date-picker-example'
---
::
### As a DateRangePicker
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date range picker.
::component-example
---
name: 'calendar-date-range-picker-example'
---
::
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
## Theme
:component-theme

View File

@@ -67,7 +67,7 @@ class: 'p-8'
### Prev / Next
Use the `prev` and `next` props to customize the prev and next buttons with any [Button](/components/button) props.
Use the `prev` and `next` props to customize the prev and next buttons.
::component-example
---
@@ -76,7 +76,7 @@ class: 'p-8'
---
::
### Prev / Next Icons
### Prev Icon / Next Icon
Use the `prev-icon` and `next-icon` props to customize the buttons [Icon](/components/icon). Defaults to `i-lucide-arrow-left` / `i-lucide-arrow-right`.
@@ -94,16 +94,8 @@ options:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize these icons globally in your `app.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize these icons globally in your `vite.config.ts` under `ui.icons.arrowLeft` / `ui.icons.arrowRight` key.
:::
::
### Dots

View File

@@ -58,16 +58,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.minus` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.minus` key.
:::
::
### Label
@@ -123,16 +115,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Color

View File

@@ -42,7 +42,6 @@ Each group takes some `items` as an array of objects with the following properti
- `avatar?: AvatarProps`{lang="ts-type"}
- `chip?: ChipProps`{lang="ts-type"}
- `kbds?: string[] | KbdProps[]`{lang="ts-type"}
- `active?: boolean`{lang="ts-type"}
- `loading?: boolean`{lang="ts-type"}
- `disabled?: boolean`{lang="ts-type"}
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
@@ -216,16 +215,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.search` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.search` key.
:::
::
### Loading
@@ -285,16 +276,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -420,16 +403,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
## Examples

View File

@@ -40,7 +40,7 @@ prettier: true
collapse: true
ignore:
- items
- ui.content
- class
external:
- items
props:
@@ -90,8 +90,7 @@ props:
- option
- meta
- j
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -120,7 +119,7 @@ Use the `size` prop to change the size of the ContextMenu.
prettier: true
ignore:
- items
- ui.content
- class
external:
- items
props:
@@ -132,8 +131,7 @@ props:
icon: i-lucide-sun
- label: Dark
icon: i-lucide-moon
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -154,7 +152,7 @@ Use the `disabled` prop to disable the ContextMenu.
prettier: true
ignore:
- items
- ui.content
- class
external:
- items
props:
@@ -166,8 +164,7 @@ props:
icon: i-lucide-sun
- label: Dark
icon: i-lucide-moon
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |

View File

@@ -40,7 +40,7 @@ prettier: true
collapse: true
ignore:
- items
- ui.content
- class
external:
- items
props:
@@ -91,8 +91,7 @@ props:
- shift
- meta
- q
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -119,7 +118,7 @@ Use the `content` prop to control how the DropdownMenu content is rendered, like
prettier: true
ignore:
- items
- ui.content
- class
external:
- items
items:
@@ -144,8 +143,7 @@ props:
align: start
side: bottom
sideOffset: 8
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -165,7 +163,7 @@ prettier: true
ignore:
- arrow
- items
- ui.content
- class
external:
- items
props:
@@ -177,8 +175,7 @@ props:
icon: i-lucide-credit-card
- label: Settings
icon: i-lucide-cog
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -197,8 +194,8 @@ Use the `size` prop to control the size of the DropdownMenu.
prettier: true
ignore:
- items
- class
- content.align
- ui.content
external:
- items
props:
@@ -212,8 +209,7 @@ props:
icon: i-lucide-cog
content:
align: start
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |
@@ -240,7 +236,7 @@ Use the `disabled` prop to disable the DropdownMenu.
prettier: true
ignore:
- items
- ui.content
- class
external:
- items
props:
@@ -252,8 +248,7 @@ props:
icon: i-lucide-credit-card
- label: Settings
icon: i-lucide-cog
ui:
content: 'w-48'
class: 'w-48'
slots:
default: |

View File

@@ -64,7 +64,7 @@ It requires two props:
::
::
Errors are reported directly to the [FormField](/components/form-field) component based on the `name` or `error-pattern` prop. This means the validation rules defined for the `email` attribute in your schema will be applied to `<FormField name="email">`{lang="vue"}.
Errors are reported directly to the [FormField](/components/form-field) component based on the `name` prop. This means the validation rules defined for the `email` attribute in your schema will be applied to `<FormField name="email">`{lang="vue"}.
Nested validation rules are handled using dot notation. For example, a rule like `{ user: z.object({ email: z.string() }) }`{lang="ts"} will be applied to `<FormField name="user.email">`{lang="vue"}.

View File

@@ -1,15 +1,14 @@
---
description: A component to display any icon from Iconify.
description: A wrapper around Nuxt Icon component to display icons.
links:
- label: Icônes
to: https://icones.js.org/
target: _blank
icon: i-custom-icones-js
- label: Nuxt Icon
icon: i-simple-icons-github
to: https://github.com/nuxt/icon
---
## Usage
Use the `name` prop to display an icon:
You can use any name from the https://icones.js.org collection:
::component-code
---
@@ -19,15 +18,6 @@ props:
---
::
::framework-only
#nuxt
:::caution{to="/getting-started/icons/nuxt#collections"}
It's highly recommended to install the icons collections you need, read more about this.
:::
::tip
It's highly recommended to install the icons collections you need, read more about this in [Icons](/getting-started/icons#collections).
::
## API
### Props
:component-props

View File

@@ -189,16 +189,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Placeholder
@@ -222,42 +214,6 @@ props:
---
::
### Create Item
Use the `create-item` prop to allow user input.
::component-code
---
prettier: true
ignore:
- modelValue
- items
external:
- items
- modelValue
items:
createItem:
- true
- 'always'
props:
modelValue: 'Backlog'
items:
- Backlog
- Todo
- In Progress
- Done
createItem: true
---
::
::note
The create option shows when no match is found by default. Set it to `always` to show it even when similar values exist.
::
::tip{to="#emits"}
Use the `@create` event to handle the creation of the item. You will receive the event and the item as arguments.
::
### Content
Use the `content` prop to control how the InputMenu content is rendered, like its `align` or `side` for example.
@@ -449,16 +405,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Selected Icon
@@ -486,16 +434,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.check` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.check` key.
:::
::
### Avatar
@@ -574,16 +514,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -762,7 +694,7 @@ This example uses [refDebounced](https://vueuse.org/shared/refDebounced/#refdebo
### With custom search
Use the `filter` prop with an array of fields to filter on. Defaults to `[labelKey]`.
Use the `filter` prop with an array of fields to filter on.
::component-example
---
@@ -771,17 +703,6 @@ name: 'input-menu-filter-fields-example'
---
::
### As a CountryPicker
This example demonstrates using the InputMenu as a country picker with lazy loading - countries are only fetched when the menu is opened.
::component-example
---
collapse: true
name: 'input-menu-countries-example'
---
::
## API
### Props

View File

@@ -1,295 +0,0 @@
---
title: InputNumber
description: Input numerical values with a customizable range.
links:
- label: Number Field
icon: i-custom-radix-vue
to: https://www.radix-vue.com/components/number-field
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/InputNumber.vue
navigation.badge: New
---
::note
This component relies on the [@internationalized/number](https://react-spectrum.adobe.com/internationalized/number/index.html) package which provides utilities for formatting and parsing numbers across locales and numbering systems.
::
## Usage
Use the `v-model` directive to control the value of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
ignore:
- defaultValue
props:
defaultValue: 5
---
::
### Min / Max
Use the `min` and `max` props to set the minimum and maximum values of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
min: 0
max: 10
---
::
### Step
Use the `step` prop to set the step value of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
step: 2
---
::
### Orientation
Use the `orientation` prop to change the orientation of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
orientation: vertical
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: 'Enter a number'
---
::
### Color
Use the `color` prop to change the ring color when the InputNumber is focused.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
color: neutral
highlight: true
---
::
### Variant
Use the `variant` prop to change the variant of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
variant: subtle
color: neutral
highlight: false
---
::
### Size
Use the `size` prop to change the size of the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
size: xl
---
::
### Disabled
Use the `disabled` prop to disable the InputNumber.
::component-code
---
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
disabled: true
---
::
### Increment / Decrement
Use the `increment` and `decrement` props to customize the increment and decrement buttons with any [Button](/components/button) props. Defaults to `{ variant: 'link' }`{lang="ts-type"}.
::component-code
---
prettier: true
ignore:
- modelValue
- increment.size
- increment.color
- increment.variant
- decrement.size
- decrement.color
- decrement.variant
external:
- modelValue
props:
modelValue: 5
increment:
color: neutral
variant: solid
size: xs
decrement:
color: neutral
variant: solid
size: xs
---
::
### Increment / Decrement Icons
Use the `increment-icon` and `decrement-icon` props to customize the buttons [Icon](/components/icon). Defaults to `i-lucide-plus` / `i-lucide-minus`.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: 5
incrementIcon: 'i-lucide-arrow-right'
decrementIcon: 'i-lucide-arrow-left'
---
::
## Examples
### With decimal format
Use the `format-options` prop to customize the format of the value.
::component-example
---
name: 'input-number-decimal-example'
---
::
### With percentage format
Use the `format-options` prop with `style: 'percent'` to customize the format of the value.
::component-example
---
name: 'input-number-percentage-example'
---
::
### With currency format
Use the `format-options` prop with `style: 'currency'` to customize the format of the value.
::component-example
---
name: 'input-number-currency-example'
---
::
### Within a FormField
You can use the InputNumber within a [FormField](/components/form-field) component to display a label, help text, required indicator, etc.
::component-example
---
name: 'input-number-form-field-example'
---
::
### With slots
Use the `#increment` and `#decrement` slots to customize the buttons.
::component-example
---
name: 'input-number-slots-example'
---
::
## API
### Props
:component-props
### Slots
:component-slots
### Emits
:component-emits
### Expose
When accessing the component via a template ref, you can use the following:
| Name | Type |
|----------------------------|-------------------------------------------------|
| `inputRef`{lang="ts-type"} | `Ref<HTMLInputElement \| null>`{lang="ts-type"} |
## Theme
:component-theme

View File

@@ -25,15 +25,15 @@ props:
Use the `type` prop to change the input type. Defaults to `text`.
Some types have been implemented in their own components such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), [InputNumber](/components/input-number) etc. and others have been styled like `file` for example.
Some types have been implemented in their own components such as [Checkbox](/components/checkbox), [Radio](/components/radio-group), etc. and others have been styled like `file` for example.
::component-code
---
items:
type:
- text
- number
- password
- number
- search
- file
props:
@@ -184,16 +184,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.loading` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.loading` key.
:::
::
### Disabled
@@ -222,16 +214,6 @@ name: 'input-clear-button-example'
---
::
### With copy button
You can put a [Button](/components/button) inside the `#trailing` slot to copy the value to the clipboard.
::component-example
---
name: 'input-copy-button-example'
---
::
### With password toggle
You can put a [Button](/components/button) inside the `#trailing` slot to toggle the password visibility.
@@ -263,20 +245,6 @@ name: 'input-character-limit-example'
---
::
### With keyboard shortcut
You can use the [Kbd](/components/kbd) component inside the `#trailing` slot to add a keyboard shortcut to the Input.
::component-example
---
name: 'input-kbd-example'
---
::
::note{to="/composables/define-shortcuts"}
This example uses the `defineShortcuts` composable to focus the Input when the :kbd{value="/"} key is pressed.
::
### With floating label
You can use the `#default` slot to add a floating label to the Input.
@@ -298,7 +266,7 @@ name: 'input-form-field-example'
::
::tip{to="/components/form"}
It also provides validation and error handling when used within a **Form** component.
It also provides validation and error handling when used within a [Form](/components/form) component.
::
### Within a ButtonGroup

View File

@@ -13,7 +13,6 @@ The Link component is a wrapper around [`<NuxtLink>`](https://nuxt.com/docs/api/
- `inactive-class` prop to set a class when the link is inactive, `active-class` is used when active.
- `exact` prop to style with `active-class` when the link is active and the route is exactly the same as the current route.
- `exact-query` and `exact-hash` props to style with `active-class` when the link is active and the query or hash is exactly the same as the current query or hash.
- use `exact-query="partial"` to style with `active-class` when the link is active and the query partially match the current query.
The incentive behind this is to provide the same API as NuxtLink back in Nuxt 2 / Vue 2. You can read more about it in the Vue Router [migration from Vue 2](https://router.vuejs.org/guide/migration/#removal-of-the-exact-prop-in-router-link) guide.

View File

@@ -156,16 +156,8 @@ slots:
:placeholder{class="h-48"}
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.close` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.close` key.
:::
::
### Overlay

View File

@@ -137,7 +137,7 @@ Each item can take a `children` array of objects with the following properties t
Use the `orientation` prop to change the orientation of the NavigationMenu.
When orientation is `vertical`, a [Collapsible](/components/collapsible) component is used to display children. You can control the open state of each item using the `open` and `defaultOpen` properties.
When orientation is `vertical`, a [Collapsible](/components/collapsible) component is used to display children.
::component-code
---
@@ -152,7 +152,6 @@ props:
items:
- - label: Guide
icon: i-lucide-book-open
defaultOpen: true
children:
- label: Introduction
description: Fully styled and customizable components for Nuxt.
@@ -434,16 +433,8 @@ props:
---
::
::framework-only
#nuxt
:::tip{to="/getting-started/icons/nuxt#theme"}
::tip{to="/getting-started/icons#theme"}
You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.
:::
#vue
:::tip{to="/getting-started/icons/vue#theme"}
You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.
:::
::
### Arrow

View File

@@ -1,182 +0,0 @@
---
title: PinInput
description: An input element to enter a pin.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/PinInput.vue
navigation.badge: New
---
## Usage
Use the `v-model` directive to control the value of the PinInput.
::component-code
---
prettier: true
ignore:
- modelValue
external:
- modelValue
props:
modelValue: []
---
::
Use the `default-value` prop to set the initial value when you do not need to control its state.
::component-code
---
prettier: true
ignore:
- defaultValue
props:
defaultValue: ['1','2','3']
---
::
### Type
Use the `type` prop to change the input type. Defaults to `text`.
::component-code
---
items:
type:
- text
- number
props:
type: 'number'
---
::
::note
When `type` is set to `number`, it will only accept numeric characters.
::
### Mask
Use the `mask` prop to treat the input like a password.
::component-code
---
prettier: true
ignore:
- placeholder
- defaultValue
props:
mask: true
defaultValue: ['1','2','3','4','5']
---
::
### OTP
Use the `otp` prop to enable One-Time Password functionality. When enabled, mobile devices can automatically detect and fill OTP codes from SMS messages or clipboard content, with autocomplete support.
::component-code
---
props:
otp: true
---
::
### Length
Use the `length` prop to change the amount of inputs.
::component-code
---
props:
length: 6
---
::
### Placeholder
Use the `placeholder` prop to set a placeholder text.
::component-code
---
props:
placeholder: '○'
---
::
### Color
Use the `color` prop to change the ring color when the PinInput is focused.
::component-code
---
ignore:
- placeholder
props:
color: neutral
highlight: true
placeholder: '○'
---
::
::note
The `highlight` prop is used here to show the focus state. It's used internally when a validation error occurs.
::
### Variant
Use the `variant` prop to change the variant of the PinInput.
::component-code
---
ignore:
- placeholder
props:
color: neutral
variant: subtle
highlight: false
placeholder: '○'
---
::
### Size
Use the `size` prop to change the size of the PinInput.
::component-code
---
ignore:
- placeholder
props:
size: xl
placeholder: '○'
---
::
### Disabled
Use the `disabled` prop to disable the PinInput.
::component-code
---
ignore:
- placeholder
props:
disabled: true
placeholder: '○'
---
::
## API
### Props
:component-props
### Emits
:component-emits
## Theme
:component-theme

Some files were not shown because too many files have changed in this diff Show More