Content · 11 of 13
The two-line display heading. Its second line is one of the few sanctioned
uses of #5F20FE on text — legitimate only because it runs at
32–56px, where the 3:1 large-text floor applies.
| Prop | Type | Notes |
|---|---|---|
| text1 * | string | First line, --color-heading. |
| text2 * | string | Second line, --color-action. |
Live — this is the real component and the real GSAP SplitText (3.15,
vendored). On mount, once document.fonts.ready resolves, it
breaks the text into characters and staggers them from
opacity: 0.5 at 0.1s intervals. Reload to replay.
The selector is global, so multiple instances collide.
SplitText.create('.split-text span', …) queries the whole
document, not the component root. A second TypingEffect on the same page
causes each mount to re-split every instance's spans — including
ones already split — and the later mount animates characters belonging to
the earlier one. The fix is a template ref scoped to this component.
No reduced-motion guard.
The stagger runs unconditionally from onMounted and again on
every prop change. It is GSAP rather than CSS, and the current site ships
no prefers-reduced-motion handling at all — this is the
site's largest piece of moving text, so it is the animation most worth
guarding.
gsap.registerPlugin runs on every call.
It sits inside splitAnimation, so it re-registers on mount and
on each watch firing rather than once at module scope. Harmless
but repeated, and it makes the watch path heavier than it needs to be.
Waiting on document.fonts.ready is the right call.
Splitting before Satoshi loads would measure fallback glyphs and produce
character boxes that jump when the real font arrives. The component defers
until fonts settle — worth preserving in any rewrite.
The heading is an <h2> with two block spans.
Both lines live inside one heading element, so the accessible name is the
full sentence — correct. The :key="text1" forces a fresh node
when the copy changes, which is what makes the watch re-split
work.