# Cafe Footer With Giant Local Time

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/cafe-footer-giant-local-time-on-rolling-digits).

- Page, with a live preview: https://shadcn.exchange/r/weinc/cafe-footer-giant-local-time-on-rolling-digits
- Screenshot: https://shadcn.exchange/shot/weinc/cafe-footer-giant-local-time-on-rolling-digits/light.png
- Registry item: https://we.inc/r/cafe-footer-giant-local-time-on-rolling-digits.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/cafe-footer-giant-local-time-on-rolling-digits.json
```

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

## Files

### `components/we-inc/cafe-footer-giant-local-time-on-rolling-digits.tsx`

```tsx
import { Fragment, useEffect, useRef } from "react";
import type { CSSProperties } from "react";

const ZONE = "Europe/Lisbon"; // time zone of the shop
const CITY = "Lisbon"; // city named next to the clock
const OPEN = 8; // opening hour, 24h clock
const CLOSE = 19; // closing hour, 24h clock

// Same markup, styles and script as the plain HTML version.
const STYLES = `
/* Change these: background, text, muted text, rule colour, accent (links' focus ring and highlights). */
  .w4clk { --w4clk-bg: #9c3f25; --w4clk-ink: #f8eddf; --w4clk-muted: #f2dcc8; --w4clk-line: #b45a3e; --w4clk-accent: #ffe2b8; }
  .w4clk { position: relative; overflow: hidden; background: var(--w4clk-bg); color: var(--w4clk-ink); font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; padding: 72px clamp(20px, 4vw, 56px) 24px; }
  .w4clk *, .w4clk *::before, .w4clk *::after { box-sizing: border-box; }
  .w4clk-in { max-width: 1240px; margin: 0 auto; }
  .w4clk-top { display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr)); gap: 32px 40px; }
  .w4clk-brand { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -.01em; }
  .w4clk-blurb { margin: 12px 0 0; max-width: 32ch; font-size: 15px; line-height: 1.6; color: var(--w4clk-muted); }
  .w4clk-addr { margin: 16px 0 0; font-style: normal; font-size: 14px; line-height: 1.65; color: var(--w4clk-muted); }
  .w4clk-col h2 { margin: 0 0 6px; font-size: 12px; font-weight: 650; letter-spacing: .14em; text-transform: uppercase; color: var(--w4clk-muted); }
  .w4clk-col ul, .w4clk-legal ul { list-style: none; margin: 0; padding: 0; }
  .w4clk-col a, .w4clk-legal a { display: inline-flex; align-items: center; min-height: 40px; color: inherit; text-decoration: none; font-size: 15px; border-radius: 3px; }
  .w4clk-col a:hover, .w4clk-legal a:hover { text-decoration: underline; text-underline-offset: 4px; }
  .w4clk a:focus-visible { outline: 2px solid var(--w4clk-accent); outline-offset: 3px; }
  .w4clk-big { container-type: inline-size; margin: 56px 0 0; }
  .w4clk-legal { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0 24px; margin-top: 24px; padding-top: 6px; border-top: 1px solid var(--w4clk-line); font-size: 13px; color: var(--w4clk-muted); }
  .w4clk-legal p { margin: 10px 0; }
  .w4clk-legal ul { display: flex; flex-wrap: wrap; gap: 0 20px; }
  .w4clk-legal a { font-size: 13px; }
  .w4clk-sr { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  @media (max-width: 760px) {
    .w4clk { padding-top: 56px; }
    .w4clk-top { grid-template-columns: 1fr 1fr; gap: 24px 20px; }
    .w4clk-lead { grid-column: 1 / -1; }
  }
  /* Change these: clock size (cqi of the footer width) and how long a digit takes to roll. */
  .w4clk { --w4clk-size: 25cqi; --w4clk-roll: 1.1s; }
  .w4clk-label { margin: 0; text-align: center; font-size: 14px; letter-spacing: .2em; text-transform: uppercase; color: var(--w4clk-muted); }
  .w4clk-clock { display: flex; justify-content: center; margin: 4px 0 0; font: 650 var(--w4clk-size)/.9 ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif; font-variant-numeric: tabular-nums; letter-spacing: -.04em; white-space: nowrap; overflow: hidden; }
  .w4clk-reel { display: block; height: .9em; overflow: hidden; }
  .w4clk-strip { display: block; transform: translateY(calc(var(--d) * -10%)); transition: transform var(--w4clk-roll) cubic-bezier(.7, 0, .18, 1); }
  .w4clk-reel:nth-child(2) .w4clk-strip { transition-delay: .08s; }
  .w4clk-reel:nth-child(4) .w4clk-strip { transition-delay: .16s; }
  .w4clk-reel:nth-child(5) .w4clk-strip { transition-delay: .24s; }
  .w4clk-strip span { display: block; height: .9em; text-align: center; }
  .w4clk-colon { display: block; height: .9em; line-height: .8; padding: 0 .02em; animation: w4clk-blink 2s ease-in-out infinite; }
  .w4clk[data-off] .w4clk-colon { animation-play-state: paused; }
  @keyframes w4clk-blink { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
  .w4clk-status { margin: 14px auto 0; max-width: 46ch; text-align: center; font-size: 16px; line-height: 1.5; }
  @media (prefers-reduced-motion: reduce) { .w4clk-strip { transition: none; } .w4clk-colon { animation: none; } }
`;

