Focus Cards
interlace-ui/focus-cardsFreeComponent
A grid that spotlights one card at a time: pointing at or keyboard-focusing a card keeps it sharp while every sibling dims and blurs. Real list semantics — `ul` › `li` › `article` — and a controlled or uncontrolled active index.
Install it
npx shadcn@latest add https://ds.interlace.tools/r/focus-cards.jsonSource
1"use client";23import {4 type ComponentPropsWithoutRef,5 type ReactNode,6 useCallback,7 useState,8} from "react";910// @interlace/focus-cards v1.2.0 — Interlace design system.11// Docs, props and live preview: https://ds.interlace.tools/c/focus-cards12// What changed since: https://ds.interlace.tools/c/focus-cards#history13// Generated banner — keep it, the upgrade diff reads this version.1415/**16 * @interlace/ui — FocusCards + FocusCard17 *18 * A grid that spotlights one card at a time: pointing at or keyboard-focusing19 * a card keeps it sharp while every sibling dims and blurs. Real list20 * semantics — `ul` › `li` › `article` — and a controlled or uncontrolled21 * active index.22 *23 * The media is whatever node you pass: image, `next/image`, video, canvas.24 * Nothing here assumes an `<img>`.25 *26 * ## Provenance27 *28 * Our reimplementation of the Aceternity UI "Focus Cards" pattern. What is29 * ours: focus, not just hover, drives the active state (so the grid works from30 * the keyboard); the active index is controllable and reports why it changed;31 * the card is a `renderItem` + `caption` composition rather than a fixed32 * `{src, title}` shape; and all colour comes from tokens.33 *34 * ## Anatomy35 *36 * FocusCards (ul — data-slot="focus-cards")37 * └─ li (data-slot="focus-cards-item",38 * group/focus-item, data-active/data-dimmed,39 * owns the pointer + focus handlers)40 * └─ FocusCard (article — data-slot="focus-card")41 * ├─ div (data-slot="focus-card-media")42 * └─ div (data-slot="focus-card-caption")43 *44 * The active/dim state travels down as `group-data-[active]/focus-item` and45 * `group-data-[dimmed]/focus-item` CSS, so `renderItem` never forwards a prop46 * for it. `FocusCard`'s own `active` / `dimmed` props exist only for composing47 * a card outside the grid.48 *49 * ## Motion50 *51 * Tailwind transitions, JS-gated. `FocusCard` calls `useReducedMotion()` and52 * omits three class groups when it returns true: the53 * `transition-[filter,transform,opacity] duration-300` on the card, the54 * `scale-[0.98] blur-sm` / `scale-100 blur-none` pair, and the caption's55 * `transition-opacity`. What survives under `prefers-reduced-motion: reduce`56 * is the part that carries the meaning — `opacity-60` on the receding cards57// … truncated
More from interlace-ui
All 140 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| A11y Startera11y-starter | interlace-ui | Components | Free | no deps | |
| Accordionaccordion | interlace-ui | Components | Free | no deps | |
| Alertalert | interlace-ui | Components | Free | no deps | |
| Alert Dialogalert-dialog | interlace-ui | Components | Free | no deps | |
| Animated Gradient Textanimated-gradient-text | interlace-ui | Components | Free | no deps | |
| Animated Grid Patternanimated-grid-pattern | interlace-ui | Components | Free | no deps | |
| Animated Listanimated-list | interlace-ui | Components | Free | no deps | |
| Article Cardarticle-card | interlace-ui | Components | Free | no deps |
