Files
F-r-ldraledigplannerare/new-features/alignment-plan.md
T
2026-09-20 11:46:07 +02:00

117 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.