Acrylic
Components

Card

Dark frosted-glass surface card — flat at rest, soft float + lift on hover.

A charcoal translucent pane over a blurred backdrop. Flat at rest (no shadow, no border); interactive adds a hover lift + a soft float shadow. It's a surface primitive — compose list rows, tiles, and panels on top of it.

Installation

npx shadcn add https://acrylic-ui.vercel.app/r/card.json

Usage

San Francisco
18°
Mostly Clear · H:20° L:13°
Midnight City
M83 · Hurry Up, We're Dreaming

Example — image-thumbnail row

ImageThumbCard: a list-row composition — a 2×2 image-thumbnail tile with a count badge, a title / subtitle / meta stack, and a trailing action that fades in on hover. interactive gives each row the Card's hover lift. Copy it and swap in your own thumbnails and fields.

248
Iceland
Ring Road · Reykjavík
August 2024
56
Golden Hour
Studio portraits
September 1

Example — card in card

A nested composition (the iOS grouped-list pattern): an outer frosted Card opts into nestedSurface and holds a recessed inner card. The inner surface is not a second blurred Card — stacking another backdrop-blur over the already-translucent glass double-samples it and leaves a darkened seam. Instead it's a flat wash that recesses the surface, reading as a secondary surface on the same pane.

Today3
Morning run7:00 AM

5 miles · Marina Green

Design review11:30 AM
Call Mom6:00 PM
Trips1
Lisbon · May
Confirm Airbnb check-inApr 2

Example — cover image card

The shadcn/ui Card · Image example, restyled on the acrylic Card: a flush cover image under a dark scrim, a Featured badge, title + description, and a full-width footer action — with the body as a frosted glass surface over the wallpaper.

Concert cover
Live
Sunset Sessions
Ólafur Arnalds, live from Reykjavík — tonight at 9:00.

A media tile with two live segmented controls. Size swaps the cover's aspect-ratio (16:9 / 3:4 / 1:1) — one source stays uniform while different sources each keep their own shape, un-cropped — and style toggles default (caption under the media) against overlay (caption on a bottom scrim). Title and subtitle reuse the Card's own CardTitle / CardDescription — the same type as the Cover-image-card above — so the family reads as one; the corner chip is the acrylic Badge tinted for on-media contrast.

12:06
48 Hours in Lisbon
Studio K · 120K views · 3d ago
Album
Kyoto in Autumn
@aoi · 22 photos
Midnight Tapes
Khruangbin · 12 tracks
18:32
Cinematic Camera Moves
Film Lab · 84K views
Dusk on the Kamakura Coast
@aoi · 18 photos
City Lo-Fi, Vol. 3
Various Artists · 20 tracks

API Reference

The frosted Card follows the shadcn/ui Card anatomy — a surface root plus Header / Title / Description / Action / Content / Footer sub-parts. Only the root diverges (it adds interactive + nestedSurface); every sub-part is a layout/typography wrapper that renders a <div> with a data-slot styling hook and forwards all native <div> props (className, onClick, style, …). className is for layout only — color always flows through the --acr-* tokens.

Card

The dark-glass surface root — flat at rest (no border, no rest shadow), a soft float lift on hover when interactive. Renders <div data-slot="card">.

PropTypeDefaultDescription
interactivebooleanfalseHover lift + soft float shadow, for clickable cards.
nestedSurfacebooleanfalseOpt child Cards into the nested-surface treatment — they drop their own blur and tint one step recessed (the card-in-card pattern). Sets data-nested-surface.
classNamestringMerged onto the surface (layout only).
...propsReact.HTMLAttributes<HTMLDivElement>Forwarded to the root <div>.
<Card interactive>
  <CardHeader>
    <CardTitle>Title</CardTitle>
    <CardDescription>Description</CardDescription>
    <CardAction>
      <Button icon variant="ghost" aria-label="More" />
    </CardAction>
  </CardHeader>
  <CardContent>…</CardContent>
  <CardFooter>…</CardFooter>
</Card>

CardHeader

