KQ-1739 · ZONE/OVERLAP

Zone Overlap

A find-a-time surface: a team across time zones as rows on one shared timeline, hours outside each person's day hatched, busy blocks drawn, and an Everyone lane holding the stretches where all are free. The meeting band spans every row with each person's local time on a flag; dragged it follows the pointer 1:1 and lands on a quarter hour on springs.snap with the throw's velocity, and as it enters the everyone-free stretch it blooms — one value springs up on springs.snap (down on springs.glide) and brightens the band and widens its glow from a single CSS property. The band is a slider: arrows move it fifteen minutes, brackets change its length, Enter picks and S suggests.

KQ-1739 · ZONE/OVERLAP

Design sync

Tue 20 Oct · 14:00–14:45 Lisbon

45 min
4 of 5 freeRui outside hours

Left and Right move the meeting fifteen minutes, Shift with them an hour. The bracket keys change its length. Enter picks the time, S suggests the next time everyone is free.

  • Ana Ferreira

    Lisbon · you

    14:00–14:45Free
  • Tomé Rocha

    Lisbon

    14:00–14:45Free
  • Jonas Lindqvist

    Helsinki

    16:00–16:45Free
  • Maya Okafor

    New York

    09:00–09:45Free
  • Rui Adeyemi

    Denver

    07:00–07:45Off hours

14:00–14:45 lisbon · 4 of 5 free

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/zone-overlap

Props

bandStyle
"glass" | "solid" | "outline"
default "glass"
How the meeting band is drawn: a translucent wash, a pigment fill, or a dashed outline.
hourWidth
number
default 44
Pixels per hour of the shared timeline; the drag maps to it.
tone
"cool" | "warm" | "mono"
default "cool"
The palette: cobalt band and teal overlap, amber band and coral busy blocks, or ink with colour only where everyone is free.
people
ZonePerson[]
default defaultZonePeople
The team: { id, name, zone, offset (minutes from UTC), hours: [start, end] local hours, busy?: { start, end, title? }[] local minutes }. The first person is "you": the timeline runs on their clock.
date
number
default defaultZoneDate
Any time on the day shown, ms (its UTC date is the reference day).
now
number
default defaultZoneNow
The current time, ms, for local clocks and the now-line. Never read from the clock.
value / defaultValue
number
default — / 840
The band's start, minutes from midnight on the reference clock, controlled or initial.
onValueChange
(start: number) => void
default —
Fires from the drag, press or key that moved the band.
duration / defaultDuration
number
default — / 45
The meeting length in minutes, controlled or initial.
onDurationChange
(minutes: number) => void
default —
Fires from the stepper, bracket key or edge drag that changed the length.
minDuration
number
default 15
Shortest meeting, minutes.
maxDuration
number
default 240
Longest meeting, minutes.
onPick
(slot: ZoneSlot) => void
default —
Pick (or Enter on the band) was pressed: { start, end (ms), duration, free, total, everyoneFree }.
onSuggest
(slot: ZoneSlot) => void
default —
Suggest (or a Best times entry) moved the band: the next slot where everyone is free, or the best partial one.
status
"ready" | "loading" | "error"
default "ready"
Whether the calendars have arrived: loading draws rows as outlines, error a Try again.
onRetry
() => void
default —
Try again was pressed after the calendars failed to load.
title
string
default "Design sync"
The meeting's name.
height
number
default 540
Height of the surface in px; its body scrolls inside.
disabled
boolean
default false
Nothing can be moved or picked.
className
string
default —
Classes for the outer surface.