Alignment plan
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# Align the year-row timelines in the Overview tab
|
||||
|
||||
## Context
|
||||
|
||||
In the Dashboard Overview tab each year is a `<details class="disclosure year-row">` whose
|
||||
`<summary>` is its **own** grid: `grid-template-columns: 3.5rem minmax(0, 1fr) auto var(--icon-sm)`
|
||||
(`frontend/src/styles/components.css:225`). Because every row is an independent grid, the `auto`
|
||||
figures column is sized per row by that row's own text — "· expiring 5", a wider `saved` number,
|
||||
a longer parent name, a 3-digit vs 2-digit PL value. The `minmax(0, 1fr)` track column absorbs the
|
||||
difference, so the timeline bars end at a different x per year (see `timeline-offset.png`: 2029's
|
||||
track is clearly shortest, 2025 the longest). The figures columns are ragged for the same reason.
|
||||
|
||||
Goal: every year's timeline track starts and ends at the same x, and the figure columns line up
|
||||
down the list — without hardcoding a pixel width that breaks in Swedish (sv strings are longer than
|
||||
en) or when a number gains a digit.
|
||||
|
||||
## Approach — one shared grid via `subgrid`
|
||||
|
||||
Promote the column definition from each summary to the `.year-rows` list and have the rows opt into
|
||||
it with `grid-template-columns: subgrid`. Tracks are then sized once, by the widest content in the
|
||||
whole list, and every row inherits identical column widths. Content still sizes the columns, so it
|
||||
stays bilingual-safe.
|
||||
|
||||
Split the figures cell into its three real columns (name | PL left | vacation group) at the list
|
||||
level too, so the numbers align vertically across years as well.
|
||||
|
||||
**CSS only — no TSX changes.** The existing markup already has the right elements
|
||||
(`.year-rows` → `.year-row` (`details`) → `.year-row__summary` / `.year-row__body`,
|
||||
`.year-row__figures` → `.year-row__parent { display: contents }`).
|
||||
|
||||
### Files
|
||||
|
||||
- `frontend/src/styles/components.css` — `.year-rows` (162), `.year-row__summary` (225),
|
||||
`.year-row__summary::after` (238), `.year-row__year` (244), `.year-row__track` (252),
|
||||
`.year-row__figures` (289), `.year-row__body` (322)
|
||||
- `frontend/src/styles/responsive.css` — mobile block at 93–120 (must reset subgrid)
|
||||
|
||||
### Shape
|
||||
|
||||
`.year-rows` becomes the grid owner:
|
||||
|
||||
```css
|
||||
.year-rows {
|
||||
display: grid;
|
||||
/* year | track | name | PL left | vacation group | chevron */
|
||||
grid-template-columns: 3.5rem minmax(0, 1fr) auto auto auto var(--icon-sm);
|
||||
}
|
||||
```
|
||||
|
||||
Each row spans it and re-exposes it:
|
||||
|
||||
```css
|
||||
.year-row { /* the <details> */
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
}
|
||||
.year-row__summary {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: subgrid;
|
||||
/* keep: align-items, column-gap, row-gap, padding, font-weight */
|
||||
}
|
||||
.year-row__body { grid-column: 1 / -1; }
|
||||
```
|
||||
|
||||
Column placement inside the summary shifts to match the six tracks: year `1`, track `2`,
|
||||
`.year-row__figures` `grid-column: 3 / 6` + `grid-template-columns: subgrid` (keep
|
||||
`display: grid`, `align-items: baseline`, `white-space: nowrap`; drop its own
|
||||
`auto auto auto`), chevron `::after` `grid-column: 6`. `.year-row__parent { display: contents }`
|
||||
stays — each parent's three spans land in the three shared columns.
|
||||
|
||||
Notes:
|
||||
- `--sp-4` column-gap must be declared on `.year-rows` as well, since a subgrid inherits the
|
||||
parent's gaps unless it sets its own; keep the summary's `column-gap` so the values agree.
|
||||
- `.year-rows > .disclosure:last-child { border-bottom: none }` and the `.disclosure` hairlines
|
||||
in `primitives.css` are unaffected (borders on the `details` still paint).
|
||||
- In the solo case `.year-row__name` isn't rendered; that column collapses to `0` for every row —
|
||||
still uniform.
|
||||
- Left-over `grid-row: 1` declarations on year/track/figures stay valid.
|
||||
|
||||
### Fallback
|
||||
|
||||
Wrap the promotion in a support check so older engines keep today's (merely ragged) layout rather
|
||||
than collapsing:
|
||||
|
||||
```css
|
||||
@supports (grid-template-columns: subgrid) { /* the rules above */ }
|
||||
```
|
||||
|
||||
Keep the current `.year-row__summary` rule as the base; the `@supports` block overrides it.
|
||||
|
||||
### Mobile
|
||||
|
||||
`responsive.css` already re-lays the summary as `3rem minmax(0, 1fr) var(--icon-sm)` with the
|
||||
figures wrapped to row 2 as a flex column. Inside that media query, reset the new rules:
|
||||
`.year-rows { display: flex; flex-direction: column }` and
|
||||
`.year-row { display: block }` — so the mobile block's existing declarations apply unchanged.
|
||||
Place these resets after the `@supports` block in source order, or repeat them inside their own
|
||||
`@supports` guard, so specificity/order lets mobile win.
|
||||
|
||||
## Verification
|
||||
|
||||
1. `cd frontend && npm run dev`, open the Dashboard Overview tab with the demo workspace
|
||||
(`demo@example.com`) — delegate the browser pass to a Sonnet subagent (never drive Chrome from
|
||||
the coordinator).
|
||||
2. Check: every year's track left and right edge align to the pixel, including 2029 (shortest
|
||||
figures) vs 2025 (no expiring) vs a row with `expiring`; the "PL left" and "Vacation left"
|
||||
labels form straight columns.
|
||||
3. Repeat in **both** Swedish and English (longer sv strings are the real stress test), and in a
|
||||
solo workspace (no parent names) as well as a couple workspace.
|
||||
4. Narrow to the mobile breakpoint and confirm figures still wrap under the track, one parent per
|
||||
line (use the same-origin iframe trick — `resize_window` is a no-op in this setup).
|
||||
5. Gates (delegate to a Haiku subagent, raw output): from `frontend/` `npm run typecheck`,
|
||||
`npm run lint`, `npm run coverage`. CSS-only change — `YearRows.test.tsx` and `Dashboard.test.tsx`
|
||||
should be untouched and green.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
Reference in New Issue
Block a user