feat(e10): let an ultrawide buy bigger cards instead of more of them
The viewport contract has two ends, and only the floor was enforced. An auto-fill grid answers "more room" with more columns, never bigger ones, so it settles just above its minimum column at any width: measured at 3440px, the feed laid out 11 columns of 264px. A feed diluted across an ultrawide fails the contract as surely as a broken one at the floor. Multi-column feed views now carry a `maxCols`; past it the leftover width flows into the items. At 3440: cards 11x264 -> 8x370, cardsSmall 15 -> 11x264 (still visibly the dense card, and never as wide as cards gets), tiles 7 -> 6x498. Each cap is set so the item tops out near 1.5x its own minCol, which is what keeps a view recognisably itself. A cap can only bind ABOVE the width at which that many columns already fit, so it is a pure ultrawide affordance — 1024, 1920 and 2560 lay out exactly as before. Verified in the browser at all three widths and across all five views. The Channels grid needs no cap: the page's own 96rem --content-mw already bounds it. Also renames VirtualGrid's `maxCol` to `colMaxWidth`. It was never a column count — it is the single-column reading-width cap — and `maxCol`/`maxCols` one letter apart is the kind of drift that has bitten this repo before. The column maths moves to ui/gridColumns.ts as a pure function so both ends of the contract are unit-testable without a layout engine; the tests assert the cap's numbers, that it never binds at the floor, and that the dense card cannot grow into the standard one.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
// VirtualGrid's column-count policy, extracted as a pure function so the viewport contract's UPPER
|
||||
// end is unit-testable without a layout engine (the lower end, the 1024x768 floor, needs no policy —
|
||||
// the grid simply drops to the columns that fit).
|
||||
//
|
||||
// An auto-fill grid answers "more room" with more columns, never bigger ones, so it always settles
|
||||
// just above its minimum column width no matter how wide the screen is. Measured at 3440px (E10 S4),
|
||||
// the feed laid out 11 columns of 264px — a diluted feed is as bad a failure of the viewport contract
|
||||
// as a broken one, so `maxCols` stops the multiplication and lets the leftover width flow into the
|
||||
// items instead. The cap can only bind ABOVE the width at which that many columns already fit, which
|
||||
// is what keeps every narrower layout — the 1024px floor included — bit-for-bit unchanged.
|
||||
|
||||
/** The grid's gutter in px; the column maths has to net it out on both sides of the division. */
|
||||
export const GRID_GAP = 16;
|
||||
|
||||
/** Columns that fit `width` (never more than `maxCols`), or 1 for the single-column layout
|
||||
* (`minCol == null`). */
|
||||
export function columnsFor(
|
||||
minCol: number | null,
|
||||
width: number,
|
||||
maxCols: number | null = null,
|
||||
): number {
|
||||
if (minCol == null) return 1;
|
||||
const fit = Math.max(1, Math.floor((width + GRID_GAP) / (minCol + GRID_GAP)));
|
||||
return maxCols == null ? fit : Math.min(fit, maxCols);
|
||||
}
|
||||
|
||||
/** The px width one item gets once `columnsFor` has chosen the count — i.e. where the width saved by
|
||||
* the cap actually goes. */
|
||||
export function itemWidthFor(width: number, cols: number): number {
|
||||
return Math.floor((width - GRID_GAP * (cols - 1)) / cols);
|
||||
}
|
||||
Reference in New Issue
Block a user