{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"card-clean","type":"registry:ui","title":"Card (Clean)","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-clean/card-clean.tsx","type":"registry:ui","content":"\"use client\";\n\nimport type { ComponentPropsWithRef, ElementType, ReactNode } from \"react\";\n\n/*\n * Card – a single self-contained file, clean style.\n * The white card every screen is built from: a 0.5px hairline and a soft drop, a header with title, quiet line and an\n * action, the content and a footer. It can be a whole click target (a link or button in its title covers the card)\n * and show that it is selected with a gray fill.\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 gray fill 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  return (\n    <>\n      {/* React 19 hoists this into <head> once, no matter how many cards render */}\n      <style href=\"ui-card-clean\" precedence=\"default\">\n        {css}\n      </style>\n      <Tag\n        className={[\"ui-crdc\", className].filter(Boolean).join(\" \")}\n        data-interactive={interactive ? \"\" : undefined}\n        data-selected={selected ? \"\" : undefined}\n        {...rest}\n      >\n        {children}\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-crdc__head\", className].filter(Boolean).join(\" \")} {...rest}>\n      {has(icon) && <span className=\"ui-crdc__icon\">{icon}</span>}\n      <div className=\"ui-crdc__titles\">\n        {has(title) && <Title className=\"ui-crdc__title\">{title}</Title>}\n        {has(description) && <p className=\"ui-crdc__desc\">{description}</p>}\n        {children}\n      </div>\n      {has(action) && <div className=\"ui-crdc__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-crdc__body\", className].filter(Boolean).join(\" \")} {...rest} />;\n}\n\nexport type CardFooterProps = ComponentPropsWithRef<\"div\">;\n\n/** A row at the bottom: a status or meta on the start, buttons on the end (give the first button margin-inline-start: auto) */\nexport function CardFooter({ className, ...rest }: CardFooterProps) {\n  return <div className={[\"ui-crdc__foot\", className].filter(Boolean).join(\" \")} {...rest} />;\n}\n\n/* ---------- Styles ---------- */\n\nconst css = /* css */ `\n/* Colors as variables – override them on .ui-crdc (see Theming in the docs) */\n.ui-crdc {\n  --ui-crdc-surface: var(--aui-surface, #ffffff);\n  --ui-crdc-strong: var(--aui-ink, #000000);\n  --ui-crdc-ink: var(--aui-ink, rgba(0, 0, 0, 0.88));\n  --ui-crdc-muted: var(--aui-muted, rgba(0, 0, 0, 0.61));\n  --ui-crdc-shadow: var(--aui-shadow-card, 0 0 0 0.5px var(--aui-border, rgba(0, 0, 0, 0.06)), 0 1px 1px -1px rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.05));\n  --ui-crdc-shadow-hover: 0 0 0 0.5px var(--aui-border-strong, rgba(0, 0, 0, 0.1)), 0 2px 6px -1px rgba(0, 0, 0, 0.08), 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --ui-crdc-selected: color-mix(in srgb, var(--ui-crdc-ink) 3.5%, var(--ui-crdc-surface));\n  --ui-crdc-shadow-selected: 0 0 0 0.5px var(--aui-border-strong, rgba(0, 0, 0, 0.14)), 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n  --ui-crdc-focus: var(--aui-focus, rgba(0, 96, 226, 0.7));\n  --ui-crdc-radius: var(--aui-radius-lg, 10px);\n  --ui-crdc-pad: 16px;\n\n  position: relative;\n  display: flex;\n  flex-direction: column;\n  gap: 12px;\n  box-sizing: border-box;\n  min-width: 0;\n  margin: 0;\n  padding: var(--ui-crdc-pad);\n  border-radius: var(--ui-crdc-radius);\n  background: var(--ui-crdc-surface);\n  box-shadow: var(--ui-crdc-shadow);\n  color: var(--ui-crdc-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: box-shadow 160ms ease, background-color 160ms ease;\n}\n.ui-crdc *, .ui-crdc *::before, .ui-crdc *::after { box-sizing: border-box; }\n\n/* ----- Header: icon · title and quiet line · action ----- */\n.ui-crdc__head { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }\n.ui-crdc__icon { display: flex; flex: none; align-items: center; justify-content: center; min-width: 20px; min-height: 20px; color: var(--ui-crdc-muted); }\n/* A larger icon tile sits beside both lines; a 16px icon on the title's line */\n.ui-crdc__icon > svg { display: block; width: 16px; height: 16px; }\n.ui-crdc__titles { display: grid; flex: 1; gap: 2px; min-width: 0; }\n.ui-crdc__title { margin: 0; font-size: 13px; line-height: 20px; font-weight: 500; letter-spacing: 0; color: var(--ui-crdc-strong); overflow-wrap: anywhere; }\n.ui-crdc__desc { margin: 0; font-size: 13px; line-height: 20px; color: var(--ui-crdc-muted); overflow-wrap: anywhere; }\n/* The action sits on the title's line, centred on it – a 28px button reaches 4px above and below */\n.ui-crdc__action { display: flex; flex: none; align-items: center; gap: 4px; height: 20px; }\n\n/* ----- Content and footer ----- */\n.ui-crdc__body { min-width: 0; overflow-wrap: anywhere; }\n.ui-crdc__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; min-height: 28px; font-size: 12px; line-height: 16px; color: var(--ui-crdc-muted); }\n\n/* ----- Interactive: the whole card is a click target ----- */\n.ui-crdc[data-interactive] { cursor: pointer; }\n.ui-crdc[data-interactive]:hover { box-shadow: var(--ui-crdc-shadow-hover); }\n/* The title's link or button reads like the title and stretches over the whole card */\n.ui-crdc[data-interactive] .ui-crdc__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-crdc[data-interactive] .ui-crdc__title :is(a, button)::after {\n  content: \"\";\n  position: absolute;\n  inset: 0;\n  z-index: 0;\n  border-radius: var(--ui-crdc-radius);\n}\n/* Other controls in the card stay clickable above the stretched title */\n.ui-crdc[data-interactive] :is(a, button, input, select, textarea, label, summary, [tabindex]):not(.ui-crdc__title *) {\n  position: relative;\n  z-index: 1;\n}\n/* The ring goes around the whole card, not the title text */\n.ui-crdc[data-interactive] .ui-crdc__title :is(a, button):focus-visible { outline: none; }\n.ui-crdc[data-interactive] .ui-crdc__title :is(a, button):focus-visible::after {\n  outline: 2px solid var(--ui-crdc-focus);\n  outline-offset: 2px;\n}\n\n/* ----- Selected: a gray fill and a firmer edge, no colored ring ----- */\n.ui-crdc[data-selected] { background: var(--ui-crdc-selected); box-shadow: var(--ui-crdc-shadow-selected); }\n.ui-crdc[data-selected][data-interactive]:hover { box-shadow: var(--ui-crdc-shadow-selected); }\n\n/* Touch screens: no hover lift that sticks after a tap */\n@media (hover: none) {\n  .ui-crdc[data-interactive]:hover:not([data-selected]) { box-shadow: var(--ui-crdc-shadow); }\n}\n@media (prefers-reduced-motion: reduce) {\n  .ui-crdc { transition: none; }\n}\n/* Windows high contrast: shadows are dropped – a border keeps the card's edge */\n@media (forced-colors: active) {\n  .ui-crdc { border: 1px solid CanvasText; }\n  .ui-crdc[data-selected] { border: 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-crdc {\n  --ui-crdc-surface: var(--aui-surface, #1b1b1e);\n  --ui-crdc-strong: var(--aui-ink, #ffffff);\n  --ui-crdc-ink: var(--aui-ink, rgba(255, 255, 255, 0.9));\n  --ui-crdc-muted: var(--aui-muted, rgba(255, 255, 255, 0.62));\n  --ui-crdc-shadow: var(--aui-shadow-card, 0 0 0 0.5px var(--aui-border, rgba(255, 255, 255, 0.1)), 0 1px 2px rgba(0, 0, 0, 0.4));\n  --ui-crdc-shadow-hover: 0 0 0 0.5px var(--aui-border-strong, rgba(255, 255, 255, 0.16)), 0 2px 6px rgba(0, 0, 0, 0.4);\n  --ui-crdc-selected: color-mix(in srgb, var(--ui-crdc-ink) 6%, var(--ui-crdc-surface));\n  --ui-crdc-shadow-selected: 0 0 0 0.5px var(--aui-border-strong, rgba(255, 255, 255, 0.2)), 0 1px 2px rgba(0, 0, 0, 0.4);\n  --ui-crdc-focus: var(--aui-focus, rgba(77, 141, 255, 0.8));\n}\n`;\n"}]}