Clock Skew

Detects when a device clock is off from server time, says what breaks and corrects countdowns, so OTP codes and timers don't fail mysteriously.

StopsWrong phone clock breaks OTP codes

Installation

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

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

Usage

clock-skew-usage.tsx
import { ClockSkew, CorrectedCountdown } from "@/components/ui/clock-skew"

{times ? <CorrectedCountdown to={times.expires} className="font-medium text-foreground" />

What it catches

  • Device clocks ahead of or behind server time beyond a threshold
  • Authenticator codes that will be rejected because of the offset
  • "Expires in" timers that would show the wrong time
  • Clock fixes, by clearing itself when the offset goes away

Props

ClockSkew props
PropTypeDefault
serverTime

Server time as of now, e.g. rendered into the page by the server. Wins over `measureUrl`.

Date | number | string—
serverTimeUncertainty

How stale `serverTime` may be (network + render time), in ms.

number1000
measureUrl

Same-origin URL to read the Date header from, e.g. "/api/health". Re-checked when the tab is shown again.

string—
threshold

Smallest skew worth warning about, in ms. Default: one 30s TOTP step.

number30_000
onOffsetChange

Called whenever the estimate changes (null until known).

(estimate: ClockOffset | null) => void—
title

Templates: `{skew}` ("4 min 12 s") and `{direction}` ("behind"/"ahead").

string"Your device clock is {skew} {direction}"
otpText

Explains the effect on authenticator codes.

string"Codes from your authenticator app may be rejected."
timersBehindText

Explains timers when the device is behind.

string"Timers elsewhere would show {skew} more than you have. T…
timersAheadText

Explains timers when the device is ahead.

string"Timers elsewhere would show {skew} less than you have. T…
secureSitesText

Explains the effect on secure sites.

string"Secure sites and sign-in links may refuse to work."
fixText

Steps for fixing the device clock.

string"Turn on “Set time automatically” in your device’s date a…
deviceLabel

Label for the device's time.

string"This device"
actualLabel

Label for the actual time.

string"Actual time"
howToFixLabel

Text for the how-to-fix toggle.

string"How to fix"
checkAgainLabel

Text for the check-again button.

string"Check again"
dismissLabel

Text for the dismiss button.

string"Got it"

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

Accessibility

  • A role="region" with an aria-label from the title
  • The message is aria-live="polite"
  • "How to fix" toggle uses aria-expanded and aria-controls
  • Animation is skipped with prefers-reduced-motion

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

Limitations

  • Accuracy is limited by the Date header's one-second resolution and network round-trip
  • Needs a server time source (prop or a measurable URL)

What it stops

Wrong phone clock breaks OTP codes

Also known as

clock skew · device time · totp · authenticator · otp · server time · date header · countdown

Field type

Dependencies

Pairs well with