SuperAPI AI Directory

svg-stroke-reveal

General & other

View repository

Updated 10-16 01:13

About this skill

Skill: svg-stroke-reveal

Vanilla HTML/CSS/JS component — stroke <path> in SVG "draws" progressively as if a hand is drawing it, based on % scrolled through the viewport or once when entering the viewport. No external libraries (no Anime.js, no GSAP).

WHAT

Purpose and context

  • Purpose: stroke <path> in SVG "draws" progressively based on % scroll or once when entering the viewport (stroke-dasharray/dashoffset), vanilla with no external libs; by default use the original verbatim version at Rheinmir/uiux-asset.
  • Trigger (when to use):
    • Logo/signature/icon needs a "progressive draw" effect when entering the viewport (runs once).
    • Line charts, journey diagrams, route lines need to "draw out" continuously according to scroll progress (tied to actual scroll position, not run once).
    • User says "draw svg on scroll", "svg stroke draw", "logo progressive draw", "path animation reveal", "stroke-dashoffset", or /svg-stroke-reveal.
  • Non-goals: DO NOT use for the 7 general scroll effects (opacity/translate reveal, stagger text, highlight, nav shrink, scroll-to-top, scrollspy, parallax) — that is skills/scroll-effects. Do not use for <text> not yet converted to outline (see Rules).

Mental model

[data-svgr] > <path>×N → getTotalLength() → dasharray = dashoffset = length (ẩn) → mode "once" (IO một lần + CSS transition) hoặc mode "scroll" (IO bật cờ + 1 listener rAF ghi dashoffset theo progress) → reduce-motion: dashoffset = 0 ngay.

Input and output contract

Field Required? Meaning
In The project's real SVG (the <path> with strokes) yes <text> must be converted to outline first
In mode once or scroll yes logo/signature → once; chart/journey → scroll
In design.md of the project no if present, change the color token according to it
Out SVG with [data-svgr] + CSS/JS attached in the project yes copy the original file verbatim or adapt assets/
Out verification evidence playwright-verify yes before/during/after images (scroll) + once only triggers 1 time

Rules and capabilities

  • RULE-01 (MUST): getTotalLength() measured on the path's intrinsic coordinates, NOT affected by CSS transform — but if you scale the element containing the path with transform: scale(sx, sy) with sx != sy (NON-UNIFORM scale on two axes) AFTER setting dasharray, the drawing speed in px will be skewed between the two axes because the animation still runs according to the original length while the display has been proportionally distorted. Always set size via viewBox + width/height uniformly proportioned, avoid axis-skewed CSS scale on the element carrying the path itself.
  • RULE-02 (MUST): <text> cannot stroke-draw — must convert text/font to outline <path> first (Illustrator "Create Outlines", Figma "Flatten", or a font-to-path conversion tool), then apply [data-svgr]. Applying directly to <text> will do nothing because <text> has no getTotalLength().
  • RULE-03 (MUST): Mode "once" uses CSS transition (smooth, low CPU cost); mode "scroll" sets dashoffset directly every frame via JS (required to stay tightly in sync with scroll position) — do not swap these two approaches.
  • RULE-04 (MUST): One IntersectionObserver separate for each mode (do not merge once/scroll logic into 1 observer because behaviors differ: unobserve-once vs continuously toggle flag) and one single rAF-throttled scroll listener for all SVG mode "scroll" on the page — do not attach multiple separate scroll listeners for each SVG.
  • RULE-05 (MUST): Verify with playwright-verify: screenshot before/during/after scrolling through SVG mode "scroll" to see dashoffset decrease gradually, and assert SVG mode "once" triggers only 1 time even if scrolled in and out multiple times.
  • Capabilities: read the original (web/git) + asset skill + design.md; write HTML/CSS/JS into the target project; run a headless browser to verify.

Failure boundaries

  • Apply to <text> not yet converted to outline → nothing happens: blocked, require conversion to <path> first (RULE-02).
  • Element carrying the path is skewed off-axis by CSS scale → drawing speed distorted: failed, move sizing to viewBox + width/height (RULE-01).
  • Verify assert red (dashoffset not decreasing gradually, once triggers again) → failed, fix then verify again.

