# Card

> A contained group of related content and actions.

- Category: Cards
- Import: `import { Card } from "@sagui/ui";`
- Page: /components/card

```tsx
import { Avatar, Button, Card } from "@sagui/ui";

export function Hero() {
  return (
    <div className="w-full max-w-xs">
      <Card
        title="Atlas redesign"
        description="A quiet redesign of the workspace home."
        media={<img src={officePhoto} alt="A bright, empty office corridor" width={640} height={320} className="block h-40 w-full object-cover" />}
        avatar={<Avatar name="Maya Chen" src={mayaPhoto} size="sm" />}
        meta="Maya Chen"
        status="Updated 2 hours ago"
        action={<Button size="sm" variant="outline">Open</Button>}
      />
    </div>
  );
}
```

## When to use

- Browsable items in a grid, such as projects, listings, or posts.
- Items that should open into a larger quick look dialog without leaving the page, via details.
- Content with media, an owner byline, and a status line that updates in place.

## When not to use

- Use [metric-card](/components/metric-card) for numbers.
- Use dialog when the content has no card to grow from.

## Installation

Install the package and import the styles once. The [installation guide](/docs/installation) covers the Tailwind setup.

```bash
npm install @sagui/ui
```

## Usage

```tsx
import { Avatar, Card } from "@sagui/ui";

export function ProjectCard() {
  return (
    <Card
      title="Harbour redesign"
      description="New booking flow and room pages."
      media={
        <img
          src="https://images.unsplash.com/photo-1497366216548-37526070297c?w=640&h=320&q=80&auto=format&fit=crop"
          alt="A bright, empty office corridor"
          width={640}
          height={320}
          className="block h-40 w-full object-cover"
        />
      }
      avatar={<Avatar name="Maya Chen" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=96&h=96&q=80&auto=format&fit=crop" size="sm" />}
      meta="Maya Chen"
      status="Updated 2 hours ago"
      details={<p>Scope, milestones, and open questions.</p>}
    />
  );
}
```

## Examples

### Quick look

Pass `details` and the whole card opens and grows into a larger view. Escape or the close control morphs it back to where it was.

```tsx
import { Avatar, Button, Card } from "@sagui/ui";

export function QuickLook() {
  return (
    <div className="w-full max-w-xs">
      <Card
        title="Billing migration"
        description="Moving invoices to the new provider."
        media={<img src={dashboardPhoto} alt="A laptop showing a revenue dashboard" width={640} height={320} className="block h-40 w-full object-cover" />}
        avatar={<Avatar name="Samir Patel" src={samirPhoto} size="sm" />}
        meta="Samir Patel"
        status="Updated yesterday"
        details={<p>Everything the team needs to review the migration: scope, open questions, and the cutover checklist.</p>}
        action={<Button size="sm" variant="outline">Share</Button>}
      />
    </div>
  );
}
```

### Without media

```tsx
import { Card } from "@sagui/ui";

export function Plain() {
  return (
    <div className="w-full max-w-xs">
      <Card title="Onboarding checklist" description="Five steps to get a new teammate productive." />
    </div>
  );
}
```

### A status that changes

A new status replaces the line: the old one lifts away while the new words rise in one after another.

```tsx
import { useState } from "react";
import { Button, Card } from "@sagui/ui";

export function ChangingStatus() {
  const [saved, setSaved] = useState(false);
  return (
    <div className="grid w-full max-w-xs gap-3">
      <Card title="Pricing page copy" description="Draft for review." meta="Sam" status={saved ? "Saved just now" : "Updated 2 hours ago"} />
      <Button variant="outline" size="sm" onClick={() => setSaved((value) => !value)}>Toggle status</Button>
    </div>
  );
}
```

## API reference

### Card

A content card with optional media, byline, and action that can grow into a quick look dialog.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `string` | – | Card heading. Becomes the dialog trigger when details is set. |
| `description` | `string` | – | Supporting line under the title. |
| `media` | `ReactNode` | – | Image or visual at the top. Zooms slightly on hover. |
| `action` | `ReactNode` | – | Trailing footer control, such as a button. |
| `avatar` | `ReactNode` | – | Small leading visual in the footer, such as the owner's avatar. |
| `meta` | `ReactNode` | – | Who the card belongs to, shown in the byline. |
| `status` | `string` | – | Short status under the meta, such as "Updated 2 hours ago". Changes roll in word by word and are announced politely. |
| `details` | `ReactNode` | – | Quick look content. When set, the card opens into a larger Radix dialog. |
| `open` | `boolean` | – | Controlled quick look state. |
| `defaultOpen` | `boolean` | `false` | Initial quick look state when uncontrolled. |
| `onOpenChange` | `(open: boolean) => void` | – | Called when the quick look opens or closes. |
| `children` | `ReactNode` | – | Extra body content between the description and footer. |
| `className` | `string` | – | Added to the root article. |
| `...props` | `HTMLAttributes<HTMLElement>` | – | Forwarded to the root article. |

## Keyboard interactions

| Keys | Action |
| --- | --- |
| Enter / Space | On the title, opens the quick look when details is set. |
| Escape | Closes the quick look and returns focus to the title. |
| Tab | Stays trapped inside the open quick look. |

## Accessibility

- Renders an article with an h3 title; the quick look uses Radix Dialog with the title and description wired as its label and description.
- Only the title becomes a button, so nested actions stay separately focusable.
- Status changes are read through a role="status" region.
- Give media images alt text or an empty alt when decorative, and an aria-label to icon-only actions.

## Motion

- Hover lifts the card 2px and slowly zooms the media.
- The quick look shares layout ids with the card, so surface, photo, title, and byline travel on one spring; details fade in after.
- Reduced motion drops the lift, zoom, and morph, and the dialog simply fades.

## Responsive behavior

- The card fills its grid cell with min-width 0; the quick look panel is min(30rem, 100vw minus a gutter) wide and capped at the viewport height.
- Hover lift and media zoom run only for a mouse; touch and pen never lift.

## Performance

- The quick look shares layout ids with the card, so the morph animates several elements; the dialog mounts only while open.
- The overlay uses a 7px backdrop blur, which can cost frames on low-end devices over busy pages.

## Notes

- Use for browsable items in a grid: projects, listings, posts. Use metric-card for numbers.
- Add details only when there is real extra content; without it the card is a static article.
- Keep status short and change it in place to get the rolling update.

## Related

- [Dialog](/components/dialog): A focused surface for decisions that need attention.
