TextTransformer
Short-text transition helper: morphs per character through the text-morph engine by default (numbers roll by place value), and keeps the original whole-string blur crossfade as a mode, with a scoped text slot.
Not recommended for high-frequency real-time updates (e.g., streaming text, per-frame state changes). Better for occasional state/title/chapter/short-text changes.
Usage
TextTransformer
Best Practices
- Use it for labels, titles, badges, and occasional state changes; avoid streaming tokens or per-frame counters.
- Reach for
TxTextMorphdirectly when you want the morph without thefadecompatibility layer — it exposes the full surface: springs, the place-value switch,localeandcursorIndex. - Keep
durationMsaligned with the surroundingTxAutoSizertransaction when both are used together; themorphengine animates its own width and height, so an outer sizer is usually redundant. - Prefer
wrap=falseinside compact buttons/badges to avoid vertical jumps; enablewraponly for paragraph or chapter transitions (which fall back tofade). - Keep slot content inline and lightweight. Heavy components inside the slot are duplicated while the previous layer is visible.
- If color changes with text, set color on the transformer root; under
fadethe outgoing layer keeps the old computed color for a cleaner crossfade.
API Reference
TxTextTransformer
Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | number | - | Value rendered in the current layer and announced through the polite live region. |
mode | 'morph' | 'fade' | morph | morph hands the value to the text-morph engine (per-character diff, place-value digit rolls); fade is the original whole-string blur crossfade. The default slot and wrap both force fade. |
durationMs | number | 240 | Transition duration in milliseconds; under fade it also controls when the previous layer is removed. |
blurPx | number | 8 | Blur distance applied to the outgoing and incoming layers during the transition. fade only — the morph engine has no blur stage. |
tag | string | span | Root HTML tag used for the transformer wrapper. |
wrap | boolean | false | Allows multi-line text by switching layer whitespace from ellipsis mode to pre-line. Forces fade. |
Slots
| Slot | Props | Description |
|---|---|---|
default | { text: string } | Optional renderer for both current and previous layers. Use it to wrap the text in custom inline markup while keeping the transition logic. |
Events
No Vue events are emitted. Drive state from the parent via the text prop.
Morph and Fade
mode defaults to morph: the value goes to the text-morph engine, shared words hold their position, only the changed characters move, and digits roll by place value. fade is the original whole-string blur crossfade, and the only mode blurPx applies to.
Two situations override mode and force fade: the default slot being in play (a slot renders arbitrary nodes, and the engine has no plain-text segments to morph), and wrap being on (the engine lays its segments out on one nowrap line and animates the container's width, which a value reflowing inside a constrained box cannot be measured against).
Morph vs fade
Used with AutoSizer
When you want width/height to follow text changes smoothly, wrap TxTextTransformer in TxAutoSizer and trigger a transaction via autoSizerRef.action(() => ...).
If you do not want text to wrap during size animations (vertical/jumping lines), keep wrap=false; overflow will be clipped (overflow: hidden).
AutoSizer + TextTransformer
Long Text / Chapter Switch
Long text chapter
Title + Subtitle
Title + subtitle
Status/Badge Text
Status text
Overview
textis normalized withString(...); numbers are displayed as their string form.- Under
morphthe value goes to the engine: the whole value sits in a visually-hidden[tx-morph-sr]node and every segment isaria-hidden. The root'soverflowopens back up tovisible, or exiting segments would be cut in half — which is whymorphdoes not ellipsise. - The root uses
aria-live="polite"; underfadethe previous layer isaria-hiddenso screen readers announce only the current text. - Under
fade, whentextchanges the component snapshots the previous DOM color, then renders both layers:- the new layer first lands in its setup state (transparent and blurred) with no transition;
- the component forces one style commit, then starts the animation on the next animation frame;
- the previous layer is removed after
durationMs + 34ms. Without that commit, the setup and animating states would land in the same style recalc and the new text would appear at once instead of fading in.
- Under
prefers-reduced-motion: reduce, neither layer transitions; the new text replaces the old at once. - A newer text change cancels the previous timer/frame through an internal sequence guard, so stale transitions cannot remove the active layer.
wrap=falsekeeps one-line ellipsis behavior;wrap=trueswitches layers towhite-space: pre-linefor multiline or newline-delimited content.- The scoped slot is invoked for both current and previous text, so slot output should be deterministic from the provided
textvalue.
Technologies
- Live-region note: The root always uses
aria-live="polite". Avoid placing multiple high-churn instances in the same dense region, or screen readers may announce more changes than users can consume. - Transition note: The outgoing layer is duplicated until
durationMs + 34ms; slot content is rendered for both layers during that window. Keep slot renderers pure and cheap. - Fade-in fix (2026-09-24): Before this fix, the setup and animating states were computed in the same style recalc, so under
fadethe new text never faded in; only the old text blurred out. The setup state now lands without a transition, and a style commit is forced before the animation frame is requested.TxModeChip's label crossfade, which depends on this fade-in, is what surfaced it. - Verified coverage:
text-transformer.test.tschecks:- the default morph path, and the slot and
wrapfallbacks tofade; - on the
fadeside: live-region semantics, custom root tag, CSS variable mapping for duration/blur, wrap class, and numeric text normalization; - previous/current layers, preserved previous color,
aria-hiddenon the outgoing layer, timer cleanup, and scoped slot props; - the forced setup-state commit, the setup state landing without a tween, and the reduced-motion escape.
- the default morph path, and the slot and
- Component source:
packages/tuffex/packages/components/src/text-transformer/src/TxTextTransformer.vue. - Types:
packages/tuffex/packages/components/src/text-transformer/src/types.tsexportsTextTransformerProps. - Export alias:
packages/tuffex/packages/components/src/text-transformer/index.tsexportsTextTransformer,TxTextTransformer,TextTransformerProps, andTxTextTransformerInstance. - Coverage:
packages/tuffex/packages/components/src/text-transformer/__tests__/text-transformer.test.tsverifies both modes, both forced fallbacks, live-region defaults, transitions, CSS variables, slots, and cleanup. - Engine:
morphrendersTxTextMorph; see TextMorph for the engine itself.