State & Feedback Systems

Logo Trace Loader

A React loader where a short dash orbits a logo's outline; on completion it closes into a full stroke and fades in the fill. Defaults to the WebberUI brand curve, accepts any path and can run as a full-page overlay.

Turn your brand mark into a loading indicator. While loading, a short dash keeps orbiting the logo's outline over a faint full-length track; once isComplete turns true (or loading turns false), the dash grows forward into the full outline and the fill fades in — an open line has nothing to fill, so its stroke shifts from the mid-tone to the main colour instead. onDone fires once when the whole sequence ends. The default path is the WebberUI brand curve; pass your own path and viewBox to use any mark. Turn on fullscreen to cover the page with a translucent blurred overlay that fades out when done.

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-trace-loader.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

1.4
7.5
0.35
64
Active edited usageDefault props
<LogoTraceLoader/>

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-trace-loader.en.json

Or, once registries are configured in components.json, install it as @webberui/logo-trace-loader.

Usage

import { LogoTraceLoader } from "@/components/ui/logo-trace-loader";

const [loading, setLoading] = React.useState(true);
const [done, setDone] = React.useState(false);

// Set isComplete to true when the data arrives; onDone fires after the animation
<LogoTraceLoader
  loading={loading}
  isComplete={done}
  onDone={() => setLoading(false)}
/>

// Use your own closed path; the fill fades in on completion
<LogoTraceLoader
  path="M18 8H46A10 10 0 0 1 56 18V46A10 10 0 0 1 46 56H18A10 10 0 0 1 8 46V18A10 10 0 0 1 18 8Z"
  fill="rgb(16 185 129 / 0.18)"
  strokeWidth={5}
  isComplete={done}
/>

Props

PropTypeDefaultDescription
loadingbooleantrueWhether loading is in progress; switching to false counts as complete and starts the close
isCompletebooleanfalseWhether loading is complete; switching to true closes into a full outline, then colours it
pathstringWebberUI brand curveSVG path string to trace
viewBoxstring"0 0 64 64"SVG viewBox; must match the coordinate system of path
sizenumber64Mark size in px; the element has a fixed width and height to avoid layout shift
strokeWidthnumber7.5Stroke width when complete (viewBox units); the faint loop-phase track is half of it
loopDurationSecondsnumber1.4Seconds for the short dash to complete one orbit
fillFadeSecondsnumber0.35Seconds for the fill (or stroke colour change) to fade in on completion
fillstring"none"Fill for a closed path; with none the stroke changes colour instead
fullscreenbooleanfalseShow as a full-page overlay that fades out when done
labelstring"載入中" (Loading)Loading text for screen readers, also shown visibly under reduced motion
onDone() => void—Called once after the whole completion animation has finished
classNamestring—Forwarded to the outermost container

How it works

  • The phases run loop → closingOutline → fadingFill → done. The close is a fixed 360ms (cubic-bezier(0.23, 1, 0.32, 1)) and the fill fade uses fillFadeSeconds; phase switches are scheduled with setTimeout from the computed total rather than animationend, so onDone is never missed if an element unmounts or an animation is interrupted.
  • The short dash is drawn on a pathLength=1 path with strokeDasharray of 0.16 0.84 and strokeDashoffset moving linearly from 0 to −1, so any shape loops seamlessly; on close the dash grows forward from wherever it is into 1 0, without jumping back to the start.
  • If the loader starts out complete (for example loading={false}), it shows the final state directly without replaying the close or calling onDone; switching loading back to true restarts from the loop.
  • Colour variables: --wb-ltl-color (main colour, defaults to currentColor), --wb-ltl-track (the mid-tone after closing and before colouring, derived from currentColor mixed with transparent by default) and --wb-ltl-ease (easing for the stroke colour change). Setting a text colour through className recolours the whole set.
  • Fullscreen mode is a fixed inset-0 z-50 translucent blurred overlay (bg-white/80, bg-neutral-950/80 in dark mode) that fades in and out over 200ms.

Accessibility

  • The wrapper is a role="status", aria-live="polite" region that announces label while loading and "載入完成" (Loaded) when done
  • The SVG is aria-hidden, so the animation itself is never read out by assistive technology
  • In fullscreen mode the status text lives outside the overlay, so the completion message is still announced after the overlay fades out
  • When the user has reduce motion enabled at the system level there is no orbit: while loading it shows a static, faint full outline with visible label text, and on completion it jumps to the final state and calls onDone on the next tick