caliper uidocs

Blog grid

Block

A grid of article cards with date, tag and summary.

Installs components/ui/blog-grid.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/blog-grid.json

From the blog

Notes from the team.

When to use #

Use it for

  • Listing posts or case studies.

Skip it when

  • There is a single featured post.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/blog-grid.json

Adds its 4 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { BlogGrid } from "@/components/ui/blog-grid"

const posts = [
  { id: "p1", title: "How we test with the keyboard only", excerpt: "A weekly habit that finds more bugs than any tool.", href: "#", date: "2025-10-18", readTime: "5 min read", category: "Engineering", author: { name: "Lena Fischer" }, image: { src: "/media/ridge.svg", alt: "Ridge" } },
  { id: "p2", title: "Writing error messages people thank you for", excerpt: "Say what happened, why and what to do next.", href: "#", date: "2025-10-04", readTime: "4 min read", category: "Design", author: { name: "Noor Haddad" }, image: { src: "/media/harbour.svg", alt: "Harbour" } },
  { id: "p3", title: "Behind our new status page", excerpt: "Ninety days, honestly shown.", href: "#", date: "2025-09-22", readTime: "3 min read", category: "Company", author: { name: "Ravi Patel" }, image: { src: "/media/terrace.svg", alt: "Terrace" } },
];

export function BlogGridDemo() {
  return (
    <BlogGrid posts={posts} description="Notes from the team." />
  );
}

API reference #

BlogGrid

component
PropTypeDefaultDescription
posts *readonly BlogPost[]—
titlestring"From the blog"
descriptionstring—
featuredbooleantrueShow the first post larger when there are at least three.
loadingboolean—
classNamestring—

Accessibility #

It exposes group and status semantics, with aria-hidden, aria-label, aria-pressed and aria-busy kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.