#+TITLE: HotFX split-flap web component — technique reference #+DATE: [2026-07-18 Sat] Reference for the split-flap display work (gallery card N20, and any flip/drum date-time module the waybar chronograph grows). Craig supplied both links. - Annotated walkthrough: https://fx.hot.page/split-flap/source - Source on GitHub: https://github.com/hot-page/fx/blob/main/hotfx-split-flap/hotfx-split-flap.js - Local study copy: [[file:2026-07-18-hotfx-split-flap-source.js]] LICENSE CAUTION: the fx repo declares NO license (checked via the GitHub API 2026-07-18), so the code is all-rights-reserved by default. Study the technique, re-derive our own implementation in the GW namespace — do not copy code or CSS verbatim into the gallery or dotfiles. * The mechanics worth stealing (as ideas) ** Four-layer character cell Each character is four absolutely-stacked half-panels: =.top= / =.bottom= (current char) and =.top-next= / =.bottom-next= (incoming char), the tops clipped to the upper half and bottoms to the lower half, with a divider line at 50%. =transform-style: preserve-3d= on the cell, =backface-visibility: hidden= on flaps so a flap's reverse never shows. ** One flip = two synchronized quarter-animations Only two elements move per flip, via the Web Animations API with =Promise.all= on their =.finished=: - current =.top= rotates =rotateX(0deg)= → =rotateX(-180deg)= (falls forward) - =.bottom-next= rotates =rotateX(180deg)= → =rotateX(0deg)= (lands) Both =ease-in=, default 150ms per flap. After landing, the static halves get the new character's text and the cycle repeats. This is the minimal honest split-flap kinematic. ** The charset IS the drum It never jumps to the target character: it steps =currentIndex + 1= (wrapping) through the declared alphabet until it arrives, one flip per step — the Solari constraint that a board can only advance one flap at a time. The alphabet is a declared attribute (default =' ABC…Z0-9!.,:?"'/$'=), so the character order is data, exactly our R58 LAYOUT-as-data pattern: rewrite the alphabet and the physical behavior follows. ** Live-update semantics (relevant for a clock) A MutationObserver rebuilds the target grid on text change; an in-flight flip cascade aborts itself by identity-checking the target grid each step, and the new animation starts only after awaiting every running animation's finish. That's the answer to "the minute changed while the drum was still spinning" — abort-and-retarget, never queue. ** Also noted IntersectionObserver defers the animation until the element is half in view; =::part(flap-N-M)= attributes expose every flap and divider for external styling. ~300 lines total for all of it. * How it maps to our targets - Gallery N20 (split-flap board): the four-layer cell + two-animation flip is the web implementation; the step-through-charset rule and abort-on-retarget are behaviors our probe checks can pin regardless of implementation. - Waybar chronograph drums (the redesign mock's date/time): our drums are odometer rollers, not flaps — different mechanism — but the charset-as-drum ordering and abort-and-retarget rules carry over unchanged. - Emacs svg.el port: no Web Animations API; the flip becomes frame-stepped whole-image regeneration per the render contract, with the same one-step-at-a-time rule. Reduced-motion gate applies (widget-owned animation per the gallery tick contract).