# Contributing to `@jamunlabs/gameu-shell-v1`

This package is the canonical home for CSS that styles the gameu TV
lobby and V2 phone controller chrome. There are two contribution
flows depending on whether you're tweaking a rule or adding a new
one.

## Tweaking an existing rule

Fast path. No build, no WASM, no Leptos involved.

1. Open `crates/gameu-ui/preview/lobby-carousel.html` in a browser
   (it `<link>`s `src/lobby.css` directly). For controller iteration,
   any HTML harness that `<link>`s `src/controller.css` works the
   same way.
2. Edit `packages/gameu-shell/src/lobby.css` or
   `packages/gameu-shell/src/controller.css`.
3. Reload the preview tab. Iterate.
4. Once the design is right, open the lobby normally
   (`cargo xtask run desktop` or `trunk serve` inside
   `crates/gameu-ui/`) and confirm it still looks right. The
   extraction-gate tests in `crates/gameu-runtime/tests/` enforce
   that the in-tree HTML files only `<link>` this package — no
   parallel inline stylesheet to keep in sync.

## Adding a new rule

Use this when you're styling a brand new chrome surface.

1. Add the rule to whichever file the surface belongs to:
   - **Lobby surfaces** (TV side): `src/lobby.css`. Use a class
     prefixed with `.lobby-` or scoped by an existing chrome class
     (`.qr-card .new-thing`) so the rule can't leak to non-lobby
     consumers.
   - **Controller surfaces** (phone side): `src/controller.css`.
     Use id selectors (`#root`, `#status`, `#pad`) for shell
     layout, or class selectors prefixed `.controller-` for
     explicitly-named regions.
2. If the rule depends on a design token that doesn't exist yet
   (`--gameu-cyan` etc), the token belongs in `@jamunlabs/gameu-sdk`'s
   `gameu-theme-v1.css`, **not here**. Token additions are a
   separate PR against that package.
3. The Leptos `view!` macro in `crates/gameu-ui/src/components.rs`
   (lobby) or the runtime markup in
   `crates/gameu-runtime/src/join/page-v2.html` (controller) uses
   the class as a string literal. Keep them in sync — there's no
   compile-time linker between Rust strings and CSS class names.
4. Bump `package.json#version`. Patch for fixes, minor for
   additions, major (rename) → consider whether you actually want a
   new `gameu-shell-v2` package instead.

## When in doubt: which file?

| Surface | File |
|---|---|
| Anything visible in the TV browser at `/` | `src/lobby.css` |
| Anything visible in the phone browser at `/join/` | `src/controller.css` |
| Universal primitive used by BOTH (`.gameu-button`, `.gameu-card`, `.gameu-pill`, `.gameu-portrait`, `.gameu-corner`, `.gameu-headline`, `.gameu-eyebrow`) | `gameu-theme-v1.css` (separate package) |
| Design token (`--gameu-navy`, `--gameu-cyan`, fonts, shadows) | `gameu-theme-v1.css` (separate package) |

If a class genuinely needs to be shared between lobby and controller
chrome (other than the universal primitives that already live in the
theme), promote it to the theme rather than duplicate. The only
shared chrome elements today are body-level (halftone background,
font, navy color) — and even those have surface-specific
overrides for layout (`display: grid` on lobby vs `display: flex`
inside `#root` on controller).

## What does NOT belong in this package

- **Design tokens.** Palette, fonts, shadows, radii, strokes →
  `@jamunlabs/gameu-sdk`'s `gameu-theme-v1.css`.
- **Reusable primitives** that aren't surface-specific
  (`.gameu-card`, `.gameu-button`, `.gameu-pill`, `.gameu-portrait`)
  → the theme package.
- **Per-game CSS.** Each game ships its own scoped styles inside its
  iframe; that surface is per-game by design and intentionally
  isolated from the shell chrome.
- **Per-template controller render styles.** Today
  `crates/gameu-runtime/src/join/render.ts` builds template DOM
  with inline `style.cssText` strings (~30 sites across single_tap
  / board_grid / color_picker / action_picker / idle / custom).
  Migrating those into class-based styles in `controller.css` is a
  planned follow-up. Until that migration lands, do not add rules
  here that would conflict with the inline-style precedence
  ordering.

## Publishing

After merge, a maintainer runs:

```sh
cd packages/gameu-shell
npm publish
```

The published tarball flows to jsdelivr's CDN within minutes. Pinned
consumers (live kiosks running an older `gameu-shell-v1@x.y.z`) keep
their version until they choose to update; `@latest` consumers see
the new file on the next cache miss.
