Skip to content

Try fixing minitable truncation with CSS grid instead#3293

Open
david-crespo wants to merge 1 commit into
mainfrom
grid-minitable-truncate
Open

Try fixing minitable truncation with CSS grid instead#3293
david-crespo wants to merge 1 commit into
mainfrom
grid-minitable-truncate

Conversation

@david-crespo

@david-crespo david-crespo commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Alternative to #3182 proposed by Fable.

🤖 Fable comparison to #3182

Everything needed is in hand: both implementations, both PR discussions, the PR screenshots, and the notes from the previous session (.claude/notes/2026-07-17-minitable-grid-rewrite.md). No browser needed — the April screenshots on #3182 demonstrate the key differentiating scenario directly, and the clamp logic they illustrate is unchanged after the rebase.

Elegance

The two branches converge on an identical public API (text vs cell columns, truncate-with-tooltip via the same scrollWidth check). The difference is entirely in how column widths get decided.

Measurement branch (#3182, +185/−20): canvas measureText per cell → clamp ratios to 2.5:1 via sqrt(5/2) → normalize to percentage widths. Its main structural weakness is that it re-derives layout outside the layout engine, and has to defeat the layout engine to do it: every text cell is absolutely positioned (absolute inset-x-3) specifically so content can't influence native table sizing, then JS reconstructs what the sizes should be from a canvas measurement. Supporting apparatus follows from that choice: text-width.ts, a hand-copied '400 14px SuisseIntl' / 0.03rem font spec that must shadow text-sans-md, the spread/floor/ceiling heuristic, and a memoization contract that leaks into the API ("keep this array referentially stable"). On the plus side, it keeps genuine table display semantics and reuses Table.Header/Table.HeadCell from the big table.

Grid branch (#3293, +178/−175, net ~0 against main): the whole sizing policy is one declarative expression — minmax(0, auto) for text columns, max-content for cell columns — and the browser computes widths from the actual rendered glyphs. It deletes text-width.ts, the font constants, the clamp, the memo contract, and all 85 lines of mini-table.css nth-child tricks (styles now co-located). The cost is an ARIA tax: display: grid/display: contents strip implicit table semantics, so every element carries an explicit role and the file needs two jsx-a11y eslint disables. It also re-implements header-cell chrome inline rather than reusing Table.HeadCell — though main's css file was already a parallel implementation, so that's not a regression, just a missed reuse the other branch gets.

On elegance the grid version wins on the axis that matters most here: the measurement branch simulates the layout engine; the grid branch instructs it. Benjamin explicitly identified container-aware sizing as the ideal in the PR thread ("This could be even more clever by using absolute pixel widths and being container aware… But I'm not sure it's worth the extra complexity") — the grid gets exactly that behavior for free, because it's the layout engine's native job.

Scenarios with clearly different results

1. Long name next to short columns, with slack in the table — grid clearly better. This is the case users actually hit (#2999). The measurement floor reserves ~22% for each short column and the ceiling caps the long one at ~56%, so a long name truncates while its neighbors sit half empty. The NIC screenshot in the #3182 thread shows exactly this: asdasdasd-asdasdas… cut off while mock-vpc and mock-subnet each float in ~2× the width they need. Concretely: a name measuring 300px next to two trivial columns in a 512px table gets 55.6% ≈ 285px under measurement (truncated, ~200px dead space nearby); under grid the row's max-contents sum to well under 512, so the full name renders. A grid track never truncates while slack exists anywhere in the row.

2. First paint before the webfont loads, or future font changes — grid correct by construction. measureText against SuisseIntl before the font loads measures the fallback font, and nothing re-measures on font load; likewise, any future change to text-sans-md or letter-spacing silently desyncs the hardcoded constants. The grid version has no measurement to get wrong. Low frequency in practice (MiniTables appear after user interaction), but it's an entire bug class one approach can have and the other can't.

3. Short content everywhere — grid modestly better. Compare the two "abc" disk screenshots on #3293: grid lets badge columns hug content and gives leftover to the name; measurement stretches every text column to its clamped percentage, so dividers land disconnected from content (visible in the asda NIC screenshot on #3182). Cosmetic, but consistently in grid's favor.

4. Two long text columns under real constraint — different, neither clearly better. This is the one behavioral fork that's genuinely a matter of taste. Grid's track sizing is max-min water-filling: width distributes equally, short tracks freeze at content size, so the longest column absorbs all of the shortage beyond its equal share. Measurement shrinks proportionally (clamped), spreading the pain. Example: contents of 350px and 220px sharing 400px — grid yields 200/200 (shorter column 91% visible, longer 57%); measurement yields ~245/155 (both ~70% visible). Reasonable people could prefer either.

5. Legacy assistive tech — measurement clearly better, in principle. It's the only scenario found where measurement wins: it keeps real table display semantics, while grid depends on explicit ARIA roles to patch over display: contents semantics-stripping, which was historically buggy in browsers (fixed in current engines, and the role-based Playwright locators pass per the prior session's e2e runs). If the console had to support an old AT/browser combination, that would favor #3182; given the console's modern-browser baseline, it's a theoretical edge.

Verdict

Grid dominates on rendered results in the scenarios users encounter (1–3); its costs are confined to the code (ARIA boilerplate, subgrid idiom, duplicated header chrome) rather than the output. The measurement branch's advantages are semantic purity that its own absolute-positioning hack partially undermines, and a taste-based difference in how two simultaneously-truncating columns split the shortage. If presenting this on the PRs, scenario 1 is the strongest exhibit — the #3182 thread's own screenshots demonstrate the truncate-despite-slack behavior, and Benjamin's "container aware… not worth the extra complexity" comment frames the grid version as delivering the behavior he wanted at negative net complexity.

main

Screenshot 2026-07-17 at 6 24 32 PM Screenshot 2026-07-17 at 6 24 59 PM Screenshot 2026-07-17 at 6 40 56 PM

This PR

Screenshot 2026-07-17 at 6 26 10 PM Screenshot 2026-07-17 at 6 25 12 PM image Screenshot 2026-07-17 at 6 40 15 PM

@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
console Ready Ready Preview Jul 17, 2026 11:41pm

Request Review

Comment thread app/ui/lib/MiniTable.tsx
onMouseEnter={() => {
const el = ref.current
setIsTruncated(!!el && el.scrollWidth > el.clientWidth)
}}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is ingenious what the hell

Comment thread app/ui/lib/MiniTable.tsx
tabIndex={0}
aria-rowindex={index + 1}
key={rowKey(item, index)}
className="bg-default before:border-default relative col-span-full grid grid-cols-subgrid pt-2 before:pointer-events-none before:absolute before:inset-0 before:border-x before:content-[''] last:pb-2 last:before:rounded-b-lg last:before:border-b"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all this horrible stuff would be better as CSS. I’ll give it a try.

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