# Card

> A surface for grouping content, with three variants, edge-to-edge media, concentric radii and whole-card links.

Docs: https://hextaui.com/docs/card
Markdown: https://hextaui.com/docs/card.md

```tsx title="components/examples/card/demo.tsx"
import { IconDotsVertical } from "@tabler/icons-react"

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

export function CardDemo() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Team plan</CardTitle>
        <CardDescription>
          Billed monthly. Renews on October 28, 2026.
        </CardDescription>
        <CardAction>
          <Button variant="ghost" size="icon-sm" aria-label="More options">
            <IconDotsVertical />
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <p>
          12 of 20 seats used. Invite teammates or remove inactive members to
          free up seats.
        </p>
      </CardContent>
      <CardFooter>
        <Button>Manage seats</Button>
        <Button variant="outline">View invoices</Button>
      </CardFooter>
    </Card>
  )
}
```

## Installation

### CLI

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

This adds the component, the HextaUI theme tokens and any HextaUI components it depends on.

### Manual

Add the HextaUI theme tokens (https://hextaui.com/docs/installation#theme) to your global CSS if you haven't yet, then install the dependencies.

```bash
pnpm add @base-ui/react class-variance-authority cn
```

Copy and paste the following code into your project.

```tsx title="components/ui/card.tsx"
"use client"

import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"

function dataAttributes(slot: string, extra?: Record<string, string>) {
  return { "data-slot": slot, ...extra } as Record<string, string>
}

const CardLinkContext = React.createContext<React.Dispatch<
  React.SetStateAction<number>
> | null>(null)

const cardVariants = cva(
  "group/card relative flex flex-col gap-(--card-spacing) overflow-hidden rounded-(--card-radius) py-(--card-spacing) text-sm text-card-foreground transition-[box-shadow,background-color] duration-150 ease-out-quint in-data-[slot=card-content]:[--card-radius:max(var(--radius-md),calc(var(--card-parent-radius)-var(--card-parent-spacing)))] has-[>:is(img,video,picture,[data-slot=aspect-ratio]):first-child]:pt-0 has-[>:is(img,video,picture,[data-slot=aspect-ratio]):last-child]:pb-0 data-link:has-[[data-slot=card-link]:focus-visible]:ring-3 data-link:has-[[data-slot=card-link]:focus-visible]:inset-ring-1 data-link:has-[[data-slot=card-link]:focus-visible]:ring-focus-ring data-link:has-[[data-slot=card-link]:focus-visible]:inset-ring-ring data-link:[&_:where(a[href],button,input,select,textarea,summary,label,[role=button],[tabindex]):not([data-slot=card-link])]:relative data-link:[&_:where(a[href],button,input,select,textarea,summary,label,[role=button],[tabindex]):not([data-slot=card-link])]:z-2",
  {
    variants: {
      variant: {
        default:
          "bg-card ring-(length:--hairline) ring-foreground/10 data-link:hover:ring-foreground/15 forced-colors:border",
        outline:
          "bg-transparent ring-(length:--hairline) ring-foreground/10 data-link:hover:bg-muted/50 forced-colors:border",
        muted:
          "bg-muted text-foreground data-link:hover:bg-[color-mix(in_oklch,var(--color-muted),var(--color-foreground)_4%)]",
      },
      size: {
        default:
          "[--card-radius:var(--radius-xl)] [--card-spacing:--spacing(6)] [--card-title-size:var(--text-base)]",
        sm: "[--card-radius:var(--radius-lg)] [--card-spacing:--spacing(4)] [--card-title-size:var(--text-sm)]",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

type CardVariant = NonNullable<VariantProps<typeof cardVariants>["variant"]>
type CardSize = NonNullable<VariantProps<typeof cardVariants>["size"]>

type CardProps = useRender.ComponentProps<"div"> & {
  variant?: CardVariant
  size?: CardSize
}

function Card({
  className,
  variant = "default",
  size = "default",
  render,
  ...props
}: CardProps) {
  const [links, setLinks] = React.useState(0)

  const element = useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      { className: cn(cardVariants({ variant, size }), className) },
      props,
      dataAttributes("card", {
        "data-variant": variant,
        "data-size": size,
        ...(links > 0 ? { "data-link": "" } : {}),
      })
    ),
  })

  return (
    <CardLinkContext.Provider value={setLinks}>
      {element}
    </CardLinkContext.Provider>
  )
}

function CardHeader({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "group/card-header grid auto-rows-min items-start gap-1 px-(--card-spacing) has-[>[data-slot=card-action]]:grid-cols-[minmax(0,1fr)_auto] has-[>[data-slot=card-description]]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
          className
        ),
      },
      props,
      dataAttributes("card-header")
    ),
  })
}

function CardTitle({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "min-w-0 font-heading text-(length:--card-title-size) leading-normal font-medium text-pretty wrap-anywhere",
          className
        ),
      },
      props,
      dataAttributes("card-title")
    ),
  })
}

function CardDescription({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "min-w-0 text-sm text-pretty wrap-anywhere text-muted-foreground",
          className
        ),
      },
      props,
      dataAttributes("card-description")
    ),
  })
}

function CardAction({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "relative z-2 col-start-2 row-span-2 row-start-1 self-start justify-self-end",
          className
        ),
      },
      props,
      dataAttributes("card-action")
    ),
  })
}

function CardContent({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "flex min-w-0 flex-col gap-3 px-(--card-spacing) [--card-parent-radius:var(--card-radius)] [--card-parent-spacing:var(--card-spacing)]",
          className
        ),
      },
      props,
      dataAttributes("card-content")
    ),
  })
}

function CardFooter({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    render,
    props: mergeProps<"div">(
      {
        className: cn(
          "flex min-w-0 items-center gap-2 px-(--card-spacing) [.border-t]:pt-(--card-spacing)",
          className
        ),
      },
      props,
      dataAttributes("card-footer")
    ),
  })
}

function CardLink({
  className,
  render,
  ...props
}: useRender.ComponentProps<"a">) {
  const setLinks = React.useContext(CardLinkContext)

  React.useLayoutEffect(() => {
    if (!setLinks) {
      return
    }

    setLinks((count) => count + 1)

    return () => setLinks((count) => count - 1)
  }, [setLinks])

  return useRender({
    defaultTagName: "a",
    render,
    props: mergeProps<"a">(
      {
        className: cn(
          "outline-none [-webkit-tap-highlight-color:transparent] after:absolute after:inset-0 after:z-1 focus-visible:outline-hidden",
          className
        ),
      },
      props,
      dataAttributes("card-link")
    ),
  })
}

export {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
  cardVariants,
}
export type { CardProps, CardSize, CardVariant }
```

Update the import paths to match your project setup.

## Usage

```tsx
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"
```

```tsx
<Card>
  <CardHeader>
    <CardTitle>Team plan</CardTitle>
    <CardDescription>Billed monthly.</CardDescription>
    <CardAction>
      <Button variant="ghost" size="icon-sm" aria-label="More options">
        <IconDotsVertical />
      </Button>
    </CardAction>
  </CardHeader>
  <CardContent>12 of 20 seats used.</CardContent>
  <CardFooter>
    <Button>Manage seats</Button>
  </CardFooter>
</Card>
```

## Composition

```text
Card
├── CardHeader
│   ├── CardTitle
│   │   └── CardLink
│   ├── CardDescription
│   └── CardAction
├── CardContent
└── CardFooter
```

## Examples

### Variants

`default` sits on a hairline ring with a soft shadow, `outline` drops the fill and shadow, and `muted` uses a filled surface for quieter sections.

```tsx title="components/examples/card/variants.tsx"
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardVariants() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-3">
      <Card>
        <CardHeader>
          <CardTitle>Default</CardTitle>
          <CardDescription>Hairline ring and a soft shadow.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="outline">
        <CardHeader>
          <CardTitle>Outline</CardTitle>
          <CardDescription>Ring only, no fill, no shadow.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="muted">
        <CardHeader>
          <CardTitle>Muted</CardTitle>
          <CardDescription>Filled surface for quiet sections.</CardDescription>
        </CardHeader>
      </Card>
    </div>
  )
}
```

### Sizes

`size="sm"` tightens the spacing, radius and title size together, so a compact card still looks like part of the same family.

```tsx title="components/examples/card/sizes.tsx"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardSizes() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      <Card>
        <CardHeader>
          <CardTitle>Default</CardTitle>
          <CardDescription>Spacing and radius scale together.</CardDescription>
        </CardHeader>
        <CardContent>
          <p>Deploys run on every push to main.</p>
        </CardContent>
        <CardFooter>
          <Button variant="outline">Configure</Button>
        </CardFooter>
      </Card>
      <Card size="sm">
        <CardHeader>
          <CardTitle>Small</CardTitle>
          <CardDescription>Spacing and radius scale together.</CardDescription>
        </CardHeader>
        <CardContent>
          <p>Deploys run on every push to main.</p>
        </CardContent>
        <CardFooter>
          <Button size="sm" variant="outline">
            Configure
          </Button>
        </CardFooter>
      </Card>
    </div>
  )
}
```

### Media

An image, video, picture or aspect ratio placed first or last runs edge to edge. The card drops the padding on that side and clips the media to its corners.

```tsx title="components/examples/card/media.tsx"
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardMedia() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      <Card>
        <img
          src="/preview/landscape.svg"
          alt="Mountain landscape"
          className="aspect-video w-full object-cover"
        />
        <CardHeader>
          <CardTitle>Media first</CardTitle>
          <CardDescription>
            The image runs edge to edge and inherits the card corners.
          </CardDescription>
        </CardHeader>
      </Card>
      <Card>
        <CardHeader>
          <CardTitle>Media last</CardTitle>
          <CardDescription>Bottom padding drops for the image.</CardDescription>
        </CardHeader>
        <img
          src="/preview/landscape.svg"
          alt="Mountain landscape"
          className="aspect-video w-full object-cover"
        />
      </Card>
    </div>
  )
}
```

### Clickable card

Put a `<CardLink />` in the title to make the whole card one link. Buttons and other controls inside stay clickable and keep their own focus, like the star here.

```tsx title="components/examples/card/link.tsx"
"use client"

import * as React from "react"
import { IconGitBranch, IconStar } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

const projects = [
  {
    name: "Acme website",
    description: "Marketing site and docs, deployed on every push.",
    stars: 128,
    branch: "main",
  },
  {
    name: "Payments service",
    description: "Card processing, webhooks and the reconciliation job.",
    stars: 42,
    branch: "release/2.4",
  },
]

export function CardClickable() {
  const [starred, setStarred] = React.useState<string[]>([])

  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      {projects.map((project) => {
        const isStarred = starred.includes(project.name)

        return (
          <Card key={project.name}>
            <CardHeader>
              <CardTitle>
                <CardLink href="#">{project.name}</CardLink>
              </CardTitle>
              <CardDescription>{project.description}</CardDescription>
              <CardAction>
                <Button
                  variant="ghost"
                  size="icon-sm"
                  aria-label={`Star ${project.name}`}
                  aria-pressed={isStarred}
                  onClick={() =>
                    setStarred(
                      isStarred
                        ? starred.filter((name) => name !== project.name)
                        : [...starred, project.name]
                    )
                  }
                >
                  <IconStar className={isStarred ? "fill-current" : ""} />
                </Button>
              </CardAction>
            </CardHeader>
            <CardFooter>
              <span className="flex items-center gap-1 text-muted-foreground">
                <IconGitBranch className="size-4" />
                {project.branch}
              </span>
              <span className="ms-auto text-muted-foreground">
                {project.stars + (isStarred ? 1 : 0)} stars
              </span>
            </CardFooter>
          </Card>
        )
      })}
    </div>
  )
}
```

### Clickable variants

Each variant gets its own hover feedback once it holds a link, and the focus ring wraps the whole card when the link is focused with the keyboard.

```tsx title="components/examples/card/link-variants.tsx"
import { IconArrowUpRight } from "@tabler/icons-react"

import {
  Card,
  CardDescription,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardLinkVariants() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-3">
      <Card size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Default <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>The shadow lifts on hover.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="outline" size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Outline <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>A soft fill on hover.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="muted" size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Muted <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>The fill deepens on hover.</CardDescription>
        </CardHeader>
      </Card>
    </div>
  )
}
```

### Borders

Add `border-b` to the header or `border-t` to the footer and the card adds matching padding on the inner side.

```tsx title="components/examples/card/borders.tsx"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardBorders() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader className="border-b">
        <CardTitle>Notifications</CardTitle>
        <CardDescription>Choose what you hear about.</CardDescription>
      </CardHeader>
      <CardContent>
        <p>Mentions, replies and deploy failures are on.</p>
      </CardContent>
      <CardFooter className="border-t">
        <Button variant="outline">Reset</Button>
        <Button className="ms-auto">Save</Button>
      </CardFooter>
    </Card>
  )
}
```

### Nested

A card inside CardContent derives its radius from the parent’s radius minus the spacing, so the corners stay concentric.

```tsx title="components/examples/card/nested.tsx"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardNested() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Usage</CardTitle>
        <CardDescription>This billing period.</CardDescription>
      </CardHeader>
      <CardContent>
        <Card variant="muted" size="sm">
          <CardHeader>
            <CardTitle>Build minutes</CardTitle>
            <CardDescription>1,240 of 3,000 used</CardDescription>
          </CardHeader>
        </Card>
        <Card variant="outline" size="sm">
          <CardHeader>
            <CardTitle>
              <CardLink href="#">Bandwidth</CardLink>
            </CardTitle>
            <CardDescription>84 GB of 100 GB used</CardDescription>
          </CardHeader>
        </Card>
      </CardContent>
    </Card>
  )
}
```

### Long content

Unbroken titles, long URLs, emoji and CJK text wrap inside a narrow card instead of pushing it wider.

```tsx title="components/examples/card/long-content.tsx"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardLongContent() {
  return (
    <Card size="sm" className="w-72 max-w-full">
      <CardHeader>
        <CardTitle>
          Supercalifragilisticexpialidocious_unbroken_title_that_never_ends
        </CardTitle>
        <CardDescription>
          https://example.com/a/very/long/path/that/keeps/going/and/going?with=query
        </CardDescription>
        <CardAction>
          <Button variant="outline" size="sm">
            Edit
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <p>👩‍👩‍👧‍👦 你好世界，这是一个很长的中文句子用于测试换行</p>
      </CardContent>
      <CardFooter>
        <Button variant="outline">Cancel</Button>
        <Button>Continue</Button>
      </CardFooter>
    </Card>
  )
}
```

### Semantics

Every part takes a `render` prop, so a card can be an `<article>` with a real heading, header and footer.

```tsx title="components/examples/card/semantics.tsx"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardSemantics() {
  return (
    <Card render={<article />} className="w-full max-w-sm">
      <CardHeader render={<header />}>
        <CardTitle render={<h3 />}>Rendered as an article</CardTitle>
        <CardDescription render={<p />}>
          Every part accepts render for real semantics.
        </CardDescription>
      </CardHeader>
      <CardFooter render={<footer />}>
        <Button variant="outline">Read more</Button>
      </CardFooter>
    </Card>
  )
}
```

### Right to left

The action moves to the inline start and the footer follows the reading direction.

```tsx title="components/examples/card/rtl.tsx"
import { IconDotsVertical } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Card>
        <CardHeader>
          <CardTitle>
            <CardLink href="#">موقع أكمي</CardLink>
          </CardTitle>
          <CardDescription>
            موقع تسويقي ووثائق، يُنشر مع كل دفعة.
          </CardDescription>
          <CardAction>
            <Button variant="ghost" size="icon-sm" aria-label="المزيد">
              <IconDotsVertical />
            </Button>
          </CardAction>
        </CardHeader>
        <CardFooter>
          <Button variant="outline">إلغاء</Button>
          <Button>متابعة</Button>
        </CardFooter>
      </Card>
    </div>
  )
}
```

## Keyboard

A card is not focusable on its own. These keys apply when it holds a `<CardLink />`.

| Key | Action |
| --- | --- |
| `Tab` | Focuses the card link, which draws the focus ring around the whole card, then moves on to each control inside it. |
| `Enter` | Follows the card link. |

## Accessibility

- The link’s accessible name is its own text, so keep `<CardLink />` inside the title rather than wrapping the whole card in an anchor.
- Controls inside a clickable card are lifted above the link’s hit area, so they stay reachable by pointer and keyboard.
- Use `render` on `<CardTitle />` to give the title the heading level that fits your page.

## API reference

Every part renders a `<div>` by default, accepts its attributes and takes a `render` prop to swap the element.

### Card

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"default" \| "outline" \| "muted"` | `"default"` |  |
| `size` | `"default" \| "sm"` | `"default"` | Scales spacing, radius and title size together. |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card"` | Target the card in CSS. |
| `data-variant` | The current variant. |
| `data-size` | The current size. |
| `data-link` | Present while the card contains a CardLink. |
| `--card-spacing` | Inner spacing shared by every part. Set by the size. |
| `--card-radius` | Corner radius. Nested cards derive theirs from the parent. |
| `--card-title-size` | Font size used by CardTitle. |

### CardHeader

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-header"` | A grid that places CardAction in the top end corner. |

### CardTitle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-title"` | Target the title. |

### CardDescription

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-description"` | Target the description. |

### CardAction

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-action"` | Spans the title and description rows, above a CardLink’s hit area. |

### CardContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-content"` | Passes the card radius and spacing down to nested cards. |

### CardFooter

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<div>` |  |

| Attribute | Description |
| --- | --- |
| `data-slot="card-footer"` | Target the footer. |

### CardLink

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `href` | `string` | – |  |
| `render` | `ReactElement \| (props, state) => ReactElement` | `<a>` | Pass your router’s link component here. |

| Attribute | Description |
| --- | --- |
| `data-slot="card-link"` | Stretches its hit area over the whole card. |

## Notes for AI assistants

- Install a component with the shadcn CLI: `npx shadcn@latest add https://hextaui.com/r/<name>.json`. It adds the source, the HextaUI theme tokens and any HextaUI components it depends on. `https://hextaui.com/r/all.json` installs every component.
- The code is then owned by the project, like shadcn/ui. There is no HextaUI npm package. HextaUI is MIT licensed and free for personal and commercial use.
- Behavior and accessibility come from Base UI (`@base-ui/react`). Compose with the `render` prop, not `asChild`.
- Styling uses Tailwind CSS v4 with theme tokens. Merge classes with `cn` from the `cn` package.
- Icons come from `@tabler/icons-react`.
- Import components from `@/components/ui/<name>`, hooks from `@/hooks/<name>` and utilities from `@/lib/<name>`.

Every HextaUI doc: https://hextaui.com/llms.txt
