COMPONENT
Split-flap
A word that changes into another, one letter at a time, the way an airport departure board does it. For the same word wearing different faces — not for two different claims.
Watch it turn
The element's own text is the first word. `data-words` is the rest of the cycle, pipe-separated. Everything visible is a CSS animation on a two-row track; the script splits the word, shuffles the order and keeps the schedule.
Hi, I'm Greg
<div class="prose">
<h2>Hi, I'm <span class="splitflap" x-ds-splitflap data-words="gReg|GreG|greG" data-hold="2000" data-loops="forever">Greg</span></h2>
</div>How long, and how many times round
`data-hold` is milliseconds per word and defaults to the theme's `--ds-flap-hold`. `data-loops` is a count, or `forever`.
<div class="rule-stack" style="width:100%">
<div class="rule-row rule-row-spec">
<span class="rule-row-key">THREE TIMES ROUND</span>
<span class="rule-row-value">
<span class="splitflap" x-ds-splitflap data-words="LATE|BOARDING|ON TIME" data-hold="1600" data-loops="3">DELAYED</span>
</span>
</div>
<div class="rule-row rule-row-spec">
<span class="rule-row-key">SLOWER, AND ENDLESS</span>
<span class="rule-row-value">
<span class="splitflap" x-ds-splitflap data-words="KRAKOW|GDANSK" data-hold="3200" data-loops="forever">WARSAW</span>
</span>
</div>
</div>Reference
Read from components/splitflap.css when this page was built, so it cannot fall behind the stylesheet.
.splitflap.splitflap-cell.splitflap-track.splitflap-face.splitflap-name--flap-duration — falls back to --ds-flap-duration--flap-ease — falls back to --ds-flap-easeNotes
Each .splitflap-cell carries aria-hidden, and the directive leaves a .splitflap-name beside them holding the original word — visually hidden, and the only thing a screen reader is given. So the page announces "Greg" whether or not it is mid-flip, rather than a soup of single letters changing four times a second.
aria-hidden was not enough on its own, and the front page proved it. It governs the accessibility tree and says nothing about textContent — and a turning board holds two faces per cell, so the heading read Hi, I'm GregGGrreegg to anything walking the DOM. A crawler is exactly that. So a .splitflap-face keeps its letter in data-ch and the stylesheet paints it: generated content is not text, and the document now reads "Hi, I'm Greg" whichever tree you walk.
What follows is the whole contract: the words must be the same word wearing different faces. `Greg` to `gReg` is this component. `Designer` to `Engineer` is two different claims, and only the first one is ever announced — so that is a job for two elements and a sentence, not for a board.
WCAG 2.2.2 is Level A: motion that starts by itself and runs past five seconds needs a mechanism to pause, stop or hide it. So the default here is a count rather than forever, and a page that asks for forever owes that control.
The one this library already has works unchanged: an ancestor carrying data-ds-motion="paused" stops a board exactly as it stops a wall of clips. Reduced motion stops it dead — not just the animation but the schedule, because a board that went on swapping words without motion is still changing text under somebody who asked for things to hold still.
Shuffled once, a four-letter word turns in the same order every cycle and the eye learns it in two passes — which is the moment it stops looking like a mechanism and starts looking like an animation. A fresh shuffle each time costs nothing and never settles into a pattern.
A letter that is not changing does not turn at all. On Greg to gReg that is two of the four staying put, which is what makes the change read as a correction rather than as a whole new word.
The markup you write is a span.splitflap with a word in it. Everything under it is built at run time: one .splitflap-cell per letter, each holding a .splitflap-track of exactly two rows — what is showing, and what is coming. Turning the pair by half its own height moves it precisely one line, in any face and at any size.
The pair is rebuilt after every turn rather than grown into a stack. Two rows means that -50% is exactly one row forever; a stack would make the transform a different fraction each time and the arithmetic would drift.
A board whose frame changes width mid-turn is not a board, it is a layout shift — and the cells are laid out inline, so a shorter word would pull the line in and push it back out a second later. The padding is a cell like any other and turns like one.