import type { ComponentProps } from "react"; import { cn } from "./cn"; /* * Promoted from NOKL's local Card, 2026-08-11, after a property-by-property * comparison against Enterprise's own local Card found only four durable * differences — radius, padding density, border mechanism, and footer * tint — all expressible as component tokens, the same shape as * --button-radius. See evolution-policy.md for the comparison this * resolved and docs/design-system-adoption-audit.md item 9's sibling * reasoning on Field for the same kind of call. * * Two differences were not token-shaped and are fixed in source instead: * * - CardTitle renders a real `

`. NOKL's version rendered a `
` — * invisible to a screen reader's heading navigation and to the document * outline. Enterprise already had this right; NOKL did not. * - The border resolves through `--color-border`, the same token Dialog * and Menu already use, rather than NOKL's one-off * `ring-1 ring-foreground/10`. That tint was never wired to any semantic * token, so it drifted independently of every other bordered surface in * the same theme. * * CardAction has no Enterprise equivalent and is kept anyway: an unused * part costs nothing, and removing it would break NOKL's real call sites * for no reason. * * `gap-4`/padding are conditional on Card actually containing a * CardHeader/CardContent/CardFooter (2026-08-12, found live in Enterprise's * MSP dashboard). The property comparison that resolved Card never surfaced * this: NOKL's original Card *always* had them, because every one of NOKL's * own call sites already composed with the sub-parts. Enterprise's original * Card had neither — most of its 60 real call sites put raw, fully custom * markup straight inside ``, flush against its border by design. Once * unconditional, that raw markup got Card's padding *and* its own, stacked. * Gating both on whether Card has a card-header/card-content/card-footer * descendant reproduces the old zero-padding behavior for that shape * exactly, while leaving every properly-composed Card (all of NOKL's, and * Enterprise's own already-composed ones) unchanged. * * Two things that don't work here, confirmed against the generated * stylesheet rather than assumed from the story rendering alone: * * - One combined `has-[[data-slot=a],[data-slot=b],[data-slot=c]]` * variant. It compiles with no build error, but Tailwind silently emits * no CSS for it at all — three separate `has-data-[slot=X]` variants, * one per slot, is the form that actually generates rules. * - `py-(--card-padding)` on each of those three, overridden by a single * `has-data-[slot=card-footer]:pb-0` for the footer case, and separately * `has-[>img:first-child]:pt-0` for the leading-image case (mirroring how * the original unconditional version worked). Both relied on the override * being more specific than the `py-*`/unconditional rule they replaced — * true before, but once the base rule is *also* behind a `:has()` * variant, both sides have equal specificity, and Tailwind's own emission * order (not source order) decides which one wins — confirmed one way by * the footer case actually breaking, and confirmed the other way by the * leading-image case still working, by luck, off the same tie. * * Split into `pt`/`pb` instead, with the exclusion folded into each * trigger's own condition rather than fought afterward: `pb` fires on * `has-data-[slot=card-header]:not-has-data-[slot=card-footer]` (and the * same for card-content); `pt` fires on * `has-data-[slot=card-header]:not-has-[>img:first-child]` (and the same * for card-content/card-footer). Neither override rule exists anymore — * there's nothing left for them to need to outrank. */ function Card({ className, size = "default", ...props }: ComponentProps<"div"> & { size?: "default" | "sm" }) { return (
img:first-child]:pt-(--card-padding) has-data-[slot=card-content]:not-has-[>img:first-child]:pt-(--card-padding) has-data-[slot=card-footer]:not-has-[>img:first-child]:pt-(--card-padding)", "has-data-[slot=card-header]:not-has-data-[slot=card-footer]:pb-(--card-padding) has-data-[slot=card-content]:not-has-data-[slot=card-footer]:pb-(--card-padding)", "data-[size=sm]:has-data-[slot=card-header]:gap-3 data-[size=sm]:has-data-[slot=card-content]:gap-3 data-[size=sm]:has-data-[slot=card-footer]:gap-3", "data-[size=sm]:has-data-[slot=card-header]:not-has-[>img:first-child]:pt-(--card-padding-sm) data-[size=sm]:has-data-[slot=card-content]:not-has-[>img:first-child]:pt-(--card-padding-sm) data-[size=sm]:has-data-[slot=card-footer]:not-has-[>img:first-child]:pt-(--card-padding-sm)", "data-[size=sm]:has-data-[slot=card-header]:not-has-data-[slot=card-footer]:pb-(--card-padding-sm) data-[size=sm]:has-data-[slot=card-content]:not-has-data-[slot=card-footer]:pb-(--card-padding-sm)", "*:[img:first-child]:rounded-t-(--card-radius) *:[img:last-child]:rounded-b-(--card-radius)", className, )} {...props} /> ); } function CardHeader({ className, ...props }: ComponentProps<"div">) { return (
); } function CardTitle({ className, ...props }: ComponentProps<"h3">) { return (

); } function CardDescription({ className, ...props }: ComponentProps<"div">) { return (
); } function CardAction({ className, ...props }: ComponentProps<"div">) { return (
); } function CardContent({ className, ...props }: ComponentProps<"div">) { return (
); } function CardFooter({ className, ...props }: ComponentProps<"div">) { return (
); } export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };