Components · Learning
Hubert
The mascot. He reacts to the lesson: idle, thinking, correct, incorrect, celebrate.
Use it
<Hubert state={answerState} lookX={x} lookY={y} />How it moves
Hubert is the mascot. He reacts to the lesson, and his motion follows the personality like every other component: --ss-motion-energy scales how far and how big he moves, from 0 under reduced to 1 under playful. There are two versions with the same inputs: the drawing animated in CSS, and the Rive file hubert/hubert.riv, used when you pass rive={{ src, wasm }} (it needs the optional dependency @rive-app/canvas). The drawing shows until the Rive file is playing, and stays if it cannot load.
| State | When | What moves | Length |
|---|---|---|---|
| idle | Nothing is happening | Breathes. Blinks at uneven times, about every one and a half to five seconds, and sometimes twice. Every few seconds he glances somewhere and back: his pupils move and his stalks lean, each a little differently. When the app sets lookX and lookY, he looks there instead | Repeats |
| thinking | The app is checking or loading | Settles into his shell on the ground, his body dark inside it. His eyes look up and around, and his claws grip the rim, one tapping | Holds |
| correct | One right answer | A hop with a squash on take-off and landing, his right claw lifts in a wave, happy eyes | About half a second |
| incorrect | One wrong answer | He is upset: his smile turns to a frown, sad brows come down, his stalks droop and splay, he slumps with his claws hanging, a sweat drop slides down beside his head, and he shakes his head slowly. Then he recovers | About nine-tenths of a second |
| celebrate | The lesson is finished | A jump and a smaller second bounce, both claws up, happy eyes | About a second and a half |
Rules:
- Reactions play once and settle back to the idle pose. To play the same reaction twice, change the component's key.
- Calmer personalities make the reactions slower and smaller. Playful makes them quickest and biggest.
- Breathing, blinking, and glancing in idle, and the leg shuffle, are the only motion that repeats. Blinks and glances never come on a fixed beat. The shuffle runs in every state but thinking: each leg rocks at its hip, two shuffles to a breath, each a little out of step.
- A wrong answer gets a real reaction, but a short one: he is upset with the learner, not at them, and he recovers at once.
- One Hubert per screen.
Reduced motion: energy is 0, so he does not hop, tilt, breathe, blink, or glance. Each reaction is still clear, because his eyes and claws change pose in place: happy eyes for correct, soft eyes for incorrect, claws up for celebrate.
Screen readers: he is hidden by default, because the lesson already says what happened. Give him a label only when he carries meaning on his own.
Colors: the six character roles, --ss-color-character-shell, -shell-shade, -claw, -claw-shade, -body, -outline.
CSS variables used: --ss-motion-energy, --ss-motion-state-change-spring, --ss-motion-state-change-spring-duration, --ss-motion-state-change-duration.
Bring your own mascot
Hubert is the starting point, not a requirement. Any character built to the same contract drops into the same component: it reacts to the lesson, looks where it is told, and follows your colors and your motion personality, with no app code changes.
It can be any style: flat, hand-drawn, or 3D. And it can do more than the five states Hubert starts with, such as giving a hint, waving hello, dozing off when no one is around, or dancing when a streak grows.

