FILES: select-all checkbox misaligned with the row checkboxes (both states) #398

Closed
opened 2026-09-29 04:41:44 +00:00 by kayg · 11 comments
Owner

Bug (owner, 2026-09-29, production screenshots): the select-all checkbox is not aligned with the row checkboxes

  1. Selection mode: the header's ticked select-all circle sits left of the row checkboxes (the rows have an inset surface; the header does not), so the two ticks are not on one vertical axis.
  2. Idle (no selection): the header's empty circle sits in a column that matches nothing in the rows: rows show the folder/file icon there, not a checkbox, so the circle floats left of the icon column.

Fix

  • One column definition shared by the header and every row (the same grid template and the same inset), so the header checkbox centre and every row checkbox centre are identical at every width, in list and grid view, in light and dark, and at 200% text size.
  • Idle state: the header checkbox occupies the same column as the rows' selection affordance. Rows show their checkbox on hover/selection mode in that column (Finder/Files style), so nothing floats; or the header control only appears when selection mode is active or on hover. Pick the Finder behaviour and state it.
  • An automated alignment assertion (the #253 icon-label primitive and the layout sweep): the header checkbox centre x equals every visible row checkbox centre x within 0.5 px, and its centre y is centred on the "Name" label cap height.
  • Zoom crops at 2x of the header and the first two rows in both states at 390, 820 and 1440, light and dark (theme asserted before capture).
## Bug (owner, 2026-09-29, production screenshots): the select-all checkbox is not aligned with the row checkboxes 1. **Selection mode:** the header's ticked select-all circle sits left of the row checkboxes (the rows have an inset surface; the header does not), so the two ticks are not on one vertical axis. 2. **Idle (no selection):** the header's empty circle sits in a column that matches nothing in the rows: rows show the folder/file icon there, not a checkbox, so the circle floats left of the icon column. ## Fix - One column definition shared by the header and every row (the same grid template and the same inset), so the header checkbox centre and every row checkbox centre are identical at every width, in list and grid view, in light and dark, and at 200% text size. - Idle state: the header checkbox occupies the same column as the rows' selection affordance. Rows show their checkbox on hover/selection mode in that column (Finder/Files style), so nothing floats; or the header control only appears when selection mode is active or on hover. Pick the Finder behaviour and state it. - An automated alignment assertion (the #253 icon-label primitive and the layout sweep): the header checkbox centre x equals every visible row checkbox centre x within 0.5 px, and its centre y is centred on the "Name" label cap height. - Zoom crops at 2x of the header and the first two rows in both states at 390, 820 and 1440, light and dark (theme asserted before capture).
Author
Owner

Started #398 on branch job/select-align from dev SHA 00015e652a.
Finding: FileCollection positions the header checkbox absolutely with a separate offset, while row checkboxes appear only in selectMode and add a grid track. The header and rows have different horizontal padding. I will give both one track definition, use Finder-style hover/selection affordances, add a measured 0.5 px assertion, then capture all requested widths and themes from the production build.

Started #398 on branch job/select-align from dev SHA 00015e652a153485d9e7adda1c5ee61bc17300db. Finding: FileCollection positions the header checkbox absolutely with a separate offset, while row checkboxes appear only in selectMode and add a grid track. The header and rows have different horizontal padding. I will give both one track definition, use Finder-style hover/selection affordances, add a measured 0.5 px assertion, then capture all requested widths and themes from the production build.
Author
Owner

Root cause confirmed in FileCollection: the header used absolute positioning and a hand-tuned left offset, while list rows had different padding and added the checkbox track only after selection. The fix now keeps the same selection/icon/data grid in header and rows in both states, and reveals marks on hover or selection. Grid tiles use the same hover/selection visibility rule. Added a production layout-sweep assertion for every visible checkbox at 390/820/1440 px, both schemes, and 100%/200% text size; the rendered test is next.

Root cause confirmed in FileCollection: the header used absolute positioning and a hand-tuned left offset, while list rows had different padding and added the checkbox track only after selection. The fix now keeps the same selection/icon/data grid in header and rows in both states, and reveals marks on hover or selection. Grid tiles use the same hover/selection visibility rule. Added a production layout-sweep assertion for every visible checkbox at 390/820/1440 px, both schemes, and 100%/200% text size; the rendered test is next.
Author
Owner

Measured finding from the first real-browser pass: at 390 px the header circle was 14 px left of each visible row circle and Name was 52 px left; at 820/1440 the offsets were 10 px and 34 px. The checkbox component's later absolute-position rule beat the new parent rule, so the header control left grid flow and the spacer filled the wrong track. I raised selector specificity and am rerunning. The cap-centre offset was 0.516 px at phone 100%, so the fix includes a 0.25 px CSS adjustment. Browser run used this worktree's production SPA and a prebuilt local server for API data.

Measured finding from the first real-browser pass: at 390 px the header circle was 14 px left of each visible row circle and Name was 52 px left; at 820/1440 the offsets were 10 px and 34 px. The checkbox component's later absolute-position rule beat the new parent rule, so the header control left grid flow and the spacer filled the wrong track. I raised selector specificity and am rerunning. The cap-centre offset was 0.516 px at phone 100%, so the fix includes a 0.25 px CSS adjustment. Browser run used this worktree's production SPA and a prebuilt local server for API data.
Author
Owner

Second rendered finding: the old screenshot path exposed that a normal desktop click selects a row but leaves the touch-only selectMode false. In that state the first crop showed no header circle and no empty row circles, despite selection. FileCollection now treats selectedCount > 0 as an active selection for checkbox visibility. The measured sweep now exercises a normal desktop click; the screenshot matrix is being recaptured from the rebuilt production SPA.

Second rendered finding: the old screenshot path exposed that a normal desktop click selects a row but leaves the touch-only selectMode false. In that state the first crop showed no header circle and no empty row circles, despite selection. FileCollection now treats selectedCount > 0 as an active selection for checkbox visibility. The measured sweep now exercises a normal desktop click; the screenshot matrix is being recaptured from the rebuilt production SPA.
Author
Owner

#398 progress: On job/select-align, the header control and list rows now share the selection/icon/data grid tracks and leading inset. The idle Finder behavior hides the header mark until hover or keyboard focus; row marks appear on hover. A desktop click now reveals the complete checkbox column for an active selection. The focused FileCollection test and bun run check pass. Production-build alignment measurements and themed captures are in progress.

#398 progress: On job/select-align, the header control and list rows now share the selection/icon/data grid tracks and leading inset. The idle Finder behavior hides the header mark until hover or keyboard focus; row marks appear on hover. A desktop click now reveals the complete checkbox column for an active selection. The focused FileCollection test and `bun run check` pass. Production-build alignment measurements and themed captures are in progress.
Author
Owner

#398 measurement: On the current production web build, layout-sweep.mjs --only select-list passed all 24 idle/selected combinations at 390, 820, and 1440 px, paper and Tokyo Night, 100% and 200% text. Every visible row checkbox centre has x delta 0 px from the header; the largest absolute header-to-Name-cap-centre y delta is 0.265625 px. LAYOUT SWEEP PASSED; CSP reports 0. The local asset override was required because the compatible prebuilt server binary embeds another branch's web bundle. Refreshed 2x screenshots are being captured now.

#398 measurement: On the current production web build, `layout-sweep.mjs --only select-list` passed all 24 idle/selected combinations at 390, 820, and 1440 px, paper and Tokyo Night, 100% and 200% text. Every visible row checkbox centre has x delta 0 px from the header; the largest absolute header-to-Name-cap-centre y delta is 0.265625 px. `LAYOUT SWEEP PASSED`; CSP reports 0. The local asset override was required because the compatible prebuilt server binary embeds another branch's web bundle. Refreshed 2x screenshots are being captured now.
Author
Owner

#398 visual evidence from the current production web build. Each crop is 2x and contains the header plus the first two rows. The theme was asserted before each capture.

State Width Light Dark
Idle phone crop crop
Idle tablet crop crop
Idle desktop crop crop
Selected phone crop crop
Selected tablet crop crop
Selected desktop crop crop
#398 visual evidence from the current production web build. Each crop is 2x and contains the header plus the first two rows. The theme was asserted before each capture. | State | Width | Light | Dark | |---|---:|---|---| | Idle | phone | [crop](https://git.kayg.org/attachments/94e73104-a963-4507-aac2-1bf67ab60465) | [crop](https://git.kayg.org/attachments/dcc56c9a-bb3d-407c-8b43-77cdde08f7d2) | | Idle | tablet | [crop](https://git.kayg.org/attachments/53adb5a0-936d-41a2-9a5e-6f2cbabab55a) | [crop](https://git.kayg.org/attachments/ad41fa53-e61b-450e-a72d-e315f955c46b) | | Idle | desktop | [crop](https://git.kayg.org/attachments/d1f70aef-b3d9-4e82-b8ff-72ded1a18548) | [crop](https://git.kayg.org/attachments/4e255086-6e67-4787-bb91-67811559f9f7) | | Selected | phone | [crop](https://git.kayg.org/attachments/f65775f3-3eb4-44d0-bb58-0cc36f87e6ad) | [crop](https://git.kayg.org/attachments/224f44c4-c627-4e8e-84e5-034bd0facaca) | | Selected | tablet | [crop](https://git.kayg.org/attachments/9e34dad1-045d-4c3f-b87c-adeac6939825) | [crop](https://git.kayg.org/attachments/11c6d589-de5c-4a12-b65e-de98eb0517bd) | | Selected | desktop | [crop](https://git.kayg.org/attachments/83532cfa-b94a-403d-b334-d5993a1f5618) | [crop](https://git.kayg.org/attachments/788219c3-c063-45c6-8135-e687cfc6e959) |
Author
Owner

#398 grid evidence from the same production build and asserted themes. Grid has no column header; these captures cover the tile selection affordance at every required width.

State Width Light Dark
Idle phone screenshot screenshot
Idle tablet screenshot screenshot
Idle desktop screenshot screenshot
Selected phone screenshot screenshot
Selected tablet screenshot screenshot
Selected desktop screenshot screenshot
#398 grid evidence from the same production build and asserted themes. Grid has no column header; these captures cover the tile selection affordance at every required width. | State | Width | Light | Dark | |---|---:|---|---| | Idle | phone | [screenshot](https://git.kayg.org/attachments/05da0969-2095-42d8-b24e-1f12427c7f83) | [screenshot](https://git.kayg.org/attachments/73bb9661-58c6-475c-a745-40dc51851994) | | Idle | tablet | [screenshot](https://git.kayg.org/attachments/9aebb748-6376-43a6-8877-fbaa2febe864) | [screenshot](https://git.kayg.org/attachments/804bf7a2-818b-4fcd-9132-1aa754edfb72) | | Idle | desktop | [screenshot](https://git.kayg.org/attachments/792e0e01-49a7-4187-be77-85b9da0e933a) | [screenshot](https://git.kayg.org/attachments/fc6511c8-609e-423f-91e4-2270f45c7d82) | | Selected | phone | [screenshot](https://git.kayg.org/attachments/0bbf1830-21be-43ae-824f-a475f27d5338) | [screenshot](https://git.kayg.org/attachments/234c15f9-dad4-45b7-ad57-3eb24c26a3ca) | | Selected | tablet | [screenshot](https://git.kayg.org/attachments/7f38d246-f278-429d-94f9-e74825e70b30) | [screenshot](https://git.kayg.org/attachments/2afeda9d-79b2-4465-a82d-dcd9d042b038) | | Selected | desktop | [screenshot](https://git.kayg.org/attachments/6fd0fa27-5fba-4368-89de-993ada8a86a2) | [screenshot](https://git.kayg.org/attachments/f583baa8-5375-4f47-bd34-cf9ab049ffd0) |
Author
Owner

#398 final-build list crops after the shared grid definition was moved to the collection parent. Theme was asserted before each 2x capture. The final sweep again measured 0 px horizontal checkbox delta and at most 0.265625 px vertical delta from the Name cap centre.

State Width Light Dark
Idle phone crop crop
Idle tablet crop crop
Idle desktop crop crop
Selected phone crop crop
Selected tablet crop crop
Selected desktop crop crop
#398 final-build list crops after the shared grid definition was moved to the collection parent. Theme was asserted before each 2x capture. The final sweep again measured 0 px horizontal checkbox delta and at most 0.265625 px vertical delta from the Name cap centre. | State | Width | Light | Dark | |---|---:|---|---| | Idle | phone | [crop](https://git.kayg.org/attachments/9cf6cd75-1878-405f-9b58-0038c29eb688) | [crop](https://git.kayg.org/attachments/474e4b2b-d561-4e33-ae13-cc65035025b3) | | Idle | tablet | [crop](https://git.kayg.org/attachments/9040bb25-d1dd-4de9-9bc5-7bcd4ad7df3a) | [crop](https://git.kayg.org/attachments/9328ee9a-1521-4c98-ac25-8b05cbfaf151) | | Idle | desktop | [crop](https://git.kayg.org/attachments/1e02056d-5220-4ee0-bb39-ca51b7f48592) | [crop](https://git.kayg.org/attachments/f172c9dc-8836-4654-b8b4-fdeed6e4bcfd) | | Selected | phone | [crop](https://git.kayg.org/attachments/e9424ea0-f27b-49a8-bd9a-4624a55be87f) | [crop](https://git.kayg.org/attachments/38ce5c5a-fadf-4d9b-bb93-b78696f1b292) | | Selected | tablet | [crop](https://git.kayg.org/attachments/95c45958-4ebd-4f95-81bb-51692fcb929c) | [crop](https://git.kayg.org/attachments/27c06371-f776-42cb-95b7-711576505088) | | Selected | desktop | [crop](https://git.kayg.org/attachments/50980e1a-b9fd-4e22-a7ba-f911d3761542) | [crop](https://git.kayg.org/attachments/f00d85cb-4cf0-4b87-a348-29b6648252fc) |
Author
Owner

#398 integration note for #253: I read the current IconLabel primitive and the in-flight job/icon-align FileCollection changes. When #253 lands, preserve this job's always-rendered list/tile selection marks, the shared --fc-leading-columns definition on .fc with its compact override, and the 0.5 px header-to-row assertion. #253's IconLabel wrappers around the icon and Name pair are compatible in intent, but the two branches edit the same rows and sort header, so the merge needs a rendered check after conflict resolution. I did not merge another job branch into this worktree.

#398 integration note for #253: I read the current IconLabel primitive and the in-flight `job/icon-align` FileCollection changes. When #253 lands, preserve this job's always-rendered list/tile selection marks, the shared `--fc-leading-columns` definition on `.fc` with its compact override, and the 0.5 px header-to-row assertion. #253's IconLabel wrappers around the icon and Name pair are compatible in intent, but the two branches edit the same rows and sort header, so the merge needs a rendered check after conflict resolution. I did not merge another job branch into this worktree.
Author
Owner

#398 final report

Built: The Files list header and every row inherit one selection/icon/data grid definition and the same leading inset. The idle header mark is hidden until hover or keyboard focus; row marks appear on hover. An active selection shows the full checkbox column. The header mark is centred on the Name cap line. Grid tiles show their marks on hover or selection; grid has no column header. The shared checkbox column remains visible at 200% text size.

Files: packages/ui/src/components/files/FileCollection.svelte, apps/web/src/lib/files/FileCollection.svelte.test.ts, apps/web/e2e/layout-sweep.mjs, apps/web/e2e/files.mjs, and apps/web/e2e/harness.mjs.

Head: 887026d735537a39f2a307f2a6d8632fa21a9f7c on job/select-align, pushed to origin/job/select-align. Merged dev once. Worktree clean. No merge into dev.

Verification, with gate output quoted verbatim:

cargo fmt --check: exit 0; stdout was empty.

cargo clippy --all-targets -- -D warnings:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 125m 37s

bun run check:

Text sizes use shared role tokens.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/select-align/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test:

 Test Files  123 passed (123)
      Tests  787 passed (787)
   Start at  07:33:25
   Duration  106.71s (transform 55%, environment 19%, import 13%, tests 9%, setup 4%)

Production browser checks:

CSP REPORTS layout-sweep: 0 across 2 pages
checked 0 screens
LAYOUT SWEEP PASSED
SELECTION_SCREENSHOTS artifacts/select-align
FILES SCREENSHOTS PASSED
CSP REPORTS files: 0 across 2 pages

The layout sweep checked idle and selected states at 390, 820, and 1440 px, light and dark, 100% and 200% text. Every visible row checkbox had x delta 0 px; the largest absolute header-to-Name-cap y delta was 0.265625 px. The final build's 2x list crops and grid screenshots are attached in prior #398 comments. The originals remain in artifacts/select-align/ and were not committed.

Known gaps: cargo test did not finish inside the four-hour job window. I interrupted it during dependency compilation, before any test case ran; exit 130. Last output:

   Compiling tantivy-bitpacker v0.10.0
   Compiling tantivy-fst v0.5.0
   Compiling prettyplease v0.3.0
   Compiling rand v0.10.3
   Compiling zvariant_utils v4.2.0
   Compiling serde_repr v0.1.21
   Compiling unicode-properties v0.1.4
   Compiling unicode-bidi v0.3.18

The time-boxed API adversarial round was not run because the full test build consumed the remaining window. #253 is still in flight in the same component; preserve this job's always-rendered selection marks and shared tracks when integrating its IconLabel changes, then rerun the rendered alignment sweep.

Decision where the design is silent: Use Finder-style idle behavior: the header control stays in the selection track but its circle is hidden until hover or keyboard focus; row circles appear on hover. Grid has no header, so its tile marks follow hover and selection state.

Cleanup: Cargo target and web build output removed; screenshots retained outside Git.

#398 final report Built: The Files list header and every row inherit one selection/icon/data grid definition and the same leading inset. The idle header mark is hidden until hover or keyboard focus; row marks appear on hover. An active selection shows the full checkbox column. The header mark is centred on the Name cap line. Grid tiles show their marks on hover or selection; grid has no column header. The shared checkbox column remains visible at 200% text size. Files: `packages/ui/src/components/files/FileCollection.svelte`, `apps/web/src/lib/files/FileCollection.svelte.test.ts`, `apps/web/e2e/layout-sweep.mjs`, `apps/web/e2e/files.mjs`, and `apps/web/e2e/harness.mjs`. Head: `887026d735537a39f2a307f2a6d8632fa21a9f7c` on `job/select-align`, pushed to `origin/job/select-align`. Merged `dev` once. Worktree clean. No merge into `dev`. Verification, with gate output quoted verbatim: `cargo fmt --check`: exit 0; stdout was empty. `cargo clippy --all-targets -- -D warnings`: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 125m 37s ``` `bun run check`: ``` Text sizes use shared role tokens. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/select-align/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ``` Test Files 123 passed (123) Tests 787 passed (787) Start at 07:33:25 Duration 106.71s (transform 55%, environment 19%, import 13%, tests 9%, setup 4%) ``` Production browser checks: ``` CSP REPORTS layout-sweep: 0 across 2 pages checked 0 screens LAYOUT SWEEP PASSED SELECTION_SCREENSHOTS artifacts/select-align FILES SCREENSHOTS PASSED CSP REPORTS files: 0 across 2 pages ``` The layout sweep checked idle and selected states at 390, 820, and 1440 px, light and dark, 100% and 200% text. Every visible row checkbox had x delta 0 px; the largest absolute header-to-Name-cap y delta was 0.265625 px. The final build's 2x list crops and grid screenshots are attached in prior #398 comments. The originals remain in `artifacts/select-align/` and were not committed. Known gaps: `cargo test` did not finish inside the four-hour job window. I interrupted it during dependency compilation, before any test case ran; exit 130. Last output: ``` Compiling tantivy-bitpacker v0.10.0 Compiling tantivy-fst v0.5.0 Compiling prettyplease v0.3.0 Compiling rand v0.10.3 Compiling zvariant_utils v4.2.0 Compiling serde_repr v0.1.21 Compiling unicode-properties v0.1.4 Compiling unicode-bidi v0.3.18 ``` The time-boxed API adversarial round was not run because the full test build consumed the remaining window. #253 is still in flight in the same component; preserve this job's always-rendered selection marks and shared tracks when integrating its IconLabel changes, then rerun the rendered alignment sweep. Decision where the design is silent: Use Finder-style idle behavior: the header control stays in the selection track but its circle is hidden until hover or keyboard focus; row circles appear on hover. Grid has no header, so its tile marks follow hover and selection state. Cleanup: Cargo target and web build output removed; screenshots retained outside Git.
kayg closed this issue 2026-09-29 08:59:40 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#398
No description provided.