Campfire
Registry

Agenda

What the talk covers, as numbered rows.

slides/02-agenda.mdx
---
layout: agenda
title: Agenda
---

<AgendaItem number="01" heading="The problem" />
<AgendaItem number="02" heading="What we built" />
<AgendaItem number="03" heading="Where it goes next" />

Installation

Installs the layout plus the AgendaItem row component.

camp add agenda

Or with any shadcn-compatible CLI:

bunx shadcn@latest add https://campfire-deck.vercel.app/r/agenda.json

Copy both files. They import cn from @/lib/utils — scaffolded by camp init, or camp add utils:

layouts/agenda.tsx
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";

export type AgendaLayoutProps = ComponentProps<"main"> & {
  /** From the slide's `title` frontmatter. */
  title?: string;
};

/** What the talk covers. Pair with the AgendaItem component:
 *
 * ```mdx
 * <AgendaItem number="01" heading="The problem" />
 * <AgendaItem number="02" heading="What we built" />
 * ```
 */
export default function AgendaLayout({
  title,
  className,
  children,
  ...props
}: AgendaLayoutProps) {
  return (
    <main
      className={cn(
        "flex h-full flex-col justify-center gap-14 p-24",
        className
      )}
      data-slot="agenda-layout"
      {...props}
    >
      {title ? (
        <h1 className="font-bold font-heading text-6xl tracking-tight">
          {title}
        </h1>
      ) : null}
      <div
        className="flex max-w-4xl flex-col gap-8"
        data-slot="agenda-layout-items"
      >
        {children}
      </div>
    </main>
  );
}
components/agenda-item.tsx
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";

export type AgendaItemProps = ComponentProps<"div"> & {
  /** The item marker, e.g. 1 or "01". */
  number: number | string;
  /** What this part of the talk covers. */
  heading: string;
};

/** A numbered row for agenda slides. Pairs with the agenda layout. */
export default function AgendaItem({
  number,
  heading,
  className,
  children,
  ...props
}: AgendaItemProps) {
  return (
    <div
      className={cn("flex items-baseline gap-8", className)}
      data-slot="agenda-item"
      {...props}
    >
      <span
        className="font-mono font-semibold text-2xl text-primary tabular-nums"
        data-slot="agenda-item-number"
      >
        {number}
      </span>
      <div className="flex flex-col gap-1">
        <span
          className="font-heading font-semibold text-4xl tracking-tight"
          data-slot="agenda-item-heading"
        >
          {heading}
        </span>
        {children ? (
          <div
            className="text-xl leading-relaxed opacity-70"
            data-slot="agenda-item-detail"
          >
            {children}
          </div>
        ) : null}
      </div>
    </div>
  );
}

Usage

AgendaItem is an ordinary component — reuse it later in the deck as a "where we are" reminder, restyled through className or data-slot hooks:

slides/02-agenda.mdx
---
layout: agenda
title: Agenda
---

<AgendaItem number="01" heading="The problem">
  Five minutes of pain.
</AgendaItem>
<AgendaItem number="02" heading="What we built" />

API Reference

The layout follows the layout contract and exposes data-slot="agenda-layout" and agenda-layout-items.

AgendaItem extends ComponentProps<"div"> — native props are forwarded and className is merged last. Parts expose data-slot="agenda-item", agenda-item-number, agenda-item-heading, and agenda-item-detail.

Prop

Type

On this page