Compare commits

..

3 Commits

Author SHA1 Message Date
Daniel Roe
c532a6f930 chore: bump to 0.1.9 2025-04-08 08:48:39 -07:00
Daniel Roe
6bbcb40c9e chore: bump to 0.1.8 2025-04-08 08:14:48 -07:00
Daniel Roe
befda895b9 build: bump vue-sfc-transformer 2025-04-07 19:25:13 -07:00
407 changed files with 17553 additions and 22679 deletions

View File

@@ -42,8 +42,6 @@ jobs:
- name: Build application - name: Build application
run: pnpm run docs:build run: pnpm run docs:build
env:
NODE_OPTIONS: '--max-old-space-size=8192'
- name: Deploy to NuxtHub - name: Deploy to NuxtHub
uses: nuxt-hub/action@v1 uses: nuxt-hub/action@v1

View File

@@ -18,7 +18,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: ${{ github.event_name == 'pull_request' && fromJSON('["ubuntu-latest"]') || fromJSON('["ubuntu-latest", "windows-latest"]') }} # macos-latest os: [ubuntu-latest] # macos-latest, windows-latest
node: [22] node: [22]
env: env:
@@ -65,8 +65,6 @@ jobs:
run: pnpm run dev:vue:build run: pnpm run dev:vue:build
- name: Publish - name: Publish
# Only publish preview package on ubuntu during PRs
if: matrix.os == 'ubuntu-latest'
run: pnpx pkg-pr-new publish --compact --no-template --pnpm run: pnpx pkg-pr-new publish --compact --no-template --pnpm
starter-nuxt: starter-nuxt:

View File

