Ghost mascot
ProA floating ghost mascot whose soft sheet ripples at the hem while it hovers. It blinks when idle, turns toward you to listen, fades a little translucent while thinking, draws a tiny pen line while writing, smiles with a sparkle when done and wobbles once on an error.
- 24
- 40
- 64
- Sheet
- Ink
- Give an assistant a light, friendly face that shows what it is doing: in a reply header, in a composer, over an image canvas while it generates, or in an empty state.
- Products that want generation to feel alive without a spinner, with states that still read at 24px.
- Use agent-status when people need the steps, tools and elapsed time of a run, not just a mood.
- Use text-shimmer alone for a plain "Thinking" line in dense interfaces where a character would distract.
- Do not use it as a progress indicator with a known percentage; use a progress bar.
Installation
Pro source and install commands unlock with a Pro plan.
import { GhostMascot, type GhostStatus } from "@/components/arc/ghost-mascot/ghost-mascot"; // Inline next to a streaming replyexport function ReplyHeader({ phase }: { phase: "waiting" | "streaming" | "complete" | "failed" }) { const status: GhostStatus = phase === "streaming" ? "writing" : phase === "complete" ? "done" : phase === "failed" ? "error" : "thinking"; return ( <div style={{ display: "flex", alignItems: "center", gap: 8 }}> <GhostMascot status={status} size={24} name="Atlas" /> <span>Atlas</span> </div> );} // Empty state: the classic pale sheet<GhostMascot status="idle" size={160} tone="sheet" name="Atlas" /> // On an image canvas while it generates<GhostMascot status={generating ? "thinking" : "done"} size={64} announce /> // Brand color sheet with light eyes<GhostMascot status="listening" size={96} color="#7747ff" eyeColor="white" />GhostMascot
A floating ghost drawn in SVG: a soft sheet with a rippling hem, a quiet form shade and two simple eyes. Six states. Idle floats, blinks and glances around. Listening comes forward with wide eyes and a small head tilt. Thinking drifts up, turns translucent and looks up at a small swirl that curls in and out. Writing leans toward a line it writes with a tiny pen, eyes on the tip. Done lifts with happy eyes and a sparkle. Error wobbles once, sinks and droops its eyes. Status changes blend from the pose on screen over 400ms, never a hard cut.
status"idle" | "listening" | "thinking" | "writing" | "done" | "error"–What the assistant is doing. Leave unset to play through every state on a loop (the docs demo).sizenumber96Width and height in px, from 24 (inline next to a message) to 160 (empty state). Below 56 a compact drawing takes over: tighter framing, bigger eyes, heavier swirl, pen and line, no shadow.tone"accent" | "sheet" | "ink""accent"Sheet color from the theme: the accent with accent-foreground eyes, a pale raised surface with a hairline and foreground eyes (marks keep the accent), or the foreground ink with background eyes.colorstring–Any CSS color for the sheet. Overrides tone; the eyes become a dark mix of it unless eyeColor is set.eyeColorstring–Any CSS color for the eyes.groundbooleantrueThe soft floating shadow, which shrinks and fades as the ghost rises. Drawn only at 56px and up.pausedbooleanfalseFreeze on the current frame. The ghost also pauses by itself off screen and in background tabs, and resumes on the same frame.namestring"Assistant"Used in the accessible label, for example "Atlas, thinking".labelstring–Overrides the whole accessible label.announcebooleanfalseAnnounce status changes in a polite live region. Turn it on for the one ghost that carries status, not for decorative copies.onStatusChange(status: GhostStatus) => void–Called when the self-playing loop (status unset) moves to the next state. Use it to sync a caption or a control.classNamestring–Class on the root span. Set --ghost-body, --ghost-eye, --ghost-mark, --ghost-shade or --ghost-ground here to restyle.styleCSSProperties–Inline style on the root span.- None
- The ghost is a status image and takes no focus. Pair it with a real control when people act on what it shows.
- The SVG has role img and a label built from name and status, for example "Atlas, thinking", which updates with every state.
- With announce, status changes are read in a polite live region outside the image. Use it on one ghost per view so screen readers do not hear duplicates.
- Status is never carried by color or motion alone: each state changes the face and marks (wide eyes, eyes up with a swirl, a pen and line, happy eyes with a sparkle, droopy eyes) and the label. Show a text status next to it where the state matters.
- Every loop is a function of a phase that only grows: the float, the two hem waves, the swirl and the writing cycle. Rates blend by integrating into those phases, so speed changes never jump and nothing restarts.
- The hem is two traveling sine waves drawn as exact Hermite curves through rounded corners, rebuilt every frame. It trails the float: the sheet stretches on the way up, and the hem lifts and flares on the way down.
- A status change blends every pose value from the frame on screen to the new pose over 400ms with an ease in and out. Eyes ride a critically damped spring: quick saccades when idle, a slow drift up while thinking, tight tracking of the pen tip while writing. Nothing bounces.
- The swirl draws in from its outer end and erases behind itself; the writing line draws word by word, the pen lifting between words, then erases while the pen glides back. Both are empty at the moment their cycle wraps, so there is no seam.
- Done grows the sparkle in 320ms with an ease out and a quarter turn, then a slow twinkle. Error wobbles once, decaying to rest in about 1.2s, softened at the start so it never kicks.
- Reduced motion shows a still, representative pose for each state (a half drawn swirl, half a written line, the sparkle, the droop), switches in place and drops every loop, glance, blink and wobble.
- A fixed square of size px that never reflows its parent. The drawing is vector art and stays crisp at any device pixel ratio.
- Below 56px the compact drawing frames the ghost tighter and enlarges the eyes and marks so they stay legible at 24px.
- One requestAnimationFrame loop per ghost writes SVG attributes directly, so React does not re-render per frame; only the self-playing loop sets state, once per state.
- The loop stops off screen (IntersectionObserver) and in hidden tabs, and its clock only runs while visible, so it resumes on the same frame instead of jumping.
- Under reduced motion the loop repaints only when the status changes.
- About a dozen SVG nodes, one clip path, no filters, masks or gradients.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Map the assistant's real phases to status: microphone open is listening, waiting for the first token is thinking, streaming is writing, finished is done, failed is error, otherwise idle.
- Keep thinking short in real use; switch to writing on the first token.
- Use size 24 to 32 inline in a reply header or composer, 40 to 64 in a panel or on an image canvas, 96 to 160 in an empty state.
- The default accent tone follows the theme accent, including Arc's near-neutral grey. Use tone="sheet" for the classic pale ghost.
- Set name to the assistant's name so the label reads well. Turn announce on for one ghost only. Leave status unset only for showcases.
The full library index for assistants is at /llms.txt.