{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"card","type":"registry:ui","title":"Card","description":"The card every screen is built from – header with title, quiet line and an action, content and a footer, the whole card a click target and a gray fill when selected","dependencies":[],"files":[{"path":"registry/card/card.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { Children, isValidElement, type ComponentPropsWithRef, type ElementType, type ReactNode } from \"react\";\n\n/*\n * Card – a single self-contained file.\n * A white card on a gray tray, like the classic items: a header with title, quiet line and an action, the content –\n * and a footer that sits on the tray below the card. It can be a whole click target (a link or button in its title\n * covers the card) and show that it is selected with a darker tray.\n * The styles are injected below, so no extra files are needed.\n */\n\ntype CardElement = \"div\" | \"section\" | \"article\" | \"li\";\n\nexport type CardProps = ComponentPropsWithRef<\"div\"> & {\n  /** The element it renders as – section or article for a landmark-like card, li inside a list of cards */\n  as?: CardElement;\n  /**\n   * The whole card is a click target: it lifts on hover, and a link or button in its title (CardHeader's title)\n   * covers the card – other buttons inside stay clickable on their own\n   */\n  interactive?: boolean;\n  /** Shown as picked: a darker tray and a firmer edge. Put aria-pressed or aria-current on the title's button or link */\n  selected?: boolean;\n};\n\nexport function Card({ as = \"div\", interactive, selected, className, children, ...rest }: CardProps) {\n  const Tag = ([\"div\", \"section\", \"article\", \"li\"].includes(as) ? as : \"div\") as ElementType;\n  // The footer sits on the gray tray under the white card – everything else goes inside the card\n  const items = Children.toArray(children);\n  const isFoot = (c: unknown) => isValidElement(c) && c.type === CardFooter;\n  const main = items.filter((c) => !isFoot(c));\n  const foot = items.filter(isFoot);\n  return (\n    <>\n      {/* React 19 hoists this into <head> once, no matter how many cards render */}\n      <style href=\"ui-card\" precedence=\"default\">\n        {css}\n      </style>\n      <Tag\n        className={[\"ui-crd\", className].filter(Boolean).join(\" \")}\n        data-interactive={interactive ? \"\" : undefined}\n        data-selected={selected ? \"\" : undefined}\n        {...rest}\n      >\n        {main.length > 0 && <div className=\"ui-crd__card\">{main}</div>}\n        {foot}\n      </Tag>\n    </>\n  );\n}\n\nexport type CardHeaderProps = Omit<ComponentPropsWithRef<\"div\">, \"title\"> & {\n  /** The title – text, or a link / button that makes the whole card clickable when the Card is interactive */\n  title?: ReactNode;\n  /** A quiet line or two under the title */\n  description?: ReactNode;\n  /** Something small at the start: an icon, a logo, an agent's mark */\n  icon?: ReactNode;\n  /** Something at the end of the title line: an icon button, a menu, a badge */\n  action?: ReactNode;\n  /** The title's element – h3 by default; pick the level that fits the page's outline */\n  titleAs?: \"h2\" | \"h3\" | \"h4\" | \"p\";\n};\n\nexport function CardHeader({ title, description, icon, action, titleAs = \"h3\", className, children, ...rest }: CardHeaderProps) {\n  const Title = ([\"h2\", \"h3\", \"h4\", \"p\"].includes(titleAs) ? titleAs : \"h3\") as ElementType;\n  const has = (n: ReactNode) => n != null && n !== false && n !== \"\";\n  return (\n    <div className={[\"ui-crd__head\", className].filter(Boolean).join(\" \")} {...rest}>\n      {has(icon) && <span className=\"ui-crd__icon\">{icon}</span>}\n      <div className=\"ui-crd__titles\">\n        {has(title) && <Title className=\"ui-crd__title\">{title}</Title>}\n        {has(description) && <p className=\"ui-crd__desc\">{description}</p>}\n        {children}\n      </div>\n      {has(action) && <div className=\"ui-crd__action\">{action}</div>}\n    </div>\n  );\n}\n\nexport type CardContentProps = ComponentPropsWithRef<\"div\">;\n\nexport function CardContent({ className, ...rest }: CardContentProps) {\n  return <div className={[\"ui-crd__body\", className].filter(Boolean).join(\" \")} {...rest} />;\n}\n\nexport type CardFooterProps = ComponentPropsWithRef<\"div\">;\n\n/** A row on the tray under the card: a status or meta on the start, buttons on the end (give the first button margin-inline-start: auto). Put it directly inside Card */\nexport function CardFooter({ className, ...rest }: CardFooterProps) {\n  return <div className={[\"ui-crd__foot\", className].filter(Boolean).join(\" \")} {...rest} />;\n}\n\n/* ---------- Styles ---------- */\n\nconst css = /* css */ `\n/* Colors as variables – override them on .ui-crd (see Theming in the docs) */\n.ui-crd {\n  --ui-crd-tray: var(--aui-subtle, #f0f0f0);\n  --ui-crd-tray-selected: #e6e6e6;\n  --ui-crd-surface: var(--aui-surface, #ffffff);\n  --ui-crd-strong: var(--aui-ink, #171717);\n  --ui-crd-ink: var(--aui-ink, #404040);\n  --ui-crd-muted: var(--aui-muted, #6b6b6b);\n  --ui-crd-faint: #8f8f8f;\n  --ui-crd-line: var(--aui-border, #ececec);\n  --ui-crd-line-strong: var(--aui-border-strong, #d9d9d9);\n  --ui-crd-shadow: var(--aui-shadow-card, 0 0 0 1px var(--ui-crd-line), 0 1px 2px rgba(0, 0, 0, 0.04));\n  --ui-crd-shadow-hover: 0 0 0 1px var(--ui-crd-line-strong), 0 2px 6px -1px rgba(0, 0, 0, 0.08);\n  --ui-crd-focus: var(--aui-focus, rgba(0, 96, 226, 0.7));\n  --ui-crd-radius: calc(var(--aui-radius-lg, 16px) + 4px);\n  --ui-crd-pad: 16px;\n\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  box-sizing: border-box;\n  min-width: 0;\n  margin: 0;\n  padding: 4px;\n  border-radius: var(--ui-crd-radius);\n  background: var(--ui-crd-tray);\n  color: var(--ui-crd-ink);\n  font-family: var(--aui-font, \"Inter\", \"InterVariable\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n  font-size: 13px;\n  line-height: 20px;\n  text-align: start;\n  list-style: none;\n  -webkit-font-smoothing: antialiased;\n  transition: background-color 160ms ease;\n}\n.ui-crd *, .ui-crd *::before, .ui-crd *::after { box-sizing: border-box; }\n\n/* The white card on the tray */\n.ui-crd__card {\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  min-width: 0;\n  padding: var(--ui-crd-pad);\n  border-radius: var(--aui-radius-lg, 16px);\n  background: var(--ui-crd-surface);\n  box-shadow: var(--ui-crd-shadow);\n  transition: box-shadow 160ms ease;\n}\n\n/* ----- Header: icon · title and quiet line · action ----- */\n.ui-crd__head { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }\n.ui-crd__icon { display: flex; flex: none; align-items: center; justify-content: center; min-width: 20px; min-height: 20px; color: var(--ui-crd-muted); }\n.ui-crd__icon > svg { display: block; width: 16px; height: 16px; }\n.ui-crd__titles { display: grid; flex: 1; gap: 2px; min-width: 0; }\n.ui-crd__title { margin: 0; font-size: 14px; line-height: 20px; font-weight: 500; letter-spacing: -0.01em; color: var(--ui-crd-strong); overflow-wrap: anywhere; }\n.ui-crd__desc { margin: 0; font-size: 13px; line-height: 20px; color: var(--ui-crd-muted); overflow-wrap: anywhere; }\n/* The action sits on the title's line, centred on it – a 32px button reaches 6px above and below */\n.ui-crd__action { display: flex; flex: none; align-items: center; gap: 4px; height: 20px; }\n\n/* ----- Content ----- */\n.ui-crd__body { min-width: 0; overflow-wrap: anywhere; }\n\n/* ----- Footer on the tray: text lines up with the card's text, a 32px button sits 6px from the tray's edges ----- */\n.ui-crd__foot {\n  display: flex;\n  flex-wrap: wrap;\n  align-items: center;\n  gap: 8px;\n  min-width: 0;\n  min-height: 40px;\n  padding-block: 4px 0;\n  padding-inline: var(--ui-crd-pad) 2px;\n  font-size: 12.5px;\n  line-height: 18px;\n  color: var(--ui-crd-faint);\n}\n/* A footer alone, without a card above it */\n.ui-crd__foot:first-child { padding-block: 0; }\n\n/* ----- Interactive: the whole card is a click target ----- */\n.ui-crd[data-interactive] { cursor: pointer; }\n.ui-crd[data-interactive]:hover .ui-crd__card { box-shadow: var(--ui-crd-shadow-hover); }\n/* The title's link or button reads like the title and stretches over the whole card */\n.ui-crd[data-interactive] .ui-crd__title :is(a, button) {\n  margin: 0;\n  padding: 0;\n  border: 0;\n  background: none;\n  font: inherit;\n  color: inherit;\n  text-align: inherit;\n  text-decoration: none;\n  cursor: pointer;\n  -webkit-tap-highlight-color: transparent;\n}\n.ui-crd[data-interactive] .ui-crd__title :is(a, button)::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  z-index: 0;\n  border-radius: var(--ui-crd-radius);\n}\n/* Other controls in the card stay clickable above the stretched title */\n.ui-crd[data-interactive] :is(a, button, input, select, textarea, label, summary, [tabindex]):not(.ui-crd__title *) {\n  position: relative;\n  z-index: 1;\n}\n/* The ring goes around the whole tray, not the title text */\n.ui-crd[data-interactive] .ui-crd__title :is(a, button):focus-visible { outline: none; }\n.ui-crd[data-interactive] .ui-crd__title :is(a, button):focus-visible::after {\n  outline: 2px solid var(--ui-crd-focus);\n  outline-offset: 2px;\n}\n\n/* ----- Selected: a darker tray and a firmer edge, no colored ring ----- */\n.ui-crd[data-selected] { background: var(--ui-crd-tray-selected); }\n.ui-crd[data-selected] .ui-crd__card { box-shadow: 0 0 0 1px var(--ui-crd-line-strong), 0 1px 2px rgba(0, 0, 0, 0.04); }\n\n/* Touch screens: no hover lift that sticks after a tap */\n@media (hover: none) {\n  .ui-crd[data-interactive]:hover:not([data-selected]) .ui-crd__card { box-shadow: var(--ui-crd-shadow); }\n}\n@media (prefers-reduced-motion: reduce) {\n  .ui-crd, .ui-crd__card { transition: none; }\n}\n/* Windows high contrast: shadows are dropped – a border keeps the card's edge */\n@media (forced-colors: active) {\n  .ui-crd__card { border: 1px solid CanvasText; }\n  .ui-crd[data-selected] { outline: 2px solid Highlight; }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-crd {\n  --ui-crd-tray: var(--aui-subtle, #111113);\n  --ui-crd-tray-selected: #26262a;\n  --ui-crd-surface: var(--aui-surface, #19191b);\n  --ui-crd-strong: var(--aui-ink, #ededed);\n  --ui-crd-ink: var(--aui-ink, #c8c8cc);\n  --ui-crd-muted: var(--aui-muted, #a1a1a6);\n  --ui-crd-faint: #8b8b92;\n  --ui-crd-line: var(--aui-border, #2a2a2e);\n  --ui-crd-line-strong: var(--aui-border-strong, #3a3a40);\n  --ui-crd-shadow: var(--aui-shadow-card, 0 0 0 1px var(--ui-crd-line));\n  --ui-crd-shadow-hover: 0 0 0 1px var(--ui-crd-line-strong), 0 2px 6px rgba(0, 0, 0, 0.4);\n  --ui-crd-focus: var(--aui-focus, rgba(77, 141, 255, 0.8));\n}\n`;\n"}]}