Day Ring
Show scheduled runs on a daily dial, with overlaps and the next run highlighted, such as a job schedule.
import { DayRing, ScheduleJob, ScheduleOverlap } from '@youj/react'
Installation & usage
npm i @youj/reactFor 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 day ring where people plan recurring work around the clock: cron jobs, backups, report exports, maintenance windows. Each job gets its own track, so a day of runs reads at a glance, and where runs of different jobs collide a warning zone brackets them and a marker points at it from outside the ruler. For a timeline of what already happened, use a Timeline; for one bounded value, a Gauge.
import { DayRing, findOverlaps } from '@youj/react'
const jobs = [
{ id: 'backup', name: 'backup-db', runs: [120], duration: 40 },
{ id: 'reindex', name: 'reindex-search', runs: [150], duration: 25 },
{ id: 'sync', name: 'sync-crm', runs: [15, 255, 495, 735, 975, 1215], duration: 10 },
]
<DayRing jobs={jobs} timeZone="UTC" />
findOverlaps(jobs) // [{ start: 150, end: 160, jobs: ['backup', 'reindex'] }]runs are the starts today, in minutes after midnight, in timeZone (default: the browser's). A job with a duration draws its runs as arcs as long as they take; without one, as ticks. tone picks the job's color (the chart colors in turn by default), and paused draws it dim and dashed and leaves it out of every collision. highlight keeps one job lit, dims the rest and counts down to its next run: point it at the row a person is hovering in your own list. now pins the clock (tests, screenshots); by default it's live, and on the server there's no hand or countdown until the browser has the time.
Description
Five scheduled jobs on a 24-hour ring in UTC, where you can highlight one job and see how many runs overlap.
Runs collide when runs of two or more jobs share any minute; runs that only touch (one ends as the next starts) don't. A run without a duration counts as one minute, and a collision across midnight is one collision. findOverlaps(jobs) returns them (start, end in minutes, which passes 1440 across midnight, and the jobs involved), so your list can say who collides and when.
Code
day-ring-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsjobs, now, timeZone… (7)
Props
| Prop | Type | |
|---|---|---|
jobsRequired | readonly ScheduleJob[] | – |
now | Date | numberThe time the hand shows. Default: the live clock. | – |
timeZone | stringThe time zone the runs are in. Default: the browser's. | – |
highlight | stringA job id: its runs stay lit, the others dim, and the center counts down to its next run. | – |
locale | stringDefault: the YoujProvider locale. | – |
label | stringAccessible name. Default: the labels' "Daily schedule". | – |
nextLabel | stringThe center's caption over the countdown. Default: the labels' "Next run". | – |
formatTime | (minute: number) => stringFormats a minute of the day. Default: the locale's 24-hour HH:mm. | – |
Other props pass through: extends ComponentProps<'div'>. | ||
StylingParts and data attributes
Styling
Parts: day-ring, day-ring-svg, day-ring-ruler, day-ring-elapsed, day-ring-hour, day-ring-track, day-ring-overlap (with data-jobs), day-ring-runs (a job's group, with data-job, data-tone, data-paused, data-highlighted and data-dimmed), day-ring-run (with data-minute), day-ring-hand, day-ring-center, day-ring-label, day-ring-next, day-ring-value and day-ring-description. It fills its box's width up to 20rem; the center's type scales with it.
MotionFirst view, hand, A run moves… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| First viewthe runs appear as a radar pass: each fades in a step later the further round the day it sits | duration.deliberate560ms | 560ms | |
| Handturns from midnight to now on the smooth spring, with the elapsed shading on the same spring; then it steps each minute | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| A run movesit glides round its track to the new time | duration.slowease.standard340ms · cubic-bezier(0.2, 0, 0, 1) | 340mscubic-bezier(0.2, 0, 0, 1) | |
| Highlightthe other jobs fade down | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Collisionszones fade in and out as runs move or jobs pause | duration.baseease.enter220ms · cubic-bezier(0.16, 1, 0.3, 1) | 220mscubic-bezier(0.16, 1, 0.3, 1) |
Reduced motion
Drawn in place; a run that moves jumps. The countdown still ticks.
Reduced motion
Every run and the hand are drawn in place, with no radar pass; runs that move jump. The countdown keeps ticking: it's information, not motion.
AccessibilityARIA, announcements
Accessibility
- The ring is an image (
role="img") named bylabel(default: the labels' "Daily schedule") and described in words: how many runs a day, how many collisions, the next run, and one sentence per collision ("backup-db and reindex-search overlap at 02:30"). - The countdown isn't live, so it doesn't talk every second; announce a start yourself if the page needs it.
- A dial turns clockwise in every script, so the ring keeps its direction under RTL; the text in the center follows the page.
- Its words come from the labels (
dayRing,nextRun,runsIn,scheduleRunAt,scheduleRuns,scheduleOverlaps,scheduleOverlapAt), translated by the language packs.