@@ -1,54 +0,0 @@
name: release
on:
push:
tags:
- 'v3*'
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest] # macos-latest, windows-latest
node: [22]
env:
NUXT_GITHUB_TOKEN: ${{ secrets.NUXT_GITHUB_TOKEN }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: pnpm
- name: Install dependencies
run: pnpm install
- name: Prepare
run: pnpm run dev:prepare
- name: Lint
run: pnpm run lint
- name: Typecheck
run: pnpm run typecheck
- name: Test
run: pnpm run test run
- name: Test (vue)
run: pnpm run test:vue run
- name: Publish
run: ./scripts/release.sh
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

1
.npmrc
View File

@@ -1,3 +1,4 @@
shamefully-hoist=true shamefully-hoist=true
auto-install-peers=true auto-install-peers=true
ignore-workspace-root-check=true ignore-workspace-root-check=true
shell-emulator=true

View File

@@ -3,9 +3,6 @@
"commitMessage": "chore(release): v${version}", "commitMessage": "chore(release): v${version}",
"tagName": "v${version}" "tagName": "v${version}"
}, },
"npm": {
"publish": false
},
"github": { "github": {
"release": true, "release": true,
"releaseName": "v${version}", "releaseName": "v${version}",

View File

@@ -1,74 +1,5 @@
# Changelog # Changelog
## [3.1.0](https://github.com/nuxt/ui/compare/v3.0.2...v3.1.0) (2025-04-24)
### ⚠ BREAKING CHANGES
* **OverlayProvider:** return an overlay instance from `.open()` (#3829)
### Features
* **App:** add global `portal` prop ([#3688](https://github.com/nuxt/ui/issues/3688)) ([29fa462](https://github.com/nuxt/ui/commit/29fa46276d6bf69b5b87880c476c6f778c2820bf))
* **Carousel:** add `select` event ([#3678](https://github.com/nuxt/ui/issues/3678)) ([22edfd7](https://github.com/nuxt/ui/commit/22edfd708ae3eeadbd4ff6c830cdfd5632948286))
* **CheckboxGroup:** new component ([#3862](https://github.com/nuxt/ui/issues/3862)) ([9c3d53a](https://github.com/nuxt/ui/commit/9c3d53a02d6254f6b5c90e5fed826b8aefcdb042))
* **components:** add new `content-top` and `content-bottom` slots ([#3886](https://github.com/nuxt/ui/issues/3886)) ([1a46394](https://github.com/nuxt/ui/commit/1a463946681e152aa18372118d0fef4a7d8055a5))
* **Form:** add `attach` prop to opt-out of nested form attachement ([#3939](https://github.com/nuxt/ui/issues/3939)) ([1a0d7a3](https://github.com/nuxt/ui/commit/1a0d7a3103cf7591b019ef3ad685e2f3786ef6f2))
* **Form:** export loading state ([#3861](https://github.com/nuxt/ui/issues/3861)) ([fdee252](https://github.com/nuxt/ui/commit/fdee2522bb9d8361ff3e9fdd4aa2350be8e49b05))
* **InputMenu/SelectMenu:** handle `resetSearchTermOnSelect` ([cea881a](https://github.com/nuxt/ui/commit/cea881abdc139b39df89b503cf2ab872f4246c8f)), closes [#3782](https://github.com/nuxt/ui/issues/3782)
* **InputNumber:** add support for `stepSnapping` & `disableWheelChange` props ([#3731](https://github.com/nuxt/ui/issues/3731)) ([f5e6284](https://github.com/nuxt/ui/commit/f5e62849c9313063396ab0e3a9b7d22d98ef69bc))
* **locale:** add Bulgarian language ([#3783](https://github.com/nuxt/ui/issues/3783)) ([a0c9731](https://github.com/nuxt/ui/commit/a0c9731f634020e76aa98a9a68d673591d35e8c9))
* **locale:** add Kazakh language ([#3875](https://github.com/nuxt/ui/issues/3875)) ([43153c4](https://github.com/nuxt/ui/commit/43153c4e91034b728059e7a9bed05888e48f8890))
* **locale:** add Tajik language ([#3850](https://github.com/nuxt/ui/issues/3850)) ([f42a79b](https://github.com/nuxt/ui/commit/f42a79b5efe8dc65430a83799ebb0ee737773820))
* **locale:** add Uyghur language ([#3878](https://github.com/nuxt/ui/issues/3878)) ([b7fc69b](https://github.com/nuxt/ui/commit/b7fc69baa718ff65b3988d0fa9f143306fa8fac4))
* **Modal/Popover/Slideover:** add `close:prevent` event ([#3958](https://github.com/nuxt/ui/issues/3958)) ([f486423](https://github.com/nuxt/ui/commit/f4864233812eac0ed37e0a2d076a95c285a22c01))
* **module:** define default color shades ([#3916](https://github.com/nuxt/ui/issues/3916)) ([7ac7aa9](https://github.com/nuxt/ui/commit/7ac7aa9ba73b6aca1bc29b0de2e95c60b2700135))
* **module:** define neutral utilities ([#3629](https://github.com/nuxt/ui/issues/3629)) ([d49e0da](https://github.com/nuxt/ui/commit/d49e0dadeea2a58e05e60b2c461b29ce1d334d2b))
* **module:** dynamic `rounded-*` utilities ([#3906](https://github.com/nuxt/ui/issues/3906)) ([f9737c8](https://github.com/nuxt/ui/commit/f9737c8f401bf8bc5307674fad6defe2aeeeb907))
* **OverlayProvider:** return an overlay instance from `.open()` ([#3829](https://github.com/nuxt/ui/issues/3829)) ([f3098df](https://github.com/nuxt/ui/commit/f3098df84a3b7f58f7ccc1233bc8b45eab99ee10))
* **PinInput:** add `autofocus` / `autofocus-delay` props ([0456670](https://github.com/nuxt/ui/commit/0456670dac1153340220603c8c116e3b71f72ae7)), closes [#3717](https://github.com/nuxt/ui/issues/3717)
* **RadioGroup:** add `card` and `table` variants ([#3178](https://github.com/nuxt/ui/issues/3178)) ([4d138ad](https://github.com/nuxt/ui/commit/4d138ad6719a074f5f994006d12745ca05bec9c4))
* **Select:** handle `onSelect` field in items ([8640831](https://github.com/nuxt/ui/commit/864083156a79dfb5d0be868658b7f9fc77570178))
* **Table:** conditionally apply classes to `tr` and `td` ([#3866](https://github.com/nuxt/ui/issues/3866)) ([80dfa88](https://github.com/nuxt/ui/commit/80dfa88ea442571ee1dc673317cc7baa8cacd8a3))
* **Tabs:** add `list-leading` and `list-trailing` slots ([#3837](https://github.com/nuxt/ui/issues/3837)) ([3447a06](https://github.com/nuxt/ui/commit/3447a062b636a469089d6e9bdcfcb3dce9063ee5))
* **Textarea:** add `autoresize-delay` prop ([06414d3](https://github.com/nuxt/ui/commit/06414d344b151ad6e1a3225a9f5f1f76d58d319c)), closes [#3730](https://github.com/nuxt/ui/issues/3730)
* **Textarea:** add `icon`, `loading`, etc. props to match Input ([cb193f1](https://github.com/nuxt/ui/commit/cb193f1d25b5c73ca03dcf10864800350dd1c290))
* **Textarea:** add `resize-none` class with `autoresize` prop ([ffafd81](https://github.com/nuxt/ui/commit/ffafd81e1ed25074430668c792e5e1c6afc22bd0))
* **unplugin:** routing support for inertia ([#3845](https://github.com/nuxt/ui/issues/3845)) ([d059efc](https://github.com/nuxt/ui/commit/d059efca258da7ae5116e829189a492824ac1d87))
### Bug Fixes
* **Accordion:** use `div` instead of `h3` for header tag ([75e4792](https://github.com/nuxt/ui/commit/75e4792f7f00c55229253289c4f806f2b6fc9854)), closes [#3963](https://github.com/nuxt/ui/issues/3963)
* **Alert/Toast:** display actions when using slots ([5086363](https://github.com/nuxt/ui/commit/50863635d653c8083772046ddc5b828fba7047d0)), closes [#3950](https://github.com/nuxt/ui/issues/3950)
* **Carousel:** move arrows inside container on mobile ([d339dcb](https://github.com/nuxt/ui/commit/d339dcbfb8fe244bd198d247d8448e3ef856dfef)), closes [#3813](https://github.com/nuxt/ui/issues/3813)
* **CheckboxGroup:** proxy slots & `ui` prop ([bc06185](https://github.com/nuxt/ui/commit/bc061852822edd2dfb832a46dd6388123ec5771e))
* **CommandPalette:** consistent alignement with other components ([d25265c](https://github.com/nuxt/ui/commit/d25265c8b7d34e01af8827d9af5eccb98bf30e9e))
* **CommandPalette:** increase input font size to avoid zoom ([d227a10](https://github.com/nuxt/ui/commit/d227a105d8d409ea0753153afaecf639ddb80fed))
* **CommandPalette:** prevent hover background on disabled items ([ba534f1](https://github.com/nuxt/ui/commit/ba534f18b94383c97b2654d892ee4b8b024b3fab))
* **components:** refactor types after `@nuxt/module-builder` upgrade ([#3855](https://github.com/nuxt/ui/issues/3855)) ([39c861a](https://github.com/nuxt/ui/commit/39c861a64bbd452256ebd1a14a257b94c35855d4))
* **components:** respect `transform-origin` in popper content ([#3919](https://github.com/nuxt/ui/issues/3919)) ([01d8dc7](https://github.com/nuxt/ui/commit/01d8dc72adb0b32ad68bb4a98bf24b17f435a89c))
* **ContextMenu/DropdownMenu:** handle RTL mode ([#3744](https://github.com/nuxt/ui/issues/3744)) ([1ae5cc0](https://github.com/nuxt/ui/commit/1ae5cc09cb2eca6b6f53eb04db9dcc731b696cae))
* **ContextMenuContent/DropdownMenuContent:** remove unwanted `any` ([#3741](https://github.com/nuxt/ui/issues/3741)) ([97274f1](https://github.com/nuxt/ui/commit/97274f15b8bfe457e7e206f81b32e3febf0f875d))
* **Form:** input and output type inference ([#3938](https://github.com/nuxt/ui/issues/3938)) ([f429498](https://github.com/nuxt/ui/commit/f42949820be9be9fca41abc653dc12c033e1eeec))
* **Form:** loses focus on submit ([#3796](https://github.com/nuxt/ui/issues/3796)) ([8e78eb1](https://github.com/nuxt/ui/commit/8e78eb15c85beef1c814206c4a192d4eb00a7e86))
* **InputMenu/Select/SelectMenu:** add `min-w-fit` to `content` slot ([#3922](https://github.com/nuxt/ui/issues/3922)) ([f6b3761](https://github.com/nuxt/ui/commit/f6b376110c8bee2c41ae3137bb972aad402ebff1))
* **InputMenu/SelectMenu:** correctly call `onSelect` events ([#3735](https://github.com/nuxt/ui/issues/3735)) ([f25fed5](https://github.com/nuxt/ui/commit/f25fed58e988b304e79cdb536d544d257395cf89))
* **InputMenu/SelectMenu:** prevent `disabled` items to be selected ([8435a0f](https://github.com/nuxt/ui/commit/8435a0fe1622eb5b6863b6e4751c9d2d1be36db9)), closes [#3474](https://github.com/nuxt/ui/issues/3474)
* **InputMenu/SelectMenu:** remove `valueKey` string case ([9ca213b](https://github.com/nuxt/ui/commit/9ca213bd3340492d7503a34bd142e1f79a697050)), closes [#3949](https://github.com/nuxt/ui/issues/3949) [#3331](https://github.com/nuxt/ui/issues/3331)
* **InputMenu/SelectMenu:** support arbitrary `value` ([#3779](https://github.com/nuxt/ui/issues/3779)) ([52a97e2](https://github.com/nuxt/ui/commit/52a97e2df7903f91e3134931eb0d6bd4c528f71f))
* **InputMenu:** emit `change` on multiple item removal ([9d2fed1](https://github.com/nuxt/ui/commit/9d2fed125013e3bbfbf9435678729cd05254a5e8)), closes [#3756](https://github.com/nuxt/ui/issues/3756)
* **Link:** proxy `download` property ([#3879](https://github.com/nuxt/ui/issues/3879)) ([47cdc2e](https://github.com/nuxt/ui/commit/47cdc2e1d8cd9803ebc954ccae110d62b9a08779))
* **NavigationMenu:** add `sm:w-auto` content slot ([abe0859](https://github.com/nuxt/ui/commit/abe0859691e06564f68335bd82dcd121e976408e)), closes [#3788](https://github.com/nuxt/ui/issues/3788)
* **Skeleton:** improve accessibility ([#3613](https://github.com/nuxt/ui/issues/3613)) ([3484832](https://github.com/nuxt/ui/commit/3484832822015a224ce6fbeae5132018875557e6))
* **Stepper:** ui prop override on `icon` and `content` slots ([1d45980](https://github.com/nuxt/ui/commit/1d459803dc052a16b8966ee89c71646bf6ef1c16)), closes [#3785](https://github.com/nuxt/ui/issues/3785)
* **Table:** improve `data` reactivity ([#3967](https://github.com/nuxt/ui/issues/3967)) ([6e27304](https://github.com/nuxt/ui/commit/6e27304d8ca459a04667bac404084264a8cf58fd))
* **Table:** pass header `colspan` to `th` ([#3926](https://github.com/nuxt/ui/issues/3926)) ([122e8ac](https://github.com/nuxt/ui/commit/122e8ac8f41ba093cd350c3ce642263263f77296))
* **Tree:** simplify reusable template types ([#3836](https://github.com/nuxt/ui/issues/3836)) ([3deed4c](https://github.com/nuxt/ui/commit/3deed4c271cad4adc2a4c47d5dd02e95a14ce11a))
* **types:** allow color identifiers with dashes ([#3896](https://github.com/nuxt/ui/issues/3896)) ([e5a1e26](https://github.com/nuxt/ui/commit/e5a1e26f9db763b54caed4ca313f44d1b5fe269d))
* **types:** handle `ClassValue` in `ui` prop ([eea1415](https://github.com/nuxt/ui/commit/eea14155aa612649bc969d806ec5df4295945c70)), closes [#3860](https://github.com/nuxt/ui/issues/3860)
* **types:** improve dynamic slots ([#3857](https://github.com/nuxt/ui/issues/3857)) ([8dd9d08](https://github.com/nuxt/ui/commit/8dd9d08209e47a7d9a5654db4fb936b4cbcfc021))
* **usePortal:** adjust portal target resolution logic ([#3954](https://github.com/nuxt/ui/issues/3954)) ([db11db6](https://github.com/nuxt/ui/commit/db11db6ff1ce4b27a66aaa03f07870ba36426181))
* **vite:** vitest skipping nuxt imports transformations ([#3925](https://github.com/nuxt/ui/issues/3925)) ([c31bffa](https://github.com/nuxt/ui/commit/c31bffad1b8afeda584bca8c73bb7f790eb12a9f))
## [3.0.2](https://github.com/nuxt/ui/compare/v3.0.1...v3.0.2) (2025-03-28) ## [3.0.2](https://github.com/nuxt/ui/compare/v3.0.1...v3.0.2) (2025-03-28)
### Features ### Features

View File

@@ -16,10 +16,6 @@ Nuxt UI harnesses the combined strengths of [Reka UI](https://reka-ui.com/), [Ta
> [!NOTE] > [!NOTE]
> You are on the `v3` development branch, check out the [v2 branch](https://github.com/nuxt/ui/tree/v2) for Nuxt UI v2. > You are on the `v3` development branch, check out the [v2 branch](https://github.com/nuxt/ui/tree/v2) for Nuxt UI v2.
> [!TIP]
> **Looking for more components ?**
> Check out [Nuxt UI Pro](https://ui.nuxt.com/pro), a collection of premium Vue components, composables, and utilities built on top of Nuxt UI for faster and more powerful app development.
## Documentation ## Documentation
Visit https://ui.nuxt.com to explore the documentation. Visit https://ui.nuxt.com to explore the documentation.

View File

@@ -6,14 +6,8 @@ export default defineBuildConfig({
'./src/unplugin', './src/unplugin',
'./src/vite' './src/vite'
], ],
rollup: { replace: {
replace: { 'process.env.DEV': 'false'
delimiters: ['', ''],
values: {
// Used in development to import directly from theme
'const isUiDev = true': 'const isUiDev = false'
}
}
}, },
hooks: { hooks: {
'mkdist:entry:options'(ctx, entry, options) { 'mkdist:entry:options'(ctx, entry, options) {

View File

@@ -31,10 +31,13 @@ const component = ({ name, primitive, pro, prose, content }) => {
? ` ? `
<script lang="ts"> <script lang="ts">
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}' import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
import type { ComponentConfig } from '../types/utils' import { tv } from '../utils/tv'
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}> const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
export interface ${upperName}Props { export interface ${upperName}Props {
/** /**
@@ -43,7 +46,7 @@ export interface ${upperName}Props {
*/ */
as?: any as?: any
class?: any class?: any
ui?: ${upperName}['slots'] ui?: Partial<typeof ${camelName}.slots>
} }
export interface ${upperName}Slots { export interface ${upperName}Slots {
@@ -52,17 +55,12 @@ export interface ${upperName}Slots {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { Primitive } from 'reka-ui' import { Primitive } from 'reka-ui'
import { useAppConfig } from '#imports'
import { tv } from '../utils/tv'
const props = defineProps<${upperName}Props>() const props = defineProps<${upperName}Props>()
defineSlots<${upperName}Slots>() defineSlots<${upperName}Slots>()
const appConfig = useAppConfig() as ${upperName}['AppConfig'] const ui = ${camelName}()
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })())
</script> </script>
<template> <template>
@@ -73,16 +71,22 @@ const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName}
` `
: ` : `
<script lang="ts"> <script lang="ts">
import type { VariantProps } from 'tailwind-variants'
import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui' import type { ${upperName}RootProps, ${upperName}RootEmits } from 'reka-ui'
import type { AppConfig } from '@nuxt/schema' import type { AppConfig } from '@nuxt/schema'
import _appConfig from '#build/app.config'
import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}' import theme from '#build/${path}/${prose ? 'prose/' : ''}${content ? 'content/' : ''}${kebabName}'
import type { ComponentConfig } from '../types/utils' import { tv } from '../utils/tv'
type ${upperName} = ComponentConfig<typeof theme, AppConfig, ${upperName}${pro ? `, '${key}'` : ''}> const appConfig${camelName} = _appConfig as AppConfig & { ${key}: { ${prose ? 'prose: { ' : ''}${camelName}: Partial<typeof theme> } }${prose ? ' }' : ''}
const ${camelName} = tv({ extend: tv(theme), ...(appConfig${camelName}.${key}?.${prose ? 'prose?.' : ''}${camelName} || {}) })
type ${upperName}Variants = VariantProps<typeof ${camelName}>
export interface ${upperName}Props extends Pick<${upperName}RootProps> { export interface ${upperName}Props extends Pick<${upperName}RootProps> {
class?: any class?: any
ui?: ${upperName}['slots'] ui?: Partial<typeof ${camelName}.slots>
} }
export interface ${upperName}Emits extends ${upperName}RootEmits {} export interface ${upperName}Emits extends ${upperName}RootEmits {}
@@ -91,21 +95,16 @@ export interface ${upperName}Slots {}
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'
import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui' import { ${upperName}Root, useForwardPropsEmits } from 'reka-ui'
import { reactivePick } from '@vueuse/core' import { reactivePick } from '@vueuse/core'
import { useAppConfig } from '#imports'
import { tv } from '../utils/tv'
const props = defineProps<${upperName}Props>() const props = defineProps<${upperName}Props>()
const emits = defineEmits<${upperName}Emits>() const emits = defineEmits<${upperName}Emits>()
const slots = defineSlots<${upperName}Slots>() const slots = defineSlots<${upperName}Slots>()
const appConfig = useAppConfig() as ${upperName}['AppConfig']
const rootProps = useForwardPropsEmits(reactivePick(props), emits) const rootProps = useForwardPropsEmits(reactivePick(props), emits)
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.${camelName} || {}) })()) const ui = ${camelName}()
</script> </script>
<template> <template>

View File

@@ -23,7 +23,7 @@ useHead({
{ key: 'theme-color', name: 'theme-color', content: color } { key: 'theme-color', name: 'theme-color', content: color }
], ],
link: [ link: [
// { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }, { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' },
{ rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` } { rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` }
], ],
style: [ style: [
@@ -40,8 +40,6 @@ useServerSeoMeta({
twitterCard: 'summary_large_image' twitterCard: 'summary_large_image'
}) })
useFaviconFromTheme()
const { frameworks, modules } = useSharedData() const { frameworks, modules } = useSharedData()
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
@@ -87,5 +85,5 @@ provide('navigation', mappedNavigation)
</template> </template>
<style> <style>
/* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 max-h-[341px] */ /* Safelist (do not remove): [&>div]:*:my-0 [&>div]:*:w-full h-64 !px-0 !py-0 !pt-0 !pb-0 !p-0 !justify-start !justify-end !min-h-96 h-136 */
</style> </style>

View File

@@ -1,7 +1,7 @@
@import "tailwindcss" theme(static) source("../../../.."); @import "tailwindcss" theme(static) source("../../../..");
@import "@nuxt/ui-pro"; @import "@nuxt/ui-pro";
@source "../../../content/**/*"; @source "../../../content";
@source "../../../node_modules/.c12"; @source "../../../node_modules/.c12";
@theme static { @theme static {

View File

@@ -23,27 +23,27 @@ onMounted(() => {
@reference "../assets/css/main.css"; @reference "../assets/css/main.css";
.carbon :deep(#carbonads) { .carbon :deep(#carbonads) {
@apply relative border border-default rounded-md hover:bg-elevated/50 w-full transition-colors min-h-[220px] p-2; @apply relative border border-(--ui-border) rounded-[calc(var(--ui-radius)*1.5)] hover:bg-(--ui-bg-elevated)/50 w-full transition-colors min-h-[220px] p-2;
.carbon-img { .carbon-img {
@apply flex justify-center w-full; @apply flex justify-center w-full;
& > img { & > img {
@apply !max-w-full w-full rounded-sm; @apply !max-w-full w-full rounded-(--ui-radius);
} }
} }
.carbon-text { .carbon-text {
@apply text-sm text-muted transition-colors text-center text-pretty flex pt-2; @apply text-sm text-(--ui-text-muted) transition-colors text-center text-pretty flex pt-2;
} }
.carbon-poweredby { .carbon-poweredby {
@apply block text-xs text-center text-muted pt-2; @apply block text-xs text-center text-(--ui-text-muted) pt-2;
} }
&:hover { &:hover {
.carbon-text { .carbon-text {
@apply text-default; @apply text-(--ui-text);
} }
} }
} }

View File

@@ -22,8 +22,8 @@ const links = [{
<UFooter> <UFooter>
<template #left> <template #left>
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-muted"> <NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="text-sm text-(--ui-text-muted)">
Published under <span class="text-highlighted">MIT License</span> Published under <span class="text-(--ui-text-highlighted)">MIT License</span>
</NuxtLink> </NuxtLink>
</template> </template>

View File

@@ -19,8 +19,8 @@ watch(framework, () => {
:content="false" :content="false"
color="neutral" color="neutral"
:ui="{ :ui="{
indicator: 'bg-default', indicator: 'bg-(--ui-bg)',
trigger: 'px-1 data-[state=active]:text-highlighted' trigger: 'px-1 data-[state=active]:text-(--ui-text-highlighted)'
}" }"
size="xs" size="xs"
@update:model-value="(framework = $event as string)" @update:model-value="(framework = $event as string)"

View File

@@ -41,7 +41,7 @@ const mobileLinks = computed(() => [
<template> <template>
<UHeader :ui="{ left: 'min-w-0' }" :menu="{ shouldScaleBackground: true }"> <UHeader :ui="{ left: 'min-w-0' }" :menu="{ shouldScaleBackground: true }">
<template #left> <template #left>
<NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-highlighted min-w-0 focus-visible:outline-primary shrink-0" aria-label="Nuxt UI"> <NuxtLink to="/" class="flex items-end gap-2 font-bold text-xl text-(--ui-text-highlighted) min-w-0 focus-visible:outline-(--ui-primary) shrink-0" aria-label="Nuxt UI">
<Logo v-if="route.path === '/'" class="w-auto h-6 shrink-0" /> <Logo v-if="route.path === '/'" class="w-auto h-6 shrink-0" />
<LogoPro v-else-if="route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" /> <LogoPro v-else-if="route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" />
<template v-else> <template v-else>
@@ -63,7 +63,7 @@ const mobileLinks = computed(() => [
trailing-icon="i-lucide-chevron-down" trailing-icon="i-lucide-chevron-down"
size="xs" size="xs"
class="-mb-[6px] font-semibold rounded-full truncate" class="-mb-[6px] font-semibold rounded-full truncate"
:class="[open && 'bg-primary/15 ']" :class="[open && 'bg-(--ui-primary)/15 ']"
:ui="{ :ui="{
trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ') trailingIcon: ['transition-transform duration-200', open ? 'rotate-180' : undefined].filter(Boolean).join(' ')
}" }"
@@ -108,7 +108,7 @@ const mobileLinks = computed(() => [
<span class="inline-flex items-center gap-0.5"> <span class="inline-flex items-center gap-0.5">
{{ link.title }} {{ link.title }}
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup> <sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
</span> </span>
</template> </template>
</UContentNavigation> </UContentNavigation>

View File

@@ -19,8 +19,8 @@ watch(module, () => {
:content="false" :content="false"
color="neutral" color="neutral"
:ui="{ :ui="{
indicator: 'bg-default', indicator: 'bg-(--ui-bg)',
trigger: 'px-1 data-[state=active]:text-highlighted' trigger: 'px-1 data-[state=active]:text-(--ui-text-highlighted)'
}" }"
size="xs" size="xs"
@update:model-value="(module = $event as string)" @update:model-value="(module = $event as string)"

View File

@@ -153,8 +153,7 @@ const options = computed(() => {
const items = propItems.length const items = propItems.length
? propItems.map((item: any) => ({ ? propItems.map((item: any) => ({
value: item, value: item,
label: String(item), label: String(item)
chip: key.toLowerCase().endsWith('color') ? { color: item } : undefined
})) }))
: prop?.type === 'boolean' || prop?.type === 'boolean | undefined' : prop?.type === 'boolean' || prop?.type === 'boolean | undefined'
? [{ value: true, label: 'true' }, { value: false, label: 'false' }] ? [{ value: true, label: 'true' }, { value: false, label: 'false' }]
@@ -329,16 +328,16 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
<template> <template>
<div class="my-5"> <div class="my-5">
<div class="relative"> <div>
<div v-if="options.length" class="flex flex-wrap items-center gap-2.5 border border-muted border-b-0 relative rounded-t-md px-4 py-2.5 overflow-x-auto"> <div v-if="options.length" class="flex flex-wrap items-center gap-2.5 border border-(--ui-border-muted) 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"> <template v-for="option in options" :key="option.name">
<UFormField <UFormField
:label="option.label" :label="option.label"
size="sm" size="sm"
class="inline-flex ring ring-accented rounded-sm" class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
:ui="{ :ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented', wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
label: 'text-muted px-2 py-1.5', label: 'text-(--ui-text-muted) px-2 py-1.5',
container: 'mt-0' container: 'mt-0'
}" }"
> >
@@ -349,7 +348,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
value-key="value" value-key="value"
color="neutral" color="neutral"
variant="soft" variant="soft"
class="rounded-sm rounded-l-none min-w-12" class="rounded-(--ui-radius) rounded-l-none min-w-12"
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']" :class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
:ui="{ itemLeadingChip: 'size-2' }" :ui="{ itemLeadingChip: 'size-2' }"
@update:model-value="setComponentProp(option.name, $event)" @update:model-value="setComponentProp(option.name, $event)"
@@ -371,14 +370,14 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
:model-value="getComponentProp(option.name)" :model-value="getComponentProp(option.name)"
color="neutral" color="neutral"
variant="soft" variant="soft"
:ui="{ base: 'rounded-sm rounded-l-none min-w-12' }" :ui="{ base: 'rounded-(--ui-radius) rounded-l-none min-w-12' }"
@update:model-value="setComponentProp(option.name, $event)" @update:model-value="setComponentProp(option.name, $event)"
/> />
</UFormField> </UFormField>
</template> </template>
</div> </div>
<div v-if="component" class="flex justify-center border border-b-0 border-muted relative p-4 z-[1]" :class="[!options.length && 'rounded-t-md', props.class, { 'overflow-hidden': props.overflowHidden }]"> <div v-if="component" class="flex justify-center border border-b-0 border-(--ui-border-muted) relative p-4 z-[1]" :class="[!options.length && 'rounded-t-[calc(var(--ui-radius)*1.5)]', props.class, { 'overflow-hidden': props.overflowHidden }]">
<component :is="component" v-bind="{ ...componentProps, ...componentEvents }"> <component :is="component" v-bind="{ ...componentProps, ...componentEvents }">
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]> <template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<slot :name="slot" mdc-unwrap="p"> <slot :name="slot" mdc-unwrap="p">

View File

@@ -150,8 +150,8 @@ const urlSearchParams = computed(() => {
<template> <template>
<div ref="el" class="my-5"> <div ref="el" class="my-5">
<template v-if="preview"> <template v-if="preview">
<div class="border border-muted relative z-[1]" :class="[{ 'border-b-0 rounded-t-md': props.source, 'rounded-md': !props.source, 'overflow-hidden': props.overflowHidden }]"> <div class="border border-(--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, 'overflow-hidden': props.overflowHidden }]">
<div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-muted"> <div v-if="props.options?.length || !!slots.options" class="flex gap-4 p-4 border-b border-(--ui-border-muted)">
<slot name="options" /> <slot name="options" />
<UFormField <UFormField
@@ -160,10 +160,10 @@ const urlSearchParams = computed(() => {
:label="option.label" :label="option.label"
:name="option.name" :name="option.name"
size="sm" size="sm"
class="inline-flex ring ring-accented rounded-sm" class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
:ui="{ :ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented', wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
label: 'text-muted px-2 py-1.5', label: 'text-(--ui-text-muted) px-2 py-1.5',
container: 'mt-0' container: 'mt-0'
}" }"
> >
@@ -175,7 +175,7 @@ const urlSearchParams = computed(() => {
:value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined" :value-key="option.name.toLowerCase().endsWith('color') ? 'value' : undefined"
color="neutral" color="neutral"
variant="soft" variant="soft"
class="rounded-sm rounded-l-none min-w-12" class="rounded-(--ui-radius) rounded-l-none min-w-12"
:multiple="option.multiple" :multiple="option.multiple"
:class="[option.name.toLowerCase().endsWith('color') && 'pl-6']" :class="[option.name.toLowerCase().endsWith('color') && 'pl-6']"
:ui="{ itemLeadingChip: 'size-2' }" :ui="{ itemLeadingChip: 'size-2' }"
@@ -196,7 +196,7 @@ const urlSearchParams = computed(() => {
:model-value="get(optionsValues, option.name)" :model-value="get(optionsValues, option.name)"
color="neutral" color="neutral"
variant="soft" variant="soft"
:ui="{ base: 'rounded-sm rounded-l-none min-w-12' }" :ui="{ base: 'rounded-(--ui-radius) rounded-l-none min-w-12' }"
@update:model-value="set(optionsValues, option.name, $event)" @update:model-value="set(optionsValues, option.name, $event)"
/> />
</UFormField> </UFormField>

View File

@@ -112,7 +112,7 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
<ProseTd> <ProseTd>
<HighlightInlineType v-if="prop.type" :type="prop.type" /> <HighlightInlineType v-if="prop.type" :type="prop.type" />
<MDC v-if="prop.description" :value="prop.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-description`" /> <MDC v-if="prop.description" :value="prop.description" class="text-(--ui-text-toned) mt-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-description`" />
<ComponentPropsLinks v-if="prop.tags?.length" :prop="prop" /> <ComponentPropsLinks v-if="prop.tags?.length" :prop="prop" />
<ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" /> <ComponentPropsSchema v-if="prop.schema" :prop="prop" :ignore="ignore" />

View File

@@ -43,7 +43,7 @@ const schemaProps = computed(() => {
<ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name"> <ProseLi v-for="schemaProp in schemaProps" :key="schemaProp.name">
<HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" /> <HighlightInlineType :type="`${schemaProp.name}${schemaProp.required === false ? '?' : ''}: ${schemaProp.type}`" />
<MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-muted my-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-${schemaProp.name}-description`" /> <MDC v-if="schemaProp.description" :value="schemaProp.description" class="text-(--ui-text-muted) my-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-${schemaProp.name}-description`" />
</ProseLi> </ProseLi>
</ProseUl> </ProseUl>
</ProseCollapsible> </ProseCollapsible>

View File

@@ -36,7 +36,7 @@ const meta = await fetchComponentMeta(name as any)
<ProseTd> <ProseTd>
<HighlightInlineType v-if="slot.type" :type="slot.type" /> <HighlightInlineType v-if="slot.type" :type="slot.type" />
<MDC v-if="slot.description" :value="slot.description" class="text-toned mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" /> <MDC v-if="slot.description" :value="slot.description" class="text-(--ui-text-toned) mt-1" :cache-key="`${kebabCase(route.path)}-${slot.name}-description`" />
</ProseTd> </ProseTd>
</ProseTr> </ProseTr>
</ProseTbody> </ProseTbody>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div class="relative overflow-hidden rounded-sm border border-dashed border-accented opacity-75 px-4 flex items-center justify-center"> <div class="relative overflow-hidden rounded-(--ui-radius) border border-dashed border-(--ui-border-accented) opacity-75 px-4 flex items-center justify-center">
<svg class="absolute inset-0 h-full w-full stroke-inverted/10" fill="none"> <svg class="absolute inset-0 h-full w-full stroke-(--ui-border-inverted)/10" fill="none">
<defs> <defs>
<pattern <pattern
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e" id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"

View File

@@ -22,7 +22,6 @@ function getEmojiFlag(locale: string): string {
hi: 'in', // Hindi -> India hi: 'in', // Hindi -> India
hy: 'am', // Armenian -> Armenia hy: 'am', // Armenian -> Armenia
ja: 'jp', // Japanese -> Japan ja: 'jp', // Japanese -> Japan
kk: 'kz', // Kazakh -> Kazakhstan
km: 'kh', // Khmer -> Cambodia km: 'kh', // Khmer -> Cambodia
ko: 'kr', // Korean -> South Korea ko: 'kr', // Korean -> South Korea
nb: 'no', // Norwegian Bokmål -> Norway nb: 'no', // Norwegian Bokmål -> Norway

View File

@@ -20,7 +20,7 @@ const items: AccordionItem[] = [
<template> <template>
<UAccordion :items="items"> <UAccordion :items="items">
<template #content="{ item }"> <template #content="{ item }">
<p class="pb-3.5 text-sm text-muted"> <p class="pb-3.5 text-sm text-(--ui-text-muted)">
This is the {{ item.label }} panel. This is the {{ item.label }} panel.
</p> </p>
</template> </template>

View File

@@ -24,7 +24,7 @@ const items = [
<template> <template>
<UAccordion :items="items"> <UAccordion :items="items">
<template #colors="{ item }"> <template #colors="{ item }">
<p class="text-sm pb-3.5 text-primary"> <p class="text-sm pb-3.5 text-(--ui-primary)">
{{ item.content }} {{ item.content }}
</p> </p>
</template> </template>

View File

@@ -11,6 +11,7 @@ const items = shallowRef<AccordionItem[]>([
{ {
label: 'Colors', label: 'Colors',
icon: 'i-lucide-swatch-book', icon: 'i-lucide-swatch-book',
slot: 'colors' as const,
content: 'Choose a primary and a neutral color from your Tailwind CSS theme.' content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
}, },
{ {

View File

@@ -3,7 +3,7 @@
<ULink <ULink
to="https://github.com/benjamincanac" to="https://github.com/benjamincanac"
target="_blank" target="_blank"
class="hover:ring-primary transition" class="hover:ring-(--ui-primary) transition"
raw raw
> >
<UAvatar <UAvatar
@@ -15,7 +15,7 @@
<ULink <ULink
to="https://github.com/romhml" to="https://github.com/romhml"
target="_blank" target="_blank"
class="hover:ring-primary transition" class="hover:ring-(--ui-primary) transition"
raw raw
> >
<UAvatar <UAvatar
@@ -27,7 +27,7 @@
<ULink <ULink
to="https://github.com/noook" to="https://github.com/noook"
target="_blank" target="_blank"
class="hover:ring-primary transition" class="hover:ring-(--ui-primary) transition"
raw raw
> >
<UAvatar <UAvatar

View File

@@ -1,35 +1,26 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BreadcrumbItem } from '@nuxt/ui' import type { BreadcrumbItem } from '@nuxt/ui'
const items = [ const items = [{
{ label: 'Home',
label: 'Home', to: '/'
to: '/' }, {
}, slot: 'dropdown' as const,
{ icon: 'i-lucide-ellipsis',
slot: 'dropdown' as const, children: [{
icon: 'i-lucide-ellipsis', label: 'Documentation'
children: [ }, {
{ label: 'Themes'
label: 'Documentation' }, {
}, label: 'GitHub'
{ }]
label: 'Themes' }, {
}, label: 'Components',
{ to: '/components'
label: 'GitHub' }, {
} label: 'Breadcrumb',
] to: '/components/breadcrumb'
}, }] satisfies BreadcrumbItem[]
{
label: 'Components',
to: '/components'
},
{
label: 'Breadcrumb',
to: '/components/breadcrumb'
}
] satisfies BreadcrumbItem[]
</script> </script>
<template> <template>

View File

@@ -1,26 +1,22 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BreadcrumbItem } from '@nuxt/ui' import type { BreadcrumbItem } from '@nuxt/ui'
const items: BreadcrumbItem[] = [ const items: BreadcrumbItem[] = [{
{ label: 'Home',
label: 'Home', to: '/'
to: '/' }, {
}, label: 'Components',
{ to: '/components'
label: 'Components', }, {
to: '/components' label: 'Breadcrumb',
}, to: '/components/breadcrumb'
{ }]
label: 'Breadcrumb',
to: '/components/breadcrumb'
}
]
</script> </script>
<template> <template>
<UBreadcrumb :items="items"> <UBreadcrumb :items="items">
<template #separator> <template #separator>
<span class="mx-2 text-muted">/</span> <span class="mx-2 text-(--ui-text-muted)">/</span>
</template> </template>
</UBreadcrumb> </UBreadcrumb>
</template> </template>

View File

@@ -1,30 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import type { DropdownMenuItem } from '@nuxt/ui' import type { DropdownMenuItem } from '@nuxt/ui'
const items: DropdownMenuItem[] = [ const items: DropdownMenuItem[] = [{
{ label: 'Team',
label: 'Team', icon: 'i-lucide-users'
icon: 'i-lucide-users' }, {
}, label: 'Invite users',
{ icon: 'i-lucide-user-plus',
label: 'Invite users', children: [{
icon: 'i-lucide-user-plus', label: 'Invite by email',
children: [ icon: 'i-lucide-send-horizontal'
{ }, {
label: 'Invite by email', label: 'Invite by link',
icon: 'i-lucide-send-horizontal' icon: 'i-lucide-link'
}, }]
{ }, {
label: 'Invite by link', label: 'New team',
icon: 'i-lucide-link' icon: 'i-lucide-plus'
} }]
]
},
{
label: 'New team',
icon: 'i-lucide-plus'
}
]
</script> </script>
<template> <template>

View File

@@ -1,21 +0,0 @@
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'
const date = shallowRef(new CalendarDate(2025, 4, 2))
</script>
<template>
<div class="flex flex-col gap-4">
<UCalendar v-model="date" :month-controls="false" :year-controls="false" />
<div class="flex justify-between gap-4">
<UButton color="neutral" variant="outline" @click="date = date.subtract({ months: 1 })">
Prev
</UButton>
<UButton color="neutral" variant="outline" @click="date = date.add({ months: 1 })">
Next
</UButton>
</div>
</div>
</template>

View File

@@ -1,54 +0,0 @@
<script setup lang="ts">
const items = [
'https://picsum.photos/640/640?random=1',
'https://picsum.photos/640/640?random=2',
'https://picsum.photos/640/640?random=3',
'https://picsum.photos/640/640?random=4',
'https://picsum.photos/640/640?random=5',
'https://picsum.photos/640/640?random=6'
]
const carousel = useTemplateRef('carousel')
const activeIndex = ref(0)
function onClickPrev() {
activeIndex.value--
}
function onClickNext() {
activeIndex.value++
}
function onSelect(index: number) {
activeIndex.value = index
carousel.value?.emblaApi?.scrollTo(index)
}
</script>
<template>
<div class="flex-1 w-full">
<UCarousel
ref="carousel"
v-slot="{ item }"
arrows
:items="items"
:prev="{ onClick: onClickPrev }"
:next="{ onClick: onClickNext }"
class="w-full max-w-xs mx-auto"
>
<img :src="item" width="320" height="320" class="rounded-lg">
</UCarousel>
<div class="flex gap-1 justify-between pt-4 max-w-xs mx-auto">
<div
v-for="(item, index) in items"
:key="index"
class="size-11 opacity-25 hover:opacity-100 transition-opacity"
:class="{ 'opacity-100': activeIndex === index }"
@click="onSelect(index)"
>
<img :src="item" width="44" height="44" class="rounded-lg">
</div>
</div>
</div>
</template>

View File

@@ -1,79 +1,76 @@
<script setup lang="ts"> <script setup lang="ts">
const groups = [ const groups = [{
{ id: 'settings',
id: 'settings', items: [
items: [ {
{ label: 'Profile',
label: 'Profile', icon: 'i-lucide-user',
icon: 'i-lucide-user', kbds: ['meta', 'P']
kbds: ['meta', 'P'] },
}, {
{ label: 'Billing',
label: 'Billing', icon: 'i-lucide-credit-card',
icon: 'i-lucide-credit-card', kbds: ['meta', 'B'],
kbds: ['meta', 'B'], slot: 'billing' as const
slot: 'billing' as const },
}, {
{ label: 'Notifications',
label: 'Notifications', icon: 'i-lucide-bell'
icon: 'i-lucide-bell' },
}, {
{ label: 'Security',
label: 'Security', icon: 'i-lucide-lock'
icon: 'i-lucide-lock' }
} ]
] }, {
}, id: 'users',
{ label: 'Users',
id: 'users', slot: 'users' as const,
label: 'Users', items: [
slot: 'users' as const, {
items: [ label: 'Benjamin Canac',
{ suffix: 'benjamincanac',
label: 'Benjamin Canac', to: 'https://github.com/benjamincanac',
suffix: 'benjamincanac', target: '_blank'
to: 'https://github.com/benjamincanac', },
target: '_blank' {
}, label: 'Sylvain Marroufin',
{ suffix: 'smarroufin',
label: 'Sylvain Marroufin', to: 'https://github.com/smarroufin',
suffix: 'smarroufin', target: '_blank'
to: 'https://github.com/smarroufin', },
target: '_blank' {
}, label: 'Sébastien Chopin',
{ suffix: 'atinux',
label: 'Sébastien Chopin', to: 'https://github.com/atinux',
suffix: 'atinux', target: '_blank'
to: 'https://github.com/atinux', },
target: '_blank' {
}, label: 'Romain Hamel',
{ suffix: 'romhml',
label: 'Romain Hamel', to: 'https://github.com/romhml',
suffix: 'romhml', target: '_blank'
to: 'https://github.com/romhml', },
target: '_blank' {
}, label: 'Haytham A. Salama',
{ suffix: 'Haythamasalama',
label: 'Haytham A. Salama', to: 'https://github.com/Haythamasalama',
suffix: 'Haythamasalama', target: '_blank'
to: 'https://github.com/Haythamasalama', },
target: '_blank' {
}, label: 'Daniel Roe',
{ suffix: 'danielroe',
label: 'Daniel Roe', to: 'https://github.com/danielroe',
suffix: 'danielroe', target: '_blank'
to: 'https://github.com/danielroe', },
target: '_blank' {
}, label: 'Neil Richter',
{ suffix: 'noook',
label: 'Neil Richter', to: 'https://github.com/noook',
suffix: 'noook', target: '_blank'
to: 'https://github.com/noook', }
target: '_blank' ]
} }]
]
}
]
</script> </script>
<template> <template>

View File

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

View File

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

View File

@@ -3,23 +3,19 @@ import type { ContextMenuItem } from '@nuxt/ui'
const loading = ref(true) const loading = ref(true)
const items = [ const items: ContextMenuItem[] = [{
{ label: 'Refresh the Page',
label: 'Refresh the Page', slot: 'refresh'
slot: 'refresh' as const }, {
}, label: 'Clear Cookies and Refresh'
{ }, {
label: 'Clear Cookies and Refresh' label: 'Clear Cache and Refresh'
}, }]
{
label: 'Clear Cache and Refresh'
}
] satisfies ContextMenuItem[]
</script> </script>
<template> <template>
<UContextMenu :items="items" :ui="{ content: 'w-48' }"> <UContextMenu :items="items" :ui="{ content: 'w-48' }">
<div class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"> <div class="flex items-center justify-center rounded-md border border-dashed border-(--ui-border-accented) text-sm aspect-video w-72">
Right click here Right click here
</div> </div>
@@ -28,7 +24,7 @@ const items = [
</template> </template>
<template #refresh-trailing> <template #refresh-trailing>
<UIcon v-if="loading" name="i-lucide-refresh-cw" class="shrink-0 size-5 text-primary animate-spin" /> <UIcon v-if="loading" name="i-lucide-refresh-cw" class="shrink-0 size-5 text-(--ui-primary) animate-spin" />
</template> </template>
</UContextMenu> </UContextMenu>
</template> </template>

View File

@@ -7,7 +7,7 @@ const open = ref(false)
<UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" /> <UButton label="Open" color="neutral" variant="subtle" trailing-icon="i-lucide-chevron-up" />
<template #header> <template #header>
<h2 class="text-highlighted font-semibold"> <h2 class="text-(--ui-text-highlighted) font-semibold">
Drawer non-dismissible Drawer non-dismissible
</h2> </h2>

View File

@@ -21,7 +21,7 @@ const items = [
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" /> <UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<template #profile-trailing> <template #profile-trailing>
<UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-primary" /> <UIcon name="i-lucide-badge-check" class="shrink-0 size-5 text-(--ui-primary)" />
</template> </template>
</UDropdownMenu> </UDropdownMenu>
</template> </template>

View File

@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
} }
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<typeof state>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }

View File

@@ -30,9 +30,6 @@ const schema = z.object({
radioGroup: z.string().refine(value => value === 'option-2', { radioGroup: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2' message: 'Select Option 2'
}), }),
checkboxGroup: z.any().refine(values => !!values?.find((option: any) => option === 'option-2'), {
message: 'Include Option 2'
}),
slider: z.number().max(20, { message: 'Must be less than 20' }), slider: z.number().max(20, { message: 'Must be less than 20' }),
pin: z.string().regex(/^\d$/).array().length(5) pin: z.string().regex(/^\d$/).array().length(5)
}) })
@@ -50,7 +47,7 @@ const items = [
] ]
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }
@@ -104,14 +101,11 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
<UFormField label="Textarea" name="textarea"> <UFormField label="Textarea" name="textarea">
<UTextarea v-model="state.textarea" class="w-full" /> <UTextarea v-model="state.textarea" class="w-full" />
</UFormField> </UFormField>
<div class="flex gap-4">
<UFormField name="radioGroup"> <UFormField name="radioGroup">
<URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" /> <URadioGroup v-model="state.radioGroup" legend="Radio group" :items="items" />
</UFormField> </UFormField>
<UFormField name="checkboxGroup">
<UCheckboxGroup v-model="state.checkboxGroup" legend="Checkbox group" :items="items" />
</UFormField>
</div>
<UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/"> <UFormField name="pin" label="Pin Input" :error-pattern="/(pin)\..*/">
<UPinInput v-model="state.pin" /> <UPinInput v-model="state.pin" />
</UFormField> </UFormField>

View File

@@ -15,7 +15,7 @@ const state = reactive({
}) })
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<typeof state>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }

View File

@@ -18,7 +18,7 @@ type NestedSchema = z.output<typeof nestedSchema>
const state = reactive<Partial<Schema & NestedSchema>>({ }) const state = reactive<Partial<Schema & NestedSchema>>({ })
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }
@@ -39,7 +39,7 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
<UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" /> <UCheckbox v-model="state.news" name="news" label="Register to our newsletter" @update:model-value="state.email = undefined" />
</div> </div>
<UForm v-if="state.news" :state="state" :schema="nestedSchema" attach> <UForm v-if="state.news" :state="state" :schema="nestedSchema">
<UFormField label="Email" name="email"> <UFormField label="Email" name="email">
<UInput v-model="state.email" placeholder="john@lennon.com" /> <UInput v-model="state.email" placeholder="john@lennon.com" />
</UFormField> </UFormField>

View File

@@ -34,7 +34,7 @@ function removeItem() {
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<Schema>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }
@@ -51,14 +51,7 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
<UInput v-model="state.customer" placeholder="Wonka Industries" /> <UInput v-model="state.customer" placeholder="Wonka Industries" />
</UFormField> </UFormField>
<UForm <UForm v-for="item, count in state.items" :key="count" :state="item" :schema="itemSchema" class="flex gap-2">
v-for="item, count in state.items"
:key="count"
:state="item"
:schema="itemSchema"
attach
class="flex gap-2"
>
<UFormField :label="!count ? 'Description' : undefined" name="description"> <UFormField :label="!count ? 'Description' : undefined" name="description">
<UInput v-model="item.description" /> <UInput v-model="item.description" />
</UFormField> </UFormField>

View File

@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
} }
const toast = useToast() const toast = useToast()
async function onSubmit(event: FormSubmitEvent<typeof state>) { async function onSubmit(event: FormSubmitEvent<any>) {
toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' }) toast.add({ title: 'Success', description: 'The form has been submitted.', color: 'success' })
console.log(event.data) console.log(event.data)
} }

View File

@@ -36,7 +36,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
<template #item-label="{ item }"> <template #item-label="{ item }">
{{ item.label }} {{ item.label }}
<span class="text-muted"> <span class="text-(--ui-text-muted)">
{{ item.email }} {{ item.email }}
</span> </span>
</template> </template>

View File

@@ -15,7 +15,7 @@ const domain = ref(domains[0])
}" }"
> >
<template #leading> <template #leading>
<p class="text-sm text-muted"> <p class="text-sm text-(--ui-text-muted)">
https:// https://
</p> </p>
</template> </template>

View File

@@ -13,7 +13,7 @@ const maxLength = 15
<template #trailing> <template #trailing>
<div <div
id="character-count" id="character-count"
class="text-xs text-muted tabular-nums" class="text-xs text-(--ui-text-muted) tabular-nums"
aria-live="polite" aria-live="polite"
role="status" role="status"
> >

View File

@@ -4,8 +4,8 @@ const value = ref('')
<template> <template>
<UInput v-model="value" placeholder="" :ui="{ base: 'peer' }"> <UInput v-model="value" placeholder="" :ui="{ base: 'peer' }">
<label class="pointer-events-none absolute left-0 -top-2.5 text-highlighted text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-highlighted peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-dimmed peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal"> <label class="pointer-events-none absolute left-0 -top-2.5 text-(--ui-text-highlighted) text-xs font-medium px-1.5 transition-all peer-focus:-top-2.5 peer-focus:text-(--ui-text-highlighted) peer-focus:text-xs peer-focus:font-medium peer-placeholder-shown:text-sm peer-placeholder-shown:text-(--ui-text-dimmed) peer-placeholder-shown:top-1.5 peer-placeholder-shown:font-normal">
<span class="inline-flex bg-default px-1">Email address</span> <span class="inline-flex bg-(--ui-bg) px-1">Email address</span>
</label> </label>
</UInput> </UInput>
</template> </template>

View File

@@ -77,7 +77,7 @@ const text = computed(() => {
v-for="(req, index) in strength" v-for="(req, index) in strength"
:key="index" :key="index"
class="flex items-center gap-0.5" class="flex items-center gap-0.5"
:class="req.met ? 'text-success' : 'text-muted'" :class="req.met ? 'text-(--ui-success)' : 'text-(--ui-text-muted)'"
> >
<UIcon :name="req.met ? 'i-lucide-circle-check' : 'i-lucide-circle-x'" class="size-4 shrink-0" /> <UIcon :name="req.met ? 'i-lucide-circle-check' : 'i-lucide-circle-x'" class="size-4 shrink-0" />

View File

@@ -76,11 +76,11 @@ const items = [
</li> </li>
<li v-for="child in item.children" :key="child.label"> <li v-for="child in item.children" :key="child.label">
<ULink class="text-sm text-left rounded-md p-3 transition-colors hover:bg-elevated/50"> <ULink class="text-sm text-left rounded-md p-3 transition-colors hover:bg-(--ui-bg-elevated)/50">
<p class="font-medium text-highlighted"> <p class="font-medium text-(--ui-text-highlighted)">
{{ child.label }} {{ child.label }}
</p> </p>
<p class="text-muted line-clamp-2"> <p class="text-(--ui-text-muted) line-clamp-2">
{{ child.description }} {{ child.description }}
</p> </p>
</ULink> </ULink>

View File

@@ -1,21 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui' import type { NavigationMenuItem } from '@nuxt/ui'
const items = [ const items: NavigationMenuItem[] = [
{ {
label: 'Guide', label: 'Guide',
icon: 'i-lucide-book-open' icon: 'i-lucide-book-open'
}, },
{ {
label: 'Composables', label: 'Composables',
icon: 'i-lucide-database' icon: 'i-lucide-database'
}, },
{ {
label: 'Components', label: 'Components',
icon: 'i-lucide-box', icon: 'i-lucide-box',
slot: 'components' as const slot: 'components'
} }
] satisfies NavigationMenuItem[] ]
</script> </script>
<template> <template>

View File

@@ -8,7 +8,7 @@ const open = ref(false)
<template #content> <template #content>
<div class="flex items-center gap-4 mb-4"> <div class="flex items-center gap-4 mb-4">
<h2 class="text-highlighted font-semibold"> <h2 class="text-(--ui-text-highlighted) font-semibold">
Popover non-dismissible Popover non-dismissible
</h2> </h2>

View File

@@ -36,7 +36,7 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode
<template #item-label="{ item }"> <template #item-label="{ item }">
{{ item.label }} {{ item.label }}
<span class="text-muted"> <span class="text-(--ui-text-muted)">
{{ item.email }} {{ item.email }}
</span> </span>
</template> </template>

View File

@@ -1,23 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import type { StepperItem } from '@nuxt/ui' import type { StepperItem } from '@nuxt/ui'
const items = [ const items: StepperItem[] = [
{ {
slot: 'address' as const, slot: 'address',
title: 'Address', title: 'Address',
description: 'Add your address here', description: 'Add your address here',
icon: 'i-lucide-house' icon: 'i-lucide-house'
}, { }, {
slot: 'shipping' as const, slot: 'shipping',
title: 'Shipping', title: 'Shipping',
description: 'Set your preferred shipping method', description: 'Set your preferred shipping method',
icon: 'i-lucide-truck' icon: 'i-lucide-truck'
}, { }, {
slot: 'checkout' as const, slot: 'checkout',
title: 'Checkout', title: 'Checkout',
description: 'Confirm your order' description: 'Confirm your order'
} }
] satisfies StepperItem[] ]
</script> </script>
<template> <template>

View File

@@ -3,14 +3,17 @@ import type { StepperItem } from '@nuxt/ui'
const items: StepperItem[] = [ const items: StepperItem[] = [
{ {
slot: 'address',
title: 'Address', title: 'Address',
description: 'Add your address here', description: 'Add your address here',
icon: 'i-lucide-house' icon: 'i-lucide-house'
}, { }, {
slot: 'shipping',
title: 'Shipping', title: 'Shipping',
description: 'Set your preferred shipping method', description: 'Set your preferred shipping method',
icon: 'i-lucide-truck' icon: 'i-lucide-truck'
}, { }, {
slot: 'checkout',
title: 'Checkout', title: 'Checkout',
description: 'Confirm your order' description: 'Confirm your order'
} }

View File

@@ -100,7 +100,7 @@ const columnFilters = ref([{
<template> <template>
<div class="flex flex-col flex-1 w-full"> <div class="flex flex-col flex-1 w-full">
<div class="flex px-4 py-3.5 border-b border-accented"> <div class="flex px-4 py-3.5 border-b border-(--ui-border-accented)">
<UInput <UInput
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)" :model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
class="max-w-sm" class="max-w-sm"

View File

@@ -131,7 +131,7 @@ function getHeader(column: Column<Payment>, label: string) {
'variant': 'ghost', 'variant': 'ghost',
label, label,
'icon': isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down', 'icon': isSorted ? (isSorted === 'asc' ? 'i-lucide-arrow-up-narrow-wide' : 'i-lucide-arrow-down-wide-narrow') : 'i-lucide-arrow-up-down',
'class': '-mx-2.5 data-[state=open]:bg-elevated', 'class': '-mx-2.5 data-[state=open]:bg-(--ui-bg-elevated)',
'aria-label': `Sort by ${isSorted === 'asc' ? 'descending' : 'ascending'}` 'aria-label': `Sort by ${isSorted === 'asc' ? 'descending' : 'ascending'}`
})) }))
} }

View File

@@ -100,7 +100,7 @@ const columnVisibility = ref({
<template> <template>
<div class="flex flex-col flex-1 w-full"> <div class="flex flex-col flex-1 w-full">
<div class="flex justify-end px-4 py-3.5 border-b border-accented"> <div class="flex justify-end px-4 py-3.5 border-b border-(--ui-border-accented)">
<UDropdownMenu <UDropdownMenu
:items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({ :items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({
label: upperFirst(column.id), label: upperFirst(column.id),

View File

@@ -1,82 +0,0 @@
<script setup lang="ts">
import type { TableColumn } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable.mjs'
type Payment = {
id: string
date: string
email: string
amount: number
}
const data = ref<Payment[]>([{
id: '4600',
date: '2024-03-11T15:30:00',
email: 'james.anderson@example.com',
amount: 594
}, {
id: '4599',
date: '2024-03-11T10:10:00',
email: 'mia.white@example.com',
amount: 276
}, {
id: '4598',
date: '2024-03-11T08:50:00',
email: 'william.brown@example.com',
amount: 315
}, {
id: '4597',
date: '2024-03-10T19:45:00',
email: 'emma.davis@example.com',
amount: 529
}])
const columns: TableColumn<Payment>[] = [{
accessorKey: 'id',
header: '#',
cell: ({ row }) => `#${row.getValue('id')}`
}, {
accessorKey: 'date',
header: 'Date',
cell: ({ row }) => {
return new Date(row.getValue('date')).toLocaleString('en-US', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
cell: ({ row }) => {
const amount = Number.parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'EUR'
}).format(amount)
return h('div', { class: 'text-right font-medium' }, formatted)
}
}]
useSortable('.my-table-tbody', data, {
animation: 150
})
</script>
<template>
<div class="w-full">
<UTable
ref="table"
:data="data"
:columns="columns"
:ui="{
tbody: 'my-table-tbody'
}"
/>
</div>
</template>

View File

@@ -265,7 +265,7 @@ function randomize() {
</script> </script>
<template> <template>
<div class="flex-1 divide-y divide-accented w-full"> <div class="flex-1 divide-y divide-(--ui-border-accented) w-full">
<div class="flex items-center gap-2 px-4 py-3.5 overflow-x-auto"> <div class="flex items-center gap-2 px-4 py-3.5 overflow-x-auto">
<UInput <UInput
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)" :model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
@@ -313,7 +313,7 @@ function randomize() {
</template> </template>
</UTable> </UTable>
<div class="px-4 py-3.5 text-sm text-muted"> <div class="px-4 py-3.5 text-sm text-(--ui-text-muted)">
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of {{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected. {{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div> </div>

View File

@@ -36,7 +36,7 @@ const columns: TableColumn<User>[] = [{
size: 'lg' size: 'lg'
}), }),
h('div', undefined, [ h('div', undefined, [
h('p', { class: 'font-medium text-highlighted' }, row.original.name), h('p', { class: 'font-medium text-(--ui-text-highlighted)' }, row.original.name),
h('p', { class: '' }, `@${row.original.username}`) h('p', { class: '' }, `@${row.original.username}`)
]) ])
]) ])

View File

@@ -95,7 +95,7 @@ const globalFilter = ref('45')
<template> <template>
<div class="flex flex-col flex-1 w-full"> <div class="flex flex-col flex-1 w-full">
<div class="flex px-4 py-3.5 border-b border-accented"> <div class="flex px-4 py-3.5 border-b border-(--ui-border-accented)">
<UInput <UInput
v-model="globalFilter" v-model="globalFilter"
class="max-w-sm" class="max-w-sm"

View File

@@ -1,88 +0,0 @@
<script setup lang="ts">
import type { TableColumn } from '@nuxt/ui'
import { useInfiniteScroll } from '@vueuse/core'
const UAvatar = resolveComponent('UAvatar')
type User = {
id: number
firstName: string
username: string
email: string
image: string
}
type UserResponse = {
users: User[]
total: number
skip: number
limit: number
}
const skip = ref(0)
const { data, status, execute } = await useFetch('https://dummyjson.com/users?limit=10&select=firstName,username,email,image', {
key: 'table-users-infinite-scroll',
params: { skip },
transform: (data?: UserResponse) => {
return data?.users
},
lazy: true,
immediate: false
})
const columns: TableColumn<User>[] = [{
accessorKey: 'id',
header: 'ID'
}, {
accessorKey: 'image',
header: 'Avatar',
cell: ({ row }) => h(UAvatar, { src: row.original.image })
}, {
accessorKey: 'firstName',
header: 'First name'
}, {
accessorKey: 'email',
header: 'Email'
}, {
accessorKey: 'username',
header: 'Username'
}]
const users = ref<User[]>([])
watch(data, () => {
users.value = [
...users.value,
...(data.value || [])
]
})
execute()
const table = useTemplateRef<ComponentPublicInstance>('table')
onMounted(() => {
useInfiniteScroll(table.value?.$el, () => {
skip.value += 10
}, {
distance: 200,
canLoadMore: () => {
return status.value !== 'pending'
}
})
})
</script>
<template>
<div class="w-full">
<UTable
ref="table"
:data="users"
:columns="columns"
:loading="status === 'pending'"
sticky
class="flex-1 h-80"
/>
</div>
</template>

View File

@@ -162,7 +162,7 @@ const pagination = ref({
class="flex-1" class="flex-1"
/> />
<div class="flex justify-center border-t border-default pt-4"> <div class="flex justify-center border-t border-(--ui-border) pt-4">
<UPagination <UPagination
:default-page="(table?.tableApi?.getState().pagination.pageIndex || 0) + 1" :default-page="(table?.tableApi?.getState().pagination.pageIndex || 0) + 1"
:items-per-page="table?.tableApi?.getState().pagination.pageSize" :items-per-page="table?.tableApi?.getState().pagination.pageSize"

View File

@@ -112,7 +112,7 @@ const expanded = ref({ 1: true })
v-model:expanded="expanded" v-model:expanded="expanded"
:data="data" :data="data"
:columns="columns" :columns="columns"
:ui="{ tr: 'data-[expanded=true]:bg-elevated/50' }" :ui="{ tr: 'data-[expanded=true]:bg-(--ui-bg-elevated)/50' }"
class="flex-1" class="flex-1"
> >
<template #expanded="{ row }"> <template #expanded="{ row }">

View File

@@ -122,7 +122,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
@select="onSelect" @select="onSelect"
/> />
<div class="px-4 py-3.5 border-t border-accented text-sm text-muted"> <div class="px-4 py-3.5 border-t border-[var(--ui-border-accented)] text-sm text-[var(--ui-text-muted)]">
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of {{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected. {{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div> </div>

View File

@@ -113,7 +113,7 @@ const rowSelection = ref({ 1: true })
:columns="columns" :columns="columns"
/> />
<div class="px-4 py-3.5 border-t border-accented text-sm text-muted"> <div class="px-4 py-3.5 border-t border-(--ui-border-accented) text-sm text-(--ui-text-muted)">
{{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of {{ table?.tableApi?.getFilteredSelectedRowModel().rows.length || 0 }} of
{{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected. {{ table?.tableApi?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div> </div>

View File

@@ -97,7 +97,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<UAvatar :src="`https://i.pravatar.cc/120?img=${row.original.id}`" size="lg" :alt="`${row.original.name} avatar`" /> <UAvatar :src="`https://i.pravatar.cc/120?img=${row.original.id}`" size="lg" :alt="`${row.original.name} avatar`" />
<div> <div>
<p class="font-medium text-highlighted"> <p class="font-medium text-(--ui-text-highlighted)">
{{ row.original.name }} {{ row.original.name }}
</p> </p>
<p> <p>

View File

@@ -28,7 +28,7 @@ const state = reactive({
<template> <template>
<UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }"> <UTabs :items="items" variant="link" class="gap-4 w-full" :ui="{ trigger: 'flex-1' }">
<template #account="{ item }"> <template #account="{ item }">
<p class="text-muted mb-4"> <p class="text-(--ui-text-muted) mb-4">
{{ item.description }} {{ item.description }}
</p> </p>
@@ -45,7 +45,7 @@ const state = reactive({
</template> </template>
<template #password="{ item }"> <template #password="{ item }">
<p class="text-muted mb-4"> <p class="text-(--ui-text-muted) mb-4">
{{ item.description }} {{ item.description }}
</p> </p>

View File

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

View File

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

View File

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

View File

@@ -6,23 +6,21 @@ const items = [
label: 'app/', label: 'app/',
slot: 'app' as const, slot: 'app' as const,
defaultExpanded: true, defaultExpanded: true,
children: [ children: [{
{ label: 'composables/',
label: 'composables/', children: [
children: [ { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' }, { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' } ]
] },
}, {
{ label: 'components/',
label: 'components/', defaultExpanded: true,
defaultExpanded: true, children: [
children: [ { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' } ]
] }]
}
]
}, },
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' } { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }

View File

@@ -5,24 +5,22 @@ const items: TreeItem[] = [
{ {
label: 'app/', label: 'app/',
value: 'app', value: 'app',
children: [ children: [{
{ label: 'composables/',
label: 'composables/', value: 'composables',
value: 'composables', children: [
children: [ { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' }, { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' } ]
] },
}, {
{ label: 'components/',
label: 'components/', value: 'components',
value: 'components', children: [
children: [ { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' } ]
] }]
}
]
}, },
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' } { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }

View File

@@ -5,23 +5,21 @@ const items: TreeItem[] = [
{ {
label: 'app/', label: 'app/',
defaultExpanded: true, defaultExpanded: true,
children: [ children: [{
{ label: 'composables/',
label: 'composables/', children: [
children: [ { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' }, { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' } ]
] },
}, {
{ label: 'components/',
label: 'components/', defaultExpanded: true,
defaultExpanded: true, children: [
children: [ { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' } ]
] }]
}
]
}, },
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' } { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }

View File

@@ -8,23 +8,21 @@ const items: TreeItem[] = [
onSelect: (e: Event) => { onSelect: (e: Event) => {
e.preventDefault() e.preventDefault()
}, },
children: [ children: [{
{ label: 'composables/',
label: 'composables/', children: [
children: [ { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' }, { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' } ]
] },
}, {
{ label: 'components/',
label: 'components/', defaultExpanded: true,
defaultExpanded: true, children: [
children: [ { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' } ]
] }]
}
]
}, },
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' } { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }

View File

@@ -8,23 +8,21 @@ const items: TreeItem[] = [
onToggle: (e: Event) => { onToggle: (e: Event) => {
e.preventDefault() e.preventDefault()
}, },
children: [ children: [{
{ label: 'composables/',
label: 'composables/', children: [
children: [ { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
{ label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' }, { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
{ label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' } ]
] },
}, {
{ label: 'components/',
label: 'components/', defaultExpanded: true,
defaultExpanded: true, children: [
children: [ { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' }
{ label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' } ]
] }]
}
]
}, },
{ label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' }, { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
{ label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' } { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }

View File

@@ -20,7 +20,7 @@ const { width } = useElementSize(el)
<template> <template>
<div <div
class="isolate rounded-full relative circle w-full aspect-[1/1] p-8 sm:p-12 md:p-14 lg:p-10 xl:p-16 before:absolute before:inset-px before:bg-default before:rounded-full z-(--level)" class="isolate rounded-full relative circle w-full aspect-[1/1] p-8 sm:p-12 md:p-14 lg:p-10 xl:p-16 before:absolute before:inset-px before:bg-(--ui-bg) before:rounded-full z-(--level)"
:class="{ 'animation-paused': paused }" :class="{ 'animation-paused': paused }"
:style="{ :style="{
'--duration': `${((level + 1) * 8)}s`, '--duration': `${((level + 1) * 8)}s`,
@@ -65,7 +65,7 @@ const { width } = useElementSize(el)
:src="`https://ipx.nuxt.com/s_56x56/gh_avatar/${contributor.username}`" :src="`https://ipx.nuxt.com/s_56x56/gh_avatar/${contributor.username}`"
:srcset="`https://ipx.nuxt.com/s_112x112/gh_avatar/${contributor.username} 2x`" :srcset="`https://ipx.nuxt.com/s_112x112/gh_avatar/${contributor.username} 2x`"
:alt="contributor.username" :alt="contributor.username"
class="ring-2 ring-default lg:hover:ring-inverted transition rounded-full size-7" class="ring-2 ring-(--ui-border) lg:hover:ring-(--ui-border-inverted) transition rounded-full size-7"
loading="lazy" loading="lazy"
> >
</NuxtLink> </NuxtLink>

View File

@@ -69,7 +69,7 @@ function setBlackAsPrimary(value: boolean) {
:variant="open ? 'soft' : 'ghost'" :variant="open ? 'soft' : 'ghost'"
square square
aria-label="Color picker" aria-label="Color picker"
:ui="{ leadingIcon: 'text-primary' }" :ui="{ leadingIcon: 'text-(--ui-primary)' }"
/> />
</template> </template>

View File

@@ -18,8 +18,8 @@ const slots = defineSlots<{
variant="outline" variant="outline"
:icon="icon" :icon="icon"
:label="label" :label="label"
class="capitalize ring-default rounded-sm text-[11px]" class="capitalize ring-(--ui-border) rounded-[calc(var(--ui-radius))] text-[11px]"
:class="[selected ? 'bg-elevated' : 'hover:bg-elevated/50']" :class="[selected ? 'bg-(--ui-bg-elevated)' : 'hover:bg-(--ui-bg-elevated)/50']"
> >
<template v-if="chip || !!slots.leading" #leading> <template v-if="chip || !!slots.leading" #leading>
<slot name="leading"> <slot name="leading">

View File

@@ -1,54 +0,0 @@
import { onMounted, watch } from 'vue'
import FaviconSvg from 'public/icon.svg?raw'
export function useFaviconFromTheme() {
const colorMode = useColorMode()
function generateFaviconSvg(color: string) {
const parser = new DOMParser()
const doc = parser.parseFromString(FaviconSvg, 'image/svg+xml')
const svg = doc.documentElement
svg.querySelectorAll('path').forEach((path) => {
path.setAttribute('fill', color)
})
return new XMLSerializer().serializeToString(svg)
}
function updateFavicon() {
const root = document.documentElement
const color = getComputedStyle(root).getPropertyValue('--ui-primary').trim() || '#00DC82'
const svg = generateFaviconSvg(color)
const encoded = `data:image/svg+xml,${encodeURIComponent(svg)}`
useFavicon(encoded)
}
function setupMutationObserver() {
const styleTag = document.getElementById('nuxt-ui-colors')
if (!styleTag) return
const observer = new MutationObserver(() => {
updateFavicon()
})
observer.observe(styleTag, {
characterData: true,
subtree: true,
childList: true
})
}
onMounted(() => {
watch(colorMode, () => {
updateFavicon()
}, {
immediate: true,
flush: 'post'
})
setupMutationObserver()
})
}

View File

@@ -26,7 +26,7 @@ useHead({
{ key: 'theme-color', name: 'theme-color', content: color } { key: 'theme-color', name: 'theme-color', content: color }
], ],
link: [ link: [
// { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' } { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
], ],
style: [ style: [
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 }, { innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
@@ -47,8 +47,6 @@ useServerSeoMeta({
twitterCard: 'summary_large_image' twitterCard: 'summary_large_image'
}) })
useFaviconFromTheme()
const { frameworks, modules } = useSharedData() const { frameworks, modules } = useSharedData()
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)

View File

@@ -22,7 +22,7 @@ const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
<span class="inline-flex items-center gap-0.5"> <span class="inline-flex items-center gap-0.5">
{{ link.title }} {{ link.title }}
<sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup> <sup v-if="link.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
</span> </span>
</template> </template>
</UContentNavigation> </UContentNavigation>

View File

@@ -101,7 +101,7 @@ design_system:
@import "@nuxt/ui"; @import "@nuxt/ui";
:root { :root {
--ui-radius: 0.25rem; --ui-radius: var(--radius-sm);
--ui-container: 90rem; --ui-container: 90rem;
--ui-bg: var(--ui-color-neutral-50); --ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900); --ui-text: var(--ui-color-neutral-900);

View File

@@ -130,7 +130,7 @@ const communityLinks = computed(() => [{
</template> </template>
<template #title> <template #title>
{{ page.title }}<sup v-if="page.module === 'ui-pro'" class="ml-1 text-xs align-super font-medium text-primary">PRO</sup> {{ page.title }}<sup v-if="page.module === 'ui-pro'" class="ml-1 text-xs align-super font-medium text-(--ui-primary)">PRO</sup>
</template> </template>
<template #description> <template #description>

View File

@@ -82,7 +82,7 @@ onMounted(() => {
:ui="{ title: 'text-balance', container: 'relative' }" :ui="{ title: 'text-balance', container: 'relative' }"
> >
<template #top> <template #top>
<div class="absolute z-[-1] rounded-full bg-primary blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" /> <div class="absolute z-[-1] rounded-full bg-(--ui-primary) blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
</template> </template>
<template #headline> <template #headline>
@@ -97,7 +97,7 @@ onMounted(() => {
/> />
</template> </template>
<template #title> <template #title>
Build beautiful UI with <span class="text-primary">{{ components!.length }}+</span> powerful components Build beautiful UI with <span class="text-(--ui-primary)">{{ components!.length }}+</span> powerful components
</template> </template>
<template #links> <template #links>
@@ -121,22 +121,22 @@ onMounted(() => {
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
</UPageHero> </UPageHero>
<div v-for="category in categories" :key="category.id"> <div v-for="category in categories" :key="category.id">
<div data-track-sticky class="group mb-4 sm:mb-6 lg:mb-8 sticky top-[calc(var(--ui-header-height)-1px)] bg-default/75 backdrop-blur z-[1]"> <div data-track-sticky class="group mb-4 sm:mb-6 lg:mb-8 sticky top-[calc(var(--ui-header-height)-1px)] bg-(--ui-bg)/75 backdrop-blur z-[1]">
<div class="relative border-y border-default py-4 sm:not-group-[[data-stuck]]:py-6 lg:not-group-[[data-stuck]]:py-8 transition-all duration-300"> <div class="relative border-y border-(--ui-border) py-4 sm:not-group-[[data-stuck]]:py-6 lg:not-group-[[data-stuck]]:py-8 transition-all duration-300">
<UContainer> <UContainer>
<h2 class="relative text-pretty font-bold text-highlighted text-base sm:not-group-[[data-stuck]]:text-xl lg:not-group-[[data-stuck]]:text-2xl transition-all duration-300 "> <h2 class="relative text-pretty font-bold text-(--ui-text-highlighted) text-base sm:not-group-[[data-stuck]]:text-xl lg:not-group-[[data-stuck]]:text-2xl transition-all duration-300 ">
<a :href="`#${category.id}`" class="group lg:not-group-[[data-stuck]]:ps-2 lg:not-group-[[data-stuck]]:-ms-2"> <a :href="`#${category.id}`" class="group lg:not-group-[[data-stuck]]:ps-2 lg:not-group-[[data-stuck]]:-ms-2">
<span class="absolute -ms-8 top-1 opacity-0 group-hover:opacity-100 group-focus:opacity-100 p-1 bg-elevated hover:text-primary rounded-md hidden lg:not-group-[[data-stuck]]:flex text-muted transition"> <span class="absolute -ms-8 top-1 opacity-0 group-hover:opacity-100 group-focus:opacity-100 p-1 bg-(--ui-bg-elevated) hover:text-(--ui-primary) rounded-[calc(var(--ui-radius)*1.5)] hidden lg:not-group-[[data-stuck]]:flex text-(--ui-text-muted) transition">
<UIcon name="i-lucide-hash" class="size-4 shrink-0" /> <UIcon name="i-lucide-hash" class="size-4 shrink-0" />
</span> </span>
{{ category.title }} {{ category.title }}
</a> </a>
</h2> </h2>
<p class="text-pretty text-muted text-sm sm:not-group-[[data-stuck]]:text-base lg:not-group-[[data-stuck]]:text-lg mt-1 sm:not-group-[[data-stuck]]:mt-2 line-clamp-1 transition-all duration-300"> <p class="text-pretty text-(--ui-text-muted) text-sm sm:not-group-[[data-stuck]]:text-base lg:not-group-[[data-stuck]]:text-lg mt-1 sm:not-group-[[data-stuck]]:mt-2 line-clamp-1 transition-all duration-300">
{{ category.description }} {{ category.description }}
</p> </p>
</UContainer> </UContainer>
@@ -157,11 +157,11 @@ onMounted(() => {
<template #title> <template #title>
<div class="flex items-center gap-0.5"> <div class="flex items-center gap-0.5">
<span>{{ component.title }}</span> <span>{{ component.title }}</span>
<sup v-if="component.module === 'ui-pro'" class="text-[8px] font-medium text-primary">PRO</sup> <sup v-if="component.module === 'ui-pro'" class="text-[8px] font-medium text-(--ui-primary)">PRO</sup>
</div> </div>
</template> </template>
<div class="rounded-md border border-muted overflow-hidden aspect-[16/9]"> <div class="rounded-[calc(var(--ui-radius)*1.5)] border border-(--ui-border-muted) overflow-hidden aspect-[16/9]">
<UColorModeImage <UColorModeImage
:light="`${component.path.replace('/components/', '/components/light/')}.png`" :light="`${component.path.replace('/components/', '/components/light/')}.png`"
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`" :dark="`${component.path.replace('/components/', '/components/dark/')}.png`"

View File

@@ -27,7 +27,7 @@ features1:
description: Start with essential components, or unlock Pro for complete blocks and templates. description: Start with essential components, or unlock Pro for complete blocks and templates.
icon: i-lucide-files icon: i-lucide-files
cta1: cta1:
title: Everything you need in a [single file]{class="text-primary"}. title: Everything you need in a [single file]{class="text-(--ui-primary)"}.
description: Design and development in perfect sync with our [Free](https://www.figma.com/community/file/1288455405058138934/nuxt-ui-v3-official-design-kit-free) and Pro files. Developers can implement designs faster, while designers work with production-ready components. description: Design and development in perfect sync with our [Free](https://www.figma.com/community/file/1288455405058138934/nuxt-ui-v3-official-design-kit-free) and Pro files. Developers can implement designs faster, while designers work with production-ready components.
section1: section1:
title: Customize in a few clicks to fit your needs title: Customize in a few clicks to fit your needs
@@ -181,7 +181,7 @@ pricing:
# discount: $119 # discount: $119
billing_period: one-time payment billing_period: one-time payment
billing_cycle: plus local taxes billing_cycle: plus local taxes
class: bg-elevated/50 class: bg-(--ui-bg-elevated)/50
features: features:
- '**1 Designer**' - '**1 Designer**'
- Nuxt UI & Nuxt UI Pro Components - Nuxt UI & Nuxt UI Pro Components
@@ -203,7 +203,7 @@ pricing:
# discount: $279 # discount: $279
billing_period: one-time payment billing_period: one-time payment
billing_cycle: plus local taxes billing_cycle: plus local taxes
class: bg-elevated/50 class: bg-(--ui-bg-elevated)/50
features: features:
- '**Up to 20 Designers**' - '**Up to 20 Designers**'
- Nuxt UI & Nuxt UI Pro Components - Nuxt UI & Nuxt UI Pro Components

View File

@@ -57,7 +57,7 @@ onMounted(async () => {
<template> <template>
<div class="relative"> <div class="relative">
<div id="cursor1" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }"> <div id="cursor1" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-inverted"> <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-white dark:text-(--ui-bg)">
<path <path
fill="var(--ui-info)" fill="var(--ui-info)"
stroke="currentColor" stroke="currentColor"
@@ -72,7 +72,7 @@ onMounted(async () => {
</UBadge> </UBadge>
</div> </div>
<div id="cursor2" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }"> <div id="cursor2" class="absolute z-10 pointer-events-none" :style="{ opacity: 0 }">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-inverted"> <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" class="absolute top-0 left-0 drop-shadow-[0_1px_2px_rgb(0,0,0,0.25)] text-white dark:text-(--ui-bg)">
<path <path
fill="var(--ui-success)" fill="var(--ui-success)"
stroke="currentColor" stroke="currentColor"
@@ -99,7 +99,7 @@ onMounted(async () => {
<template #description> <template #description>
<MDC :value="page.hero.description" unwrap="p" cache-key="figma-hero-description" /> <MDC :value="page.hero.description" unwrap="p" cache-key="figma-hero-description" />
</template> </template>
<!-- <img src="/pro/figma/nuxt-ui-figma.png" alt="Screnshot of the Nuxt UI Figma design kit" class="w-full h-auto border border-default border-b-0"> --> <!-- <img src="/pro/figma/nuxt-ui-figma.png" alt="Screnshot of the Nuxt UI Figma design kit" class="w-full h-auto border border-(--ui-border) border-b-0"> -->
<div class="relative"> <div class="relative">
<video <video
ref="video" ref="video"
@@ -126,10 +126,10 @@ onMounted(async () => {
</div> </div>
</div> </div>
<Motion as-child :initial="{ height: 0 }" :animate="{ height: 'auto' }" :transition="{ delay: 0.2, duration: 1 }"> <Motion as-child :initial="{ height: 0 }" :animate="{ height: 'auto' }" :transition="{ delay: 0.2, duration: 1 }">
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
</Motion> </Motion>
</UPageHero> </UPageHero>
<UPageSection v-bind="page.features1" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-default" /> <UPageSection v-bind="page.features1" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-(--ui-border)" />
<UPageCTA <UPageCTA
v-if="page.cta1" v-if="page.cta1"
variant="naked" variant="naked"
@@ -138,7 +138,7 @@ onMounted(async () => {
wrapper: 'grid grid-cols-1 lg:grid-cols-2', wrapper: 'grid grid-cols-1 lg:grid-cols-2',
description: 'lg:mt-0' }" description: 'lg:mt-0' }"
orientation="horizontal" orientation="horizontal"
class="rounded-none bg-gradient-to-b from-elevated/50 to-default" class="rounded-none bg-gradient-to-b from-(--ui-bg-muted) to-(--ui-bg)"
> >
<template #title> <template #title>
<MDC :value="page.cta1.title" unwrap="p" cache-key="figma-cta-1-title" /> <MDC :value="page.cta1.title" unwrap="p" cache-key="figma-cta-1-title" />
@@ -155,7 +155,7 @@ onMounted(async () => {
:height="item.height" :height="item.height"
:src="item.src" :src="item.src"
:alt="item.alt" :alt="item.alt"
class="w-full h-auto rounded-lg" class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
loading="lazy" loading="lazy"
/> />
</template> </template>
@@ -165,7 +165,7 @@ onMounted(async () => {
<NuxtImg <NuxtImg
v-if="page.section2.image" v-if="page.section2.image"
v-bind="page.section2.image" v-bind="page.section2.image"
class="w-full h-auto rounded-lg" class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
loading="lazy" loading="lazy"
/> />
</UPageSection> </UPageSection>
@@ -173,7 +173,7 @@ onMounted(async () => {
<NuxtImg <NuxtImg
v-if="page.section3.image" v-if="page.section3.image"
v-bind="page.section3.image" v-bind="page.section3.image"
class="w-full h-auto rounded-lg" class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
loading="lazy" loading="lazy"
/> />
</UPageSection> </UPageSection>
@@ -192,27 +192,27 @@ onMounted(async () => {
<template #description> <template #description>
<MDC :value="page.section4.description" unwrap="p" cache-key="figma-section-4-description" /> <MDC :value="page.section4.description" unwrap="p" cache-key="figma-section-4-description" />
</template> </template>
<div aria-hidden="true" class="absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center border border-default border-b-0 sm:divide-x divide-y lg:divide-y-0 divide-default"> <ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center border border-(--ui-border) border-b-0 sm:divide-x divide-y lg:divide-y-0 divide-(--ui-border)">
<li v-for="(step, index) in page?.section4.steps" :key="step.title" class="flex flex-col gap-y-4 justify-start group h-full p-4"> <li v-for="(step, index) in page?.section4.steps" :key="step.title" class="flex flex-col gap-y-4 justify-start group h-full p-4">
<NuxtImg <NuxtImg
v-if="step.image" v-if="step.image"
v-bind="step.image" v-bind="step.image"
class="rounded-sm" class="rounded-(--ui-radius)"
loading="lazy" loading="lazy"
/> />
<div> <div>
<h2 class="font-semibold inline-flex items-center gap-x-1"> <h2 class="font-semibold inline-flex items-center gap-x-1">
<UBadge :label="index + 1" size="sm" color="neutral" variant="subtle" class="rounded-full tabular-nums" /> {{ step.title }} <UBadge :label="index + 1" size="sm" color="neutral" variant="subtle" class="rounded-full tabular-nums" /> {{ step.title }}
</h2> </h2>
<p class="text-muted text-sm"> <p class="text-(--ui-text-muted) text-sm">
{{ step.description }} {{ step.description }}
</p> </p>
</div> </div>
</li> </li>
</ul> </ul>
</UPageSection> </UPageSection>
<UPageSection v-bind="page.features2" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-default" /> <UPageSection v-bind="page.features2" :ui="{ container: 'py-16 sm:py-16 lg:py-16', features: 'mt-0' }" class="border-y border-(--ui-border)" />
<UPageSection <UPageSection
v-if="page.pricing" v-if="page.pricing"
:title="page.pricing.title" :title="page.pricing.title"
@@ -226,7 +226,7 @@ onMounted(async () => {
wrapper: 'sm:pl-8' wrapper: 'sm:pl-8'
}" }"
> >
<div aria-hidden="true" class="absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<UPricingPlans compact class="-space-x-px"> <UPricingPlans compact class="-space-x-px">
<UPricingPlan <UPricingPlan
v-for="(plan, index) in page.pricing.plans" v-for="(plan, index) in page.pricing.plans"
@@ -246,8 +246,8 @@ onMounted(async () => {
> >
<template #features> <template #features>
<li v-for="(feature, i) in plan.features" :key="i" class="flex items-center gap-2 min-w-0"> <li v-for="(feature, i) in plan.features" :key="i" class="flex items-center gap-2 min-w-0">
<UIcon name="i-lucide-circle-check" class="size-5 shrink-0 text-primary" /> <UIcon name="i-lucide-circle-check" class="size-5 shrink-0 text-(--ui-primary)" />
<MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-accented" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" /> <MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-(--ui-text-accented)" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" />
</li> </li>
</template> </template>
<template #button> <template #button>
@@ -278,7 +278,7 @@ onMounted(async () => {
</UPageMarquee> </UPageMarquee>
</UPageCTA> </UPageCTA>
<UPageSection v-bind="page.faq" :ui="{ container: 'relative' }"> <UPageSection v-bind="page.faq" :ui="{ container: 'relative' }">
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<UPageAccordion <UPageAccordion
multiple multiple
:items="(page.faq.items as any[])" :items="(page.faq.items as any[])"

View File

@@ -48,7 +48,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
}" }"
> >
<template #title> <template #title>
The Intuitive <br> <span class="text-primary">Vue UI Library</span> The Intuitive <br> <span class="text-(--ui-primary)">Vue UI Library</span>
</template> </template>
<template #description> <template #description>
{{ page.hero.description }} {{ page.hero.description }}
@@ -81,14 +81,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
pause-on-hover pause-on-hover
:overlay="false" :overlay="false"
:ui="{ :ui="{
root: '[--gap:--spacing(4)] [--duration:40s] border-default absolute w-full left-0 border-y lg:border-x lg:border-y-0 lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:flex-col', root: '[--gap:--spacing(4)] [--duration:40s] border-(--ui-border) absolute w-full left-0 border-y lg:border-x lg:border-y-0 lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:flex-col',
content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content]' content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content]'
}" }"
> >
<ULink <ULink
v-for="component of components?.slice(0, 10)" v-for="component of components?.slice(0, 10)"
:key="component.path" :key="component.path"
class="relative group/link aspect-video border-default w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y" class="relative group/link aspect-video border-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
:to="component.path" :to="component.path"
> >
<UColorModeImage <UColorModeImage
@@ -98,7 +98,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
width="290" width="290"
height="163" height="163"
format="webp" format="webp"
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-default 2xl:border-y-0" class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
loading="lazy" loading="lazy"
/> />
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" /> <UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
@@ -110,14 +110,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
reverse reverse
:overlay="false" :overlay="false"
:ui="{ :ui="{
root: '[--gap:--spacing(4)] [--duration:40s] border-default absolute w-full mt-[180px] left-0 border-y lg:mt-auto lg:left-auto lg:border-y-0 lg:border-x lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:right-0 lg:flex-col', root: '[--gap:--spacing(4)] [--duration:40s] border-(--ui-border) absolute w-full mt-[180px] left-0 border-y lg:mt-auto lg:left-auto lg:border-y-0 lg:border-x lg:w-[calc(50%-6px)] 2xl:w-[320px] lg:right-0 lg:flex-col',
content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content] lg:[animation-direction:reverse]' content: 'lg:w-auto lg:flex-col lg:animate-[marquee-vertical_var(--duration)_linear_infinite] lg:rtl:animate-[marquee-vertical-rtl_var(--duration)_linear_infinite] lg:h-[fit-content] lg:[animation-direction:reverse]'
}" }"
> >
<ULink <ULink
v-for="component of components?.slice(10, 20)" v-for="component of components?.slice(10, 20)"
:key="component.path" :key="component.path"
class="relative group/link aspect-video border-default w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y" class="relative group/link aspect-video border-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
:to="component.path" :to="component.path"
> >
<UColorModeImage <UColorModeImage
@@ -127,7 +127,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
width="290" width="290"
height="163" height="163"
format="webp" format="webp"
class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-default 2xl:border-y-0" class="hover:scale-105 lg:hover:scale-110 transition-transform aspect-video w-full border-x lg:border-x-0 lg:border-y border-(--ui-border) 2xl:border-y-0"
loading="lazy" loading="lazy"
/> />
<UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" /> <UBadge color="neutral" variant="outline" size="md" :label="component.title" class="hidden lg:block absolute mx-auto top-4 left-6 xl:left-4 group-hover/link:opacity-100 opacity-0 transition-all duration-300 pointer-events-none -translate-y-2 group-hover/link:translate-y-0" />
@@ -168,11 +168,11 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
<UIcon :name="feature.icon" class="size-5 shrink-0" /> <UIcon :name="feature.icon" class="size-5 shrink-0" />
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<h2 class="font-medium text-highlighted inline-flex items-center gap-x-1"> <h2 class="font-medium text-(--ui-text-highlighted) inline-flex items-center gap-x-1">
{{ feature.title }} {{ feature.title }}
<UIcon v-if="feature.to" name="i-lucide-arrow-right" class="size-4 shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-200 -translate-x-1 group-hover:translate-x-0" /> <UIcon v-if="feature.to" name="i-lucide-arrow-right" class="size-4 shrink-0 opacity-0 group-hover:opacity-100 transition-all duration-200 -translate-x-1 group-hover:translate-x-0" />
</h2> </h2>
<p class="text-sm text-muted"> <p class="text-sm text-(--ui-text-muted)">
{{ feature.description }} {{ feature.description }}
</p> </p>
</div> </div>
@@ -215,33 +215,33 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
:links="page.community.links" :links="page.community.links"
orientation="horizontal" orientation="horizontal"
:ui="{ features: 'flex items-center gap-4 lg:gap-8' }" :ui="{ features: 'flex items-center gap-4 lg:gap-8' }"
class="border-b border-default" class="border-b border-(--ui-border)"
> >
<template #features> <template #features>
<li> <li>
<NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0"> <NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0">
<p class="text-4xl font-semibold text-highlighted truncate"> <p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
{{ format(module?.stats?.downloads ?? 0) }}+ {{ format(module?.stats?.downloads ?? 0) }}+
</p> </p>
<p class="text-muted text-sm truncate">monthly downloads</p> <p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
</NuxtLink> </NuxtLink>
</li> </li>
<li> <li>
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0"> <NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0">
<p class="text-4xl font-semibold text-highlighted truncate"> <p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
{{ format(module?.stats?.stars ?? 0) }}+ {{ format(module?.stats?.stars ?? 0) }}+
</p> </p>
<p class="text-muted text-sm truncate">GitHub stars</p> <p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
</NuxtLink> </NuxtLink>
</li> </li>
<li> <li>
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0"> <NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
<p class="text-4xl font-semibold text-highlighted truncate"> <p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
175+ 175+
</p> </p>
<p class="text-muted text-sm truncate">Contributors</p> <p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
</NuxtLink> </NuxtLink>
</li> </li>
</template> </template>
@@ -253,10 +253,10 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
<UPageSection :ui="{ container: 'relative !pb-0 overflow-hidden' }"> <UPageSection :ui="{ container: 'relative !pb-0 overflow-hidden' }">
<template #title> <template #title>
Build faster with Nuxt UI <span class="text-primary">Pro</span>. Build faster with Nuxt UI <span class="text-(--ui-primary)">Pro</span>.
</template> </template>
<template #description> <template #description>
A collection of premium Vue components, composables and utils built on top of Nuxt UI. <br> Focused on structure and layout, these <span class="text-default">responsive components</span> are designed to be the perfect <span class="text-default">building blocks for your next idea</span>. A collection of premium Vue components, composables and utils built on top of Nuxt UI. <br> Focused on structure and layout, these <span class="text-(--ui-text)">responsive components</span> are designed to be the perfect <span class="text-(--ui-text)">building blocks for your next idea</span>.
</template> </template>
<template #links> <template #links>
<UButton to="/pro" size="lg"> <UButton to="/pro" size="lg">
@@ -269,8 +269,8 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<div class="relative h-[400px] border border-default bg-muted overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full"> <div class="relative h-[400px] border border-(--ui-border) bg-(--ui-bg-muted) overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full">
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -left-[100px] -top-[300px] h-[940px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -left-[100px] -top-[300px] h-[940px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
<img <img
v-for="i in 4" v-for="i in 4"
@@ -280,7 +280,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
height="258" height="258"
loading="lazy" loading="lazy"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
<UPageMarquee orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -top-[400px] left-[480px] h-[1160px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -top-[400px] left-[480px] h-[1160px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
@@ -292,7 +292,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
height="258" height="258"
loading="lazy" loading="lazy"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: 'hidden md:flex [--duration:40s] absolute w-[460px] -top-[300px] left-[1020px] h-[1060px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: 'hidden md:flex [--duration:40s] absolute w-[460px] -top-[300px] left-[1020px] h-[1060px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
@@ -304,7 +304,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
height="258" height="258"
loading="lazy" loading="lazy"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
</div> </div>

View File

@@ -2,7 +2,7 @@ title: Nuxt UI Pro Pricing
description: Start for free in development mode, then upgrade to a paid plan to unlock the full features of Nuxt UI Pro when you are ready to launch. description: Start for free in development mode, then upgrade to a paid plan to unlock the full features of Nuxt UI Pro when you are ready to launch.
pricing: pricing:
headline: Pricing headline: Pricing
title: Upgrade to Nuxt UI [Pro]{class="text-primary"}. title: Upgrade to Nuxt UI [Pro]{class="text-(--ui-primary)"}.
description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products. description: On top of 40+ open source components from Nuxt UI, Pro gives you access to 50+ premium Vue components to create beautiful & responsive Nuxt applications in minutes. It includes all primitives to build landing pages, documentations, blogs, dashboards or entire SaaS products.
freePlan: freePlan:
title: Free in development title: Free in development

View File

@@ -1,8 +1,8 @@
title: Build faster with Nuxt UI Pro. title: Build faster with Nuxt UI Pro.
description: A collection of premium Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your application. description: A collection of premium Vue components, composables and utils built on top of Nuxt UI, oriented on structure and layout and designed to be used as building blocks for your application.
hero: hero:
title: Build faster with Nuxt UI [Pro]{class="text-primary"}. title: Build faster with Nuxt UI [Pro]{class="text-(--ui-primary)"}.
description: A collection of premium Vue components, composables and utils built on top of Nuxt UI. :br Focused on structure and layout, these [responsive components]{class="text-default"} are designed to be the perfect [building blocks for your next idea]{class="text-default"}. description: A collection of premium Vue components, composables and utils built on top of Nuxt UI. :br Focused on structure and layout, these [responsive components]{class="text-(--ui-text)"} are designed to be the perfect [building blocks for your next idea]{class="text-(--ui-text)"}.
links: links:
- label: Buy a license - label: Buy a license
size: xl size: xl
@@ -62,7 +62,7 @@ testimonial:
# avatar: # avatar:
# src: https://github.com/benjamincanac.png # src: https://github.com/benjamincanac.png
mainSection: mainSection:
title: Meet the [Pro Components]{class="text-primary"}. title: Meet the [Pro Components]{class="text-(--ui-primary)"}.
description: Code with 50+ components and sections of Nuxt UI Pro to build your next application by reducing the amount of code you need to write. description: Code with 50+ components and sections of Nuxt UI Pro to build your next application by reducing the amount of code you need to write.
sections: sections:
- title: The freedom to build anything - title: The freedom to build anything

View File

@@ -1,7 +1,7 @@
title: Official Nuxt UI Pro Templates title: Official Nuxt UI Pro Templates
description: 'Ready to use templates powered by our premium Vue components and Nuxt Content. The templates are responsive, accessible and easy to customize so you can get started in no time.' description: 'Ready to use templates powered by our premium Vue components and Nuxt Content. The templates are responsive, accessible and easy to customize so you can get started in no time.'
hero: hero:
title: Ship [in minutes]{.text-primary} with :br Nuxt UI Pro Templates title: Ship [in minutes]{.text-(--ui-primary)} with :br Nuxt UI Pro Templates
description: 'Ready to use templates powered by our premium Vue components and Nuxt Content.<br class="hidden lg:block"> The templates are responsive, accessible and easy to customize so you can get started in no time.' description: 'Ready to use templates powered by our premium Vue components and Nuxt Content.<br class="hidden lg:block"> The templates are responsive, accessible and easy to customize so you can get started in no time.'
navigation: false navigation: false
links: links:
@@ -16,34 +16,8 @@ links:
variant: outline variant: outline
trailingIcon: i-lucide-arrow-right trailingIcon: i-lucide-arrow-right
templates: templates:
- title: 'Chat'
description: "An AI chatbot template designed to help you build your own chatbot with Nuxt UI Pro components and deployed on [NuxtHub](https://hub.nuxt.com)."
icon: i-lucide-message-circle
thumbnail:
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2NoYXQtdGVtcGxhdGUubnV4dC5kZXYiLCJpYXQiOjE3NDI4NDY2ODB9.n4YCsoNz8xatox7UMoYZFNo7iS1mC_DT0h0A9cKRoTw.jpg?theme=dark
light: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2NoYXQtdGVtcGxhdGUubnV4dC5kZXYiLCJpYXQiOjE3NDI4NDY2ODB9.n4YCsoNz8xatox7UMoYZFNo7iS1mC_DT0h0A9cKRoTw.jpg?theme=light
features:
- title: Powered by Cloudflare AI models
icon: i-simple-icons-cloudflare
- title: GitHub OAuth authentication
icon: i-lucide-lock
- title: Saved chats and messages
icon: i-lucide-database
links:
- label: Preview
to: https://chat-template.nuxt.dev
target: _blank
leadingIcon: i-logos-nuxt-icon
trailingIcon: i-lucide-arrow-up-right
color: neutral
- label: Nuxt Template
to: https://github.com/nuxt-ui-pro/chat
target: _blank
icon: i-simple-icons-github
color: neutral
variant: outline
- title: 'Dashboard' - title: 'Dashboard'
description: "A template to illustrate how to build your own dashboard with 15+ Nuxt UI Pro components, designed specifically to create a consistent look and feel." description: "A template to illustrate how to build your own dashboard with the 15+ latest Nuxt UI Pro components, designed specifically to create a consistent look and feel."
icon: i-lucide-bar-chart-big icon: i-lucide-bar-chart-big
thumbnail: thumbnail:
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2Rhc2hib2FyZC10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTczOTQ2MzU2N30._VElt4uvLjvAMdnTLytCInOajMElzWDKbmvOaMZhZUI.jpg?theme=dark dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2Rhc2hib2FyZC10ZW1wbGF0ZS5udXh0LmRldiIsImlhdCI6MTczOTQ2MzU2N30._VElt4uvLjvAMdnTLytCInOajMElzWDKbmvOaMZhZUI.jpg?theme=dark

View File

@@ -33,7 +33,7 @@ const activating = ref(false)
const successMessage = ref() const successMessage = ref()
const errorMessage = ref('') const errorMessage = ref('')
async function submit(event: FormSubmitEvent<Schema>) { async function submit(event: FormSubmitEvent<any>) {
activating.value = true activating.value = true
errorMessage.value = '' errorMessage.value = ''
successMessage.value = '' successMessage.value = ''
@@ -73,9 +73,9 @@ onMounted(() => {
<UPageHero headline="License Activation" :title="title" :description="description" :ui="{ container: 'relative overflow-hidden', wrapper: 'lg:px-12', description: 'text-pretty' }"> <UPageHero headline="License Activation" :title="title" :description="description" :ui="{ container: 'relative overflow-hidden', wrapper: 'lg:px-12', description: 'text-pretty' }">
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<div class="px-4 py-10 lg:border border-default bg-default"> <div class="px-4 py-10 lg:border border-(--ui-border) bg-(--ui-bg)">
<div class="max-w-xl mx-auto"> <div class="max-w-xl mx-auto">
<UForm <UForm
:schema="schema" :schema="schema"

View File

@@ -35,9 +35,9 @@ useSeoMeta({
<LazyStarsBg /> <LazyStarsBg />
<Motion as-child :initial="{ height: 0 }" :animate="{ height: 'auto' }" :transition="{ delay: 0.2, duration: 1 }"> <Motion as-child :initial="{ height: 0 }" :animate="{ height: 'auto' }" :transition="{ delay: 0.2, duration: 1 }">
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
</Motion> </Motion>
<div class="relative h-[400px] border border-default bg-muted overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full"> <div class="relative h-[400px] border border-(--ui-border) bg-(--ui-bg-muted) overflow-hidden border-x-0 -mx-4 sm:-mx-6 lg:mx-0 lg:border-x w-screen lg:w-full">
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -left-[100px] -top-[300px] h-[940px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -left-[100px] -top-[300px] h-[940px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
<img <img
v-for="i in 4" v-for="i in 4"
@@ -46,7 +46,7 @@ useSeoMeta({
width="460" width="460"
height="258" height="258"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
<UPageMarquee orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -top-[400px] left-[480px] h-[1160px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee orientation="vertical" :overlay="false" :ui="{ root: '[--duration:40s] absolute w-[460px] -top-[400px] left-[480px] h-[1160px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
@@ -57,7 +57,7 @@ useSeoMeta({
width="460" width="460"
height="258" height="258"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
<UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: 'hidden md:flex [--duration:40s] absolute w-[460px] -top-[300px] left-[1020px] h-[1060px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }"> <UPageMarquee reverse orientation="vertical" :overlay="false" :ui="{ root: 'hidden md:flex [--duration:40s] absolute w-[460px] -top-[300px] left-[1020px] h-[1060px] transform-3d rotate-x-55 rotate-y-0 rotate-z-30' }">
@@ -68,7 +68,7 @@ useSeoMeta({
width="460" width="460"
height="258" height="258"
:alt="`Nuxt UI Pro Screenshot ${i}`" :alt="`Nuxt UI Pro Screenshot ${i}`"
class="aspect-video border border-default rounded-lg bg-white" class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
> >
</UPageMarquee> </UPageMarquee>
</div> </div>
@@ -101,10 +101,10 @@ useSeoMeta({
container: 'relative', container: 'relative',
wrapper: 'sm:px-8' wrapper: 'sm:px-8'
}" }"
class="border-t border-default" class="border-t border-(--ui-border)"
> >
<Motion as-child :initial="{ height: 0 }" :while-in-view="{ height: 'auto' }" :transition="{ delay: 0.4, duration: 1 }"> <Motion as-child :initial="{ height: 0 }" :while-in-view="{ height: 'auto' }" :transition="{ delay: 0.4, duration: 1 }">
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
</Motion> </Motion>
</UPageSection> </UPageSection>
@@ -116,7 +116,7 @@ useSeoMeta({
wrapper: 'grid grid-cols-1 lg:grid-cols-2', wrapper: 'grid grid-cols-1 lg:grid-cols-2',
description: 'lg:mt-0' }" description: 'lg:mt-0' }"
orientation="horizontal" orientation="horizontal"
class="rounded-none border-t border-default bg-gradient-to-b from-elevated/50 to-default" class="rounded-none border-t border-(--ui-border) bg-gradient-to-b from-(--ui-bg-elevated)/50 to-(--ui-bg)"
> >
<template #title> <template #title>
<MDC :value="page.mainSection.title" tag="span" unwrap="p" cache-key="pro-main-section-title" /> <MDC :value="page.mainSection.title" tag="span" unwrap="p" cache-key="pro-main-section-title" />
@@ -134,7 +134,7 @@ useSeoMeta({
:reverse="section.reverse" :reverse="section.reverse"
:features="section.features" :features="section.features"
orientation="horizontal" orientation="horizontal"
:class="{ 'border-b border-default': index === page.sections.length - 1 }" :class="{ 'border-b border-(--ui-border)': index === page.sections.length - 1 }"
:ui="{ :ui="{
container: index === 0 ? 'pb-0 sm:pb-0 lg:pb-0 py-16 sm:py-16 lg:py-16' : '' container: index === 0 ? 'pb-0 sm:pb-0 lg:pb-0 py-16 sm:py-16 lg:py-16' : ''
}" }"
@@ -145,10 +145,10 @@ useSeoMeta({
<UPageSection <UPageSection
id="templates" id="templates"
v-bind="page.templates" v-bind="page.templates"
class="overflow-hidden border-x border-default" class="overflow-hidden border-x border-(--ui-border)"
:ui="{ container: 'relative' }" :ui="{ container: 'relative' }"
> >
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<UCarousel <UCarousel
v-slot="{ item }" v-slot="{ item }"
loop loop
@@ -160,7 +160,7 @@ useSeoMeta({
:ui="{ :ui="{
item: 'basis-1/2', item: 'basis-1/2',
container: 'py-2', container: 'py-2',
viewport: 'border-x border-default', viewport: 'border-x border-(--ui-border)',
arrows: 'hidden 2xl:block' arrows: 'hidden 2xl:block'
}" }"
> >
@@ -181,7 +181,7 @@ useSeoMeta({
:light="item.thumbnail.light" :light="item.thumbnail.light"
:dark="item.thumbnail.dark" :dark="item.thumbnail.dark"
:alt="item.title" :alt="item.title"
class="rounded-lg w-full border border-default aspect-video" class="rounded-lg w-full border border-(--ui-border) aspect-video"
loading="lazy" loading="lazy"
/> />
</UPageCard> </UPageCard>
@@ -199,7 +199,7 @@ useSeoMeta({
<LazyStarsBg /> <LazyStarsBg />
<video <video
class="rounded-sm z-10" class="rounded-[var(--ui-radius)] z-10"
preload="none" preload="none"
poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg" poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg"
:controls="true" :controls="true"

View File

@@ -29,13 +29,13 @@ useSeoMeta({
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<div class="flex flex-col bg-default gap-8 lg:gap-0"> <div class="flex flex-col bg-(--ui-bg) gap-8 lg:gap-0">
<UPricingPlan <UPricingPlan
v-bind="page.pricing.freePlan" v-bind="page.pricing.freePlan"
variant="naked" variant="naked"
class="lg:rounded-none border-x border-default border-t border-b lg:border-b-0" class="lg:rounded-none border-x border-(--ui-border) border-t border-b lg:border-b-0"
/> />
<UPricingPlans compact> <UPricingPlans compact>
<UPricingPlan <UPricingPlan
@@ -48,7 +48,7 @@ useSeoMeta({
:billing-period="plan.billing_period" :billing-period="plan.billing_period"
:billing-cycle="plan.billing_cycle" :billing-cycle="plan.billing_cycle"
:variant="plan.highlight ? 'soft' : 'outline'" :variant="plan.highlight ? 'soft' : 'outline'"
:class="['lg:rounded-none', { 'border-2 lg:border lg:border-x-0 border-primary lg:border-default': plan.highlight }]" :class="['lg:rounded-none', { 'border-2 lg:border lg:border-x-0 border-(--ui-primary) lg:border-(--ui-border)': plan.highlight }]"
:features="plan.features" :features="plan.features"
:button="plan.button" :button="plan.button"
/> />
@@ -58,12 +58,12 @@ useSeoMeta({
variant="naked" variant="naked"
:billing-period="page.pricing.figma.billing_period" :billing-period="page.pricing.figma.billing_period"
:billing-cycle="page.pricing.figma.billing_cycle" :billing-cycle="page.pricing.figma.billing_cycle"
class="lg:rounded-none border lg:border-y-0 border-default" class="lg:rounded-none border lg:border-y-0 border-(--ui-border)"
> >
<template #features> <template #features>
<li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0"> <li v-for="(feature, index) in page.pricing.figma.features" :key="index" class="flex items-center gap-2 min-w-0">
<UIcon name="i-lucide-circle-check" class="size-5 text-primary shrink-0" /> <UIcon name="i-lucide-circle-check" class="size-5 text-(--ui-primary) shrink-0" />
<MDC :value="feature" unwrap="p" class="text-sm truncate text-toned" :cache-key="`pro-pricing-figma-feature-${index}`" /> <MDC :value="feature" unwrap="p" class="text-sm truncate text-(--ui-text-toned)" :cache-key="`pro-pricing-figma-feature-${index}`" />
</li> </li>
</template> </template>
</UPricingPlan> </UPricingPlan>
@@ -73,7 +73,7 @@ useSeoMeta({
<UPageSection <UPageSection
id="testimonials" id="testimonials"
v-bind="page.testimonials" v-bind="page.testimonials"
class="border-y border-default" class="border-y border-(--ui-border)"
> >
<UPageMarquee pause-on-hover :ui="{ root: '[--duration:40s]' }"> <UPageMarquee pause-on-hover :ui="{ root: '[--duration:40s]' }">
<img <img
@@ -110,7 +110,7 @@ useSeoMeta({
class="scroll-mt-(--ui-header-height)" class="scroll-mt-(--ui-header-height)"
:ui="{ container: 'relative' }" :ui="{ container: 'relative' }"
> >
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<UPageAccordion <UPageAccordion
multiple multiple
:items="(page.faq.items as any[])" :items="(page.faq.items as any[])"

View File

@@ -20,7 +20,7 @@ useSeoMeta({
<UPageHero :links="page.links" :ui="{ container: 'relative' }"> <UPageHero :links="page.links" :ui="{ container: 'relative' }">
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<template #title> <template #title>
<MDC :value="page.hero.title" unwrap="p" cache-key="pro-templates-hero-title" /> <MDC :value="page.hero.title" unwrap="p" cache-key="pro-templates-hero-title" />
@@ -38,10 +38,10 @@ useSeoMeta({
:links="template.links" :links="template.links"
:features="template.features" :features="template.features"
orientation="horizontal" orientation="horizontal"
class="lg:border-t border-default" class="lg:border-t border-(--ui-border)"
:ui="{ :ui="{
title: 'lg:text-4xl', title: 'lg:text-4xl',
wrapper: 'lg:py-16 lg:border-r border-default order-last lg:pr-16', wrapper: 'lg:py-16 lg:border-r border-(--ui-border) order-last lg:pr-16',
container: 'lg:py-0', container: 'lg:py-0',
links: 'gap-x-3' links: 'gap-x-3'
}" }"
@@ -50,12 +50,12 @@ useSeoMeta({
<MDC :value="template.description" unwrap="p" :cache-key="`pro-templates-${index}-description`" /> <MDC :value="template.description" unwrap="p" :cache-key="`pro-templates-${index}-description`" />
</template> </template>
<div class="lg:border-x border-default h-full flex items-center lg:bg-muted/20"> <div class="lg:border-x border-(--ui-border) h-full flex items-center lg:bg-(--ui-bg-muted)/20">
<Motion class="flex-1" :initial="{ opacity: 0, transform: 'translateY(10px)' }" :while-in-view="{ opacity: 1, transform: 'translateY(0px)' }" :in-view-options="{ once: true }" :transition="{ duration: 0.5, delay: 0.2 }"> <Motion class="flex-1" :initial="{ opacity: 0, transform: 'translateY(10px)' }" :while-in-view="{ opacity: 1, transform: 'translateY(0px)' }" :in-view-options="{ once: true }" :transition="{ duration: 0.5, delay: 0.2 }">
<UColorModeImage <UColorModeImage
v-if="template.thumbnail" v-if="template.thumbnail"
v-bind="template.thumbnail" v-bind="template.thumbnail"
class="w-full h-auto border lg:border-y lg:border-x-0 border-default rounded-sm lg:rounded-none" class="w-full h-auto border lg:border-y lg:border-x-0 border-(--ui-border) rounded-(--ui-radius) lg:rounded-none"
:alt="`Template ${index} thumbnail`" :alt="`Template ${index} thumbnail`"
width="656" width="656"
height="369" height="369"

View File

@@ -29,19 +29,19 @@ defineOgImageComponent('Docs', {
}" }"
> >
<template #top> <template #top>
<div class="absolute z-[-1] rounded-full bg-primary blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" /> <div class="absolute z-[-1] rounded-full bg-(--ui-primary) blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
</template> </template>
<LazyStarsBg /> <LazyStarsBg />
<div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-default inset-0 mx-4 sm:mx-6 lg:mx-8" /> <div aria-hidden="true" class="hidden lg:block absolute z-[-1] border-x border-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
<div class="border-l border-t border-default"> <div class="border-l border-t border-(--ui-border)">
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center divide-y divide-x divide-default"> <ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center divide-y divide-x divide-(--ui-border)">
<li <li
v-for="item in page.items" v-for="item in page.items"
:key="item.name" :key="item.name"
class="group relative flex items-center justify-center flex-1 size-full p-2 last:border-r last:border-b border-default overflow-hidden" class="group relative flex items-center justify-center flex-1 size-full p-2 last:border-r last:border-b border-(--ui-border) overflow-hidden"
> >
<NuxtLink class="inset-0 absolute" :to="item.url" target="_blank"> <NuxtLink class="inset-0 absolute" :to="item.url" target="_blank">
<span class="sr-only">Go to {{ item.name }}</span> <span class="sr-only">Go to {{ item.name }}</span>

View File

@@ -41,7 +41,7 @@ const icons = {
:ui="{ title: 'text-balance', container: 'relative' }" :ui="{ title: 'text-balance', container: 'relative' }"
> >
<template #top> <template #top>
<div class="absolute z-[-1] rounded-full bg-primary blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" /> <div class="absolute z-[-1] rounded-full bg-(--ui-primary) blur-[300px] size-60 sm:size-80 transform -translate-x-1/2 left-1/2 -translate-y-80" />
</template> </template>
<LazyStarsBg /> <LazyStarsBg />
@@ -58,7 +58,7 @@ const icons = {
container: 'gap-y-4 lg:p-8', container: 'gap-y-4 lg:p-8',
leading: 'flex justify-center', leading: 'flex justify-center',
title: 'text-center', title: 'text-center',
description: 'text-center text-muted' description: 'text-center text-(--ui-text-muted)'
}" }"
variant="subtle" variant="subtle"
> >
@@ -126,7 +126,7 @@ const icons = {
container: 'gap-y-2', container: 'gap-y-2',
leading: 'flex justify-center', leading: 'flex justify-center',
title: 'text-center', title: 'text-center',
description: 'text-center text-muted' description: 'text-center text-(--ui-text-muted)'
}" }"
> >
<template #leading> <template #leading>

View File

@@ -16,12 +16,12 @@ function handleMessage(message) {
async function handleFormatMessage(message) { async function handleFormatMessage(message) {
if (!globalThis.prettier) { if (!globalThis.prettier) {
await Promise.all([ await Promise.all([
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/standalone.js'), import('https://unpkg.com/prettier@3.5.2/standalone.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/babel.js'), import('https://unpkg.com/prettier@3.5.2/plugins/babel.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/estree.js'), import('https://unpkg.com/prettier@3.5.2/plugins/estree.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/html.js'), import('https://unpkg.com/prettier@3.5.2/plugins/html.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/markdown.js'), import('https://unpkg.com/prettier@3.5.2/plugins/markdown.js'),
import('https://cdn.jsdelivr.net/npm/prettier@3.5.2/plugins/typescript.js') import('https://unpkg.com/prettier@3.5.2/plugins/typescript.js')
]) ])
} }

View File

@@ -4,7 +4,7 @@ description: 'Nuxt UI harnesses the combined strengths of Reka UI, Tailwind CSS,
navigation.icon: i-lucide-house navigation.icon: i-lucide-house
--- ---
<iframe width="100%" height="100%" src="https://www.youtube-nocookie.com/embed/_eQxomah-nA?si=pDSzchUBDKb2NQu7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="aspect-ratio: 16/9;" class="rounded-md"></iframe> <iframe width="100%" height="100%" src="https://www.youtube-nocookie.com/embed/_eQxomah-nA?si=pDSzchUBDKb2NQu7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="aspect-ratio: 16/9;" class="rounded-[calc(var(--ui-radius)*1.5)]"></iframe>
## Reka UI ## Reka UI

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