Reference

Animated boxes

Ghosty's box robots for your site or app — states, shapes, screens, colours, themes and a summary row you reorder by dragging. Package @ghostystudio/boxes.

Updated 2026-10-11

Every Ghosty box is a TV-headed robot: its face says what it is doing (asleep, listening, working, failing), it follows the mouse and hops when clicked. It is the same one you see in Space boxes, and we publish it as a free package (MIT) so you can use it in your own pages and apps: @ghostystudio/boxes.

Install

html
<script src="https://unpkg.com/@ghostystudio/boxes/dist/boxes.iife.js"></script>
<ghosty-box state="happy"></ghosty-box>

It works in plain HTML and in any framework: it is a custom element. React gets two components: <RobotHead> (one box) and <GhostyBoxes> (the summary row). They ship with "use client", so in Next.js (App Router) you can use them as they are inside a Server Component. Every example below has a React tab.

States

What the box is doing. Each state has its own face, motion and light.

idle
thinking
searching
listening
speaking
working
happy
success
error
sleeping
html
<ghosty-box state="idle" size="96" title="idle"></ghosty-box><ghosty-box state="thinking" size="96" title="thinking"></ghosty-box><ghosty-box state="searching" size="96" title="searching"></ghosty-box><ghosty-box state="listening" size="96" title="listening"></ghosty-box><ghosty-box state="speaking" size="96" title="speaking"></ghosty-box><ghosty-box state="working" size="96" title="working"></ghosty-box><ghosty-box state="happy" size="96" title="happy"></ghosty-box><ghosty-box state="success" size="96" title="success"></ghosty-box><ghosty-box state="error" size="96" title="error"></ghosty-box><ghosty-box state="sleeping" size="96" title="sleeping"></ghosty-box>

Shapes

rectangle
square
circle
hexagon
html
<ghosty-box shape="rectangle" size="110" title="rectangle"></ghosty-box><ghosty-box shape="square" size="110" title="square"></ghosty-box><ghosty-box shape="circle" size="110" title="circle"></ghosty-box><ghosty-box shape="hexagon" size="110" title="hexagon"></ghosty-box>

Seasonal ones too: tree (Christmas) and pumpkin (Halloween).

Screen

face picks what it shows: eyes, a terminal or nothing. With the terminal, screen-icon adds an icon: mail, inbox, build, ci, preview, desktop or web (register your own with registerScreenIcon).

eyes
terminal
mail
inbox · working
off
html
<ghosty-box face="eyes" size="110" title="eyes"></ghosty-box><ghosty-box face="terminal" state="listening" size="110" title="terminal"></ghosty-box><ghosty-box face="terminal" screen-icon="mail" size="110" title="mail"></ghosty-box><ghosty-box face="terminal" screen-icon="inbox" state="working" size="110" title="inbox · working"></ghosty-box><ghosty-box face="off" size="110" title="off"></ghosty-box>

Colours, themes and finishes

A theme paints the box with one word; anything you set by hand wins over it. color is the shell, trim-color the ears and screen-color the LEDs. texture changes the finish and wear (0–1) adds wear and tear.

ghosty
mailmask
mailmaskGreen
by hand
metal
wood · wear
html
<ghosty-box theme="ghosty" size="110" title="ghosty"></ghosty-box><ghosty-box theme="mailmask" size="110" title="mailmask"></ghosty-box><ghosty-box theme="mailmaskGreen" size="110" title="mailmaskGreen"></ghosty-box><ghosty-box color="#7c3aed" trim-color="#fbbf24" screen-color="#fde68a" size="110" title="by hand"></ghosty-box><ghosty-box color="#8a8f98" texture="metal" size="110" title="metal"></ghosty-box><ghosty-box color="#a0522d" texture="wood" wear="0.6" size="110" title="wood · wear"></ghosty-box>

Your own theme: registerTheme('mybrand', { color: '#…', trimColor: '#…', screenColor: '#…' }), then theme="mybrand".

A summary row you reorder by dragging

<ghosty-boxes> is the summary row: title, counter, one box per item with its name, state and dots, and free slots switched off. Grab a box: it lifts, swings and smiles; drop it and it lands with a hop. Keyboard too: Alt + ← / →.

html
<ghosty-boxes theme="mailmask" data='{"title":"Mailboxes","count":"4/6","sub":"12 unread · 1 importing","items":[{"id":"hello","name":"hello@","label":"8 unread","tone":"talk","dots":{"on":8,"cap":10},"head":{"state":"happy"}},{"id":"sales","name":"sales@","label":"importing","tone":"work","dots":{"on":3,"cap":10},"head":{"state":"working","screenIcon":"inbox"}},{"id":"support","name":"support@","label":"4 unread","tone":"talk","dots":{"on":4,"cap":10},"head":{"state":"listening"}},{"id":"invoices","name":"invoices@","label":"almost full","tone":"error","dots":{"on":10,"cap":10},"head":{"state":"error"}},{"id":"f1","name":"free","off":true},{"id":"f2","name":"free","off":true}]}'></ghosty-boxes>

Data can also come from code, and the new order comes back in an event:

js
const row = document.querySelector('ghosty-boxes');row.data = { title: 'Mailboxes', count: '4/6', items: [/* … */] };row.addEventListener('box-reorder', (e) => save(e.detail.order)); // { id, from, to, order }row.addEventListener('box-open', (e) => open(e.detail.id));

Each item has id, name, and optional label, tone (work, talk, error, sleep, calm), dots ({ on, cap }), off (a fixed free slot), group (it only moves within its group) and head (its box's props). The card's colours come from CSS variables on .gbx: --gbx-bg, --gbx-border, --gbx-fg, --gbx-muted, --gbx-work, --gbx-talk, --gbx-error, --gbx-dot, --gbx-radius.

<ghosty-box> attributes

AttributeValuesDefault
themeghosty, mailmask, mailmaskGreen or your own—
stateidle, thinking, searching, listening, speaking, working, happy, success, error, sleepingidle
shaperectangle, square, circle, hexagon, tree, pumpkinrectangle
sizepx160
color · trim-color · screen-colorhex or rgb()blue · steel · white
faceeyes, terminal, offeyes
screen-iconwith terminal: mail, inbox, build, ci, preview, desktop, web—
texturegrain, smooth, paper, fabric, metal, leather, wood, ribsgrain
wear0–10
speedmultiplier; 0 freezes it1
seed0–1, so a row does not blink in unisonrandom
paused · interactive="false" · antenna="false" · floor-shadow="false"flags—

In React they are the same props in camelCase (trimColor, screenIcon, floorShadow), with numbers and flags in braces (size={96}, antenna={false}). It respects prefers-reduced-motion. Attribute changes show up instantly.

The robot is by Fayaz Ahmed (MIT), with states, shapes, screens, themes and finishes added by Ghosty.