Files row: selection circle, icon and label are not vertically centred together #538

Closed
opened 2026-09-30 17:28:34 +00:00 by kayg · 20 comments
Owner

Owner report (2026-09-30, screenshot): "misalignment strikes again?"

In the Files list row for the folder "Backgrounds", the selection circle, the folder icon and the name label do not share one vertical centre line. The label sits visibly higher than the icon and the circle.
Fix:

  • Every Files list row (and grid tile caption) centres the circle, icon and label on one axis. Use the shared icon+label row primitive (reuse gate): icons use the same box size and the label line-height from the type tokens, and there is no baseline hack or numeric nudge (tokens only, #287).
  • Check the same row pattern in Photos albums, Mail, Tasks, Contacts, Money and Settings lists, and fix every instance in the shared primitive, not per screen.
    Test: a layout assertion (e2e) that the centres of the circle, icon and label text box agree within 1 CSS px at 360, 768 and 1440 px, in light and dark. Attach zoomed screenshots of one row per list type (a production build).
## Owner report (2026-09-30, screenshot): "misalignment strikes again?" In the Files list row for the folder "Backgrounds", the selection circle, the folder icon and the name label do not share one vertical centre line. The label sits visibly higher than the icon and the circle. **Fix:** - Every Files list row (and grid tile caption) centres the circle, icon and label on one axis. Use the shared icon+label row primitive (reuse gate): icons use the same box size and the label line-height from the type tokens, and there is no baseline hack or numeric nudge (tokens only, #287). - Check the same row pattern in Photos albums, Mail, Tasks, Contacts, Money and Settings lists, and fix every instance in the shared primitive, not per screen. **Test:** a layout assertion (e2e) that the centres of the circle, icon and label text box agree within 1 CSS px at 360, 768 and 1440 px, in light and dark. Attach zoomed screenshots of one row per list type (a production build).
Author
Owner

Started work on job/align-538 from origin/dev at base aa372eef6c. I am tracing shared list primitives and the existing e2e layout harness before changing the row alignment.

Started work on job/align-538 from origin/dev at base aa372eef6c9312403f59a65d5b90cb3648ff52ed. I am tracing shared list primitives and the existing e2e layout harness before changing the row alignment.
Author
Owner

Finding: the Files list already uses grid align-items:center, but its filename adds .optical-caps (line-height: 1, text-box trimming, padding and negative margins), so the measured label box does not use the text role's line-height. The shared UI package has no IconLabel yet (#253 remains open); SidebarLinks, SettingsRow and FileCollection are the reusable list implementations present on this branch. Photos albums and Contacts routes are not built, and Mail/Money/Tasks have no matching leading icon+label row in their current main lists. I will add the smallest shared icon+label layout primitive and apply it to the existing matching list rows, then cover Files in production e2e.

Finding: the Files list already uses grid align-items:center, but its filename adds .optical-caps (line-height: 1, text-box trimming, padding and negative margins), so the measured label box does not use the text role's line-height. The shared UI package has no IconLabel yet (#253 remains open); SidebarLinks, SettingsRow and FileCollection are the reusable list implementations present on this branch. Photos albums and Contacts routes are not built, and Mail/Money/Tasks have no matching leading icon+label row in their current main lists. I will add the smallest shared icon+label layout primitive and apply it to the existing matching list rows, then cover Files in production e2e.
Author
Owner

Further row audit for #538: Calendar Agenda task rows put the status square in a time-width track while task text can wrap; Files pinned rows used their own flex icon and label. Commit 44f0fa941 routes both through IconLabel. Mail thread rows use an unread status dot, not an item icon. This build has no Contacts list or Photos album list screen, so those row types have no implementation to update.

Further row audit for #538: Calendar Agenda task rows put the status square in a time-width track while task text can wrap; Files pinned rows used their own flex icon and label. Commit 44f0fa941 routes both through IconLabel. Mail thread rows use an unread status dot, not an item icon. This build has no Contacts list or Photos album list screen, so those row types have no implementation to update.
Author
Owner

Production evidence at 360 px found the Files title-to-metadata text boxes 4.33 CSS px apart, above the existing 2–4 px row-rhythm assertion. I will adjust only the compact Files stack spacing with a shared spacing token and keep the assertion unchanged.

Production evidence at 360 px found the Files title-to-metadata text boxes 4.33 CSS px apart, above the existing 2–4 px row-rhythm assertion. I will adjust only the compact Files stack spacing with a shared spacing token and keep the assertion unchanged.
Author
Owner

Computed production styles isolated the cause: IconLabel's .icon-label-text display:block was winning over Files' equal-specificity .fc-namecell display:grid rule. The title/meta spacing token therefore had no layout effect. I have made the Files grid-stack selector explicit so the token applies while preserving token line-height.

Computed production styles isolated the cause: IconLabel's .icon-label-text display:block was winning over Files' equal-specificity .fc-namecell display:grid rule. The title/meta spacing token therefore had no layout effect. I have made the Files grid-stack selector explicit so the token applies while preserving token line-height.
Author
Owner

The Files screenshot pages completed their matrix, then the unbounded screenshots-only path entered the unrelated Recent/Trash capture and failed because applyEvidenceTheme is undefined. I will rerun with --shot-surface files, which bounds this run to the requested Files evidence; the capture code’s unrelated branch remains a known e2e gap.

The Files screenshot pages completed their matrix, then the unbounded screenshots-only path entered the unrelated Recent/Trash capture and failed because applyEvidenceTheme is undefined. I will rerun with --shot-surface files, which bounds this run to the requested Files evidence; the capture code’s unrelated branch remains a known e2e gap.
Author
Owner

Follow-up: I added the shared setTheme-backed applyEvidenceTheme helper and verified the script parses; the Recent capture advanced past the undefined reference, then stopped when Sharp could not load because this host lacks libstdc++.so.6. The requested Files production matrix uses Playwright-native row crops and passed.

Follow-up: I added the shared setTheme-backed applyEvidenceTheme helper and verified the script parses; the Recent capture advanced past the undefined reference, then stopped when Sharp could not load because this host lacks libstdc++.so.6. The requested Files production matrix uses Playwright-native row crops and passed.
Author
Owner

#538 complete

Built the shared token-driven IconLabel primitive and moved the existing icon-and-label rows onto it. The Files list now centres the selection mark, icon box, and full label box on one axis. Files grid captions and thumbnail placeholders use their type-role line height and icon size. Sidebar links, Photos folder links, pinned Files rows, Settings rows, Mail’s swipe Copy link action, and Calendar Agenda Task rows use the same primitive.

Production measurement found that Files’ label wrapper stayed display: block: its rule had the same specificity as IconLabel’s wrapper rule, so the title/meta row gap had no effect. The combined selector now makes the Files text stack a grid. With the shared compact gap token, the phone title-to-metadata spacing measures 3.33 CSS px; the existing #142 assertion remains unchanged and its 2–4 px range passes.

Branch / head: job/align-538 / ba24c57755ad65dd8535a8f0baf22d87c90498fb.

Files changed:

  • packages/ui/src/components/IconLabel.svelte, packages/ui/src/index.ts, packages/ui/src/tokens.css
  • packages/ui/src/components/files/FileCollection.svelte, packages/ui/src/components/files/FileThumb.svelte
  • packages/ui/src/components/calendar/AgendaList.svelte
  • apps/web/src/lib/components/SidebarLinks.svelte, apps/web/src/lib/styles/sidebar.css
  • apps/web/src/lib/files/FilesSidebar.svelte, apps/web/src/lib/photos/PhotosSidebar.svelte
  • apps/web/src/lib/mail/MailView.svelte, apps/web/src/routes/settings/parts/SettingsRow.svelte
  • apps/web/e2e/files.mjs

Decisions not specified by DESIGN:

  • Added IconLabel as a snippet-based shared component because no existing primitive laid out an icon box and text box together. Callers pass the relevant icon, leading, and gap tokens.
  • Added --stack-title-meta-compact-gap at 0.125rem for Files’ dense touch rows. Keeping full type-token line boxes is required for the shared centre line; the token gap keeps the visible text rhythm inside the existing range without a baseline trim or numeric offset.
  • The Photos album list and Contacts list are not present in this build, so I did not create either screen. Mail message rows have an unread status dot but no leading identity icon; the available Inbox sidebar row is captured below.

Production evidence: the real server and API-created Backgrounds folder passed the circle/icon/label layout assertion at 360, 390, 768, 820, and 1440 px in light and dark. Files produced 130 production captures. Eighteen additional real-row crops cover Calendar Tasks and Settings at all three device widths and both themes, plus Photos, Mail, and Money sidebar rows at desktop in both themes. The Money crops include only the account icon and name.

Gates:

bun run check output:

$ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/align-538/apps/web
Getting Svelte diagnostics...
svelte-check found 0 errors and 0 warnings

Verbatim bun run test result lines:

 Test Files  140 passed (140)
      Tests  914 passed (914)
   Start at  21:52:10
   Duration  157.80s (transform 58%, environment 17%, import 13%, tests 9%, setup 3%)

  Transform  |component| transforming modules took 294.05s · 49% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

bun run build passed. The production Files screenshot run reported FILES SCREENSHOTS PASSED and CSP REPORTS files: 0 across 11 pages. node --check apps/web/e2e/files.mjs passed. cargo build -p calternal-server passed for local production capture; no Rust source changed. cargo clean output: Removed 7169 files, 4.5GiB total. I removed apps/web/build, apps/web/.svelte-kit, and the temporary worktree target directory after capture.

Known gaps: the Photos album and Contacts lists are unavailable in this build. Photos, Mail, and Money sidebar crops are desktop-only because their shared sidebar is collapsed at phone and tablet widths. The supplementary Recent screenshot path reached Sharp and then stopped because this host lacks libstdc++.so.6; the requested Files-only native Playwright capture does not use Sharp and passed. Vitest also emitted its existing jsdom CSS and Window.scrollTo() warnings before the passing summary above.

## #538 complete Built the shared token-driven `IconLabel` primitive and moved the existing icon-and-label rows onto it. The Files list now centres the selection mark, icon box, and full label box on one axis. Files grid captions and thumbnail placeholders use their type-role line height and icon size. Sidebar links, Photos folder links, pinned Files rows, Settings rows, Mail’s swipe Copy link action, and Calendar Agenda Task rows use the same primitive. Production measurement found that Files’ label wrapper stayed `display: block`: its rule had the same specificity as IconLabel’s wrapper rule, so the title/meta row gap had no effect. The combined selector now makes the Files text stack a grid. With the shared compact gap token, the phone title-to-metadata spacing measures 3.33 CSS px; the existing #142 assertion remains unchanged and its 2–4 px range passes. **Branch / head:** `job/align-538` / `ba24c57755ad65dd8535a8f0baf22d87c90498fb`. **Files changed:** - `packages/ui/src/components/IconLabel.svelte`, `packages/ui/src/index.ts`, `packages/ui/src/tokens.css` - `packages/ui/src/components/files/FileCollection.svelte`, `packages/ui/src/components/files/FileThumb.svelte` - `packages/ui/src/components/calendar/AgendaList.svelte` - `apps/web/src/lib/components/SidebarLinks.svelte`, `apps/web/src/lib/styles/sidebar.css` - `apps/web/src/lib/files/FilesSidebar.svelte`, `apps/web/src/lib/photos/PhotosSidebar.svelte` - `apps/web/src/lib/mail/MailView.svelte`, `apps/web/src/routes/settings/parts/SettingsRow.svelte` - `apps/web/e2e/files.mjs` **Decisions not specified by DESIGN:** - Added `IconLabel` as a snippet-based shared component because no existing primitive laid out an icon box and text box together. Callers pass the relevant icon, leading, and gap tokens. - Added `--stack-title-meta-compact-gap` at `0.125rem` for Files’ dense touch rows. Keeping full type-token line boxes is required for the shared centre line; the token gap keeps the visible text rhythm inside the existing range without a baseline trim or numeric offset. - The Photos album list and Contacts list are not present in this build, so I did not create either screen. Mail message rows have an unread status dot but no leading identity icon; the available Inbox sidebar row is captured below. **Production evidence:** the real server and API-created `Backgrounds` folder passed the circle/icon/label layout assertion at 360, 390, 768, 820, and 1440 px in light and dark. Files produced 130 production captures. Eighteen additional real-row crops cover Calendar Tasks and Settings at all three device widths and both themes, plus Photos, Mail, and Money sidebar rows at desktop in both themes. The Money crops include only the account icon and name. - **Files / Backgrounds row:** [390 light](https://git.kayg.org/attachments/b5f4aaf3-a764-457a-8f0c-b7a1b93af7c5), [390 dark](https://git.kayg.org/attachments/5bbad9e8-cf4a-4dfa-a09c-f0081a5ed675), [820 light](https://git.kayg.org/attachments/38599647-6e3c-44f2-b517-fbb2c1644bd6), [820 dark](https://git.kayg.org/attachments/a4b5c0ba-aac6-42b7-b2c7-1716fa8bcbef), [1440 light](https://git.kayg.org/attachments/523b7496-7f27-496d-b2b6-2b3564f92b28), [1440 dark](https://git.kayg.org/attachments/e95e2d95-b89e-4e2e-b434-cde490a51604). - **Calendar Task row:** [390 light](https://git.kayg.org/attachments/5e376426-f061-4f58-afde-8915b4c94dd7), [390 dark](https://git.kayg.org/attachments/e4d4e7c3-3bfa-455c-82b4-5e4177d227f6), [820 light](https://git.kayg.org/attachments/01f05dca-9736-4490-a92f-4279fad0cf82), [820 dark](https://git.kayg.org/attachments/8fc709a4-fcf6-4f4c-bf41-975138ce580b), [1440 light](https://git.kayg.org/attachments/b3600ea4-d329-442a-8987-e409c8cd445c), [1440 dark](https://git.kayg.org/attachments/9351dcf8-a0ba-4b7b-8b5c-e5fe318cc6c3). - **Settings row:** [390 light](https://git.kayg.org/attachments/5f939df8-c90d-459c-b5af-8c22730b576a), [390 dark](https://git.kayg.org/attachments/ec824b98-bce5-4219-a545-b94c5d0e7b8e), [820 light](https://git.kayg.org/attachments/53e09bb5-b3af-4fcc-a6a9-26df93820295), [820 dark](https://git.kayg.org/attachments/732b8610-5fc0-4923-a941-c221530182fc), [1440 light](https://git.kayg.org/attachments/0e699d75-4035-4be7-8ac9-fa527fccd168), [1440 dark](https://git.kayg.org/attachments/134dc449-b49b-489a-98aa-2b2381146709). - **Photos folder row:** [light](https://git.kayg.org/attachments/6b683b18-914f-468f-bb12-4c324bf1adf6), [dark](https://git.kayg.org/attachments/95d5ba65-e25d-4b13-8278-939f51083740). - **Mail Inbox row:** [light](https://git.kayg.org/attachments/c301c311-9a72-4573-992a-a25a6d381beb), [dark](https://git.kayg.org/attachments/c29a3d4b-11f2-402c-9bba-291f013ac18b). - **Money account row:** [light](https://git.kayg.org/attachments/4fd94a77-13f3-4d91-8675-540663067e62), [dark](https://git.kayg.org/attachments/9bc0b5fc-9fd1-467d-b6e2-1d8839851584). **Gates:** `bun run check` output: ```text $ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/align-538/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` Verbatim `bun run test` result lines: ```text Test Files 140 passed (140) Tests 914 passed (914) Start at 21:52:10 Duration 157.80s (transform 58%, environment 17%, import 13%, tests 9%, setup 3%) Transform |component| transforming modules took 294.05s · 49% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` `bun run build` passed. The production Files screenshot run reported `FILES SCREENSHOTS PASSED` and `CSP REPORTS files: 0 across 11 pages`. `node --check apps/web/e2e/files.mjs` passed. `cargo build -p calternal-server` passed for local production capture; no Rust source changed. `cargo clean` output: `Removed 7169 files, 4.5GiB total`. I removed `apps/web/build`, `apps/web/.svelte-kit`, and the temporary worktree target directory after capture. **Known gaps:** the Photos album and Contacts lists are unavailable in this build. Photos, Mail, and Money sidebar crops are desktop-only because their shared sidebar is collapsed at phone and tablet widths. The supplementary Recent screenshot path reached Sharp and then stopped because this host lacks `libstdc++.so.6`; the requested Files-only native Playwright capture does not use Sharp and passed. Vitest also emitted its existing jsdom CSS and `Window.scrollTo()` warnings before the passing summary above.
Author
Owner

Round 2 started on job/align-538, based on origin/dev at 15e17aeafc8ea160c109e62fba57f6961c39d21f (merge-base). Reviewing all IconLabel consumers and the Files metadata columns before applying the spacing and layout fixes.

Round 2 started on `job/align-538`, based on `origin/dev` at `15e17aeafc8ea160c109e62fba57f6961c39d21f` (merge-base). Reviewing all IconLabel consumers and the Files metadata columns before applying the spacing and layout fixes.
Author
Owner

Finding: IconLabel assigns its default var(--icon-label-gap) into a local property with the same name, so the shared gap token is shadowed by a self-reference; the reviewed Files sidebar consequently has 0 px between glyph and label. SidebarLinks previously used gap: 0.68rem, so its replacement keeps that distance as a token inside IconLabel. In FileCollection the Size sort button had margin-right: -6px, reducing the Size/Kind track gutter. I have corrected the component property, restored the sidebar distance, removed the negative header margin, and added gap and metadata assertions.

Finding: IconLabel assigns its default `var(--icon-label-gap)` into a local property with the same name, so the shared gap token is shadowed by a self-reference; the reviewed Files sidebar consequently has 0 px between glyph and label. SidebarLinks previously used `gap: 0.68rem`, so its replacement keeps that distance as a token inside IconLabel. In FileCollection the Size sort button had `margin-right: -6px`, reducing the Size/Kind track gutter. I have corrected the component property, restored the sidebar distance, removed the negative header margin, and added gap and metadata assertions.
Author
Owner

Finding: the first Files matrix run at 1440 measured Size and Kind values 8.55px apart with Size aligned to the end and Kind to the start, but their header button boxes were only 2.55px apart against an 8.56px column-gap token. The new assertion caught the header overlap after the value tracks passed; I am checking the computed header boxes before the next e2e run.

Finding: the first Files matrix run at 1440 measured Size and Kind values 8.55px apart with Size aligned to the end and Kind to the start, but their header button boxes were only 2.55px apart against an 8.56px column-gap token. The new assertion caught the header overlap after the value tracks passed; I am checking the computed header boxes before the next e2e run.
Author
Owner

Finding: the 2.55px header gap came from .fc-sort applying margin-left: -6px to every sort button. Size had a local reset, but Kind still pulled its button into the gutter. I switched the default to a logical inline margin and reset Kind at its track start; its label now starts with its values, and the buttons retain the grid column gap.

Finding: the 2.55px header gap came from `.fc-sort` applying `margin-left: -6px` to every sort button. Size had a local reset, but Kind still pulled its button into the gutter. I switched the default to a logical inline margin and reset Kind at its track start; its label now starts with its values, and the buttons retain the grid column gap.
Author
Owner

Finding: the Files matrix and the new Size/Kind assertions pass, then the full Files e2e reaches its existing Pinned-before-Smart-folders assertion. allTextContents() returns ["Pinned ", "Smart folders "], so exact indexOf("Pinned") returns -1. I added the values to the failure message for evidence but left the assertion and expected order unchanged, per the test rule.

Finding: the Files matrix and the new Size/Kind assertions pass, then the full Files e2e reaches its existing Pinned-before-Smart-folders assertion. `allTextContents()` returns `["Pinned ", "Smart folders "]`, so exact `indexOf("Pinned")` returns -1. I added the values to the failure message for evidence but left the assertion and expected order unchanged, per the test rule.
Author
Owner

The first complete layout sweep measured the shared sidebar icon-to-label gap at 10.9 px, which matches the 0.68rem token, but my new assertion parsed the raw custom property as 0.68 px. It also reported NaN for Files row labels because CSSOM preserves var() text in custom properties. The rendered spacing is present; the assertion needs to read the resolved grid column-gap in pixels. I will correct that check and verify the #538 gap again.

The first complete layout sweep measured the shared sidebar icon-to-label gap at 10.9 px, which matches the 0.68rem token, but my new assertion parsed the raw custom property as 0.68 px. It also reported NaN for Files row labels because CSSOM preserves var() text in custom properties. The rendered spacing is present; the assertion needs to read the resolved grid column-gap in pixels. I will correct that check and verify the #538 gap again.
Author
Owner

After correcting the CSSOM conversion, the focused Files layout run checked 18 screens and reported no IconLabel gap failures. It exited 1 on two 390px touch-target checks: the select-all checkbox's 44×44 probe reaches the Files header at its upper-left corner (files 390 collapsed paper and toast-files 390 collapsed paper). The full layout sweep also reported unrelated calendar-grid, header-collapse, toast and broad break-it failures. I left those expectations unchanged; they need separate triage. The Files gap assertion now compares the measured icon-to-label distance with the grid's computed column-gap in pixels.

After correcting the CSSOM conversion, the focused Files layout run checked 18 screens and reported no IconLabel gap failures. It exited 1 on two 390px touch-target checks: the select-all checkbox's 44×44 probe reaches the Files header at its upper-left corner (`files 390 collapsed paper` and `toast-files 390 collapsed paper`). The full layout sweep also reported unrelated calendar-grid, header-collapse, toast and broad break-it failures. I left those expectations unchanged; they need separate triage. The Files gap assertion now compares the measured icon-to-label distance with the grid's computed column-gap in pixels.
Author
Owner

The existing Files route performance profile was attempted locally once after the UI changes. It stopped while waiting for .files-frame [data-file-item] to become visible on the initial 390px Files route, before the profile could write its JSON results. There are no run measurements to compare with the baseline. The baseline route readiness p50/p95 values are 902/985 ms at 390px, 1004/1032 ms at 820px, and 1240/1442 ms at 1440px. The profile already covers the 5k-file folder and request burst, so I did not add a duplicate profile.

The existing Files route performance profile was attempted locally once after the UI changes. It stopped while waiting for `.files-frame [data-file-item]` to become visible on the initial 390px Files route, before the profile could write its JSON results. There are no run measurements to compare with the baseline. The baseline route readiness p50/p95 values are 902/985 ms at 390px, 1004/1032 ms at 820px, and 1240/1442 ms at 1440px. The profile already covers the 5k-file folder and request burst, so I did not add a duplicate profile.
Author
Owner

align-538 round 2 report

Head: d36b64b48062e13f06601f7b7df89dc0f07d9ed8

Commits:

  • 7b1c6a08a Keep Files metadata columns spaced and aligned
  • be94a7913 Restore token gaps in shared IconLabel rows
  • d36b64b48 Resolve IconLabel gap in layout audit

Changes

  • packages/ui/src/components/IconLabel.svelte now applies its own positive column gap. It resolves the configured gap through a separate CSS property.
  • packages/ui/src/tokens.css, apps/web/src/lib/components/SidebarLinks.svelte, and apps/web/src/lib/photos/PhotosSidebar.svelte restore the shared sidebar gap. Other migrated consumers use the shared IconLabel default gap.
  • packages/ui/src/components/files/FileCollection.svelte spaces the Size and Kind metadata columns with the column-gap token, aligns Size to the end and Kind to the start, and keeps Name flexible.
  • apps/web/e2e/files.mjs checks metadata spacing/alignment and the 390/820/1440 layouts in light/dark with the sidebar open/collapsed.
  • apps/web/e2e/layout-sweep.mjs asserts the resolved icon-label gap for sidebar and list rows.

Decisions not covered by DESIGN.md

  • Added --sidebar-icon-label-gap: 0.68rem to preserve the spacing that SidebarLinks used before the migration.
  • Used var(--toolbar-gap) between Files metadata columns, with Size right-aligned and Kind left-aligned.
  • The layout assertion reads computed columnGap in CSS pixels, so it checks the resolved token instead of parsing an unresolved custom-property value.

Verification

bun run check passed. Output:

$ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/align-538/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test passed. Output:

$ vitest run

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/align-538/apps/web

Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Not implemented: Window's scrollTo() method
Could not parse CSS stylesheet

 Test Files  140 passed (140)
      Tests  914 passed (914)
   Start at  00:30:07
   Duration  175.57s (transform 53%, environment 17%, import 16%, tests 10%, setup 4%)

Environment  |component| jsdom was created 43 times · 153.34s total, 26% of tracked time
             create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
             learn more: https://vitest.dev/guide/improving-performance#test-environments

The Files browser run checked the new sizing matrix and metadata assertions, then stopped at the existing Pinned/Smart folders ordering assertion. I left that expectation unchanged. The diagnostic is that the labels include trailing spaces (["Pinned ", "Smart folders "]), so the existing exact indexOf comparison does not match. Output:

$ bun e2e/files.mjs
CSP REPORTS files: 0 across 13 pages
732 | 		await row(page, 'Inbox').dblclick();
733 | 		await row(page, 'gamma.md').waitFor();
734 | 		await page.getByRole('heading', { name: 'Pinned', exact: true }).waitFor();
735 | 		await page.getByRole('heading', { name: 'Smart folders', exact: true }).waitFor();
736 | 		const sidebarSections = await page.locator('.sidebar-section-label').allTextContents();
737 | 		assert.ok(sidebarSections.indexOf('Pinned') >= 0 && sidebarSections.indexOf('Pinned') < sidebarSections.indexOf('Smart folders'));
               ^
AssertionError: false == true
 generatedMessage: true,
     actual: false,
   expected: true,
   operator: "==",
       diff: "simple",
       code: "ERR_ASSERTION"

      at main (/home/kayg/Developer/calternal-wt/align-538/apps/web/e2e/files.mjs:737:10)
      at processTicksAndRejections (native:7:39)

Bun v1.4.2 (Linux x64)
error: script "test:e2e:files" exited with code 1

The post-fix Files-only layout sweep checked 18 screens with no IconLabel gap failures. It exited 1 on two existing 390px touch-target checks:

CSP REPORTS layout-sweep: 0 across 2 pages
checked 18 screens
FAIL files 390 collapsed paper: hit area button.checkbox-control.file "Select all 10 items in F" is under 44×44 (15.59,65.75 44.00×44.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); 21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00); hit area button.fc-sort.col-name "Name, sorted ascending. " is under 44×44 (89.59,75.00 274.81×26.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); -21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00)
FAIL toast-files 390 collapsed paper: hit area button.checkbox-control.file "Select all 10 items in F" is under 44×44 (15.59,65.75 44.00×44.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); 21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00); hit area button.fc-sort.col-name "Name, sorted ascending. " is under 44×44 (89.59,75.00 274.81×26.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); -21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00)

