Detent web components
By Shane Logsdon

Media & effects

Matrix Clock

Implemented

World time, one rolling matrix character at a time

detent-matrix-clock

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-cinematic
Group
Media & effects

9.6 KB gzip for the class module · inlines the shared spring

Variants

A live world clock in 5×7 dot-matrix glyphs. Times come from the platform's IANA zone rules, so no time service is contacted. Add a city through the native country and city selects, remove one with the row's × button (the last row cannot be removed), and hover a row to watch its characters roll through the staggered rebuild. The board announces one concise name and hides every decorative dot from assistive technology.

Force reduced motion Use a supplied city list
No city change yet.

Recorded states

  • error recorded state; no setter
  • reduced motion recorded state; no setter

Usage

Import

import '/components/matrix-clock/define.js';

// or register it yourself
import { DetentMatrixClock } from '/components/matrix-clock/matrix-clock.js';

Attributes

  • format
  • reduce-motion
  • show-controls
  • show-country
  • show-seconds
  • update-interval

Properties

  • cities
  • onCitiesChange
  • onRemove

Slots

No slots are recorded for this component.

Events

  • detent-cities-change — { cities: AirportMatrixCityId[] }
  • detent-remove — { id: AirportMatrixCityId }

Example

Runnable example: site/examples/matrix-clock.ts. The snippet below is generated from the catalogued attributes.

<detent-matrix-clock
  format="24h"
  reduce-motion
  show-controls
  show-country
  show-seconds
  update-interval="1000"
></detent-matrix-clock>

Property-only values

const element = document.querySelector('detent-matrix-clock');
if (element !== null) {
  // cities: AirportMatrixCityId[]
  element.cities = [];
  // onCitiesChange: (cities: AirportMatrixCityId[]) => void
  element.onCitiesChange = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/AirportMatrixClock.tsx sha256 2d9938547212…

Native files

The native module and its artifacts:

  • src/components/matrix-clock/matrix-clock.ts
  • src/components/matrix-clock/styles.css
  • src/components/matrix-clock/define.ts
  • docs/components/matrix-clock.md
  • site/examples/matrix-clock.ts
  • tests/browser/matrix-clock.spec.ts
  • evidence/components/matrix-clock.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
formatformat'12h' | '24h''24h'Kebab-case attribute; camelCase property.
reduce-motion———Recorded in the catalog as an attribute.
show-controlsshowControlsbooleantrue when cities is omittedPresence semantics: present means true, absent means false.
show-countryshowCountrybooleanfalsePresence semantics: present means true, absent means false.
show-secondsshowSecondsbooleanfalsePresence semantics: present means true, absent means false.
update-intervalupdateIntervalnumber1000Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
citiesAirportMatrixCityId[]['los-angeles', 'london', 'tokyo']Property-only: assign from script; not accepted as an attribute.
onCitiesChange(cities: AirportMatrixCityId[]) => voidundefinedProperty-only: assign from script; not accepted as an attribute.
onRemove——Recorded in the catalog without a top-level type; pinned from source when the row is ported.

Events

Events
EventUpstream propPayload typeNotes
detent-cities-changeonCitiesChange(cities: AirportMatrixCityId[]) => voidComposed, bubbles; fires only for a user action.
detent-removeonRemove(id: AirportMatrixCityId) => voidComposed, bubbles; fires only for a user action.

Slots

No slots are recorded for this component.

CSS customisation

  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • char, row-count, row-index, country, row and text are MatrixGlyph/MatrixText/CountryLabel/row params in the pinned source, not board settings; they are dropped from the native surface and recorded here so the row does not keep dead API.
  • A clock tick rolls only the cells that changed, while upstream animates only on a pointerenter rebuild (its rebuildId never changes on the interval); the native port follows the row parity feature with springSnappy 380/30/0.5, +/-72% travel and a stagger of min(index*12, 90) ms.
  • The clock pauses while hidden or offscreen; the upstream setInterval kept running.
  • reduce-motion is an attribute as well as the media query.
  • The error state is the pinned invalid-input fallback, not an error surface.

What it does

A live world clock drawn as an airport split-flap board: one compact 5×7 dot-matrix character per cell, the time and the city name in the same glyph alphabet, an optional country line, and built-in country/city selectors that add and remove cities.

Upstream behaviour
  • IANA timezone formatting handles local time and daylight-saving changes without a time service
  • Three-city default with a five-city maximum, duplicate removal, and safe fallback for invalid input
  • Built-in country and city selectors let viewers add or remove cities from the supported set
  • Compact bitmap glyphs for city names and 12-hour or 24-hour time
  • Only changed character cells roll; unchanged cells retain their rendered state
  • Hovering a city or time rolls its characters through a brief staggered rebuild
  • Dedicated dark matrix board surface with authentic high-contrast dot matrix typography
Differences from upstream
  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • char, row-count, row-index, country, row and text are MatrixGlyph/MatrixText/CountryLabel/row params in the pinned source, not board settings; they are dropped from the native surface and recorded here so the row does not keep dead API.
  • A clock tick rolls only the cells that changed, while upstream animates only on a pointerenter rebuild (its rebuildId never changes on the interval); the native port follows the row parity feature with springSnappy 380/30/0.5, +/-72% travel and a stagger of min(index*12, 90) ms.
  • The clock pauses while hidden or offscreen; the upstream setInterval kept running.
  • reduce-motion is an attribute as well as the media query.
  • The error state is the pinned invalid-input fallback, not an error surface.
Previous: AI Response Next: Alert

Docs

Components

Project