Design captured 2026-06-15→17 from a brainstorm, then built. Sibling of Message Bottle — Design. Inventory is the shared layer both object-mechanics — bottles and quarantine-tape — pull items from. It’s the missing layer that governs holding, equipping, and (later) acquiring the things you use as you browse. The interaction is built and running; final art + a couple of follow-ups remain before merge (see the end).
The shape
Inventory is the satchel — one on-page surface, reachable on any page at any time, that holds your items and lets you arm one so your cursor becomes that tool (the tape-gun, the bottle-sealer). Today both object-mechanics assume their item is always available; inventory is the layer underneath that actually governs what you hold and how you wield it.
The whole problem is one tension: accessible on every page, but never naggy. It has to be there when you reach for it and gone when you’re not — no nagging, no clutter on someone else’s page. That balance drove every decision below.
It’s shared infrastructure, so it lands as its own branch (feat/inventory) that both feature branches build on — designing it inside either feature would create a backwards dependency. It extends the existing social-experiment scaffold rather than replacing anything: experiments register() their items into it, and read which tool is armed.
The four decisions
Four axes, each explored with interactive mockups before committing. Recorded with the rejected alternatives so the reasoning survives.
1 · Surface — where the satchel lives at rest
The resting state is the whole “not annoying” problem. Decision: a persistent but movable, collapsible bag that rests as an edge nub — a small sliver clinging to the nearest screen edge, nearly invisible until you reach for it. It’s the in-between of an always-visible corner badge (too present) and a keyboard-only summon (undiscoverable). The bag is always there (discoverable, no keyboard dependency) but draggable — you park it where you like — and collapsed small. Click the nub → the kit opens at the nub.
Rejected: persistent corner satchel (always on screen), edge peek-drawer (edge can be missed), invisible keyboard-only (hard to discover), ghosted hover-peek (subtlety over clarity).
Keyboard summon (secondary fast-path): built via the extension manifest commands API, not a content-script keydown — because a page can swallow key events, but a manifest command is browser-routed and fires above the page, so it works on every site and is user-remappable. It opens the kit at the cursor. Default Cmd+Shift+I on Mac (free there — DevTools is Cmd+Option+I); Windows/Linux needs a different default (Ctrl+Shift+I is DevTools there); Safari needs its own when supported.
2 · Aesthetic — how the open satchel looks
The open kit has to bridge two looks — quarantine-tape’s industrial caution-tape and bottles’ found-object — inside the extension’s warm-linen design system. Decision: a cozy-game inventory, browser-native (“The Satchel”).
References pulled from Stardew Valley (carved warm frame, recessed slot wells) and Animal Crossing (puffy rounded slots, playful patterned backing, bounce) — translated into web materials, not pixel art. The kit is a chunky carved warm frame with a worn paper/canvas texture layered in (so it reads like a real, slightly-weathered kit), a dotted backing tray, and a two-row grid of puffy rounded pockets. Item icons are background-removed photo cut-outs that break out of their pockets (the FigJam-pen “object pokes out of the slot” effect), with a slight tilt and a soft shadow. Springy bounce on hover; the armed pocket glows teal and gently pulses; empty pockets are dimpled and dashed — visibly room to grow as you pick items up. Count color encodes the tier: teal ∞ for permanent tools, gold count for finite found items.
Arc to get here: a 5-treatment survey → narrowed to Field Kit + Hotbar (both leave room to grow) → a “Kid Pix” toy skin (rejected: too loud) → plain warm-linen (rejected: too muted) → this cozy-but-warm synthesis. The collapsed bag uses the Tiny Internets backpack illustration.
3 · Equip — from “have item” to “armed cursor”
Decision: click to arm. Click a pocket and the tool arms instantly; Esc (or clicking the armed item again) un-arms. Matches the quarantine-tape spec’s already-locked flow. Rejected: drag-onto-page, hold-to-charge.
Wielded cursor: when armed, your normal cursor stays visible and a tiny version of the item rides right beside it — so it reads as the cursor holding the tool, not a replaced cursor. The item follows the pointer; disarm removes it.
4 · Economy — how you get & refill items
Decision: defer it, and decide per-item. The model has two tiers — system tools (the tape-gun, the bottle-sealer) are permanent and infinite, and found items are scarce. Because system tools are infinite, bottles and tape both ship fully usable with no economy at all — so the economy is genuinely deferrable and shouldn’t block either feature. When the first scarce/found item is designed, its acquisition is chosen then (candidates: found-on-pages — most on-brand; daily ration as a floor; earned-by-browsing as a tuning knob).
Why the hook still exists: if tape ever becomes scarce, a per-item cost reinforces quarantine-tape’s “many independent hands” anti-brigading mechanic — one person can’t blanket the web if marking costs a finite thing.
Architecture
Inventory is infrastructure, not an experiment. It initializes once — before the experiments run — and exposes an InventoryAPI to each experiment through the shared GlobalFeatureDeps. An experiment, in its init, calls deps.inventory.register({…}) to declare its tool(s) and subscribes to arm/disarm changes to enter/exit “tool mode.” Inventory owns which tool is armed; the experiment owns using it (taping a page, sealing a bottle).
The InventoryAPI surface: register(item), list(), arm(itemId), disarm(), getArmed(), onArmedChange(cb), count(itemId) (Infinity for system tools). Held items + the armed tool are personal, per-browser (local storage — not synced). Found-item placements, when they exist, will be collective per-page via a playhtml pageData channel, exactly like bottles/tape (keyed map, last-write-wins, written only from explicit user events per the CRDT rules).
The on-page satchel is injected via the extension’s Shadow-DOM helpers so host-page CSS can’t bleed in or out; the host container is full-viewport pointer-events:none so the page stays interactive, and only the nub/kit capture events.
What’s built (status: implemented, on feat/inventory)
The full v1 is implemented and tested — system-tools-only, no economy yet. Pure core (registry, armed-state, held-store, manager → the InventoryAPI) with unit tests; the cozy-Satchel surface (draggable edge-nub → opens the textured kit), click-to-arm, the wielded cursor, and the manifest keyboard command. GlobalFeatureDeps gained inventory; it’s built before experiments run; bottles registers a “message-bottle” item (the one real item today — the satchel shows it plus empty pockets to grow into). All extension tests pass; the extension builds clean and the assets ship.
The sequencing: (1) feat/inventory lands the satchel + API (this). (2) bottles and quarantine-tape rebase onto it, register their tools, drop their “item always available” assumption. (3) the first found item (future) brings its acquisition model + the collective placement channel.
Before merge
Replace placeholder item icons with final cut-out art — bottle.png is a placeholder; the eventual tape item wants a yellow caution-tape roll (not grey duct tape). Decide whether the Tiny Internets backpack is final. In-browser human verification — drag / arm / wield / keyboard on a real page. (Automated build + a Playwright recording of the flow are done; the OS-cursor-beside-item read can’t be captured in a Playwright video, so confirm live.) Follow-up (can be post-merge): arm-gate bottles’ seal flow on the armed item + auto-disarm after seal — the same hookup quarantine-tape will reuse. Per-OS keyboard keys: Windows/Linux default (avoid Ctrl+Shift+I = DevTools); Safari key when supported. Full engineering spec + the interactive mockups live in the repo (internal-docs/specs/2026-06-15-inventory-design.md, gitignored). This page is the durable design record.