Overview
Hand-drawn brush stroke mask reveal: an inline SVG <mask> contains a path that animates via stroke-dasharray + pathLength, and a visible content div references it via mask-image: url(#brush-reveal). The path acts like a paint stroke painting the content into view.
When to use it
Reach for brush-stroke masks on editorial heroes, gallery introductions, and brand reveals that want a human-made signature feel. Skip it for utility chrome — the hand-drawn brush is loud enough to feel marketing-y. Skip it for performance-critical surfaces; the mask path animation costs a re-rasterization per keyframe step.
How it works
Define an SVG mask in the document head: <svg><defs><mask id="brush"><path d="..." stroke="white" stroke-width="80" fill="none" pathLength="1" stroke-dasharray="1" stroke-dashoffset="1" /></mask></defs></svg>. Reference it from CSS: .hero { mask-image: url(#brush); -webkit-mask-image: url(#brush) }. Animate the mask path’s stroke-dashoffset from 1 down to 0 via CSS or JS — as the dash recedes, the white “paint” expands along the path, revealing more of the masked content underneath.
Production gotchas
Mask animations require a re-rasterization per keyframe step in older browsers — cap the path complexity (under ~5 control points) for smooth playback on mid-range mobile. Safari iOS still requires the -webkit-mask-image prefix alongside the unprefixed property. The brush stroke width determines how much of the underlying content reveals at each step; too narrow and the reveal feels stingy, too wide and the hand-drawn character is lost. The masked content needs explicit width + height on the wrapper or the mask positioning drifts on responsive viewports.
Accessibility
The content under the mask is real DOM and remains announced by screen readers from the start — the mask only changes what is visually painted. Under prefers-reduced-motion: reduce set stroke-dashoffset: 0 immediately (skip the animation) so the full content appears without the brush reveal. Verify masked-out content does not become a focus trap; tab order should match visual reveal order or skip masked regions until they paint in.
References
Implementation depth
The mask should behave like a brush, not a rectangular wipe. A wide stroked SVG path inside a mask gives the reveal irregular edges while the underlying content remains normal DOM and can be read immediately.
Mask sizing is the fragile part. Tie the mask viewBox and the content box together, then test responsive widths; otherwise the brush stroke drifts away from the text it is meant to reveal.