Files
HealthLog/src/components/ui/card.tsx
T
Marc-André Bombeck 14d0eeeb46 fix(ui): stop CardHeader drawing a phantom gap without a description
grid-rows-[auto_auto] was unconditional, so a header with a title and
no CardDescription still reserved an empty second row track and drew
the gap-2 for it — 32px instead of the intended 24px between heading
and body on desktop. Make the second row conditional on a description
being present, and widen CardAction to row-span-full so it still
tracks the header's row count when an action lands in a
description-less card.

Also correct two assessment cards that had drifted onto gap-1.5
instead of the sanctioned gap-2 compact tier.
2026-07-30 15:50:14 +02:00

109 lines
2.7 KiB
TypeScript

import * as React from "react";
import { cn } from "@/lib/utils";
/**
* v1.4.33 IW9 — Card padding normalised on `p-4 md:p-6`.
*
* Pre-v1.4.33 the shadcn defaults shipped `py-6` + `px-6` (uniform
* 24 px on every breakpoint) while the dashboard tile-strip + chart
* cards used `p-4 md:p-6` (16 px mobile, 24 px md+). Side-by-side on
* `/insights` the correlation cards + daily-briefing read as "denser"
* than the chart cards next to them; the visual reads as two card
* languages on the same page. Matching the dashboard token here gives
* the whole app a single card-padding contract.
*
* Refs `.planning/round-v1433-audit-polish.md` §4.1 + Win 4.
*/
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-4 rounded-xl border py-4 shadow-sm md:gap-6 md:py-6",
className,
)}
{...props}
/>
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min items-start gap-2 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] md:px-6 [.border-b]:pb-4 md:[.border-b]:pb-6",
className,
)}
{...props}
/>
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-full row-start-1 self-start justify-self-end",
className,
)}
{...props}
/>
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-4 md:px-6", className)}
{...props}
/>
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn(
"flex items-center px-4 md:px-6 [.border-t]:pt-4 md:[.border-t]:pt-6",
className,
)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};