Technical Documentation

AmsterGammon Clock

Version 25.1210.04-JJ

A deep dive into the architecture, technology stack, and engineering challenges behind the offline-first Progressive Web App.

The Technology Stack

React (No Build Step)

Unlike typical React apps that use bundlers (Webpack/Vite), this app runs directly in the browser using React.createElement. This allows the app to be a single, transparent HTML file without complex compilation steps, making it easy to host and inspect.

Tailwind CSS (Runtime)

Styling is handled by the standalone tailwindcss.js script. This parses classes in the DOM at runtime, enabling rapid utility-first styling without a CSS preprocessor pipeline.

PWA & Service Workers

A custom sw.js ensures total offline capability. It caches the HTML engine and all local assets immediately upon load, allowing the app to function indefinitely without an internet connection.

Local Persistence

State management is tightly coupled with localStorage. Every clock tick, score change, and setting adjustment is serialized and saved, ensuring the match state survives hard refreshes or browser crashes.

Key Challenges & Solutions

1. The "Z-Index" Touch Target Bug

Challenge: On mid-sized screens (like laptops), the "Play/Pause" and "Game Status" buttons became partially unresponsive. The top or bottom halves of the buttons would not register clicks.

Solution: The massive player timers (scaled via viewport units) were actually rendering invisible bounding boxes that overlapped the control bar. We applied overflow-hidden to the player panels and forced the control bar to the top of the stack using relative z-20.

2. Extreme Typography Scaling

Challenge: The clock needs to look perfect on a generic narrow iPhone SE and a widescreen 27-inch 4K monitor. Standard media queries were insufficient for the massive font sizes required for readability.

Solution: We implemented CSS clamp() functions combining rem, vmin, and max limits. This ensures the text fluidly resizes based on the smallest viewport dimension while respecting minimum legibility standards.

3. Total Offline Independence

Challenge: Relying on Google Fonts or external CDNs breaks the app if a user tries to play without internet, or in a location with poor reception (e.g., a basement or airplane).

Solution: 1. Embedded Fonts: Fonts are converted to Base64 strings and injected directly into the PWA.
2. Inline SVGs: Instead of an icon library, all UI icons (Settings, Play, History) are drawn as inline SVG paths within the React code.

4. Browser Hardware Constraints

Challenge: Phones automatically go to sleep to save battery, but with a backgammon clock, the screen must stay on! Also, browsers block audio that plays automatically.

Solution: We integrated the Screen Wake Lock API to keep the device awake while the clock runs. For audio, the AudioContext is initialized strictly upon the first user interaction (clicking "Play"), ensuring beeps play reliably thereafter.

5. Data Portability

Challenge: Users requested the ability to transfer their custom match presets and settings (fonts, colors) between devices, or simply backup their configuration.

Solution: We implemented a purely client-side backup system. For Export, we use the Blob and URL.createObjectURL APIs to generate a downloadable JSON file on the fly. For Import, a FileReader parses the uploaded file and immediately injects the data into the app's React state and LocalStorage.