All components

Citation Cards

AI

Inline source markers with hover cards, the way AI answers footnote their claims. The card is portaled and collision-aware — it flips above the marker near the viewport edge and shifts to stay on-screen with its arrow still pointing home. Hovering a marker or its source row syncs both ways, and it's fully keyboard-navigable.

To reveal a panel smoothly, ease grid-template-rows from 0fr to 1fr1 so it animates to its real height with no measurement. Gate the transition behind a reduced-motion check2 for accessibility, and render any floating card through a portal3 so it never gets clipped by an overflow container.

Sources
  1. [1]
    Animating height with grid-template-rowscss-tricks.com

    Transitioning grid-template-rows from 0fr to 1fr lets a container ease open to its content's natural height — no fixed pixel value, no JS measurement.

  2. [2]
    prefers-reduced-motion — MDNdeveloper.mozilla.org

    A media feature that detects whether the user has asked the system to minimize non-essential motion, so animations can be toned down or removed.

  3. [3]
    Rendering overlays with createPortalreact.dev

    createPortal renders children into a different part of the DOM — ideal for popovers and tooltips that must escape an ancestor's overflow clipping.

Installation

Terminal
npx shadcn@latest add https://ui.saumyarex.xyz/r/citation-cards.json

Props

PropTypeDefaultDescription
Citations.sourcesCitationSource[]The source data: { id, title, url?, snippet? }. Marker numbers follow this order.
Cite.idstringReferences a source by id; renders the numbered inline marker (an anchor to its footnote).
CitationList.labelReact.ReactNode"Sources"Heading above the footnote list. Pass null to hide it.
CitationSource{ id; title; url?; snippet? }Shape of each source. url drives the hostname label and outbound link; snippet shows in the card.

Dependencies

clsxtailwind-merge