# Quarterly Roadmap Columns

> Product roadmap laid out as four quarterly columns of feature cards, each with a category and a color coded shipped, in progress, or planned status.

A shadcn/ui block from the [@7ovr](https://7ovr.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/7ovr/roadmap-4).

- Page, with a live preview: https://shadcn.exchange/r/7ovr/roadmap-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/7ovr/roadmap-4-light-b30e73b138d4fd1f.png
- Registry item: https://7ovr.com/r/roadmap-4.json
- Categories: roadmap
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://7ovr.com/r/roadmap-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@base-ui/react`
- Registry items: `badge`

## Files

### `registry/blocks/roadmap/4/roadmap-block.tsx`

```tsx
import { Badge } from "@/components/ui/badge"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"
import type * as React from "react"

type IconProps = { className?: string; size?: number | string }

type IconRenderer = (props: IconProps) => React.ReactNode

type Status = "Shipped" | "In Progress" | "Planned"

type Item = {
  title: string
  description: string
  category: string
  status: Status
}

type Quarter = {
  label: string
  period: string
  items: Item[]
}

const statusMeta: Record<Status, { icon: IconRenderer; className: string }> = {
  Shipped: {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Check"
        tabler="IconCheck"
        hugeicons="Tick02Icon"
        phosphor="Check"
        remixicon="RiCheckLine"
        {...p}
      />
    ),
    className: "text-success",
  },
  "In Progress": {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="LoaderCircle"
        tabler="IconLoader2"
        hugeicons="Loading03Icon"
        phosphor="Spinner"
        remixicon="RiLoader4Line"
        {...p}
      />
    ),
    className: "text-warning",
  },
  Planned: {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Clock"
        tabler="IconClock"
        hugeicons="TimeIcon"
        phosphor="Clock"
        remixicon="RiTimeLine"
        {...p}
      />
    ),
    className: "text-muted-foreground",
  },
}

const quarters: Quarter[] = [
  {
    label: "Q1",
    period: "Jan – Mar",
    items: [
      {
        title: "Saved Views",
        description: "Pin filtered lists to the sidebar and share them.",
        category: "Core",
        status: "Shipped",
      },
      {
        title: "Dark Theme",
        description: "A full dark palette that follows the system setting.",
        category: "Design",
        status: "Shipped",
      },
    ],
  },
  {
    label: "Q2",
    period: "Apr – Jun",
    items: [
      {
        title: "Audit Log",
        description: "An exportable record of every workspace action.",
        category: "Security",
        status: "Shipped",
      },
      {
        title: "Team Roles",
        description: "Granular owner, editor, and viewer permissions.",
        category: "Core",
        status: "Shipped",
      },
    ],
  },
  {
    label: "Q3",
    period: "Jul – Sep",
    items: [
      {
        title: "Public API v2",
        description: "A faster REST API with typed SDKs and webhooks.",
        category: "Platform",
        status: "In Progress",
      },
      {
        title: "Mobile App",
        description: "Native iOS and Android apps for on-the-go access.",
        category: "Apps",
        status: "In Progress",
      },
      {
        title: "Slack Integration",
        description: "Post updates and create items straight from Slack.",
        category: "Integrations",
        status: "Planned",
      },
    ],
  },
  {
    label: "Q4",
    period: "Oct – Dec",
    items: [
      {
        title: "Custom Dashboards",
        description: "Build your own views from any metric in the workspace.",
        category: "Analytics",
        status: "Planned",
      },
      {
        title: "SSO and SCIM",
        description: "Enterprise sign-on with automated user provisioning.",
        category: "Security",
        status: "Planned",
      },
    ],
  },
]

function RoadmapItem({ item }: { item: Item }) {
  const meta = statusMeta[item.status]
  return (
    <div className="flex flex-col gap-2 rounded-lg border border-border bg-background p-3.5">
      <div className="flex items-center justify-between gap-2">
        <Badge>{item.category}</Badge>
        <meta.icon
          className={"size-4 shrink-0 " + meta.className}
          aria-hidden="true"
        />
      </div>
      <p className="text-sm font-medium text-pretty">{item.title}</p>
      <p className="text-xs text-pretty text-muted-foreground">
        {item.description}
      </p>
      <span className={"mt-0.5 text-xs font-medium " + meta.className}>
        {item.status}
      </span>
    </div>
  )
}

function QuarterColumn({ quarter }: { quarter: Quarter }) {
  return (
    <div className="flex min-w-64 flex-1 flex-col gap-3 sm:min-w-0">
      <div className="border-b border-border pb-3">
        <h3 className="font-heading text-sm font-semibold">
          {quarter.label} 2026
        </h3>
        <p className="text-xs text-muted-foreground">{quarter.period}</p>
      </div>
      <div className="flex flex-col gap-3">
        {quarter.items.map((item) => (
          <RoadmapItem key={item.title} item={item} />
        ))}
      </div>
    </div>
  )
}

export default function RoadmapBlock() {
  return (
    <section className="flex min-h-svh w-full justify-center bg-background px-6 py-16 text-foreground">
      <div className="w-full max-w-5xl">
        <div className="mb-8 flex flex-col items-start">
          <Badge variant="secondary">Roadmap</Badge>
          <h2 className="mt-4 font-heading text-2xl font-bold tracking-tight text-balance sm:text-3xl">
            What We Are Building In 2026
          </h2>
          <p className="mt-2 text-pretty text-muted-foreground">
            A quarter-by-quarter look at what has shipped and what is coming
            next.
          </p>
        </div>
        <div className="flex gap-4 overflow-x-auto pb-2 sm:grid sm:grid-cols-2 sm:overflow-visible lg:grid-cols-4">
          {quarters.map((quarter) => (
            <QuarterColumn key={quarter.label} quarter={quarter} />
          ))}
        </div>
      </div>
    </section>
  )
}
```
