Docs
One component, <Mascot>. Everything below is a prop. Click any tile to
copy it.
Install
Svelte 5 is the only peer dependency.
pnpm add mascbob<script> import { Mascot } from 'mascbob'; let mood = $state('idle');</script><Mascot {mood} theme="og" onboop={() => (mood = 'love')} />Every list on this page is exported, so pickers you build stay in sync with the library:
import { MOODS, SHAPES, EYE_STYLES, ACCESSORIES, OUTFITS, SHOES, THEMES, REACTIONS } from 'mascbob';Moods 17
mood sets eyes, lids, mouth, blush, effect and hand pose. Switching tweens every
feature, so it morphs instead of cutting.
Colorways 13
Sneaker-style presets: a matte neutral body and one loud accent. Pass an object to override single colors on top of a preset.
<Mascot theme={{ base: 'noir', accent: '#ff4fd8', eye: '#00ffc6' }} />Shapes 10
Eyes 9
Accessories 20
Combine as many as you like: accessories={['halo', 'glasses']}.
Outfits & shoes
The full figure (body, on by default) starts bare. Use body={false} for a square, head-only avatar.
Reactions 7
With interactive on, it reacts to the pointer. Reactions override mood only briefly.
follow- Leans toward the cursor.
pet- Stroke back and forth over its head. Emits
{ type: "pet", strokes }. startleopt-in- Flick the cursor past it, fast. Emits
{ type: "startle", speed }. dizzy- Circle around it twice. Emits
{ type: "dizzy", direction }. shyopt-in- Get really close. Emits
{ type: "shy" }. tickle- Boop it again and again. Emits
{ type: "tickle", level, boops }. bored- Leave the mouse alone for 20 s. Emits
bored, thenwakewhen you come back.
<!-- defaults plus shy, without bored --><Mascot reactions={{ shy: true, bored: false }} /><!-- exactly these, and listen for them --><Mascot reactions={['pet', 'startle']} onreaction={(e) => console.log(e.type)}/>Voice
Set mood="talking" and feed an amplitude in 0..1 into level.
Without level, it babbles in fake syllables on its own. Stop the tracks and
close the AudioContext when you're done.
<script> import { Mascot } from 'mascbob'; let level = $state(0); async function listen() { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const ctx = new AudioContext(); const analyser = ctx.createAnalyser(); ctx.createMediaStreamSource(stream).connect(analyser); const samples = new Float32Array(analyser.fftSize); const tick = () => { analyser.getFloatTimeDomainData(samples); const rms = Math.sqrt(samples.reduce((sum, s) => sum + s * s, 0) / samples.length); const target = Math.min(1, Math.max(0, (rms - 0.01) * 7)); // open fast, close slower, so the mouth doesn't flicker level += (target - level) * (target > level ? 0.5 : 0.2); requestAnimationFrame(tick); }; tick(); }</script><button onclick={listen}>Talk</button><Mascot mood="talking" {level} />Styling with CSS
Every color is a CSS variable. Set them on the mascot or any ancestor, and they win over theme.
--mascbob-body-light- Body highlight
--mascbob-body-mid- Body base color
--mascbob-body-dark- Body shade
--mascbob-visor- Ink: soles, outlines
--mascbob-eye- Face print
--mascbob-cheek- Blush
--mascbob-accent- Accent plate and gear
--mascbob-sprout- Sprout accessory
.brand { --mascbob-body-light: …; --mascbob-body-mid: …; --mascbob-body-dark: …; --mascbob-visor: …; --mascbob-eye: …; --mascbob-cheek: …; --mascbob-accent: …; --mascbob-sprout: …;}Custom accessories
The accessory snippet draws your own SVG on top of the head, in the head's
200×200 coordinates. top is the crown's y and halfWidth its half width.
<Mascot> {#snippet accessory({ top })} <circle cx="100" cy={top - 10} r="8" fill="gold" /> {/snippet}</Mascot>Accessibility & motion
- With
interactiveit renders a real<button>, reachable by keyboard. Name it withlabel. motion="auto"followsprefers-reduced-motion: loops stop and moods switch without tweening. Force either withfullorreduced.- Mascots off screen pause their timers and animations, so a page full of them stays smooth.
All props
| Prop | Type | Default |
|---|---|---|
mood | Mood Facial expression, effect, hand pose and float speed. Changes morph smoothly. | 'idle' |
theme | ThemeName | { base?, ...colors } A colorway preset, or a preset with individual colors overridden. | 'og' |
shape | Shape Head silhouette. | 'capsule' |
eyes | EyeStyle Eye shape. Every style morphs through all moods. | 'round' |
accessories | Accessory[] Any combination of the built-in accessories. | [] |
body | boolean Full 2:3 figure with arms and legs. | true |
outfit | Outfit Clothing, only visible with | 'none' |
shoes | Shoes Footwear, only visible with | 'none' |
build | Build Body proportions with | 'standard' |
hands | boolean Hands that gesture with the mood. | true |
lookAt | 'pointer' | 'wander' | 'none' | { x, y } Where the eyes go. | 'pointer' |
level | number Mouth opening 0..1 while | – |
size | number | string Width in pixels, or any CSS length. | 160 |
float | boolean Idle hover animation. | true |
effects | boolean Particles around the head: mood effects (sparkles, hearts, zzz) and boop bursts. | true |
motion | 'auto' | 'full' | 'reduced'
| 'auto' |
interactive | boolean Renders a real | true |
label | string Accessible name. | 'Mascbob' |
onboop | () => void Click, tap or keyboard press. | – |
reactions | boolean | Reaction[] | { [reaction]: boolean } Pointer reactions. | true |
onreaction | (event: ReactionEvent) => void Fires when a reaction triggers. | – |
accessory | Snippet<[{ top, halfWidth }]> Custom SVG on top of the head, in the head's 200×200 coordinates. | – |
class | string Class on the root element. | '' |