The screenshot-only Files run passed:

FILES SCREENSHOTS PASSED
CSP REPORTS files: 0 across 11 pages

The full layout sweep exited 1 with existing failures in other areas (including calendar clipping, keyboard navigation, sheet scrolling, RTL overflow, and toast checks). The earlier sweep also exposed a gap-parser false positive; the parser is fixed, and the focused post-fix Files sweep found no gap failures. I did not change unrelated expectations.

The existing route-perf profile was attempted once locally. It timed out waiting for .files-frame [data-file-item] on the first 390px Files route and wrote no result JSON. Baseline route readiness p50/p95 is 902/985 ms at 390px, 1004/1032 ms at 820px, and 1240/1442 ms at 1440px. No current comparison is available. The release web build succeeded. Rust source was not changed, so no Rust crate gates apply. Final cargo clean output: Removed 0 files.

Screenshots

All screenshots are 3×. The Settings phone image shows the real mobile settings sheet. Files list captures cover 820px and 1440px in both themes. Files sidebar captures include open and collapsed states at 390px and 820px.

Known gaps: the unchanged Files ordering assertion fails because of trailing whitespace; the focused layout run exits on two touch-target checks; and the route performance profile produced no measurements. The web unit/type gates pass.

# align-538 round 2 report Head: `d36b64b48062e13f06601f7b7df89dc0f07d9ed8` Commits: - `7b1c6a08a` Keep Files metadata columns spaced and aligned - `be94a7913` Restore token gaps in shared IconLabel rows - `d36b64b48` Resolve IconLabel gap in layout audit ## Changes - `packages/ui/src/components/IconLabel.svelte` now applies its own positive column gap. It resolves the configured gap through a separate CSS property. - `packages/ui/src/tokens.css`, `apps/web/src/lib/components/SidebarLinks.svelte`, and `apps/web/src/lib/photos/PhotosSidebar.svelte` restore the shared sidebar gap. Other migrated consumers use the shared IconLabel default gap. - `packages/ui/src/components/files/FileCollection.svelte` spaces the Size and Kind metadata columns with the column-gap token, aligns Size to the end and Kind to the start, and keeps Name flexible. - `apps/web/e2e/files.mjs` checks metadata spacing/alignment and the 390/820/1440 layouts in light/dark with the sidebar open/collapsed. - `apps/web/e2e/layout-sweep.mjs` asserts the resolved icon-label gap for sidebar and list rows. ## Decisions not covered by DESIGN.md - Added `--sidebar-icon-label-gap: 0.68rem` to preserve the spacing that SidebarLinks used before the migration. - Used `var(--toolbar-gap)` between Files metadata columns, with Size right-aligned and Kind left-aligned. - The layout assertion reads computed `columnGap` in CSS pixels, so it checks the resolved token instead of parsing an unresolved custom-property value. ## Verification `bun run check` passed. Output: ```text $ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/align-538/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test` passed. Output: ```text $ vitest run RUN v5.0.1 /home/kayg/Developer/calternal-wt/align-538/apps/web Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Not implemented: Window's scrollTo() method Could not parse CSS stylesheet Test Files 140 passed (140) Tests 914 passed (914) Start at 00:30:07 Duration 175.57s (transform 53%, environment 17%, import 16%, tests 10%, setup 4%) Environment |component| jsdom was created 43 times · 153.34s total, 26% of tracked time create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files) learn more: https://vitest.dev/guide/improving-performance#test-environments ``` The Files browser run checked the new sizing matrix and metadata assertions, then stopped at the existing Pinned/Smart folders ordering assertion. I left that expectation unchanged. The diagnostic is that the labels include trailing spaces (`["Pinned ", "Smart folders "]`), so the existing exact `indexOf` comparison does not match. Output: ```text $ bun e2e/files.mjs CSP REPORTS files: 0 across 13 pages 732 | await row(page, 'Inbox').dblclick(); 733 | await row(page, 'gamma.md').waitFor(); 734 | await page.getByRole('heading', { name: 'Pinned', exact: true }).waitFor(); 735 | await page.getByRole('heading', { name: 'Smart folders', exact: true }).waitFor(); 736 | const sidebarSections = await page.locator('.sidebar-section-label').allTextContents(); 737 | assert.ok(sidebarSections.indexOf('Pinned') >= 0 && sidebarSections.indexOf('Pinned') < sidebarSections.indexOf('Smart folders')); ^ AssertionError: false == true generatedMessage: true, actual: false, expected: true, operator: "==", diff: "simple", code: "ERR_ASSERTION" at main (/home/kayg/Developer/calternal-wt/align-538/apps/web/e2e/files.mjs:737:10) at processTicksAndRejections (native:7:39) Bun v1.4.2 (Linux x64) error: script "test:e2e:files" exited with code 1 ``` The post-fix Files-only layout sweep checked 18 screens with no IconLabel gap failures. It exited 1 on two existing 390px touch-target checks: ```text CSP REPORTS layout-sweep: 0 across 2 pages checked 18 screens FAIL files 390 collapsed paper: hit area button.checkbox-control.file "Select all 10 items in F" is under 44×44 (15.59,65.75 44.00×44.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); 21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00); hit area button.fc-sort.col-name "Name, sorted ascending. " is under 44×44 (89.59,75.00 274.81×26.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); -21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00) FAIL toast-files 390 collapsed paper: hit area button.checkbox-control.file "Select all 10 items in F" is under 44×44 (15.59,65.75 44.00×44.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); 21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00); hit area button.fc-sort.col-name "Name, sorted ascending. " is under 44×44 (89.59,75.00 274.81×26.00; ::before auto/auto/auto/auto rgba(0, 0, 0, 0); -21,-21 hits div.fc-head.svelte-w3r92k "Name" at 13.59,66.00 362.81×44.00) ``` The screenshot-only Files run passed: ```text FILES SCREENSHOTS PASSED CSP REPORTS files: 0 across 11 pages ``` The full layout sweep exited 1 with existing failures in other areas (including calendar clipping, keyboard navigation, sheet scrolling, RTL overflow, and toast checks). The earlier sweep also exposed a gap-parser false positive; the parser is fixed, and the focused post-fix Files sweep found no gap failures. I did not change unrelated expectations. The existing route-perf profile was attempted once locally. It timed out waiting for `.files-frame [data-file-item]` on the first 390px Files route and wrote no result JSON. Baseline route readiness p50/p95 is 902/985 ms at 390px, 1004/1032 ms at 820px, and 1240/1442 ms at 1440px. No current comparison is available. The release web build succeeded. Rust source was not changed, so no Rust crate gates apply. Final `cargo clean` output: `Removed 0 files`. ## Screenshots All screenshots are 3×. The Settings phone image shows the real mobile settings sheet. Files list captures cover 820px and 1440px in both themes. Files sidebar captures include open and collapsed states at 390px and 820px. - [Files 390px sidebar (light, 3×)](https://git.kayg.org/attachments/6fc97f58-41c6-468b-a396-4195bb7c4d23) - [Files 390px sidebar (dark, 3×)](https://git.kayg.org/attachments/3fc3f075-cfc2-42b2-a4be-09159e1b8cab) - [Files 820px sidebar (light, 3×)](https://git.kayg.org/attachments/5c8c0eff-234f-482a-b965-75cfc2a55d02) - [Files 820px sidebar (dark, 3×)](https://git.kayg.org/attachments/9d5dd392-de6f-4aa3-96a6-d87fcb1a7dd4) - [Files 1440px sidebar (light, 3×)](https://git.kayg.org/attachments/4fa8255e-5a6c-4860-9c48-548001b36ea2) - [Files 1440px sidebar (dark, 3×)](https://git.kayg.org/attachments/35094a31-4c81-49d9-9180-997f28e93e36) - [Calendar 390px sidebar (light, 3×)](https://git.kayg.org/attachments/bac63db7-9bd5-42f1-9140-bdc33c374b59) - [Calendar 390px sidebar (dark, 3×)](https://git.kayg.org/attachments/3329232a-44f5-4ce9-81ad-aa9566850cce) - [Calendar 820px sidebar (light, 3×)](https://git.kayg.org/attachments/723251e2-fc65-4970-bc9c-0e4ed91f06b1) - [Calendar 820px sidebar (dark, 3×)](https://git.kayg.org/attachments/0614ec07-c221-44ba-b56f-bf56dedbfa55) - [Calendar 1440px sidebar (light, 3×)](https://git.kayg.org/attachments/42e3406c-65f3-4688-8022-071072705db7) - [Calendar 1440px sidebar (dark, 3×)](https://git.kayg.org/attachments/16b4e44a-dad9-4b26-b5bd-809cf8b089cd) - [Settings 390px mobile sheet (light, 3×)](https://git.kayg.org/attachments/fc2f2f09-4914-4632-814d-1b2bfb771b56) - [Settings 390px mobile sheet (dark, 3×)](https://git.kayg.org/attachments/efb33db2-caa3-4db1-b224-f5e9836f773d) - [Settings 820px sidebar (light, 3×)](https://git.kayg.org/attachments/9cfcc335-6099-48bd-9cd2-70ece25096f3) - [Settings 820px sidebar (dark, 3×)](https://git.kayg.org/attachments/3ecd56c3-d6f0-472e-90cd-011cbd822d77) - [Settings 1440px sidebar (light, 3×)](https://git.kayg.org/attachments/e48ecc53-505f-489e-9578-3289e689a467) - [Settings 1440px sidebar (dark, 3×)](https://git.kayg.org/attachments/257fbe97-d7cb-46d6-9782-e155e3e449ff) - [Files 390px sidebar collapsed (light, 3×)](https://git.kayg.org/attachments/196a027c-1957-41f1-b485-78d20e70389d) - [Files 390px sidebar collapsed (dark, 3×)](https://git.kayg.org/attachments/857b479d-9668-41e3-bee9-d811a356635d) - [Files 820px sidebar collapsed (light, 3×)](https://git.kayg.org/attachments/bda69268-f0d9-44eb-bbf7-cb6bff156b2b) - [Files 820px sidebar collapsed (dark, 3×)](https://git.kayg.org/attachments/c751eb7e-b689-4a6f-b6df-ee73dc73b146) - [Files list 820px (light, 3×)](https://git.kayg.org/attachments/06fc69e6-b9e3-43e0-91ce-9b7171f91a94) - [Files list 820px (dark, 3×)](https://git.kayg.org/attachments/b1798c97-2c46-43ff-8dcf-aa25a9689a6a) - [Files list 1440px (light, 3×)](https://git.kayg.org/attachments/9d16627f-d015-4902-b07c-e4154c05c838) - [Files list 1440px (dark, 3×)](https://git.kayg.org/attachments/4e6d4076-0916-48ec-ad16-d458fdadfc94) Known gaps: the unchanged Files ordering assertion fails because of trailing whitespace; the focused layout run exits on two touch-target checks; and the route performance profile produced no measurements. The web unit/type gates pass.
Author
Owner

