Skip to content

fix(ui-core): four product screens stop widening the page at narrow widths - #772

Merged
YaelAnaya merged 3 commits into
mainfrom
refactor/cross-product-quality-pass
Aug 24, 2026
Merged

fix(ui-core): four product screens stop widening the page at narrow widths#772
YaelAnaya merged 3 commits into
mainfrom
refactor/cross-product-quality-pass

Conversation

@YaelAnaya

Copy link
Copy Markdown
Collaborator

Four product screens widened the page at narrow widths. Two of them were known
and had been carried through three phases; the other two were found by measuring
every route instead of the two anybody remembered. A guard now holds the floor.

This is the responsive slice of the cross-product quality pass, landed on its own
rather than held back for the rest of it: it closes both outstanding debts, it
stands up without the batches that follow, and a diff that also carried
accessibility, theme and motion work would be a great deal harder to read.

What was measured

Ten routes at 320, 375, 480, 768, 1024, 1280 and 1536, driven in chromium against
a running stack. The test is the document's own overflow -
documentElement.scrollWidth - clientWidth - because that is the thing a person
drags sideways. A local scroller that does not widen the page is not a defect,
and the project navigation strip is deliberately one.

Seven defects, on four surfaces:

Home     320           +35px   the resume control, 178px and unable to shrink
Schema   320/375/480   +204/+149/+44px   note field 224px + two controls = 460px
Dataset  320           +33px   a tab trigger cannot shrink past its label and count
Gallery  320/375       +118/+63px   the batch actions row, then the state filter

The schema figure is the one worth reading twice: the row measured right=524,
which is the residual width recorded when this was first deferred - "~524px
content at a 500px viewport". It is the same defect, unchanged, not a new one
wearing its clothes.

What changed, and at which layer

A button that shrinks is a button whose label truncates, so shrink-0 on the
primitive is correct and nothing here touches it. Every fix is a composition:

  • Schema and Gallery's action rows wrap. The schema's note field takes the
    full width while the row is wrapped and its own 224 once the row fits again.
  • Home's resume row wraps, so the control keeps its width and takes a second
    line rather than pushing the page.
  • Dataset's tab strip and Gallery's state filter scroll inside their own
    row. Both are atomic - a trigger carries a label and a count, a filter is one
    joined pill of five segments - so neither can shrink without truncating, and a
    tab nobody can read costs more than a scroll does. The project navigation
    answered the same shape the same way, so this follows that rather than
    inventing a second answer.

Two things the sweep taught, recorded because they will come up again

A culprit search has to skip local scrollers. The first pass blamed the
project navigation on three different screens. Its strip is overflow-x-auto by
design and already has a test proving it does not widen the page at 320: its
children sit past the viewport without widening anything. Counting them hid the
real causes - the dataset tab strip only appeared once they were filtered out.

Fixing the widest defect uncovers the next. With the gallery's header wrapped
the screen still overflowed, now by way of the state filter, which had been
sitting behind the larger number the whole time. The sweep was re-run after each
fix rather than trusted once.

The guard

Five scenarios in project-nav.spec.ts, one per project route, at all three floor
widths. It asserts the document rather than elements, precisely so a legitimate
local scroller is not a false positive - an element-level check would have to
special-case the navigation strip, and would call the dataset tabs broken after
this branch fixed them.

One test per route rather than one sweep: fifteen navigations in a single test
runs past the suite's 20s budget, and the timeout reads as "the floor is broken"
when it means the test did too much.

It was verified by breaking it. With the wrap fixes reverted, schema and the batch
detail fail, each naming the width that broke it; restored, all five pass.

Test plan

Every command ran inside Docker/Linux; no repository validation ran natively on
the host.

Node gates, full tree and frozen install in node:24-bookworm:

install (pnpm install --frozen-lockfile)  PASS
build (pnpm -r build)                     PASS
@visionset/ui-core lint (incl. typecheck) PASS
@visionset/ui-core test                   PASS
@visionset/app build                      PASS
@visionset/app lint                       PASS
@visionset/annotator test                 PASS
pnpm test:scripts                         PASS
pnpm version:check                        PASS

Browser, in mcr.microsoft.com/playwright:v1.62.1-noble with CI=1:

full suite   261 passed   (256 existing, 5 new)

The 261 matters more than the five: these are layout changes on four screens that
the suite already asserts behaviour for, so the existing scenarios staying green
is what says the wrapping did not move anything a person relies on.

Scope

Shared primitive changes:  None
Routes changed:            No
Product IA changed:        No
Backend / wire / domain:   No

Still open

The rest of the quality pass is untouched and unclaimed by this branch: content
stress, accessibility, keyboard operation, focus restoration and layering,
light/dark, motion and reduced motion, scroll ownership, dialog and popover
safety at small viewports, browser zoom, a visual-regression baseline, spacing
consistency, and the async/empty/error states.

Two notes for whoever picks those up. Gallery's state filter and the dataset tab
strip now scroll horizontally at 320 rather than stacking, which is a tradeoff
worth a second opinion. And the sweep only covered a project that exists with the
data it happens to hold; hostile content - a very long class name, a long model
reference, a large annotation count - is the next thing to point it at.

…ning the page

Both rows put Nova buttons in a row that could not give. The schema's note field
is 224px and its two controls another 220 with the gaps, which comes to 460 and
stops fitting somewhere around 500px; the gallery's batch actions did the same a
little later. Measured against the running stack, the schema overflowed the
document by 204px at 320, 149 at 375 and 44 at 480, and the gallery by 118 and
63 - the residual width both surfaces were carrying into this phase.

A button that shrinks is a button whose label truncates, so `shrink-0` on the
primitive is right and the row is the thing that had to change. Both now wrap,
and the schema's note takes the full width while it is wrapped and its own 224
once the row fits again.

The gallery's state filter needed the other answer. It is one joined pill of
five segments, 324px wide, so it is wider than the narrowest viewport on its own
and wrapping inside the pill would break the control rather than fit it. It
scrolls within its own row, which is what the project navigation does with the
same shape at the same width.
…g the page

Two more of the same defect, found by sweeping every product route rather than
the two that were already known. Home's resume row put a thumbnail, a column of
counts and a 178px control on one line and overflowed by 35px at 320. The dataset
tabs overflowed by 33: each trigger carries a label and a count, so `flex-1`
cannot shrink it past its own content.

Home's row wraps. The tab strip scrolls inside its row instead, for the reason
the project navigation's does - a tab that truncates its label to fit is a tab
nobody can read, and the strip is the product's one tab shape rather than a
composition this screen is free to restyle.
… overflow

Five scenarios, one per route, at 320, 375 and 480. All four fixes in this branch
were found by measuring rather than by looking, and without a guard the next
fixed-width row puts the scrollbar back.

The assertion is on the document, not on elements: a local scroller that does not
widen the page is not a defect, and this file's own navigation strip is
deliberately one. An element-level check would have to special-case it, and would
have called the dataset tab strip broken after this branch fixed it.

One test per route rather than one sweep over fifteen navigations, which runs past
the suite's 20s budget and reports a timeout where it means the test did too much.
Verified by breaking it: with the wrap fixes reverted, schema and the batch detail
fail with the width that broke them, and both pass again restored.
@YaelAnaya
YaelAnaya merged commit e1b925d into main Aug 24, 2026
15 checks passed
@YaelAnaya
YaelAnaya deleted the refactor/cross-product-quality-pass branch August 24, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant