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
375 changed files with 16946 additions and 19374 deletions

View File

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

View File

@@ -18,7 +18,7 @@ jobs:
strategy:
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]
env:
@@ -65,8 +65,6 @@ jobs:
run: pnpm run dev:vue:build
- 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
starter-nuxt:

1
.npmrc
View File

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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@ useHead({
{ key: 'theme-color', name: 'theme-color', content: color }
],
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)}` }
],
style: [
@@ -40,8 +40,6 @@ useServerSeoMeta({
twitterCard: 'summary_large_image'
})
useFaviconFromTheme()
const { frameworks, modules } = useSharedData()
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
@@ -87,5 +85,5 @@ provide('navigation', mappedNavigation)
</template>
<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>

View File

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

View File

@@ -23,27 +23,27 @@ onMounted(() => {
@reference "../assets/css/main.css";
.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 {
@apply flex justify-center w-full;
& > img {
@apply !max-w-full w-full rounded-sm;
@apply !max-w-full w-full rounded-(--ui-radius);
}
}
.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 {
@apply block text-xs text-center text-muted pt-2;
@apply block text-xs text-center text-(--ui-text-muted) pt-2;
}
&:hover {
.carbon-text {
@apply text-default;
@apply text-(--ui-text);
}
}
}

View File

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

View File

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

View File

@@ -41,7 +41,7 @@ const mobileLinks = computed(() => [
<template>
<UHeader :ui="{ left: 'min-w-0' }" :menu="{ shouldScaleBackground: true }">
<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" />
<LogoPro v-else-if="route.path.startsWith('/pro')" class="w-auto h-6 shrink-0" />
<template v-else>
@@ -63,7 +63,7 @@ const mobileLinks = computed(() => [
trailing-icon="i-lucide-chevron-down"
size="xs"
class="-mb-[6px] font-semibold rounded-full truncate"
:class="[open && 'bg-primary/15 ']"
:class="[open && 'bg-(--ui-primary)/15 ']"
:ui="{
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">
{{ 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>
</template>
</UContentNavigation>

View File

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

View File

@@ -328,16 +328,16 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
<template>
<div class="my-5">
<div class="relative">
<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>
<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">
<UFormField
:label="option.label"
size="sm"
class="inline-flex ring ring-accented rounded-sm"
class="inline-flex ring ring-(--ui-border-accented) rounded-(--ui-radius)"
:ui="{
wrapper: 'bg-elevated/50 rounded-l-sm flex border-r border-accented',
label: 'text-muted px-2 py-1.5',
wrapper: 'bg-(--ui-bg-elevated)/50 rounded-l-(--ui-radius) flex border-r border-(--ui-border-accented)',
label: 'text-(--ui-text-muted) px-2 py-1.5',
container: 'mt-0'
}"
>
@@ -348,7 +348,7 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
value-key="value"
color="neutral"
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']"
:ui="{ itemLeadingChip: 'size-2' }"
@update:model-value="setComponentProp(option.name, $event)"
@@ -370,14 +370,14 @@ const { data: ast } = await useAsyncData(`component-code-${name}-${hash({ props:
:model-value="getComponentProp(option.name)"
color="neutral"
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)"
/>
</UFormField>
</template>
</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 }">
<template v-for="slot in Object.keys(slots || {})" :key="slot" #[slot]>
<slot :name="slot" mdc-unwrap="p">

View File

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

View File

@@ -112,7 +112,7 @@ const metaProps: ComputedRef<ComponentMeta['props']> = computed(() => {
<ProseTd>
<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" />
<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">
<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>
</ProseUl>
</ProseCollapsible>

View File

@@ -36,7 +36,7 @@ const meta = await fetchComponentMeta(name as any)
<ProseTd>
<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>
</ProseTr>
</ProseTbody>

View File

@@ -1,6 +1,6 @@
<template>
<div class="relative overflow-hidden rounded-sm border border-dashed 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">
<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-(--ui-border-inverted)/10" fill="none">
<defs>
<pattern
id="pattern-5c1e4f0e-62d5-498b-8ff0-cf77bb448c8e"

View File

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

View File

@@ -20,7 +20,7 @@ const items: AccordionItem[] = [
<template>
<UAccordion :items="items">
<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.
</p>
</template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ const items = computed<ContextMenuItem[]>(() => [{
<template>
<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
</div>
</UContextMenu>

View File

@@ -28,7 +28,7 @@ const items: ContextMenuItem[][] = [
<template>
<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
</div>
</UContextMenu>

View File

@@ -3,23 +3,19 @@ import type { ContextMenuItem } from '@nuxt/ui'
const loading = ref(true)
const items = [
{
label: 'Refresh the Page',
slot: 'refresh' as const
},
{
label: 'Clear Cookies and Refresh'
},
{
label: 'Clear Cache and Refresh'
}
] satisfies ContextMenuItem[]
const items: ContextMenuItem[] = [{
label: 'Refresh the Page',
slot: 'refresh'
}, {
label: 'Clear Cookies and Refresh'
}, {
label: 'Clear Cache and Refresh'
}]
</script>
<template>
<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
</div>
@@ -28,7 +24,7 @@ const items = [
</template>
<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>
</UContextMenu>
</template>

View File

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

View File

@@ -21,7 +21,7 @@ const items = [
<UButton label="Open" color="neutral" variant="outline" icon="i-lucide-menu" />
<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>
</UDropdownMenu>
</template>

View File

@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
}
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' })
console.log(event.data)
}

View File

@@ -47,7 +47,7 @@ const items = [
]
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' })
console.log(event.data)
}

View File

@@ -15,7 +15,7 @@ const state = reactive({
})
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' })
console.log(event.data)
}

View File

@@ -18,7 +18,7 @@ type NestedSchema = z.output<typeof nestedSchema>
const state = reactive<Partial<Schema & NestedSchema>>({ })
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' })
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" />
</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">
<UInput v-model="state.email" placeholder="john@lennon.com" />
</UFormField>

View File

@@ -34,7 +34,7 @@ function removeItem() {
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' })
console.log(event.data)
}
@@ -51,14 +51,7 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
<UInput v-model="state.customer" placeholder="Wonka Industries" />
</UFormField>
<UForm
v-for="item, count in state.items"
:key="count"
:state="item"
:schema="itemSchema"
attach
class="flex gap-2"
>
<UForm v-for="item, count in state.items" :key="count" :state="item" :schema="itemSchema" class="flex gap-2">
<UFormField :label="!count ? 'Description' : undefined" name="description">
<UInput v-model="item.description" />
</UFormField>

View File

@@ -14,7 +14,7 @@ const validate = (state: any): FormError[] => {
}
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' })
console.log(event.data)
}

View File

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

View File

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

View File

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

View File

@@ -4,8 +4,8 @@ const value = ref('')
<template>
<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">
<span class="inline-flex bg-default px-1">Email address</span>
<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-(--ui-bg) px-1">Email address</span>
</label>
</UInput>
</template>

View File

@@ -77,7 +77,7 @@ const text = computed(() => {
v-for="(req, index) in strength"
:key="index"
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" />

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ const open = ref(false)
<template #content>
<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
</h2>

View File

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

View File

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

View File

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

View File

@@ -100,7 +100,7 @@ const columnFilters = ref([{
<template>
<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
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
class="max-w-sm"

View File

@@ -131,7 +131,7 @@ function getHeader(column: Column<Payment>, label: string) {
'variant': 'ghost',
label,
'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'}`
}))
}

View File

@@ -100,7 +100,7 @@ const columnVisibility = ref({
<template>
<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
:items="table?.tableApi?.getAllColumns().filter(column => column.getCanHide()).map(column => ({
label: upperFirst(column.id),

View File

@@ -265,7 +265,7 @@ function randomize() {
</script>
<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">
<UInput
:model-value="(table?.tableApi?.getColumn('email')?.getFilterValue() as string)"
@@ -313,7 +313,7 @@ function randomize() {
</template>
</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?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div>

View File

@@ -36,7 +36,7 @@ const columns: TableColumn<User>[] = [{
size: 'lg'
}),
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}`)
])
])

View File

@@ -95,7 +95,7 @@ const globalFilter = ref('45')
<template>
<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
v-model="globalFilter"
class="max-w-sm"

View File

@@ -162,7 +162,7 @@ const pagination = ref({
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
:default-page="(table?.tableApi?.getState().pagination.pageIndex || 0) + 1"
:items-per-page="table?.tableApi?.getState().pagination.pageSize"

View File

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

View File

@@ -122,7 +122,7 @@ function onSelect(row: TableRow<Payment>, e?: Event) {
@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?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div>

View File

@@ -113,7 +113,7 @@ const rowSelection = ref({ 1: true })
: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?.getFilteredRowModel().rows.length || 0 }} row(s) selected.
</div>

View File

@@ -97,7 +97,7 @@ function getDropdownActions(user: User): DropdownMenuItem[][] {
<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`" />
<div>
<p class="font-medium text-highlighted">
<p class="font-medium text-(--ui-text-highlighted)">
{{ row.original.name }}
</p>
<p>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ const { width } = useElementSize(el)
<template>
<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 }"
:style="{
'--duration': `${((level + 1) * 8)}s`,
@@ -65,7 +65,7 @@ const { width } = useElementSize(el)
:src="`https://ipx.nuxt.com/s_56x56/gh_avatar/${contributor.username}`"
:srcset="`https://ipx.nuxt.com/s_112x112/gh_avatar/${contributor.username} 2x`"
: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"
>
</NuxtLink>

View File

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

View File

@@ -18,8 +18,8 @@ const slots = defineSlots<{
variant="outline"
:icon="icon"
:label="label"
class="capitalize ring-default rounded-sm text-[11px]"
:class="[selected ? 'bg-elevated' : 'hover:bg-elevated/50']"
class="capitalize ring-(--ui-border) rounded-[calc(var(--ui-radius))] text-[11px]"
:class="[selected ? 'bg-(--ui-bg-elevated)' : 'hover:bg-(--ui-bg-elevated)/50']"
>
<template v-if="chip || !!slots.leading" #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 }
],
link: [
// { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
{ rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }
],
style: [
{ innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 },
@@ -47,8 +47,6 @@ useServerSeoMeta({
twitterCard: 'summary_large_image'
})
useFaviconFromTheme()
const { frameworks, modules } = useSharedData()
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">
{{ 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>
</template>
</UContentNavigation>

View File

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

View File

@@ -130,7 +130,7 @@ const communityLinks = computed(() => [{
</template>
<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 #description>

View File

@@ -82,7 +82,7 @@ onMounted(() => {
:ui="{ title: 'text-balance', container: 'relative' }"
>
<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 #headline>
@@ -97,7 +97,7 @@ onMounted(() => {
/>
</template>
<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 #links>
@@ -121,22 +121,22 @@ onMounted(() => {
<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>
<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 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 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-(--ui-border) py-4 sm:not-group-[[data-stuck]]:py-6 lg:not-group-[[data-stuck]]:py-8 transition-all duration-300">
<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">
<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" />
</span>
{{ category.title }}
</a>
</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 }}
</p>
</UContainer>
@@ -157,11 +157,11 @@ onMounted(() => {
<template #title>
<div class="flex items-center gap-0.5">
<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>
</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
:light="`${component.path.replace('/components/', '/components/light/')}.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.
icon: i-lucide-files
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.
section1:
title: Customize in a few clicks to fit your needs
@@ -181,7 +181,7 @@ pricing:
# discount: $119
billing_period: one-time payment
billing_cycle: plus local taxes
class: bg-elevated/50
class: bg-(--ui-bg-elevated)/50
features:
- '**1 Designer**'
- Nuxt UI & Nuxt UI Pro Components
@@ -203,7 +203,7 @@ pricing:
# discount: $279
billing_period: one-time payment
billing_cycle: plus local taxes
class: bg-elevated/50
class: bg-(--ui-bg-elevated)/50
features:
- '**Up to 20 Designers**'
- Nuxt UI & Nuxt UI Pro Components

View File

@@ -57,7 +57,7 @@ onMounted(async () => {
<template>
<div class="relative">
<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
fill="var(--ui-info)"
stroke="currentColor"
@@ -72,7 +72,7 @@ onMounted(async () => {
</UBadge>
</div>
<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
fill="var(--ui-success)"
stroke="currentColor"
@@ -99,7 +99,7 @@ onMounted(async () => {
<template #description>
<MDC :value="page.hero.description" unwrap="p" cache-key="figma-hero-description" />
</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">
<video
ref="video"
@@ -126,10 +126,10 @@ onMounted(async () => {
</div>
</div>
<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>
</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
v-if="page.cta1"
variant="naked"
@@ -138,7 +138,7 @@ onMounted(async () => {
wrapper: 'grid grid-cols-1 lg:grid-cols-2',
description: 'lg:mt-0' }"
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>
<MDC :value="page.cta1.title" unwrap="p" cache-key="figma-cta-1-title" />
@@ -155,7 +155,7 @@ onMounted(async () => {
:height="item.height"
:src="item.src"
:alt="item.alt"
class="w-full h-auto rounded-lg"
class="w-full h-auto rounded-[calc(var(--ui-radius)*2)]"
loading="lazy"
/>
</template>
@@ -165,7 +165,7 @@ onMounted(async () => {
<NuxtImg
v-if="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"
/>
</UPageSection>
@@ -173,7 +173,7 @@ onMounted(async () => {
<NuxtImg
v-if="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"
/>
</UPageSection>
@@ -192,27 +192,27 @@ onMounted(async () => {
<template #description>
<MDC :value="page.section4.description" unwrap="p" cache-key="figma-section-4-description" />
</template>
<div aria-hidden="true" class="absolute z-[-1] border-x border-default 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">
<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-(--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">
<NuxtImg
v-if="step.image"
v-bind="step.image"
class="rounded-sm"
class="rounded-(--ui-radius)"
loading="lazy"
/>
<div>
<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 }}
</h2>
<p class="text-muted text-sm">
<p class="text-(--ui-text-muted) text-sm">
{{ step.description }}
</p>
</div>
</li>
</ul>
</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
v-if="page.pricing"
:title="page.pricing.title"
@@ -226,7 +226,7 @@ onMounted(async () => {
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">
<UPricingPlan
v-for="(plan, index) in page.pricing.plans"
@@ -246,8 +246,8 @@ onMounted(async () => {
>
<template #features>
<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" />
<MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-accented" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" />
<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-(--ui-text-accented)" :cache-key="`figma-pricing-plan-${index}-feature-${i}`" />
</li>
</template>
<template #button>
@@ -278,7 +278,7 @@ onMounted(async () => {
</UPageMarquee>
</UPageCTA>
<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
multiple
:items="(page.faq.items as any[])"

View File

@@ -48,7 +48,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
}"
>
<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 #description>
{{ page.hero.description }}
@@ -81,14 +81,14 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
pause-on-hover
:overlay="false"
: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]'
}"
>
<ULink
v-for="component of components?.slice(0, 10)"
: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"
>
<UColorModeImage
@@ -98,7 +98,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
width="290"
height="163"
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"
/>
<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
:overlay="false"
: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]'
}"
>
<ULink
v-for="component of components?.slice(10, 20)"
: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"
>
<UColorModeImage
@@ -127,7 +127,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
width="290"
height="163"
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"
/>
<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" />
</div>
<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 }}
<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>
<p class="text-sm text-muted">
<p class="text-sm text-(--ui-text-muted)">
{{ feature.description }}
</p>
</div>
@@ -215,33 +215,33 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
:links="page.community.links"
orientation="horizontal"
:ui="{ features: 'flex items-center gap-4 lg:gap-8' }"
class="border-b border-default"
class="border-b border-(--ui-border)"
>
<template #features>
<li>
<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) }}+
</p>
<p class="text-muted text-sm truncate">monthly downloads</p>
<p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
</NuxtLink>
</li>
<li>
<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) }}+
</p>
<p class="text-muted text-sm truncate">GitHub stars</p>
<p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
</NuxtLink>
</li>
<li>
<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+
</p>
<p class="text-muted text-sm truncate">Contributors</p>
<p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
</NuxtLink>
</li>
</template>
@@ -253,10 +253,10 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
<UPageSection :ui="{ container: 'relative !pb-0 overflow-hidden' }">
<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 #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 #links>
<UButton to="/pro" size="lg">
@@ -269,8 +269,8 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
<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 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 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-(--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' }">
<img
v-for="i in 4"
@@ -280,7 +280,7 @@ useIntersectionObserver(contributorsRef, ([entry]) => {
height="258"
loading="lazy"
: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 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"
loading="lazy"
: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 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"
loading="lazy"
: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>
</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.
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.
freePlan:
title: Free in development

View File

@@ -1,8 +1,8 @@
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.
hero:
title: Build faster with Nuxt UI [Pro]{class="text-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"}.
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-(--ui-text)"} are designed to be the perfect [building blocks for your next idea]{class="text-(--ui-text)"}.
links:
- label: Buy a license
size: xl
@@ -62,7 +62,7 @@ testimonial:
# avatar:
# src: https://github.com/benjamincanac.png
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.
sections:
- title: The freedom to build anything

View File

@@ -1,7 +1,7 @@
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.'
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.'
navigation: false
links:
@@ -16,34 +16,8 @@ links:
variant: outline
trailingIcon: i-lucide-arrow-right
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'
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
thumbnail:
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 errorMessage = ref('')
async function submit(event: FormSubmitEvent<Schema>) {
async function submit(event: FormSubmitEvent<any>) {
activating.value = true
errorMessage.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' }">
<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">
<UForm
:schema="schema"

View File

@@ -35,9 +35,9 @@ useSeoMeta({
<LazyStarsBg />
<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>
<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' }">
<img
v-for="i in 4"
@@ -46,7 +46,7 @@ useSeoMeta({
width="460"
height="258"
: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 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"
height="258"
: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 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"
height="258"
: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>
</div>
@@ -101,10 +101,10 @@ useSeoMeta({
container: 'relative',
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 }">
<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>
</UPageSection>
@@ -116,7 +116,7 @@ useSeoMeta({
wrapper: 'grid grid-cols-1 lg:grid-cols-2',
description: 'lg:mt-0' }"
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>
<MDC :value="page.mainSection.title" tag="span" unwrap="p" cache-key="pro-main-section-title" />
@@ -134,7 +134,7 @@ useSeoMeta({
:reverse="section.reverse"
:features="section.features"
orientation="horizontal"
:class="{ 'border-b border-default': index === page.sections.length - 1 }"
:class="{ 'border-b border-(--ui-border)': index === page.sections.length - 1 }"
:ui="{
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
id="templates"
v-bind="page.templates"
class="overflow-hidden border-x border-default"
class="overflow-hidden border-x border-(--ui-border)"
: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
v-slot="{ item }"
loop
@@ -160,7 +160,7 @@ useSeoMeta({
:ui="{
item: 'basis-1/2',
container: 'py-2',
viewport: 'border-x border-default',
viewport: 'border-x border-(--ui-border)',
arrows: 'hidden 2xl:block'
}"
>
@@ -181,7 +181,7 @@ useSeoMeta({
:light="item.thumbnail.light"
:dark="item.thumbnail.dark"
: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"
/>
</UPageCard>
@@ -199,7 +199,7 @@ useSeoMeta({
<LazyStarsBg />
<video
class="rounded-sm z-10"
class="rounded-[var(--ui-radius)] z-10"
preload="none"
poster="https://res.cloudinary.com/nuxt/video/upload/so_3.3/v1708511800/ui-pro/video-nuxt-ui-pro_kwfbdh.jpg"
:controls="true"

View File

@@ -29,13 +29,13 @@ useSeoMeta({
<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
v-bind="page.pricing.freePlan"
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>
<UPricingPlan
@@ -48,7 +48,7 @@ useSeoMeta({
:billing-period="plan.billing_period"
:billing-cycle="plan.billing_cycle"
: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"
:button="plan.button"
/>
@@ -58,12 +58,12 @@ useSeoMeta({
variant="naked"
:billing-period="page.pricing.figma.billing_period"
: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>
<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" />
<MDC :value="feature" unwrap="p" class="text-sm truncate text-toned" :cache-key="`pro-pricing-figma-feature-${index}`" />
<UIcon name="i-lucide-circle-check" class="size-5 text-(--ui-primary) shrink-0" />
<MDC :value="feature" unwrap="p" class="text-sm truncate text-(--ui-text-toned)" :cache-key="`pro-pricing-figma-feature-${index}`" />
</li>
</template>
</UPricingPlan>
@@ -73,7 +73,7 @@ useSeoMeta({
<UPageSection
id="testimonials"
v-bind="page.testimonials"
class="border-y border-default"
class="border-y border-(--ui-border)"
>
<UPageMarquee pause-on-hover :ui="{ root: '[--duration:40s]' }">
<img
@@ -110,7 +110,7 @@ useSeoMeta({
class="scroll-mt-(--ui-header-height)"
: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
multiple
:items="(page.faq.items as any[])"

View File

@@ -20,7 +20,7 @@ useSeoMeta({
<UPageHero :links="page.links" :ui="{ container: 'relative' }">
<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>
<MDC :value="page.hero.title" unwrap="p" cache-key="pro-templates-hero-title" />
@@ -38,10 +38,10 @@ useSeoMeta({
:links="template.links"
:features="template.features"
orientation="horizontal"
class="lg:border-t border-default"
class="lg:border-t border-(--ui-border)"
:ui="{
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',
links: 'gap-x-3'
}"
@@ -50,12 +50,12 @@ useSeoMeta({
<MDC :value="template.description" unwrap="p" :cache-key="`pro-templates-${index}-description`" />
</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 }">
<UColorModeImage
v-if="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`"
width="656"
height="369"

View File

@@ -29,19 +29,19 @@ defineOgImageComponent('Docs', {
}"
>
<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>
<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">
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 items-start justify-center divide-y divide-x divide-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-(--ui-border)">
<li
v-for="item in page.items"
: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">
<span class="sr-only">Go to {{ item.name }}</span>

View File

@@ -41,7 +41,7 @@ const icons = {
:ui="{ title: 'text-balance', container: 'relative' }"
>
<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>
<LazyStarsBg />
@@ -58,7 +58,7 @@ const icons = {
container: 'gap-y-4 lg:p-8',
leading: 'flex justify-center',
title: 'text-center',
description: 'text-center text-muted'
description: 'text-center text-(--ui-text-muted)'
}"
variant="subtle"
>
@@ -126,7 +126,7 @@ const icons = {
container: 'gap-y-2',
leading: 'flex justify-center',
title: 'text-center',
description: 'text-center text-muted'
description: 'text-center text-(--ui-text-muted)'
}"
>
<template #leading>

View File

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

View File

@@ -76,18 +76,16 @@ export default defineNuxtConfig({
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension for VSCode and add the following settings:
```json [.vscode/settings.json]
{
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
```
::

View File

@@ -136,18 +136,16 @@ app.mount('#app')
It's recommended to install the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension for VSCode and add the following settings:
```json [.vscode/settings.json]
{
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
}
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": "on"
},
"tailwindCSS.classAttributes": ["class", "ui"],
"tailwindCSS.experimental.classRegex": [
["ui:\\s*{([^)]*)\\s*}", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
]
```
::

View File

@@ -180,7 +180,7 @@ In Nuxt UI v2, we had a mix between a design system with `primary`, `gray`, `err
This change introduces several breaking changes that you need to be aware of:
- The `gray` color has been renamed to `neutral`
1. The `gray` color has been renamed to `neutral`
```diff
<template>
@@ -195,15 +195,24 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
```diff
<template>
- <p class="text-gray-500 dark:text-gray-400" />
+ <p class="text-muted" />
+ <p class="text-(--ui-text-muted)" />
- <p class="text-gray-900 dark:text-white" />
+ <p class="text-highlighted" />
+ <p class="text-(--ui-text-highlighted)" />
</template>
```
::
- The `gray`, `black` and `white` in the `color` props have been removed in favor of `neutral`:
2. The `DEFAULT` shade that let you write `text-primary` no longer exists, you can use [color shades](/getting-started/theme#color-shades) instead:
```diff
<template>
- <p class="text-primary">Hello</p>
+ <p class="text-(--ui-primary)">Hello</p>
</template>
```
3. The `gray`, `black` and `white` in the `color` props have been removed in favor of `neutral`:
```diff
- <UButton color="black" />
@@ -216,7 +225,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
+ <UButton color="neutral" variant="outline" />
```
- You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
4. You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
```diff
- <UButton color="red" />
@@ -227,7 +236,7 @@ You can also use the new [design tokens](/getting-started/theme#neutral-palette)
Learn how to extend the design system to add new color aliases.
::
- The color configuration in `app.config.ts` has been moved into a `colors` object:
5. The color configuration in `app.config.ts` has been moved into a `colors` object:
```diff
export default defineAppConfig({
@@ -246,7 +255,7 @@ export default defineAppConfig({
Nuxt UI components are now styled using the [Tailwind Variants API](/getting-started/theme#components-theme), which makes all the overrides you made using the `app.config.ts` and the `ui` prop obsolete.
- Update your [`app.config.ts`](/getting-started/theme#config) to override components with their new theme:
1. Update your [`app.config.ts`](/getting-started/theme#config) to override components with their new theme:
```diff
export default defineAppConfig({
@@ -269,7 +278,7 @@ export default defineAppConfig({
})
```
- Update your [`ui` props](/getting-started/theme#props) to override each component's slots using their new theme:
2. Update your [`ui` props](/getting-started/theme#props) to override each component's slots using their new theme:
```diff
<template>
@@ -342,7 +351,7 @@ Here are the Nuxt UI Pro components that have been renamed or removed:
In addition to the renamed components, there are lots of changes to the components API. Let's detail the most important ones:
- The `links` and `options` props have been renamed to `items` for consistency:
1. The `links` and `options` props have been renamed to `items` for consistency:
```diff
<template>
@@ -358,25 +367,7 @@ In addition to the renamed components, there are lots of changes to the componen
This change affects the following components: `Breadcrumb`, `HorizontalNavigation`, `InputMenu`, `RadioGroup`, `Select`, `SelectMenu`, `VerticalNavigation`.
::
- The `click` field in different components has been removed in favor of the native Vue `onClick` event:
```diff
<script setup lang="ts">
const items = [{
label: 'Edit',
- click: () => {
+ onClick: () => {
console.log('Edit')
}
}]
</script>
```
::note
This change affects the `Toast` component as well as all component that have `items` links like `NavigationMenu`, `DropdownMenu`, `CommandPalette`, etc.
::
- The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](/components/app) component:
2. The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](/components/app) component:
```diff [app.vue]
<template>
@@ -389,7 +380,7 @@ This change affects the `Toast` component as well as all component that have `it
</template>
```
- The `v-model:open` directive and `default-open` prop are now used to control visibility:
3. The `v-model:open` directive and `default-open` prop are now used to control visibility:
```diff
<template>
@@ -402,7 +393,7 @@ This change affects the `Toast` component as well as all component that have `it
This change affects the following components: `ContextMenu`, `Modal` and `Slideover` and enables controlling visibility for `InputMenu`, `Select`, `SelectMenu` and `Tooltip`.
::
- The default slot is now used for the trigger and the content goes inside the `#content` slot (you don't need to use a `v-model:open` directive with this method):
4. The default slot is now used for the trigger and the content goes inside the `#content` slot (you don't need to use a `v-model:open` directive with this method):
```diff
<script setup lang="ts">
@@ -429,7 +420,7 @@ This change affects the following components: `ContextMenu`, `Modal` and `Slideo
This change affects the following components: `Modal`, `Popover`, `Slideover`, `Tooltip`.
::
- A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
5. A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
```diff
<template>
@@ -448,9 +439,10 @@ This change affects the following components: `Modal`, `Popover`, `Slideover`, `
This change affects the following components: `Modal`, `Slideover`.
::
### Changed composables
- The `useToast()` composable `timeout` prop has been renamed to `duration`:
1. The `useToast()` composable `timeout` prop has been renamed to `duration`:
```diff
<script setup lang="ts">
@@ -461,7 +453,7 @@ const toast = useToast()
</script>
```
- The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
2. The `useModal` and `useSlideover` composables have been removed in favor of a more generic `useOverlay` composable:
Some important differences:
- The `useOverlay` composable is now used to create overlay instances

View File

@@ -118,7 +118,17 @@ Learn more about automatic content detection in the detecting classes in source
## Design system
Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design system with pre-configured color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference). This allows for easy customization and quick adaptation of the UI to your brand's aesthetic.
Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design system with pre-configured color aliases and CSS variables. This allows for easy customization and quick adaptation of the UI to your brand's aesthetic.
### Colors
::framework-only
#nuxt
Nuxt UI leverages Nuxt [App Config](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
#vue
Nuxt UI leverages Vite config to provide customizable color aliases based on [Tailwind CSS colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference):
::
| Color | Default | Description |
| --- | --- | --- |
@@ -130,27 +140,10 @@ Nuxt UI extends Tailwind CSS's theming capabilities, providing a flexible design
| `error`{color="error"} | `red` | Used for form error validation states. |
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
These colors are used to style the components but also to generate the `color` props:
::component-code{slug="button"}
---
props:
color: primary
slots:
default: Button
---
::
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-primary"} theme picker in the header above to change `primary` and `neutral` colors.
::
### Configuration
::framework-only
#nuxt
:::div
You can configure these color aliases at runtime in your [`app.config.ts`](https://nuxt.com/docs/guide/directory-structure/app-config#app-config-file) file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
You can configure these color aliases at runtime in your `app.config.ts` file under the `ui.colors` key, allowing for dynamic theme customization without requiring an application rebuild:
```ts [app.config.ts]
export default defineAppConfig({
@@ -229,14 +222,27 @@ export default defineConfig({
::
### Extend colors
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-(--ui-primary)"} theme picker in the header above to change `primary` and `neutral` colors.
::
These colors are used to style the components but also to generate the `color` variants:
::component-code{slug="button"}
---
props:
color: primary
slots:
default: Button
---
::
::framework-only
#nuxt
:::div
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file:
:::tip
You can add you own dynamic color aliases in your `app.config.ts`, you just have to make sure to define them in the [`ui.theme.colors`](/getting-started/installation/nuxt#themecolors) option in your `nuxt.config.ts` file.
```ts [app.config.ts]{4}
```ts [app.config.ts]
export default defineAppConfig({
ui: {
colors: {
@@ -246,19 +252,11 @@ export default defineAppConfig({
})
```
```ts [nuxt.config.ts]{7}
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ui: {
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
}
})
@@ -268,9 +266,9 @@ export default defineNuxtConfig({
#vue
:::div
:::tip
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin:
You can add you own dynamic color aliases in your `vite.config.ts`, you just have to make sure to also define them in the [`theme.colors`](/getting-started/installation/vue#themecolors) option of the `ui` plugin.
::::module-only
@@ -278,7 +276,7 @@ You can add you own dynamic color aliases in your `vite.config.ts`, you just hav
:::::div
```ts [vite.config.ts]{11,18}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
@@ -293,15 +291,7 @@ export default defineConfig({
}
},
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
})
]
@@ -314,7 +304,7 @@ export default defineConfig({
:::::div
```ts [vite.config.ts]{11,18}
```ts [vite.config.ts]
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
@@ -326,18 +316,10 @@ export default defineConfig({
ui: {
colors: {
tertiary: 'indigo'
}
},
},
theme: {
colors: [
'primary',
'secondary',
'tertiary',
'info',
'success',
'warning',
'error'
]
colors: ['primary', 'secondary', 'tertiary', 'info', 'success', 'warning', 'error']
}
})
]
@@ -352,13 +334,13 @@ export default defineConfig({
::
## CSS Variables
### Tokens
Nuxt UI leverages a robust system of CSS variables as design tokens to ensure consistent and flexible component styling. These tokens form the foundation of the theming system, offering smooth support for both light and dark modes.
### Colors
#### Color Shades
Nuxt UI provides a CSS variable for each color alias you define which represent the default shade used in both light and dark modes:
Nuxt UI automatically creates a CSS variable for each color alias you define which represent the default shade used in both light and dark modes:
::code-group
@@ -386,125 +368,12 @@ Nuxt UI provides a CSS variable for each color alias you define which represent
::
These CSS variables are defined in Tailwind CSS's `@theme` so you can use them as classes:
::code-preview
[Primary]{class="text-primary text-sm px-4"}
[Secondary]{class="text-secondary text-sm px-4"}
[Success]{class="text-success text-sm px-4"}
[Info]{class="text-info text-sm px-4"}
[Warning]{class="text-warning text-sm px-4"}
[Error]{class="text-error text-sm px-4"}
#code
```vue
<template>
<span class="text-primary">Primary</span>
<span class="text-secondary">Secondary</span>
<span class="text-success">Success</span>
<span class="text-info">Info</span>
<span class="text-warning">Warning</span>
<span class="text-error">Error</span>
</template>
```
::
::note
This is how the `@theme` is generated for each color alias:
:::code-collapse{class="[&>div]:!my-0"}
```scss
@theme default {
--color-primary: var(--ui-primary);
--color-primary-50: var(--ui-color-primary-50);
--color-primary-100: var(--ui-color-primary-100);
--color-primary-200: var(--ui-color-primary-200);
--color-primary-300: var(--ui-color-primary-300);
--color-primary-400: var(--ui-color-primary-400);
--color-primary-500: var(--ui-color-primary-500);
--color-primary-600: var(--ui-color-primary-600);
--color-primary-700: var(--ui-color-primary-700);
--color-primary-800: var(--ui-color-primary-800);
--color-primary-900: var(--ui-color-primary-900);
--color-primary-950: var(--ui-color-primary-950);
--color-secondary: var(--ui-secondary);
--color-secondary-50: var(--ui-color-secondary-50);
--color-secondary-100: var(--ui-color-secondary-100);
--color-secondary-200: var(--ui-color-secondary-200);
--color-secondary-300: var(--ui-color-secondary-300);
--color-secondary-400: var(--ui-color-secondary-400);
--color-secondary-500: var(--ui-color-secondary-500);
--color-secondary-600: var(--ui-color-secondary-600);
--color-secondary-700: var(--ui-color-secondary-700);
--color-secondary-800: var(--ui-color-secondary-800);
--color-secondary-900: var(--ui-color-secondary-900);
--color-secondary-950: var(--ui-color-secondary-950);
--color-success: var(--ui-success);
--color-success-50: var(--ui-color-success-50);
--color-success-100: var(--ui-color-success-100);
--color-success-200: var(--ui-color-success-200);
--color-success-300: var(--ui-color-success-300);
--color-success-400: var(--ui-color-success-400);
--color-success-500: var(--ui-color-success-500);
--color-success-600: var(--ui-color-success-600);
--color-success-700: var(--ui-color-success-700);
--color-success-800: var(--ui-color-success-800);
--color-success-900: var(--ui-color-success-900);
--color-success-950: var(--ui-color-success-950);
--color-info: var(--ui-info);
--color-info-50: var(--ui-color-info-50);
--color-info-100: var(--ui-color-info-100);
--color-info-200: var(--ui-color-info-200);
--color-info-300: var(--ui-color-info-300);
--color-info-400: var(--ui-color-info-400);
--color-info-500: var(--ui-color-info-500);
--color-info-600: var(--ui-color-info-600);
--color-info-700: var(--ui-color-info-700);
--color-info-800: var(--ui-color-info-800);
--color-info-900: var(--ui-color-info-900);
--color-info-950: var(--ui-color-info-950);
--color-warning: var(--ui-warning);
--color-warning-50: var(--ui-color-warning-50);
--color-warning-100: var(--ui-color-warning-100);
--color-warning-200: var(--ui-color-warning-200);
--color-warning-300: var(--ui-color-warning-300);
--color-warning-400: var(--ui-color-warning-400);
--color-warning-500: var(--ui-color-warning-500);
--color-warning-600: var(--ui-color-warning-600);
--color-warning-700: var(--ui-color-warning-700);
--color-warning-800: var(--ui-color-warning-800);
--color-warning-900: var(--ui-color-warning-900);
--color-warning-950: var(--ui-color-warning-950);
--color-error: var(--ui-error);
--color-error-50: var(--ui-color-error-50);
--color-error-100: var(--ui-color-error-100);
--color-error-200: var(--ui-color-error-200);
--color-error-300: var(--ui-color-error-300);
--color-error-400: var(--ui-color-error-400);
--color-error-500: var(--ui-color-error-500);
--color-error-600: var(--ui-color-error-600);
--color-error-700: var(--ui-color-error-700);
--color-error-800: var(--ui-color-error-800);
--color-error-900: var(--ui-color-error-900);
--color-error-950: var(--ui-color-error-950);
--color-neutral-50: var(--ui-color-neutral-50);
--color-neutral-100: var(--ui-color-neutral-100);
--color-neutral-200: var(--ui-color-neutral-200);
--color-neutral-300: var(--ui-color-neutral-300);
--color-neutral-400: var(--ui-color-neutral-400);
--color-neutral-500: var(--ui-color-neutral-500);
--color-neutral-600: var(--ui-color-neutral-600);
--color-neutral-700: var(--ui-color-neutral-700);
--color-neutral-800: var(--ui-color-neutral-800);
--color-neutral-900: var(--ui-color-neutral-900);
--color-neutral-950: var(--ui-color-neutral-950);
}
```
:::
You can use these variables in classes like `text-(--ui-primary)`, it will automatically adapt to the current color scheme.
::
You can change which shade is used for each color on light and dark mode in your `main.css` file:
::tip
You can change which shade is used for each color on light and dark mode:
::module-only
#ui
@@ -544,6 +413,10 @@ You can change which shade is used for each color on light and dark mode in your
:::
::
::
#### Black as Primary Color
::framework-only
#nuxt
:::p
@@ -594,7 +467,7 @@ You cannot set `primary: 'black'`{lang="ts-type"} in your [`vite.config.ts`](#co
:::
::
### Neutral
#### Neutral Palette
Nuxt UI provides a comprehensive set of design tokens for the `neutral` color palette, ensuring consistent and accessible UI styling across both light and dark modes. These tokens offer fine-grained control over text, background, and border colors:
@@ -602,262 +475,89 @@ Nuxt UI provides a comprehensive set of design tokens for the `neutral` color pa
```css [Light]
:root {
/* Least prominent text */
--ui-text-dimmed: var(--ui-color-neutral-400);
/* Slightly muted text */
--ui-text-muted: var(--ui-color-neutral-500);
/* Moderately prominent text */
--ui-text-toned: var(--ui-color-neutral-600);
/* Default text color */
--ui-text: var(--ui-color-neutral-700);
/* Most prominent text */
--ui-text-highlighted: var(--ui-color-neutral-900);
--ui-text-inverted: var(--color-white);
/* Main background color */
--ui-bg: var(--color-white);
/* Subtle background */
--ui-bg-muted: var(--ui-color-neutral-50);
/* Slightly elevated background */
--ui-bg-elevated: var(--ui-color-neutral-100);
/* More prominent background */
--ui-bg-accented: var(--ui-color-neutral-200);
/* Inverted background color */
--ui-bg-inverted: var(--ui-color-neutral-900);
/* Default border color */
--ui-border: var(--ui-color-neutral-200);
/* Subtle border */
--ui-border-muted: var(--ui-color-neutral-200);
/* More prominent border */
--ui-border-accented: var(--ui-color-neutral-300);
/* Inverted border color */
--ui-border-inverted: var(--ui-color-neutral-900);
}
```
```css [Dark]
.dark {
/* Least prominent text */
--ui-text-dimmed: var(--ui-color-neutral-500);
/* Slightly muted text */
--ui-text-muted: var(--ui-color-neutral-400);
/* Moderately prominent text */
--ui-text-toned: var(--ui-color-neutral-300);
/* Default text color */
--ui-text: var(--ui-color-neutral-200);
/* Most prominent text */
--ui-text-highlighted: var(--color-white);
--ui-text-inverted: var(--ui-color-neutral-900);
/* Main background color */
--ui-bg: var(--ui-color-neutral-900);
/* Subtle background */
--ui-bg-muted: var(--ui-color-neutral-800);
/* Slightly elevated background */
--ui-bg-elevated: var(--ui-color-neutral-800);
/* More prominent background */
--ui-bg-accented: var(--ui-color-neutral-700);
/* Inverted background color */
--ui-bg-inverted: var(--color-white);
/* Default border color */
--ui-border: var(--ui-color-neutral-800);
/* Subtle border */
--ui-border-muted: var(--ui-color-neutral-700);
/* More prominent border */
--ui-border-accented: var(--ui-color-neutral-700);
/* Inverted border color */
--ui-border-inverted: var(--color-white);
}
```
::
These CSS variables are defined in Tailwind CSS's `@theme` so you can use them as classes:
::code-preview
[Dimmed]{class="text-dimmed text-sm px-4 py-1.5 inline-block rounded-md"}
[Muted]{class="text-muted text-sm px-4 py-1.5 inline-block rounded-md"}
[Toned]{class="text-toned text-sm px-4 py-1.5 inline-block rounded-md"}
[Text]{class="text-default text-sm px-4 py-1.5 inline-block rounded-md"}
[Highlighted]{class="text-highlighted text-sm px-4 py-1.5 inline-block rounded-md"}
[Inverted]{class="text-inverted bg-inverted text-sm px-4 py-1.5 inline-block rounded-md"}
#code
```vue
<template>
<span class="text-dimmed">Dimmed</span>
<span class="text-muted">Muted</span>
<span class="text-toned">Toned</span>
<span class="text-default">Text</span>
<span class="text-highlighted">Highlighted</span>
<span class="text-inverted bg-inverted">Inverted</span>
</template>
```
::
::code-preview
[Default]{class="bg-default text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Muted]{class="bg-muted text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Elevated]{class="bg-elevated text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Accented]{class="bg-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Inverted]{class="bg-inverted text-inverted text-sm px-4 py-1.5 inline-block rounded-md"}
#code
```vue
<template>
<div class="bg-default">Default</div>
<div class="bg-muted">Muted</div>
<div class="bg-elevated">Elevated</div>
<div class="bg-accented">Accented</div>
<div class="bg-inverted text-inverted">Inverted</div>
</template>
```
::
::code-preview
[Default]{class="border-2 border-default text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Muted]{class="border-2 border-muted text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Accented]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[Inverted]{class="border-2 border-inverted text-sm px-4 py-1.5 inline-block rounded-md"}
#code
```vue
<template>
<div class="border border-default">Default</div>
<div class="border border-muted">Muted</div>
<div class="border border-accented">Accented</div>
<div class="border border-inverted">Inverted</div>
</template>
```
::
::note
This is how the `@theme` is generated for each design token:
:::code-collapse{class="[&>div]:!my-0"}
```scss
@theme default {
--text-color-dimmed: var(--ui-text-dimmed);
--text-color-muted: var(--ui-text-muted);
--text-color-toned: var(--ui-text-toned);
--text-color-default: var(--ui-text);
--text-color-highlighted: var(--ui-text-highlighted);
--text-color-inverted: var(--ui-text-inverted);
--background-color-default: var(--ui-bg);
--background-color-muted: var(--ui-bg-muted);
--background-color-elevated: var(--ui-bg-elevated);
--background-color-accented: var(--ui-bg-accented);
--background-color-inverted: var(--ui-bg-inverted);
--background-color-border: var(--ui-border);
--border-color-default: var(--ui-border);
--border-color-muted: var(--ui-border-muted);
--border-color-accented: var(--ui-border-accented);
--border-color-inverted: var(--ui-border-inverted);
--ring-color-default: var(--ui-border);
--ring-color-muted: var(--ui-border-muted);
--ring-color-accented: var(--ui-border-accented);
--ring-color-inverted: var(--ui-border-inverted);
--ring-color-bg: var(--ui-bg);
--divide-color-default: var(--ui-border);
--divide-color-muted: var(--ui-border-muted);
--divide-color-accented: var(--ui-border-accented);
--divide-color-inverted: var(--ui-border-inverted);
--outline-color-default: var(--ui-border);
--outline-color-inverted: var(--ui-border-inverted);
--stroke-color-default: var(--ui-border);
--stroke-color-inverted: var(--ui-border-inverted);
--fill-color-default: var(--ui-border);
--fill-color-inverted: var(--ui-border-inverted);
}
```
:::
::
You can customize these CSS variables to tailor the appearance of your application in your `main.css` file:
::module-only
#ui
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
```
:::
#ui-pro
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
```
:::
::
::note
Nuxt UI applies a text and background color on the `<body>` element of your app:
Nuxt UI automatically applies a text and background color on the `<body>` element of your app:
```css
body {
@apply antialiased text-default bg-default scheme-light dark:scheme-dark;
@apply antialiased text-(--ui-text) bg-(--ui-bg);
}
```
::
### Radius
Nuxt UI provides a centralized border radius system through the `--ui-radius` CSS variable.
```css
:root {
--ui-radius: 0.25rem;
}
```
This CSS variable replaces Tailwind CSS's default `rounded-*` utilities so you can use the same class names:
::code-preview
[xs]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-xs mr-2"}
[sm]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-sm mr-2"}
[md]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-md mr-2"}
[lg]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-lg mr-2"}
[xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-xl mr-2"}
[2xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-2xl mr-2"}
[3xl]{class="border-2 border-accented text-sm px-4 py-1.5 inline-block rounded-3xl mr-2"}
#code
```vue
<template>
<div class="rounded-xs">xs</div>
<div class="rounded-sm">sm</div>
<div class="rounded-md">md</div>
<div class="rounded-lg">lg</div>
<div class="rounded-xl">xl</div>
<div class="rounded-2xl">2xl</div>
<div class="rounded-3xl">3xl</div>
</template>
```
::
::note
This is how the `@theme` is generated for each radius value:
:::code-collapse{class="[&>div]:!my-0"}
```scss
@theme default {
--radius-xs: calc(var(--ui-radius) * 0.5); /* 0.125rem */
--radius-sm: var(--ui-radius); /* 0.25rem */
--radius-md: calc(var(--ui-radius) * 1.5); /* 0.375rem */
--radius-lg: calc(var(--ui-radius) * 2); /* 0.5rem */
--radius-xl: calc(var(--ui-radius) * 3); /* 0.75rem */
--radius-2xl: calc(var(--ui-radius) * 4); /* 1rem */
--radius-3xl: calc(var(--ui-radius) * 6); /* 1.5rem */
}
```
:::
::
You can customize the base radius value in your `main.css` file:
::tip
You can customize these CSS variables to tailor the appearance of your application:
::module-only
#ui
@@ -868,7 +568,13 @@ You can customize the base radius value in your `main.css` file:
@import "@nuxt/ui";
:root {
--ui-radius: 0.5rem;
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
```
@@ -882,20 +588,73 @@ You can customize the base radius value in your `main.css` file:
@import "@nuxt/ui-pro";
:root {
--ui-radius: 0.5rem;
--ui-bg: var(--ui-color-neutral-50);
--ui-text: var(--ui-color-neutral-900);
}
.dark {
--ui-bg: var(--ui-color-neutral-950);
--ui-border: var(--ui-color-neutral-900);
}
```
:::
::
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-primary"} theme picker in the header above to change the base radius value.
::
### Container
#### Border Radius
Nuxt UI provides a `--ui-container` CSS variable that controls the maximum width of the [Container](/components/container) component.
Nuxt UI uses a global `--ui-radius` CSS variable for consistent border rounding. Components use variations of this base value, like `rounded-[calc(var(--ui-radius)*2)]`, to create different levels of roundness throughout the UI:
```css
:root {
--ui-radius: var(--radius-sm);
}
```
::note
Try the :prose-icon{name="i-lucide-swatch-book" class="text-(--ui-primary)"} theme picker in the header above to change the base radius value.
::
::tip
You can customize the default radius value using the default Tailwind CSS variables or a value of your choice:
::module-only
#ui
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui";
:root {
--ui-radius: var(--radius-sm);
}
```
:::
#ui-pro
:::div{class="*:!mb-0 *:!mt-2.5"}
```css [app/assets/css/main.css]
@import "tailwindcss";
@import "@nuxt/ui-pro";
:root {
--ui-radius: var(--radius-sm);
}
```
:::
::
::
#### Container
Nuxt UI uses a global `--ui-container` CSS variable to define the width of the container:
```css
:root {
@@ -903,7 +662,8 @@ Nuxt UI provides a `--ui-container` CSS variable that controls the maximum width
}
```
You can customize this value in your `main.css` file to adjust container widths consistently throughout your application:
::tip
You can customize the default container width using the default Tailwind CSS variables or a value of your choice:
::module-only
#ui
@@ -943,6 +703,8 @@ You can customize this value in your `main.css` file to adjust container widths
:::
::
::
## Components theme
Nuxt UI components are styled using the [Tailwind Variants](https://www.tailwind-variants.org/) API, which provides a powerful way to create variants and manage component styles. Let's explore the key features of this API:
@@ -956,7 +718,7 @@ Components in Nuxt UI can have multiple `slots`, each representing a distinct HT
```ts [src/theme/card.ts]
export default {
slots: {
root: 'bg-default ring ring-default divide-y divide-default rounded-lg',
root: 'bg-(--ui-bg) ring ring-(--ui-border) divide-y divide-(--ui-border) rounded-[calc(var(--ui-radius)*2)]',
header: 'p-4 sm:px-6',
body: 'p-4 sm:p-6',
footer: 'p-4 sm:px-6'
@@ -1015,7 +777,7 @@ Nuxt UI components use `variants` to change the `slots` styles based on props. H
```ts [src/theme/avatar.ts]
export default {
slots: {
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-elevated',
root: 'inline-flex items-center justify-center shrink-0 select-none overflow-hidden rounded-full align-middle bg-(--ui-bg-elevated)',
image: 'h-full w-full rounded-[inherit] object-cover'
},
variants: {

View File

@@ -26,8 +26,8 @@ const isDark = computed({
get() {
return colorMode.value === 'dark'
},
set(_isDark) {
colorMode.preference = _isDark ? 'dark' : 'light'
set() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>

View File

@@ -330,7 +330,7 @@ props:
color: neutral
variant: outline
ui:
leadingIcon: 'text-primary'
leadingIcon: 'text-(--ui-primary)'
slots:
default: |

View File

@@ -32,7 +32,6 @@ Use the `items` prop as an array of objects with the following properties:
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- `onSelect?(e: Event): void`{lang="ts-type"}
- [`onUpdateChecked?(checked: boolean): void`{lang="ts-type"}](#with-checkbox-items)
- `children?: ContextMenuItem[] | ContextMenuItem[][]`{lang="ts-type"}
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
@@ -99,12 +98,12 @@ props:
slots:
default: |
<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
</div>
---
:div{class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"}[Right click here]
: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]
::
::note
@@ -143,12 +142,12 @@ props:
slots:
default: |
<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
</div>
---
:div{class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"}[Right click here]
: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]
::
### Disabled
@@ -179,12 +178,12 @@ props:
slots:
default: |
<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
</div>
---
:div{class="flex items-center justify-center rounded-md border border-dashed border-accented text-sm aspect-video w-72"}[Right click here]
: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]
::
## Examples

View File

@@ -248,7 +248,7 @@ Make sure to add the `data-vaul-drawer-wrapper` directive to a parent element of
```vue [app.vue]
<template>
<UApp>
<div class="bg-default" data-vaul-drawer-wrapper>
<div class="bg-(--ui-bg)" data-vaul-drawer-wrapper>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
@@ -262,7 +262,7 @@ export default defineNuxtConfig({
app: {
rootAttrs: {
'data-vaul-drawer-wrapper': '',
'class': 'bg-default'
'class': 'bg-(--ui-bg)'
}
}
})

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