feat(account): collapse the character manifest to one status chip per row - #167
Conversation
Gate passed: measured against the running app (56.5px one-line baseline vs 74.3-74.8px two-line pre-change), the one-line layout holds for ten ordinary characters and for a realistic long, unshortened structure name at the narrowest supported viewport. No fallback needed.
…ayout's horizontal cost Fix round 1 on c1286e3. The one-line flex row for name+location was structurally guaranteed to pass its own vertical test (nowrap + ellipsis already capped every string to one row), so it never measured what a long location name actually costs: at the 320px narrowest viewport it forced ~413px of horizontal scroll before reaching STATUS/ACTIONS, against ~146px for the stacked layout it replaced. Per the plan's documented fallback, .char-line reverts to the same two-line stacked layout .stack already used; two lines per character is still a win over the three this task started from. Also fixes a vacuous location assertion, an unreproducible hardcoded height threshold (now derived from an in-page reference row), and unscoped table locators.
Fix round 2 on the manifest-density Task 4 fallback. The horizontal test was the load-bearing gate but never confirmed the seed actually rendered a location, and a broken seed would misdiagnose as "the flex row came back" in the other tests rather than "the seed broke." Also hoists the repeated manifest selector, adds maxScrollLeft > 0 so "nothing to scroll" can't read as success, and clarifies two comments that overclaimed (an inert .stack comparison, a non-independent cellWidth/regionWidth ratio check).
…n up two comments Whole-branch review found the offline "last seen" hidden-text substitute (Task 3) had never been exercised by any seed, so deleting it would fail zero tests and leave a docked-offline character announced to a screen reader as present. Adds an e2e case that seeds locationOnline: false plus a merely-stale-but-online sibling, asserting the hidden span's own text so a deletion of the span (not just its content) trips the test, and that the stale sibling does NOT get the hidden text since it is genuinely where the line says it is. Also rewrites five e2e comments pointing at the gitignored task-4-report.md to point at the committed CSS comment instead, and names the invariant behind the collapsed row's hardcoded "token ok" aria-label.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 20 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
Comment |
…169) * perf(account): tighten the manifest row from 75px to 63px The row's height floor was never the STATUS cell — it is the NAME cell's two lines plus cell padding. Round 1 collapsed a cell that had 30px of slack in a 50px block, so the pitch did not move. Replaces #167's height-band assertion with a pitch ceiling. The band required a located row to be 10-30px taller than a no-location row, which is two lines by definition: it passed at 75px and would pass at 95px. * feat(account): shorten the manifest's make-main control to 'main' 89px to 50px per character column, with the verb moved into a per-character accessible name — the same trade the unlink beside it already makes. * feat(account): render the manifest STATUS column only on exception On the common account every row reads ok, costing a header, a column and 82px of a 320px viewport to report the absence of news. The column now renders when at least one character is not ok, so its presence is the signal. `map on|off` varies per character while the chip reads ok either way, so the cell's accessible name was its only home; it moves to a visually-hidden span in the NAME cell at zero vertical cost. `aria-describedby` cannot dangle: a contact remedy implies attention or stalled, never ok, so a remedy existing implies the column renders. * feat(account): fold STANDING into the page head 539px of chrome sat above the first manifest row at 1440x900 — 60% of the fold — and 171px of it was a rule-head, a two-row definition grid and a collapsed margin holding two facts. The verdict joins the h1's line at zero vertical cost; tier and Discord become one meta line under the lede. The <dt>s were the only thing naming those values, so each fact keeps a visually-hidden label. The confirm cost keeps its own line via .page__meta-item > .confirm-cost: without it the revealed sentence re-centres the button out from under a stationary pointer and the pointerLeave disarms the control (#112), pinned by a boundingBox test at 700px. * test(account): gate the fold count at three viewports The success criterion is characters above the fold, so assert that directly — at 1440x900, 1280x800 and 390x844, not only the desktop case. Re-baselines the 320px horizontal gate onto a ten-character seed. Its previous 250px threshold came from a one-character account; a realistic account measured 258px, so the number the gate protected had never been taken at the size that matters. * fix(account): post-review cleanup — dead CSS, a false caption, three stale comments Whole-branch review of the manifest-density work (a4a292b..7c5654f) came back SHIP with only Minors: Task 4 duplicated .facts__lead/.facts__lead > .confirm-cost as .page__meta-item variants instead of renaming them, leaving the originals with no consumer; the all-ok manifest caption asserted every character is healthy even at zero characters, where showStatusColumn is vacuously false; and three comments described a `.facts` grid or a "block below" layout that Task 4's fold into the page head replaced. - Remove the dead .facts__lead / .facts__lead > .confirm-cost rules and point every comment that cited them (confirm-submit.tsx, globals.css's discord-id comment) at the live .page__meta-item selectors instead. - Give the zero-character case its own caption sentence instead of routing it through the all-healthy branch. - Correct standing.tsx's `.facts` grid reference, globals.css's "block below" verdict comment, and the .verdict margin-top comment (dead in practice — every call site is inside .page__head-row, which zeroes it) to match the post-fold layout. - Document that .page__meta-item > .confirm-cost's `>` combinator depends on ConfirmArmScope staying a pass-through with no wrapper element (the #112 fix goes dark silently otherwise). * test(account): pin the manifest caption's zero-character branch The no-STATUS-column sentence is selected by `showStatusColumn`, a `.some()` over the crew — false for an empty account as readily as for a healthy one. The zero-character branch added in post-review cleanup was the only user-visible copy on this branch with no test behind it; removing it turns this test red.
Pull request
What changed and why
PR #162 merged six manifest columns into four to fix horizontal crowding at 10+
characters. It fixed that and made the original complaint worse: the STATUS cell
stacked three
.status-linerows and the name cell gained a location line, so arow that was one line tall became three. At ten characters the manifest went from
~10 lines of vertical space to ~30 — on a page whose stated problem was crowding.
The three lines almost always read
ok/ok/on. The page was spending itsscarcest resource on saying nothing was wrong.
The rule: a row shows detail only when the member can act on it.
src/core/account-health.tsexportsclassifyCharacter, a three-wayattention/stalled/oktaxonomy built from theneedsAttentionandisStalledpredicates that were already there and stay private.computeAccountHealthnow calls it too, so the row rendering and theaccount-level verdict cannot drift into two taxonomies. One exported classifier
rather than two exported booleans, so callers cannot invent a fourth
combination. No new imports —
src/core/purity is preserved.The STATUS cell collapses to one chip for
okandstalledrows, andexpands to detail lines only for
attention. This is static, not adisclosure: no click, no client component, the manifest stays a server
component. A stalled character keeps its own chip —
sync disabled,sync failed— and is never shown a greenokit hasn't earned.formatLocationstops printing the system name twice. EVE dock namesalready contain their system (an NPC station is
<celestial> - <name>wherethe celestial begins with the system name, and players name structures after
the hole they live in), so
${system} — ${dock}renderedJita — Jita IV - Moon 4 - Caldari Navy Assembly Plant. Taking the dock's last-segment givesJita — Caldari Navy Assembly Plant. The system staysbecause a J-code is the operationally useful half.
last seenleaves the visible string and moves to a.visually-hiddenspan, since the existing
.dimtreatment already carries "true, but not now"visually. This is the one place here where sighted users get less text than
screen-reader users, and it is deliberate.
Net effect: roughly three lines per character down to two.
The one-line layout was measured and rejected
The design proposed putting name and location on one line, and gated it on
measurement because the name column sets the manifest's width — spending
horizontal budget in exactly the column #162 was compressing. The gate resolved
against it. At the 320px narrowest supported viewport, a flex row forced 413px of
horizontal scroll against a 286px-wide scroll region, versus ~146px stacked:
close to a full extra screen of blind scrolling to reach the STATUS and ACTIONS
columns a member came for.
.char-lineis therefore a stacked grid, ande2e/account.spec.tspins the measurement that forced the call so a futurechange cannot reintroduce the flex row without re-checking it.
Accessibility
The collapsed row must not fix a sighted-density problem by taking information
away from screen-reader users, which would invert the work in #137, #150, #159
and #162.
okis shown only for theokstate.
a screen-reader user hears what a sighted user read across three lines. This is
how
map: offstays reachable while the chip readsok.aria-describedbystays gated onhasContactRemedy, now deliberately adifferent predicate from the expand rule rather than accidentally the same one.
<caption>is updated; it described a STATUS column that always showedthree states, which is now false for most rows.
What CI cannot check
Nothing beyond CI for the logic. The layout decision rests on a measurement CI
now runs as a test rather than on a screenshot:
That test asserts the location actually rendered on the row it measures and
floors
maxScrollLeftabove zero, so a broken seed fails loudly instead ofpassing under the ceiling.
Deploy notes
None. No schema change, no migration, no new secret, no
fly.tomlchange.Flags
map: offrenders a plainokchip. This is the one place the designshows a green chip over a false-ish fact, and it is deliberate: there is
nothing a member can do about map membership from this page, and
src/core/account-health.ts:27-35already excludes it from the account verdictbecause
wanderer_acl_observationis a delete-and-replace snapshot — "off theACL legitimately" and "job never ran" are the same absent row. A chip reporting
it as a fault would raise an alarm it cannot substantiate for every member with
an off-map alt. The fact stays readable in the chip's accessible name.
A player structure with an internal
-(e.g."Home - Refinery") losesits first segment. Accepted cost of a rule with no special cases.
Two deferred Minors, both judged non-blocking. (1) The
— not managed/not yet runcopy is duplicated betweenstandingsSummaryandcontact-state.tsx; it can only drift for two cases that never renderContactStateon a collapsed row. (2).char-lineduplicates.stackexactly and no selector references it — kept because the comment above it is
the only committed home for the measurement that forced the fallback, written
for whoever retries the flex row.
Out of scope, unchanged: the admin members drawer's layout. It renders
CharacterLocationtoo, so the location text change reaches it — intended,since the redundancy was the same there.