Files
jackboxpartypack-gamepicker/docs/superpowers/specs/2026-08-23-sticker-visibility-toggle-design.md

5.1 KiB

Sticker Visibility Toggle — Design Spec

Overview

Add a 3-way segmented toggle to the Picker's Game Session screen that lets admins control sticker layer visibility on the downstream vote-app overlay. The three modes are Auto (default), Show, and Hide, matching the downstream API contract defined in docs/external-downstream-stickers.md.

Motivation

The vote-app overlay shows stickers that voters purchase during a session. By default, stickers follow poll state (visible during polls, hidden between). Admins need the ability to override this — force-showing stickers between rounds for a "sticker wall showcase" or force-hiding them during a busy poll for a cleaner view.

Modes

Mode Behavior
auto Default — stickers visible during polls, hidden between polls
show Force stickers visible regardless of poll state
hide Force stickers hidden regardless of poll state

When a new poll starts, the downstream vote-app auto-resets mode to auto and sends a stickers.mode event.

Architecture

State ownership

  • stickerMode state (useState('auto')) lives in the Picker component, where the toggle UI renders.
  • A stickerSendRef ref is created in Picker and passed to SessionInfo, which populates it with a send function once the WebSocket connection is authenticated.

Props

SessionInfo receives two new props (following the existing pattern for setPollActive, setLeadingGame, etc.):

  • setStickerMode — setter called when inbound stickers.mode WS events arrive.
  • stickerSendRef — ref that SessionInfo populates with (mode) => ws.send(...).

WebSocket integration

All communication uses the existing /api/sessions/live WebSocket connection managed by SessionInfo.connectWs.

Receiving (stickers.mode events):

if (message.type === 'stickers.mode') {
  setStickerMode(message.mode);
  return;
}

Added alongside the existing poll.* and game.* handlers in the ws.onmessage callback.

Sending (stickers.setMode commands):

After successful auth and subscribe, SessionInfo populates the ref:

stickerSendRef.current = (mode) => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'stickers.setMode', data: { mode } }));
  }
};

The ref is cleared on disconnect/cleanup.

No HTTP fallback. The default auto state is correct on fresh load, and the WS connection handles all sync from that point.

UI Design

Placement

The sticker toggle renders in Picker's right column (md:col-span-2), above the poll leader indicator and poll control cards. It is always visible when an active session exists, regardless of poll state.

┌─────────────────────────────────────────────┐
│  Stickers       [Auto] [Show] [Hide]        │
└─────────────────────────────────────────────┘
   ↕ poll leader indicator (conditional)
   ↕ currently playing card (conditional)
   ↕ poll control card (conditional)
   ↕ SessionInfo games list

Control style

A compact single-row card with:

  • Label ("Stickers") on the left.
  • 3-way segmented control on the right, matching the Drawing/Family Friendly filter pattern (three adjacent buttons, active one highlighted).

Segment colors

Segment Active background
Auto Neutral gray (bg-gray-500)
Show Green (bg-green-500)
Hide Red (bg-red-500)

Inactive segments use a muted/transparent style with border, same as the existing filter toggles.

Auto-reset pulse

When a stickers.mode event arrives with mode: "auto" and the current local state is not already auto, a brief CSS pulse animation plays on the toggle card:

  • A gentle border/background highlight flash that fades over ~2 seconds.
  • Implemented via a CSS @keyframes animation toggled by a transient state flag.
  • The flag is set on receiving an auto-reset event and cleared after the animation completes.

Interaction behavior

  • Clicking a segment calls stickerSendRef.current(mode) and optimistically updates local state.
  • If the subsequent stickers.mode event returns a different value, state corrects to match.
  • The toggle is visually disabled (reduced opacity, no pointer events) if the WebSocket is not connected.

Files changed

File Change
frontend/src/pages/Picker.jsx Add stickerMode state, stickerSendRef ref, render sticker toggle card, pass new props to SessionInfo
frontend/src/pages/Picker.jsx (SessionInfo) Accept setStickerMode and stickerSendRef props, handle stickers.mode WS events, populate send ref

Scope boundaries

  • In scope: Frontend toggle UI + WS message send/receive in the game picker.
  • Out of scope: Backend relay of stickers.setMode messages to the vote-app (assumed handled by existing infrastructure or a separate task). Vote-app sticker rendering (already implemented downstream).