Examples
Basic
Prop | Default | Type | Description |
---|---|---|---|
title | - | string | The card title. |
description | - | string | The card description. |
Create account now
Fill in the form below to create an account.
<template>
<div class="grid w-full place-items-center">
<NCard
title="Create account now"
description="Fill in the form below to create an account."
class="sm:w-100"
>
<div class="flex flex-col gap-4">
<NFormGroup
label="Email"
>
<NInput
placeholder="phojrengel@gmail.com"
leading="i-heroicons-envelope-20-solid"
/>
</NFormGroup>
<NFormGroup
label="Password"
>
<NInput
type="password"
placeholder="*********"
leading="i-heroicons-key-20-solid"
/>
</NFormGroup>
</div>
<template #footer>
<div class="mt-2 w-full flex justify-end gap-4">
<NButton btn="soft-gray block">
Login
</NButton>
<NButton btn="solid-black block">
Create an account
</NButton>
</div>
</template>
</NCard>
</div>
</template>
Variant
Prop | Default | Type | Description |
---|---|---|---|
card | outline | {variant} | The variant of the card. |
Variant | Description |
---|---|
outline | The default variant. |
soft | The soft variant. |
~ | The unstyle or base variant |
Outline variant
Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.
Soft variant
Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.
Base variant
Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.
<template>
<div class="flex flex-col gap-4">
<NCard
title="Outline variant"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum."
card="outline"
/>
<NCard
title="Soft variant"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum."
card="soft"
/>
<NCard
title="Base variant"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum."
card="~"
/>
</div>
</template>
Color
Prop | Default | Type | Description |
---|---|---|---|
card | {variant}-primary | {variant}-{color} | The color of the card. |
outline-purple
Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.
soft-info
Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum.
<template>
<div class="flex flex-col gap-4">
<NCard
title="outline-purple"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum."
card="outline-purple"
/>
<NCard
title="soft-info"
description="Lorem ipsum dolor sit amet consectetur adipisicing elit. aliquid pariatur, ipsum similique veniam quo totam eius aperiam dolorum."
card="soft-info"
/>
</div>
</template>
Slots
Name | Props | Description |
---|---|---|
header | - | The header slot. |
default | - | The default slot. |
title | - | The title slot. |
description | - | The description slot. |
footer | - | The footer slot. |
Advanced Card
This example shows how you can use named templates for customization
<template>
<div
class="grid w-full place-items-center"
>
<NCard class="sm:w-100">
<template #header>
<div h-40>
<img
src="https://images.unsplash.com/photo-1700527736181-67795948c0b1?q=80&w=2352&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="Card image"
class="h-full w-full object-cover"
>
</div>
</template>
<template #title>
<div class="flex items-center justify-between">
Advanced Card
<NButton
size="sm"
icon
class="rounded-full"
btn="ghost-gray"
square
label="i-heroicons-ellipsis-vertical"
/>
</div>
</template>
<template #description>
This example shows how you can use named templates for customization
<div class="mt-2 flex items-center space-x-2">
<NBadge
label="New"
badge="soft-success"
/>
<NBadge
label="Beta"
/>
</div>
</template>
<!-- content -->
<div class="flex flex-col gap-4">
<div class="flex items-center gap-2">
<NIcon name="i-heroicons-user-circle-solid" />
<span>John Doe</span>
</div>
<div class="flex items-baseline gap-2">
<NIcon name="i-heroicons-map-pin-solid" />
<span>San Francisco, CA</span>
</div>
<div class="flex items-center gap-2">
<NIcon name="i-heroicons-calendar-solid" />
<span>Joined on 2021-01-01</span>
</div>
</div>
<template #footer>
<div class="w-full flex flex-col">
<div class="mt-2 w-full flex items-center justify-between gap-4">
<NButton
size="md"
btn="ghost-warning"
class="-mx-2"
>
Explore
</NButton>
<NButton
size="md"
icon
label="i-heroicons-chevron-down-solid"
btn="text-gray"
class="rounded-full"
/>
</div>
</div>
</template>
</NCard>
</div>
</template>
Presets
type CardPrefix = 'card'
export const staticCard: Record<`${CardPrefix}-${string}` | CardPrefix, string> = {
// base
'card': 'relative flex flex-col rounded-lg overflow-hidden shadow-sm',
'card-default-variant': 'card-outline-gray',
// components
'card-header': '',
'card-title': 'text-2xl font-semibold leading-none tracking-tight',
'card-description': 'text-sm text-muted',
'card-content': 'px-4 py-5 pt-0 sm:p-6 sm:pt-0',
'card-footer': 'flex items-center p-6 pt-0',
'card-about': 'flex flex-col gap-y-1.5 px-4 py-5 sm:p-6',
// static variants
'card-soft-gray': 'bg-muted border border-base',
'card-outline-gray': 'bg-base border border-base',
}
export const dynamicCard = [
// dynamic variants
[/^card-soft(-(\S+))?$/, ([, , c = 'gray']) => `bg-${c}-50 dark:bg-${c}-900 border-${c}-200 dark:border-${c}-700/58`],
[/^card-outline(-(\S+))?$/, ([, , c = 'gray']) => `border border-${c}-200 dark:border-${c}-700/58`],
]
export const card = [
...dynamicCard,
staticCard,
]
Props
import type { HTMLAttributes } from 'vue'
interface BaseExtensions {
class?: HTMLAttributes['class']
}
export interface NCardProps extends BaseExtensions {
/**
* Allows you to add `UnaUI` card preset properties,
* Think of it as a shortcut for adding options or variants to the preset if available.
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/card.ts
* @example
* card="outline-green"
*/
card?: string
/**
* Add a title to the card.
*/
title?: string
/**
* Add a description to the card.
*/
description?: string
// sub-components
_cardContent?: Partial<NCardContentProps>
_cardTitle?: Partial<NCardTitleProps>
_cardDescription?: Partial<NCardDescriptionProps>
_cardHeader?: Partial<NCardHeaderProps>
_cardAbout?: Partial<NCardAboutProps>
_cardFooter?: Partial<NCardFooterProps>
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/card.ts
*/
una?: {
cardDefaultVariant: HTMLAttributes['class']
card?: HTMLAttributes['class']
}
}
export interface NCardContentProps extends BaseExtensions {
una?: {
cardContent?: HTMLAttributes['class']
}
}
export interface NCardTitleProps extends BaseExtensions {
una?: {
cardTitle?: HTMLAttributes['class']
}
}
export interface NCardDescriptionProps extends BaseExtensions {
una?: {
cardSubtitle?: HTMLAttributes['class']
}
}
export interface NCardHeaderProps extends BaseExtensions {
una?: {
cardHeader?: HTMLAttributes['class']
}
}
export interface NCardAboutProps extends BaseExtensions {
una?: {
cardAbout?: HTMLAttributes['class']
}
}
export interface NCardFooterProps extends BaseExtensions {
una?: {
cardFooter?: HTMLAttributes['class']
}
}
Components
<script setup lang="ts">
import type { NCardProps } from '../../../types/card'
import { computed } from 'vue'
import { cn } from '../../../utils'
import CardAbout from './CardAbout.vue'
import CardContent from './CardContent.vue'
import CardDescription from './CardDescription.vue'
import CardFooter from './CardFooter.vue'
import CardHeader from './CardHeader.vue'
import CardTitle from './CardTitle.vue'
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<NCardProps>(), {
una: () => ({
cardDefaultVariant: 'card-default-variant',
}),
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const cardVariants = ['soft', 'outline'] as const
const hasVariant = computed(() => cardVariants.some(cardVariant => props.card?.includes(cardVariant)))
const isBaseVariant = computed(() => props.card?.includes('~'))
</script>
<template>
<div
v-bind="{ ...$attrs, delegatedProps }"
:card="card"
:class="cn(
'card',
!hasVariant && !isBaseVariant ? una?.cardDefaultVariant : '',
props.class,
una?.card,
)"
>
<slot name="root">
<CardHeader
v-bind="delegatedProps._cardHeader"
>
<slot name="header" />
</CardHeader>
<CardAbout
v-if="$slots.about || title || description || $slots.title || $slots.description"
v-bind="delegatedProps._cardAbout"
>
<slot name="about">
<CardTitle
v-if="$slots.title || title"
v-bind="delegatedProps._cardTitle"
>
<slot name="title">
{{ title }}
</slot>
</CardTitle>
<CardDescription
v-if="$slots.description || description"
v-bind="delegatedProps._cardDescription"
>
<slot name="description">
{{ description }}
</slot>
</CardDescription>
</slot>
</CardAbout>
<CardContent
v-if="$slots.default"
v-bind="delegatedProps._cardContent"
>
<slot />
</CardContent>
<CardFooter
v-if="$slots.footer"
v-bind="delegatedProps._cardFooter"
>
<slot name="footer" />
</CardFooter>
</slot>
</div>
</template>
<script setup lang="ts">
import type { NCardContentProps } from '../../../types/card'
import { cn } from '../../../utils'
const props = defineProps<NCardContentProps>()
</script>
<template>
<div
:class="cn(
'card-content',
props.class,
props.una?.cardContent,
)"
>
<slot />
</div>
</template>
<script setup lang="ts">
import type { NCardAboutProps } from '../../../types'
import { cn } from '../../../utils'
const props = defineProps<NCardAboutProps>()
</script>
<template>
<div
:class="cn(
'card-about',
props.class,
props.una?.cardAbout,
)"
>
<slot />
</div>
</template>
<script setup lang="ts">
import type { NCardTitleProps } from '../../../types/card'
import { cn } from '../../../utils'
const props = defineProps<NCardTitleProps>()
</script>
<template>
<h3
:class="
cn('card-title',
props.class,
props.una?.cardTitle,
)
"
>
<slot />
</h3>
</template>
<script setup lang="ts">
import type { NCardDescriptionProps } from '../../../types/card'
import { cn } from '../../../utils'
const props = defineProps<NCardDescriptionProps>()
</script>
<template>
<p
:class="cn(
'card-description',
props.class,
props.una?.cardSubtitle,
)"
>
<slot />
</p>
</template>
<script setup lang="ts">
import type { NCardHeaderProps } from '../../../types/card'
import { cn } from '../../../utils'
const props = defineProps<NCardHeaderProps>()
</script>
<template>
<div
:class="cn(
'card-header',
props.class,
props.una?.cardHeader,
)"
>
<slot />
</div>
</template>
<script setup lang="ts">
import type { NCardFooterProps } from '../../../types/card'
import { cn } from '../../../utils'
const props = defineProps<NCardFooterProps>()
</script>
<template>
<div
:class="cn(
'card-footer',
props.class,
props.una?.cardFooter,
)"
>
<slot />
</div>
</template>