Skip to content

World Clock

Compare several time zones side by side and find a time that works for everyone.

import { WorldClock, WorldClockZone } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a world clock wherever people in different places have to meet: a team page, a meeting planner, a support rota, a booking that crosses borders. Each row is a zone or a person, with its name, its offset from UTC and from the first row, the time there with a sun or a moon, and a strip of the day. For one place's day as a ring, use Day Ring; for shifts across a week, Time Lanes.

import { WorldClock, type WorldClockZone } from '@youj/react'

const zones: WorldClockZone[] = [
  { timeZone: 'Europe/London', label: 'Ada · London' },
  { timeZone: 'America/New_York', label: 'Grace · New York' },
  { timeZone: 'Asia/Singapore', label: 'Lin · Singapore', workingHours: [8.5, 17.5] },
]

const [value, setValue] = useState<Date | null>(null)

<WorldClock zones={zones} value={value} onValueChange={setValue} step={30} />

A zone is an IANA timeZone, a label, and its workingHours as local hours from 0 to 24 ([9, 17] by default; [9, 17.5] ends at 17:30, and [22, 6] is a night shift). The waking hours outside work run from 07:00 to 22:00, and the rest is night.

The rows share one time axis: the first zone's day, midnight to midnight. On the days its clocks change that day is 23 or 25 hours long, and every offset is read from each zone's own clock at each moment, so the gap between two zones follows their changes too. Where a zone's date turns over, a line marks it and the cell names the new day.

The cursor is the chosen moment. With no value it follows the clock (now, by default the device's, ticking every second once the page is live); drag it, click a strip, or use the keys, and it snaps to step minutes (15 by default). onValueChange gets a Date, or null when Now is pressed. Moving past either end of the day moves the axis to the next or the previous one. Times read in 12 or 24 hours by the locale, unless hourCycle says otherwise; compact keeps one line per zone and drops the legend.

Reference

Propszones, value, now… (11)

Props

WorldClock props
PropType
zonesRequiredWorldClockZone[]

The zones, one row each. The first is the reference: the axis is its day, and the others say how far they are from it.

–
valueDate | number | null

The chosen moment. null follows the clock. Controlled.

–
defaultValueDate | number | null

The moment chosen at first.

default null

null
onValueChange(value: Date | null) => void

Called with the chosen moment, or null when Now is pressed.

–
nowDate | number

The clock. Default: the device's, ticking every second once the page is live.

–
stepnumber

Minutes the cursor snaps to and the arrow keys move by.

default 15

15
hourCycle12 | 24

12- or 24-hour times. Default: the locale's.

–
compactboolean

One line per zone and a slimmer strip, for a sidebar or a card.

default false

false
labelstring

The accessible name of the whole. Default: the labels' "World clock".

–
localestring

Default: the YoujProvider locale.

–
Other props pass through: extends ComponentProps<'div'>.
StylingParts and data attributes

Styling

Parts: world-clock (with data-state: live or set, data-yj-compact, and data-yj-overlap while the cursor is in hours everyone shares), world-clock-header, world-clock-overlap-summary, world-clock-now, world-clock-scroll, world-clock-grid, world-clock-ruler, world-clock-zone (with data-state: the period at the cursor, work, evening or night), world-clock-info, world-clock-daylight (with data-state: day or night), world-clock-label, world-clock-time, world-clock-offset, world-clock-date, world-clock-strip, world-clock-hour (with data-state: work, evening or night), world-clock-day-line, world-clock-track (with data-dragging), world-clock-overlap, world-clock-cursor, world-clock-handle (with data-state) and world-clock-legend. Working hours are the accent's subtle tint, the hours around them the band, night the well, and the shared hours a ring in success. Below 40rem (30rem compact) the rows scroll sideways.

MotionCursor, clock tick · reduced motion

Motion

Motion tokens
MomentTokens
Cursor movesfrom a key, a click or Now, the cursor glides to its new time; while dragged it follows the pointer with no lag
spring.snappy

0.2s, bounce 0

0.2s, bounce 0
Clock tickeach time ticks over letter by letter: the digits that change leave and arrive, the rest stay
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The cursor jumps to each new time and the times change in place.

Reduced motion

The cursor jumps to each new time instead of gliding, and the times change in place.

AccessibilityKeyboard, announcements

Accessibility

  • The whole is a group named by label (default: the labels' "World clock"). Each zone is a list item that reads its name, then its time, day, offset and whether it's day or night ("05:00, Tue 13 Jan, GMT-5, Night"); the strips are hidden from screen readers.
  • The cursor's handle is a slider named "Proposed time", described by how to move it, and its value reads the moment in the first zone ("Tue 13 Jan, 10:00 in London").
  • Left and right move it a step (mirrored in RTL), as do up and down; Shift with an arrow, or Page Up and Page Down, an hour; Home and End go to the ends of the day. Now returns to the clock and puts focus back on the handle.
  • After every change a polite live region says the moment in every zone: "10:00 in London is 05:00 in New York and 18:00 in Singapore", with "tomorrow" or "yesterday" where a zone is on another day.
  • Its words come from the labels (worldClock, worldClockCursor, worldClockHint, worldClockTime, worldClockSummary, worldClockOverlap, worldClockNoOverlap, worldClockWorkingHours, worldClockOutsideHours, worldClockNight, worldClockDaytime, worldClockEveryone and now), translated by the language packs; dates, offsets and "tomorrow" come from Intl in the locale.