NIMA MZ

Meridian Rail

Component 2

About

Time-zone meeting picker where each row is the same day shifted into someone else's zone, so daylight staircases down the list; drag the meridian and every local clock moves with it while the hours everyone works stay lit. Zero deps, DST-correct via Intl, keyboard accessible.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Meridian Rail to your project and fits it to your theme.

Code

How Meridian Rail is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client"

import * as React from "react"
// Relative import on purpose: the 21st CLI rewrites it to the published path (`@/components/ui/<slug>`)
// and to a temporary path during `21st render`. An absolute "@/…" import breaks the render build.
import { MeridianRail, type ZoneSpec } from "@/components/ui/meridian-rail"

// A real distributed release crew. Priya works India-evening on purpose — that shifted row is the only
// reason an hour exists that suits all five, which is exactly the argument the rail is here to make.
const TEAM: ZoneSpec[] = [
  {
    timeZone: "Asia/Kolkata",
    label: "Bengaluru",
    person: "Priya Raman",
    workStart: 12,
    workEnd: 20,
  },
  { timeZone: "Europe/Berlin", label: "Berlin", person: "Jonas Weber" },
  { timeZone: "Europe/London", label: "London", person: "Ada Nwosu" },
  { timeZone: "America/Sao_Paulo", label: "São Paulo", person: "Marta Silva" },
  {
    timeZone: "America/New_York",
    label: "New York",
    person: "Chris Boone",
    workStart: 8,
    workEnd: 16,
  },
]

// Module-level `settings` + a default export that accepts them as props gives this demo a live controls
// panel on 21st.dev. Signature knobs first, then look, then copy.
const settings = {
  // The axis of the rail. Type any IANA zone and the whole staircase re-shuffles around it.
  yourZone: "Europe/Berlin",
  // Noon: a place a scheduler would honestly open on, three of five awake, and 210 minutes short of
  // the shared window so the recorded click on the best-window button has somewhere to travel.
  meridianAt: 720,
  spanHours: 24,
  workStart: 9,
  workEnd: 17,
  hour12: false,
  showOverlap: true,
  // Off for the cover: a third vertical at an unpredictable position is one line too many in a still.
  showNow: false,
  rowHeight: 48,
  eyebrow: "Scheduling",
  headline: "Find the hour everyone is awake.",
  subline: "Five people, five zones, one day. Drag the meridian and every clock moves with it.",
}

export default function Demo(props: Partial<typeof settings>) {
  const s = { ...settings, ...props }
  // Controlled on purpose: it is the API most consumers need, and it lets the knob move the meridian.
  const [minutes, setMinutes] = React.useState(s.meridianAt)
  React.useEffect(() => setMinutes(s.meridianAt), [s.meridianAt])

  return (
    <div className="bg-background flex min-h-[max(560px,100svh)] w-full flex-col items-center justify-center px-5 py-16 sm:px-8">
      <div className="flex w-full max-w-4xl flex-col items-center">
        <p className="text-muted-foreground border-border mb-5 inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-medium">
          <span aria-hidden="true" className="bg-primary size-1.5 rounded-full" />
          {s.eyebrow}
        </p>
        <h1 className="text-foreground text-center text-4xl font-semibold tracking-tight text-balance sm:text-5xl">
          {s.headline}
        </h1>
        <p className="text-muted-foreground mt-4 max-w-xl text-center text-sm text-pretty sm:text-base">
          {s.subline}
        </p>

        <div className="mt-10 w-full">
          <MeridianRail
            zones={TEAM}
            baseZone={s.yourZone}
            value={minutes}
            onValueChange={setMinutes}
            spanHours={s.spanHours}
            workStart={s.workStart}
            workEnd={s.workEnd}
            hour12={s.hour12}
            showOverlap={s.showOverlap}
            showNow={s.showNow}
            rowHeight={s.rowHeight}
          />
        </div>
      </div>
    </div>
  )
}

More by NIMA MZ

View all
  • Agent Trace

    Agent Trace

    692
  • Sonar Grid

    Sonar Grid

    584
  • Receipt Pricing

    Receipt Pricing

    148
  • Import Patchbay

    Import Patchbay

    124
  • Lookout

    Lookout

    84
  • Moiré Field

    Moiré Field

    31
  • Split Flap Button

    Split Flap Button

    1
  • Ink Marbling

    Ink Marbling