← all tickets

48 — Deck editor: stabilize browser-row column geometry

Status: fixed

What to fix: In the deck editor card browser, rows with the not impl (and/or banned) badge have their fixed-width columns shifted relative to normal rows, and the cost column can overflow on its own. Two causes, one symptom ("columns misaligned"):

1. Badges are content-sized elements injected mid-flex-row. Rows are div.browser-card-row with display: flex; gap: 8px (static/editor.html:237-244); the only flexible column is the title (flex: 1, editor.html:246-251). .unimpl-badge (static/editor.js:535-541, CSS editor.html:314-323) and .banned-badge (editor.js:543-551, CSS editor.html:324-332) have no width and no flex-shrink: 0, so on affected rows the title absorbs ~60px less and every fixed column (type 80px / cost 28px / faction 100px) sits at a different x-offset than on unaffected rows. 2. Cost column overflows. editor.js:520 renders card.cost + '💳' inside a hard width: 28px (editor.html:258-263); the emoji plus digits frequently exceeds 28px and collides with the faction column, independent of the badges.

Fix direction (agreed): give every row identical column geometry — a fixed-width status slot that is always rendered (empty/visibility: hidden when not applicable) holding the unimpl/banned badges, with flex-shrink: 0; and make the cost column wide enough for its content (or drop/resize the emoji). Apply the same discipline to the deck-side rows (makeDeckRow, editor.js:395-452, .deck-card-row editor.html:151-165), which carry the banned badge. Check the mobile breakpoint too: static/style.css:805-874 hides type/faction but keeps badges.

Definition of done: unimplemented/banned rows pixel-align with normal rows in the browser list and deck list, desktop and mobile widths; cost column never overflows for 1–2 digit costs.

Comments

Fixed. Both rows now have one flexible column (the title) and nothing else: .browser-card-status / .deck-card-status are always rendered and hold the not impl / banned badges, every fixed column is flex-shrink: 0, and the titles get min-width: 0 + ellipsis so a long card name yields instead of squeezing type/cost/faction. The cost column went 28px → 40px with nowrap (two digits plus the credit glyph is ~30px at 12px type).

One judgement call worth recording: the browser status column is sized for both badges at once (104px), which is free on desktop but a third of a 360px row. At the breakpoint it narrows to 52px and drops not implbanned is the badge that explains why the + button refuses — and sheetMeta now appends "not implemented" so the fact is still one tap away. That is the same trade the breakpoint already makes for the type and faction columns.

Follow-up (review). The first pass left one column conditional: the deck row's influence slot was appended only when the card actually cost influence, and it was min-width: 20px holding .repeat(inf × qty) — so its presence *and* its width varied, and the −/+ buttons still moved between rows. Influence is now a column like the others: always appended, width: 30px, empty for in-faction cards. The pips became a count plus a single pip (3●), because fifteen dots — three copies of a 5-influence card — is ~165px, a width no fixed column can hold; that is the same number-plus-glyph shape the browser's cost column uses, and the N influence tooltip is unchanged.

Guards: tests/test_static_pages.py, section "Row column geometry (ticket 48)" — the badges are appended to the status column and never to the row, every fixed column declares a width and flex-shrink: 0, the titles declare min-width: 0, the cost column is at least 2.5em wide, and the breakpoint keeps a fixed (narrower) status column. There is no browser in the suite, so these are source-level guards on the geometry contract, not pixel assertions.