Partial Period

Compares today so far with yesterday at the same minute by default, so a still-filling period doesn't read as a slump.

StopsToday looks like a slump vs yesterday

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/partial-period.json

The files land in your project. They're yours to edit.

Usage

partial-period-usage.tsx
import { PartialPeriod } from "@/components/ui/partial-period"

<PartialPeriod
  className="max-w-md"
  label="Orders today"
  current={today}
  previous={yesterday}
  elapsed={14.25}
  cutoffLabel="2:15 PM"
  bucketLabels={hours}
  defaultMode="full"
/>

What it catches

  • Today-so-far compared against all of yesterday
  • The current bucket shown as if complete: hatched instead
  • Projection presented as fact: labelled as an estimate
  • Full-period mode chosen: warning with a one-tap switch back

Props

PartialPeriod props
PropTypeDefault
label* (required)

Metric name, e.g. "Orders".

string—
current* (required)

Current period per bucket; buckets that haven't happened yet can be null.

(number | null)[]—
previous* (required)

Previous, complete period per bucket.

number[]—
elapsed* (required)

Buckets elapsed in the current period, with a fraction for the one in progress.

number—
bucketLabels

Axis label per bucket, e.g. "12 AM", "6 AM". Every few are shown.

string[]—
cutoffLabel* (required)

Where the current period is cut, e.g. "2:15 PM" or "13 Oct".

string—
currentLabel

Name of the current period, e.g. "Today".

string"Today"
previousLabel

Name of the previous period, lower case, e.g. "yesterday".

string"yesterday"
mode

"to-date" compares like for like; "full" compares with the whole previous period.

"to-date" | "full"—
defaultMode

Initial comparison mode when uncontrolled.

"to-date" | "full""to-date"
onModeChange

Called when the user switches between to-date and full.

(mode: "to-date" | "full") => void—
higherIsBetter

Whether a rise is good news.

booleantrue
showProjection

Show where the period is heading, marked as an estimate.

booleantrue
format

Number format for totals.

(value: number) => string(v) => plain.format(v)
toDateLabel

Templates: `{current}`, `{previous}`, `{cutoff}`, `{pct}`, `{change}` and `{value}` are filled in.

string"By {cutoff}"
fullLabel

Label for the full-period option.

string"All of {previous}"
soFarText

Template for the so-far comparison.

string"{current} so far · {pct} of the period"
warningText

Shown when comparing against the full previous period.

string"{current} is only {pct} done, so comparing it with all o…
fixLabel

Button text to switch back to like for like.

string"Compare by {cutoff}"
projectionText

Template for the projected finish.

string"On pace for about {value} if the rest follows {previous}."
partialLabel

Label for the bucket still filling.

string"so far"
estimateLabel

Label marking the projection as an estimate.

string"estimate"

* required. Also accepts the native props of <figure> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Mode switch is a labelled radiogroup
  • Change and warnings are in polite live regions with text direction, not only arrows
  • The bar chart is aria-hidden; the numbers are given in text

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Projection assumes today follows yesterday's shape
  • Works with the buckets you pass; it doesn't fetch data
  • One previous period, not a seasonal baseline

What it stops

Today looks like a slump vs yesterday

Also known as

analytics · dashboard · today vs yesterday · period comparison · kpi · mtd · chart · projection

Dependencies

Pairs well with