Skip to content
Template Empire
Browse components

Expandable Card Grid

A free expandable card grid React component with native disclosures and Motion layout reflow.

  • Skyline 5050 km, 2,150 m of climb, 12-hour cut-off.

    Saturday 6 June, 06:00 from the field barn at Wraycote. Feed stations at 18 km and 34 km, a drop bag at both, and a minibus back from the finish every half hour until dark. Entry is £58 and the field is capped at 400.

    Enter the Skyline 50
  • Ridgeway 3030 km, 1,100 m of climb, 8-hour cut-off.

    Saturday 6 June, 09:00, sharing the back half of the long course. One feed station at 16 km. Waterproof, hat and whistle are checked at registration, and we do turn people away without them. Entry is £34.

    Enter the Ridgeway 30
  • Moorfoot 1515 km, 480 m of climb, and no cut-off at all.

    Sunday 7 June, 10:00, on farm tracks and one long grass climb. Walkers are welcome and the sweeper stays behind the last person. Entry is £18, or £10 if you are under 18.

    Enter the Moorfoot 15
  • Nightfell 1010 km after dark, head torch compulsory.

    Friday 5 June, 21:30, out to the trig point and back on the same line. Head torch and spare batteries are compulsory and both are checked at the start. Two hundred places, and it fills first every year.

    Enter the Nightfell 10

Customise

3
2
14px
4px
20px

Code

const items = [
  {
    id: "skyline",
    title: "Skyline 50",
    summary: "50 km, 2,150 m of climb, 12-hour cut-off.",
    detail: <p>Saturday 6 June, 06:00 from the field barn at Wraycote.</p>,
  },
  // Add two to seven more stable-ID cards.
];

<CardExpandableGrid items={items} />

Expandable Card Grid is a free expandable card grid React component for Next.js. Three to eight stable-ID cards use native details and summary elements, so every disclosure remains keyboard operable and its complete content stays in the document before JavaScript loads. The enhanced grid coordinates one open card in controlled or uncontrolled mode and lets that card span two or three desktop columns.

Motion projects each keyed card from its current position using transform-only layout animation. Detail copy reverses through opacity and a configurable vertical offset before the grid settles, rapid choices retarget the latest valid ID, mobile keeps a one-column reading order, and reduced motion makes every state change instant. Removing the active item resolves the grid closed, reordering preserves a surviving selection, and a controlled close returns focus to the summary before its detail becomes inert.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/card-expandable-grid

Usage

import { CardExpandableGrid } from "@/components/card-expandable-grid";

const items = [
  {
    id: "research",
    title: "Research",
    summary: "The evidence behind the direction.",
    detail: <p>Interview themes, constraints and opportunities.</p>,
  },
  {
    id: "prototype",
    title: "Prototype",
    summary: "The interaction before production.",
    detail: <p>A focused, testable model of the final experience.</p>,
  },
  {
    id: "delivery",
    title: "Delivery",
    summary: "The system prepared for launch.",
    detail: <p>Production code, documentation and rollout notes.</p>,
  },
];

export function Example() {
  return (
    <CardExpandableGrid
      items={items}
      defaultActiveId="research"
      columns={3}
      expandedSpan={2}
    />
  );
}

Props

PropTypeDefaultDescription
items{ id; title; summary; detail; media? }[]requiredThree to eight cards with unique stable IDs, visible title and summary copy, complete detail content and optional media.
activeIdstring | nullundefinedControlled open card ID. Invalid or removed IDs resolve to a closed grid.
defaultActiveIdstring | nullnullInitially open stable ID in uncontrolled usage.
onActiveChange(id: string | null) => voidundefinedCalled after the visitor requests a native disclosure to open or close.
columns2 | 33Desktop column count. The grid remains one column on mobile.
expandedSpan2 | 32Desktop span of the open card, clamped to the available columns.
motionPreset"snappy" | "soft""soft"House spring preset for card reflow and detail movement.
contentOffsetnumber14Detail entrance distance in pixels, clamped from 0 to 28.
hoverLiftnumber4Closed-card hover and focus lift in pixels, clamped from 0 to 8 and disabled for touch or reduced motion.
radiusnumber20Card corner radius in pixels, clamped from 8 to 32.
classNamestringundefinedAdditional classes merged onto the semantic card list.

Customisation

Use the six playground controls for grid geometry and effect behaviour. Five Tier 3 variables retheme the surfaces without editing the component: --card-expandable-grid-surface, --card-expandable-grid-border, --card-expandable-grid-accent, --card-expandable-grid-muted and --card-expandable-grid-media. Consumer media and detail nodes retain their own semantics and styling.

<CardExpandableGrid
  items={items}
  columns={2}
  expandedSpan={2}
  motionPreset="snappy"
  contentOffset={20}
  hoverLift={6}
  radius={28}
/>

<style>{`
  :root {
    --card-expandable-grid-accent: var(--primary);
    --card-expandable-grid-media: var(--surface);
  }
`}</style>

Frequently asked questions

Is Expandable Card Grid free for commercial use?
Yes. Expandable Card Grid is MIT licensed, so you can use it in personal and commercial projects without attribution.
Does the expandable card grid work without JavaScript?
Yes. Every card contains a native details and summary disclosure, complete detail content is present in the server HTML, and the shared native name keeps one disclosure open at a time in supporting browsers. JavaScript only adds controlled state, focus restoration and Motion layout reflow.
How does Expandable Card Grid handle accessibility and reduced motion?
The native summary is the only disclosure control, visible title and summary copy never depend on position, and detail focus returns to that summary before a controlled close makes the content inert. Reduced motion disables layout, opacity, lift and transform animation synchronously, while mobile keeps a single-column source order.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this disclosure and motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates