{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stat-card",
  "type": "registry:ui",
  "title": "Stat Card",
  "description": "Metric tile with label, value, optional icon and a trend row.",
  "dependencies": [
    "@mantine/core@^9",
    "@tabler/icons-react@^3"
  ],
  "files": [
    {
      "path": "registry/ui/stat-card.tsx",
      "type": "registry:ui",
      "content": "import {\n  type BoxProps,\n  type ElementProps,\n  type Factory,\n  factory,\n  Group,\n  Paper,\n  type StylesApiProps,\n  Text,\n  ThemeIcon,\n  useProps,\n  useStyles,\n} from \"@mantine/core\";\nimport { IconTrendingDown, IconTrendingUp } from \"@tabler/icons-react\";\nimport type { ReactNode } from \"react\";\n\nimport classes from \"./stat-card.module.css\";\n\nexport type StatCardStylesNames = \"root\" | \"label\" | \"value\" | \"icon\" | \"diff\";\n\nexport interface StatCardProps\n  extends BoxProps,\n    StylesApiProps<StatCardFactory>,\n    ElementProps<\"div\"> {\n  label: string;\n  value: ReactNode;\n  /** Percentage change vs. the previous period. Omit to hide the trend row. */\n  diff?: number;\n  icon?: ReactNode;\n}\n\nexport type StatCardFactory = Factory<{\n  props: StatCardProps;\n  ref: HTMLDivElement;\n  stylesNames: StatCardStylesNames;\n}>;\n\nexport const StatCard = factory<StatCardFactory>((_props) => {\n  const props = useProps(\"StatCard\", null, _props);\n  const {\n    classNames,\n    className,\n    style,\n    styles,\n    unstyled,\n    vars,\n    attributes,\n    ref,\n    label,\n    value,\n    diff,\n    icon,\n    ...others\n  } = props;\n\n  const getStyles = useStyles<StatCardFactory>({\n    name: \"StatCard\",\n    classes,\n    props,\n    className,\n    style,\n    classNames,\n    styles,\n    unstyled,\n    attributes,\n    vars,\n  });\n\n  const positive = (diff ?? 0) >= 0;\n\n  return (\n    <Paper\n      ref={ref}\n      withBorder\n      p=\"md\"\n      radius=\"md\"\n      unstyled={unstyled}\n      {...getStyles(\"root\")}\n      {...others}\n    >\n      <Group justify=\"space-between\" wrap=\"nowrap\">\n        <div>\n          <Text {...getStyles(\"label\")}>{label}</Text>\n          <Text {...getStyles(\"value\")}>{value}</Text>\n        </div>\n        {icon && (\n          <ThemeIcon variant=\"light\" size={38} radius=\"md\" {...getStyles(\"icon\")}>\n            {icon}\n          </ThemeIcon>\n        )}\n      </Group>\n\n      {diff !== undefined && (\n        <Group gap={4} mt=\"xs\" {...getStyles(\"diff\")}>\n          <ThemeIcon variant=\"transparent\" size=\"sm\" c={positive ? \"teal\" : \"red\"}>\n            {positive ? <IconTrendingUp size={16} /> : <IconTrendingDown size={16} />}\n          </ThemeIcon>\n          <Text size=\"sm\" c={positive ? \"teal\" : \"red\"} fw={500}>\n            {positive ? \"+\" : \"\"}\n            {diff}%\n          </Text>\n          <Text size=\"sm\" c=\"dimmed\">\n            vs last period\n          </Text>\n        </Group>\n      )}\n    </Paper>\n  );\n});\n\nStatCard.classes = classes;\nStatCard.displayName = \"StatCard\";\n\nexport namespace StatCard {\n  export type Props = StatCardProps;\n  export type StylesNames = StatCardStylesNames;\n  export type Factory = StatCardFactory;\n}\n"
    },
    {
      "path": "registry/ui/stat-card.module.css",
      "type": "registry:file",
      "target": "@ui/stat-card.module.css",
      "content": ".root {\n  /*\n   * Structural anchor for the Styles API root selector. The visual\n   * defaults (withBorder, padding, radius) stay as component props on\n   * Paper, not CSS, so a caller's passthrough props can still override\n   * them exactly as they could pre-conversion.\n   */\n}\n\n.label {\n  /*\n   * Matches Text's own size=\"xs\" resolution (both --text-fz and --text-lh\n   * derive from `size` in Text's varsResolver), so the default render is\n   * unchanged now that the value moved from a prop to this class.\n   */\n  font-size: var(--mantine-font-size-xs);\n  line-height: var(--mantine-line-height-xs);\n  color: var(--mantine-color-dimmed);\n  text-transform: uppercase;\n  font-weight: 600;\n}\n\n.value {\n  /*\n   * fz={28} and mt={4} previously resolved through Mantine's rem()\n   * converter (value/16 -> rem, scaled by --mantine-scale). Reproduced\n   * here verbatim so the default render doesn't drift under a themed\n   * `scale` override. lh={1.2} was a unitless number, passed through\n   * as-is by lineHeightResolver, so it needs no such conversion.\n   */\n  font-size: calc(1.75rem * var(--mantine-scale));\n  font-weight: 700;\n  line-height: 1.2;\n  margin-top: calc(0.25rem * var(--mantine-scale));\n}\n\n.icon {\n  /*\n   * Structural selector for the optional ThemeIcon slot. Its visual\n   * defaults (variant, size, radius) stay as component props so a\n   * caller's passthrough props can still override them.\n   */\n}\n\n.diff {\n  /*\n   * Structural selector for the trend row (arrow ThemeIcon + diff text +\n   * \"vs last period\" caption share this one selector, per the Styles API\n   * conversion guide's rule that conditional/repeated parts of the same\n   * kind get one name rather than one each). Colors stay dynamic props\n   * (positive/negative) rather than CSS since they depend on the diff sign.\n   */\n}\n"
    }
  ],
  "meta": {
    "mantine": {
      "requires": ">=9",
      "stylesApi": {
        "StatCard": [
          "root",
          "label",
          "value",
          "icon",
          "diff"
        ]
      },
      "props": {
        "StatCard": [
          {
            "name": "label",
            "type": "string",
            "required": true,
            "description": "Renders the uppercase, muted caption identifying what the stat measures."
          },
          {
            "name": "value",
            "type": "ReactNode",
            "required": true,
            "description": "Renders the large, bold headline figure for the stat."
          },
          {
            "name": "diff",
            "type": "number",
            "required": false,
            "description": "Percentage change vs. the previous period; renders a colored trend row with an up/down arrow, and is hidden entirely when omitted."
          },
          {
            "name": "icon",
            "type": "ReactNode",
            "required": false,
            "description": "Renders inside a light-variant ThemeIcon at the top-right of the card; the icon slot is omitted entirely when not provided."
          },
          {
            "name": "...others",
            "type": "BoxProps & ElementProps<\"div\">",
            "description": "All other props are forwarded to the root Paper, after its own withBorder/p=\"md\"/radius=\"md\", so both can be overridden. Mantine Styles API props (classNames, styles, unstyled) are also accepted."
          }
        ]
      },
      "usage": {
        "path": "registry/ui/stat-card.usage.tsx",
        "content": "import { SimpleGrid } from \"@mantine/core\";\nimport { IconCoin, IconUsers } from \"@tabler/icons-react\";\nimport { StatCard } from \"@/components/ui/stat-card\";\n\nexport function RevenueOverview() {\n  return (\n    <SimpleGrid cols={2}>\n      <StatCard label=\"Revenue\" value=\"$48,200\" diff={12.4} icon={<IconCoin size={20} />} />\n      <StatCard label=\"Active users\" value=\"3,128\" diff={-4.1} icon={<IconUsers size={20} />} />\n    </SimpleGrid>\n  );\n}\n"
      }
    }
  }
}