HOW

Main workflow

Step Type Inputs Action Outputs/exit Failure/next
W01 judgment SVG + requirements Choose mode once or scroll; check whether the SVG is already <path> (no <text>) mode <text> → blocked until converted
W02 effect original Pen table Open link run, copy the original file .html (+ _assets/) into the project, keep the attribution comment original file in the project must not pull in external libs → B01
W03 effect file + actual SVG + design.md Only edit content/images/colors (replace path d=) adapted SVG —
W04 deterministic page running over HTTP Verify with playwright-verify (RULE-05) images + asserts red → fix, repeat W04

Branches

ID Kind Guard Behavior Skip / failure Rejoin
B01 conditional_required the project must NOT pull external libs (original version) use the trimmed vanilla version assets/ (Reference section — Assets), read then ADAPT — W04
B02 conditional_required prefers-reduced-motion: reduce strokeDashoffset = 0 right at init, remove observer/listener — W04

Validation and stopping

Deterministic assert at W04 (scroll mode: dashoffset decreases across before/mid/after images; once mode: triggers only once even when scrolling in and out). Needs eyes: smoothness, stroke order. Stop when asserts are green.

Examples

  • Positive: "logo draws gradually when entering the screen" → data-svgr="once" on a logo with 4 paths → when 30% enters viewport, draw sequentially (stagger 160ms in DOM order), scrolling out and back in does not redraw.
  • Boundary/failure: the signature is <text> script font → no getTotalLength(), nothing draws → stop, request converting to outline (Figma "Flatten") into <path> then apply [data-svgr].

Reference — Original — use FIRST (copy the file as-is, do not rewrite)

The author's VERBATIM original code (public CodePen = MIT) is in the external repo Rheinmir/uiux-asset — live gallery: https://rheinmir.github.io/uiux-asset/scroll-effects/ . Process: open link run to see the exact effect → copy the file as-is .html (+ folder _assets/ if the file references it) into the project → keep the attribution comment on the first line → only change content/images/colors. Run locally over HTTP, not file://. git clone --depth 1 https://github.com/Rheinmir/uiux-asset if the full offline set is needed.

Original Pen — author Link
Interactive Cicada Genomics Landing Page — alyona-mysiura run · code
Scroll-Driven SVG Map Editorial Gallery — knyttneve run · code
Scroll-Driven Godzilla Walk-and-Destroy Animation — creativeocean run · code

assets/ in this skill is only a self-written trimmed vanilla version — use when the project must NOT pull external libs; by default use the original version above.

Reference — Assets

assets/demo.html + assets/svg-stroke-reveal.css + assets/svg-stroke-reveal.js — read then ADAPT into the project's actual SVG (replace path d=, change color tokens per design.md if any), do not copy mechanically.

Reference — Mechanism

  • For EACH <path> in [data-svgr]: JS calls path.getTotalLength(), set inline style.strokeDasharray = length and style.strokeDashoffset = length at init → stroke fully hidden (dash exactly equal to path length, offset pushes the entire dash outside).
  • Mode data-svgr="once" (one-time threshold — logo/signature): IntersectionObserver threshold 0.3, when entering viewport → set strokeDashoffset = 0 for all child paths → CSS transition: stroke-dashoffset run once → unobserve immediately after trigger, no repeat even if scrolled out and back in.
  • Mode data-svgr="scroll" (continuous progress — chart/journey): IntersectionObserver only toggles the "in viewport" flag; while it is on, a rAF-throttled scroll listener (shared single listener, not multiple) computes progress = (innerHeight - rect.top) / (innerHeight + rect.height) clamped 0..1, then writes directly strokeDashoffset = length * (1 - progress) every frame — do NOT use CSS transition in this mode (transition will make the stroke lag behind the actual scroll position).
  • Complex path with many <path> children (multi-stroke logo, signature) — "once" mode staggers via transition-delay: calc(var(--svgr-i) * 160ms), JS assigns --svgr-i in the ORDER of paths in the DOM (first path draws first).
  • prefers-reduced-motion: reduce → JS sets strokeDashoffset = 0 immediately for all paths at init, bypassing both observer/listener — shows full stroke instantly, no animation.

Origin