Compare commits
52 Commits
v3.0.0-bet
...
pr/3001
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
723065afa7 | ||
|
|
e67305e412 | ||
|
|
33ed3935a3 | ||
|
|
af2f8987a3 | ||
|
|
f6250f979a | ||
|
|
87ecee6308 | ||
|
|
476408f6e5 | ||
|
|
e016fdea18 | ||
|
|
d9a6218524 | ||
|
|
2ae338e8a5 | ||
|
|
a6f93ca1ce | ||
|
|
63266d366d | ||
|
|
5671618c9a | ||
|
|
60497174a3 | ||
|
|
704abf7917 | ||
|
|
a1de006055 | ||
|
|
4f51d19e2b | ||
|
|
196ffbc989 | ||
|
|
a6f58cba14 | ||
|
|
977fed0122 | ||
|
|
bd2d4848d2 | ||
|
|
e3ce1f7a41 | ||
|
|
08f092fd15 | ||
|
|
3f7df7be9b | ||
|
|
99e531d8df | ||
|
|
a47c5ff466 | ||
|
|
60b7e2d69e | ||
|
|
01fa230eae | ||
|
|
e823022b19 | ||
|
|
25e503bc83 | ||
|
|
629c54261a | ||
|
|
fb4e05c65f | ||
|
|
ccbd89c908 | ||
|
|
145fce1b30 | ||
|
|
52a92c658f | ||
|
|
557dca9a22 | ||
|
|
bd23cf3e9d | ||
|
|
4514171a3f | ||
|
|
d76f9c4fe4 | ||
|
|
62f71f3fbc | ||
|
|
6daa8f1e31 | ||
|
|
5783d0d931 | ||
|
|
58568c642d | ||
|
|
02329c78c2 | ||
|
|
9f241de1b5 | ||
|
|
7f420d9bec | ||
|
|
80b701d270 | ||
|
|
21175c8c59 | ||
|
|
dd708da235 | ||
|
|
f1758ef9d0 | ||
|
|
0d1fccc3c5 | ||
|
|
6446130b5d |
@@ -2,6 +2,7 @@
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@source "../../../content";
|
||||
@source "../../../node_modules/.c12";
|
||||
|
||||
@theme static {
|
||||
--container-8xl: 90rem;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
|
||||
const links = [{
|
||||
label: 'Figma',
|
||||
to: '/figma'
|
||||
@@ -16,7 +18,7 @@ const links = [{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<USeparator icon="i-simple-icons-nuxtdotjs" class="h-px" />
|
||||
<USeparator :icon="route.path === '/' ? undefined : 'i-simple-icons-nuxtdotjs'" class="h-px" />
|
||||
|
||||
<UFooter>
|
||||
<template #left>
|
||||
|
||||
@@ -30,8 +30,12 @@ const mobileLinks = computed(() => props.links.map(link => ({ ...link, defaultOp
|
||||
<UHeader :ui="{ left: 'min-w-0' }" :menu="{ shouldScaleBackground: true }">
|
||||
<template #left>
|
||||
<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">
|
||||
<LogoPro class="w-auto h-6 shrink-0 ui-pro-only" />
|
||||
<Logo class="w-auto h-6 shrink-0 ui-only" />
|
||||
<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>
|
||||
<LogoPro class="w-auto h-6 shrink-0 ui-pro-only" />
|
||||
<Logo class="w-auto h-6 shrink-0 ui-only" />
|
||||
</template>
|
||||
</NuxtLink>
|
||||
|
||||
<UDropdownMenu
|
||||
|
||||
91
docs/app/components/SkyBg.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
interface Star {
|
||||
x: number
|
||||
y: number
|
||||
size: number
|
||||
twinkleDelay: number
|
||||
id: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
starCount?: number
|
||||
color?: string
|
||||
size?: { min: number, max: number }
|
||||
speed?: 'slow' | 'normal' | 'fast'
|
||||
}>(), {
|
||||
starCount: 50,
|
||||
color: 'var(--ui-primary)',
|
||||
size: () => ({
|
||||
min: 1,
|
||||
max: 3
|
||||
}),
|
||||
speed: 'normal'
|
||||
})
|
||||
|
||||
// Generate random stars
|
||||
const generateStars = (count: number): Star[] => {
|
||||
return Array.from({ length: count }, () => {
|
||||
const x = Math.floor(Math.random() * 100)
|
||||
const y = Math.floor(Math.random() * 100)
|
||||
const size = Math.random() * (props.size.max - props.size.min) + props.size.min
|
||||
const twinkleDelay = Math.random() * 5
|
||||
|
||||
return { x, y, size, twinkleDelay, id: Math.random().toString(36).substring(2, 9) }
|
||||
})
|
||||
}
|
||||
|
||||
// Generate all stars
|
||||
const stars = ref<Star[]>(generateStars(props.starCount))
|
||||
|
||||
// Compute twinkle animation duration based on speed
|
||||
const twinkleDuration = computed(() => {
|
||||
const speedMap: Record<string, string> = {
|
||||
slow: '4s',
|
||||
normal: '2s',
|
||||
fast: '1s'
|
||||
}
|
||||
return speedMap[props.speed]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute pointer-events-none z-[-1] inset-y-0 left-4 right-4 lg:right-[50%] overflow-hidden">
|
||||
<ClientOnly>
|
||||
<div
|
||||
v-for="star in stars"
|
||||
:key="star.id"
|
||||
class="star absolute"
|
||||
:style="{
|
||||
'left': `${star.x}%`,
|
||||
'top': `${star.y}%`,
|
||||
'transform': 'translate(-50%, -50%)',
|
||||
'--star-size': `${star.size}px`,
|
||||
'--star-color': color,
|
||||
'--twinkle-delay': `${star.twinkleDelay}s`,
|
||||
'--twinkle-duration': twinkleDuration
|
||||
}"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.star {
|
||||
width: var(--star-size);
|
||||
height: var(--star-size);
|
||||
background-color: var(--star-color);
|
||||
border-radius: 50%;
|
||||
animation: twinkle var(--twinkle-duration) ease-in-out infinite;
|
||||
animation-delay: var(--twinkle-delay);
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0%, 100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { upperFirst, camelCase } from 'scule'
|
||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||
import type { ComponentMeta } from 'vue-component-meta'
|
||||
import * as theme from '#build/ui'
|
||||
import * as themePro from '#build/ui-pro'
|
||||
@@ -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-(--ui-text-toned) mt-1" />
|
||||
<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" />
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { kebabCase } from 'scule'
|
||||
import type { PropertyMeta } from 'vue-component-meta'
|
||||
|
||||
const props = defineProps<{
|
||||
prop: PropertyMeta
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const links = computed(() => props.prop.tags?.filter((tag: any) => tag.name === 'link'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProseUl v-if="links?.length">
|
||||
<ProseLi v-for="link in links" :key="link.name">
|
||||
<MDC :value="link.text ?? ''" class="my-1" />
|
||||
<ProseLi v-for="(link, index) in links" :key="index">
|
||||
<MDC :value="link.text ?? ''" class="my-1" :cache-key="`${kebabCase(route.path)}-${prop.name}-link-${index}`" />
|
||||
</ProseLi>
|
||||
</ProseUl>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { kebabCase } from 'scule'
|
||||
import type { PropertyMeta } from 'vue-component-meta'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -6,6 +7,8 @@ const props = defineProps<{
|
||||
ignore?: string[]
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
function getSchemaProps(schema: PropertyMeta['schema']): any {
|
||||
if (!schema || typeof schema === 'string' || !schema.schema) {
|
||||
return []
|
||||
@@ -40,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-(--ui-text-muted) my-1" />
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { upperFirst, camelCase } from 'scule'
|
||||
import { upperFirst, camelCase, kebabCase } from 'scule'
|
||||
|
||||
const props = defineProps<{
|
||||
prose?: boolean
|
||||
@@ -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-(--ui-text-toned) mt-1" />
|
||||
<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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { murmurHash } from 'ohash'
|
||||
import { hash } from 'ohash'
|
||||
|
||||
const props = defineProps<{
|
||||
type: string
|
||||
@@ -23,7 +23,7 @@ const type = computed(() => {
|
||||
return type
|
||||
})
|
||||
|
||||
const { data: ast } = await useAsyncData(`hightlight-inline-code-${murmurHash(type.value)}`, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
const { data: ast } = await useAsyncData(`hightlight-inline-code-${hash(type.value).slice(0, 10)}`, () => parseMarkdown(`\`${type.value}\`{lang="ts-type"}`))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -9,21 +9,22 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
function getEmojiFlag(locale: string): string {
|
||||
const languageToCountry: Record<string, string> = {
|
||||
ar: 'sa',
|
||||
bn: 'bd',
|
||||
cs: 'cz',
|
||||
da: 'dk',
|
||||
el: 'gr',
|
||||
et: 'ee',
|
||||
en: 'gb',
|
||||
hi: 'in',
|
||||
ja: 'jp',
|
||||
km: 'kh',
|
||||
ko: 'kr',
|
||||
nb: 'no',
|
||||
sv: 'se',
|
||||
uk: 'ua',
|
||||
vi: 'vn'
|
||||
ar: 'sa', // Arabic -> Saudi Arabia
|
||||
bn: 'bd', // Bengali -> Bangladesh
|
||||
cs: 'cz', // Czech -> Czech Republic (note: modern country code is actually 'cz')
|
||||
da: 'dk', // Danish -> Denmark
|
||||
el: 'gr', // Greek -> Greece
|
||||
et: 'ee', // Estonian -> Estonia
|
||||
en: 'gb', // English -> Great Britain
|
||||
he: 'il', // Hebrew -> Israel
|
||||
hi: 'in', // Hindi -> India
|
||||
ja: 'jp', // Japanese -> Japan
|
||||
km: 'kh', // Khmer -> Cambodia
|
||||
ko: 'kr', // Korean -> South Korea
|
||||
nb: 'no', // Norwegian Bokmål -> Norway
|
||||
sv: 'se', // Swedish -> Sweden
|
||||
uk: 'ua', // Ukrainian -> Ukraine
|
||||
vi: 'vn' // Vietnamese -> Vietnam
|
||||
}
|
||||
|
||||
const baseLanguage = locale.split('-')[0]?.toLowerCase() || locale
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { CalendarDate, HebrewCalendar } from '@internationalized/date'
|
||||
|
||||
const hebrewDate = shallowRef(new CalendarDate(new HebrewCalendar(), 5781, 1, 1))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UCalendar v-model="hebrewDate" />
|
||||
</template>
|
||||
@@ -2,6 +2,7 @@
|
||||
const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
|
||||
@@ -11,7 +11,7 @@ const items = [
|
||||
level: 2
|
||||
},
|
||||
{
|
||||
id: '/getting-started#reka-ui-radix-vue',
|
||||
id: '/getting-started#reka-ui',
|
||||
label: 'Reka UI',
|
||||
level: 3
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
params: { q: searchTerm },
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users-email',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
|
||||
@@ -3,6 +3,7 @@ const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const searchTerm = ref('')
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'command-palette-users',
|
||||
params: { q: searchTerm },
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users-email',
|
||||
transform: (data: { id: number, name: string, email: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
|
||||
@@ -3,6 +3,7 @@ const searchTerm = ref('')
|
||||
const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'typicode-users',
|
||||
transform: (data: { id: number, name: string }[]) => {
|
||||
return data?.map(user => ({
|
||||
label: user.name,
|
||||
|
||||
@@ -13,6 +13,7 @@ type User = {
|
||||
}
|
||||
|
||||
const { data, status } = await useFetch<User[]>('https://jsonplaceholder.typicode.com/users', {
|
||||
key: 'table-users',
|
||||
transform: (data) => {
|
||||
return data?.map(user => ({
|
||||
...user,
|
||||
|
||||
139
docs/app/components/home/HomeContributors.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<script setup lang="ts">
|
||||
const props = withDefaults(defineProps<{
|
||||
contributors?: {
|
||||
username: string
|
||||
}[]
|
||||
level?: number
|
||||
max?: number
|
||||
paused?: boolean
|
||||
}>(), {
|
||||
level: 0,
|
||||
max: 4,
|
||||
paused: false
|
||||
})
|
||||
|
||||
const contributors = computed(() => props.contributors?.slice(0, 5) ?? [])
|
||||
|
||||
const el = ref(null)
|
||||
const { width } = useElementSize(el)
|
||||
</script>
|
||||
|
||||
<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-(--ui-bg) before:rounded-full z-(--level)"
|
||||
:class="{ 'animation-paused': paused }"
|
||||
:style="{
|
||||
'--duration': `${((level + 1) * 8)}s`,
|
||||
'--level': level + 1
|
||||
}"
|
||||
>
|
||||
<HomeContributors
|
||||
v-if="(level + 1) < max"
|
||||
:max="max"
|
||||
:level="level + 1"
|
||||
:contributors="props.contributors?.slice(5) ?? []"
|
||||
:paused="paused"
|
||||
/>
|
||||
|
||||
<div
|
||||
ref="el"
|
||||
class="avatars absolute inset-0 grid"
|
||||
:style="{
|
||||
'--total': contributors.length,
|
||||
'--offset': `${width / 2}px`
|
||||
}"
|
||||
>
|
||||
<UTooltip
|
||||
v-for="(contributor, index) in contributors"
|
||||
:key="contributor.username"
|
||||
:text="contributor.username"
|
||||
:delay-duration="0"
|
||||
>
|
||||
<NuxtLink
|
||||
:to="`https://github.com/${contributor.username}`"
|
||||
:aria-label="contributor.username"
|
||||
target="_blank"
|
||||
class="avatar flex absolute top-1/2 left-1/2"
|
||||
tabindex="-1"
|
||||
:style="{
|
||||
'--index': index + 1
|
||||
}"
|
||||
>
|
||||
<img
|
||||
width="56"
|
||||
height="56"
|
||||
: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-(--ui-border) lg:hover:ring-(--ui-border-inverted) transition rounded-full size-7"
|
||||
loading="lazy"
|
||||
>
|
||||
</NuxtLink>
|
||||
</UTooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.circle:after {
|
||||
--start: 0deg;
|
||||
--end: 360deg;
|
||||
--border-color: var(--ui-border);
|
||||
--highlight-color: var(--ui-color-neutral-400);
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: -1px;
|
||||
opacity: 1;
|
||||
border-radius: 9999px;
|
||||
z-index: -1;
|
||||
background: var(--border-color);
|
||||
|
||||
@supports (background: paint(houdini)) {
|
||||
background: linear-gradient(var(--angle), var(--border-color), var(--border-color), var(--border-color), var(--border-color), var(--highlight-color));
|
||||
animation: var(--duration) rotate linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.dark .circle:after {
|
||||
--highlight-color: var(--color-white);
|
||||
}
|
||||
|
||||
.animation-paused.circle:after,
|
||||
.animation-paused .avatars {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.avatars {
|
||||
--start: calc(var(--level) * 36deg);
|
||||
--end: calc(360deg + (var(--level) * 36deg));
|
||||
transform: rotate(var(--angle));
|
||||
animation: calc(var(--duration) + 60s) rotate linear infinite;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
--deg: calc(var(--index) * (360deg / var(--total)));
|
||||
--transformX: calc(cos(var(--deg)) * var(--offset));
|
||||
--transformY: calc(sin(var(--deg)) * var(--offset));
|
||||
transform: translate(calc(-50% + var(--transformX)), calc(-50% + var(--transformY))) rotate(calc(360deg - var(--angle)));
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
--angle: var(--start);
|
||||
}
|
||||
to {
|
||||
--angle: var(--end);
|
||||
}
|
||||
}
|
||||
|
||||
@property --angle {
|
||||
syntax: '<angle>';
|
||||
initial-value: 0deg;
|
||||
inherits: true;
|
||||
}
|
||||
</style>
|
||||
@@ -37,6 +37,10 @@ export const useContentNavigation = (navigation: Ref<ContentNavigationItem[] | u
|
||||
return {
|
||||
...item,
|
||||
children: item.children?.filter((child: any) => {
|
||||
if (child.path.startsWith('/components')) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (child.framework && child.framework !== framework.value) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -12,23 +12,17 @@ export function useLinks() {
|
||||
to: '/components',
|
||||
active: route.path === '/components',
|
||||
children: [{
|
||||
label: 'Layout',
|
||||
to: '/components#layout',
|
||||
description: 'Container, grid, divider and responsive layout.',
|
||||
icon: 'i-lucide-layout',
|
||||
active: route.fullPath === '/components#layout'
|
||||
label: 'Element',
|
||||
to: '/components#element',
|
||||
description: 'Button, badge, icon, alert, and small UI elements.',
|
||||
icon: 'i-lucide-mouse-pointer',
|
||||
active: route.fullPath === '/components#element'
|
||||
}, {
|
||||
label: 'Form',
|
||||
to: '/components#form',
|
||||
description: 'Input, select, checkbox, radio and form validation.',
|
||||
icon: 'i-lucide-text-cursor-input',
|
||||
active: route.fullPath === '/components#form'
|
||||
}, {
|
||||
label: 'Element',
|
||||
to: '/components#element',
|
||||
description: 'Button, badge, icon, alert, and small UI elements.',
|
||||
icon: 'i-lucide-mouse-pointer',
|
||||
active: route.fullPath === '/components#element'
|
||||
}, {
|
||||
label: 'Data',
|
||||
to: '/components#data',
|
||||
@@ -47,6 +41,12 @@ export function useLinks() {
|
||||
description: 'Modal, tooltip, dialog and popover.',
|
||||
icon: 'i-lucide-layers',
|
||||
active: route.fullPath === '/components#overlay'
|
||||
}, {
|
||||
label: 'Layout',
|
||||
to: '/components#layout',
|
||||
description: 'Container, grid, divider and responsive layout.',
|
||||
icon: 'i-lucide-layout',
|
||||
active: route.fullPath === '/components#layout'
|
||||
}]
|
||||
}, {
|
||||
label: 'Pro',
|
||||
@@ -98,7 +98,7 @@ export function useLinks() {
|
||||
label: 'Raycast Extension',
|
||||
description: 'Access Nuxt UI components without leaving your editor.',
|
||||
icon: 'i-simple-icons-raycast',
|
||||
to: 'https://github.com/HugoRCD/nuxt-ui-raycast-extension',
|
||||
to: 'https://www.raycast.com/HugoRCD/nuxt-ui',
|
||||
target: '_blank'
|
||||
}, {
|
||||
label: 'Figma to Code',
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
182
docs/app/pages/.index.yml
Normal file
@@ -0,0 +1,182 @@
|
||||
title: The Intuitive Vue UI Library
|
||||
description: Create beautiful, responsive & accessible web apps quickly with Vue or Nuxt. Nuxt UI is an open-source UI library of 50+ customizable components built with Tailwind CSS and Reka UI.
|
||||
hero:
|
||||
title: The Intuitive Vue UI Library
|
||||
description: Create beautiful, responsive & accessible web apps quickly with Vue or Nuxt. Nuxt UI is an open-source UI library of 50+ customizable components built with Tailwind CSS and Reka UI.
|
||||
links:
|
||||
- label: Get started
|
||||
to: /getting-started/installation/nuxt
|
||||
class: 'ui-only nuxt-only'
|
||||
- label: Get started
|
||||
to: /getting-started/installation/vue
|
||||
class: 'ui-only vue-only'
|
||||
- label: Get started
|
||||
to: /getting-started/installation/pro/nuxt
|
||||
class: 'ui-pro-only nuxt-only'
|
||||
- label: Get started
|
||||
to: /getting-started/installation/pro/vue
|
||||
class: 'ui-pro-only vue-only'
|
||||
- label: Explore components
|
||||
to: /components
|
||||
variant: outline
|
||||
color: neutral
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
features:
|
||||
- icon: i-logos-tailwindcss-icon
|
||||
title: Styled with Tailwind CSS v4
|
||||
description: Beautifully styled by default, overwrite any style you want.
|
||||
- icon: i-custom-reka-ui
|
||||
title: Accessible with Reka UI
|
||||
description: Robust accessibility out of the box.
|
||||
- icon: i-logos-typescript-icon
|
||||
title: Type-safe with TypeScript
|
||||
description: Auto-complete and type safety for all components.
|
||||
features:
|
||||
- title: Build for the modern web
|
||||
description: Powered by Tailwind CSS v4 and Reka UI for top performance and accessibility.
|
||||
icon: i-lucide-rocket
|
||||
to: /getting-started
|
||||
- title: Flexible design system
|
||||
description: Beautiful by default and easily customizable with design tokens to your brand.
|
||||
icon: i-lucide-swatch-book
|
||||
to: /getting-started/theme#design-system
|
||||
- title: Internationalization (i18n)
|
||||
description: Nuxt UI is translated into 30+ languages, works well with i18n and multi-directional support (LTR/RTL).
|
||||
icon: i-lucide-globe
|
||||
to: /getting-started/i18n/nuxt
|
||||
- title: Easy font customization
|
||||
description: Performance-optimized fonts with first-class @nuxt/fonts integration.
|
||||
icon: i-lucide-a-large-small
|
||||
to: /getting-started/fonts
|
||||
- title: Large icons sets
|
||||
description: Access to over 200,000 customizable icons from Iconify, seamlessly integrated with Iconify.
|
||||
icon: i-lucide-smile
|
||||
to: /getting-started/icons
|
||||
- title: Light & Dark
|
||||
description: Dark mode-ready components, seamless integration with @nuxtjs/color-mode.
|
||||
icon: i-lucide-sun-moon
|
||||
to: /getting-started/color-mode/nuxt
|
||||
design_system:
|
||||
title: Flexible design system
|
||||
description: Build your next project faster with Nuxt UI's comprehensive design system. Featuring semantic color aliases, comprehensive design tokens, and automatic light/dark mode support for accessible components out of the box.
|
||||
links:
|
||||
- label: Customize design system
|
||||
to: /getting-started/theme#design-system
|
||||
variant: outline
|
||||
color: neutral
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
features:
|
||||
- title: Color aliases via AppConfig
|
||||
description: Configure 7 semantic color aliases (primary, secondary, success, info, warning, error, neutral) at runtime through AppConfig without rebuilding your application
|
||||
icon: i-lucide-palette
|
||||
- title: Comprehensive design tokens
|
||||
description: Extensive set of neutral palette tokens for text, backgrounds, and borders with automatic light/dark mode support via CSS variables like --ui-text, --ui-bg, --ui-border
|
||||
icon: i-lucide-component
|
||||
- title: Global style variables
|
||||
description: Customize global styling with --ui-radius for consistent border rounding and --ui-container for layout widths across your entire application
|
||||
icon: i-lucide-ruler
|
||||
code: |
|
||||
::code-group
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
colors: {
|
||||
primary: 'indigo',
|
||||
secondary: 'pink',
|
||||
success: 'green',
|
||||
info: 'blue',
|
||||
warning: 'orange',
|
||||
error: 'red',
|
||||
neutral: 'zinc'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
```
|
||||
|
||||
```css [main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
:root {
|
||||
--ui-radius: var(--radius-sm);
|
||||
--ui-container: 90rem;
|
||||
--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);
|
||||
}
|
||||
```
|
||||
|
||||
::
|
||||
component_customization:
|
||||
title: Powerful component customization
|
||||
description: Nuxt UI leverages [Tailwind Variants](https://www.tailwind-variants.org/) to provide a powerful, maintainable system for managing component styles and intelligently merging Tailwind CSS classes without conflicts.
|
||||
links:
|
||||
- label: Customize components
|
||||
to: /getting-started/theme#customize-theme
|
||||
variant: outline
|
||||
color: neutral
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
features:
|
||||
- title: Powerful slot and variant system
|
||||
description: Customize component parts with slots and apply different styles based on props, creating consistent UI patterns with granular control over styling
|
||||
icon: i-lucide-layout-grid
|
||||
- title: Global theme with AppConfig
|
||||
description: Configure component styles project-wide with a centralized AppConfig that maintains consistency across your application without rebuilding
|
||||
icon: i-lucide-settings-2
|
||||
- title: Per-component customization
|
||||
description: Fine-tune individual components with the ui prop for slot-specific styling and class prop for root element overrides, providing maximum flexibility
|
||||
icon: i-lucide-component
|
||||
code: |
|
||||
::code-group
|
||||
|
||||
```ts [app.config.ts]
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
button: {
|
||||
slots: {
|
||||
base: 'group font-bold',
|
||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||
},
|
||||
defaultVariants: {
|
||||
color: 'neutral',
|
||||
variant: 'subtle'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```vue [Collapsible.vue]
|
||||
<template>
|
||||
<UCollapsible>
|
||||
<UButton
|
||||
label="Open"
|
||||
color="neutral"
|
||||
variant="subtle"
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
:ui="{
|
||||
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
||||
}"
|
||||
class="group font-bold"
|
||||
/>
|
||||
</UCollapsible>
|
||||
</template>
|
||||
```
|
||||
|
||||
::
|
||||
community:
|
||||
title: Nuxt UI open-source community
|
||||
description: Join our thriving community to contribute code, report issues, suggest features, or help with documentation. Every contribution makes Nuxt UI better for everyone.
|
||||
links:
|
||||
- label: Star on GitHub
|
||||
color: neutral
|
||||
variant: outline
|
||||
to: https://github.com/nuxt/ui
|
||||
target: _blank
|
||||
icon: i-lucide-star
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { kebabCase } from 'scule'
|
||||
import type { ContentNavigationItem } from '@nuxt/content'
|
||||
import { findPageBreadcrumb, mapContentNavigation } from '#ui-pro/utils/content'
|
||||
|
||||
@@ -9,7 +10,7 @@ definePageMeta({
|
||||
layout: 'docs'
|
||||
})
|
||||
|
||||
const { data: page } = await useAsyncData(route.path, () => queryCollection('content').path(route.path).first())
|
||||
const { data: page } = await useAsyncData(kebabCase(route.path), () => queryCollection('content').path(route.path).first())
|
||||
if (!page.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true })
|
||||
}
|
||||
@@ -24,7 +25,7 @@ watch(page, () => {
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => {
|
||||
const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround`, () => {
|
||||
return queryCollectionItemSurroundings('content', route.path, {
|
||||
fields: ['description']
|
||||
}).orWhere(group => group.where('framework', '=', framework.value).where('framework', 'IS NULL'))
|
||||
@@ -137,7 +138,7 @@ const communityLinks = computed(() => [{
|
||||
</template>
|
||||
|
||||
<template #description>
|
||||
<MDC v-if="page.description" :value="page.description" unwrap="p" />
|
||||
<MDC v-if="page.description" :value="page.description" unwrap="p" :cache-key="`${kebabCase(route.path)}-description`" />
|
||||
</template>
|
||||
|
||||
<template v-if="page.links?.length" #links>
|
||||
|
||||
@@ -15,7 +15,7 @@ useSeoMeta({
|
||||
ogImage: joinURL(url, '/og-image.png')
|
||||
})
|
||||
|
||||
const { data: components } = await useAsyncData('components', () => {
|
||||
const { data: components } = await useAsyncData('all-components', () => {
|
||||
return queryCollection('content')
|
||||
.where('path', 'LIKE', '/components/%')
|
||||
.where('extension', '=', 'md')
|
||||
@@ -31,17 +31,13 @@ const componentsPerCategory = computed(() => {
|
||||
})
|
||||
|
||||
const categories = [{
|
||||
id: 'layout',
|
||||
title: 'Layout',
|
||||
description: 'Structural components for organizing content, including containers, grids, dividers, and responsive layout systems.'
|
||||
id: 'element',
|
||||
title: 'Element',
|
||||
description: 'Core UI building blocks like buttons, badges, icons, avatars, and other fundamental interface elements.'
|
||||
}, {
|
||||
id: 'form',
|
||||
title: 'Form',
|
||||
description: 'Interactive form elements including inputs, selects, checkboxes, radio buttons, and advanced form validation components.'
|
||||
}, {
|
||||
id: 'element',
|
||||
title: 'Element',
|
||||
description: 'Core UI building blocks like buttons, badges, icons, avatars, and other fundamental interface elements.'
|
||||
}, {
|
||||
id: 'data',
|
||||
title: 'Data',
|
||||
@@ -54,6 +50,10 @@ const categories = [{
|
||||
id: 'overlay',
|
||||
title: 'Overlay',
|
||||
description: 'Floating UI elements like modals, dialogs, tooltips, popovers, and other components that overlay the main content.'
|
||||
}, {
|
||||
id: 'layout',
|
||||
title: 'Layout',
|
||||
description: 'Structural components for organizing content, including containers, grids, dividers, and responsive layout systems.'
|
||||
}]
|
||||
|
||||
const { y } = useWindowScroll()
|
||||
@@ -81,7 +81,10 @@ onMounted(() => {
|
||||
orientation="vertical"
|
||||
:ui="{ title: 'text-balance', container: 'relative' }"
|
||||
>
|
||||
<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 #top>
|
||||
<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>
|
||||
<UButton
|
||||
to="https://tailwindcss.com"
|
||||
@@ -96,6 +99,7 @@ onMounted(() => {
|
||||
<template #title>
|
||||
Build beautiful UI with <span class="text-(--ui-primary)">{{ components!.length }}+</span> powerful components
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
<UButton
|
||||
to="/getting-started/installation/vue"
|
||||
@@ -114,10 +118,9 @@ onMounted(() => {
|
||||
size="xl"
|
||||
/>
|
||||
</template>
|
||||
<template #top>
|
||||
<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" />
|
||||
<StarsBg />
|
||||
</template>
|
||||
|
||||
<StarsBg />
|
||||
<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">
|
||||
@@ -148,6 +151,7 @@ onMounted(() => {
|
||||
:description="component.description"
|
||||
:to="component.path"
|
||||
:ui="{ wrapper: 'order-last', container: 'lg:flex' }"
|
||||
class="group"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-0.5">
|
||||
@@ -156,7 +160,7 @@ onMounted(() => {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="group rounded-[calc(var(--ui-radius)*1.5)] border border-(--ui-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`"
|
||||
|
||||
@@ -8,7 +8,6 @@ hero:
|
||||
links:
|
||||
- label: Purchase Pro Kit
|
||||
to: 'https://nuxt.lemonsqueezy.com/buy/17213c49-621b-4c2c-9478-3a50a099003d'
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
target: _blank
|
||||
- label: Free Figma Kit
|
||||
to: 'https://go.nuxt.com/figma'
|
||||
@@ -121,7 +120,6 @@ section4:
|
||||
links:
|
||||
- label: Get access now
|
||||
to: 'https://nuxt.lemonsqueezy.com/buy/17213c49-621b-4c2c-9478-3a50a099003d'
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
- label: Preview UI Pro Design Kit
|
||||
to: 'https://go.nuxt.com/figma-pro'
|
||||
icon: i-logos-figma
|
||||
|
||||
@@ -93,10 +93,10 @@ onMounted(async () => {
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<MDC :value="page.hero.title" unwrap="p" />
|
||||
<MDC :value="page.hero.title" unwrap="p" cache-key="figma-hero-title" />
|
||||
</template>
|
||||
<template #description>
|
||||
<MDC :value="page.hero.description" unwrap="p" />
|
||||
<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-(--ui-border) border-b-0"> -->
|
||||
<div class="relative">
|
||||
@@ -140,10 +140,10 @@ onMounted(async () => {
|
||||
class="rounded-none bg-gradient-to-b from-(--ui-bg-muted) to-(--ui-bg)"
|
||||
>
|
||||
<template #title>
|
||||
<MDC :value="page.cta1.title" unwrap="p" />
|
||||
<MDC :value="page.cta1.title" unwrap="p" cache-key="figma-cta-1-title" />
|
||||
</template>
|
||||
<template #description>
|
||||
<MDC :value="page.cta1.description" unwrap="p" />
|
||||
<MDC :value="page.cta1.description" unwrap="p" cache-key="figma-cta-1-description" />
|
||||
</template>
|
||||
</UPageCTA>
|
||||
<UPageSection v-bind="page.section1" orientation="horizontal" :ui="{ container: 'py-16 sm:py-16 lg:py-16' }">
|
||||
@@ -189,7 +189,7 @@ onMounted(async () => {
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<MDC :value="page.section4.description" unwrap="p" />
|
||||
<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-(--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)">
|
||||
@@ -245,7 +245,7 @@ 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-(--ui-primary)" />
|
||||
<MDC :value="feature" unwrap="p" tag="span" class="text-sm truncate text-(--ui-text-accented)" />
|
||||
<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>
|
||||
@@ -281,8 +281,8 @@ onMounted(async () => {
|
||||
:items="(page.faq.items as any[])"
|
||||
class="max-w-4xl mx-auto"
|
||||
>
|
||||
<template #body="{ item }">
|
||||
<MDC :value="item.content" unwrap="p" />
|
||||
<template #body="{ item, index }">
|
||||
<MDC :value="item.content" unwrap="p" :cache-key="`figma-faq-${index}-content`" />
|
||||
</template>
|
||||
</UPageAccordion>
|
||||
</UPageSection>
|
||||
|
||||
307
docs/app/pages/index.vue
Normal file
@@ -0,0 +1,307 @@
|
||||
<script setup lang="ts">
|
||||
import { joinURL } from 'ufo'
|
||||
// @ts-expect-error yaml is not typed
|
||||
import page from '.index.yml'
|
||||
|
||||
const { url } = useSiteConfig()
|
||||
|
||||
useSeoMeta({
|
||||
titleTemplate: `%s - Nuxt UI`,
|
||||
title: page.title,
|
||||
description: page.description,
|
||||
ogTitle: `${page.title} - Nuxt UI`,
|
||||
ogDescription: page.description,
|
||||
ogImage: joinURL(url, '/og-image.png')
|
||||
})
|
||||
|
||||
const { data: components } = await useAsyncData('ui-components', () => {
|
||||
return queryCollection('content')
|
||||
.where('path', 'LIKE', '/components/%')
|
||||
.where('extension', '=', 'md')
|
||||
.where('module', 'IS NULL')
|
||||
.select('path', 'title', 'description', 'category', 'module')
|
||||
.all()
|
||||
})
|
||||
|
||||
const { data: module } = await useFetch<{
|
||||
stats: {
|
||||
downloads: number
|
||||
stars: number
|
||||
}
|
||||
contributors: {
|
||||
username: string
|
||||
}[]
|
||||
}>('https://api.nuxt.com/modules/ui', {
|
||||
key: 'stats',
|
||||
transform: ({ stats, contributors }) => ({ stats, contributors })
|
||||
})
|
||||
|
||||
const { format } = Intl.NumberFormat('en', { notation: 'compact' })
|
||||
|
||||
const contributorsRef = ref(null)
|
||||
const isContributorsInView = ref(false)
|
||||
const isContributorsHovered = useElementHover(contributorsRef)
|
||||
|
||||
useIntersectionObserver(contributorsRef, ([entry]) => {
|
||||
isContributorsInView.value = entry?.isIntersecting || false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UPageHero
|
||||
orientation="horizontal"
|
||||
:ui="{
|
||||
container: 'pb-0 sm:pb-0 lg:py-0',
|
||||
title: 'lg:mt-16',
|
||||
links: 'lg:mb-16',
|
||||
description: 'text-balance'
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
The Intuitive <br> <span class="text-(--ui-primary)">Vue UI Library</span>
|
||||
</template>
|
||||
<template #description>
|
||||
{{ page.hero.description }}
|
||||
</template>
|
||||
|
||||
<template #links>
|
||||
<UButton v-for="link of page.hero.links" :key="link.label" v-bind="link" size="xl" />
|
||||
<div class="w-full my-6">
|
||||
<USeparator class="w-1/2" type="dashed" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<Motion
|
||||
v-for="(feature, index) in page.hero.features"
|
||||
:key="feature.title"
|
||||
as-child
|
||||
:initial="{ opacity: 0, transform: 'translateX(-10px)' }"
|
||||
:in-view="{ opacity: 1, transform: 'translateX(0)' }"
|
||||
:transition="{ delay: 0.2 + 0.4 * index }"
|
||||
:in-view-options="{ once: true }"
|
||||
>
|
||||
<UPageFeature v-bind="feature" class="opacity-0" />
|
||||
</Motion>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<SkyBg />
|
||||
|
||||
<div class="h-[344px] lg:h-full lg:relative w-full lg:min-h-[calc(100vh-var(--ui-header-height)-1px)] overflow-hidden">
|
||||
<UPageMarquee
|
||||
pause-on-hover
|
||||
:overlay="false"
|
||||
:ui="{
|
||||
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:h-full 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-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
:to="component.path"
|
||||
>
|
||||
<UColorModeImage
|
||||
|
||||
:light="`${component.path.replace('/components/', '/components/light/')}.png`"
|
||||
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`"
|
||||
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"
|
||||
/>
|
||||
<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" />
|
||||
</ULink>
|
||||
</UPageMarquee>
|
||||
|
||||
<UPageMarquee
|
||||
pause-on-hover
|
||||
reverse
|
||||
:overlay="false"
|
||||
:ui="{
|
||||
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:h-full 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-(--ui-border) w-[290px] xl:w-[330px] 2xl:w-[320px] 2xl:p-2 2xl:border-y"
|
||||
:to="component.path"
|
||||
>
|
||||
<UColorModeImage
|
||||
:light="`${component.path.replace('/components/', '/components/light/')}.png`"
|
||||
:dark="`${component.path.replace('/components/', '/components/dark/')}.png`"
|
||||
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"
|
||||
/>
|
||||
<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" />
|
||||
</ULink>
|
||||
</UPageMarquee>
|
||||
</div>
|
||||
</UPageHero>
|
||||
|
||||
<USeparator />
|
||||
|
||||
<UPageSection :ui="{ container: 'lg:py-16' }">
|
||||
<ul class="grid grid-cols-1 gap-x-6 sm:grid-cols-2 lg:grid-cols-3 gap-y-6 lg:gap-x-8 lg:gap-y-8 xl:gap-y-10">
|
||||
<Motion
|
||||
v-for="(feature, index) in page?.features"
|
||||
:key="feature.title"
|
||||
as="li"
|
||||
:initial="{ opacity: 0, transform: 'translateY(10px)' }"
|
||||
:in-view="{ opacity: 1, transform: 'translateY(0)' }"
|
||||
:transition="{ delay: 0.1 * index }"
|
||||
:in-view-options="{ once: true }"
|
||||
class="flex items-start gap-x-3 relative group"
|
||||
>
|
||||
<NuxtLink v-if="feature.to" :to="feature.to" class="absolute inset-0 z-10" />
|
||||
|
||||
<div class="relative p-3">
|
||||
<svg class="absolute inset-0" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="6.5" x2="6.5" y2="44" stroke="var(--ui-border)" />
|
||||
<line x1="38.5" x2="38.5" y2="44" stroke="var(--ui-border)" />
|
||||
<line y1="5.5" x2="44" y2="5.5" stroke="var(--ui-border)" />
|
||||
<line y1="37.5" x2="44" y2="37.5" stroke="var(--ui-border)" />
|
||||
<circle cx="6.53613" cy="5.45508" r="1.5" fill="var(--ui-border-accented)" />
|
||||
<circle cx="38.5957" cy="5.45508" r="1.5" fill="var(--ui-border-accented)" />
|
||||
<circle cx="6.53711" cy="37.4551" r="1.5" fill="var(--ui-border-accented)" />
|
||||
<circle cx="38.5957" cy="37.4551" r="1.5" fill="var(--ui-border-accented)" />
|
||||
</svg>
|
||||
<UIcon :name="feature.icon" class="size-5 flex-shrink-0" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<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 flex-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-(--ui-text-muted)">
|
||||
{{ feature.description }}
|
||||
</p>
|
||||
</div>
|
||||
</Motion>
|
||||
</ul>
|
||||
</UPageSection>
|
||||
|
||||
<USeparator />
|
||||
|
||||
<UPageSection
|
||||
:title="page.design_system.title"
|
||||
:description="page.design_system.description"
|
||||
:features="page.design_system.features"
|
||||
:links="page.design_system.links"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<MDC :value="page.design_system.code" />
|
||||
</UPageSection>
|
||||
|
||||
<USeparator />
|
||||
|
||||
<UPageSection
|
||||
:title="page.component_customization.title"
|
||||
:features="page.component_customization.features"
|
||||
:links="page.component_customization.links"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<template #description>
|
||||
<MDC :value="page.component_customization.description" />
|
||||
</template>
|
||||
|
||||
<MDC :value="page.component_customization.code" />
|
||||
</UPageSection>
|
||||
|
||||
<USeparator />
|
||||
|
||||
<UPageSection
|
||||
:title="page.community.title"
|
||||
:description="page.community.description"
|
||||
:links="page.community.links"
|
||||
orientation="horizontal"
|
||||
:ui="{ features: 'flex items-center gap-4 lg:gap-8' }"
|
||||
class="border-b border-(--ui-border)"
|
||||
>
|
||||
<template #features>
|
||||
<NuxtLink to="https://npm.chart.dev/@nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.downloads ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">monthly downloads</p>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="https://github.com/nuxt/ui" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
{{ format(module?.stats?.stars ?? 0) }}+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">GitHub stars</p>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="https://github.com/nuxt/ui/graphs/contributors" target="_blank" class="min-w-0">
|
||||
<p class="text-4xl font-semibold text-(--ui-text-highlighted) truncate">
|
||||
175+
|
||||
</p>
|
||||
<p class="text-(--ui-text-muted) text-sm truncate">Contributors</p>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<div ref="contributorsRef" class="p-4 sm:px-6 md:px-8 lg:px-12 xl:px-14 overflow-hidden flex relative">
|
||||
<LazyHomeContributors :contributors="module?.contributors" :paused="!isContributorsInView || isContributorsHovered" />
|
||||
</div>
|
||||
</UPageSection>
|
||||
|
||||
<UPageSection :ui="{ container: 'relative !pb-0 overflow-hidden' }">
|
||||
<template #title>
|
||||
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-(--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">
|
||||
Discover Nuxt UI Pro
|
||||
</UButton>
|
||||
<UButton to="/pro/templates" size="lg" variant="outline" trailing-icon="i-lucide-arrow-right" color="neutral">
|
||||
Explore templates
|
||||
</UButton>
|
||||
</template>
|
||||
|
||||
<StarsBg />
|
||||
|
||||
<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"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
loading="lazy"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
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' }">
|
||||
<img
|
||||
v-for="i in [5, 6, 7, 8]"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
loading="lazy"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
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' }">
|
||||
<img
|
||||
v-for="i in [9, 10, 11, 12]"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
loading="lazy"
|
||||
class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
|
||||
>
|
||||
</UPageMarquee>
|
||||
</div>
|
||||
</UPageSection>
|
||||
</UMain>
|
||||
</template>
|
||||
@@ -29,7 +29,7 @@ pricing:
|
||||
- Free [preview available](https://www.figma.com/design/mxXR9binOSLU3rYKZZRPXs/PREVIEW---NuxtUIPro-V3-BETA?m=auto&t=c4598Wr0rZwKPs5M-1)
|
||||
- Includes Nuxt UI [Figma Kit](https://www.figma.com/community/file/1288455405058138934)
|
||||
button:
|
||||
label: Explore Figma Kit Pricing
|
||||
label: Buy Figma Kit
|
||||
to: 'https://nuxt.lemonsqueezy.com/buy/17213c49-621b-4c2c-9478-3a50a099003d'
|
||||
color: 'neutral'
|
||||
plans:
|
||||
@@ -119,12 +119,12 @@ testimonials:
|
||||
to: 'https://www.linkedin.com/in/anthonybettini/'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://media.licdn.com/dms/image/v2/C4E03AQEY3pmXsH8hDg/profile-displayphoto-shrink_200_200/profile-displayphoto-shrink_200_200/0/1519741249602?e=1743638400&v=beta&t=lw2K6vS0OOCZWGtHY1buJVkRItQCa4OQw0vzAhhpJk8'
|
||||
src: 'https://media.licdn.com/dms/image/v2/C4E03AQEY3pmXsH8hDg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1519741249442?e=1746057600&v=beta&t=dvQfBT9ah03MPNy9cnly30ugreeCdxG4nrxV3lwKAC8'
|
||||
- quote: "Wow, Nuxt UI Pro is a total game-changer! I'm seriously impressed with the quality, attention to detail, and the insane variety of components you get. It's like hitting the jackpot for any developer. I've saved countless hours that I would've spent stressing over making my apps look good, with amazing accessible UX, and instead, I've been able to focus on the real deal – building the app itself. It's an instant buy for me, every single time. No second thoughts!"
|
||||
user:
|
||||
name: 'Yaz Jallad'
|
||||
description: 'Founder Ninjaparade Digital'
|
||||
to: 'https://twitter.com/ninjaparade/'
|
||||
to: 'https://x.com/ninjaparade/'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://pbs.twimg.com/profile_images/1824690890222485504/lQ7v1AGt_400x400.jpg'
|
||||
@@ -158,8 +158,8 @@ testimonials:
|
||||
- quote: "I jumped at the chance to buy the Nuxt team's new UI kit as soon as I saw it. While I'm already a fan of Nuxt UI, the pro version takes it to a whole new level and lets me paste entire blocks into all my projects, saving me a ton of time."
|
||||
user:
|
||||
name: 'Thomas Sanlis'
|
||||
description: 'Freelance developer and designer'
|
||||
to: 'https://twitter.com/T_Zahil'
|
||||
description: 'Founder of Uneed'
|
||||
to: 'https://x.com/T_Zahil'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://pbs.twimg.com/profile_images/1374040164180299791/ACw4G3nZ_400x400.jpg'
|
||||
@@ -167,7 +167,7 @@ testimonials:
|
||||
user:
|
||||
name: 'Benjamin Code'
|
||||
description: 'YouTuber and SaaS builder'
|
||||
to: 'https://twitter.com/benjamincode'
|
||||
to: 'https://x.com/benjamincode'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://pbs.twimg.com/profile_images/1607353032420769793/I8qQSUfQ_400x400.jpg'
|
||||
@@ -175,15 +175,23 @@ testimonials:
|
||||
user:
|
||||
name: 'Estéban Soubiran'
|
||||
description: 'Web developer and UnJS member'
|
||||
to: 'https://twitter.com/soubiran_'
|
||||
to: 'https://x.com/soubiran_'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://pbs.twimg.com/profile_images/1801649350319218689/aS_X_iTm_400x400.jpg'
|
||||
- quote: "I used to code all my components from scratch, but since I started using Nuxt UI Pro, I just can’t work without it. It has all the components I need: for landing pages, for dashboards and even for blog content! This is a no-brainer investment to ship faster, and I’ve already made my money back 10x in time saved!"
|
||||
user:
|
||||
name: 'Nico Jeannen'
|
||||
description: 'Founder of Ads Template'
|
||||
to: 'https://x.com/nico_jeannen'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://pbs.twimg.com/profile_images/1754025659180187649/AAG78n19_400x400.jpg'
|
||||
- quote: "As someone who builds a lot of projects, Nuxt UI Pro has been a game-changer. It's not just about saving time – it's about having components that are thoughtfully designed and just work. The developer experience is exceptional, and I can focus on building features instead of tweaking UI components."
|
||||
user:
|
||||
name: 'Hugo Richard'
|
||||
description: 'Frontend Engineer at NuxtLabs'
|
||||
to: 'https://twitter.com/hugorcd__'
|
||||
description: 'Founder of Shelve'
|
||||
to: 'https://x.com/hugorcd__'
|
||||
target: _blank
|
||||
avatar:
|
||||
src: 'https://ipx.nuxt.com/f_auto,s_40x40/gh_avatar/hugorcd'
|
||||
|
||||
@@ -7,13 +7,13 @@ hero:
|
||||
- label: Buy a license
|
||||
size: xl
|
||||
to: /pro/pricing
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
- label: Get started
|
||||
- label: Try for free
|
||||
trailing: true
|
||||
color: neutral
|
||||
variant: ghost
|
||||
variant: outline
|
||||
to: /getting-started/installation/pro/nuxt
|
||||
size: xl
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
features:
|
||||
title: Create stunning Vue applications faster.
|
||||
description: Nuxt UI Pro comes packed with powerful features to help you build modern, performant, accessible and responsive Nuxt applications at record speed. From pre-built UI sections to Figma design kits, every detail is crafted to speed up your development and deliver a polished user experience.
|
||||
@@ -157,8 +157,8 @@ cta:
|
||||
links:
|
||||
- label: Buy a license
|
||||
to: '/pro/pricing'
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
- label: Get started
|
||||
to: '/getting-started/license'
|
||||
variant: ghost
|
||||
- label: Try for free
|
||||
to: /getting-started/installation/pro/nuxt
|
||||
variant: outline
|
||||
color: neutral
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
|
||||
@@ -5,16 +5,16 @@ hero:
|
||||
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:
|
||||
- label: Get started
|
||||
to: /getting-started/installation/pro/nuxt#use-an-official-template
|
||||
color: neutral
|
||||
- label: Buy a license
|
||||
color: primary
|
||||
size: xl
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
- label: Purchase a license
|
||||
to: /pro/pricing
|
||||
- label: Try for free
|
||||
to: /getting-started/installation/pro/nuxt#use-an-official-template
|
||||
size: xl
|
||||
color: neutral
|
||||
variant: outline
|
||||
to: /pro/pricing
|
||||
trailingIcon: i-lucide-arrow-right
|
||||
templates:
|
||||
- title: 'Dashboard'
|
||||
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."
|
||||
@@ -30,13 +30,26 @@ templates:
|
||||
- title: Resizable multi-column layout
|
||||
icon: i-lucide-columns-3
|
||||
links:
|
||||
- label: Live preview
|
||||
- label: Preview
|
||||
to: https://dashboard-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Use this template
|
||||
to: https://github.com/nuxt-ui-pro/dashboard/tree/v3
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/dashboard
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
variant: outline
|
||||
- label: Preview
|
||||
to: https://vue-dashboard-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-vue
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Vue Template
|
||||
to: https://github.com/nuxt-ui-pro/dashboard-vue
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
@@ -55,13 +68,14 @@ templates:
|
||||
- title: Authentication pages (login, register)
|
||||
icon: i-lucide-user-round-check
|
||||
links:
|
||||
- label: Live preview
|
||||
- label: Preview
|
||||
to: https://saas-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Use this template
|
||||
to: https://github.com/nuxt-ui-pro/saas/tree/v3
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/saas
|
||||
target: _blank
|
||||
variant: outline
|
||||
icon: i-simple-icons-github
|
||||
@@ -80,13 +94,14 @@ templates:
|
||||
- title: Write content in YAML
|
||||
icon: i-simple-icons-yaml
|
||||
links:
|
||||
- label: Live preview
|
||||
- label: Preview
|
||||
to: https://landing-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Use this template
|
||||
to: https://github.com/nuxt-ui-pro/landing/tree/v3
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/landing
|
||||
target: _blank
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
@@ -105,14 +120,51 @@ templates:
|
||||
- title: Full-text search out of the box
|
||||
icon: i-lucide-search
|
||||
links:
|
||||
- label: Live preview
|
||||
- label: Preview
|
||||
to: https://docs-template.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-nuxt-icon
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Use this template
|
||||
- label: Nuxt Template
|
||||
to: https://github.com/nuxt-ui-pro/docs/tree/v3
|
||||
target: _blank
|
||||
variant: outline
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
- title: 'Starter'
|
||||
description: "A minimal starter template to build your own project with Nuxt UI Pro components."
|
||||
icon: i-lucide-flower
|
||||
thumbnail:
|
||||
dark: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3VpLXByby1zdGFydGVyLm51eHQuZGV2IiwiaWF0IjoxNzM5NDYzMzk4fQ.XLzPkSW6nRbPW07QO1RkMwz_RAPA4KfeyrWrK3li9YI.jpg?theme=dark
|
||||
light: https://assets.hub.nuxt.com/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL3VpLXByby1zdGFydGVyLm51eHQuZGV2IiwiaWF0IjoxNzM5NDYzMzk4fQ.XLzPkSW6nRbPW07QO1RkMwz_RAPA4KfeyrWrK3li9YI.jpg?theme=light
|
||||
features:
|
||||
- title: ESLint Configured
|
||||
icon: i-simple-icons-eslint
|
||||
- title: Nuxt 4 Compatibility Enabled
|
||||
icon: i-simple-icons-nuxtdotjs
|
||||
links:
|
||||
- label: Preview
|
||||
to: https://ui-pro-starter.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/starter
|
||||
target: _blank
|
||||
variant: outline
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
- label: Preview
|
||||
to: https://ui-pro-starter-vue.nuxt.dev
|
||||
target: _blank
|
||||
leadingIcon: i-logos-vue
|
||||
trailingIcon: i-lucide-arrow-up-right
|
||||
color: neutral
|
||||
- label: Vue Template
|
||||
to: https://github.com/nuxt-ui-pro/starter-vue
|
||||
target: _blank
|
||||
variant: outline
|
||||
icon: i-simple-icons-github
|
||||
color: neutral
|
||||
|
||||
@@ -70,14 +70,12 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UPageHero headline="License Activation" :title="title" :description="description" :ui="{ container: 'relative' }">
|
||||
<template #top>
|
||||
<StarsBg />
|
||||
</template>
|
||||
<UPageHero headline="License Activation" :title="title" :description="description" :ui="{ container: 'relative overflow-hidden', wrapper: 'lg:px-12', description: 'text-pretty' }">
|
||||
<StarsBg />
|
||||
<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="lg:border-y border-(--ui-border)">
|
||||
<UCard class="lg:w-1/2 m-auto lg:rounded-none overflow-hidden" variant="outline" :ui="{ footer: 'bg-(--ui-bg-muted)' }">
|
||||
<div class="px-4 py-10 lg:border border-(--ui-border) bg-(--ui-bg)">
|
||||
<div class="max-w-xl mx-auto">
|
||||
<UForm
|
||||
:schema="schema"
|
||||
:validate-on="['blur']"
|
||||
@@ -107,12 +105,13 @@ onMounted(() => {
|
||||
</UAlert>
|
||||
<UAlert v-else-if="errorMessage" color="error" variant="subtle" :title="errorMessage" />
|
||||
</UForm>
|
||||
<template #footer>
|
||||
<p class="text-sm text-center text-neutral-500 dark:text-neutral-400">
|
||||
If you purchased a license with multiple seats, activate the license key for each member of your team.
|
||||
</p>
|
||||
</template>
|
||||
</UCard>
|
||||
|
||||
<ProseHr />
|
||||
|
||||
<ProseNote>
|
||||
If you purchased a license with multiple seats, activate the license key for each member of your team.
|
||||
</ProseNote>
|
||||
</div>
|
||||
</div>
|
||||
</UPageHero>
|
||||
</UMain>
|
||||
|
||||
@@ -26,21 +26,52 @@ useSeoMeta({
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<MDC :value="page.hero.title" unwrap="p" />
|
||||
<MDC :value="page.hero.title" tag="span" unwrap="p" cache-key="pro-hero-title" />
|
||||
</template>
|
||||
<template #description>
|
||||
<MDC :value="page.hero.description" unwrap="p" />
|
||||
</template>
|
||||
<template #top>
|
||||
<StarsBg />
|
||||
<MDC :value="page.hero.description" tag="span" unwrap="p" cache-key="pro-hero-description" />
|
||||
</template>
|
||||
|
||||
<StarsBg />
|
||||
|
||||
<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-(--ui-border) inset-0 mx-4 sm:mx-6 lg:mx-8" />
|
||||
</Motion>
|
||||
<Motion as-child :initial="{ opacity: 0 }" :animate="{ opacity: 1 }" :transition="{ delay: 0.6, duration: 0.6 }">
|
||||
<NuxtImg src="/pro/hero.png" width="1374" height="439" alt="Nuxt UI Pro" class="w-full border-t border-x border-(--ui-border) bg-(--ui-bg-muted)" />
|
||||
</Motion>
|
||||
<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"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
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' }">
|
||||
<img
|
||||
v-for="i in [5, 6, 7, 8]"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
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' }">
|
||||
<img
|
||||
v-for="i in [9, 10, 11, 12]"
|
||||
:key="i"
|
||||
:src="`/pro/blocks/image${i}.png`"
|
||||
width="460"
|
||||
height="258"
|
||||
:alt="`Nuxt UI Pro Screenshot ${i}`"
|
||||
class="aspect-video border border-(--ui-border) rounded-[calc(var(--ui-radius)*2)] bg-white"
|
||||
>
|
||||
</UPageMarquee>
|
||||
</div>
|
||||
</UPageHero>
|
||||
<UPageCTA
|
||||
variant="outline"
|
||||
@@ -52,7 +83,7 @@ useSeoMeta({
|
||||
>
|
||||
<template #description>
|
||||
<Motion :initial="{ opacity: 0, transform: 'translateY(10px)' }" :in-view="{ opacity: 1, transform: 'translateY(0)' }" :in-view-options="{ once: true }" :transition="{ delay: 0.2 }">
|
||||
<MDC :value="page.testimonial.quote" unwrap="p" class="before:content-[open-quote] after:content-[close-quote] " />
|
||||
<MDC :value="page.testimonial.quote" tag="span" unwrap="p" class="before:content-[open-quote] after:content-[close-quote]" cache-key="pro-testimonial-quote" />
|
||||
</Motion>
|
||||
</template>
|
||||
<Motion :initial="{ opacity: 0, transform: 'translateY(10px)' }" :in-view="{ opacity: 1, transform: 'translateY(0)' }" :in-view-options="{ once: true }" :transition="{ delay: 0.3 }">
|
||||
@@ -85,13 +116,13 @@ useSeoMeta({
|
||||
wrapper: 'grid grid-cols-1 lg:grid-cols-2',
|
||||
description: 'lg:mt-0' }"
|
||||
orientation="horizontal"
|
||||
class="rounded-none border-t border-(--ui-border) bg-gradient-to-b from-(--ui-bg-muted) to-(--ui-bg)"
|
||||
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" unwrap="p" />
|
||||
<MDC :value="page.mainSection.title" tag="span" unwrap="p" cache-key="pro-main-section-title" />
|
||||
</template>
|
||||
<template #description>
|
||||
<MDC :value="page.mainSection.description" unwrap="p" />
|
||||
<MDC :value="page.mainSection.description" tag="span" unwrap="p" cache-key="pro-main-section-description" />
|
||||
</template>
|
||||
</UPageCTA>
|
||||
<UPageSection
|
||||
@@ -108,7 +139,7 @@ useSeoMeta({
|
||||
container: index === 0 ? 'pb-0 sm:pb-0 lg:pb-0 py-16 sm:py-16 lg:py-16' : ''
|
||||
}"
|
||||
>
|
||||
<MDC :value="section.code" />
|
||||
<MDC :value="section.code" :cache-key="`pro-section-${index}-code`" />
|
||||
</UPageSection>
|
||||
|
||||
<UPageSection
|
||||
@@ -166,6 +197,7 @@ useSeoMeta({
|
||||
orientation="horizontal"
|
||||
>
|
||||
<StarsBg />
|
||||
|
||||
<video
|
||||
class="rounded-[var(--ui-radius)] z-10"
|
||||
preload="none"
|
||||
|
||||
@@ -24,11 +24,10 @@ useSeoMeta({
|
||||
}"
|
||||
>
|
||||
<template #title>
|
||||
<MDC :value="page.pricing.title" unwrap="p" />
|
||||
</template>
|
||||
<template #top>
|
||||
<StarsBg />
|
||||
<MDC :value="page.pricing.title" unwrap="p" cache-key="pro-pricing-title" />
|
||||
</template>
|
||||
|
||||
<StarsBg />
|
||||
<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-(--ui-bg) gap-8 lg:gap-0">
|
||||
<UPricingPlan
|
||||
@@ -59,7 +58,7 @@ useSeoMeta({
|
||||
<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-(--ui-primary) shrink-0" />
|
||||
<MDC :value="feature" unwrap="p" class="text-sm truncate text-(--ui-text-toned)" />
|
||||
<MDC :value="feature" unwrap="p" class="text-sm truncate text-(--ui-text-toned)" :cache-key="`pro-pricing-figma-feature-${index}`" />
|
||||
</li>
|
||||
</template>
|
||||
</UPricingPlan>
|
||||
@@ -111,8 +110,8 @@ useSeoMeta({
|
||||
:items="(page.faq.items as any[])"
|
||||
class="max-w-4xl mx-auto"
|
||||
>
|
||||
<template #body="{ item }">
|
||||
<MDC :value="item.content" unwrap="p" />
|
||||
<template #body="{ item, index }">
|
||||
<MDC :value="item.content" unwrap="p" :cache-key="`pro-pricing-faq-${index}-content`" />
|
||||
</template>
|
||||
</UPageAccordion>
|
||||
</UPageSection>
|
||||
|
||||
@@ -16,59 +16,60 @@ useSeoMeta({
|
||||
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<UPageHero :links="page.links" :ui="{ container: 'relative' }">
|
||||
<template #top>
|
||||
<div class="relative">
|
||||
<UPageHero :links="page.links" :ui="{ container: 'relative' }">
|
||||
<StarsBg />
|
||||
</template>
|
||||
<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 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" />
|
||||
</template>
|
||||
<template #title>
|
||||
<MDC :value="page.hero.title" unwrap="p" cache-key="pro-templates-hero-title" />
|
||||
</template>
|
||||
|
||||
<template #description>
|
||||
<MDC :value="page.hero.description" unwrap="p" />
|
||||
</template>
|
||||
</UPageHero>
|
||||
<template #description>
|
||||
<MDC :value="page.hero.description" unwrap="p" cache-key="pro-templates-hero-description" />
|
||||
</template>
|
||||
</UPageHero>
|
||||
|
||||
<UPageSection
|
||||
v-for="(template, index) in page.templates"
|
||||
:key="index"
|
||||
:title="template.title"
|
||||
:links="template.links"
|
||||
:features="template.features"
|
||||
orientation="horizontal"
|
||||
class="lg:border-t border-(--ui-border)"
|
||||
:ui="{
|
||||
title: 'lg:text-4xl',
|
||||
wrapper: 'lg:py-16 lg:border-r border-(--ui-border) order-last lg:pr-16',
|
||||
container: 'lg:py-0'
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<MDC :value="template.description" unwrap="p" />
|
||||
</template>
|
||||
<UPageSection
|
||||
v-for="(template, index) in page.templates"
|
||||
:key="index"
|
||||
:title="template.title"
|
||||
:links="template.links"
|
||||
:features="template.features"
|
||||
orientation="horizontal"
|
||||
class="lg:border-t border-(--ui-border)"
|
||||
:ui="{
|
||||
title: 'lg:text-4xl',
|
||||
wrapper: 'lg:py-16 lg:border-r border-(--ui-border) order-last lg:pr-16',
|
||||
container: 'lg:py-0',
|
||||
links: 'gap-x-3'
|
||||
}"
|
||||
>
|
||||
<template #description>
|
||||
<MDC :value="template.description" unwrap="p" :cache-key="`pro-templates-${index}-description`" />
|
||||
</template>
|
||||
|
||||
<div class="lg:border-x border-(--ui-border) h-full flex items-center lg:bg-(--ui-bg-muted)/20">
|
||||
<Motion :initial="{ opacity: 0, transform: 'translateY(10px)' }" :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-(--ui-border) rounded-(--ui-radius) lg:rounded-none"
|
||||
width="656"
|
||||
height="369"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UCarousel
|
||||
v-else-if="template.images"
|
||||
v-slot="{ item }"
|
||||
:items="(template.images as any[])"
|
||||
dots
|
||||
>
|
||||
<NuxtImg v-bind="item" class="w-full h-full object-cover" width="576" height="360" />
|
||||
</UCarousel>
|
||||
<Placeholder v-else class="w-full h-full aspect-video" />
|
||||
</Motion>
|
||||
</div>
|
||||
</UPageSection>
|
||||
<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)' }" :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-(--ui-border) rounded-(--ui-radius) lg:rounded-none"
|
||||
width="656"
|
||||
height="369"
|
||||
loading="lazy"
|
||||
/>
|
||||
<UCarousel
|
||||
v-else-if="template.images"
|
||||
v-slot="{ item }"
|
||||
:items="(template.images as any[])"
|
||||
dots
|
||||
>
|
||||
<NuxtImg v-bind="item" class="w-full h-full object-cover" width="576" height="360" />
|
||||
</UCarousel>
|
||||
<Placeholder v-else class="w-full h-full aspect-video" />
|
||||
</Motion>
|
||||
</div>
|
||||
</UPageSection>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -11,7 +11,7 @@ We're thrilled to introduce this major update to our UI library, bringing signif
|
||||
|
||||
<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 (Radix Vue)
|
||||
### Reka UI
|
||||
|
||||
We've transitioned from [Headless UI](https://headlessui.com/) to [Reka UI](https://reka-ui.com/) as our core component foundation. This shift brings several key advantages:
|
||||
|
||||
@@ -31,8 +31,8 @@ Nuxt UI v3 integrates the latest Tailwind CSS v4, bringing significant improveme
|
||||
- **CSS-first configuration**: A reimagined developer experience where you customize and extend the framework directly in CSS instead of a JavaScript configuration file.
|
||||
- **Designed for the modern web**: Built on native cascade layers, wide-gamut colors, and including first-class support for modern CSS features like container queries, @starting-style, popovers, and more.
|
||||
|
||||
::note{to="https://tailwindcss.com/docs/upgrade-guide" target="_blank" aria-label="Tailwind CSS v4 upgrade guide"}
|
||||
Learn how to upgrade your project from Tailwind CSS v3 to v4.
|
||||
::note{to="https://tailwindcss.com/docs/upgrade-guide#changes-from-v3" target="_blank" aria-label="Tailwind CSS v4 upgrade guide"}
|
||||
Learn about all the breaking changes in Tailwind CSS v4.
|
||||
::
|
||||
|
||||
### Tailwind Variants
|
||||
@@ -105,7 +105,7 @@ We want to be transparent: migrating from Nuxt UI v2 to v3 will require signific
|
||||
|
||||
Key points to consider:
|
||||
|
||||
- A comprehensive migration guide will be available in the coming weeks.
|
||||
- Read our [migration guide](/getting-started/migration) to upgrade your project from v2 to v3.
|
||||
- Review the new documentation and components carefully before attempting to upgrade.
|
||||
- If you encounter any issues, please report them on our [GitHub repository](https://github.com/nuxt/ui/issues).
|
||||
|
||||
@@ -121,7 +121,7 @@ Key points to consider:
|
||||
::
|
||||
|
||||
::accordion-item{label="What about Nuxt UI Pro?"}
|
||||
We've also rebuilt Nuxt UI Pro from scratch and released a `v3.0.0-alpha.x` package with almost all components ready. This is a free update, so the license you buy now is valid for all UI Pro versions. We're actively working to finish the rewrite of all Nuxt UI Pro components.
|
||||
We've also rebuilt Nuxt UI Pro from scratch as v3 to match Nuxt UI version. The license you bought or will buy is valid for both Nuxt UI Pro v1 and v3, this is a **free update**. You can follow the [installation guide](/getting-started/installation/pro/nuxt) to get started.
|
||||
::
|
||||
|
||||
::accordion-item{label="Will Nuxt UI v3 work with other CSS frameworks like UnoCSS?"}
|
||||
@@ -137,7 +137,7 @@ Key points to consider:
|
||||
::
|
||||
|
||||
::accordion-item{label="Is this version stable and suitable for production use?"}
|
||||
As Nuxt UI v3 is currently in alpha, we recommend thorough testing before using it in production environments. We're actively working on stabilization and welcome feedback from early adopters to improve the library. Feel free to report any issues you encounter on our [GitHub repository](https://github.com/nuxt/ui/issues).
|
||||
Nuxt UI v3 is now in beta and is stable enough to be used in production. We now recommend using v3 over v2. We welcome feedback from users to help improve the library further. Feel free to report any issues you encounter on our [GitHub repository](https://github.com/nuxt/ui/issues).
|
||||
::
|
||||
::
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Looking for the **Vue** version?
|
||||
|
||||
::steps{level="4"}
|
||||
|
||||
#### Install the Nuxt UI v3 alpha package
|
||||
#### Install the Nuxt UI v3 beta package
|
||||
|
||||
::code-group{sync="pm"}
|
||||
|
||||
@@ -56,19 +56,14 @@ export default defineNuxtConfig({
|
||||
|
||||
#### Import Tailwind CSS and Nuxt UI in your CSS
|
||||
|
||||
```css [assets/css/main.css]
|
||||
::code-group
|
||||
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
```
|
||||
|
||||
::warning
|
||||
The `theme(static)` is required since [`tailwindcss@4.0.8`](https://github.com/tailwindlabs/tailwindcss/releases/tag/v4.0.8) introduced a breaking change to only expose used CSS variables.
|
||||
::
|
||||
|
||||
::tip
|
||||
Use the `css` property in your `nuxt.config.ts` to import your CSS file.
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
```ts [nuxt.config.ts] {3}
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css']
|
||||
@@ -77,6 +72,10 @@ export default defineNuxtConfig({
|
||||
|
||||
::
|
||||
|
||||
::warning
|
||||
The `theme(static)` is required since [`tailwindcss@4.0.8`](https://github.com/tailwindlabs/tailwindcss/releases/tag/v4.0.8) introduced a breaking change to only expose used CSS variables.
|
||||
::
|
||||
|
||||
::callout{icon="i-simple-icons-visualstudiocode"}
|
||||
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:
|
||||
|
||||
@@ -141,6 +140,7 @@ Use the `prefix` option to change the prefix of the components.
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
prefix: 'Nuxt'
|
||||
}
|
||||
@@ -156,6 +156,7 @@ Use the `fonts` option to enable or disable the [`@nuxt/fonts`](https://github.c
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
fonts: false
|
||||
}
|
||||
@@ -171,6 +172,7 @@ Use the `colorMode` option to enable or disable the [`@nuxt/color-mode`](https:/
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
colorMode: false
|
||||
}
|
||||
@@ -186,6 +188,7 @@ Use the `theme.colors` option to define the dynamic color aliases used to genera
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
theme: {
|
||||
colors: ['primary', 'error']
|
||||
@@ -207,6 +210,7 @@ Use the `theme.transitions` option to enable or disable transitions on component
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
theme: {
|
||||
transitions: false
|
||||
|
||||
@@ -20,7 +20,7 @@ Looking for the **Nuxt** version?
|
||||
|
||||
::steps{level="4"}
|
||||
|
||||
#### Install the Nuxt UI v3 alpha package
|
||||
#### Install the Nuxt UI v3 beta package
|
||||
|
||||
::code-group{sync="pm"}
|
||||
|
||||
|
||||
457
docs/content/1.getting-started/2.migration.md
Normal file
@@ -0,0 +1,457 @@
|
||||
---
|
||||
title: Migration
|
||||
description: 'A comprehensive guide to migrate your application from Nuxt UI v2 to Nuxt UI v3.'
|
||||
---
|
||||
|
||||
Nuxt UI v3.0 is a new major version rebuilt from the ground up, introducing a modern architecture with significant performance improvements and an enhanced developer experience. This major release includes several breaking changes alongside powerful new features and capabilities:
|
||||
|
||||
- **Tailwind CSS v4**: Migration from JavaScript to CSS-based configuration
|
||||
- **Reka UI**: Replacing Headless UI as the underlying component library
|
||||
- **Tailwind Variants**: New styling API for component variants
|
||||
|
||||
This guide provides step by step instructions to migrate your application from v2 to v3.
|
||||
|
||||
## Migrate your project
|
||||
|
||||
::steps
|
||||
|
||||
### Update Tailwind CSS
|
||||
|
||||
Tailwind CSS v4 introduces significant changes to its configuration approach. The official Tailwind upgrade tool will help automate most of the migration process.
|
||||
|
||||
::note{to="https://tailwindcss.com/docs/upgrade-guide#changes-from-v3" target="_blank"}
|
||||
For a detailed walkthrough of all changes, refer to the official **Tailwind CSS v4 upgrade guide**.
|
||||
::
|
||||
|
||||
1. Create a `main.css` file and import it in your `nuxt.config.ts` file:
|
||||
|
||||
::code-group
|
||||
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
```
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
css: ['~/assets/css/main.css']
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
2. Run the Tailwind CSS upgrade tool:
|
||||
|
||||
```bash
|
||||
npx @tailwindcss/upgrade
|
||||
```
|
||||
|
||||
### Update Nuxt UI
|
||||
|
||||
3. Install the latest version of the package:
|
||||
|
||||
::module-only
|
||||
#ui
|
||||
:::div
|
||||
|
||||
::::code-group{sync="pm"}
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @nuxt/ui@next
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @nuxt/ui@next
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @nuxt/ui@next
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @nuxt/ui@next
|
||||
```
|
||||
|
||||
::::
|
||||
|
||||
:::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
::::code-group{sync="pm"}
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @nuxt/ui-pro@next
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @nuxt/ui-pro@next
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @nuxt/ui-pro@next
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @nuxt/ui-pro@next
|
||||
```
|
||||
|
||||
::::
|
||||
|
||||
:::
|
||||
|
||||
::
|
||||
|
||||
4. Import it in your CSS:
|
||||
|
||||
::module-only
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```css [app/assets/css/main.css]{2}
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```css [app/assets/css/main.css]{2}
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
```
|
||||
|
||||
:::
|
||||
::
|
||||
|
||||
::module-only
|
||||
#ui
|
||||
|
||||
:::div
|
||||
5. Wrap you app with the [App](/components/app) component:
|
||||
:::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
5. Add the `@nuxt/ui-pro` module in your `nuxt.config.ts` file as it's no longer a layer:
|
||||
|
||||
```diff [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
- extends: ['@nuxt/ui-pro'],
|
||||
- modules: ['@nuxt/ui']
|
||||
+ modules: ['@nuxt/ui-pro']
|
||||
})
|
||||
```
|
||||
|
||||
6. Wrap you app with the [App](/components/app) component:
|
||||
:::
|
||||
|
||||
::
|
||||
|
||||
```vue [app.vue] {2,4}
|
||||
<template>
|
||||
<UApp>
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
## Changes from v2
|
||||
|
||||
Now that you have updated your project, you can start migrating your code. Here's a comprehensive list of all the breaking changes in Nuxt UI v3.
|
||||
|
||||
### Updated design system
|
||||
|
||||
In Nuxt UI v2, we had a mix between a design system with `primary`, `gray`, `error` aliases and all the colors from Tailwind CSS. We've replaced it with a proper [design system](/getting-started/theme#design-system) with 7 color aliases:
|
||||
|
||||
| Color | Default | Description |
|
||||
| --- | --- | --- |
|
||||
| `primary`{color="primary"} | `green` | Main brand color, used as the default color for components. |
|
||||
| `secondary`{color="secondary"} | `blue` | Secondary color to complement the primary color. |
|
||||
| `success`{color="success"} | `green` | Used for success states. |
|
||||
| `info`{color="info"} | `blue` | Used for informational states. |
|
||||
| `warning`{color="warning"} | `yellow` | Used for warning states. |
|
||||
| `error`{color="error"} | `red` | Used for form error validation states. |
|
||||
| `neutral` | `slate` | Neutral color for backgrounds, text, etc. |
|
||||
|
||||
This change introduces several breaking changes that you need to be aware of:
|
||||
|
||||
1. The `gray` color has been renamed to `neutral`
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <p class="text-gray-500 dark:text-gray-400" />
|
||||
+ <p class="text-neutral-500 dark:text-neutral-400" />
|
||||
</template>
|
||||
```
|
||||
|
||||
::note
|
||||
You can also use the new [design tokens](/getting-started/theme#neutral-palette) to handle light and dark mode:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <p class="text-gray-500 dark:text-gray-400" />
|
||||
+ <p class="text-(--ui-text-muted)" />
|
||||
|
||||
- <p class="text-gray-900 dark:text-white" />
|
||||
+ <p class="text-(--ui-text-highlighted)" />
|
||||
</template>
|
||||
```
|
||||
::
|
||||
|
||||
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" />
|
||||
+ <UButton color="neutral" />
|
||||
|
||||
- <UButton color="gray" />
|
||||
+ <UButton color="neutral" variant="subtle" />
|
||||
|
||||
- <UButton color="white" />
|
||||
+ <UButton color="neutral" variant="outline" />
|
||||
```
|
||||
|
||||
4. You can no longer use Tailwind CSS colors in the `color` props, use the new aliases instead:
|
||||
|
||||
```diff
|
||||
- <UButton color="red" />
|
||||
+ <UButton color="error" />
|
||||
```
|
||||
|
||||
::note{to="/getting-started/theme#colors"}
|
||||
Learn how to extend the design system to add new color aliases.
|
||||
::
|
||||
|
||||
5. The color configuration in `app.config.ts` has been moved into a `colors` object:
|
||||
|
||||
```diff
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
- primary: 'green',
|
||||
- gray: 'cool'
|
||||
+ colors: {
|
||||
+ primary: 'green',
|
||||
+ neutral: 'slate'
|
||||
+ }
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Updated theming system
|
||||
|
||||
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.
|
||||
|
||||
1. Update your [`app.config.ts`](/getting-started/theme#config) to override components with their new theme:
|
||||
|
||||
```diff
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
button: {
|
||||
- font: 'font-bold',
|
||||
- default: {
|
||||
- size: 'md',
|
||||
- color: 'primary'
|
||||
- }
|
||||
+ slots: {
|
||||
+ base: 'font-medium'
|
||||
+ },
|
||||
+ defaultVariants: {
|
||||
+ size: 'md',
|
||||
+ color: 'primary'
|
||||
+ }
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
2. Update your [`ui` props](/getting-started/theme#props) to override each component's slots using their new theme:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <UButton :ui="{ font: 'font-bold' }" />
|
||||
+ <UButton :ui="{ base: 'font-bold' }" />
|
||||
</template>
|
||||
```
|
||||
|
||||
::tip{to="/components/button#theme"}
|
||||
We can't detail all the changes here but you can check each component's theme in the **Theme** section.
|
||||
::
|
||||
|
||||
### Renamed components
|
||||
|
||||
We've renamed some Nuxt UI components to align with the Reka UI naming convention:
|
||||
|
||||
| v2 | v3 |
|
||||
| --- | --- |
|
||||
| `Divider` | [`Separator`](/components/separator) |
|
||||
| `Dropdown` | [`DropdownMenu`](/components/dropdown-menu) |
|
||||
| `FormGroup` | [`FormField`](/components/form-field) |
|
||||
| `Range` | [`Slider`](/components/slider) |
|
||||
| `Toggle` | [`Switch`](/components/switch) |
|
||||
| `Notification` | [`Toast`](/components/toast) |
|
||||
| `VerticalNavigation` | [`NavigationMenu`](/components/navigation-menu) with `orientation="vertical"` |
|
||||
| `HorizontalNavigation` | [`NavigationMenu`](/components/navigation-menu) with `orientation="horizontal"` |
|
||||
|
||||
::module-only
|
||||
#ui-pro
|
||||
:::div
|
||||
Here are the Nuxt UI Pro components that have been renamed or removed:
|
||||
|
||||
| v1 | v3 |
|
||||
| --- | --- |
|
||||
| `BlogList` | [`BlogPosts`](/components/blog-posts) |
|
||||
| `ColorModeToggle` | [`ColorModeSwitch`](/components/color-mode-switch) |
|
||||
| `DashboardCard` | Removed (use [`PageCard`](/components/page-card) instead) |
|
||||
| `DashboardLayout` | [`DashboardGroup`](/components/dashboard-group) |
|
||||
| `DashboardModal` | Removed (use [`Modal`](/components/modal) instead) |
|
||||
| `DashboardNavbarToggle` | [`DashboardSidebarToggle`](/components/dashboard-sidebar-toggle) |
|
||||
| `DashboardPage` | Removed |
|
||||
| `DashboardPanelContent` | Removed (use `#body` slot instead) |
|
||||
| `DashboardPanelHandle` | [`DashboardResizeHandle`](/components/dashboard-resize-handle) |
|
||||
| `DashboardSection` | Removed (use [`PageCard`](/components/page-card) instead) |
|
||||
| `DashboardSidebarLinks` | Removed (use [`NavigationMenu`](/components/navigation-menu) instead) |
|
||||
| `DashboardSlideover` | Removed (use [`Slideover`](/components/slideover) instead) |
|
||||
| `FooterLinks` | Removed (use [`NavigationMenu`](/components/navigation-menu) instead) |
|
||||
| `HeaderLinks` | Removed (use [`NavigationMenu`](/components/navigation-menu) instead) |
|
||||
| `LandingCard` | Removed (use [`PageCard`](/components/page-card) instead) |
|
||||
| `LandingCTA` | [`PageCTA`](/components/page-cta) |
|
||||
| `LandingFAQ` | Removed (use [`PageAccordion`](/components/page-accordion) instead) |
|
||||
| `LandingGrid` | Removed (use [`PageGrid`](/components/page-grid) instead) |
|
||||
| `LandingHero` | Removed (use [`PageHero`](/components/page-hero) instead) |
|
||||
| `LandingLogos` | [`PageLogos`](/components/page-logos) |
|
||||
| `LandingSection` | [`PageSection`](/components/page-section) |
|
||||
| `LandingTestimonial` | Removed (use [`PageCard`](/components/page-card#as-a-testimonial) instead) |
|
||||
| `NavigationAccordion` | [`ContentNavigation`](/components/content-navigation) |
|
||||
| `NavigationLinks` | [`ContentNavigation`](/components/content-navigation) |
|
||||
| `NavigationTree` | [`ContentNavigation`](/components/content-navigation) |
|
||||
| `PageError` | [`Error`](/components/error) |
|
||||
| `PricingCard` | [`PricingPlan`](/components/pricing-plan) |
|
||||
| `PricingGrid` | [`PricingPlans`](/components/pricing-plans) |
|
||||
| `PricingSwitch` | Removed (use [`Switch`](/components/switch) or [`Tabs`](/components/tabs) instead) |
|
||||
|
||||
:::
|
||||
|
||||
::
|
||||
|
||||
### Changed components
|
||||
|
||||
In addition to the renamed components, there are lots of changes to the components API. Let's detail the most important ones:
|
||||
|
||||
1. The `links` and `options` props have been renamed to `items` for consistency:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <USelect :options="countries" />
|
||||
+ <USelect :items="countries" />
|
||||
|
||||
- <UHorizontalNavigation :links="links" />
|
||||
+ <UNavigationMenu :items="links" />
|
||||
</template>
|
||||
```
|
||||
|
||||
::note
|
||||
This change affects the following components: `Breadcrumb`, `HorizontalNavigation`, `InputMenu`, `RadioGroup`, `Select`, `SelectMenu`, `VerticalNavigation`.
|
||||
::
|
||||
|
||||
2. The global `Modals`, `Slideovers` and `Notifications` components have been removed in favor the [App](/components/app) component:
|
||||
|
||||
```diff [app.vue]
|
||||
<template>
|
||||
+ <UApp>
|
||||
+ <NuxtPage />
|
||||
+ </UApp>
|
||||
- <UModals />
|
||||
- <USlideovers />
|
||||
- <UNotifications />
|
||||
</template>
|
||||
```
|
||||
|
||||
3. The `v-model:open` directive and `default-open` prop are now used to control visibility:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <UModal v-model="open" />
|
||||
+ <UModal v-model:open="open" />
|
||||
</template>
|
||||
```
|
||||
|
||||
::note
|
||||
This change affects the following components: `ContextMenu`, `Modal` and `Slideover` and enables controlling visibility for `InputMenu`, `Select`, `SelectMenu` and `Tooltip`.
|
||||
::
|
||||
|
||||
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">
|
||||
- const open = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
- <UButton label="Open" @click="open = true" />
|
||||
|
||||
- <UModal v-model="open">
|
||||
+ <UModal>
|
||||
+ <UButton label="Open" />
|
||||
|
||||
+ <template #content>
|
||||
<div class="p-4">
|
||||
<Placeholder class="h-48" />
|
||||
</div>
|
||||
+ </template>
|
||||
</UModal>
|
||||
</template>
|
||||
```
|
||||
|
||||
::note
|
||||
This change affects the following components: `Modal`, `Popover`, `Slideover`, `Tooltip`.
|
||||
::
|
||||
|
||||
5. A `#header`, `#body` and `#footer` slots have been added inside the `#content` slot like the `Card` component:
|
||||
|
||||
```diff
|
||||
<template>
|
||||
- <UModal>
|
||||
+ <UModal title="Title" description="Description">
|
||||
- <div class="p-4">
|
||||
+ <template #body>
|
||||
<Placeholder class="h-48" />
|
||||
+ </template>
|
||||
- </div>
|
||||
</UModal>
|
||||
</template>
|
||||
```
|
||||
|
||||
::note
|
||||
This change affects the following components: `Modal`, `Slideover`.
|
||||
::
|
||||
|
||||
6. The `Toast` component `timeout` prop has been renamed to `duration`:
|
||||
|
||||
```diff
|
||||
<script setup lang="ts">
|
||||
const toast = useToast()
|
||||
|
||||
- toast.add({ title: 'Invitation sent', timeout: 0 })
|
||||
+ toast.add({ title: 'Invitation sent', duration: 0 })
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
::warning
|
||||
This page is a work in progress, we'll improve it regularly.
|
||||
::
|
||||
@@ -6,7 +6,7 @@ navigation.icon: i-lucide-swatch-book
|
||||
|
||||
## Tailwind CSS
|
||||
|
||||
Nuxt UI v3 uses Tailwind CSS v4, you can read the [upgrade guide](https://tailwindcss.com/docs/upgrade-guide) to learn how to upgrade your project from v3 to v4.
|
||||
Nuxt UI v3 uses Tailwind CSS v4, you can read the official [upgrade guide](https://tailwindcss.com/docs/upgrade-guide#changes-from-v3) to learn about all the breaking changes.
|
||||
|
||||
### `@theme`
|
||||
|
||||
@@ -16,7 +16,7 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -44,7 +44,7 @@ Tailwind CSS v4 takes a CSS-first configuration approach, you now customize your
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -86,13 +86,13 @@ This can be useful when writing Tailwind CSS classes in markdown files with [`@n
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@source "../../content";
|
||||
/* Use this if using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
|
||||
@source "../../../content";
|
||||
/* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
|
||||
@source "../../content";
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -100,13 +100,13 @@ This can be useful when writing Tailwind CSS classes in markdown files with [`@n
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@source "../../content";
|
||||
/* Use this if using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
|
||||
@source "../../../content";
|
||||
/* Use this if you're not using compatibilityVersion: 4: https://nuxt.com/docs/getting-started/upgrade#opting-in-to-nuxt-4 */
|
||||
@source "../../content";
|
||||
```
|
||||
|
||||
:::
|
||||
@@ -142,7 +142,7 @@ Nuxt UI leverages Vite config to provide customizable color aliases based on [Ta
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
::div
|
||||
:::div
|
||||
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]
|
||||
@@ -156,14 +156,19 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
:::
|
||||
|
||||
#vue
|
||||
::module-only
|
||||
#ui
|
||||
|
||||
:::div
|
||||
You can configure these color aliases at runtime in your `vite.config.ts` file under the `ui.colors` key:
|
||||
|
||||
::::module-only
|
||||
|
||||
#ui
|
||||
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
@@ -183,11 +188,12 @@ export default defineConfig({
|
||||
]
|
||||
})
|
||||
```
|
||||
:::
|
||||
:::::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
You can configure these color aliases at runtime in your `vite.config.ts` file under the `uiPro.colors` key:
|
||||
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
@@ -207,9 +213,12 @@ export default defineConfig({
|
||||
]
|
||||
})
|
||||
```
|
||||
:::
|
||||
|
||||
::
|
||||
:::::
|
||||
|
||||
::::
|
||||
|
||||
:::
|
||||
|
||||
::
|
||||
|
||||
@@ -256,11 +265,17 @@ export default defineNuxtConfig({
|
||||
:::
|
||||
|
||||
#vue
|
||||
::module-only
|
||||
#ui
|
||||
|
||||
:::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.
|
||||
|
||||
::::module-only
|
||||
|
||||
#ui
|
||||
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
@@ -283,11 +298,11 @@ export default defineConfig({
|
||||
})
|
||||
```
|
||||
|
||||
:::
|
||||
:::::
|
||||
|
||||
#ui-pro
|
||||
:::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 `uiPro` plugin.
|
||||
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
@@ -311,9 +326,12 @@ export default defineConfig({
|
||||
})
|
||||
```
|
||||
|
||||
:::::
|
||||
|
||||
::::
|
||||
|
||||
:::
|
||||
|
||||
::
|
||||
::
|
||||
|
||||
### Tokens
|
||||
@@ -361,7 +379,7 @@ You can change which shade is used for each color on light and dark mode:
|
||||
#ui
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -379,7 +397,7 @@ You can change which shade is used for each color on light and dark mode:
|
||||
#ui-pro
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -415,7 +433,7 @@ You cannot set `primary: 'black'`{lang="ts-type"} in your [`vite.config.ts`](#co
|
||||
#ui
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -433,7 +451,7 @@ You cannot set `primary: 'black'`{lang="ts-type"} in your [`vite.config.ts`](#co
|
||||
#ui-pro
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -545,7 +563,7 @@ You can customize these CSS variables to tailor the appearance of your applicati
|
||||
#ui
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -565,7 +583,7 @@ You can customize these CSS variables to tailor the appearance of your applicati
|
||||
#ui-pro
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -606,7 +624,7 @@ You can customize the default radius value using the default Tailwind CSS variab
|
||||
#ui
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -620,7 +638,7 @@ You can customize the default radius value using the default Tailwind CSS variab
|
||||
#ui-pro
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -651,7 +669,7 @@ You can customize the default container width using the default Tailwind CSS var
|
||||
#ui
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -669,7 +687,7 @@ You can customize the default container width using the default Tailwind CSS var
|
||||
#ui-pro
|
||||
:::div{class="*:!mb-0 *:!mt-2.5"}
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
@@ -820,14 +838,14 @@ You can explore the theme for each component in two ways:
|
||||
|
||||
- Check the `Theme` section in the documentation of each individual component.
|
||||
- Browse the source code directly in the GitHub repository at [`v3/src/theme`](https://github.com/nuxt/ui/tree/v3/src/theme).
|
||||
|
||||
::
|
||||
|
||||
### Config
|
||||
|
||||
::framework-only
|
||||
#nuxt
|
||||
::div
|
||||
|
||||
:::div
|
||||
You can override the theme of components globally inside your `app.config.ts` by using the exact same structure as the theme object.
|
||||
|
||||
Let's say you want to change the font weight of all your buttons, you can do it like this:
|
||||
@@ -844,17 +862,21 @@ export default defineAppConfig({
|
||||
})
|
||||
```
|
||||
|
||||
::
|
||||
:::
|
||||
|
||||
#vue
|
||||
|
||||
::module-only
|
||||
#ui
|
||||
:::div
|
||||
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
|
||||
|
||||
Let's say you want to change the font weight of all your buttons, you can do it like this:
|
||||
|
||||
::::module-only
|
||||
|
||||
#ui
|
||||
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
@@ -875,13 +897,12 @@ export default defineConfig({
|
||||
]
|
||||
})
|
||||
```
|
||||
:::
|
||||
|
||||
:::::
|
||||
|
||||
#ui-pro
|
||||
:::div
|
||||
You can override the theme of components globally inside your `vite.config.ts` by using the exact same structure as the theme object.
|
||||
|
||||
Let's say you want to change the font weight of all your buttons, you can do it like this:
|
||||
:::::div
|
||||
|
||||
```ts [vite.config.ts]
|
||||
import { defineConfig } from 'vite'
|
||||
@@ -903,8 +924,12 @@ export default defineConfig({
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
:::::
|
||||
|
||||
::::
|
||||
|
||||
:::
|
||||
::
|
||||
|
||||
::
|
||||
|
||||
|
||||
@@ -99,9 +99,8 @@ In your `nuxt.config.ts`, add an item in `icon.customCollections`:
|
||||
|
||||
```ts
|
||||
export default defineNuxtConfig({
|
||||
modules: [
|
||||
'@nuxt/ui'
|
||||
],
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
icon: {
|
||||
customCollections: [{
|
||||
prefix: 'custom',
|
||||
|
||||
@@ -18,7 +18,7 @@ Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/font
|
||||
#ui
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui";
|
||||
|
||||
@@ -32,7 +32,7 @@ Nuxt UI automatically registers the [`@nuxt/fonts`](https://github.com/nuxt/font
|
||||
#ui-pro
|
||||
:::div
|
||||
|
||||
```css [assets/css/main.css]
|
||||
```css [app/assets/css/main.css]
|
||||
@import "tailwindcss" theme(static);
|
||||
@import "@nuxt/ui-pro";
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ You can disable the `@nuxtjs/color-mode` module with the `ui.colorMode` option i
|
||||
|
||||
```ts [nuxt.config.ts]
|
||||
export default defineNuxtConfig({
|
||||
modules: ['@nuxt/ui'],
|
||||
css: ['~/assets/css/main.css'],
|
||||
ui: {
|
||||
colorMode: false
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ export default defineNuxtConfig({
|
||||
'@nuxt/ui',
|
||||
'@nuxtjs/i18n'
|
||||
],
|
||||
css: ['~/assets/css/main.css'],
|
||||
i18n: {
|
||||
locales: [{
|
||||
code: 'de',
|
||||
|
||||
@@ -29,23 +29,6 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### Link
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- label
|
||||
- target
|
||||
props:
|
||||
to: https://github.com/nuxt/ui
|
||||
target: _blank
|
||||
slots:
|
||||
default: Button
|
||||
---
|
||||
::
|
||||
|
||||
### Color
|
||||
|
||||
Use the `color` prop to change the color of the Button.
|
||||
@@ -160,6 +143,96 @@ props:
|
||||
---
|
||||
::
|
||||
|
||||
### Link
|
||||
|
||||
You can pass any property from the [Link](/components/link#props) component such as `to`, `target`, etc.
|
||||
|
||||
::component-code
|
||||
---
|
||||
ignore:
|
||||
- target
|
||||
props:
|
||||
to: https://github.com/nuxt/ui
|
||||
target: _blank
|
||||
slots:
|
||||
default: Button
|
||||
---
|
||||
::
|
||||
|
||||
When the Button is a link or when using the `active` prop, you can use the `active-color` and `active-variant` props to customize the active state.
|
||||
|
||||
::component-code
|
||||
---
|
||||
prettier: true
|
||||
ignore:
|
||||
- color
|
||||
- variant
|
||||
items:
|
||||
activeColor:
|
||||
- primary
|
||||
- secondary
|
||||
- success
|
||||
- info
|
||||
- warning
|
||||
- error
|
||||
- neutral
|
||||
activeVariant:
|
||||
- solid
|
||||
- outline
|
||||
- soft
|
||||
- subtle
|
||||
- ghost
|
||||
- link
|
||||
props:
|
||||
active: true
|
||||
color: neutral
|
||||
variant: outline
|
||||
activeColor: primary
|
||||
activeVariant: solid
|
||||
slots:
|
||||
default: |
|
||||
|
||||
Button
|
||||
---
|
||||
|
||||
Button
|
||||
::
|
||||
|
||||
You can also use the `active-class` and `inactive-class` props to customize the active state.
|
||||
|
||||
::component-code
|
||||
---
|
||||
props:
|
||||
active: true
|
||||
activeClass: 'font-bold'
|
||||
inactiveClass: 'font-light'
|
||||
slots:
|
||||
default: Button
|
||||
---
|
||||
|
||||
Button
|
||||
::
|
||||
|
||||
::tip
|
||||
You can configure these styles globally in your `app.config.ts` file under the `ui.button.variants.active` key.
|
||||
|
||||
```ts
|
||||
export default defineAppConfig({
|
||||
ui: {
|
||||
button: {
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
base: 'font-bold'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
::
|
||||
|
||||
### Loading
|
||||
|
||||
Use the `loading` prop to show a loading icon and disable the Button.
|
||||
|
||||
@@ -209,6 +209,20 @@ name: 'calendar-min-max-dates-example'
|
||||
---
|
||||
::
|
||||
|
||||
### With other calendar systems
|
||||
|
||||
You can use other calenders from `@internationalized/date` to implement a different calendar system.
|
||||
|
||||
::component-example
|
||||
---
|
||||
name: 'calendar-other-system-example'
|
||||
---
|
||||
::
|
||||
|
||||
::note{to="https://react-spectrum.adobe.com/internationalized/date/Calendar.html#implementations"}
|
||||
You can check all the available calendars on `@internationalized/date` docs.
|
||||
::
|
||||
|
||||
### As a DatePicker
|
||||
|
||||
Use a [Button](/components/button) and a [Popover](/components/popover) component to create a date picker.
|
||||
|
||||
@@ -334,7 +334,7 @@ name: 'modal-programmatic-example'
|
||||
::
|
||||
|
||||
::tip
|
||||
You can close the modal within the modal component by calling `modal.close()`.
|
||||
You can close the modal within the modal component by emitting `"emit('close')`.
|
||||
::
|
||||
|
||||
### Nested modals
|
||||
|
||||
@@ -333,7 +333,7 @@ name: 'slideover-programmatic-example'
|
||||
::
|
||||
|
||||
::tip
|
||||
You can close the slideover within the slideover component by calling `slideover.close()`.
|
||||
You can close the slideover within the slideover component by emitting `emit('close')`.
|
||||
::
|
||||
|
||||
### Nested slideovers
|
||||
|
||||
@@ -4,11 +4,11 @@ category: data
|
||||
links:
|
||||
- label: Tree
|
||||
icon: i-custom-reka-ui
|
||||
to: https://www.reka-ui.com/components/tree.html
|
||||
to: https://reka-ui.com/docs/components/tree
|
||||
- label: GitHub
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/ui/tree/v3/src/runtime/components/Tree.vue
|
||||
navigation.badge: Soon
|
||||
navigation.badge: New
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -85,8 +85,8 @@ export default defineNuxtConfig({
|
||||
},
|
||||
|
||||
routeRules: {
|
||||
'/': { redirect: '/getting-started', prerender: false },
|
||||
'/getting-started/installation': { redirect: '/getting-started/installation/nuxt', prerender: false },
|
||||
'/getting-started/installation/pro': { redirect: '/getting-started/installation/pro/nuxt', prerender: false },
|
||||
'/getting-started/icons': { redirect: '/getting-started/icons/nuxt', prerender: false },
|
||||
'/getting-started/color-mode': { redirect: '/getting-started/color-mode/nuxt', prerender: false },
|
||||
'/getting-started/i18n': { redirect: '/getting-started/i18n/nuxt', prerender: false },
|
||||
|
||||
@@ -4,27 +4,27 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@iconify-json/logos": "^1.2.4",
|
||||
"@iconify-json/lucide": "^1.2.27",
|
||||
"@iconify-json/simple-icons": "^1.2.26",
|
||||
"@iconify-json/lucide": "^1.2.28",
|
||||
"@iconify-json/simple-icons": "^1.2.27",
|
||||
"@iconify-json/vscode-icons": "^1.2.16",
|
||||
"@nuxt/content": "^3.2.2",
|
||||
"@nuxt/content": "^3.3.0",
|
||||
"@nuxt/image": "^1.9.0",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@c02527f",
|
||||
"@nuxthub/core": "^0.8.17",
|
||||
"@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@02b7ea0",
|
||||
"@nuxthub/core": "^0.8.18",
|
||||
"@nuxtjs/plausible": "^1.2.0",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"@vueuse/nuxt": "^12.7.0",
|
||||
"@vueuse/nuxt": "^12.8.2",
|
||||
"joi": "^17.13.3",
|
||||
"motion": "^12.4.7",
|
||||
"motion-v": "0.11.0-beta.4",
|
||||
"motion": "^12.4.10",
|
||||
"motion-v": "0.11.1",
|
||||
"nuxt": "^3.15.4",
|
||||
"nuxt-component-meta": "^0.10.0",
|
||||
"nuxt-llms": "^0.1.0",
|
||||
"nuxt-og-image": "^4.1.4",
|
||||
"prettier": "^3.5.2",
|
||||
"shiki-transformer-color-highlight": "^0.2.0",
|
||||
"nuxt-og-image": "^4.2.0",
|
||||
"prettier": "^3.5.3",
|
||||
"shiki-transformer-color-highlight": "^1.0.0",
|
||||
"superstruct": "^2.0.2",
|
||||
"ufo": "^1.5.4",
|
||||
"valibot": "^0.42.1",
|
||||
@@ -32,6 +32,6 @@
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.111.0"
|
||||
"wrangler": "^3.114.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
docs/public/pro/blocks/image1.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/public/pro/blocks/image10.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/public/pro/blocks/image11.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/public/pro/blocks/image12.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
docs/public/pro/blocks/image13.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
docs/public/pro/blocks/image14.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/public/pro/blocks/image15.png
Normal file
|
After Width: | Height: | Size: 354 KiB |
BIN
docs/public/pro/blocks/image16.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
docs/public/pro/blocks/image17.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/public/pro/blocks/image2.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
docs/public/pro/blocks/image3.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
docs/public/pro/blocks/image4.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
docs/public/pro/blocks/image5.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
docs/public/pro/blocks/image6.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/public/pro/blocks/image7.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
docs/public/pro/blocks/image8.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
docs/public/pro/blocks/image9.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
18
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "@nuxt/ui",
|
||||
"description": "A UI Library for Modern Web Apps, powered by Vue & Tailwind CSS.",
|
||||
"version": "3.0.0-beta.2",
|
||||
"packageManager": "pnpm@10.5.2",
|
||||
"packageManager": "pnpm@10.6.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nuxt/ui.git"
|
||||
@@ -86,12 +86,12 @@
|
||||
"@nuxt/kit": "^3.15.4",
|
||||
"@nuxt/schema": "^3.15.4",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@tailwindcss/postcss": "^4.0.9",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"@tailwindcss/postcss": "^4.0.12",
|
||||
"@tailwindcss/vite": "^4.0.12",
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@unhead/vue": "^1.11.20",
|
||||
"@vueuse/core": "^12.7.0",
|
||||
"@vueuse/integrations": "^12.7.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"@vueuse/integrations": "^12.8.2",
|
||||
"colortranslator": "^4.1.0",
|
||||
"consola": "^3.4.0",
|
||||
"defu": "^6.1.4",
|
||||
@@ -106,12 +106,12 @@
|
||||
"knitwork": "^1.2.0",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.4",
|
||||
"ohash": "^1.1.4",
|
||||
"ohash": "^2.0.11",
|
||||
"pathe": "^2.0.3",
|
||||
"reka-ui": "^2.0.2",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-variants": "^0.3.1",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"tailwindcss": "^4.0.12",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"unplugin": "^2.2.0",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
@@ -121,7 +121,7 @@
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint-config": "^1.1.0",
|
||||
"@nuxt/module-builder": "^0.8.4",
|
||||
"@nuxt/test-utils": "^3.17.0",
|
||||
"@nuxt/test-utils": "^3.17.1",
|
||||
"@release-it/conventional-changelog": "^10.0.0",
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
@@ -133,7 +133,7 @@
|
||||
"release-it": "^18.1.2",
|
||||
"superstruct": "^2.0.2",
|
||||
"valibot": "^0.42.1",
|
||||
"vitest": "^3.0.7",
|
||||
"vitest": "^3.0.8",
|
||||
"vitest-environment-nuxt": "^1.0.1",
|
||||
"vue-tsc": "^2.2.0",
|
||||
"yup": "^1.6.1",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^6.2.0",
|
||||
"vite": "^6.2.1",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { h, resolveComponent } from 'vue'
|
||||
import { upperFirst } from 'scule'
|
||||
import type { TableColumn } from '@nuxt/ui'
|
||||
import type { TableColumn, TableRow } from '@nuxt/ui'
|
||||
import { getPaginationRowModel } from '@tanstack/vue-table'
|
||||
|
||||
const UButton = resolveComponent('UButton')
|
||||
@@ -279,6 +279,10 @@ function randomize() {
|
||||
data.value = [...data.value].sort(() => Math.random() - 0.5)
|
||||
}
|
||||
|
||||
function onSelect(row: TableRow<Payment>) {
|
||||
console.log(row)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
@@ -337,6 +341,7 @@ onMounted(() => {
|
||||
}"
|
||||
sticky
|
||||
class="border border-(--ui-border-accented) rounded-(--ui-radius)"
|
||||
@select="onSelect"
|
||||
>
|
||||
<template #expanded="{ row }">
|
||||
<pre>{{ row.original }}</pre>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
"generate": "nuxi generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/lucide": "^1.2.27",
|
||||
"@iconify-json/simple-icons": "^1.2.26",
|
||||
"@iconify-json/lucide": "^1.2.28",
|
||||
"@iconify-json/simple-icons": "^1.2.27",
|
||||
"@nuxt/ui": "latest",
|
||||
"@nuxthub/core": "^0.8.17",
|
||||
"@nuxthub/core": "^0.8.18",
|
||||
"nuxt": "^3.15.4"
|
||||
}
|
||||
}
|
||||
|
||||
1785
pnpm-lock.yaml
generated
@@ -12,7 +12,13 @@ const accordion = tv({ extend: tv(theme), ...(appConfigAccordion.ui?.accordion |
|
||||
|
||||
export interface AccordionItem {
|
||||
label?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
trailingIcon?: string
|
||||
slot?: string
|
||||
content?: string
|
||||
@@ -31,6 +37,7 @@ export interface AccordionProps<T> extends Pick<AccordionRootProps, 'collapsible
|
||||
/**
|
||||
* The icon displayed on the right side of the trigger.
|
||||
* @defaultValue appConfig.ui.icons.chevronDown
|
||||
* @IconifyIcon
|
||||
*/
|
||||
trailingIcon?: string
|
||||
/**
|
||||
|
||||
@@ -20,10 +20,23 @@ export interface AlertProps {
|
||||
as?: any
|
||||
title?: string
|
||||
description?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
avatar?: AvatarProps
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: AlertVariants['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: AlertVariants['variant']
|
||||
/**
|
||||
* The orientation between the content and the actions.
|
||||
* @defaultValue 'vertical'
|
||||
*/
|
||||
orientation?: AlertVariants['orientation']
|
||||
/**
|
||||
* Display a list of actions:
|
||||
@@ -42,6 +55,7 @@ export interface AlertProps {
|
||||
/**
|
||||
* The icon displayed in the close button.
|
||||
* @defaultValue appConfig.ui.icons.close
|
||||
* @IconifyIcon
|
||||
*/
|
||||
closeIcon?: string
|
||||
class?: any
|
||||
|
||||
@@ -19,10 +19,17 @@ export interface AvatarProps {
|
||||
as?: any
|
||||
src?: string
|
||||
alt?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
text?: string
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: AvatarVariants['size']
|
||||
class?: any
|
||||
style?: any
|
||||
ui?: Partial<typeof avatar.slots>
|
||||
}
|
||||
|
||||
@@ -77,7 +84,7 @@ function onError() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })">
|
||||
<Primitive :as="as" :class="ui.root({ class: [props.class, props.ui?.root] })" :style="props.style">
|
||||
<component
|
||||
:is="ImageComponent"
|
||||
v-if="src && !error"
|
||||
|
||||
@@ -17,6 +17,9 @@ export interface AvatarGroupProps {
|
||||
* @defaultValue 'div'
|
||||
*/
|
||||
as?: any
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: AvatarGroupVariants['size']
|
||||
/**
|
||||
* The maximum number of avatars to display.
|
||||
|
||||
@@ -20,8 +20,17 @@ export interface BadgeProps extends Omit<UseComponentIconsProps, 'loading' | 'lo
|
||||
*/
|
||||
as?: any
|
||||
label?: string | number
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: BadgeVariants['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: BadgeVariants['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: BadgeVariants['size']
|
||||
class?: any
|
||||
ui?: Partial<typeof badge.slots>
|
||||
|
||||
@@ -12,6 +12,9 @@ const breadcrumb = tv({ extend: tv(theme), ...(appConfigBreadcrumb.ui?.breadcrum
|
||||
|
||||
export interface BreadcrumbItem extends Omit<LinkProps, 'raw' | 'custom'> {
|
||||
label?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
avatar?: AvatarProps
|
||||
slot?: string
|
||||
@@ -27,6 +30,7 @@ export interface BreadcrumbProps<T> {
|
||||
/**
|
||||
* The icon to use as a separator.
|
||||
* @defaultValue appConfig.ui.icons.chevronRight
|
||||
* @IconifyIcon
|
||||
*/
|
||||
separatorIcon?: string
|
||||
/**
|
||||
|
||||
@@ -17,8 +17,19 @@ type ButtonVariants = VariantProps<typeof button>
|
||||
|
||||
export interface ButtonProps extends UseComponentIconsProps, Omit<LinkProps, 'raw' | 'custom'> {
|
||||
label?: string
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: ButtonVariants['color']
|
||||
activeColor?: ButtonVariants['color']
|
||||
/**
|
||||
* @defaultValue 'solid'
|
||||
*/
|
||||
variant?: ButtonVariants['variant']
|
||||
activeVariant?: ButtonVariants['variant']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ButtonVariants['size']
|
||||
/** Render the button with equal padding on all sides. */
|
||||
square?: boolean
|
||||
@@ -50,7 +61,11 @@ import UIcon from './Icon.vue'
|
||||
import UAvatar from './Avatar.vue'
|
||||
import ULink from './Link.vue'
|
||||
|
||||
const props = defineProps<ButtonProps>()
|
||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||
active: undefined,
|
||||
activeClass: '',
|
||||
inactiveClass: ''
|
||||
})
|
||||
const slots = defineSlots<ButtonSlots>()
|
||||
|
||||
const linkProps = useForwardProps(pickLinkProps(props))
|
||||
@@ -78,7 +93,19 @@ const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponen
|
||||
computed(() => ({ ...props, loading: isLoading.value }))
|
||||
)
|
||||
|
||||
const ui = computed(() => button({
|
||||
const ui = computed(() => tv({
|
||||
extend: button,
|
||||
variants: {
|
||||
active: {
|
||||
true: {
|
||||
base: props.activeClass
|
||||
},
|
||||
false: {
|
||||
base: props.inactiveClass
|
||||
}
|
||||
}
|
||||
}
|
||||
})({
|
||||
color: props.color,
|
||||
variant: props.variant,
|
||||
size: buttonSize.value,
|
||||
@@ -93,26 +120,37 @@ const ui = computed(() => button({
|
||||
|
||||
<template>
|
||||
<ULink
|
||||
v-slot="{ active, ...slotProps }"
|
||||
:type="type"
|
||||
:disabled="disabled || isLoading"
|
||||
:class="ui.base({ class: [props.class, props.ui?.base] })"
|
||||
v-bind="omit(linkProps, ['type', 'disabled'])"
|
||||
raw
|
||||
@click="onClickWrapper"
|
||||
custom
|
||||
>
|
||||
<slot name="leading">
|
||||
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon })" />
|
||||
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar })" />
|
||||
</slot>
|
||||
<ULinkBase
|
||||
v-bind="slotProps"
|
||||
:class="ui.base({
|
||||
class: [props.class, props.ui?.base],
|
||||
active,
|
||||
...(active && activeVariant ? { variant: activeVariant } : {}),
|
||||
...(active && activeColor ? { color: activeColor } : {})
|
||||
})"
|
||||
@click="onClickWrapper"
|
||||
>
|
||||
<slot name="leading">
|
||||
<UIcon v-if="isLeading && leadingIconName" :name="leadingIconName" :class="ui.leadingIcon({ class: props.ui?.leadingIcon, active })" />
|
||||
<UAvatar v-else-if="!!avatar" :size="((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])" v-bind="avatar" :class="ui.leadingAvatar({ class: props.ui?.leadingAvatar, active })" />
|
||||
</slot>
|
||||
|
||||
<slot>
|
||||
<span v-if="label" :class="ui.label({ class: props.ui?.label })">
|
||||
{{ label }}
|
||||
</span>
|
||||
</slot>
|
||||
<slot>
|
||||
<span v-if="label" :class="ui.label({ class: props.ui?.label, active })">
|
||||
{{ label }}
|
||||
</span>
|
||||
</slot>
|
||||
|
||||
<slot name="trailing">
|
||||
<UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
|
||||
</slot>
|
||||
<slot name="trailing">
|
||||
<UIcon v-if="isTrailing && trailingIconName" :name="trailingIconName" :class="ui.trailingIcon({ class: props.ui?.trailingIcon, active })" />
|
||||
</slot>
|
||||
</ULinkBase>
|
||||
</ULink>
|
||||
</template>
|
||||
|
||||
@@ -17,6 +17,9 @@ export interface ButtonGroupProps {
|
||||
* @defaultValue 'div'
|
||||
*/
|
||||
as?: any
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ButtonGroupVariants['size']
|
||||
/**
|
||||
* The orientation the buttons are laid out.
|
||||
|
||||
@@ -29,24 +29,34 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Omi
|
||||
/**
|
||||
* The icon to use for the next year control.
|
||||
* @defaultValue appConfig.ui.icons.chevronDoubleRight
|
||||
* @IconifyIcon
|
||||
*/
|
||||
nextYearIcon?: string
|
||||
/**
|
||||
* The icon to use for the next month control.
|
||||
* @defaultValue appConfig.ui.icons.chevronRight
|
||||
* @IconifyIcon
|
||||
*/
|
||||
nextMonthIcon?: string
|
||||
/**
|
||||
* The icon to use for the previous year control.
|
||||
* @defaultValue appConfig.ui.icons.chevronDoubleLeft
|
||||
* @IconifyIcon
|
||||
*/
|
||||
prevYearIcon?: string
|
||||
/**
|
||||
* The icon to use for the previous month control.
|
||||
* @defaultValue appConfig.ui.icons.chevronLeft
|
||||
* @IconifyIcon
|
||||
*/
|
||||
prevMonthIcon?: string
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: CalendarVariants['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: CalendarVariants['size']
|
||||
/** Whether or not a range of dates can be selected */
|
||||
range?: R & boolean
|
||||
|
||||
@@ -17,6 +17,9 @@ export interface CardProps {
|
||||
* @defaultValue 'div'
|
||||
*/
|
||||
as?: any
|
||||
/**
|
||||
* @defaultValue 'outline'
|
||||
*/
|
||||
variant?: CardVariants['variant']
|
||||
class?: any
|
||||
ui?: Partial<typeof card.slots>
|
||||
|
||||
@@ -35,6 +35,7 @@ export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'conta
|
||||
/**
|
||||
* The icon displayed in the prev button.
|
||||
* @defaultValue appConfig.ui.icons.arrowLeft
|
||||
* @IconifyIcon
|
||||
*/
|
||||
prevIcon?: string
|
||||
/**
|
||||
@@ -45,6 +46,7 @@ export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'conta
|
||||
/**
|
||||
* The icon displayed in the next button.
|
||||
* @defaultValue appConfig.ui.icons.arrowRight
|
||||
* @IconifyIcon
|
||||
*/
|
||||
nextIcon?: string
|
||||
/**
|
||||
@@ -57,6 +59,10 @@ export interface CarouselProps<T> extends Omit<EmblaOptionsType, 'axis' | 'conta
|
||||
* @defaultValue false
|
||||
*/
|
||||
dots?: boolean
|
||||
/**
|
||||
* The orientation of the carousel.
|
||||
* @defaultValue 'horizontal'
|
||||
*/
|
||||
orientation?: CarouselVariants['orientation']
|
||||
items?: T[]
|
||||
/**
|
||||
|
||||
@@ -20,16 +20,24 @@ export interface CheckboxProps extends Pick<CheckboxRootProps, 'disabled' | 'req
|
||||
as?: any
|
||||
label?: string
|
||||
description?: string
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: CheckboxVariants['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: CheckboxVariants['size']
|
||||
/**
|
||||
* The icon displayed when checked.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
/**
|
||||
* The icon displayed when the checkbox is indeterminate.
|
||||
* @defaultValue appConfig.ui.icons.minus
|
||||
* @IconifyIcon
|
||||
*/
|
||||
indeterminateIcon?: string
|
||||
class?: any
|
||||
|
||||
@@ -19,8 +19,18 @@ export interface ChipProps {
|
||||
as?: any
|
||||
/** Display some text inside the chip. */
|
||||
text?: string | number
|
||||
/**
|
||||
* @defaultValue 'primary'
|
||||
*/
|
||||
color?: ChipVariants['color']
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ChipVariants['size']
|
||||
/**
|
||||
* The position of the chip.
|
||||
* @defaultValue 'top-right'
|
||||
*/
|
||||
position?: ChipVariants['position']
|
||||
/** When `true`, keep the chip inside the component for rounded elements. */
|
||||
inset?: boolean
|
||||
|
||||
@@ -63,6 +63,9 @@ export type ColorPickerProps = {
|
||||
* @defaultValue 'hex'
|
||||
*/
|
||||
format?: 'hex' | 'rgb' | 'hsl' | 'cmyk' | 'lab'
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ColorPickerVariants['size']
|
||||
class?: any
|
||||
ui?: Partial<typeof colorPicker.slots>
|
||||
|
||||
@@ -18,6 +18,9 @@ export interface CommandPaletteItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
|
||||
prefix?: string
|
||||
label?: string
|
||||
suffix?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
avatar?: AvatarProps
|
||||
chip?: ChipProps
|
||||
@@ -42,7 +45,10 @@ export interface CommandPaletteGroup<T> {
|
||||
ignoreFilter?: boolean
|
||||
/** Filter group items after the search happened. */
|
||||
postFilter?: (searchTerm: string, items: T[]) => T[]
|
||||
/** The icon displayed when an item is highlighted. */
|
||||
/**
|
||||
* The icon displayed when an item is highlighted.
|
||||
* @IconifyIcon
|
||||
*/
|
||||
highlightedIcon?: string
|
||||
}
|
||||
|
||||
@@ -55,11 +61,13 @@ export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multi
|
||||
/**
|
||||
* The icon displayed in the input.
|
||||
* @defaultValue appConfig.ui.icons.search
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
/**
|
||||
* The icon displayed when an item is selected.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
* @IconifyIcon
|
||||
*/
|
||||
selectedIcon?: string
|
||||
/**
|
||||
@@ -82,6 +90,7 @@ export interface CommandPaletteProps<G, T> extends Pick<ListboxRootProps, 'multi
|
||||
/**
|
||||
* The icon displayed in the close button.
|
||||
* @defaultValue appConfig.ui.icons.close
|
||||
* @IconifyIcon
|
||||
*/
|
||||
closeIcon?: string
|
||||
groups?: G[]
|
||||
|
||||
@@ -16,6 +16,9 @@ type ContextMenuVariants = VariantProps<typeof contextMenu>
|
||||
|
||||
export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
||||
label?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
color?: ContextMenuVariants['color']
|
||||
avatar?: AvatarProps
|
||||
@@ -38,22 +41,28 @@ export interface ContextMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custo
|
||||
}
|
||||
|
||||
export interface ContextMenuProps<T> extends Omit<ContextMenuRootProps, 'dir'> {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: ContextMenuVariants['size']
|
||||
items?: T[] | T[][]
|
||||
/**
|
||||
* The icon displayed when an item is checked.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
* @IconifyIcon
|
||||
*/
|
||||
checkedIcon?: string
|
||||
/**
|
||||
* The icon displayed when an item is loading.
|
||||
* @defaultValue appConfig.ui.icons.loading
|
||||
* @IconifyIcon
|
||||
*/
|
||||
loadingIcon?: string
|
||||
/**
|
||||
* The icon displayed when the item is an external link.
|
||||
* Set to `false` to hide the external icon.
|
||||
* @defaultValue appConfig.ui.icons.external
|
||||
* @IconifyIcon
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
/** The content of the menu. */
|
||||
|
||||
@@ -11,8 +11,17 @@ interface ContextMenuContentProps<T> extends Omit<RekaContextMenuContentProps, '
|
||||
portal?: boolean
|
||||
sub?: boolean
|
||||
labelKey: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
checkedIcon?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
loadingIcon?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
class?: any
|
||||
ui: typeof _contextMenu
|
||||
|
||||
@@ -16,6 +16,9 @@ type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
|
||||
|
||||
export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
|
||||
label?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
icon?: string
|
||||
color?: DropdownMenuVariants['color']
|
||||
avatar?: AvatarProps
|
||||
@@ -38,22 +41,28 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
|
||||
}
|
||||
|
||||
export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
|
||||
/**
|
||||
* @defaultValue 'md'
|
||||
*/
|
||||
size?: DropdownMenuVariants['size']
|
||||
items?: T[] | T[][]
|
||||
/**
|
||||
* The icon displayed when an item is checked.
|
||||
* @defaultValue appConfig.ui.icons.check
|
||||
* @IconifyIcon
|
||||
*/
|
||||
checkedIcon?: string
|
||||
/**
|
||||
* The icon displayed when an item is loading.
|
||||
* @defaultValue appConfig.ui.icons.loading
|
||||
* @IconifyIcon
|
||||
*/
|
||||
loadingIcon?: string
|
||||
/**
|
||||
* The icon displayed when the item is an external link.
|
||||
* Set to `false` to hide the external icon.
|
||||
* @defaultValue appConfig.ui.icons.external
|
||||
* @IconifyIcon
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,17 @@ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps,
|
||||
portal?: boolean
|
||||
sub?: boolean
|
||||
labelKey: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
checkedIcon?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
loadingIcon?: string
|
||||
/**
|
||||
* @IconifyIcon
|
||||
*/
|
||||
externalIcon?: boolean | string
|
||||
class?: any
|
||||
ui: typeof _dropdownMenu
|
||||
|
||||