diff --git a/.claude/skills/deckbuilder-shape/references/architecture.md b/.claude/skills/deckbuilder-shape/references/architecture.md index ef4eb3f..bcf07d8 100644 --- a/.claude/skills/deckbuilder-shape/references/architecture.md +++ b/.claude/skills/deckbuilder-shape/references/architecture.md @@ -42,14 +42,15 @@ while — this is a snapshot, not a guarantee. hardcoded to any one feature or shape name — see `views/gameEditor/__tests__/GameEditor.test.tsx` and `deckBuilder/__tests__/deckRules.test.ts` for the behavior this guarantees. -- **Symbols render tiny.** `CardSvg` places each symbol in a - `SYMBOL_SIZE = MAIN_VIEWPORT_SIZE / 3 - 5 = 35` unit box inside the 120-unit - card viewport — a 0.29x downscale of the shape's own `0 0 120 120` space — - and `src/components/game/card/index.css` caps the card at `max-height: 10vh`, - so a card is ~90px (~60px on a short window) and a symbol lands near 26 device - pixels. Combined scale from shape units to screen pixels: **~0.22x**. This is - the fact `references/iconography.md` exists to address, and why - `definePathShape`'s `strokeWidth="1"` is effectively invisible. +- **Symbols render small.** `CardSvg` places each symbol in a + `SYMBOL_SIZE = MAIN_VIEWPORT_SIZE / 3 - 2 = 38` unit box inside the 120-unit + card viewport — a 0.317x downscale of the shape's own `0 0 120 120` space — + and `src/components/game/card/index.css` sizes the card at + `min(26vw, 20vh)`, so a card is ~86px on a 330px-wide phone (~180px on + desktop) and a symbol lands near 27 device pixels at the small end. Combined + scale from shape units to screen pixels on a phone: **~0.23x**. This is the + fact `references/iconography.md` exists to address, and why `definePathShape`'s + `strokeWidth="1"` is effectively invisible. - Test runner is CRA/Jest (`yarn test` / `npm test`, both wrap `react-app-rewired test`). Tests assert markup, never appearance — the visual gate is `yarn render:shape ""` (`scripts/render-shape.mjs`), which diff --git a/.claude/skills/deckbuilder-shape/references/iconography.md b/.claude/skills/deckbuilder-shape/references/iconography.md index 5b182ee..6a9fe63 100644 --- a/.claude/skills/deckbuilder-shape/references/iconography.md +++ b/.claude/skills/deckbuilder-shape/references/iconography.md @@ -5,24 +5,25 @@ deliberately not being restyled — they are what the rules exist to improve on, not the pattern to copy. -## Why: a symbol is ~26 device pixels +## Why: a symbol is ~27 device pixels on a phone -Three scale reductions stack between the coordinates you type and what a player sees: +Two scale reductions stack between the coordinates you type and what a player sees: | Step | Where | Effect | | --- | --- | --- | -| Shape space → symbol box | `CardSvg.tsx` — `SYMBOL_SIZE = 120/3 - 5 = 35` | **0.29x** | -| Card viewport → card element | typical card ≈ 90px for a 120-unit viewport | **0.75x** | -| Combined | | **≈0.22x** | +| Shape space → symbol box | `CardSvg.tsx` — `SYMBOL_SIZE = 120/3 - 2 = 38` | **0.317x** | +| Card viewport → card element | phone card ≈ 90px for a 120-unit viewport | **0.75x** | +| Combined | | **≈0.23x** | -Plus `src/components/game/card/index.css` caps the card at `max-height: 10vh`, so on a -short window a card is ~60px and a symbol lands near **20px**. +`src/components/game/card/index.css` sizes the card at `min(26vw, 20vh)`, so the card +tracks the viewport: ~86px on a 330px-wide phone (symbol ≈ **27px**) up to ~180px on +desktop (symbol ≈ **57px**). Design against the phone figure — it is the one that hurts. -Concretely, in the default `0 0 120 120` shape space: +Concretely, in the default `0 0 120 120` shape space, at a 90px card: -- `stroke-width="1"` (what `definePathShape` emits) → **0.22px**. Invisible. -- `stroke-width="6"` → **1.3px**. The thinnest line that reliably survives. -- An 8-unit detail → **1.7px**. About the floor for anything you want seen. +- `stroke-width="1"` (what `definePathShape` emits) → **0.23px**. Invisible. +- `stroke-width="6"` → **1.4px**. The thinnest line that reliably survives. +- An 8-unit detail → **1.8px**. About the floor for anything you want seen. Set is a game of spotting differences fast. A symbol that needs squinting is a broken symbol, no matter how good it looks zoomed in. diff --git a/.claude/skills/deckbuilder-shape/scripts/card-markup.tsx b/.claude/skills/deckbuilder-shape/scripts/card-markup.tsx index 3e306c7..e50ba80 100644 --- a/.claude/skills/deckbuilder-shape/scripts/card-markup.tsx +++ b/.claude/skills/deckbuilder-shape/scripts/card-markup.tsx @@ -13,15 +13,13 @@ */ import * as React from "react"; import * as ReactDOMServer from "react-dom/server"; -import { CardSvg, MAIN_VIEWPORT_SIZE } from "../../../../src/deckBuilder/CardSvg"; +import { CardSvg, MAIN_VIEWPORT_SIZE, SYMBOL_SIZE } from "../../../../src/deckBuilder/CardSvg"; import { CardData, DEFAULT_CARD, NUMBERS, ROTATIONS, YOLKS } from "../../../../src/deckBuilder/features"; import { COLOR_SETS, ColorName, clampColorSet } from "../../../../src/deckBuilder/features/colors"; import { PATTERN_DEFS, PATTERN_NAMES, PatternName } from "../../../../src/deckBuilder/features/patterns"; import { SHAPE_NAMES, SHAPE_REGISTRY, ShapeName } from "../../../../src/deckBuilder/shapes"; import { Rotation } from "../../../../src/deckBuilder/types"; -/** Mirrors CardSvg's own symbol box: 1/3 of the card viewport, less a margin. */ -const SYMBOL_SIZE = MAIN_VIEWPORT_SIZE / 3 - 5; const DEFAULT_VIEW_BOX = "0 0 120 120"; /** Colors worth eyeballing: a mid hue, a dark hue, and pure black (the worst diff --git a/.claude/skills/deckbuilder-shape/scripts/render-shape.mjs b/.claude/skills/deckbuilder-shape/scripts/render-shape.mjs index 6a5b228..1464f49 100644 --- a/.claude/skills/deckbuilder-shape/scripts/render-shape.mjs +++ b/.claude/skills/deckbuilder-shape/scripts/render-shape.mjs @@ -25,9 +25,9 @@ const HERE = path.dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = path.resolve(HERE, "../../../.."); const MARKUP_SCRIPT = path.join(HERE, "card-markup.tsx"); -/** Card sizes to eyeball, bracketing the app's real `max-height: 10vh` card. */ -const TRUE_SIZES = [60, 90, 140]; -/** The size the checks are stated against — a typical real card. */ +/** Card sizes to eyeball, bracketing the app's real `min(26vw, 20vh)` card. */ +const TRUE_SIZES = [90, 130, 180]; +/** The size the checks are stated against — the phone card, the worst case. */ const REFERENCE_CARD_PX = 90; const ZOOM_PX = 240; const VARIANT_PX = 90; diff --git a/src/components/game/board/index.css b/src/components/game/board/index.css index 90980e7..721bf0c 100644 --- a/src/components/game/board/index.css +++ b/src/components/game/board/index.css @@ -2,5 +2,6 @@ display: grid; grid-template-columns: auto auto auto; justify-content: center; + gap: 4px; } diff --git a/src/components/game/board/index.tsx b/src/components/game/board/index.tsx index 0d2a6eb..3570dd8 100644 --- a/src/components/game/board/index.tsx +++ b/src/components/game/board/index.tsx @@ -25,7 +25,7 @@ export const Board = (props: Props) => { {props.board.map((id: string) => { return (