Complete changed-file list on this branch versus origin/dev (round 1 plus round 2):

  • apps/web/e2e/files.mjs
  • apps/web/e2e/layout-sweep.mjs
  • apps/web/src/lib/components/SidebarLinks.svelte
  • apps/web/src/lib/files/FilesSidebar.svelte
  • apps/web/src/lib/mail/MailView.svelte
  • apps/web/src/lib/photos/PhotosSidebar.svelte
  • apps/web/src/lib/styles/sidebar.css
  • apps/web/src/routes/settings/parts/SettingsRow.svelte
  • packages/ui/src/components/IconLabel.svelte
  • packages/ui/src/components/calendar/AgendaList.svelte
  • packages/ui/src/components/files/FileCollection.svelte
  • packages/ui/src/components/files/FileThumb.svelte
  • packages/ui/src/index.ts
  • packages/ui/src/tokens.css
Complete changed-file list on this branch versus `origin/dev` (round 1 plus round 2): - `apps/web/e2e/files.mjs` - `apps/web/e2e/layout-sweep.mjs` - `apps/web/src/lib/components/SidebarLinks.svelte` - `apps/web/src/lib/files/FilesSidebar.svelte` - `apps/web/src/lib/mail/MailView.svelte` - `apps/web/src/lib/photos/PhotosSidebar.svelte` - `apps/web/src/lib/styles/sidebar.css` - `apps/web/src/routes/settings/parts/SettingsRow.svelte` - `packages/ui/src/components/IconLabel.svelte` - `packages/ui/src/components/calendar/AgendaList.svelte` - `packages/ui/src/components/files/FileCollection.svelte` - `packages/ui/src/components/files/FileThumb.svelte` - `packages/ui/src/index.ts` - `packages/ui/src/tokens.css`
Author
Owner

#427 Files production evidence is attached at https://git.kayg.org/attachments/fd3cbd26-fb49-430d-a2f7-e1a5211a69f9 (390/820/1440, Light/Dark, plus zoom). Existing row-box geometry assertions pass, but phone rows align icons with the combined filename/metadata box; this is not proof of title cap-height alignment. No CSS or icon alignment changed in #427. Keep cap-height visual signoff with Claude under this issue; do not treat the geometry checks as visual approval.

#427 Files production evidence is attached at https://git.kayg.org/attachments/fd3cbd26-fb49-430d-a2f7-e1a5211a69f9 (390/820/1440, Light/Dark, plus zoom). Existing row-box geometry assertions pass, but phone rows align icons with the combined filename/metadata box; this is not proof of title cap-height alignment. No CSS or icon alignment changed in #427. Keep cap-height visual signoff with Claude under this issue; do not treat the geometry checks as visual approval.
Author
Owner

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).
kayg closed this issue 2026-10-01 09:17:41 +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#538
No description provided.