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:
@@ -21,7 +21,15 @@ export interface FeedViewSpec {
|
||||
minCol: number | null;
|
||||
/** Single-column views only: the reading-width cap in px (null = fill). A long line is hard to
|
||||
* scan, so the stacked row keeps a cap; the one-line row doesn't need as tight a one. */
|
||||
maxCol: number | null;
|
||||
colMaxWidth: number | null;
|
||||
/** Multi-column views only: the most columns to lay out (null = as many as fit). The UPPER end of
|
||||
* the 1024x768..ultrawide contract (E10 S4). An auto-fill grid always settles just above `minCol`,
|
||||
* so without a cap a wider screen buys more, smaller items rather than better ones — measured at
|
||||
* 3440px, `cards` laid out 11 columns of 264px. Each cap here is set so the item tops out near
|
||||
* 1.5x its own `minCol` on a 3440px ultrawide, which keeps each view recognisably itself (a capped
|
||||
* `cardsSmall` must not grow into a `cards`). Below the width where that many columns already fit
|
||||
* the cap does nothing at all, so 1024 through 2560 are byte-identical to before it existed. */
|
||||
maxCols: number | null;
|
||||
/** Show the video thumbnail. False only for the bare row — whose badges (duration, live state,
|
||||
* saved, resume bar) then move into the meta line, since Thumb is what draws them. */
|
||||
thumb: boolean;
|
||||
@@ -37,7 +45,9 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
||||
cards: {
|
||||
family: "card",
|
||||
minCol: 260,
|
||||
maxCol: null,
|
||||
colMaxWidth: null,
|
||||
// 8 -> 369px cards at 3440 (vs 11 x 264 uncapped); binds only above ~2560.
|
||||
maxCols: 8,
|
||||
thumb: true,
|
||||
dense: false,
|
||||
actionsBelow: false,
|
||||
@@ -45,7 +55,9 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
||||
cardsSmall: {
|
||||
family: "card",
|
||||
minCol: 180,
|
||||
maxCol: null,
|
||||
colMaxWidth: null,
|
||||
// 11 -> 264px at 3440: still visibly the DENSE card, and never as wide as `cards` gets.
|
||||
maxCols: 11,
|
||||
thumb: true,
|
||||
dense: true,
|
||||
actionsBelow: false,
|
||||
@@ -57,7 +69,8 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
||||
rows: {
|
||||
family: "row",
|
||||
minCol: null,
|
||||
maxCol: 896,
|
||||
colMaxWidth: 896,
|
||||
maxCols: null,
|
||||
thumb: true,
|
||||
dense: false,
|
||||
actionsBelow: false,
|
||||
@@ -68,7 +81,8 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
||||
rowsCompact: {
|
||||
family: "row",
|
||||
minCol: null,
|
||||
maxCol: 1472,
|
||||
colMaxWidth: 1472,
|
||||
maxCols: null,
|
||||
thumb: false,
|
||||
dense: false,
|
||||
actionsBelow: false,
|
||||
@@ -83,7 +97,11 @@ export const FEED_VIEW_SPEC: Record<FeedView, FeedViewSpec> = {
|
||||
tiles: {
|
||||
family: "row",
|
||||
minCol: 380,
|
||||
maxCol: null,
|
||||
colMaxWidth: null,
|
||||
// 6 -> 498px tiles at 3440. A tile spends extra width on its TEXT column rather than on a bigger
|
||||
// thumbnail, so it tolerates dilution better than a card — but it still bottoms out at its 380px
|
||||
// minimum on an ultrawide, which is the same waste, so it gets the same treatment.
|
||||
maxCols: 6,
|
||||
thumb: true,
|
||||
dense: false,
|
||||
actionsBelow: true,
|
||||
|
||||
Reference in New Issue
Block a user