ComponentsData display

Date label

A date written the way people say it: words near today, a short date this year, month and year before that. In the agency's time zone and date order.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

import { DateLabel } from '@/ui/date-label';<DateLabel value="2026-10-19" now="2026-10-15" />

Relative

The default, and the one tables use. Words within 7 days either side (Today, Tomorrow, Yesterday, the weekday), then the date: day and month this year, month and year in another. Now here is Thu 15 Oct 2026.

2026-10-15
2026-10-16
2026-10-14
2026-10-19
2026-10-10
2026-11-03
2024-02-12
import { DateLabel } from '@/ui/date-label';

const dates = ['2026-10-15', '2026-10-16', '2026-10-14', '2026-10-19', '2026-10-10', '2026-11-03', '2024-02-12'];

// Now is Thu 15 Oct 2026. Words within 7 days either side, the date after. Each stored date, then the label.
export default function DateLabelRelative() {
  return (
    <dl className="grid grid-cols-[auto_auto] items-center gap-x-400 gap-y-100">
      {dates.map((date) => (
        <div key={date} className="contents">
          <dt className="text-body leading-body text-text-subtle tabular-nums">{date}</dt>
          <dd>
            <DateLabel value={date} now="2026-10-15" />
          </dd>
        </div>
      ))}
    </dl>
  );
}

Full date and date order

display="full" writes the whole date, for dates where the year matters every time: a Placement's start, a guarantee's end. format sets the order: day first (the default), month first or ISO. The month is always a name, so 6 Oct never reads as 10 Jun.

import { DateLabel } from '@/ui/date-label';

export default function DateLabelFull() {
  return (
    <div className="flex flex-wrap items-center gap-400">
      <DateLabel value="2026-10-06" display="full" />
      <DateLabel value="2026-10-06" display="full" format="MM/DD/YYYY" />
      <DateLabel value="2026-10-06" display="full" format="YYYY-MM-DD" />
    </div>
  );
}

Appearance

Neutral ink for most dates, upcoming in the faintest text for something planned. warning and danger draw the status pair for a date that needs action: a Job ordered a week ago with no CV sent, a task two weeks overdue. statusLabel says why to screen readers.

import { DateLabel } from '@/ui/date-label';

export default function DateLabelAppearance() {
  return (
    <div className="flex flex-wrap items-center gap-400">
      <DateLabel value="2026-10-13" now="2026-10-15" prefix="Met" />
      <DateLabel value="2026-10-19" now="2026-10-15" prefix="Meeting" appearance="upcoming" />
      <DateLabel value="2026-10-02" now="2026-10-15" appearance="warning" statusLabel="No CV sent for a week" />
      <DateLabel value="2026-09-28" now="2026-10-15" appearance="danger" statusLabel="Overdue" />
    </div>
  );
}

Exact date in the tooltip

Hover the date for the exact date and time, then any counts from detail. A timestamp falls on its day in the agency's timeZone.

import { DateLabel } from '@/ui/date-label';

export default function DateLabelTooltip() {
  return (
    <DateLabel
      value="2026-10-13T07:20:00Z"
      timeZone="Asia/Ho_Chi_Minh"
      now="2026-10-15"
      prefix="Met"
      detail="3 meetings, 12 emails"
    />
  );
}

Times and time zones

A timestamp within a day of now reads "5m ago" or "3h ago". Its day falls in the agency's timeZone; its time of day reads in the viewer's zone, named when it differs from the agency's (timeOfDay="agency" keeps everything in the agency's zone). Here the viewer is in London and the agency in Ho Chi Minh City: hover or focus each one.

import { DateLabel } from '@/ui/date-label';

// Activity times within a day read "5m ago"; the tooltip gives the time of day in the viewer's zone, named because
// this viewer is in London and the agency in Ho Chi Minh City.
export default function DateLabelTimestamps() {
  const zones = { timeZone: 'Asia/Ho_Chi_Minh', viewerTimeZone: 'Europe/London', now: '2026-10-15T09:30:00Z' };
  return (
    <div className="flex flex-wrap items-center gap-400">
      <DateLabel value="2026-10-15T09:25:00Z" {...zones} />
      <DateLabel value="2026-10-15T06:30:00Z" {...zones} />
      <DateLabel value="2026-10-13T07:20:00Z" prefix="Emailed" {...zones} />
    </div>
  );
}

Edge cases

A value that isn't a date shows as stored, never as "Invalid Date". A long label ends in an ellipsis and its tooltip gives the full words. A far-future date reads as written.

import { DateLabel } from '@/ui/date-label';

// A value that isn't a date shows as stored, never "Invalid Date". A long prefix ends in an ellipsis in a narrow cell.
export default function DateLabelEdgeCases() {
  return (
    <div className="flex flex-col items-start gap-100">
      <DateLabel value="next Tuesday" now="2026-10-15" />
      <div className="w-30">
        <DateLabel value="2026-10-19" now="2026-10-15" prefix="Interview with Nguyễn Thị Minh Khai" />
      </div>
      <DateLabel value="2091-01-01" now="2026-10-15" display="full" />
    </div>
  );
}

On this page