# Card

> Bordered surface that groups one unit the user acts on or compares, with header, action, content and footer slots.

Source: https://docs.nasaqui.com/components/card

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/card.json
```

A container on the `card` surface (level 1) with a border and card radius. It is built from slots:
`CardHeader` (with `CardTitle`, `CardDescription` and an optional `CardAction` on the inline end),
`CardContent` and `CardFooter`. It has no variants and no behaviour.

Nasaq layouts are flat by default. Use a card only when the content is a unit that needs a visible edge.

## When to use

- The item is a unit the user acts on or compares: a KPI, an app in a store grid, a plan in a pricing table.
- The group needs its own actions (header menu, footer buttons) that must visibly belong to it.
- The content sits beside unrelated content in a grid and would otherwise bleed into it.

## When not to use

- A page section, a form, a list or a single table: use whitespace and a heading instead.
- A card inside a card: use a divider or a heading for the sub-group.
- Floating layers (menus, dialogs, sheets): use their own components; they use the overlay surface.
- Filler such as "welcome" cards or decorative widgets: leave the cell empty.
- Nothing to show: use [`EmptyState`](https://docs.nasaqui.com/components/states).

## Import

```tsx
import {
  Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
} from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import {
  Badge, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
} from "@fadymondy/nasaq/web";

export function ProjectSettings() {
  return (
    <Card className="max-w-sm">
      <CardHeader>
        <CardTitle>إعدادات المشروع</CardTitle>
        <CardDescription>الاسم والظهور والمسؤول الافتراضي.</CardDescription>
        <CardAction>
          <Badge variant="success">نشط</Badge>
        </CardAction>
      </CardHeader>
      <CardContent className="text-body-sm text-muted-foreground">
        تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="ghost">إلغاء</Button>
        <Button variant="primary">حفظ</Button>
      </CardFooter>
    </Card>
  );
}
```

## Anatomy

```
Card                          data-slot="card"          flex column, gap-4, py-4
├─ CardHeader                 data-slot="card-header"   grid, px-4
│  ├─ CardTitle               data-slot="card-title"
│  ├─ CardDescription         data-slot="card-description"
│  └─ CardAction              data-slot="card-action"   inline end, spans both header rows
├─ CardContent                data-slot="card-content"  px-4
└─ CardFooter                 data-slot="card-footer"   flex, items-center, gap-2, px-4
```

When a `CardAction` is present the header switches to two columns (`1fr auto`). Every part is optional.

## API

Every part is a `div` (`ComponentProps<"div">`), forwards its remaining props and merges `className`.
There are no custom props and no variants.

| Export | data-slot | Default classes |
| --- | --- | --- |
| `Card` | `card` | `flex flex-col gap-4 rounded-card border border-border bg-card py-4 text-card-foreground` |
| `CardHeader` | `card-header` | `grid auto-rows-min items-start gap-1 px-4`, two columns when a `card-action` child exists |
| `CardTitle` | `card-title` | `text-label text-foreground`. `CardTitleProps` adds `as?: "div" \| "h1" \| … \| "h6"` (default `"div"`). |
| `CardDescription` | `card-description` | `text-body-sm text-muted-foreground` |
| `CardAction` | `card-action` | `col-start-2 row-span-2 row-start-1 self-start justify-self-end` |
| `CardContent` | `card-content` | `px-4` |
| `CardFooter` | `card-footer` | `flex items-center gap-2 px-4` |

## Examples

### Metric card

```tsx
import { Badge, Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from "@fadymondy/nasaq/web";
import { TrendingUp } from "lucide-react";

export function RevenueCard() {
  return (
    <Card className="max-w-xs gap-2 py-3.5">
      <CardHeader>
        <CardDescription>الإيرادات</CardDescription>
        <CardTitle className="text-h2 leading-tight tabular-nums">$48,210</CardTitle>
        <CardAction>
          <Badge variant="outline">
            <TrendingUp />
            <span dir="ltr">+12.4%</span>
          </Badge>
        </CardAction>
      </CardHeader>
      <CardFooter className="text-caption text-muted-foreground">في ارتفاع هذا الشهر</CardFooter>
    </Card>
  );
}
```

Metric cards are denser than content cards (`gap-2 py-3.5`, about 12% shorter). The value gets
`leading-tight`: it is digits, so it doesn't need the tall line-height Arabic text uses, while the label and note
keep theirs. That keeps the number dominant without cramping Arabic.

### KPI grid

```tsx
import { Card, CardDescription, CardHeader, CardTitle } from "@fadymondy/nasaq/web";

const kpis = [
  { label: "المشاريع النشطة", value: "23" },
  { label: "الساعات المسجلة", value: "412" },
];

export function Kpis() {
  return (
    <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
      {kpis.map((k) => (
        <Card key={k.label} className="gap-2 py-3.5">
          <CardHeader>
            <CardDescription>{k.label}</CardDescription>
            <CardTitle className="text-h2 leading-tight tabular-nums">{k.value}</CardTitle>
          </CardHeader>
        </Card>
      ))}
    </div>
  );
}
```

For currency and other numbers inside Arabic text, format with `Num` from the numeric component.

## Accessibility

A card is a plain `div`. It has no role, focus or keyboard behaviour.

| Key | Action |
| --- | --- |
| `Tab` | Moves through the interactive children (buttons, links) in DOM order. |

- `CardTitle` is a `div` by default. Pass `as="h3"` (any of `h1`-`h6`) so the card title is a heading in the
  document outline, or add `role="group"` with `aria-labelledby`.
- A clickable card is not provided. Put a real `<a>` or `Button` inside instead of adding `onClick` to the `div`.
- Localise all copy yourself; the component has no strings.

## RTL & i18n

- Layout is logical: `CardAction` sits on the inline end (left in RTL) and needs no change.
- Keep numbers and deltas LTR inside Arabic text: `<span dir="ltr">+12.4%</span>` or `Num`.
- Use `justify-end` in `CardFooter` for the primary action; it flips with `dir`.

## Styling & tokens

- Surface `bg-card` (level 1), text `text-card-foreground`, edge `border-border`, radius `rounded-card`.
- Tune spacing with `gap-*` on `Card` (for example `gap-3` for metric cards) and `pb-*` when a table fills the bottom.
- Target parts with `[data-slot=card]`, `[data-slot=card-header]`, `[data-slot=card-action]`.
- Extend with `className`. Never override colours with raw hex.

## Do / Don't

- **Do** use a card for a unit users act on or compare, or a group with its own actions.
- **Do** keep one primary action in the footer.
- **Do** use whitespace and a heading for sections, forms and single tables.
- **Don't** nest cards. Use a divider or heading inside.
- **Don't** add cards to fill a grid: no decorative charts or "welcome" cards.
- **Don't** hand-tune padding per screen; use the density tokens.

## Related

- [Table](https://docs.nasaqui.com/components/table) · [Badge](https://docs.nasaqui.com/components/badge) · [States](https://docs.nasaqui.com/components/states)
- [AppShell](https://docs.nasaqui.com/components/app-shell)

## Lab

https://docs.nasaqui.com/?path=/docs/components-layout-card--docs

## Code

### React

```tsx
import {
  Badge, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,
} from "@fadymondy/nasaq/web";

export function ProjectSettings() {
  return (
    <Card className="max-w-sm">
      <CardHeader>
        <CardTitle>إعدادات المشروع</CardTitle>
        <CardDescription>الاسم والظهور والمسؤول الافتراضي.</CardDescription>
        <CardAction>
          <Badge variant="success">نشط</Badge>
        </CardAction>
      </CardHeader>
      <CardContent className="text-body-sm text-muted-foreground">
        تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="ghost">إلغاء</Button>
        <Button variant="primary">حفظ</Button>
      </CardFooter>
    </Card>
  );
}
```

### shadcn

```tsx
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";

export function ProjectSettings() {
  return (
    <Card className="max-w-sm">
      <CardHeader>
        <CardTitle>إعدادات المشروع</CardTitle>
        <CardDescription>الاسم والظهور والمسؤول الافتراضي.</CardDescription>
        <CardAction>
          <Badge variant="success">نشط</Badge>
        </CardAction>
      </CardHeader>
      <CardContent className="text-body-sm text-muted-foreground">
        تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="ghost">إلغاء</Button>
        <Button variant="primary">حفظ</Button>
      </CardFooter>
    </Card>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import {
  NqBadge, NqButton, NqCard, NqCardAction, NqCardContent, NqCardDescription, NqCardFooter, NqCardHeader, NqCardTitle,
} from "@fadymondy/nasaq/vue";
</script>

<template>
  <NqCard class="max-w-sm">
    <NqCardHeader>
      <NqCardTitle>إعدادات المشروع</NqCardTitle>
      <NqCardDescription>الاسم والظهور والمسؤول الافتراضي.</NqCardDescription>
      <NqCardAction>
        <NqBadge variant="success">نشط</NqBadge>
      </NqCardAction>
    </NqCardHeader>
    <NqCardContent class="text-body-sm text-muted-foreground">
      تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.
    </NqCardContent>
    <NqCardFooter class="justify-end gap-2">
      <NqButton variant="ghost">إلغاء</NqButton>
      <NqButton variant="primary">حفظ</NqButton>
    </NqCardFooter>
  </NqCard>
</template>
```

### Blade

```blade
<x-nq::card class="max-w-sm">
    <x-nq::card.header>
        <x-nq::card.title>إعدادات المشروع</x-nq::card.title>
        <x-nq::card.description>الاسم والظهور والمسؤول الافتراضي.</x-nq::card.description>
        <x-nq::card.action>
            <x-nq::badge variant="success">نشط</x-nq::badge>
        </x-nq::card.action>
    </x-nq::card.header>
    <x-nq::card.content class="text-body-sm text-muted-foreground">
        تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.
    </x-nq::card.content>
    <x-nq::card.footer class="justify-end gap-2">
        <x-nq::button variant="ghost">إلغاء</x-nq::button>
        <x-nq::button variant="primary">حفظ</x-nq::button>
    </x-nq::card.footer>
</x-nq::card>
```

### HTML + Alpine

```html
<div data-slot="card" class="flex flex-col gap-4 rounded-card border border-border bg-card py-4 text-card-foreground max-w-sm"><div data-slot="card-header" class="grid auto-rows-min items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]"><div data-slot="card-title" class="text-label text-foreground">إعدادات المشروع</div>
        <div data-slot="card-description" class="text-body-sm text-muted-foreground">الاسم والظهور والمسؤول الافتراضي.</div>
        <div data-slot="card-action" class="col-start-2 row-span-2 row-start-1 self-start justify-self-end"><span data-slot="badge"     class="inline-flex h-5 shrink-0 items-center gap-1 whitespace-nowrap rounded-[4px] border px-1.5 text-caption font-medium [&_svg]:size-3 border-nq-success/40 bg-nq-success-soft text-nq-success-text">نشط</span></div></div>
    <div data-slot="card-content" class="px-4 text-body-sm text-muted-foreground">تجمع البطاقة موضوعًا واحدًا. اجعل إجراءً رئيسيًا واحدًا في التذييل.</div>
    <div data-slot="card-footer" class="flex items-center px-4 justify-end gap-2"><button data-slot="button"
     type="button"                         class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        إلغاء</button>
        <button data-slot="button"
     type="button"                         class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-[color-mix(in_oklab,var(--nq-action)_88%,var(--nq-fg))] h-control px-[var(--nq-control-pad)]">
        حفظ</button></div></div>
```
