Skip to content
Ndop UI

Card

Displays a card with a header, content and footer.

  • v1.0.0
  • SSR
  • Zoneless
  • 0.7 kB gzip

Sign in to your account

Enter your email below to sign in.

import { Component } from '@angular/core';
import { Button } from '@/ui/button/button';
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@/ui/card/card';
import { Input } from '@/ui/input/input';
import { Label } from '@/ui/label/label';

/** A sign-in form in a card. */
@Component({
  selector: 'example-card-demo',
  imports: [
    Button,
    Card,
    CardAction,
    CardContent,
    CardDescription,
    CardFooter,
    CardHeader,
    CardTitle,
    Input,
    Label,
  ],
  template: `
    <section ndCard class="w-full max-w-sm" aria-labelledby="card-demo-title">
      <div ndCardHeader>
        <h2 ndCardTitle id="card-demo-title">Sign in to your account</h2>
        <p ndCardDescription>Enter your email below to sign in.</p>
        <div ndCardAction>
          <a ndButton variant="link" href="#sign-up">Sign up</a>
        </div>
      </div>
      <form ndCardContent id="card-demo-form" class="grid gap-6">
        <div class="grid gap-2">
          <label ndLabel for="card-demo-email">Email</label>
          <input
            ndInput
            id="card-demo-email"
            type="email"
            placeholder="m@example.com"
            required
          />
        </div>
        <div class="grid gap-2">
          <div class="flex items-center">
            <label ndLabel for="card-demo-password">Password</label>
            <a
              href="#forgot"
              class="ms-auto text-sm underline-offset-4 hover:underline"
              >Forgot your password?</a
            >
          </div>
          <input ndInput id="card-demo-password" type="password" required />
        </div>
      </form>
      <div ndCardFooter class="flex-col gap-2">
        <button ndButton type="submit" form="card-demo-form" class="w-full">
          Sign in
        </button>
      </div>
    </section>
  `,
})
export class CardDemoExample {}

A card groups related content and actions on a bordered surface: a summary, a form, a setting. ndCard and its parts only style the elements they're on, so you choose the semantics: a section with a heading, an article in a feed, or a plain div.

Installation

npx @ndop/cli add card

Usage

import { Button } from '@/ui/button/button';
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@/ui/card/card';
import { Input } from '@/ui/input/input';
import { Label } from '@/ui/label/label';
<section ndCard class="w-full max-w-sm" aria-labelledby="card-demo-title">
  <div ndCardHeader>
    <h2 ndCardTitle id="card-demo-title">Sign in to your account</h2>
    <p ndCardDescription>Enter your email below to sign in.</p>
    <div ndCardAction>
      <a ndButton variant="link" href="#sign-up">Sign up</a>
    </div>
  </div>
  <form ndCardContent id="card-demo-form" class="grid gap-6">
    <div class="grid gap-2">
      <label ndLabel for="card-demo-email">Email</label>
      <input
        ndInput
        id="card-demo-email"
        type="email"
        placeholder="m@example.com"
        required
      />
    </div>
    <div class="grid gap-2">
      <div class="flex items-center">
        <label ndLabel for="card-demo-password">Password</label>
        <a
          href="#forgot"
          class="ms-auto text-sm underline-offset-4 hover:underline"
          >Forgot your password?</a
        >
      </div>
      <input ndInput id="card-demo-password" type="password" required />
    </div>
  </form>
  <div ndCardFooter class="flex-col gap-2">
    <button ndButton type="submit" form="card-demo-form" class="w-full">
      Sign in
    </button>
  </div>
</section>

Examples

Card with action

A card with a header action, content and footer.

Pro plan

Billed monthly, renews on November 1.

Current

$24 / month

  • Unlimited projects
  • 10 team members
  • Priority support
import { Component } from '@angular/core';
import { Badge } from '@/ui/badge/badge';
import { Button } from '@/ui/button/button';
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@/ui/card/card';

/** A card with a header action, content and a footer. */
@Component({
  selector: 'example-card-with-action',
  imports: [
    Badge,
    Button,
    Card,
    CardAction,
    CardContent,
    CardDescription,
    CardFooter,
    CardHeader,
    CardTitle,
  ],
  template: `
    <section
      ndCard
      class="w-full max-w-sm"
      aria-labelledby="card-with-action-title"
    >
      <div ndCardHeader>
        <h2 ndCardTitle id="card-with-action-title">Pro plan</h2>
        <p ndCardDescription>Billed monthly, renews on November 1.</p>
        <div ndCardAction><span ndBadge variant="secondary">Current</span></div>
      </div>
      <div ndCardContent>
        <p class="text-3xl font-semibold">
          $24<span class="text-sm font-normal text-muted-foreground">
            / month</span
          >
        </p>
        <ul class="mt-4 grid gap-2 text-sm text-muted-foreground">
          <li>Unlimited projects</li>
          <li>10 team members</li>
          <li>Priority support</li>
        </ul>
      </div>
      <div ndCardFooter class="gap-2 border-t">
        <button ndButton variant="outline">Cancel plan</button>
        <button ndButton class="ms-auto">Upgrade</button>
      </div>
    </section>
  `,
})
export class CardWithActionExample {}

Accessibility

Card has no keyboard interaction or focus handling of its own; it keeps the semantics of the element it's on.

API reference

Card

[ndCard]

A card: a bordered surface grouping related content, with optional header, content and footer parts.

Card binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard

CardHeader

[ndCardHeader]

A card's header: its title, description and an optional action, laid out in a grid.

CardHeader binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-header

CardTitle

[ndCardTitle]

A card's title. Put it on the heading element that fits the page's outline.

CardTitle binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-title

CardDescription

[ndCardDescription]

Supporting text under a card's title.

CardDescription binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-description

CardAction

[ndCardAction]

An action in a card's header, at its end, such as a button or menu.

CardAction binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-action

CardContent

[ndCardContent]

A card's main content.

CardContent binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-content

CardFooter

[ndCardFooter]

A card's footer, usually its actions.

CardFooter binding Type Default
[class]
Extra classes, merged last so they win.
string''
Data attribute Values
data-slotcard-footer

Agent notes

Do

  • Give each card a heading with ndCardTitle, at the right level for the page.
  • Use section with aria-labelledby pointing at the title when the card is a meaningful region, or article when it stands alone.
  • Put the card's main action in ndCardFooter or ndCardAction.

Don't

  • Don't make a whole card clickable with a handler on the div; put a link or button inside it.
  • Don't nest cards; use separators or headings inside one card.