Quad Ledger

About this block

Quad LedgerPRO

An image-free newsroom index with a square eyebrow, a two-column heading band over a hairline rule, four linked release cards that each pair an accent counter with a kicker, headline, dek, and byline, and a seal button to the full archive.

Blog49: Quad Ledger

An image-free blog index that opens with a Badge6 eyebrow and a two-column heading band above a hairline rule, then lists linked stories as bordered cards where every card pairs a zero-padded accent counter with a bold headline, a category-and-date kicker, a summary, and a byline. No image is rendered anywhere in the block, and an optional Button1 seal button closes the section below the grid.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/blog49?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/blog49?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/blog49.tsx and its dependencies, including the Badge6 and Button1 components it renders.

Quick start

The installed file exports blog49Demo alongside the block: the exact props behind the preview above. Spread it to get a working index in one line.

tsx
import { Blog49, blog49Demo } from "@/components/beste/block/blog49";

export default function Page() {
  return <Blog49 {...blog49Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Blog49 } from "@/components/beste/block/blog49";

export default function Page() {
  return (
    <Blog49
      label="Press office"
      heading="Company announcements"
      description="Statements and filings issued by the communications office, listed in order of release."
      items={[
        {
          category: "Operations",
          title: "Second storage site opens in Aragon",
          summary:
            "The facility connects to the regional transmission network in April.",
          date: "February 4, 2026",
          author: { name: "Maria Santos", title: "Tech Lead" },
          href: "/press/aragon-storage",
        },
        {
          category: "Governance",
          title: "Board appoints a new audit committee chair",
          summary:
            "The appointment takes effect at the close of the annual general meeting.",
          date: "January 27, 2026",
          author: { name: "David Chen" },
          href: "/press/audit-chair",
        },
      ]}
      button={{ label: "Full press archive", href: "/press" }}
    />
  );
}

Props

PropTypeDefaultDescription
labelstring–Badge6 eyebrow above the heading; omitted entirely when absent
headingstring–Heading in the left column of the heading band
descriptionstring–Muted supporting paragraph in the right column, capped at max-w-md
itemsArticle[][]Story cards rendered into the grid
buttonActionButton–Button1 seal button centered below the grid; omitted entirely when absent
classNamestring–Extra classes merged onto the outer <section>
ts
type Article = {
  category: string;
  title: string;
  summary: string;
  date: string;
  author: Author;
  href: string;
};

type Author = {
  name: string;
  title?: string;
  avatar?: { src: string; alt: string };
  href?: string;
};

type ActionButton = {
  label: string;
  href: string;
};

Behavior notes

  • Two independent breakpoints drive the layout. The heading band stacks until md, where it splits into two columns and the description bottom-aligns to the right edge. The story grid stacks until sm, where it becomes two columns; the heading band therefore stays single-column while the grid is already paired.
  • Cards are separated by a real gap-6 and each carries its own border, so an odd item count simply leaves the trailing cell empty instead of stranding a rule. The rule under the heading band always renders, with or without label, heading, and description.
  • The block is not fixed at four items despite the 2x2 preview. The counter is String(index + 1).padStart(2, "0"), so it reads 01 through 09 and then widens to 10 and beyond without truncating.
  • author.avatar and author.href exist on the interface but are never rendered: the byline is plain text built as By {name} with , {title} appended only when title is set, and the avatar is dropped because the whole card is already a single Next.js Link to item.href. The kicker joins category and date with a middot and prints both verbatim with no date formatting.
  • Hover is styling only: the headline shifts to text-primary on card hover. Cards are keyed by array index rather than href, so reordering items remounts rather than reorders them.

More Blog blocks

View all Blog
PRO

blog48

Quad Grid

PRO

blog44

Newsroom Lead and Index

PRO

blog46

Front Page Columns

Get Pro access