StringSplit Module
Granular text choreography for hero headlines, body copy, and captions. StringSplit rebuilds your markup into line, word, and character wrappers, annotates them with deterministic indexes (or controlled randomness), and exposes the data as CSS variables for your animation system.
Activation primer: Add
string="split"to every element you wantStringSplitto rebuild. Supplystring-id="your-id"when you need a stable handle for events, mirrors, orstring-copy-from; otherwise StringTune generates one automatically.
HTML Attributes
| Attribute | Applies to | Default | Controls | Practical notes |
|---|---|---|---|---|
string-split | Same element | "" (no wrappers) | Grammar defining which wrappers to build (line, word, char, word-line, char-line, char-word) + indexing rules | Tokens are pipe-separated, e.g. word[start] | char[random(0,5)]; brackets accept multiple params via ; |
string-split-restore-after | Same element | not set | Milliseconds after which original HTML is restored and -restored class is added | Use for one-off intro animations when content should return to editable state |
string-split-original | Same element | injected | Stores pristine HTML captured on first run | Managed internally — do not modify manually |
Module Snapshot
- Activation attribute:
string="split" - Wrappers emitted: optional
-s-line,-s-word,-s-charspans (only for groups requested instring-split) - CSS variables: indexes for each wrapper (
--line-index,--word-index,--char-index, etc.), totals (--word-total,--char-total,--char-global-total), plus alignment variables (--word-start,--char-center, …) - Class markers:
-before-element/-after-elementidentify tokens adjacent to inline elements;split-classlets you seed custom classes per token - Accessibility: original text stored under
aria-label; generated spans markedaria-hidden="true"to avoid double narration - Rebuild triggers: re-splits on width resize (mobile height/width rebuild toggles disabled for performance)
- Cleanup: listeners not required; module restores original markup if destroyed or when
string-split-restore-afterelapses
Basic Usage
import StringTune, { StringSplit } from '@fiddle-digital/string-tune';
const stringTune = StringTune.getInstance();
stringTune.use(StringSplit);
stringTune.start(60);
<h1 string="split" string-id="hero-heading" string-split="line[center];word[start]|charWord[start];charWord[end]" class="hero-heading">
Lift stories off the baseline
</h1>
.hero-heading {
--char-delay-step: 12ms;
}
.hero-heading .-s-line {
display: block;
transform-origin: 50% 50%;
transform: rotate(calc(var(--line-center, 0) * 0.5deg));
}
.hero-heading .-s-word {
display: inline-block;
transition-delay: calc(var(--word-start, 0) * 40ms);
}
.hero-heading .-s-char {
display: inline-block;
transition-delay: calc(var(--charWord-start, 0) * var(--char-delay-step));
transform: translateY(calc((1 - var(--charWord-end, 0)) * 1.2em));
opacity: calc(var(--charWord-end, 0));
}
Split Grammar Essentials
- Tokens are pipe-separated:
token[paramA;paramB] | another-token[...]. - Hyphenated names in HTML (
word-line) map to camelCase internally (wordLine). - Alignment params accept
start,end,center,random, andrandom(min,max);absis parsed for future use. - Each token adds or augments wrappers, producing CSS variables documented below.
- Random alignments recompute on each rebuild; persist the DOM if you need deterministic sequences.
string-split Token Reference
| Token snippet | Wrapper(s) produced | Index scope | Supported params | CSS variables emitted | Notes |
|---|---|---|---|---|---|
line[...] | Generates span.-s-line per text line | All lines inside the element | start, end, center, random(min,max), abs | --line-index, --line-*, --word-total | Adds optional <br> for gaps; enables line-level motion |
word[...] | Generates span.-s-word per word | Every word in the element | Same as above | --word-index, --word-*, --char-total | Required if you want per-word transforms or word-line metrics |
word-line[...] | Reuses -s-word wrappers | Words within the current line | Same as above | --wordLine-* | Adds line-scoped indexes so words can animate relative to their line |
char[...] | Generates span.-s-char per character | All characters across the element | Same as above | --char-index, --char-*, --char-global-total (on root) | Enables per-character animation; respects kerning adjustments |
char-word[...] | Reuses -s-char wrappers | Characters within the current word | Same as above | --charWord-* | Great for stagger effects that restart each word |
char-line[...] | Reuses -s-char wrappers | Characters within the current line | Same as above | --charLine-* | Useful for wave effects that sweep line by line |
Tip: wrap inline segments with
<split-class class="accent">...</split-class>to push custom classes onto the generated wrappers.
Generated CSS Variables
StringSplit writes variables directly on the wrappers it creates. Use them to compute delays, offsets, and transforms without extra JavaScript. Values are always numbers (integers for indexes, decimals for averages) and are only emitted when the corresponding token is present.
Line-level (span.-s-line)
| Variable | Meaning | Example value |
|---|---|---|
--line-index | Zero-based position of the line within the element | 0 for the first line |
--line-start, --line-center, --line-end | Alignment counters based on the option you declared | 2 meaning “two steps from the chosen anchor” |
--line-random | Random integer inside the requested range | 4 when line[random(0,5)] is active |
--word-total | Number of words in this line | 5 for a five-word line |
Word-level (span.-s-word)
| Variable | Meaning | Example value |
|---|---|---|
--word-index | Word order across the entire block | 7 for the eighth word |
--word-start, --word-center, --word-end, --word-random | Alignment counters driven by the word[...] token | 1 → one step from the anchor |
--wordLine-start, --wordLine-center, --wordLine-end, --wordLine-random | Line-scoped alignment from word-line[...] | 0 meaning “first word in its line” |
--char-total | Character count inside this word (excluding spaces) | 6 for “String” |
Character-level (span.-s-char)
| Variable | Meaning | Example value |
|---|---|---|
--char-index | Global character order across the element | 23 for the 24th glyph |
--char-start, --char-center, --char-end, --char-random | Alignment counters from the char[...] token | 3 meaning “third from the chosen edge” |
--charWord-start, --charWord-center, --charWord-end, --charWord-random | Word-scoped alignment from char-word[...] | 0 for the first letter in its word |
--charLine-start, --charLine-center, --charLine-end, --charLine-random | Line-scoped alignment from char-line[...] | 5 for the sixth glyph in the line |
--char-global-total (set on the root element) | Total number of characters across all lines | 74 for a long paragraph |
Variables appear only when their corresponding token is declared, keeping unused CSS clean.
Output Structure & Styling Hooks
- DOM rebuild: original HTML is cloned into a
DocumentFragment, measured, and rebuilt into the output fragment. Inline elements (e.g.,<em>,<svg>) are cloned wholesale and reinserted as words so your semantic markup survives. - Whitespace management: the engine inserts non-breaking spaces (
\u00a0) where needed so the visual spacing mirrors your source copy.<br>tokens force line breaks in the rebuilt structure. - Kerning preservation: character wrappers compute pixel-perfect widths using a hidden canvas (
FontMetrics) and apply precisemargin-rightvalues so browsers with loose kerning still align flawlessly. - Special classes: wrapping text with
<split-class class="accent">…</split-class>pushesaccentonto either the entire word wrapper (if all chars inherit it) or individual-s-charnodes, letting you flag tokens for bespoke animation. - Element adjacency: spans bordering inline elements gain
-before-elementor-after-elementso you can smooth transitions between text and icons. - Data mirrors: every wrapper carries
data-split-contentwith its textual payload—a handy escape hatch for debugging or fallbacks.
Alignment Strategies Explained
- start / end / center: produce deterministic integers beginning at 0.
startcounts forward,endcounts backwards, andcenterreturns the distance to the mid-point (useful for symmetric easing). - random(min,max): produces an integer inside the provided inclusive range. Omit parameters to default to the full span (
0→length - 1). - word-line / char-line / char-word: scope the counter to the enclosing line or word. Example: combine
word[start]withcharWord[start]to orchestrate wave animations that reset for each word.
Restoring & Re-running
- Caching: the untouched HTML is stored in
string-split-original; repeated connections reuse the cache to prevent progressive degradation. - Timed restore: setting
string-split-restore-after="2500"restores the raw HTML after 2.5 s, adds-restored, and keeps aria attributes intact—ideal for animating in then letting editors retain editability. - Manual refresh: call
stringTune.onResize(true)after changing thestring-splitattribute to rebuild the structure.
Accessibility & Performance Notes
- Screen readers read
aria-label(the original string) while generated spans remain hidden, so you get visuals without duplicating narration. - Width-only rebuilds prevent choppy behavior on mobile orientation changes; if you inject fonts dynamically, call
stringTune.onResize(true)once the family is ready so kerning recalculates. - Random alignments recompute on each rebuild; persist the generated structure if you need fixed ordering (e.g., snapshot the DOM before hydration completes).
Event Signals
StringSplit does not emit custom bus events. Hook into object:connected:<id> from the core engine if you need to react once splitting completes.
StringSplit turns textual content into a choreography-ready data structure—annotated spans, predictable indexes, and kerning-aware wrappers—so you can script expressive typography without hand-crafting DOM for every headline.