C&C Docs
Log in

cnc-cards

A card grid that keeps the insides of its cards aligned. Columns fit themselves to the available width, and each card's own rows are a subgrid of the container's — so every image is the same height, every title sits on the same line, and every footer lines up along the bottom, however uneven the text is. It is pure CSS: no controller, no JavaScript, nothing to hydrate.

Quick start

Set --card-rows to the number of sections each card has, then give every card exactly that many direct children. Here it is three: a banner, a body, and a footer.

<cnc-cards style="--card-width: 220px; --card-rows: 3; gap: 1rem">
  <div class="rounded-lg overflow-hidden">
    <div class="banner"></div>   <!-- row 1 -->
    <div class="p-4">…</div>      <!-- row 2 -->
    <div class="px-4 pb-4">…</div> <!-- row 3 -->
  </div>
  …
</cnc-cards>

Properties

There are no attributes. The widget is configured entirely through CSS custom properties and the ordinary gap, which is why every example on this page sets them in a style.

Custom property Default Purpose
--card-width 260px The narrowest a column may get before the grid drops to fewer columns. Not a fixed width — columns stretch to share the space.
--card-rows 4 How many rows each card claims, and therefore how many of its children get aligned with its neighbours'.
gap 0 The ordinary grid gap. Zero by default, so cards touch until you say otherwise.

A cards array is declared on the controller and read nowhere. The widget's whole template is <slot></slot> — cards are markup you write, not data you pass.

Things to know

  • Get --card-rows right or the card breaks. It is not a hint. A card with three children under --card-rows: 1 renders all three in the same track, on top of one another.
  • The card is the direct child; its sections are that child's children. Wrapping a card in an extra <div> makes the wrapper the card and collapses the alignment — the subgrid only reaches one level down.
  • A card cannot set its own padding on the outer element. The card becomes a grid with row-gap: 0, so padding on it pushes every row in. Pad the sections instead, which is why the examples here use px-4 pt-4 on the parts rather than p-4 on the card.
  • Row gaps come from the sections too. gap on the widget spaces cards apart, not the rows inside them; the inner row-gap is pinned to zero so the subgrid tracks meet.
  • Subgrid is required. Every current browser supports it, but a very old one falls back to a plain grid: the cards still lay out, they simply stop aligning internally. Nothing breaks.
  • The cards property does nothing. It is declared on the controller and never read. Cards are markup.

Usage guidance

Reach for cnc-cards when the cards have a repeating internal structure that ought to line up — a product grid, a pricing table, a set of dashboard tiles. When the items are a single block each, an ordinary Tailwind grid says the same thing with less to explain. For a list rather than a grid, use cnc-list; for tiles the user can move and resize, cnc-grid-stack; and for equal-sized viewports that must fill a fixed box, cnc-autolayout.

← Back to Widgets