The three card grids -- albums, artists, genres -- laid out with `justify: 'center'` and a fixed 8px gap and padding, which gives the row a fixed width and pushes everything left over to the two margins. Measured on a 1440px window: cards 16px apart inside 78px of nothing down each side. The outside was five times the inside. `utils/grid-spacing.ts` computes one number instead, from what the row could not spend on another card: the same value between two cards, between two rows, and down each edge. That window now reads 30px outside against 34px between, and it holds at any width. The virtualizer has a word for this -- `justify: 'space-evenly'` with `gap: 'auto'` -- and it cannot be used. It fits `floor(width / cardWidth)` columns without reserving the gap it is about to need, so a width one card short of exact leaves seven cards a pixel apart. On the window above it would fit 7 columns with 1px between them. Deciding the column count here is what puts a floor under the spacing. Two consequences. The layout is rebuilt when the container width changes the spacing rather than only when the cover size changes, so each grid observes its own scroller -- keyed on the spacing, or every pixel of a drag rebuilds a layout that comes out the same. And `cover-grid`'s ScrollManager took `GRID_GAP`/`GRID_PADDING` as constants, which stopped describing anything the moment the spacing became elastic: it asks the host for the geometry now, since a scroll position rebuilt from a stale 8px lands in the wrong row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MeQt5hgXg5YGoNZQ9ozG7L
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
/**
|
||
* Even spacing for the three card grids — albums, artists, genres.
|
||
*
|
||
* All three used `justify: 'center'` with a fixed 8px gap and 8px
|
||
* padding, which gives the row a fixed width and pushes everything left
|
||
* over to the two margins: on a 1440px window the albums grid drew its
|
||
* cards 16px apart inside 78px of nothing down each side. The outside
|
||
* was five times the inside.
|
||
*
|
||
* The fix is to spend the leftover on the spacing instead, so there is
|
||
* one number: between two cards, between two rows, and down each edge.
|
||
* The virtualizer has a word for that — `justify: 'space-evenly'` with
|
||
* `gap: 'auto'` — and it cannot be used, because it fits
|
||
* `floor(width / cardWidth)` columns without reserving the gap it is
|
||
* about to need: a width one card short of exact fits seven cards a
|
||
* pixel apart. Deciding the column count here is what puts a floor
|
||
* under the spacing, and the grid is then given plain numbers.
|
||
*/
|
||
|
||
/** The narrowest the spacing is allowed to get. */
|
||
export const MIN_GRID_SPACING = 8;
|
||
|
||
/**
|
||
* How many cards of `cardWidth` fit across `width`.
|
||
*
|
||
* A row of c cards spends c×cardWidth on cards and (c+1)×spacing on the
|
||
* spaces between and beside them, so c is bounded by
|
||
* (width − spacing) / (cardWidth + spacing) at the minimum spacing.
|
||
*/
|
||
export function gridColumnsFor(
|
||
width: number,
|
||
cardWidth: number,
|
||
): number {
|
||
if (cardWidth <= 0) return 1;
|
||
|
||
const fit = Math.floor(
|
||
(width - MIN_GRID_SPACING) / (cardWidth + MIN_GRID_SPACING),
|
||
);
|
||
|
||
return Math.max(1, fit);
|
||
}
|
||
|
||
/**
|
||
* The spacing `width` produces — the gap, the row gap and the padding,
|
||
* which are all the same number.
|
||
*/
|
||
export function gridSpacingFor(
|
||
width: number,
|
||
cardWidth: number,
|
||
): number {
|
||
const columns = gridColumnsFor(width, cardWidth);
|
||
const leftover = width - columns * cardWidth;
|
||
|
||
return Math.max(
|
||
MIN_GRID_SPACING,
|
||
Math.floor(leftover / (columns + 1)),
|
||
);
|
||
}
|