GlassKit UI
Components

Clock

The home time / date complication: a big tabular time, a quieter date, and an optional meta line (weather, alarm). Pass preformatted strings — you own the locale.

9:41Tuesday, June 972° · Sunny
600 × 600 · live

Installation

npx @glasskit-ui/cli add clock

Install the SDK (it provides GlassViewport, useDpad and the stylesheet), then copy these files into your project:

npm install @glasskit-ui/react
// components/lib/utils.tsexport type ClassValue = string | number | null | undefined | false;/** * Join truthy class names. Dependency-free on purpose: the lens components * style via bespoke semantic classes (no conflicting Tailwind utilities to * de-dupe), so this needs no clsx/tailwind-merge and resolves from anywhere * the registry is vendored. */export function cn(...inputs: ClassValue[]): string {  return inputs.filter(Boolean).join(" ");}/** * Accessible name from a free-form `label` prop: the label itself when it's a * plain string, otherwise undefined (a ReactNode can't become an aria-label). */export function stringLabel(label: unknown): string | undefined {  return typeof label === "string" ? label : undefined;}
// components/glasskit/clock.tsx"use client";import { useEffect, useMemo, useState, type ReactNode } from "react";import { cn } from "../lib/utils";/** * <Clock> — the home time/date complication: a big tabular time and a quieter * date line. Self-ticking by default (minute-aligned — no per-second renders); * pass `time` to control it instead (the prop always wins, and you own the * formatting). In live mode an omitted `date` auto-formats too. Optional * `meta` for a trailing line (weather, alarm). */export function Clock({  time,  date,  meta,  locale,  hour12,  className,}: {  /** Controlled, preformatted time. Omit to tick live. */  time?: ReactNode;  /** Date line. In live mode, omitting it shows the live date. */  date?: ReactNode;  meta?: ReactNode;  /** BCP 47 locale for live formatting (default: the device locale). */  locale?: string;  /** Force 12/24-hour live time (default: the locale's convention). */  hour12?: boolean;  className?: string;}) {  const live = time == null;  // null until mounted → server and first client render agree on the  // placeholder, then the first tick swaps in the real time (hydration-safe).  const [now, setNow] = useState<Date | null>(null);  useEffect(() => {    if (!live) return;    let timer: ReturnType<typeof setTimeout>;    const tick = () => {      const d = new Date();      setNow(d);      // Re-tick just past the next minute boundary.      timer = setTimeout(        tick,        60_000 - (d.getSeconds() * 1000 + d.getMilliseconds()) + 50,      );    };    tick();    return () => clearTimeout(timer);  }, [live]);  // Intl formatters are expensive to construct — build once per locale, not  // per minute tick.  const timeFormat = useMemo(    () =>      new Intl.DateTimeFormat(locale, {        hour: "numeric",        minute: "2-digit",        hour12,      }),    [locale, hour12],  );  const dateFormat = useMemo(    () =>      new Intl.DateTimeFormat(locale, {        weekday: "long",        month: "long",        day: "numeric",      }),    [locale],  );  const liveTime = now ? timeFormat.format(now) : "--:--";  const liveDate = now ? dateFormat.format(now) : null;  const shownTime = live ? liveTime : time;  const shownDate = date ?? (live ? liveDate : null);  return (    <div className={cn("gk-clock", className)}>      <span className="gk-clock__time">{shownTime}</span>      {shownDate != null ? (        <span className="gk-clock__date t-body">{shownDate}</span>      ) : null}      {meta != null ? (        <span className="gk-clock__meta t-caption">{meta}</span>      ) : null}    </div>  );}

Usage

// self-ticking, device locale<Clock meta="72° · Sunny" />// or controlled — you own the formatting<Clock time="9:41" date="Tuesday, June 9" />

Props

Prop

Type