Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
920d5ec
docs(specs): draft spec 078, a mana bar label fits its segment or isn…
plainprogrammer Nov 12, 2026
06471e6
docs(specs): spec 078 0.2.0, spec review round 1 taken
plainprogrammer Nov 12, 2026
b4334e6
docs(specs): spec 078 0.2.1, spec review round 2 taken
plainprogrammer Nov 12, 2026
952477e
docs(specs): plan 078, a mana bar label fits its segment or isn't dra…
plainprogrammer Nov 12, 2026
0474d8f
docs(specs): plan 078, plan review round 1 taken
plainprogrammer Nov 12, 2026
da6dbb2
test(deck shape): a mana label fits its segment or isn't drawn, red f…
plainprogrammer Nov 12, 2026
acf5095
fix(design system): a mana bar label fits its segment or isn't drawn …
plainprogrammer Nov 12, 2026
cc4d7bb
docs(design system): DeckShape says a mana label that doesn't fit isn…
plainprogrammer Nov 12, 2026
a3e1714
docs(architecture): the panel's mana label fit rule and the closed #3…
plainprogrammer Nov 12, 2026
9ff0387
docs(specs): spec 078 before and after screenshots of the mana bar
plainprogrammer Nov 12, 2026
6f1280a
docs(specs): spec 078 verification, with the maintainer's phone check…
plainprogrammer Nov 12, 2026
de80c52
docs(specs): spec 078 implementation review recorded (SPEC-ALIGNED WI…
plainprogrammer Nov 12, 2026
dabd6ad
docs(architecture): distil spec 078 into collection and lots
plainprogrammer Nov 12, 2026
41756d2
docs(specs): mark spec 078 frozen by the collection and lots area doc…
plainprogrammer Nov 12, 2026
cc5db79
docs(specs): spec 078 verification records the distillation
plainprogrammer Nov 12, 2026
e8042b3
chore(memory): capture session learnings (spec 078)
plainprogrammer Nov 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions app/assets/stylesheets/collector/additions.css
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,20 @@ body:has(.c-locations) .c-appbar__back { min-height:var(--touch-floor); min-widt

/* ---------- DeckShape mana bar (spec 060): one bar split by colour, the only chart that may use the mana fills ---------- */
/* README Charts rule: every segment has its letter and count (the caption carries a segment under 10%, which the markup
marks narrow), and a 1px line-strong outline and divider, because the white and black fills vanish on the ground. */
marks narrow, and one whose label does not fit (spec 078)), and a 1px line-strong outline and divider, because the white
and black fills vanish on the ground. */
.c-manabar { display:flex; height:32px; margin:0; padding:0; list-style:none; border:1px solid var(--line-strong); border-radius:var(--radius-sm); overflow:hidden; }
.c-manabar__seg { flex:var(--n) 1 0; min-width:0; display:flex; align-items:center; justify-content:center; background:var(--mana-c); color:var(--on-mana-c);
/* Spec 078 (#330): a label is drawn only if it fits its segment, on one line, whatever its length or the bar's width. The segment is a
wrapping flex container whose first item, the ::before, is zero wide and as high as the segment, so it always stays on the first line
and gives that line the segment's height (the label stays centred); the label does not shrink. The browser's line breaking then keeps
the label on that line only if it fits the segment's content width, and otherwise moves it to a second line below the segment, where
overflow:clip hides it (clip, not hidden, so the segment is no scroll container and find-in-page or scrollIntoView cannot scroll to
it). No number about the font: the rule follows the rendered label. The 10% narrow class below stays: a segment under 10% has no
label at all; the caption carries the count of a colour whose label is not drawn. */
.c-manabar__seg { flex:var(--n) 1 0; min-width:0; display:flex; flex-wrap:wrap; align-content:flex-start; align-items:center; justify-content:center; overflow:clip; background:var(--mana-c); color:var(--on-mana-c);
font:600 12px/16px var(--font-mono); }
.c-manabar__seg::before { content:""; height:100%; }
.c-manabar__seg > span { flex:none; }
.c-manabar__seg + .c-manabar__seg { border-left:1px solid var(--line-strong); }
.c-manabar__seg--w { background:var(--mana-w); color:var(--on-mana-w); }
.c-manabar__seg--u { background:var(--mana-u); color:var(--on-mana-u); }
Expand Down
8 changes: 5 additions & 3 deletions docs/architecture/collection-and-lots.md

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion docs/design-system/components/DeckShape.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ The `details` copy, shown below 640px of the page container, holds the same thre
- One bar, 32px high, with a 1px `line-strong` outline and `radius-sm` corners, split into segments whose widths are proportional to their symbol counts (`--n`). Consecutive segments are divided by a 1px `line-strong` border.
- Each segment fills with its `mana-*` token and its text is the matching `on-mana-*` token, the same in both themes. The segment's label is its letter and count ("U 44"), in mono. From 1,000 the count is abbreviated ("W 2.6k": six characters, about 43.5px on a Mac and 44.7px on Linux, which fits one line in the narrowest five-equal-segment bar at a 320px viewport); the caption and the `aria-label` keep the exact count (spec 077 decision 2).
- A segment under 10% of the symbols adds `c-manabar__seg--narrow`, which hides its label: the server sets the class, with no JavaScript. The caption carries that segment's count.
- **A label that does not fit is not drawn** (spec 078 decision 1, #330). The server's 10% class is a floor; above it the browser decides from the label's rendered width. A segment is a wrapping flex container with `overflow:clip`; its first flex item is a zero-width `::before` as high as the segment, and the label does not shrink (`flex:none`). The label stays on the first line only if it fits the segment's content width (its box less the 1px divider); otherwise flex line breaking moves it to a second line below the segment, where `overflow:clip` hides it. So a label is on one line inside its segment or is not drawn, at any width of the bar and for any label length ("U 44", "W 999", "W 2.6k"), with no number about fonts in the rule, and a label exactly as wide as its content width is drawn. The caption and the bar's `aria-label` carry every colour's exact count, so nothing is lost; the caption carries the count of a colour whose label is not drawn as well as one under 10%. A clipped label stays in the DOM (the bar is `role="img"`): find-in-page may match it, and `overflow:clip` means nothing scrolls to show it.
- **Browsers:** the rule relies on flex line breaking (an item moves to a new line when the line's items exceed the container, and the first item of a line always stays) and on a percentage height in a stretched flex item (the `::before`'s `height:100%`). Safari 17.2, Chrome 120 and Firefox 121 and later are the floor (`allow_browser versions: :modern`) and all have it. The suite runs it in headless Firefox; a check on a phone (WebKit) is a manual item of spec 078's verification. A label within half a pixel of its content width may be drawn on one platform and clipped on another (a Mac and the Linux runner draw the mono face a pixel apart).
- The caption (`c-manacap`) names every colour present with its count and its percentage: "Blue 44 · 73% · Red 16 · 27%". In a five-colour deck whose white segment is under 10%, that segment shows no label and the caption still gives white's count and percentage. The caption is a run of `c-manacap__item` spans, "Blue 44 · 73% ·" and, last, "Red 16 · 27%", separated by one space, each `white-space:nowrap`: a line breaks only between colours, so no line begins with "·" and a name never parts from its count; the paragraph's text is the same string as before (spec 077 decision 4; `MTG::DeckShape#caption_items`).
- The bar is `role="img"` and its `aria-label` reads the counts ("Coloured mana symbols in Main: blue 44, red 16").
- With no coloured symbol the part has no bar and no caption and says "No coloured mana symbols." in a `c-manacap` paragraph (spec 069 decision 14).
Expand All @@ -85,7 +87,7 @@ The `details` copy, shown below 640px of the page container, holds the same thre
- The panel itself is `surface-raised` with a 1px `line` border, `radius-md` corners, `space-4` padding and `space-6` between its parts. The title is the sans family at 20px and weight 600; the scope line is mono and `ink-muted`.
- Fills and outlines hold 3:1 on `surface`, `surface-raised` and `surface-sunken` in both themes (the tokens' contrast is guarded by spec 057 AC-2.3).

**Width.** The panel works in 238px, the space inside its border and padding at a 320px viewport (a phone's 320px less 16px body margin, 32px page padding, 2px border and 32px panel padding), and every count, the legend, the types and the mana labels fit with no horizontal overflow at the worst-case counts of spec 069's fixture (spec 077 AC-1.3, AC-2.2, AC-3.1). It was drawn for 279px (spec 060 AC-2.4); the measured interiors are 238px (a 320px viewport), 263px (a 360px browser frame, which keeps a 15px scrollbar a phone doesn't), 278px (a 360px viewport) and 286px (the 320px aside; beside the zones the panel is 320px wide, spec 060 decision 6). **Known limits:** (1) a mana segment narrower than its label: a label of four characters ("U 44") needs about 14% of the bar, five ("W 999") about 17% and six ("W 2.6k") about 20% at a 320px viewport (measured to the half percent on a 236px bar), and the rule hides a label only under 10% (spec 060 decision 4), so a segment between 10% and those shares wraps its label and cuts it off; the caption carries the count; it exists at ordinary counts too, and is [#330](https://github.com/plainprogrammer/Collector/issues/330) (spec 077 decision 2, ruled option A). (2) A Card types row with a five-digit count and suffix at a 320px viewport wraps its name, even mid-word; counts of six digits are not specified.
**Width.** The panel works in 238px, the space inside its border and padding at a 320px viewport (a phone's 320px less 16px body margin, 32px page padding, 2px border and 32px panel padding), and every count, the legend, the types and every drawn mana label fit with no horizontal overflow at the worst-case counts of spec 069's fixture (spec 077 AC-1.3, AC-2.2, AC-3.1). It was drawn for 279px (spec 060 AC-2.4); the measured interiors are 238px (a 320px viewport), 263px (a 360px browser frame, which keeps a 15px scrollbar a phone doesn't), 278px (a 360px viewport) and 286px (the 320px aside; beside the zones the panel is 320px wide, spec 060 decision 6). **Known limit:** a Card types row with a five-digit count and suffix at a 320px viewport wraps its name, even mid-word; counts of six digits are not specified. (A mana segment too narrow for its label no longer wraps it: the label is not drawn and the caption carries the count; spec 078, [#330](https://github.com/plainprogrammer/Collector/issues/330), which closed the limit spec 077 had recorded.)

**The two copies** (spec 060 decision 7, AC-3.4). A closed `details` hides its content whatever the CSS says, and `::details-content` would put a browser floor under the page, so the deck page renders the panel twice and each copy hides the other with `display: none`.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
date: 2026-11-12
spec: 078 (#330)
tags: [design-system, css, process, specs]
---

# Lesson: when an issue lists options with costs, probe the cheapest missing candidate before asking for the ruling

## Context

Issue #330 (a mana bar label wider than its segment wraps and is cut off) offered three options: A, leave it as the documented limit; B, a server-side rule by label length; C, a container query with the label's length as a class. B and C both hard-coded font metrics or added a class per length, and the maintainer's ruling was needed because B superseded a ruled rule (the 10% narrow class).

## What happened

- Before writing to the coordinator, a throwaway system spec (a deck per label length, 4, 5 and 6 characters, at a 320px viewport, a 360px viewport and the 1280px aside) measured the labels. It showed the real cause: a label wraps at its own space ("W" over "12"), and a label is 29.2px, 36.4px or 43.6px wide, so whether it fits depends on the bar's width as well as the label's length.
- The same probe tried a fourth candidate that the issue did not list: make each segment a wrapping flex container with a zero-width first item, so the browser's own line breaking moves a label that cannot fit onto a second line that `overflow:clip` hides. Every label that fit stayed drawn (including a 12% "W 12" on a 360px phone), every label that did not fit left its segment, and none wrapped or was cut off.
- The question to the coordinator then carried the measurement and a fifth option with its trade-offs, and the maintainer ruled it (D1) in one round trip, keeping the 10% rule. The spec's review rounds spent their effort on the examples' tolerance (the integer `clientWidth` would have failed the Linux runner), not on whether the approach worked.

## What to do next time

When an issue lists options whose costs are about hard-coded numbers or extra markup, spend a few minutes on a throwaway probe (an uncommitted spec that builds the decks and measures the candidate in the real browser, as the screenshots method does) for an option the issue does not list, before asking for the ruling. Put the measurement in the question. A probe that finds nothing better costs the same few minutes and still turns the options' costs into numbers.

## Signals to watch for

- Options in an issue that all trade one literal (a pixel threshold, a font width) for another.
- A "documented limit" option recorded because the fix looked server-side or JavaScript-only; check whether the browser's own layout can make the decision (line breaking, `overflow`, container size) before accepting that.
Loading
Loading