Docs Block Reference Kinetic Block

Kinetic Block

Animated "kinetic poster" typography. Turns 1-4 short lines of text into a dramatic, motion-design title sequence — letters and lines animate in, hold, and animate out on a shared timeline.

Type: kinetic


When to use

USE for: hero / opening titles, taglines, manifesto statements (1-4 declarative lines), section dividers, short pull-quotes, animated logotypes.

DO NOT use for: body paragraphs, lists, long sentences, prices/data, anything over 4 lines or ~4 words per line. There is no auto-chunker — the block animates exactly the lines you give it.

Strip terminal punctuation. Prefer ALL CAPS or Title Case for heroes.


Two authoring modes

A kinetic block has a Mode toggle:

  • Composition (default) — pick a numbered preset from the catalog and type your lines into one textarea. The preset decides each line's motion/timing. Three composable layers (Hierarchy, Reveal, Accent) refine the look. Fastest path.
  • Per-Line — author each line independently: per line you set text, motion, size (px), font, weight. Maximum control. Use when a preset is close but a specific line needs a different motion/size/font.
  • Most generation should use Composition mode.

    The per-line size, weight and colour controls (line_size_0..3, line_weight_0..3, disc_color_0..3) belong to COMPOSITION mode and have nothing to do with which mode is selected. Having per-line colour and weight does not make a block "per-line mode". The modes differ only in how the TEXT is authored: Composition takes the whole title in one field, Per-Line takes each line as its own row with its own motion. Do not infer the mode from the presence of per-line styling keys. Reach for Per-Line when the design calls for mixed fonts/sizes per line.


    Settings reference (keys for generation)

    > Audited against the engine, both renderers and the panel on 2026-08-24. Before > that this table documented a reveal layer that has never existed in the code, > a removed grad_speed, and four defaults that were the panel's slider > positions rather than what an omitted key actually produces. If you are about > to rely on a row here that matters, the engine is the authority.

    | key | type | default | applies | notes | |---|---|---|---|---| | composition | enum (see catalog) | block-slam | Composition mode | the preset key | | tier | hero / label / equal | hero | scene comps | size hierarchy (see Hierarchy) | | accent | none/slash/backslash/brackets/frame/numeral | none | scene comps | poster overlay. NO PANEL CONTROL as of 2026-08-24: the engine renders it and the renderers emit it, but nothing in the panel carries the value, so a generated accent is dropped the first time the owner saves anything in that block. Safe to read, fragile to write | | accent_text | string | "" | accent=numeral | the big background number, e.g. 2024 | | custom_mode | 1/0 | 0 | — | 1 = Per-Line mode | | custom_lines | JSON array | [] | Per-Line | per-line entries (see schema) | | color | hex | #ffffff | all | the block's base text colour, and the FALLBACK for any line with no colour of its own. This is NOT a one-colour limit: disc_color_0..3 sets a colour per LINE in Composition mode | | loop | 1/0 | 0 | all | replay continuously. A block that omits this does NOT loop | | speed | 0.25–3 | 1 | all | timing multiplier | | intensity | 0.5–2 | 1 | all | motion-amplitude multiplier | | height_mode | auto/fixed/vh/fit | auto | scene comps only | a TEXT composition hugs its type and the control is hidden: the space around it belongs to the section, which has its own height and padding. Scene comps paint an absolute stage and add no flow height, so they keep it | | height | px | 400 | height_mode=fixed | acts as a FLOOR, not a cage: the block grows past it rather than cropping | | height_vh | 10-100 | 100 | height_mode=vh | viewport height, so 100 really is a full screen | | as_background | 1/0 | 0 | all | fill the whole SECTION behind the other content, taking no flow space. Height is ignored | | lead_screen | 1/0 | 1 (ON) | all | hold the rest of the section's content hidden until this headline settles, then fade it in. Default ON, so a generated headline leads unless told otherwise. Released after at most 2s whatever the entrance is doing, and skipped entirely when the reader arrives from BELOW (scrolling back up), where holding content already on screen would be three events nobody asked for | | cycle | 1/0 | 0 | Per-Line | show one line at a time, rotating through them on a loop | | scroll_exit | off/spring/fade | off | all | how the text leaves when its section scrolls away. off keeps it with the section | | tag | h1-h6/div | h2 | all | the real heading level. A page carries exactly ONE h1 and it belongs to the hero headline | | cursor_magnetic | 1/0 | 0 | letter path | letters lean toward the pointer | | cursor_spotlight | 1/0 | 0 | letter path | a light follows the pointer across the type | | cursor_gradient | 1/0 | 0 | letter path, gradient only | the gradient tracks the pointer. Inert unless some line actually paints a gradient | | cursor_tilt | 1/0 | 0 | letter path, not Discs/Cube | the whole block tips toward the pointer | | disc_buttons | 1/0 | 0 | Discs | render the circles as real buttons | | comp_font | px, 0-200 | 0 | Composition | type size in PIXELS. 0 = auto-fit. Replaces the old 1-10 comp_size, which multiplied a viewport-derived base and so meant a different size on every screen. comp_size / comp_scale are still honoured on blocks that carry them | | kinetic_font | font key | system | Composition | family. Per-Line carries a font per line, so this is hidden there | | kinetic_weight | blank / 400-900 | blank | Composition | weight. Blank keeps each composition's own default | | align | left/center/right | center | all | honoured by every composition. On Discs it moves the whole cluster, since the circles sit on a spread rather than being aligned as text | | line_c2_0..3 | hex | "" | all | the second colour of line N, and the whole gradient model now. One colour on a line = solid, two = that line is a gradient from its own colour to this one. Lines are independent, so a headline can mix gradient and solid lines - which fill could never do, being a block-level mode | | fill | solid/gradient | solid | all | LEGACY block-level gradient, kept so blocks saved before per-line pairs keep painting exactly as they did. Its panel control is gone; new work sets line_c2_* instead. Resolution order is a line's own pair first, then this | | grad_scope | line/block | line | gradient | whether the gradient runs across each line or the whole block | | grad_c1 / grad_c2 | hex | - | fill=gradient | the two stops | | grad_mode | travel/spin/wipe | travel | fill=gradient | how the gradient moves. wipe is the old Wiper composition, which is a gradient MODE now | | line_gap | -50 to 100 | 0 | all | space between lines, and between the rows of a line that wraps | | max_width | 0-100 | 0 | Per-Line / line-stack | % of the block a line may fill. 0 means wrapping is OFF | | max_width_tablet | 0-100 | inherit | as above | per-device override | | max_width_mobile | 0-100 | inherit | as above | per-device override | | line_size_0..3 | px, 0-400 | 0 | Composition | size of that line. 0 = the composition's automatic size, and it IS the default: 60/40/30/24 are the panel's slider positions, not what an omitted key produces. A sized line WRAPS rather than shrinking: the px asked for is the px given | | line_weight_0..3 | 0-900 | 0 | Composition | weight of that line. 0 = the block weight, and it IS the default (900/600/300 are panel slider positions). Per-Line carries its own weight per row instead | | kperf | enum (see Performance) | "" | Composition | the ARRIVAL pattern. Per device; none turns it off for a breakpoint | | ksteady | enum (see Performance) | "" | Composition | the STEADY state, what the lines keep doing once arrived. Per device | | kperf_hero | 0-200 | 100 | kperf=handoff/highlight | how far the arrival pushes, as a SHARE of that pattern's own lift. Meaningless on stack/converge, which have no hero | | kpar | 0-100 | 0 | Composition | per-line pointer parallax: the lines lean by different amounts, so the stack reads as having depth. Off on touch and under reduced-motion | | disc_font | px, 20-400 | 0 (auto) | Discs | text size. The CIRCLE is built from the word at this size, not the other way round | | disc_ink | hex | "" | Discs | label colour. Blank picks black or white per circle for contrast | | disc_color_0..3 | hex/rgba | "" | Composition (all comps) | colour of line N. The disc_ prefix is historical: the engine resolves it via _lineInk(i), which every standard composition calls, so this is the per-line colour control in Composition mode generally, not a Discs-only setting. On Discs it is the ink of circle N. Blank falls back to color | | disc_link_0..3 | URL | "" | Discs / most comps | a URL makes that LINE (or circle) a real anchor. Blank leaves it plain text | | cube_style | tiles/faces | tiles | Cube | tiles = a letter per sticker, faces = words across the faces | | cube_mono | 1/0 | 0 | Cube | one derived tone instead of the Rubik six | | ransom_gran | letter/word/line | letter | Ransom | what gets its own torn scrap. letter is the cut-up poster, line is a pasted strip | | paper | hex | #f2ede1 | Ransom | the scrap colour. color is the INK. When the ink would vanish into the paper it falls to near-black or near-white on its own, so a block left at the default white color stays readable on cream |

    Background is transparent — the containing section/cell shows through. For ambient color, use the section's gradient/mesh background, NOT the text.

    With as_background: 1 the block instead fills its whole section, sits behind the other content and takes no flow space — the way to put a composition behind a headline without pushing it down. Height settings do not apply in that mode.


    Per-composition options

    | key | type | default | applies | notes | |---|---|---|---|---| | disc_font | px, 20-400 | 0 (auto) | discs | type size, and therefore circle size: each circle is built to hold its own word | | disc_color_0..3 | hex/rgba | blank | Composition (all comps) | colour of line N; on Discs, the ink of circle N. Blank falls back to color. Not Discs-only, see the main settings table | | disc_ink | hex/rgba | blank | discs | label colour. Blank = automatic black or white for contrast against each circle | | disc_clip | 1/0 | 1 | discs | 1 cuts the circles at the block edge; 0 lets them bleed into the section | | disc_glow | 1/0 | 0 | discs | soft bloom in each circle's own colour | | disc_sub_0..3 | string | blank | discs | a small second line inside a circle, dropped automatically when the circle is too small for both | | cube_style | faces/tiles | tiles | cube | a word per face, or a letter per sticker | | cube_mono | 1/0 | 0 | cube | replaces the six real cube colours with one derived tone | | cube_size | 30-90 | 55 | cube | cube edge, % of the stage | | cyl_diameter | % | 70 | cylinder | drum diameter | | echo_count | 2-16 | 5 | echo | trailing layers | | tide_bubbles | 1/0 | 0 | tide | bubbles rise inside the wave and vanish at the surface |

    Lines as buttons

    | key | type | default | applies | notes | |---|---|---|---|---| | disc_link_0..3 | URL | blank | see below | line N (or circle N on Discs) becomes a real anchor. A filled URL makes it a button and a blank one leaves it alone: there is no separate on/off switch |

    Supported on: Discs, every standard composition, plus Tide, Water, Glitch Loop, Bar Sweep and Echo. NOT available on Cylinder, Puzzle Cube, Extruded, Camera Arc, Type Pulse or Helix, where letters are transformed away from their layout box so a link's clickable area would not match what the reader sees. Do not emit link keys for those.

    The panel hides any control a composition does not read, so a setting that is absent from the UI for a given composition genuinely does nothing there.


    Long headlines go tiny on phones unless you set max_width

    A kinetic line that does not fit on one row is NOT wrapped by default: the font shrinks until it fits. The longer the headline, the smaller the type, and a phone is where it bites. Measured on live pages 2026-08-11: trade-page H1s rendered at 17px on a 375px screen, one marketing hero at 12px, both while looking perfect on a desktop.

    The setting is max_width (percent of the block a line may fill):

    • 0 (the default) means wrapping is off: shrink to fit one row.
    • 100 caps the line at the block width and **wraps at word
    • boundaries**, keeping the intended size. The same headline that rendered at 17px rendered at 54px across two rows.

    Set max_width: 100 on any headline longer than about 20 characters. On desktop it is a no-op when the line already fits, so it is safe everywhere. If a specific breakpoint needs different behaviour, use max_width_tablet / max_width_mobile.

    Composable layers (Composition mode)

    These stack on top of any composition. NOTE that there are TWO of them, not three: a reveal layer was documented here for a long time and never existed in the code (no control, no attribute, nothing in the engine), so anything generated with it was silently dropped. Do not emit reveal.

    Hierarchy (tier) — size relationship between lines

    • hero (default): line 1 is the big hero, the rest are small captions.
    • label: line 1 is a small label, line 2 is the hero, the rest are captions.
    • equal: every line the same size.

    Accent (accent) — poster decoration overlay

    • slash // / backslash \\: a mark at the top-left.
    • brackets [ ]: rules flanking the block.
    • frame: a border inset from the block edges.
    • numeral: a big faint number behind the text — set accent_text (e.g. 2024, 24/7).

    Performance layers

    <!-- BEGIN GENERATED PERFORMANCE --> Two layers, and they are independent: a block can do either, both or neither. The ARRIVAL choreographs the lines against each other on the way in and then ends. The STEADY state is what they keep doing for as long as the page is open. Both run purely on transforms, so neither can change the block's height or move anything else on the page.

    Set kperf for the arrival and ksteady for the steady state. Empty means none. Both are per device (_tablet / _mobile), and none is how a breakpoint switches one OFF.

    Arrival patterns (kperf)

    • Handoff (handoff) - each line is the hero of its own beat, then yields it _(relay, 1400ms per beat, hero 1.55x)_
    • Stack (stack) - the same arrival rhythm with no size change: pure making-room _(relay, 900ms per beat)_
    • Highlight (highlight) - all land together, then one line is lifted at a time while the rest hold back _(walk, 1200ms per beat, hero 1.30x)_
    • Converge (converge) - the lines open spread apart and close into place: one move, no hero _(gather, 1600ms per beat)_

    Steady states (ksteady)

    • Drift (drift) - each line wanders its own small ellipse on its own clock, so the group never repeats a formation _(9000ms)_
    • Wave (wave) - a sine travelling down the stack, each line a third of a cycle behind the one above _(5200ms)_

    A relay pattern retimes each line to arrive ON its beat, so the composition's own entrance offsets are overridden while one is set.

    On a narrow viewport (<768px) the whole arrival runs at 70%, and a hero beat inverts: instead of the arriving line growing, the settled lines recede. The fit already spent the width budget getting the type to fit at all, so there is nothing left to grow into.

    Both are suppressed under prefers-reduced-motion, and neither runs with loop or cycle, which already own the timeline. <!-- END GENERATED PERFORMANCE -->


    Composition catalog

    <!-- BEGIN GENERATED CATALOG --> 34 compositions, generated from the engine catalog. If a key is not listed here it does not exist.

    Compositions marked scene paint their own absolute stage: they honour Hierarchy / Reveal / Accent and they are the only ones that carry a block Height. Everything else hugs its type, and the space around it belongs to the section. custom path means its own renderer without an absolute stage, so the per-letter cursor effects do not apply.

    Scene

    • Cylinder (cylinder) - Lines wrap a horizontal drum that rolls around the X axis. _(scene, 1-4 lines)_
    • Extruded (brutal) - Bold extruded letters slide in horizontally with a hard depth shadow. _(scene, 1-4 lines)_
    • Puzzle Cube (cube) - Text rides a tumbling puzzle cube; layer twists slice it apart and reassemble it. _(scene, 1-6 lines)_
    • Glitch Loop (glitchloop) - Bold line, calm between short slice-and-split glitch bursts. _(scene, 1-2 lines)_
    • Echo Zoom (echo) - A word breathes with fading depth echoes; lines take turns. _(scene, 1 line)_
    • Helix (helix) - The text winds a slowly rotating spiral ribbon. _(scene, 1-2 lines)_
    • Water (curtain) - Tall stretched letters ripple like hanging fabric. _(scene, 1-2 lines)_
    • Type Pulse (typepulse) - A packed text block where letters swell and the words repack. _(scene, 1-6 lines)_
    • Tide (tide) - A wavy divider sweeps across and inverts the text it passes. _(scene, 1-4 lines)_
    • Discs (discs) - Words ride overlapping circles that drift in slow orbits. _(scene, 1-4 lines)_
    • Camera Arc (arc) - Letters blast in from the camera, assemble, then blast back out. _(scene, 1-4 lines)_

    Cascade and build

    • Block Slam (block-slam) - Hero rises through a tight mask, subtitle bounce-slides from alternating sides. Both fade out. _(1-2 lines)_
    • Triple Cascade (triple-cascade) - Hero zooms with bounce, sub bounce-slides from sides, label rises. Distinct exits. _(1-3 lines)_
    • Wave Build (wave-build) - Each line waves in via a different motion: rise → drop → zoom → blur, all sine-staggered. _(1-4 lines)_
    • Spotlight (spotlight) - Hero blurs in dramatically. Subtitle letter-spaces in below. Both fade out. _(1-2 lines)_
    • Up Cascade (reverse-cascade) - Bottom label glides in, sub bounce-slides, hero rises. Hero exits first via drop. _(1-3 lines)_
    • Slide Stack (slide-stack) - Lines build from alternating sides with bounce and per-letter kick, staggered into a layered stack. _(1-4 lines)_
    • Mixed Cascade (mixed-cascade) - Hero scatter-bursts. Sub bounce-slides from sides. Label blurs in with kick. All blur out. _(1-3 lines)_

    Letter level

    • Pulse Zoom (pulse-zoom) - Hero zooms with bounce. Sub letter-spaces with kick. Label spring-rises. Each line a different motion. _(1-3 lines)_
    • Typewriter (typewriter) - Hero strikes in letter-by-letter. Sub drops in. Label glides in. Then the whole thing un-types, erasing backward letter by letter. _(1-3 lines)_
    • Edge Burst (edge-burst) - Letters converge from the edges to the centre. Hold, then fly out the same way. _(1-2 lines)_
    • Center Burst (center-burst) - Letters explode outward from the centre. Pull back to centre on exit. _(1-2 lines)_
    • Random Rain (random-rain) - Hero letters drop randomly. Sub letters rise randomly from below. Label blurs in randomly. Chaotic build. _(1-3 lines)_
    • Quick Flash (quick-flash) - Snappy zoom in, sub kicks in with letter-spacing pulse. Snappy zoom out. _(1-2 lines)_
    • Side Step (side-step) - Lines glide in from alternating sides. Exit out the OPPOSITE side they came from. _(1-4 lines)_
    • Letter Blur (letter-blur) - Hero letters blur in, sub letter-spaces in, label letters distort in. Different distortion per line. _(1-3 lines)_
    • Rise Fall (rise-fall) - Hero rises in dramatically, falls out via drop. Sub bounce-slides from sides. Letter-kick label optional. _(1-2 lines)_
    • Ransom (ransom) - Every letter on its own torn paper scrap, stamped into place at a slight angle. _(1-3 lines)_

    Energetic

    • Explosion (explosion) - Letters scatter into place from random angles at full intensity. Sub bounces. Hero blasts out via scatter. _(1-2 lines)_
    • Tremor (tremor) - Hero shakes into place. Sub bounce-slides from sides. Label glitches in. Distinct per line. _(1-3 lines)_
    • Static (static) - Hero noise-flickers in like a broken neon sign. Sub glitches in with chromatic aberration. Holds, fades. _(1-2 lines)_
    • Glitch (glitch) - Hero glitches in with chromatic aberration. Sub distorts in through blur-contrast noise. Both fade with glitch-out. _(1-2 lines)_
    • Distortion (distortion) - Hero resolves out of heavy blur and contrast noise — analog video tuning in. Sub kicks in. Both fade through blur. _(1-2 lines)_

    Not in the picker

    Reachable only on blocks that already carry the key.

    • Sticker (sticker) - Die-cut badge. Block lines with a marker line across them, wrapped in one contour that follows the letters, with a shadow under it.
    • <!-- END GENERATED CATALOG -->

    Retired, do not emit: slow-burn (an alias that resolves to spotlight), bracketed, viewfinder, scribbled. These were documented but are not in the shipped catalog.


    Per-Line mode schema

    custom_mode: 1 and custom_lines = a JSON array (max 4 entries):

    ``json [ { "text": "BIG HEAVY", "motion": "rise", "px": 96, "weight": "900" }, { "text": "small serif light", "motion": "fade-in", "px": 34, "font": "Georgia, serif", "weight": "300" }, { "text": "mono medium", "motion": "glide-from-l", "px": 50, "font": "monospace", "weight": "600" } ] ``

    Per entry:

    • text — the line.
    • motion — one of the entry-motion keys below. The matching exit is auto-paired.
    • px — line size in pixels. Omit/blank = auto-fit (engine sizes the line to fit).
    • font — a CSS font-family stack (or omit to inherit). The UI offers a curated set: System, Sans, Serif, Editorial, Humanist, Couture, Display, Stamped, Script, Hand, Marker, Chalk, Mono, Typewriter.
    • weight — 300–900 (omit to inherit the block's heavy default).

    Entry-motion keys (for motion)

    Translate: rise, drop, glide-from-l, glide-from-r, bounce-slide-l, bounce-slide-split, bounce-rise Scale: zoom-in, scatter-in Fade/blur: fade-in, blur-in, spacing-in, letter-kick-in Clip-mask: circle-reveal, slit-open, wipe-right, wipe-left Glitch/noise: glitch-in, distort-in, shake-in, noise-flicker


    Screen-level Auto-advance

    For multi-screen kinetic sequences, set screen_autoplay_seconds (>0) on the screen (Scroll Timeline section), not the block. The screen auto-advances N seconds after becoming active. Pauses when the tab is hidden. Off by default.


    Examples

    Bold hero (Composition): ``yaml composition: block-slam text: | THE FUTURE STARTS HERE ``

    Cinematic single word: ``yaml composition: spotlight text: | AWAKE ``

    Poster with year numeral + brackets: ``yaml composition: slide accent: numeral accent_text: "2024" tier: label text: | EST NOIR STUDIO since two thousand ``

    3D drum (short lines): ``yaml composition: cylinder height_mode: fit text: | EAT DRINK REPEAT ``

    Mixed typography (Per-Line): ``yaml custom_mode: 1 custom_lines: | [ { "text": "BREAKING", "motion": "letter-kick-in", "px": 88, "weight": "900" }, { "text": "the mold", "motion": "glide-from-r", "px": 40, "font": "Georgia, serif", "weight": "400" } ] ``


    Anti-patterns

    • No paragraphs, no >4 lines, no >~4 words/line — they clip at the block edges.
    • No terminal punctuation in kinetic copy.
    • Don't use Cylinder with long multi-word lines — keep them short or the drum faces collide.
    • Don't combine Composition mode and Per-Line mode in one block — pick one (custom_mode decides).
    • Gradient fill does exist (fill: gradient), but it is an accent. Do not put one on every block.
    • Don't enable accent on every block by default: it's a deliberate accent, and it has no panel control, so a value you write is fragile (see the settings table).
    • All motion uses ease-in-out; only the Cylinder spin is linear (continuous loop).