export default function CafeFooterGiantLocalTimeOnRollingDigits() {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    const root: any = ref.current;
    if (!root) return;
    var strips = [].slice.call(root.querySelectorAll(".w4clk-strip"));
    var label = root.querySelector(".w4clk-label"), status = root.querySelector(".w4clk-status");
    var fmt = new Intl.DateTimeFormat("en-GB", { timeZone: ZONE, hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
    var timer = 0;
    function two(n) { return (n < 10 ? "0" : "") + n; }
    function show() {
      clearTimeout(timer);
      var t = fmt.format(new Date()).replace(/\D/g, "");
      for (var i = 0; i < 4; i++) strips[i].style.setProperty("--d", t[i]);
      var hr = +t.slice(0, 2), open = hr >= OPEN && hr < CLOSE;
      label.textContent = "Right now in " + CITY;
      status.textContent = "It is " + t.slice(0, 2) + ":" + t.slice(2) + " here. " + (open ? "We are open until " + two(CLOSE) + ":00." : "We are closed; doors open at " + two(OPEN) + ":00.");
      /* wake again just after the next minute turns */
      timer = setTimeout(show, 60050 - (Date.now() % 60000));
    }
    var io = new IntersectionObserver(function (en) {
      if (en[0].isIntersecting) { root.removeAttribute("data-off"); show(); }
      else { root.setAttribute("data-off", ""); clearTimeout(timer); }
    });
    io.observe(root);
    return function () { clearTimeout(timer); io.disconnect(); };
  }, []);

  return (
    <footer className="w4clk" ref={ref}>
      <style>{STYLES}</style>
      <div className="w4clk-in">
        <div className="w4clk-top">
          <div className="w4clk-lead">
            <p className="w4clk-brand">Rua Doce Café</p>
            <p className="w4clk-blurb">
              Custard tarts at eight, bica all day, and a long marble counter facing the tram stop.
            </p>
            <address className="w4clk-addr">
              Rua das Flores 61, 1200-194 Lisboa
              <br />
              Every day, 08:00 to 19:00
              <br />
              +351 21 555 0148
            </address>
          </div>
          <div className="w4clk-col">
            <h2>Café</h2>
            <ul>
              <li>
                <a href="#">Menu</a>
              </li>
              <li>
                <a href="#">Pastry times</a>
              </li>
              <li>
                <a href="#">Takeaway</a>
              </li>
            </ul>
          </div>
          <div className="w4clk-col">
            <h2>Visit</h2>
            <ul>
              <li>
                <a href="#">Directions</a>
              </li>
              <li>
                <a href="#">Seating</a>
              </li>
              <li>
                <a href="#">Private mornings</a>
              </li>
            </ul>
          </div>
          <div className="w4clk-col">
            <h2>Say olá</h2>
            <ul>
              <li>
                <a href="#">Write to us</a>
              </li>
              <li>
                <a href="#">Instagram</a>
              </li>
              <li>
                <a href="#">Work with us</a>
              </li>
            </ul>
          </div>
        </div>
        <div className="w4clk-big">
          <p className="w4clk-label">Doors open at</p>
          <div className="w4clk-clock" aria-hidden="true">
            {[0, 8, 0, 0].map((d, k) => (
              <Fragment key={k}>
                {k === 2 && <span className="w4clk-colon">:</span>}
                <span className="w4clk-reel">
                  <span className="w4clk-strip" style={{ "--d": d } as CSSProperties}>
                    {"0123456789".split("").map((n) => <span key={n}>{n}</span>)}
                  </span>
                </span>
              </Fragment>
            ))}
          </div>
          <p className="w4clk-status">Open every day from 08:00 until 19:00.</p>
        </div>
        <div className="w4clk-legal">
          <p>© 2026 Rua Doce, Lda. Prices include VAT.</p>
          <ul>
            <li>
              <a href="#">Privacy</a>
            </li>
            <li>
              <a href="#">Allergens</a>
            </li>
            <li>
              <a href="#">Complaints book</a>
            </li>
          </ul>
        </div>
      </div>
    </footer>
  );
}
```
