Skip to content

Card

A bordered, elevated panel for grouping related content.

A bordered, elevated panel for grouping related content. Compose it from the exported registry parts so local content and behaviour stay in the application while the visual contract stays token-driven.

Root class newt-card
import { Card, CardDescription, CardTitle } from "@/registry/bases/newt/ui/card"

export default function CardDemo() {
  return (
    <Card className="w-full max-w-sm">
      <CardTitle>Card title</CardTitle>
      <CardDescription>Card description text.</CardDescription>
    </Card>
  )
}
<script setup lang="ts">
import {
  Card,
  CardDescription,
  CardTitle,
} from "~~/registry/bases/newt/ui/card"
</script>

<template>
  <Card class="w-full max-w-sm">
    <CardTitle>Card title</CardTitle>
    <CardDescription>Card description text.</CardDescription>
  </Card>
</template>

Installation

pnpm dlx newtui@latest add card
npx newtui@latest add card
yarn dlx newtui@latest add card
bunx newtui@latest add card

Copy the source into your project

import * as React from "react"

import { cn } from "@/lib/utils"

const Card = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
  ({ className, ...props }, ref) => (
    <div
      ref={ref}
      className={cn(
        "rounded-lg border border-newt-border bg-newt-bg-elevated p-5 text-newt-text-primary",
        className
      )}
      {...props}
    />
  )
)
Card.displayName = "Card"

const CardTitle = React.forwardRef<HTMLDivElement, React.ComponentProps<"div">>(
  ({ className, ...props }, ref) => (
    <div
      ref={ref}
      className={cn("mb-1 text-base font-semibold", className)}
      {...props}
    />
  )
)
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
  HTMLParagraphElement,
  React.ComponentProps<"p">
>(({ className, ...props }, ref) => (
  <p
    ref={ref}
    className={cn("m-0 text-sm text-newt-text-secondary", className)}
    {...props}
  />
))
CardDescription.displayName = "CardDescription"

export { Card, CardTitle, CardDescription }
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div
    :class="
      cn(
        'rounded-lg border border-newt-border bg-newt-bg-elevated p-5 text-newt-text-primary',
        props.class
      )
    "
  >
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <div :class="cn('mb-1 text-base font-semibold', props.class)">
    <slot />
  </div>
</template>
<script setup lang="ts">
import type { HTMLAttributes } from "vue"

import { cn } from "@/lib/utils"

const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>

<template>
  <p :class="cn('m-0 text-sm text-newt-text-secondary', props.class)">
    <slot />
  </p>
</template>
export { default as Card } from "./Card.vue"
export { default as CardTitle } from "./CardTitle.vue"
export { default as CardDescription } from "./CardDescription.vue"

Update the import paths

The files import @/lib/utils and sibling components through the aliases in your components.json. Point them at wherever those live in your project.

Usage

import { Card } from "@/components/ui/card"

export function Example() {
  return <Card />
}
<script setup lang="ts">
import { Card, CardTitle, CardDescription } from "@/components/ui/card"
</script>

<template>
  <Card />
</template>

API reference

Card

PropType
children / default slot?React.ReactNode / Vue slot

Content rendered inside the component.

TypeReact.ReactNode / Vue slot
className / class?string

Additional classes merged with the component defaults.

Typestring

The registry exports Card, CardTitle, CardDescription. Parts without a table accept the native attributes for the element they render; React uses className, while Vue uses class and fallthrough attributes.

Accessibility

Preserve the native semantics of the rendered elements and pass through ARIA attributes when the surrounding context needs a label or description. Any interactive child needs a visible focus indicator, and meaning must not rely on colour alone.

Tokens

Card reads its colours, radii and motion from the --newt-* tokens installed with the library. Override a token and every component that reads it follows, including this one — see the token layer. In the plain HTML and CSS distribution the same component is newt-card.