1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
#+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).
|