Logo Reveal Set
Five common logo entrances (bloom, stroke-then-fill, shine sweep, lockup slide-out and wordmark morph) triggered on mount, on entering the viewport or manually.
Five brand logo entrances in one component. bloom grows a glowing point past full size, then settles; stroke draws along an SVG path and then deepens the light stroke into the final colour; shine fades in and sweeps a diagonal highlight that stays inside the logo strokes; lockup brings in the mark first and slides the wordmark out from a mask behind it; morph turns the wordmark into another string character by character, with shared characters sliding to their new place instead of reappearing. Play it on mount, when it enters the viewport, or manually with ref.play(); onComplete fires when it finishes.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add logo-reveal-set.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<LogoRevealSet/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/logo-reveal-set.en.jsonOr, once registries are configured in components.json, install it as @webberui/logo-reveal-set.
Usage
import * as React from "react";
import {
LogoRevealSet,
type LogoRevealSetHandle,
} from "@/components/ui/logo-reveal-set";
const MARK = "M9.5 52C14 52 19 12 24 12C29 12 32 43 37 43C40.5 43 41.5 36 45 36L55 36";
export function Hero() {
const ref = React.useRef<LogoRevealSetHandle>(null);
return (
<>
<LogoRevealSet
ref={ref}
variant="stroke"
trigger="inView"
path={MARK}
strokeWidth={7.5}
wordmark="Harbor Mist"
onComplete={() => console.log("entrance finished")}
/>
<button type="button" onClick={() => ref.current?.play()}>
Replay
</button>
</>
);
}
// The mark can also be any node (for variants that do not draw a stroke)
<LogoRevealSet variant="lockup" wordmark="Ridge Goods">
<svg width="40" height="40" viewBox="0 0 40 40" aria-hidden="true">
<circle cx="20" cy="20" r="16" fill="currentColor" />
</svg>
</LogoRevealSet>
// Wordmark morph
<LogoRevealSet variant="morph" wordmark="WebberUI" morphTo="Webber UI 元件庫" path={MARK} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | The mark: an SVG or any node; when path is provided the mark is drawn from path and children are not shown |
wordmark | ReactNode | — | The wordmark; must be a string for the morph variant |
morphTo | string | — | Target wordmark for morph; the wordmark morphs character by character from wordmark into this text |
path | string | — | SVG path (d attribute) for the mark; the stroke variant needs it to draw |
viewBox | string | "0 0 64 64" | SVG viewBox for path; must match the path coordinate system |
size | number | 48 | Side length of the path mark (px) |
strokeWidth | number | 6 | Stroke width of the path (viewBox units) |
fill | string | "none" | Fill for the path; with none, the fill phase of an open line deepens the stroke colour instead |
variant | "bloom" | "stroke" | "shine" | "lockup" | "morph" | "bloom" | Entrance variant |
trigger | "mount" | "inView" | "manual" | "mount" | When to play; with manual, call ref.play() |
duration | number | 1.2 | Total entrance duration (s); when omitted, reads --wb-lrs-duration and falls back to 1.2 |
glow | number | 0.6 | Glow strength 0–1, mapped to a two-layer drop-shadow of 0–16px |
glowColor | string | "var(--wb-lrs-glow, currentColor)" | Glow colour |
delay | number | 0 | Delay before starting once triggered (s) |
onComplete | () => void | — | Called when the entrance has fully finished; with reduce motion it fires immediately when triggered |
ariaLabel | string | wordmark text | Accessible name of the outer role="img" |
wordmarkClassName | string | — | Classes passed to the wordmark text (size, weight, etc.) |
ref | Ref<LogoRevealSetHandle> | — | Imperative handle: play() and reset() |
className | string | — | Passed to the outermost container |
LogoRevealSetHandle
| Field | Type | Default | Description |
|---|---|---|---|
play | () => void | — | Play the entrance from the start (works in every trigger mode; handy for a replay button) |
reset | () => void | — | Stop and return to the hidden pre-entrance state |
How it works
- Each variant splits
durationinto phases:bloomgrows from scale 0.05 to 1.15 with the glow at its peak over the first 45%, then settles to 1 with the glow down to glow×0.3;strokedraws for the first 70% and deepens the colour over the last 30%;shinefades in over 40% and sweeps the highlight over the remaining 60%;lockupscales the mark 0.8→1 over 40%, then slides the wordmark out on a spring (stiffness 220, damping 26);morphswitches the text at 35%, with new characters fading in from a blur. - The glow is two
drop-shadowlayers (radius R and 2R), so it follows the outline of the mark rather than drawing a box shadow. - The shine highlight is a white copy of the same content, moved under a diagonal
mask-imageband, so the highlight only shows inside the strokes. If children is an SVG withids (gradients, clipPaths), the copy duplicates those ids; preferpathor an id-free SVG. - Timing and colour can be overridden with CSS variables:
--wb-lrs-duration(duration, e.g.1.6s),--wb-lrs-glow(glow colour) and--wb-lrs-stroke(the light colour during the stroke phase). Adurationprop takes precedence. - With
trigger="inView", an IntersectionObserver plays it once when 35% of it is visible and then stops observing;ref.play()replays in any mode. - The first render (server included) is the hidden pre-entrance state, matching the client, so there is no hydration mismatch.
Accessibility
- The outer element is
role="img"with anaria-labelthat defaults to thewordmarktext and can be overridden withariaLabel; every animated layer isaria-hidden, so morphing characters are not announced one by one - The animation never moves focus or captures the keyboard; the replay button is yours to provide, so use
type="button"and a clear label - When the user has reduce motion enabled, every variant shows its final state directly (no glow pulse,
morphshows the target text), andonCompletefires immediately when triggered
Karaoke Text
Lyric lines colored character by character along a timeline, with the current line scaled up in focus.
Strike Reframe Text
A strike-and-reframe text React component: a sentence is struck through with a hand-drawn line, then a new sentence rises from below to replace it. Chainable for objection-to-answer and old-claim-to-new-claim reframes.