Card

Displays a card with header, content, and footer.

Examples

Basic

PropDefaultTypeDescription
title-stringSets the main heading text displayed in the card header
description-stringProvides secondary text shown below the title
Preview
Code

Notifications

You have 3 unread messages.

Push Notifications

Send notifications to device.

Your call has been confirmed.

1 hour ago

You have a new message!

1 hour ago

Your subscription is expiring soon!

2 hours ago

Variant

PropDefaultTypeDescription
cardoutline{variant}Controls the visual style of the card.
VariantDescription
outlineAdds a subtle border while maintaining a clean background.
softApplies a light background color with matching border.
~Removes all variant styling, keeping only core card structure.
Preview
Code

Outline variant

A simple outline variant card with a border. This is the default variant if none is specified.

Soft variant

A soft variant card with a subtle background color and border.

Base variant

A base variant card without any predefined styles except for the base card styles.

Color

PropDefaultTypeDescription
card{variant}-primary{variant}-{color}Combines variant and color to define the card's appearance (e.g. soft-blue)
Preview
Code

Free Plan

Perfect for getting started

$0/month
  • Up to 3 projects
  • Community support
  • Basic analytics

Pro Plan

Best for professionals

$29/month
  • Unlimited projects
  • Priority support
  • Advanced analytics
  • Custom domains

Enterprise

For large organizations

$99/month
  • Everything in Pro
  • 24/7 Support
  • SLA guarantee
  • Custom integration

Slots

NamePropsDescription
header-The header section of the card, typically containing the title and description.
default-The main content area of the card.
title-Custom title content that overrides the title prop.
description-Custom description content that overrides the description prop.
footer-The footer section of the card, typically for actions or additional information.
Preview
Code
Card image
JD

John Doe

@johndoe

2.4k followers
San Francisco, CA

Full-stack developer passionate about building beautiful user interfaces and scalable applications. Always learning and sharing knowledge with the community.

Presets

shortcuts/card.ts
type CardPrefix = 'card'

export const staticCard: Record<`${CardPrefix}-${string}` | CardPrefix, string> = {
  // base
  'card': 'rounded-xl shadow text-base',

  // components
  'card-header': 'flex flex-col gap-y-1.5 p-6',
  'card-title': 'font-semibold leading-none tracking-tight',
  'card-description': 'text-sm text-muted',
  'card-content': 'p-6 pt-0',
  'card-footer': 'flex items-center p-6 pt-0',

  // static variants
  'card-soft-gray': 'bg-muted border border-base',
  'card-outline-gray': 'bg-base border border-base',
}

export const dynamicCard = [
  [/^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

types/card.ts
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>
  _cardFooter?: Partial<NCardFooterProps>
  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/card.ts
   */
  una?: NCardUnaProps
}

export interface NCardContentProps extends BaseExtensions {
  una?: Pick<NCardUnaProps, 'cardContent'>
}

export interface NCardTitleProps extends BaseExtensions {
  una?: Pick<NCardUnaProps, 'cardTitle'>
}

export interface NCardDescriptionProps extends BaseExtensions {
  una?: Pick<NCardUnaProps, 'cardDescription'>
}

export interface NCardHeaderProps extends BaseExtensions {
  una?: Pick<NCardUnaProps, 'cardHeader'>
}

export interface NCardFooterProps extends BaseExtensions {
  una?: Pick<NCardUnaProps, 'cardFooter'>
}

export interface NCardUnaProps {
  cardDefaultVariant?: HTMLAttributes['class']
  cardTitle?: HTMLAttributes['class']
  cardDescription?: HTMLAttributes['class']
  cardContent?: HTMLAttributes['class']
  cardHeader?: HTMLAttributes['class']
  cardFooter?: HTMLAttributes['class']
}

Components

Card.vue
CardContent.vue
CardTitle.vue
CardDescription.vue
CardHeader.vue
CardFooter.vue
<script setup lang="ts">
import type { NCardProps } from '../../../types/card'
import { computed } from 'vue'
import { cn } from '../../../utils'
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>(), {
  card: 'outline-gray',
})

const delegatedProps = computed(() => {
  const { class: _, ...delegated } = props

  return delegated
})
</script>

<template>
  <div
    v-bind="{ ...$attrs, delegatedProps }"
    :card="card"
    :class="cn(
      'card',
      props.class,
    )"
  >
    <slot name="root">
      <CardHeader
        v-if="$slots.header || $slots.title || $slots.description || title || description"
        v-bind="delegatedProps._cardHeader"
        :una
      >
        <slot name="header">
          <CardTitle
            v-if="$slots.title || title"
            v-bind="delegatedProps._cardTitle"
            :una
          >
            <slot name="title">
              {{ title }}
            </slot>
          </CardTitle>

          <CardDescription
            v-if="$slots.description || description"
            v-bind="delegatedProps._cardDescription"
            :una
          >
            <slot name="description">
              {{ description }}
            </slot>
          </CardDescription>
        </slot>
      </CardHeader>

      <CardContent
        v-if="$slots.default"
        v-bind="delegatedProps._cardContent"
        :una
      >
        <slot />
      </CardContent>

      <CardFooter
        v-if="$slots.footer"
        v-bind="delegatedProps._cardFooter"
        :una
      >
        <slot name="footer" />
      </CardFooter>
    </slot>
  </div>
</template>