# Golden Hour: Studio Clocks

> A design studio's contact card lit for the local time in each city. Switching from Lisbon to Tokyo carries the sun through the hours between.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/golden-hour-studios-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/golden-hour-studios-demo
- Screenshot: https://shadcn.exchange/shot/keyframeui/golden-hour-studios-demo/light.png
- Registry item: https://keyframeui.com/r/golden-hour-studios-demo.json
- Categories: background, hero, svg
- Listed: 2026-11-02, source last changed 2026-11-02

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/keyframeui/golden-hour-studios-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/golden-hour.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/golden-hour#golden-hour-studios-demo

## Files

### `registry/demos/golden-hour-studios-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { GoldenHour } from "@/components/ui/golden-hour";

const STUDIOS = [
  { city: "Lisbon", zone: "Europe/Lisbon", address: "Rua das Flores 28", team: "6 in the studio", open: [9, 19] },
  { city: "New York", zone: "America/New_York", address: "55 Water St, Brooklyn", team: "11 in the studio", open: [9, 18] },
  { city: "Tokyo", zone: "Asia/Tokyo", address: "2-14 Kamimeguro, Meguro", team: "4 in the studio", open: [10, 19] },
] as const;

// The time now, read on the client only: the page is prerendered, so a server
// clock would be stale by the time anyone reads it.
const subscribe = (onChange: () => void) => {
  const timer = window.setInterval(onChange, 30_000);
  return () => window.clearInterval(timer);
};
const readMinute = () => Math.floor(Date.now() / 60_000);
const noMinute = () => null;

function hourIn(zone: string, minute: number) {
  const parts = new Intl.DateTimeFormat("en-US", { timeZone: zone, hour: "numeric", minute: "numeric", hourCycle: "h23" }).formatToParts(
    new Date(minute * 60_000),
  );
  const get = (type: string) => Number(parts.find((part) => part.type === type)?.value ?? 0);
  return get("hour") + get("minute") / 60;
}

function clock(hour: number) {
  const minutes = Math.round(hour * 60) % (24 * 60);
  const h = Math.floor(minutes / 60);
  return `${((h + 11) % 12) + 1}:${String(minutes % 60).padStart(2, "0")} ${h < 12 ? "AM" : "PM"}`;
}

function hourLabel(hour: number) {
  return `${((hour + 11) % 12) + 1} ${hour < 12 ? "AM" : "PM"}`;
}

export function GoldenHourStudiosDemo() {
  const minute = React.useSyncExternalStore(subscribe, readMinute, noMinute);
  const [picked, setPicked] = React.useState(0);
  const tabs = React.useRef<(HTMLButtonElement | null)[]>([]);
  const studio = STUDIOS[picked];
  const hour = minute === null ? null : hourIn(studio.zone, minute);
  const isOpen = hour !== null && hour >= studio.open[0] && hour < studio.open[1];

  const pick = (index: number) => {
    const next = (index + STUDIOS.length) % STUDIOS.length;
    setPicked(next);
    tabs.current[next]?.focus();
  };

  return (
    <div className="w-full max-w-3xl">
      <GoldenHour
        id="golden-hour-studio"
        role="tabpanel"
        aria-labelledby={`golden-hour-studio-tab-${picked}`}
        time={hour ?? "golden"}
        className="flex aspect-[4/5] w-full items-end rounded-3xl p-4 sm:aspect-[16/10] sm:p-6"
      >
        <div className="w-full max-w-xs rounded-2xl bg-white/75 p-4 text-zinc-950 shadow-sm ring-1 ring-white/60 backdrop-blur-xl sm:p-5 dark:bg-zinc-950/55 dark:text-white dark:ring-white/10">
          <p className="text-xs font-medium text-zinc-500 dark:text-zinc-400">Tinta · {studio.city} studio</p>
          <p className="mt-1 text-4xl font-medium tracking-[-0.03em] tabular-nums">{hour === null ? "--:--" : clock(hour)}</p>
          <p className="mt-1.5 flex items-center gap-1.5 text-sm text-zinc-600 dark:text-zinc-300">
            <span
              aria-hidden
              className={`size-1.5 rounded-full ${isOpen ? "bg-emerald-500" : "bg-zinc-400 dark:bg-zinc-500"}`}
            />
            {hour === null
              ? studio.team
              : isOpen
                ? `Open until ${hourLabel(studio.open[1])} · ${studio.team}`
                : `Closed · opens at ${hourLabel(studio.open[0])}`}
          </p>
          <p className="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">{studio.address}</p>
          <a
            href="#book"
            className="mt-4 inline-flex h-9 items-center rounded-full bg-zinc-950 px-4 text-sm font-medium text-white transition-[background-color,scale] duration-150 ease-out hover:bg-zinc-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-950 active:scale-[0.97] dark:bg-white dark:text-zinc-950 dark:hover:bg-zinc-200 dark:focus-visible:outline-white"
          >
            {isOpen || hour === null ? "Book a call" : "Leave a note for the morning"}
          </a>
        </div>
      </GoldenHour>

      <div
        role="tablist"
        aria-label="Studios"
        className="mt-4 grid grid-cols-3 gap-1 rounded-2xl bg-zinc-100 p-1 dark:bg-white/[0.06]"
        onKeyDown={(event) => {
          if (event.key === "ArrowRight") pick(picked + 1);
          else if (event.key === "ArrowLeft") pick(picked - 1);
          else return;
          event.preventDefault();
        }}
      >
        {STUDIOS.map((place, index) => {
          const selected = index === picked;
          return (
            <button
              key={place.city}
              ref={(element) => {
                tabs.current[index] = element;
              }}
              id={`golden-hour-studio-tab-${index}`}
              type="button"
              role="tab"
              aria-selected={selected}
              aria-controls="golden-hour-studio"
              tabIndex={selected ? 0 : -1}
              onClick={() => setPicked(index)}
              className={`flex flex-col items-start rounded-xl px-3 py-2 text-left transition-[background-color,box-shadow] duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-950 dark:focus-visible:outline-white ${
                selected
                  ? "bg-white shadow-sm ring-1 ring-zinc-950/5 dark:bg-white/10 dark:ring-white/10"
                  : "hover:bg-white/60 dark:hover:bg-white/[0.04]"
              }`}
            >
              <span className="text-sm font-medium text-zinc-950 dark:text-white">{place.city}</span>
              <span className="text-xs text-zinc-500 tabular-nums dark:text-zinc-400">
                {minute === null ? "--:--" : clock(hourIn(place.zone, minute))}
              </span>
            </button>
          );
        })}
      </div>
    </div>
  );
}
```
