{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "card",
  "title": "Card",
  "description": "A bordered surface that groups related content, with a header, content and footer.",
  "dependencies": [
    "@base-ui/react",
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json"
  ],
  "files": [
    {
      "path": "src/components/primitives/card.tsx",
      "content": "import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport type { ComponentProps } from \"react\";\n\nimport type { Sx } from \"@/lib/constella/styles\";\nimport { typeStyles } from \"@/lib/constella/styles\";\nimport {\n  cardVars,\n  colors,\n  fontSizes,\n  fontWeights,\n  lineHeights,\n  radii,\n  space,\n  strokes,\n} from \"@/lib/constella/tokens.stylex\";\n\nconst cardSizes = [\"sm\", \"default\", \"flush\"] as const;\nconst cardVariants = [\"default\", \"well\", \"filled\"] as const;\n\ntype CardSize = (typeof cardSizes)[number];\ntype CardVariant = (typeof cardVariants)[number];\n\ntype Styled<T> = Omit<T, \"className\" | \"style\"> & {\n  sx?: Sx;\n};\n\ntype CardProps = Styled<ComponentProps<\"div\">> & {\n  size?: CardSize;\n  variant?: CardVariant;\n};\n\nconst inWell = \":is([data-slot='card'][data-variant='well'] > *)\";\nconst hasFooter = \":has(> [data-slot='card-footer'])\";\n// In a well, a header's text lines up with the text of the card under it.\nconst wellInset = `calc(${space.md} + ${strokes.border})`;\nconst corner = { default: radii.lg, [inWell]: radii.md };\n\nconst styles = stylex.create({\n  card: {\n    backgroundColor: colors.raised,\n    borderBlockEndColor: colors.border,\n    borderBlockStartColor: colors.border,\n    borderInlineEndColor: colors.border,\n    borderInlineStartColor: colors.border,\n    borderBlockEndWidth: strokes.border,\n    borderBlockStartWidth: strokes.border,\n    borderInlineEndWidth: strokes.border,\n    borderInlineStartWidth: strokes.border,\n    borderBlockEndStyle: \"solid\",\n    borderBlockStartStyle: \"solid\",\n    borderInlineEndStyle: \"solid\",\n    borderInlineStartStyle: \"solid\",\n    borderStartStartRadius: corner,\n    borderStartEndRadius: corner,\n    borderEndStartRadius: corner,\n    borderEndEndRadius: corner,\n    color: colors.textPrimary,\n    display: \"flex\",\n    flexDirection: \"column\",\n    gap: cardVars.spacing,\n    overflow: \"hidden\",\n    paddingBlockEnd: { default: cardVars.spacing, [hasFooter]: 0 },\n    paddingBlockStart: cardVars.spacing,\n  },\n  default: { [cardVars.spacing]: space.md },\n  sm: { [cardVars.spacing]: space.sm },\n  flush: { [cardVars.spacing]: \"0px\" },\n  // A tinted tray around a card and the rows that label it, a ladder step out from the card.\n  well: {\n    [cardVars.spacing]: space.xxs,\n    backgroundColor: colors.fillSubtle,\n    paddingBlockEnd: cardVars.spacing,\n    paddingInlineEnd: cardVars.spacing,\n    paddingInlineStart: cardVars.spacing,\n  },\n  // A flat tinted surface with no edge, for a section or stat that sits on the page.\n  filled: {\n    backgroundColor: colors.fillSubtle,\n    borderBlockEndWidth: 0,\n    borderBlockStartWidth: 0,\n    borderInlineEndWidth: 0,\n    borderInlineStartWidth: 0,\n  },\n  header: {\n    alignItems: \"start\",\n    display: \"grid\",\n    gap: space.xxs,\n    gridAutoRows: \"min-content\",\n    gridTemplateColumns: {\n      default: null,\n      \":has(> [data-slot='card-action'])\": \"minmax(0, 1fr) auto\",\n    },\n    paddingBlockEnd: { default: 0, [inWell]: space.xs },\n    paddingBlockStart: { default: 0, [inWell]: space.xs },\n    paddingInlineEnd: { default: cardVars.spacing, [inWell]: wellInset },\n    paddingInlineStart: { default: cardVars.spacing, [inWell]: wellInset },\n  },\n  title: {\n    fontSize: {\n      default: fontSizes.textBase,\n      \":is([data-slot='card'][data-size='sm'] *)\": fontSizes.textSm,\n    },\n    fontWeight: fontWeights.medium,\n    lineHeight: {\n      default: lineHeights.textBase,\n      \":is([data-slot='card'][data-size='sm'] *)\": lineHeights.textSm,\n    },\n    marginBlock: 0,\n    overflowWrap: \"anywhere\",\n    textWrap: \"balance\",\n  },\n  description: {\n    color: colors.textSecondary,\n    overflowWrap: \"anywhere\",\n    textWrap: \"pretty\",\n  },\n  action: {\n    alignSelf: \"start\",\n    gridColumnStart: 2,\n    // Spans the description's row only when there is one, so a lone title gets no empty row.\n    gridRowEnd: {\n      default: \"span 1\",\n      \":is(:has(> [data-slot='card-description']) > *)\": \"span 2\",\n    },\n    gridRowStart: 1,\n    justifySelf: \"end\",\n  },\n  content: {\n    paddingInlineEnd: cardVars.spacing,\n    paddingInlineStart: cardVars.spacing,\n  },\n  // The dialog's footer: a tinted bar set into the card's bottom edge.\n  footer: {\n    alignItems: \"center\",\n    backgroundColor: colors.fillSubtle,\n    borderBlockStartColor: colors.borderSubtle,\n    borderBlockStartStyle: \"solid\",\n    borderBlockStartWidth: strokes.border,\n    display: \"flex\",\n    gap: space.xs,\n    paddingBlockEnd: cardVars.spacing,\n    paddingBlockStart: cardVars.spacing,\n    paddingInlineEnd: cardVars.spacing,\n    paddingInlineStart: cardVars.spacing,\n  },\n});\n\nconst sizeStyles = {\n  default: styles.default,\n  flush: styles.flush,\n  sm: styles.sm,\n} satisfies Record<CardSize, stylex.StyleXStyles>;\n\nfunction Card({\n  size = \"default\",\n  sx,\n  variant = \"default\",\n  ...props\n}: CardProps) {\n  return (\n    <div\n      data-size={size}\n      data-slot=\"card\"\n      data-variant={variant}\n      {...props}\n      {...stylex.props(\n        [typeStyles.textSm, styles.card],\n        sizeStyles[size],\n        variant === \"well\" && styles.well,\n        variant === \"filled\" && styles.filled,\n        sx\n      )}\n    />\n  );\n}\n\nfunction CardHeader({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"card-header\"\n      {...props}\n      {...stylex.props(styles.header, sx)}\n    />\n  );\n}\n\nfunction CardTitle({\n  render,\n  sx,\n  ...props\n}: Styled<useRender.ComponentProps<\"div\">>) {\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(props, stylex.props(styles.title, sx)),\n    render,\n    state: { slot: \"card-title\" },\n  });\n}\n\nfunction CardDescription({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"card-description\"\n      {...props}\n      {...stylex.props([typeStyles.textSm, styles.description], sx)}\n    />\n  );\n}\n\nfunction CardAction({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"card-action\"\n      {...props}\n      {...stylex.props(styles.action, sx)}\n    />\n  );\n}\n\nfunction CardContent({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"card-content\"\n      {...props}\n      {...stylex.props(styles.content, sx)}\n    />\n  );\n}\n\nfunction CardFooter({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"card-footer\"\n      {...props}\n      {...stylex.props(styles.footer, sx)}\n    />\n  );\n}\n\nexport {\n  Card,\n  CardAction,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  cardSizes,\n  CardTitle,\n  cardVariants,\n};\nexport type { CardProps, CardSize, CardVariant };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/card/index.md",
  "type": "registry:ui"
}