Grid header for the title / description stack; when a CardAction is present it auto-adds a trailing column (has-data-[slot=card-action]:grid-cols-[1fr_auto]) to hold it. Renders <div data-slot="card-header">.

PropTypeDefaultDescription
classNamestringMerged onto the header grid.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardTitle

The 15px semibold title (self-center within the header grid). Renders <div data-slot="card-title">.

PropTypeDefaultDescription
classNamestringMerged onto the title.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardDescription

The 13px text-muted-foreground description. Renders <div data-slot="card-description">.

PropTypeDefaultDescription
classNamestringMerged onto the description.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardAction

Top-right action slot inside CardHeader (a menu or icon button), pinned to the header's trailing column (col-start-2 row-start-1 justify-self-end). Renders <div data-slot="card-action">.

PropTypeDefaultDescription
classNamestringMerged onto the action wrapper.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardContent

The card body wrapper — horizontal padding only; the root owns the vertical rhythm, so compose it with flex flex-col gap-*. Renders <div data-slot="card-content">.

PropTypeDefaultDescription
classNamestringMerged onto the content.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardFooter

Footer row for actions, aligned on a single line (flex items-center). Renders <div data-slot="card-footer">.

PropTypeDefaultDescription
classNamestringMerged onto the footer.
...propsReact.ComponentProps<"div">Forwarded to the <div>.

CardMedia

The fixed-ratio cover frame for the gallery / media-card anatomy. Give it src and it owns the cover image — object-cover filling and cropping the ratio box — with the same load-error strategy as MediaBox: a failed load retries with exponential backoff, then after maxRetries shows fallback (default ImageOff); a missing src with a fallback shows the placeholder immediately (the avatar-with-no-photo case). Without src it's just a frame — a direct <img> child is still auto-covered ([&>img]:size-full [&>img]:object-cover). Either way a CardMediaOverlay, corner Badge, or any positioned child rides inside it. Pass ratio to lock the aspect so a grid of tiles stays uniform. Renders <div data-slot="card-media">.

PropTypeDefaultDescription
ratiostringCSS aspect-ratio for the frame, e.g. "16 / 9", "2 / 3", "1 / 1". Merged into style (an explicit style.aspectRatio still wins).
srcstringCover image URL. When set, CardMedia renders and manages the object-cover <img> (retry-on-error + fallback). Omit to hand it your own media as children.
altstring""Alt text for the owned image.
fallbackReact.ReactNode<ImageOff />Placeholder shown after retries are exhausted, or immediately when src is absent.
imageClassNamestringMerged onto the owned <img>.
maxRetriesnumber2Failed loads to retry with backoff before giving up.
retryDelayMsnumber800Base backoff delay in ms; doubles each attempt.
onNaturalSize(width, height) => voidFired with the owned image's intrinsic pixel size once it loads — lets a parent (e.g. MediaBox) size a frame to the media's natural aspect.
classNamestringMerged onto the frame (layout only).
...propsReact.ComponentProps<"div">Forwarded to the <div>.
<Card interactive className="overflow-hidden p-0">
  <CardMedia ratio="2 / 3" src={src} alt={name} fallback={<UserRound className="size-8" />}>
    <CardMediaOverlay>
      <CardTitle>{name}</CardTitle>
      <CardDescription>{role}</CardDescription>
    </CardMediaOverlay>
  </CardMedia>
</Card>

CardMediaOverlay

The caption-on-media variant: a bottom scrim (bg-gradient-to-t from-black/85) placed inside CardMedia that darkens the media so text stays legible. Nest the same CardTitle / CardDescription inside — the overlay retypes them for on-media contrast: white / white-70, stepped down to the caption scale (title 15→13, description 13→12) and truncated. The "caption under the media" variant needs no component — it's CardTitle / CardDescription in a padded div below CardMedia. Renders <div data-slot="card-media-overlay">.

PropTypeDefaultDescription
classNamestringMerged onto the scrim (layout only).
...propsReact.ComponentProps<"div">Forwarded to the <div>.

On this page