{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card",
  "title": "Card",
  "description": "Surface/raised/glass/outline card with interactive lift and slot subcomponents.",
  "dependencies": [
    "@radix-ui/react-slot",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@hjconsult/lib",
    "@hjconsult/heading",
    "@hjconsult/text"
  ],
  "files": [
    {
      "path": "src/components/card/index.tsx",
      "content": "import { Slot } from '@radix-ui/react-slot'\nimport type { ComponentPropsWithoutRef, Ref } from 'react'\nimport { cn, cva, focusRing, type VariantProps } from '../../lib'\nimport { Heading, type HeadingProps } from '../heading'\nimport { Text, type TextProps } from '../text'\n\nconst lift =\n  'cursor-pointer hover:-translate-y-0.5 hover:border-line-strong hover:shadow-2 active:translate-y-0'\n\nexport const cardVariants = cva(\n  [\n    'group/card relative flex flex-col rounded-2xl text-fg',\n    'motion-safe:transition motion-safe:duration-[var(--hj-motion-duration-base)] motion-safe:ease-out-expo',\n  ],\n  {\n    variants: {\n      variant: {\n        surface: 'border border-line bg-surface',\n        raised: 'border border-line bg-surface-raised shadow-1',\n        glass: 'border border-glass-line bg-glass backdrop-blur-xl',\n        outline: 'border border-line-strong bg-transparent',\n      },\n      padding: {\n        none: 'p-0',\n        sm: 'p-4',\n        md: 'p-6',\n        lg: 'p-8',\n      },\n      interactive: {\n        true: [lift, focusRing],\n        /**\n         * Lifts like `true`, but the click target is a `Card.Link` inside the card (usually in\n         * the title), stretched over it, and the ring follows that link's focus.\n         */\n        link: [\n          lift,\n          'has-[[data-card-link]:focus-visible]:ring-2 has-[[data-card-link]:focus-visible]:ring-focus',\n          'has-[[data-card-link]:focus-visible]:ring-offset-2 has-[[data-card-link]:focus-visible]:ring-offset-canvas',\n        ],\n        false: '',\n      },\n    },\n    defaultVariants: { variant: 'surface', padding: 'md', interactive: false },\n  },\n)\n\nexport interface CardProps\n  extends ComponentPropsWithoutRef<'div'>,\n    VariantProps<typeof cardVariants> {\n  as?: 'div' | 'article' | 'section' | 'li' | 'a' | 'button'\n  /** Render the child element (e.g. Next.js `Link`) as the card surface. */\n  asChild?: boolean\n  ref?: Ref<HTMLDivElement>\n}\n\nfunction CardRoot({\n  as = 'div',\n  asChild = false,\n  variant,\n  padding,\n  interactive,\n  className,\n  ref,\n  ...props\n}: CardProps) {\n  const Comp = (asChild ? Slot : as) as 'div'\n  const isInteractive = interactive === true\n  return (\n    <Comp\n      ref={ref}\n      className={cn(\n        cardVariants({\n          variant,\n          padding,\n          interactive: interactive === 'link' ? 'link' : isInteractive,\n        }),\n        className,\n      )}\n      tabIndex={isInteractive && !asChild && as !== 'a' && as !== 'button' ? 0 : undefined}\n      {...props}\n    />\n  )\n}\n\nexport interface CardHeaderProps extends ComponentPropsWithoutRef<'div'> {\n  ref?: Ref<HTMLDivElement>\n}\n\nfunction CardHeader({ className, ...props }: CardHeaderProps) {\n  return <div className={cn('flex flex-col gap-1.5', className)} {...props} />\n}\n\nexport type CardTitleProps = HeadingProps\n\nfunction CardTitle({ as = 'h3', size = 'h4', className, ...props }: CardTitleProps) {\n  return <Heading as={as} size={size} className={cn('text-fg', className)} {...props} />\n}\n\nexport type CardDescriptionProps = TextProps\n\nfunction CardDescription({ size = 'small', tone = 'muted', ...props }: CardDescriptionProps) {\n  return <Text size={size} tone={tone} {...props} />\n}\n\nexport interface CardBodyProps extends ComponentPropsWithoutRef<'div'> {\n  ref?: Ref<HTMLDivElement>\n}\n\nfunction CardBody({ className, ...props }: CardBodyProps) {\n  return (\n    <div className={cn('flex flex-1 flex-col gap-4 pt-4 text-fg-muted', className)} {...props} />\n  )\n}\n\nexport interface CardFooterProps extends ComponentPropsWithoutRef<'div'> {\n  ref?: Ref<HTMLDivElement>\n}\n\nfunction CardFooter({ className, ...props }: CardFooterProps) {\n  return <div className={cn('mt-auto flex items-center gap-3 pt-6', className)} {...props} />\n}\n\nexport interface CardLinkProps extends ComponentPropsWithoutRef<'a'> {\n  /** Render the child element (e.g. Next.js `Link`) as the link. */\n  asChild?: boolean\n  ref?: Ref<HTMLAnchorElement>\n}\n\n/**\n * The click target of an `interactive=\"link\"` card: a link, usually the title, stretched over\n * the whole card by a pseudo-element. The card still clicks anywhere, while the link is named by\n * what it shows and everything else on the card stays text a screen reader reads.\n */\nfunction CardLink({ asChild = false, className, ...props }: CardLinkProps) {\n  const Comp = (asChild ? Slot : 'a') as 'a'\n  return (\n    <Comp\n      data-card-link=\"\"\n      className={cn(\n        \"after:absolute after:inset-0 after:content-[''] focus-visible:outline-none\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\n/**\n * Surface for grouped content. `interactive` adds lift, shadow and a focus ring; pair it with\n * `asChild` and a link so the whole card is one click target, when the card is little more than\n * its name. A card that holds more (a summary, tags, a date) takes `interactive=\"link\"` and a\n * `Card.Link` in its title instead: a link is named by everything inside it, and an `aria-label`\n * that shortens the name replaces the content, so screen readers skip it.\n */\nexport const Card = Object.assign(CardRoot, {\n  Header: CardHeader,\n  Title: CardTitle,\n  Link: CardLink,\n  Description: CardDescription,\n  Body: CardBody,\n  Footer: CardFooter,\n})\n\nexport { CardBody, CardDescription, CardFooter, CardHeader, CardLink, CardTitle }\n",
      "type": "registry:ui",
      "target": "hj/components/card/index.tsx"
    }
  ],
  "cssVars": {},
  "type": "registry:ui"
}