registry
@automindzsheets 01–77shadcn 4.x
SHEET 33

Bento Grid

block / cards

An asymmetric feature grid where every card declares its own footprint in tracks, clamped so no span can widen the grid or scroll the page.

type
block
files
1
registry deps
card, skeleton, @automindz/backdrop, @automindz/render-link
npm deps
lucide-react

installs

  • components/automindz/bento-grid.tsx
  • A span wider than the grid is clamped, never handed to CSS.
  • Copy sits at the cell's floor, so a short card pads not stretches.
  • The cta reveals on hover and on focus, and is always on under touch.
  • A skeleton holds the card's declared footprint, so nothing reflows.

BentoGrid owns the tracks, BentoCard owns its footprint. A span is in **tracks**, not t-shirt sizes, and it is clamped to the grid's own column count before it reaches CSS — grid-column: span 4 in a three-track grid does not clamp itself, it widens the implicit grid and scrolls the page sideways. Cards must be **direct children** of the grid to receive that count; wrap the grid, not the card. rowHeight is a floor (minmax(rowHeight, auto)), never a fixed height. Copy sits at the bottom of the cell and is clamped at two title lines and three description lines — write to the clamp. background takes a node: pass <Backdrop pattern="mesh" /> from @automindz/backdrop, which installs with this item so no pattern is re-implemented here. href makes the whole cell one anchor and turns cta into decoration; action puts your own control in the cell, and the types reject both. Pass renderLink for next/link. No stylesheet, no client boundary.

Variations

one state per section

01

Three columns, mixed footprints

The default: 3 tracks from lg, 2 from sm, 1 below it. The lead card is 2×2 with a mesh backdrop behind its copy, the webhooks card is 2×1, and the rest are single cells. Hover any linked card and its call to action rises into view; the copy stays pinned to the bottom of the cell, so the short cards pad above the text instead of stretching it.

fluid
Identity

Single sign-on, and the awkward half of it

Google, Microsoft Entra, Okta and SAML behind one button, with the account-linking prompt already written for the case where the email already exists.

Workspaces and roles

Owner, admin and member, invites that expire, and a switcher that survives a user belonging to nine of them.

Seat and usage billing

Stripe checkout, proration on a seat change, and a portal link that fails closed until the session resolves the customer.

Delivery

Webhooks with replay

Signed deliveries, exponential backoff, and a replay button per event so a customer outage is not your data loss.

Audit log

Every privileged action recorded with the actor, the before value and the request id, exportable as CSV.

02

The hover reveal, from a keyboard

Tab into this grid. The call to action on the middle card is a real link and it appears the moment focus enters the card, not only under a cursor — the reveal is written inside a hover:hover + motion-safe block, so a touch screen and a reduced-motion visitor never see it hidden at all. The first card is a link, so its own label is aria-hidden decoration and there is exactly one tab stop for that cell.

fluid

Whole card is the link

The anchor wraps the title and a stretched ::after covers the cell. The label below is decoration.

Contained link

No card-level href, so the call to action is a genuine anchor with its own focus ring.

Read the reference

Contained control

An action instead of a link. The union in the props makes a button inside the card's anchor impossible to write.

03

A span the grid cannot honour

Both cards ask for four columns inside a two-column grid. The span is clamped to the tracks that exist rather than handed to CSS, which would widen the implicit grid and put a horizontal scrollbar on the page. Narrow the window past 640px and every card becomes one column wide, whatever it asked for.

fluid

cols: 4 in a two-column grid

Clamped to two. No implicit track, no page-wide scrollbar.

cols: 4, rows: 2

The row span survives the column clamp — they are clamped separately, against different ceilings.

One track

The default footprint, and the one that never needs a class.

04

Four columns, dense

grid-auto-flow: row dense, so the single-cell cards backfill the hole the 3×1 card leaves rather than pushing it down the grid. Worth knowing what it costs: dense packing moves cards out of source order visually, so the reading order and the tab order no longer match the layout.

fluid

Dashboards

Charts with honest axes and an empty state that is not the same thing as a failed load.

Audit log

Workspaces

Owner, admin, member.

Billing

Seats, usage and proration.

Webhooks

05

Loading, at the declared footprint

Three of the five cards are still loading and each skeleton occupies exactly the cell its content will: same span, same row floor. Nothing in the grid moves when the data lands, which is the only reason a skeleton beats a spinner here.

fluid
Loading

Workspaces and roles

Already landed, so you can see the two states side by side at the same size.

Loading
Loading

Audit log

Every privileged action recorded with the actor and the request id.

06

Disabled, with the reason

A dashed outline rather than a shade, so the state survives a greyscale screenshot, and the reason is in the card instead of in a tooltip nobody on a phone can open. The anchor is not rendered at all — the cell is unreachable by keyboard rather than reachable and then talked out of activating — and the call to action goes with the interactivity.

fluid

Usage billing

Meter events, tiered rates and a soft cap per workspace.

Available on Scale. Ask an owner to upgrade.

SAML

Enterprise identity providers behind the same button as the rest.

Your plan includes OIDC only.

Audit log

On, and reachable — the contrast is the point of putting it here.

07

No cards at all

Two grids are rendered here and only one of them is on the page: the second is mapped over an empty list and returns null, gaps included. A bento with the tracks but none of the cards reads as a failed load, so there is nothing below the first row.

fluid

This grid has cards

So it renders