Split-flap
Text that lands like a railway departures sign.
Documentation
-
Copy to Webflow
Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: Webflow removes any font your site has not installed. -
Add custom CSS
Custom CSS in WebflowThis CSS already ships inside the Webflow embed, so a pasted section needs nothing added. Paste it yourself, in the page or the site custom code, only if the effect runs on several pages. The tutorial explains where.
See the tutorialThe tiles are built by the script beside the text, and their size and colours are settings, written on the element: nothing to style by hand. Until the script runs, and on the Designer canvas, the text shows as written. Leave the element and its close parents withoutoverflow: hidden: the flaps swing out of their tiles as they fall. -
Add custom JS
Custom JavaScript in WebflowThis script already ships inside the Webflow embed, settings block included. You paste it yourself, before the closing body tag of the page or the site custom code, only when the effect has to run on several pages, and the tutorial says how to choose.
See the tutorial -
Use it on your own elements
How to add a custom attribute in WebflowA custom attribute is a pair, a name and a value, typed in the Settings panel of the element you selected, under Custom attributes. The name is what the script looks for, the value is what it reads. The tutorial shows the panel.
See the tutorialCustom attributesSelect the element on the canvas and open the Settings panel. Under Custom attributes, add a pair: the name on the left, the value on the right.Custom attribute What it does data-split-flap Marks a heading or a text block whose text lands as a split-flap sign: every character becomes a tile that turns through its flaps to the right one as the element comes into view. It takes no value: leave the value field empty. On a block holding several texts marked data-split-flap-field, it runs them all on one clock. data-split-flap-values A list of values, separated by |, on the marked element or on a field: 04471|04472|04480. The element shows each in turn, every interval seconds, and only the characters that change turn. Its own text is the first value. Without it, the text plays once, as it enters the window. [ Optional ] data-split-flap-field On a text inside a marked block: each field is a row of tiles of its own, and all the fields of the block turn together and move on together. Without any, the marked element itself is the one row. [ Optional ] data-split-flap-state Written by the engine on the marked element: ready while blank tiles wait, playing, done at rest, static under reduced motion. Never written by hand. [ Optional ] SettingsEvery setting is a custom attribute too, added the same way and on the same element. An attribute always wins over the defaults written in the code.Attribute Default What it is data-split-flap-tile #15100c Colour of a tile's face, any CSS colour. Options: any CSS colour, ink by default. data-split-flap-ink #f4ece0 Colour of the glyphs on the tiles, any CSS colour. Options: any CSS colour, cream by default. data-split-flap-hinge #f4ece0 Colour of the cut across the middle of each tile, usually the colour behind the tiles. Options: any CSS colour, cream by default. data-split-flap-flip 0.075 Time one flap takes to fall, in seconds; a character turns through several. data-split-flap-flips 9 Most flaps a character turns through before it lands. A character close behind its target on the drum turns fewer. data-split-flap-stagger 0.035 Delay between the start of one character and the next, left to right, in seconds. data-split-flap-chars ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:.-/>#& The drum: the characters a tile turns through, in order. A blank always comes first; lowercase is read as uppercase. A character of the text that is not on the drum still lands, after a run of drum flaps. Options: any string, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for a plain sign. data-split-flap-hover false Whether the pointer turns it again. A single text turns through its flaps back to itself; a list moves on to its next value at once. Options: false (it plays as it enters the window, then only its list moves), true. data-split-flap-interval 4 Time a value of a list stays up before the next one turns in, in seconds, counted once the last flap has landed. 0 stops the list. data-split-flap-offset 0.12 How far over the bottom edge of the window the element has to come before it plays, as a share of the window height, 0 to 0.9. data-split-flap-replay false Whether it plays again each time it comes back into view. Options: false (once), true (blank tiles again once it has fully left the window). data-split-flap-room 0.2 Width a tile adds to its glyph, in em of the text. data-split-flap-height 1.22 Height of a tile, in em of the text. data-split-flap-gap 0.06 Space between two tiles, in em of the text. data-split-flap-radius 0.06 Rounding of a tile's corners, in em of the text.
Two files, no build step.
It runs in the browser only. Load it with a script tag in plain HTML, in a
client-side script in Astro, never in the frontmatter. In React, call
SplitFlap.mount(ref.current) in useEffect and
destroy() in its cleanup.
-
Tune it, either way
Per element with an attribute, or once for the whole site by editingDEFAULTSat the top of the file.Attribute Default What it is Lower Higher data-split-flap-tile #15100c Colour of a tile's face, any CSS colour. Options: any CSS colour, ink by default. data-split-flap-ink #f4ece0 Colour of the glyphs on the tiles, any CSS colour. Options: any CSS colour, cream by default. data-split-flap-hinge #f4ece0 Colour of the cut across the middle of each tile, usually the colour behind the tiles. Options: any CSS colour, cream by default. data-split-flap-flip 0.075 Time one flap takes to fall, in seconds; a character turns through several. A fast, buzzing sign A slow, heavy mechanism, each flap seen data-split-flap-flips 9 Most flaps a character turns through before it lands. A character close behind its target on the drum turns fewer. Every character lands almost at once, a small flutter A long spin; the row takes longer to settle data-split-flap-stagger 0.035 Delay between the start of one character and the next, left to right, in seconds. The whole row starts together A wave that runs along the text data-split-flap-chars ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:.-/>#& The drum: the characters a tile turns through, in order. A blank always comes first; lowercase is read as uppercase. A character of the text that is not on the drum still lands, after a run of drum flaps. Options: any string, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' for a plain sign. data-split-flap-hover false Whether the pointer turns it again. A single text turns through its flaps back to itself; a list moves on to its next value at once. Options: false (it plays as it enters the window, then only its list moves), true. data-split-flap-interval 4 Time a value of a list stays up before the next one turns in, in seconds, counted once the last flap has landed. 0 stops the list. The list turns over and over Each value holds long enough to read twice data-split-flap-offset 0.12 How far over the bottom edge of the window the element has to come before it plays, as a share of the window height, 0 to 0.9. It plays as soon as it enters at the bottom It waits until it has climbed higher up the screen data-split-flap-replay false Whether it plays again each time it comes back into view. Options: false (once), true (blank tiles again once it has fully left the window). data-split-flap-room 0.2 Width a tile adds to its glyph, in em of the text. Tiles tight around the letters, a dense sign Wide tiles, letters floating in them; small text wants more data-split-flap-height 1.22 Height of a tile, in em of the text. Tiles that clip close over the capitals Tall tiles with room above and below the glyph data-split-flap-gap 0.06 Space between two tiles, in em of the text. Tiles touching, one strip Tiles standing apart data-split-flap-radius 0.06 Rounding of a tile's corners, in em of the text. Square tiles Soft, rounded tiles -
Drive it from JavaScript
When an element arrives after the page has loaded, when it has to play again from code, or when a list has to move on at a moment the page chooses.const element = document.querySelector('[data-split-flap]'); element.addEventListener('split-flap:done', (e) => { console.log(e.detail); }); const sign = SplitFlap.mount(element); // the element auto() mounted, or a new one sign.replay(); // blank tiles, then played again sign.next(); // every list of the element moves on now // Elements added after the page loaded: mount everything marked inside it. SplitFlap.auto(container); // When the element leaves the page: sign.destroy(); // the text goes back exactly as it was
-
Fit it to your page
- One attribute, on the element itself. Put
data-split-flapon the heading, paragraph or span whose text should land as a sign; it takes no value. The script builds the tiles from the element's own text and plays them as it enters the window. In Webflow, select the Heading or the Text Block, open the Settings panel and add the custom attributedata-split-flapwith an empty value. - A list of values:
data-split-flap-values="04471|04472|04480"on the same element. Its own text is the first value when it is not in the list. The element shows each value in turn, everyintervalseconds, and only the characters that change turn. A list is one row, as wide as its longest value, so it never wraps and never changes width. - Several lists on one clock:
data-split-flapon the group, anddata-split-flap-fieldon each piece of text inside it, like the four facts of the preview. The fields turn together, one after the other, and move on together. Settings go on the group. - Settings on one element:
data-split-flap-<key>on it,data-split-flap-hover="true"ordata-split-flap-room="0.44". For every element of the page, without opening the file:window.SplitFlapSettings = { flip: 0.09 }in a script placed beforesplit-flap.js. The three levels, least specific first, areDEFAULTS, then that object, then the attribute. - The size of the tiles follows the text.
room,height,gapandradiusare in em, so a tile grows with the font size. Small text reads better with more room around each glyph: the facts of the preview carryroom0.44,height1.6,gap0.12 andradius0.12. - A monospace face is best, Geist Mono in the preview. Any face works, since every tile has the same width,
1chof the text plusroom, but a wide letter in a proportional face can touch the edge of its tile. - Uppercase reads as a sign. The drum is uppercase; a lowercase letter of the text lands as itself after uppercase flaps. The preview's classes set
text-transform: uppercase. - In the Webflow Designer the text is plain. The canvas runs no Embed, so a marked heading shows its words as written, editable; the tiles only exist on the published page and in the preview.
- Screen readers read the text once: it stays in the element, visually hidden, and the tiles are
aria-hidden. A list reads its current value. - **
prefers-reduced-motion**: the text is there from the start, on still tiles. A list still moves on, without a flap turning. - An element already above the window when the page opens (a reload half way down) is shown at rest, not played behind the reader's back. One not laid out at all, in a closed tab or menu, waits until it is shown.
- States, on the element, for a style or a test:
data-split-flap-stateisready(blank tiles, waiting),playing,doneorstatic(reduced motion). Each change fires a bubbling event of the same name:split-flap:ready,split-flap:playing,split-flap:done,split-flap:static, whosedetailis{ element }. - From JavaScript:
SplitFlap.mount(el, overrides)returns the instance, which carrieselement,settings(the values it resolved),state(),play(),show()(the text at once, no flap turned),reset()(blank tiles, lists back to their first value),replay(),next()(the next value of every list, now) anddestroy().SplitFlap.auto(root)mounts every marked element insideroot,SplitFlap.replay(root)plays them again,SplitFlap.destroy(root)takes them all back.destroy()puts the original nodes back and thestyleattribute as it was, so the markup is the one of snippet.html and mounting again starts clean. In React,mountinuseEffectanddestroyin its cleanup. - The words of the markup are the preview's, and they ship as they are: pasted, the page reads the way the preview did. Put the user's own text in their place whenever they want, in the page's language.
- On Webflow, add the fonts first. Add Geist and Geist Mono to your site's fonts (Site settings, Fonts, Google Fonts) before pasting: the paste names them, and the Designer drops a font the site does not have.
- One attribute, on the element itself. Put
-
Avoid the pitfalls
- The text shows as written until the script runs, then turns into blank tiles and plays. With the Embed at the end of a long page, a heading high up can show its plain words for a moment first. The element then takes the height of its tiles, a little more than its lines of text, and what follows moves down by that much. Give the element a
min-heightwhen that matters. - Text changed in a mounted element is not read again. Change the words, then
destroy()andmount()it again, or give it a list. - **Settings handed to
mount()after the page loaded are dropped.**auto()mounts on DOM ready, andmount()on an element that already carries the effect returns the existing one untouched. A page-wide block goes inwindow.SplitFlapSettings, or the element is destroyed and mounted again. - Inline markup is flattened in the tiles. A
strong, anemor a link inside a marked heading stays in the hidden text, whole, for assistive technology, but the tiles show plain characters, and a link inside is not clickable while they show. Keep links out of a marked element. - **
overflow: hiddenon the element or a parent close around it** clips the flaps, which swing out of their tile in perspective as they fall.
- The text shows as written until the script runs, then turns into blank tiles and plays. With the Embed at the end of a long page, a heading high up can show its plain words for a moment first. The element then takes the height of its tiles, a little more than its lines of text, and what follows moves down by that much. Give the element a
One click on Copy the AI prompt copies everything an assistant needs to build this effect: the complete code, the markup, the dependency, every setting and every pitfall.
What it does
Split-flap is a text animation for Webflow and vanilla JavaScript, with no library. Also called split flap text animation, split flap effect or departure board text effect.
Text that lands like a railway departures sign. As a heading enters the window, every character becomes a tile cut at mid height, and the top half falls over the bottom one, in perspective, until the right character is up. Nothing is scrambled: each tile turns one way through an ordered drum, blank, A to Z, 0 to 9, a few signs, so a letter close behind its target lands first and the row settles in the order a real sign would. The text is the element's own, a Webflow Heading as it is, read once by a screen reader, and the tiles have a fixed width, so the line never moves while they turn. A list of values turns on its own clock, only the characters that change, and the pointer can turn it again. Under reduced motion the text is simply there. Pattern type split-flap display, Solari display, flap text.
Updated
Questions
Paste the component, select your Heading or Text Block, open the Settings panel and add the custom attribute data-split-flap with an empty value. The Designer canvas keeps showing the plain, editable words: the tiles only exist on the published page and in the preview.
No. The split-flap engine has no dependency and no build step. It builds the tiles beside the element's own text and turns them on one shared requestAnimationFrame loop that stops on the frame the last flap lands.
Set data-split-flap-flip, the time one flap takes to fall, 0.075 seconds by default: lower gives a fast, buzzing sign, higher a slow mechanism where each flap is seen. data-split-flap-flips (9 by default) caps how many flaps a character turns through, and data-split-flap-stagger (0.035 by default) is the delay between one character and the next.
Yes. Add data-split-flap-values="04471|04472|04480" to the element and it shows each value in turn, every interval seconds (4 by default), turning only the characters that change. A list is one row as wide as its longest value, so it never wraps and never changes width.
Yes. The original text stays in the element, visually hidden, and is read once, while the tiles are aria-hidden. Under prefers-reduced-motion the text is there from the start on still tiles, and a list of values still moves on without a flap turning.