ALIGN: one shared icon+label primitive with cap-height centring + automated alignment check #253
Open
opened 2026-09-27 18:04:57 +00:00 by kayg
·
108 comments
No Branch/Tag specified
dev
wip/directory-1199
wip/delete-1119
wip/collabrev-1197
wip/collabloss-1197
wip/cards2-1083
wip/cards-1083
wip/canvas-visual
wip/canvasvis2-976
wip/calhdr-1112
wip/calcards-1115
wip/browserfix
wip/blocks-1125
wip/allday-1107
wip/agenda-decks
wip/agenda-1086
wip/adv7c-1105
wip/txentry-1198
wip/trayicons2-1095
wip/trayicons-1095
wip/tagperf-1186
wip/sidebar3-1094
wip/rev2-webperf
wip/rev2-money-ident
job/adv-1202
wip/restyle-notes
wip/previewcard-1098
wip/palette2-1123
wip/palette-1093
wip/onboard2-1141
wip/onboard-1141.aborted-early
wip/onboard-1141
wip/nlpchip-1127
wip/morph-1104
wip/merge-round-7c5
wip/merge-round-7c4
job/notifloop-1194
wip/merge-round-7c3
wip/merge-round-7c2
wip/merge-round-7c
wip/mchrome-1084
wip/mailghost2-1094
wip/mailghost-1094
wip/kbpreview2-1118
wip/kbpreview-1118
wip/kanban-1092
wip/importhang-1121
wip/hiderev-1153
wip/hide4-1153
wip/hide3-1153
wip/hide2-1153
wip/hide-1153
wip/editreg-1132
job/collabloss-1197
wip/editorrail3-1113
wip/editorrail2-1113
wip/editorrail-1113
wip/e2e-b2-1071
wip/e2e-b-1071
wip/draw4-1101
wip/draw3-1101
wip/draw2-1101
wip/draw-1101
wip/directory-1199-r
job/onboard-1141
job/hide-1153
job/perf-1124
job/perf2-1124
job/tocrail-1191
job/restyle-settings
wip/restyle-settings
job/segmented-1200
wip/notifloop-1194
job/tagperf-1186
wip/segmented-1200
job/restyle-files
job/tagdnd-1187
job/merge30
job/cards-1179
wip/cards2-1179
wip/cards-1179
wip/tocrail-1191
wip/tagdnd-1187
wip/restyle-files
wip/perf-1124
wip/merge30j
job/restyle-notes
job/wizchoices-1140
wip/wizchoices-1140
wip/restyle-1190
job/moneyfmt-1180
job/txentry-1198
wip/moneyfmt2-1180
wip/moneyfmt-1180-r
wip/moneyfmt-1180
job/pillglass-1189
job/flags-1181
wip/flags-1181
job/restyle-1190
job/restyle-mailmoney
job/restyle-search
job/settingsreg-1195
job/wizard-1140
site/website
wip/wizardrev2-1140
wip/wizardrev-1140
wip/wizard5-1140
wip/wizard4-1140
wip/wizard3-1140
wip/wizard2-1140
wip/wizard-1140
wip/pillglass-1189
wip/settingsreg-1195
job/merge29
job/fu-1171
wip/merge29j
wip/fu-1171
job/fu-1166
job/directory-1199
job/proflog-1204
job/txresearch-1188
wip/fu-1166
job/merge28
job/search-1066
wip/search-1066
wip/merge28j
job/gateslot-1182
job/bulkimport-1157
job/mailnet-1160
wip/mailnetrev-1160
wip/mailnet-1160
wip/bulkrev-1157
wip/bulkimport-1157
job/startup-1161
wip/startup-1161
job/merge27
job/linkcards-1151
wip/linkcards3-1151
wip/linkcards2-1151
wip/linkcards-1151
job/traydate-1144
wip/traydate3-1144
wip/traydate2-1144
wip/traydate-1144
job/draw-1101
wip/merge27j
job/blockpill-1152
wip/blockpill3-1152
wip/blockpill2-1152
wip/blockpill-1152
job/minihover-1149
wip/minihover2-1149
wip/minihover-1149
job/merge25
wip/merge25-r
wip/merge25b
wip/merge25
job/inspector-1129
job/tags-1110
wip/inspector3-1129
wip/inspector2-1129
wip/inspector-1129
wip/tagsrev-1110
wip/tags2-1110
wip/tags-1110
job/dates-1148
wip/datesrev-1148
wip/dates2-1148
wip/dates-1148
job/licence-1145
wip/licence2-1145
wip/licence-1145
job/selfhost-1156
job/merge23
wip/merge23
job/tagfilter-1109
wip/tagfilter2-1109
wip/tagfilter-1109
job/kbd-1134
wip/kbd2-1134
wip/kbd-1134
job/palfoot-1137
wip/selfhost-1156
wip/palfoot2-1137
wip/palfoot-1137
job/toggle-1158
wip/toggle-1158
job/kbpreview-1118
job/docratchet-1155
job/perflint-1133
job/devtests-1159
wip/docratchet-1155
wip/devtests-1159
job/segv-1136
wip/toast-1142
wip/segv-1136
job/toast-1142
job/blockreload-1147
wip/blockreload-1147
job/font-1150
wip/font-1150
job/importui-1120
job/minimonth-1149
wip/importui-1120
wip/minimonth-1149
job/depcheck-1146
wip/perflint-1133
wip/depcheck-1146
job/calcards-1115
job/blocks-1125
job/plus-1128
job/shift-1138
wip/plus2-1128
wip/plus-1128
wip/shift-1138
job/moneyfid-1130
job/editorrail-1113
wip/moneyrev-1130
wip/moneyfid-1130
job/noext-851
wip/noext-851
wip/noext3-851
wip/noext2-851
job/week-1135
wip/week-1135
job/editreg-1132
job/smoke-1122
wip/smoke-1122
job/docs-1143
job/palette2-1123
job/calhdr-1112
job/nlpchip-1127
job/mailghost-1094
job/reconnect-1131
wip/reconnect-1131
job/trayicons-1095
job/delete-1119
job/importhang-1121
job/cards-1083
job/palette-1093
job/mchrome-1084
job/e2e-a-1071
job/canvas-visual
job/previewcard-1098
job/allday-1107
wip/e2e-a2-1071
wip/e2e-a-1071
job/e2e-b-1071
job/adv7c-1105
job/kanban-1092
job/agenda-1086
job/merge-round-7c
job/morph-1104
wip/surfaces-p2
job/merge-round-9
wip/merge-round-9
job/7cfix-small
wip/7cfix-small
job/mailui-1078
job/merge-round-8
wip/merge-round-8
wip/mailui-1078
job/mailround-1038
job/applemail-accept
wip/settitle-1068
wip/mailround2-1038
wip/mailround-1038
wip/e2e-7b
job/crash-1069
wip/crash-1069
job/searchlost-1066
wip/searchlost-1066
job/7b-reconcile
job/flake-1065
wip/flake-1065
wip/merge-round-7b7
wip/merge-round-7b6
wip/merge-round-7b5
wip/merge-round-7b4
wip/7b-reconcile
job/appupdate-1059
job/nfd-1044
wip/appupdate-1059
job/e2e-7b
job/loop-1062
wip/loop-1062
job/pdfprev-1045
job/invtoggle-1053
wip/pdfprev-1045
wip/nfd-1044
wip/invtoggle-1053
job/7bfix-e2e
job/mailstress-b
wip/7bfix-e2e
wip/mailstress-b
job/7bfix-adv
wip/7bfix-adv
job/mailstress-a
job/stack-1054
wip/stack-1054
wip/mailstress-a
job/mailstress-1038
wip/mailstress-1038
job/upload500-1051
wip/upload500-1051
job/share-1034
wip/share-1034
job/syncerr-1037
job/7bfix-photos
wip/7bfix-photos
job/paste-1036
job/setside-1039
wip/setside-1039
wip/paste-1036
job/lease-1042
wip/syncerr-1037
wip/lease-1042
job/7bfix-data
job/passkeybind-1043
wip/apprevoke-1041
job/invite-1035
wip/invite-1035
job/merge-round-7b2
wip/merge-round-7b2
job/mailproxy-486
job/apprevoke-1041
job/rebuild-1033
job/pillborder-1029
wip/pillborder-1029
wip/mailproxy-486
wip/applemail-486
job/headless-998
wip/headless-998
job/groups-1028
wip/groups-1028
job/rebuildwarn-1016
wip/rebuildwarn-1016
job/startup-1011
wip/startup-1011
job/monthpill-1009
job/bgthumb-1025
job/sharetitle-1012
wip/monthpill-1009
wip/bgthumb-1025
wip/sharetitle-1012
job/canvas-cards-977
wip/canvas-cards-977
job/canvas-pencil-978
job/canvas-sketch-990
wip/canvas-sketch-990
wip/canvas-pencil-978
job/canvas-files-989
wip/canvas-files-989
job/canvas-collab-991
wip/canvas-collab-991
job/weekscroll-1018
wip/weekscroll-1018
wip/canvas-core-976
job/canvas-core-976
job/round-drag
wip/round-drag
job/round-settings
job/browserfix
wip/oapi-974
job/oapi-974
job/hist2-integrate
job/mailhtml-726
wip/mailhtml-726
wip/hist2-integrate
job/moneyfu-984
job/drag-1015
wip/drag-1015
job/rename-1017
wip/rename-1017
job/hist2-api
wip/hist2-api
job/oneacct-1014
wip/oneacct-1014
wip/moneyfu-984
job/hist2-bench
job/hist2-restore
wip/hist2-bench
job/hist2-write
job/hotfix-724
wip/hotfix-724
wip/hist2-write
wip/hist2-restore
job/hist2-store
job/hist2-ui
wip/hist2-ui
wip/hist2-store
job/searchstarve-965
job/shutdown-963
wip/shutdown-963
wip/pubedit-981
job/pubedit-981
job/analytics-973
wip/searchstarve-965
job/authflash-850
job/weeklane-969
job/pvtitle-1004
job/hist-975
wip/authflash-850
job/voicepill-617
wip/pvtitle-1004
job/headring-1003
wip/weeklane-969
wip/voicepill-617
wip/headring-1003
wip/analytics-973
job/agentscope-980
wip/thumbsandbox-988
job/thumbsandbox-988
wip/hist-975
job/links-856
wip/links-856
job/davetag-966
wip/davetag-966
job/filesstorm-1000
job/hoverpad-725
wip/filesstorm-1000
job/ffmpegblas-993
job/merge-round-7a
wip/hoverpad-725
wip/ffmpegblas-993
job/nowdot-1002
wip/verify-7a
job/noteid-857
wip/nowdot-1002
wip/noteid-857
wip/merge-round-7a
wip/agentscope-980
job/imapedge
job/a11yfix2
wip/imapedge-941
wip/imapedge
wip/a11yfix2
job/notetask-986
job/logheading
wip/logheading-998
job/textthumb-652
job/photolive-987
wip/photolive-987
job/davactive-983
job/savefix-985
job/tabicons-607
wip/davactive-983
wip/tabicons-607
wip/notetask-986
wip/savefix-985
job/dirid-627
job/buildspeed-1007
wip/dirid-627
job/agenda-decks
job/perfguards-impl
job/undo-a11y
wip/undo-a11y
job/mailperf
job/wal-824
wip/settings-50
job/settings-50
job/notesfilter-606
wip/notesfilter-606
job/surfaces-p2
wip/wal-824
job/maillayouts
wip/mailperf
wip/maillayouts
job/taskmeta-659
job/money-ident
wip/money-ident
wip/taskmeta-659
job/errstates
wip/perfguards-impl
job/headings-881
wip/headings-881
wip/errstates
job/voice-619
job/gaps-827
job/notesperf
wip/notesperf
wip/voice-619
job/hddsql-549
job/perf-stream-668
wip/perf-stream-668
wip/deeplinks-fix
job/deeplinks-fix
job/authfix
job/docsfix-rust
wip/docsfix-rust
job/webperf
job/docsfix-web
job/datafix2
job/webdav-lock-476
job/copyfix
wip/copyfix
wip/webperf
job/focus-658
wip/protofix
job/mediafix
job/protofix
wip/mediafix
job/agentfix
job/hhmm-724
wip/agentfix
job/undo-722
job/reuse
wip/webdav-lock-476
wip/reuse
job/scopefix
job/datafix
wip/hhmm-724
wip/undo-722
job/surfaces-p1
wip/hddsql-549
job/voicememos-618
wip/datafix2
wip/surfaces-p1
job/fix-940
wip/fix-940
job/blaze-surfaces
wip/datafix
wip/blaze-surfaces
job/taskday-655
job/linknav-639
wip/linknav-639
wip/gaps-827
job/isolation-707
job/audiophotos-720
wip/audiophotos-720
job/advfind-664
wip/voicememos-618
wip/taskday-655
wip/isolation-707
wip/advfind-664
wip/scopefix
wip/focus-658
job/testgaps
wip/testgaps
job/overscroll-718
wip/authfix
job/deps
wip/overscroll-718
job/rev2-agentfix
job/rev2-money-ident
job/rev2-mailperf
wip/deps
job/hardening-728
wip/hardening-728
job/searchgen-832
wip/searchgen-832
job/photopw-849
job/mailsql-825
wip/photopw-849
job/sharefix
wip/sharefix
job/rev2-mailhtml-726
job/rev2-perfguards
job/copyval-723
job/lightglass-r2
wip/lightglass-r2
wip/docsfix-web
job/copy-audit
job/macinterop-staging-r2
job/design-sync
job/rev2-taskmeta-659
job/rev2-webperf
job/docs-audit
job/rev2-advfind-664
job/rev2-mailproxy-486
job/states-audit
job/rev2-datafix
job/design-drift
job/test-gaps
job/rev2-voicememos-618
job/rev2-mediafix
job/rev2-deps
job/rev2-datafix2
job/licence-audit
job/issue-hygiene
job/rev2-protofix
job/rev2-voice-619
job/rev2-isolation-707
job/rev2-surfaces-p1
job/deeplink-audit2
job/rev2-audiophotos-720
wip/test-gaps
job/rev2-overscroll-718
job/rev2-undo-722
wip/states-audit
job/rev2-dropmd-719
job/rev2-linknav-639
job/merge-7b-plan
wip/merge-7b-plan
job/rev2-taskday-655
wip/mailsql-825
job/rev2-webdav-lock-476
job/rev2-browserfix
wip/design-drift
job/rev2-hddsql-549
wip/deeplink-audit2
job/rev2-scopefix
job/rev2-authfix
job/rev2-hardening-728
job/rev2-wal-824
job/rev2-sharefix
job/calsidebar-638
job/chrome-audit
job/ioperf
wip/ioperf
wip/chrome-audit
wip/calsidebar-638
job/dropmd-719
wip/dropmd-719
job/ocr-build
wip/ocr-build
job/blaze-settings
wip/copyval-723
job/toastring-721
wip/toastring-721
job/deployfix-732
wip/deployfix-732
wip/blaze-settings
job/money-import-recheck
job/rev-a11y
job/perf-arch-db
job/rev-7b-data
wip/textthumb-652
wip/perf-arch-db
job/sec-protocols
job/sidehdr-660
job/rev-7b-security
job/research-surfaces
job/rev-design-gaps
job/rev-mcp-api
wip/sidehdr-660
job/perf-arch-memory
wip/sec-protocols
job/perf-arch-bundle
job/snapedge-714
wip/rev-mcp-api
job/sec-supplychain
wip/research-surfaces
job/perf-arch-sync
job/rev-consistency
job/perf-arch-server
wip/perf-arch-server
wip/perf-arch-memory
job/perf-arch-io
job/perf-arch-client
job/sec-fs
job/sec-mcp-scopes
job/sec-sharing
job/perf-guards
job/sec-browser
job/sec-admin-deploy
job/sec-auth
wip/snapedge-714
job/bgpicker-717
wip/perf-arch-bundle
wip/money-import-recheck
job/advsetup-654
wip/bgpicker-717
wip/advsetup-654
job/burst-709
job/kbdcaps-710
job/app-pw-chooser
wip/burst-709
wip/app-pw-chooser
job/imaptest-625
wip/kbdcaps-710
job/fix-499
wip/fix-499
job/perf-mut-667
job/calimg-589
job/perf-snap-666
wip/calimg-589
wip/perf-snap-666
wip/perf-mut-667
job/perf-cache-665
wip/perf-cache-665
job/voicefiles-620
wip/voicefiles-620
job/admin-burst-705
wip/admin-burst-705
job/voicememos-review
wip/voicememos-review
wip/ryw-653
job/ryw-653
job/writeonopen-661
job/instant-663
wip/writeonopen-661
job/money-import-review
wip/money-import-review
wip/importjs-610
review/integrations-407-round6
wip/integrations-review
job/dragghost-612
wip/dragghost-612
job/integrations
wip/integrations
job/decider-656
job/merge-round-6
job/perf-rerun
wip/merge-round-6
job/integrations-review-round5
job/selalign-576
wip/selalign-576
job/mcp-events-491
job/files-631
job/cal-e2e-569
wip/cal-e2e-569
job/reload-423
wip/reload-423
wip/mcp-events-491
wip/files-631
job/notesbridge-644
wip/notesbridge-644
job/editor-series
job/calcard-series
wip/calcard-series
job/mcp-events-review-491
wip/mcp-events-review
wip/editor-series
job/quirks-546
job/integrations-recheck
job/tocrail-636
wip/tocrail-636
wip/quirks-546
wip/reminders-643
job/reminders-643
wip/davscale-573
job/davscale-573
job/integrations-review
wip/ocr-eval-584
job/ocr-eval-584
job/esc-537
wip/esc-537
job/toastname-586
wip/toastname-586
job/submenu-579
wip/submenu-579
job/tasks-mode
wip/tasks-mode
job/agentdocs-630
job/dupwrite-634
wip/agentdocs-630
wip/dupwrite-634
job/lightglass-588
wip/lightglass-588
job/tabswitch-549
job/ghosttask-623
wip/ghosttask-623
job/toaststack-616
job/weekstate-609
job/mailsync-613
wip/mailsync-613
wip/weekstate-609
job/maildup-626
wip/tabswitch-549
wip/maildup-626
wip/toaststack-616
job/motion-611
wip/motion-611
job/tlstest-601
wip/tlstest-601
job/perf-495
job/floating-sheet
wip/floating-sheet
job/remdup-585
wip/remdup-585
job/fix-502
wip/fix-502
job/attachplay-622
job/perf-batch
wip/perf-batch-563
wip/perf-495
hotfix/mail-sync-diag
job/mail-m3
wip/mail-m3
job/attach-poof-603
job/calhover-608
job/editorbar-604
job/mentions-605
job/merge-round-4
job/allday-514
wip/merge-round-4
wip/allday-514
job/merge-round-4a
wip/merge-round-4a
job/sharestack-580
job/fix-501
wip/sharestack-580
wip/fix-501
job/perf-batch-563
job/apw-cache-review
wip/apw-cache-review
job/probe-520
wip/probe-520
job/mac-393
wip/mac-393
job/header-571
job/flake-513
wip/flake-513
job/docs-thumb-547
wip/header-571
job/webcal-572
wip/webcal-572
wip/shortcuts-542
job/shortcuts-542
wip/docs-thumb-547
job/caldav-stress
wip/caldav-stress
wip/sweep-478
job/apw-cache-512
wip/apw-cache-512
job/money-empty-540
wip/restart-505
wip/money-empty-540
wip/fix-510
job/restart-505
job/fix-503
job/perf-496
wip/perf-496
job/fix-498
wip/fix-498
job/info-inspector-465
wip/info-inspector-465
job/fix-510
job/fix-507
wip/fix-507
wip/fix-503
job/fix-493
job/money-kinds
wip/money-kinds
job/hygiene-548
job/merge-round-3
wip/fix-493
job/drag-snap-536
wip/merge-round-3
wip/merge-round-0930
wip/drag-snap-536
job/align-538
wip/align-538
job/bg-flash
wip/bg-flash
job/money-import
job/search-count-544
wip/search-count-544
wip/money-import
job/settings-key-541
wip/settings-key-541
job/toast-539
job/preview-421
wip/preview-421
wip/toast-539
job/tasks-500-531
job/title-plain-526
wip/title-plain-526
wip/tasks-500-531
job/notes-bridge
wip/parity-484
job/parity-484
job/files-slow
job/crash-525
wip/notes-bridge
wip/files-slow
wip/crash-525
job/kbd-motion-527
wip/bg-422
job/analytics-504
wip/analytics-504
wip/kbd-motion-527
job/upload-pill-523
wip/upload-pill-523
wip/tray-order
job/tray-order
wip/overflow-mid
wip/merge-round-2
job/perf-494
wip/perf-494
wip/mcp-fast-492
wip/motion-477
wip/asr-ab-489
wip/theme-variants-506
wip/overflow-511
wip/week-header-508
wip/attach-427
job/dav-delete-471
job/iso-435
wip/iso-435
wip/files-sel-keys
wip/dav-delete-471
job/align-253
job/siwc-490
wip/siwc-490
job/money-kinds-review
wip/align-253
wip/money-kinds-review
job/small-bugs-3
wip/overlay-title-487
wip/multiget-500
wip/hidden-420
wip/webcal-ui
wip/webcal-431
job/perf-367
job/location
wip/small-bugs-3
wip/location
wip/perf-367
wip/admin-deny-483
job/tag-unicode-473
wip/tag-unicode-473
job/blur-436
wip/photos-470
wip/blur-436
wip/small-bugs-4
wip/hunt-20260930
wip/settings-hdr-482
wip/chips-416
job/dedup-375
wip/dedup-375
job/doc-stack
wip/doc-stack
job/tokens-literals
wip/tokens-literals
job/jobs-leftovers
wip/send-fast
wip/paste-467
wip/money-numbers
job/money-plugin
wip/money-plugin
job/break-dav
wip/merge-batch
wip/crossday-469
wip/mac-verify
wip/mail-m2
wip/break-dav
wip/money-review2
job/money-md
job/modes-424
wip/money-md
wip/jobs-leftovers
job/agenda-413
wip/agenda-413
wip/modes-424
job/recog-417
wip/recog-417
wip/bounce-425
wip/ab-384-luna
job/webdav-perf
wip/webdav-perf
job/toast-ring
wip/toast-ring
job/money-review
wip/money-review
wip/micro-motion
wip/settings-card
wip/minical
job/notes-imap-428
job/least-priv
wip/ui-small-2
wip/flaky-426
wip/drag-end-418
job/jank
wip/jank
wip/least-priv
wip/docs-site
job/agenda
job/sec-batch
wip/sec-batch
wip/per-user-index
job/area-calendars
wip/area-calendars
job/parity
wip/parity
job/documents-research
wip/documents-research
job/test-infra
job/reminders-sync
wip/small-bugs-2
wip/reminders-sync
wip/gestures
job/google-oauth
wip/tags-merge
wip/tags
job/e2e-theme
wip/e2e-theme
job/icon-align
wip/test-infra
wip/select-align
wip/editor-385
job/voice
wip/webdav
job/webdav
job/app-pw-ui
job/editor-integrity
wip/editor-integrity
wip/voice
wip/quota
wip/cal-followups
wip/icon-align
job/composer-scale
wip/composer-scale
job/jobs-page
wip/jobs-page
job/hig-type
wip/hig-type
wip/app-pw-ui
job/motion-spring
job/mcp
wip/motion-spring
wip/mcp
job/small-bugs
wip/push-hosts
job/profile-sign
wip/touch-369
wip/profile-sign
job/mobile-focus
wip/mobile-focus
wip/ui-polish-354
wip/small-bugs
wip/dup-task
job/toast-polish
job/app-pw-scopes
wip/toast-polish
wip/app-pw-scopes
wip/cli-agent
wip/selection-pills
job/preview-attach
wip/preview-attach
job/dav-proppatch
wip/dav-proppatch
wip/cal-switcher
job/atomic-race
wip/atomic-race
job/photos-shared
wip/photos-shared
wip/cal-grid
wip/note-rewrite
wip/search-rebuild
job/mail-m1
job/paperless-import
wip/paperless-import
wip/mail-m1
wip/hidden-activity
wip/search-d
wip/pricing-research
wip/cursors
wip/auto-scheme
job/single-pills
wip/single-pills
wip/xuser-matrix
wip/money-format
wip/app-pw-setup
wip/purge-dos
wip/vault-health
wip/caldav-apple
wip/xuser-audit
wip/e2e-green
wip/tabbar
wip/adv-harness
wip/maple-mono
job/search-fix
wip/search-fix
wip/search-perf-c
job/adv-harness
wip/sidebar-headers
job/glass
wip/temp-index
job/polish
wip/polish
wip/file-protocols
wip/money-research
wip/glass
wip/voice-models
wip/collab-redo
job/voice-research
wip/hunt-20260928
wip/notes-actions-research
wip/search-pad
wip/search-perf
wip/search-sticky
wip/editor-undo
wip/chrome-rules
wip/motion
wip/appearance-research
wip/appearance
wip/audit-bugs
wip/cal-glass
wip/block-actions
wip/authz-order
wip/event-stripes
wip/chrome-sidebar
wip/auth-flaky
wip/robust-2
wip/gate-fix
wip/menu-blur
wip/import-calternaljs
wip/tray-fix
job/import-calternaljs
wip/index-order
wip/audit-fixes
wip/search-chevrons
research/mail
wip/phone-chrome
wip/dedup-break
wip/csp
wip/ui-audit
wip/select-toast
wip/perf
wip/flat-layout
wip/fonts
wip/event-tint
wip/sync-converge
wip/data-split
wip/glass-audit
wip/robustness
wip/sync-chaos
wip/search-thumbs
wip/fuzz
wip/menu-icons
wip/search-pill
wip/sync-changing
wip/heading-links
wip/date-formats
wip/a11y
wip/break-editor
wip/e2e-fix
wip/settings-sections
wip/sync-root-guard
wip/search-palette
wip/share-edit
job/toasts
wip/toasts
wip/cont-analytics
wip/authz-review
wip/popovers
wip/overlay-glass
wip/change-feed
wip/editor-modes
wip/composer-align
wip/cont-agenda
wip/agenda-merge
job/agent-conventions
wip/agent-conventions
wip/backend-misc
job/route-audit
wip/route-audit
wip/ui-batch
wip/heif-hardening
wip/grid-resize
wip/ask-page
wip/webmcp
job/deeplink-audit
wip/deeplinks
wip/shortcuts
wip/cont-tz-days
main
No results found.
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
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#253
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Owner (2026-09-27): "gpt models make a lot of horizontal alignment mistakes". Two examples from production and job screenshots: the block reminder chip (clock icon sits low next to "6:34 PM"), and search results on calternal.cloud (the calendar icon is off-centre next to "Calendar", a display-font heading row).
Goal: fix it once, app-wide, and make it testable
cap/emunits, centre on the cap-height centre (e.g.text-box: trim-both cap alphabeticwhere supported, with a metrics-based fallback usingfont-size,line-heightand the icon's own viewBox padding for Firefox), and compensate for Lucide's 24-unit viewBox padding so the glyph, not the box, is centred. Works for UI text (Google Sans / Inter), display headings (Bricolage / Fraunces) and mono.<svg/Lucide icon uses next to text.apps/web/e2e/layout-sweep.mjs: for every visible element pair (icon svg + adjacent text node in the same row), compute the icon glyph's vertical centre (from its painted bounding box) and the text's cap-height centre (from font metrics via canvasmeasureTextactualBoundingBoxAscent of "H"); fail if they differ by > 1 CSS px. Run at 1440 and 390, light + dark, for every route and open menus/popovers. Also check horizontal gaps between icon and text match the token.Depends on: #234 (fonts: metrics change) and #246 (shared surfaces/menus) merging first.
Started Forgejo #253 on branch job/icon-align, based on dev at
976b804f99. I am tracing the shared UI and layout-sweep implementation before editing; the final gate will merge dev once as required.Finding while tracing the requested reminder example: there is no reminder chip component or reminder UI in
apps/web/srcorpackages/ui/srcon the currentdevbase. DESIGN §42 defines a future clock chip, but this checkout contains no matching component/source text. I will capture the existing Agenda event time pair as the real calendar example and record the reminder chip as out of scope for this issue. The baseline layout-sweep produced menu, search, and share captures, then timed out waiting for the generic toast hook; I am checking the toast through an actual UI action.Correction to my initial reminder search: the app has real inbox reminder notices (
calendar_reminderandlog_reminder), and I migrated their icon/copy pair under IconLabel. The specific block reminder chip in this issue (clock+6:34 PM) is absent from this checkout. DESIGN §42 defines the feature, but there is no block reminder UI implementation here. I will capture the closest existing calendar pair and report the missing exact example/crop as a gap in this alignment job.Finding:
calternal-serverembedsapps/web/buildat Cargo compile time (crates/calternal-server/src/main.rs,#[derive(RustEmbed)]). I initially ran the route sweep with the server binary built before this UI change. That run did not exercise the new bundle, so I stopped it and discarded those captures. I am rebuilding the production server from the updated web bundle before the valid after sweep.Finding:
apps/web/e2e/layout-sweep.mjscaptured both Paper and Tokyo Night screenshots, but calledmeasure()only forTHEMES[0](Paper). This left dark-theme icon alignment unchecked despite the route matrix visiting it. I stopped that diagnostic run and changed the sweep to assert alignment in both themes before collecting final evidence.Finding: local
devis ahead at104b7db2and addsapps/web/src/lib/notes/editorHost.tsblock reminder decorations. The initial base had no chip, but the requiredgit merge devbrings in the requested clock/time pair. I stopped the pre-merge layout sweep and will migrate that pair to IconLabel and include a real API-created reminder in the final sweep fixture.The one full post-merge production
layout-sweepcompleted 576 screens and exited 1. It found an 8.7 px cap-centre miss for the wrapped long filename in the calendar preview and reported raw icon/label pairs in the Settings sidebar and Files sort header. It also reported note CLS values of 0.059 and 0.061. The sweep currently over-reports structural/trailing icons because the mode tray/header checks pass noIconLabelroot, and because it treats disclosure and trailing menu icons as label icons. I am fixing the confirmed UI pairs and the assertion's pair selection before focused validation; I will not rerun the full sweep.Production layout sweep finding outside #253's icon alignment scope: Analytics cards still place four right-side controls 12 px from the card edge; the existing assertion requires 18 px. On coarse pointer, the four range chips also fail the 44 px hit-area assertion. Evidence:
target/tmp/icon-align-focused-sweep-2.log, routeanalytics-week, 390 and 1440 widths. These are existing card spacing and touch-target issues; no alignment assertion failed on that route.Merge finding: the UI barrel already exported
IconLabelandcreateIconLabelElementnear the top. The merge resolution added the same exports again beside the icon geometry exports. The firstbun run checkreported four duplicate-identifier errors atpackages/ui/src/index.ts:7-8and:71-72. I removed the duplicate lower exports and am rerunning the web check.Web test finding: the first full
bun run testrun reported 12 failures and 23 unhandled errors.modeHeader.svelte.test.tsstubsResizeObserverwith a callback that takes no arguments and omitsunobserve; the new IconLabel observer then raisedentries is not iterable, and cleanup failed in tests whose stubs also omittedunobserve. I am updating only the ResizeObserver test doubles to match the browser API. No assertion or expected behavior is changing.Finished: icon alignment pass
Implemented the shared
IconLabelcomponent, cap-height and SVG glyph geometry measurement, shared/batched browser observers, and migrations across UI rows, menus, calendar, notes, files, photos, search, notifications, settings, and the mode tray. The reminder timestamp added by currentdevuses the same component. The layout sweep now checks cap-centre geometry and icon-label gaps while excluding structural and trailing action icons.Commits
b5ad894fmerge: preserve icon label alignment on current dev1a04e212fix(ui): keep icon label exports unique72c03499test(ui): match ResizeObserver interface for IconLabelBefore/after crops
Six zoomed sheets are attached below and are not committed.
Gates
cargo fmt --check: exit 0; stdout was empty.cargo clippy --all-targets -- -D warnings: exit 0. Final output line:Finisheddevprofile [unoptimized + debuginfo] target(s) in 7m 09scargo test: exit 0; 1,352 passed, 0 failed, 12 ignored across 72 test targets. The command's final doc-test output included:test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sbun run check: exit 0;svelte-check found 0 errors and 0 warningsbun run test: exit 0; exact summary:Test Files 109 passed (109)Tests 705 passed (705)Duration 60.84s (transform 61%, environment 15%, import 14%, tests 7%, setup 3%)The first web check found duplicate
IconLabelexports after thedevmerge; the exports were reconciled. The first Bun run exposed incompleteResizeObservertest doubles; those were corrected without changing test expectations. Final reruns passed.Remaining work / known gaps
The prior production layout sweep covered 576 screens and exited 1. It reported an 8.7 px cap-centre miss for a wrapped long filename in the calendar preview, raw icon/label pairs in the Settings sidebar and Files sort header, and note CLS values of 0.059 and 0.061. Source changes in this branch address the confirmed pairs and sweep selection, but the full sweep was not rerun after the final merge and fixes. These findings remain unverified in final screenshots. The sweep also previously counted structural/disclosure and trailing action icons as label pairs; its selectors were narrowed.
Current
devremoved the old Upload image / Unsplash pill fromBackgroundGroup; the icon-pill crop therefore uses the Files mode-tray item. No API changed, so the API adversarial round did not apply.Implementation decision for owner confirmation
The design specifies cap-centre alignment but not its measurement method. This implementation measures text cap height with Canvas font metrics, aligns the painted SVG path using its viewBox geometry, and shares ResizeObserver, MutationObserver, and canvas work across rows with batched updates. Confirm this mechanism as the default for future icon/text pairs.
Changed files
apps/web/e2e/layout-sweep.mjsapps/web/src/lib/backgroundGroup.svelte.test.tsapps/web/src/lib/calendar/agenda.svelte.test.tsapps/web/src/lib/components/SidebarLinks.svelteapps/web/src/lib/components/analytics/RangeBar.svelteapps/web/src/lib/files/FilesSidebar.svelteapps/web/src/lib/files/InfoPanel.svelteapps/web/src/lib/files/PublicLinkPage.svelteapps/web/src/lib/files/ShareDialog.svelteapps/web/src/lib/files/SharedByYou.svelteapps/web/src/lib/files/UploadToast.svelteapps/web/src/lib/fontsGroup.svelte.test.tsapps/web/src/lib/modeHeader.svelte.test.tsapps/web/src/lib/notes/NoteEditorSurface.svelteapps/web/src/lib/notes/NotesExplorer.svelteapps/web/src/lib/notes/editorHost.tsapps/web/src/lib/notifications/InboxPanel.svelteapps/web/src/lib/photos/PhotoTimeline.svelteapps/web/src/lib/photos/PhotoViewer.svelteapps/web/src/lib/photos/PhotosSidebar.svelteapps/web/src/lib/search/SearchPreview.svelteapps/web/src/lib/search/SearchResultRow.svelteapps/web/src/lib/themePicker.svelte.test.tsapps/web/src/lib/tray.svelte.test.tsapps/web/src/lib/ui/iconLabelAlignment.svelte.test.tsapps/web/src/lib/ui/iconLabelGeometry.test.tsapps/web/src/routes/settings/appearance/AppearanceSection.svelteapps/web/src/routes/settings/appearance/FontsGroup.svelteapps/web/src/routes/settings/parts/AccountRow.sveltepackages/ui/src/components/CopyLink.sveltepackages/ui/src/components/FloatingSidebar.sveltepackages/ui/src/components/IconLabel.sveltepackages/ui/src/components/ModeHeader.sveltepackages/ui/src/components/SegmentedControl.sveltepackages/ui/src/components/ThemePicker.sveltepackages/ui/src/components/calendar/AgendaList.sveltepackages/ui/src/components/calendar/CalendarTools.sveltepackages/ui/src/components/calendar/GridColumn.sveltepackages/ui/src/components/calendar/ItemPreview.sveltepackages/ui/src/components/calendar/MonthGrid.sveltepackages/ui/src/components/calendar/RepairList.sveltepackages/ui/src/components/calendar/TimeGrid.sveltepackages/ui/src/components/files/FileCollection.sveltepackages/ui/src/components/menu/MenuItem.sveltepackages/ui/src/components/notes/NoteMentionsCard.sveltepackages/ui/src/iconLabelAlignment.tspackages/ui/src/iconLabelGeometry.tspackages/ui/src/index.tspackages/ui/src/tokens.cssHead:
72c034994a17820ffa3617b59525f88846d30944Pushed:
origin/job/icon-align(up to date).Review (orchestrator): the shared IconLabel owner and the migrations are kept. The runtime measurement (getComputedStyle per text node, Range rects, canvas, Resize/Mutation observers on every row, interleaved read/write reflows) is rejected under the performance-first rule. The job is relaunched with this design: CSS
capunit sizing, a per-font cap-shift token computed at build time from the OS/2 metrics, optional @supports text-box-trim, no runtime measurement, a full sweep rerun, and a long-task comparison on 100k search scrolling.Restarting #253 on job/icon-align at
72c034994a(branch base10ebdfc596). I am retaining the IconLabel owner and migrations, replacing runtime geometry with CSS and static font metrics, then I will merge dev once, run the production sweep and gates, and push this job branch as requested.Finding during the requested 100k scroll microbench: after palette expansion, both the header actions and the pinned result action expose an accessible 'See all results' button. The probe initially failed with Playwright strict mode (two matches), before collecting scroll metrics. I am scoping the click to the Search actions group and rerunning the baseline.
100k search scroll baseline captured from the production server before the CSS-only icon change (Chromium, 40 scroll steps): the list had 11,634px virtual height and 21 rendered rows; scrolling took 3,328ms and produced 12 long tasks (max 101ms). The separate typing probe recorded 14 long tasks (max 148ms). The final report will include the matching after measurement.
The branch merged
devonce at the end of implementation with no conflicts. After refreshing the lockfile-backed dependencies,bun run buildcompleted successfully (✓ built in 19.72sfor SSR and✓ built in 41.26sfor the client), andcargo build -p calternal-servercompleted in 1m36s with the production bundle embedded. The full production layout sweep is running against that server now; its route matrix has not returned results yet.The first post-merge production sweep completed 294 screens and exited 1. It reported repeated mode-tray/sidebar cap-centre misses of 3.8–4.5px; a 1.9px miss on the block reminder timestamp; search row misses of 2.3px and 3.9px; and note CLS of 0.067 on cold load and 0.061 on the note route (the gate is <0.05). The
text-box: trim-both cap alphabeticpath also caused the ink-clipping assertion to report descenders cut in Settings, Files, Photos and Analytics labels. I am removing text-box trimming and replacing livecapsizing with the static cap-ratio calculation so the icon geometry stays stable while the selected font loads. I will run the affected routes again after that change.Search-fix continuation note: IconLabel was not present on the dev base when #288 started; it only existed on job/icon-align. I used the requested CSS fallback in
SearchPreview.svelte: the shared--icon-label-gap, 1cap sizing, first-line centring from the display-font cap-line metric tokens, andtext-box: trim-both cap alphabeticwhere supported. This adds no runtime font/layout measurement. The preview header can migrate to IconLabel after #253 merges.The static-metric alignment now passes the focused Files route (4 screens). After replacing text-box trimming and live measurement with cap units and build-time OS/2 tokens, the focused Note route also passes (4 screens), with CLS 0.001. The remaining note shift came from mentions content appearing while the editor expanded to its ready height; the card now waits for editor readiness. Commits:
43c2e7dbfor static icon alignment and950e0840for note CLS.The full production sweep checked 294 screens. The note CLS checks passed (0.011 cold, 0.009 route), but CSS alignment still failed when unit-bearing custom properties were resolved on the IconLabel wrapper instead of the scoped icon: mode-header chevrons missed by 3.1px, Settings → Plugins by 10.4px, and Calendar preview filenames by 4.3–4.5px; Search rows missed by 1.2–1.7px. The Files and Settings raw-pair migrations no longer appeared as missing-owner failures. I moved the
em,lhandcapcalculations onto the icon target, and wrapped the remaining Agenda attachment glyphs in the shared IconLabel owner. Focused routes and the full sweep are next.Production regression, owner screenshot (iPhone, calternal.cloud, 2026-09-28 15:23 IST): in the Files list (compact/phone layout) the file name and its date line overlap ('Attachments' / 'Yesterday at 10:01 PM' touch; descenders collide). Cause (orchestrator):
packages/ui/src/components/files/FileCollection.svelteputsoptical-caps(tokens.css:955: display:block; line-height:1; text-box: trim-both cap alphabetic; padding-block 0.3em; margin-block -0.3em) on BOTH .fc-name and .fc-sub inside .fc-namecell (grid, gap 2px), from commit17124534'align compact file labels to icon caps'. Two cap-trimmed boxes 2 px apart leave no room for descenders.Fix in this job (it owns cap alignment): a two-line title+meta stack uses ONE shared rule. Only the first line is cap-aligned to the icon; the gap between title and meta is a role token (e.g.
--stack-title-meta-gap), sized so the title's descenders and the meta's caps have clear air (compare the Settings row title/meta and Notes list rows; use the same token everywhere a title+meta stack exists: Files, Notes list, Search rows, Settings rows, Photos sidebar). Then run the layout sweep at phone (390), tablet (820) and desktop (1440) widths, in light and dark, and attach crops of every title+meta stack. The owner explicitly asks that every job's work is visually verified at all device widths.Starting work on
job/icon-alignfrom dev merge base58818bc361bf880bdf0b3e13d04d2eb7b43e8cb2. I am committing the in-progress icon alignment harness first, then I will address the latest Files row regression and run the requested responsive sweep and gates.Production layout-sweep evidence before the fix:
--routes files --only route --screenshots ...checked 12 screens and reported 8 failures, all Files list rows at 390px in Paper and Tokyo Night, open and collapsed. The title/meta ink gap fell below the required 3px because optical-caps margins were applied to both lines. I am applying one shared stack token across the Files, Notes list, Search result and Settings account stacks, and am rerunning the production sweep at 390/820/1440px in both themes.The first 60-screen production sweep passed the shared title/metadata gap checks. The new coarse-pointer 820px pass exposed real alignment and touch-target failures: Search result icons were 5.8px below the cap center and Settings account icons 1.8px below; the sidebar resize hit zone was 8px wide, Files list rows were 40px high with 26px sort controls, and Search action controls were 43×35px. I am correcting the cap offsets and sizing the virtualized rows and controls for coarse pointers, then rerunning the full sweep.
The production Search matrix still measured its first title cap 5.8 px above the icon at 390px and 820px, while desktop measured 5.3–5.5 px. The more specific CSS override applied, but its compensation was in the wrong direction. I reversed the 0.38em adjustment and am rerunning the Search matrix before the full capture.
Horizontal alignment defect for this job (orchestrator review of the #320 crops, Photos sidebar, Tokyo Night): the 'Choose folders…' row's icon and label start ≈4 px left of the other rows (label x≈97 vs 101 for 'Home', 'All photos'…; icon x≈60 vs 62). Every sidebar row must share the same icon and label columns: route it through the shared row component. Include a zoomed crop of the Photos sidebar in the evidence.
The 60-screen production sweep completed and found two remaining phone/tablet failures. Search calendar-event rows place the first title cap 5.7–5.8px below the icon at 390px and 820px in both themes; the note-only focused probe had not seeded those rows. Files grid phone actions "Show info" and "More actions" render about 36px high at 390px, below the 44px coarse-pointer target. I am adding an event-row-specific static cap adjustment and enlarging the Files grid action targets, then rerunning the affected matrix.
Correction to my previous finding: the failing clock row is a Daily note result (kind
note, stable/d/link), not a Calendar event. The first shared PillGroupspacedvariant pass also showed the generic pseudo-target selector winning by source order, so its horizontal expansion did not apply. I scoped the cap adjustment to Log/Daily note rows and raised the spaced-target selector specificity; I am rebuilding and rerunning the production check.The new Daily note cap nudge and spaced phone targets passed the three-surface phone checks. The broad nudge then failed the 1440px fine-pointer Search rows by 5.3px in the opposite direction, so I limited the 0.38em adjustment to coarse pointers; desktop keeps the shared IconLabel base rule. I am rebuilding and checking the 390/820/1440 matrix again.
The final matrix measured the Settings Account label 26.5px below its selection centre in three desktop states, but the captured screen places the active capsule on the Account row. The Search phone hit check also found the selected-result action pill over a row during the measurement, while the captured screen places the pill below that row. Both checks run during a sequence that can still have a CSS capsule transition or a pending scroll-restoration event. I am adding a test-only settle step before these existing checks; the 1px alignment and 44px hit thresholds stay unchanged.
Starting #253 on job/icon-align. Branch base: merge-base
cee93ecb38; merging origin/dev ateb4ff20a98.Finding: the merged production build failed to resolve in apps/web/src/routes/settings/account/AppPasswordsGroup.svelte. apps/web/package.json and bun.lock both declare qr 0.7.0; I am synchronizing node_modules from the locked manifest before rerunning the build.
Correction to the previous note: the module name was omitted by shell quoting. The merged production build failed to resolve the qr module imported by apps/web/src/routes/settings/account/AppPasswordsGroup.svelte. apps/web/package.json and bun.lock both declare qr 0.7.0; I am synchronizing node_modules from the locked manifest before rerunning the build.
Finding: the layout sweep stopped before browser startup because sharp failed to load with ERR_DLOPEN_FAILED: libstdc++.so.6 was not found. I am locating the host runtime library and will rerun the sweep.
Correction: libstdc++.so.6 is present on the host and ldd resolves sharp dependencies. The failure is specific to Bun loading this native binding; Node can load the app-local sharp package, so I am checking the Playwright harness under Node and will run the same sweep script there if it loads.
Finding from the isolated paper-theme sweep: at 1440 px, the reminder chip text centre is 1.8 px above its icon; the Notes Explorer calendar hint SVG is outside the shared IconLabel on 1440 and 390; and the 820 px Notes route times out before its reminder chip appears in both sidebar states. I am checking the merged CSS and markup; no sweep expectations were changed.
Finding during the merged 390/820/1440 sweep: at 820 the harness attempted to assert the fine-pointer Block hover actions toolbar on its coarse-pointer tablet profile.
NoteEditorSurface.svelteonly mounts that toolbar whenpointerCoarse.currentis false, so the 5-second timeout was a profile mismatch. I scoped that existing placement check to fine-pointer profiles; the desktop check and assertion remain unchanged.The same isolated note run measured the reminder SVG at a hard-coded 12px, overriding IconLabel's cap-based sizing. I removed those fixed dimensions. The decorative journal calendar hint was also being considered as a second label icon; it now carries the existing
data-icon-label-ignorehook. I am rerunning production screenshots and the full selected 60-case matrix.The post-build note sweep cleared the reminder at all three widths after the shared cap-box styling, and cleared the journal-row false positive after its decorative icon was excluded. It exposed a second selector-based IconLabel case in the Note Mentions headings:
variant="contents"uses.mention-heading-label, so the label lacks the primitive's standard text class and did not receive cap-box trimming. I am extending the existing build-time CSS generator to cover declared label selectors as well; the 1px alignment threshold stays unchanged.The selected production matrix completed: 60 screens at 390/820/1440, paper/Tokyo Night, open/collapsed. CSP reports were 0. It found a SearchPreview
first-lineicon 4.5px below the trimmed heading cap centre and an actual horizontal gap of 6.4px where the component declared 10px. The Notes list checkbox also has a 13×13 native input inside a 44×44 label; the sweep's corner hit checks reach the label but miss the input. I am matching the search scope to its shared gap and cap line, and making the checkbox itself the full 44×44 transparent input over its existing visual control. The alignment and hit-target thresholds remain unchanged.The 60-screen production sweep completed with zero CSP reports, but six toast-notes cases (390px collapsed and 820px open/collapsed, in both themes) failed the hit-target check. Each transparent checkbox measured 44×44, while hit testing found the associated label above it. I am raising the native input above its visual sibling, then rebuilding and rerunning the matrix.
The focused 12-screen toast-notes rerun confirmed that the unlabeled wrapper was receiving edge hits after I removed the wrapping label; the nested input still measured exactly 44×44, and edge points landed at its outermost pixel. I have expanded the transparent input by 2px on each side (48×48 hit box) while keeping the visible square at 13px, so the 44px sampled area lies inside the native input box.
The 12-screen rerun still returned the noninteractive wrapper at edge points, and the centered 48×48 input also crossed the card's required 18px inset (the sweep measured 16px). I am making the wrapper transparent to hit testing, explicitly keeping pointer events on the native input, and extending the input 48px only toward the row interior so its outer edge stays inside the card padding.
With the wrapper excluded from hit testing, the focused sweep found the note row underneath the input at edge points, even though the native input's computed box was 48×48. The input uses
appearance:noneand a transparent background; I have given its hit surface a near-zero-alpha paint so Chromium can hit the input itself.The native button also misses the sweep's exact 44px corner samples when the sample lands 1px inside its computed 44×44 box. I am making the button 48×48 and shifting its start 2px inward; the visible 13px square stays centered and the button's left edge keeps the card's 18px inset.
The 48×48 button still fell through to its containing row at a lower corner, while its bounds and card inset were correct. Its reset appearance had a fully transparent background; I have added a 1% black background to the blank hit surface so the browser can paint and hit-test the whole button. The visible 13px check square is unchanged.
The 48px button surface was still missing only its lower corner samples. The button's inherited rounded corners clipped those points, so the row received them. I have made the invisible hit surface square; the 13px visible checkbox keeps its rounded corners.
The focused production sweep now passes all 12 toast-notes cases with zero CSP reports. The cause was the rounded hit surface clipping the corner samples; a 48×48 square button target with the 13px rounded visual square inside it passes hit testing and preserves the card inset. I will use this shape in the final 60-screen run.
The final production matrix completed all 60 screens with zero CSP reports and CLS 0.006. It found two reproducible failures: (1) SearchResultRow at 390px shows the row icon 5.7px above its paired text center; (2) folder-menu buttons at 820px are 42.24px high, so adjacent menu rows intercept samples from the required 44px target. One toast/capsule overlap appeared in a single 820px Tokyo Night pass while the host was heavily loaded; I will treat that as load-sensitive unless it repeats.
The 820px coarse-pointer menu rows now use the shared
--hitminimum. The focused production sweep passed 12 menu-folder states at 390/820/1440px in paper and Tokyo Night with both sidebar states; it reported zero CSP events.The web test gate found one merge integration gap:
fonts.tsnow includes Maple Mono as the default mono font, butscripts/icon-label-font-metrics.mjsdid not generate its cap tokens. The existing static geometry test correctly failed on the missingmaple-monotoken. I am adding the bundled Maple Mono font to the generator and regenerating the CSS; no test expectation is changing.The final production sweep passed all 60 screens on the current head: 390/820/1440px, paper and Tokyo Night, sidebar open/collapsed. It reported
CSP REPORTS layout-sweep: 0 across 2 pages,checked 60 screens, andCLS note: 0.006. The final captures are in the ignored worktree pathsartifacts/icon-align-layout-final/andartifacts/icon-align-stack-crops-final/for the visual reviewer. The coarse-pointer SearchResultRow0.38emnudge remains unchanged; I recorded it as a root-cause follow-up and did not build that follow-up.Adversarial finding from the real-server search chaos probe: while a staged search rebuild was running,
/api/v1/search?q=unicodenfcsentinelreturned HTTP 200 without the committed marker. The probe reported exactly:FAIL search chaos: old Index stopped returning a live hit during staged rebuild. Its fixture had also passed the precondition that the uploaded marker was searchable before the rebuild. This is a result-integrity failure, not a latency threshold. The full adversarial round is still running; I am checking whether the later HTTP -1 fixture error is a separate shared-host timeout or a server fault. I did not change backend code because this job owns the icon alignment UI files.Additional evidence for the staged-rebuild Search result loss: the same server log has a Tantivy
ERRORstating that a segment merge was cancelled because it could not read a.termfile under.system/index/tantivy-rebuild/(FileDoesNotExist). This supports an index integrity issue; it is not just a slow response. The round is still running to capture its remaining API results. The backend fix is outside this job's UI file ownership.Finished Forgejo #253.
Branch
job/icon-alignis pushed atf1f7e0ab1d7a61898828c47f2c3057a3cd8e1c9d. I mergedorigin/devat08d52cefdd5e4036e84f73fe29c1f4df410cfc5dand resolved the seven conflicts while keeping the shared SidebarSectionHeader, search layout, vault-health, tab bar and selection work, auto colour scheme, and this job's IconLabel tokens.Built:
--hitminimum.Key files:
packages/ui/src/components/IconLabel.svelte,packages/ui/src/iconLabelGeometry.ts,packages/ui/src/iconLabelElement.ts,packages/ui/src/tokens.css,packages/ui/src/components/menu/MenuItem.svelte,apps/web/src/lib/font-metrics.generated.css,apps/web/src/lib/search/SearchPreview.svelte,apps/web/src/lib/components/NoteList.svelte,scripts/icon-label-font-metrics.mjs, the IconLabel tests, andapps/web/e2e/layout-sweep.mjs. The change covers 63 tracked files across shared UI, app screens, tests, and the layout sweep.Gates:
cargo fmt --check: no output; exit 0.cargo clippy --all-targets -- -D warnings:Finished \dev` profile [unoptimized + debuginfo] target(s) in 51m 55s`cargo test: exit 0; 73 test result groups, 1,381 passed, 0 failed, 12 ignored. Every emitted group reportedtest result: ok.bun run check:svelte-check found 0 errors and 0 warningsbun run test: 115 test files passed; 733 tests passed.bun run build: passed.CSP REPORTS layout-sweep: 0 across 2 pageschecked 60 screensCLS note: 0.006WARN settings-account: list "Signed-in devices": 2 rows share an iconLAYOUT SWEEP PASSEDThe one real-server adversarial round was bounded to 15 minutes and ended with exit 124 while the larger campaign was still running. It found Search result loss during a staged rebuild and a Tantivy missing-term-file error; evidence is on #362. The editor undo/redo mismatch is on #363. The delete/edit conflict timeout is on #364. Transport timeouts are on #368. The 2.093 s sync measurement and delayed successful responses were load-sensitive findings.
Known gaps:
.38emSearchResultRow nudge remains. The requested root-cause fix is recorded as a follow-up and was not built here.artifacts/icon-align-layout-final/andartifacts/icon-align-stack-crops-final/. The installedfj issueCLI has no attachment command, so these files remain in the worktree rather than Forgejo attachments.Decisions not covered by DESIGN.md: use font OS/2 cap metrics at build time; preserve a 48 px square checkbox hit area with a 13 px visible control; apply the shared
--hitminimum to coarse-pointer menu rows.Cleanup output:
Removed 41050 files, 15.4GiB total.apps/web/buildwas removed. No screenshots were committed.Exact raw Clippy completion line:
Started job/icon-align at branch head
f1f7e0ab1d. Fetched origin/dev at95520d547d; shared base iseb4ff20a98. I am merging current dev once, resolving both sides, then will run the requested layout sweep and final gates.After merging dev at
95520d547d, Git reported content conflicts in apps/web/src/routes/settings/appearance/AppearanceSection.svelte and apps/web/src/routes/settings/parts/AccountRow.svelte. The other shared UI and Search changes merged cleanly. I am retaining Auto colour scheme and account deep-link behavior while restoring IconLabel cap alignment and the shared title/meta stack.The first layout-sweep attempt stopped before the route loop: Sharp reported
ERR_DLOPEN_FAILED: libstdc++.so.6. The host library is present at/lib/x86_64-linux-gnu/libstdc++.so.6; a direct Sharp import succeeds with it preloaded. I will rerun the sweep withLD_PRELOADscoped to the test process. The wrapper now uses a zsh-safe exit variable.The requested real-server layout sweep completed on merged head
9330087725. It checked 868 screens and logged 266 FAIL records (many repeat across widths, themes and sidebar states); it did not print LAYOUT SWEEP PASSED. Full captures and component crops are in the ignored worktree paths artifacts/icon-align/layout-sweep and artifacts/icon-align/stack-crops.Evidence relevant to #253:
Other repeated findings on merged dev include 59 notes-tree chevrons without IconLabel, 39 calendar contrast records (4.34:1), 32 touch-target records under 44×44, 16 header-collapse wait timeouts, RTL overflow and page errors. Menu click and route wait timeouts also occurred. I am addressing the icon-alignment records in this job and will report the unrelated records for follow-up.
Starting the final #253 pass on
job/icon-align. Branch HEAD is93300877258b0381dcb0e6c6cca07dab8066460a; merge base withdevis95520d547dedb2d5286dd2dc6f61d1547621ab7c. I found three pendingIconLabelalignment refinements in SearchResultRow, AccountRow, and ItemPreview. I am reviewing and committing or discarding them before onegit merge dev, then I will run the requested web gates and production screenshot pass.Full Rust test run finding after the single
devmerge:cargo test -q -p calternal-plugin-notes: 108 passed;tests::retitle_replays_after_each_persisted_stepfailed atcrates/plugins/notes/src/lib.rs:6457withApiError(500, ... "Notes operation failed"). Focused rerun of that exact test reproduced (0 passed, 1 failed).cargo test -q -p async-imap: 68 passed;client::tests::test_parsing_errorfailed because the error text did not contain220 mail.example.org ESMTP Postcow. Focused rerun reproduced (0 passed, 1 failed).These tests and crates are outside the icon alignment change. No assertions or behavior were changed. I will report both as known gaps after the visual verification.
The production layout sweep checked 109 screens and found the AccountRow icon 2.1px above the centre of its title/meta text block (Settings → Account, e.g.
Owner Owner owner). The row had a title-line offset and a separate text translation. I removed both so the icon, full text block, and actions use the row's shared grid centre. Fix commit:72354ac87eadcd89e98b8785de1d64b0c3bdf121.After the fix,
bun run checkreported 0 errors and 0 warnings, andbun run testpassed 773 tests. I will attach a focused production capture of this row after rebuilding the app.One adversarial-round coverage gap: both
tests/adversarial/xuser_matrix.pyandauthz_matrix.pystop while building their operation roster becauseGET /api/v1/mail/accounts(operationId: mail_accounts) has no identity policy inauthz_matrix.py::DATA_PREFIXES/allowed_identities. The runtimeaccountshandler callsprincipal(context), and the Mail API probe's anonymous-access checks passed. This is a policy-map/OpenAPI coverage gap; the matrices did not exercise this operation. The probe is continuing its remaining phases once.The single adversarial round reported a non-SLOW editor data-integrity finding (seed
25608414): the “editor paste, Unicode, and block actions” undo/redo storm changed the editor block structure. The output shows a previously spacedBlock without an id hostile paste …paragraph merged intoBlock without an idhostile paste …, plus additional image/anchor blocks and a changed history-storm heading after the history storm. The API/browser adversarial script marked thisFINDING; it is outside the icon-alignment files and needs owner triage before merge.Starting resumed finalization on job/icon-align. Worktree is clean at
72354ac87e. The current alignment work was based at95520d547d; the required single dev merge is already recorded at7541a0ec91with the seven listed conflicts resolved.Post-merge production screenshot pass finding (HEAD
72354ac8): the sweep captured 78 screens. The IconLabel cap-height and horizontal-gap assertions reported no failures. The broader layout sweep exited 1 on separate checks:calendar grid: 2026-09-28 header clip ends at x=248.1 inside corner x=326.4–390.6; at 390 px, two Log entries were 28 px wide.menu-calendar-titleand some phone Files/menu route interactions timed out in Playwright (locator.click: Timeout 30000ms exceeded).The requested Files 390 px captures are in
artifacts/icon-align/restart-pass/screens/. The paper list capture shows a visible gap between each name and date line. The Tokyo Night capture rendered the grid view after the sweep changed view mode; its title/meta crops still show a gap. Contact sheets for the reminder chip, search result, menu, sidebar, pills, and toast are inartifacts/icon-align/restart-pass/crops/components-{390,820,1440}-{paper,tokyo-night}.png. No test expectation or unrelated UI code was changed.Adversarial-round finding: and both abort before completing their authorization checks because OpenAPI () has no authorization-policy entry. The focused Mail API probe passed anonymous-access, malformed/oversized-payload, hostile-ID/cursor and parallel-read cases. This is a missing matrix rule, not a confirmed server authorization bypass; I left the security matrix unchanged because Mail policy is outside #253.
Correction and full evidence for the adversarial-round finding:
tests/adversarial/xuser_matrix.pyandtests/adversarial/authz_matrix.pyboth abort before completing their authorization checks because the OpenAPI operationGET /api/v1/mail/accounts(mail_accounts) has no authorization-policy entry. The focused Mail API probe passed its anonymous-access, malformed and oversized payload, hostile ID/cursor, and parallel-read cases. This is a missing test-matrix rule, not a confirmed server authorization bypass. I left the security matrix unchanged because Mail policy is outside #253.Additional adversarial evidence:
tests/adversarial/attack.pyreceived HTTP 502 for bothappearance PUT oversized font idandappearance oversized body 3 MiB. The body waslocal adversarial server is unavailablefrom the local proxy. The run did not report a server crash, and its request burst had p95 0.183s; the 502s occurred on the subsequent oversized-body probes. I did not change the Appearance API or the proxy because they are outside #253; this needs review to determine whether the response came from a transient backend restart or a request-streaming failure.Adversarial-round finding:
tests/adversarial/editor.mjsreportedFINDING editor browser editor paste, Unicode, and block actions seed=25608414: undo/redo storm changed editor block structure. This is a non-SLOW Note editor integrity finding from the single round. Editor behavior is outside #253, so I did not alter it here; the owner needs to assign a follow-up with a regression test.Adversarial finding from the single local-server round: DAV
PUTof an event whose body insertsBEGIN:VALARM,ACTION:DISPLAY, andEND:VALARMbeforeEND:VEVENTreturned HTTP 201. The probe expected rejection (bad_if_2xx=True) for this input. The PUT used the current resource ETag. This is a confirmed accepted hostile input in the DAV probe, outside the icon-alignment scope; please triage and add a focused regression test when addressed. No unrelated DAV behavior was changed in this job.Additional observations from the same time-boxed adversarial run:
auto_scheme: {"mode":"system","location":null}on the new-user fixture; its assertion expectednull. This may be state contamination, but the run reported it as a finding.The single run reached its 25-minute cap during photo tag setup. The time limit ended it before the remaining probes completed. These items are outside #253's UI scope; I have not changed those APIs.
icon-align complete
Branch:
job/icon-alignHead:
72354ac87eadcd89e98b8785de1d64b0c3bdf121Built the shared
IconLabelprimitive and applied static font cap metrics to icon and label pairs across search, notes, files, calendar, sidebar, settings, menus, toolbar and toast rows. Added the build-time font metric generator and static geometry/alignment coverage. The singledevmerge is7541a0ec916f611174d62c00ec4dbbce31f6e6fb; the calendar grid and view switcher, select-all checkbox, type roles and layer tokens were retained.Key files:
packages/ui/src/components/IconLabel.svelte,packages/ui/src/iconLabelElement.ts,packages/ui/src/iconLabelGeometry.ts,packages/ui/src/index.ts,scripts/icon-label-font-metrics.mjs,apps/web/src/lib/font-metrics.generated.css,apps/web/src/lib/ui/iconLabelGeometry.test.ts,apps/web/src/lib/ui/iconLabelStatic.test.ts, andapps/web/e2e/layout-sweep.mjs, plus the consuming UI rows and views.Gates
bun run checkexited 0. Output:bun run testexited 0. Final output:The production screenshot pass inspected 78 screens at 390, 820 and 1440 px in paper and Tokyo Night themes. No shared IconLabel cap-height or gap assertion failed. The Files list at 390 px in paper theme has a visible name-to-date gap. The sweep exited 1 on unrelated calendar header clipping, Files sort/select hit-target overlap, and menu click timeouts. The runner also switched Files to grid before the dark 390 px capture, so a dark 390 px list screenshot is missing. Screens and crops are in
artifacts/icon-align/restart-pass/. I used Node for the screenshot runner because Bun could not load its nativesharpdependency (libstdc++.so.6). The installedscripts/fjinterface supports text comments but no issue attachment upload, so the screenshot files remain local.Adversarial round
One local-server round ran with a 25-minute cap and exited 124 during photo tag setup. It covered search chaos, calendar tags, Mail, editor stability, appearance, Files/Notes/tags/tasks and DAV probes before timing out. SLOW-only results were shared-host load. Findings were recorded in the comments above:
GET /api/v1/mail/accountshas no OpenAPI authorization-policy entry; this did not confirm an authorization bypass.local adversarial server is unavailable.VALARMwith HTTP 201, despite the probe expecting rejection.auto_schemewhere it expectednull; this may be fixture state contamination.Decisions and gaps
sharpload failed.cargo fmt,cargo clippy,cargo test) were not run; the requested web check/test gates passed.cargo cleancompleted andapps/web/buildwas removed. The working tree is clean at the head SHA above.Starting final integration on
job/icon-alignat HEAD72354ac87eadcd89e98b8785de1d64b0c3bdf121, merging localdevatd0d83a32b41e67b64f6586690ba4946e41878160once as requested.Final report for #253
devonce intojob/icon-alignand pushed the merge toorigin/job/icon-align.layout-sweep.mjs,SidebarLinks.svelte,FilesSidebar.svelte, andInboxPanel.sveltewith the icon alignment work anddev's features retained.AccountRow.sveltematchesdevbyte-for-byte, including the title-plus-meta centering decision.apps/web/e2e/layout-sweep.mjs,apps/web/src/lib/components/SidebarLinks.svelte,apps/web/src/lib/files/FilesSidebar.svelte,apps/web/src/lib/notifications/InboxPanel.svelte,apps/web/src/routes/settings/parts/AccountRow.svelte.854a49ee6b0f2f4d6ab678429209a1bb3ed31a7f.Gate output (verbatim):
bun run checkbun run testVitest also printed
Could not parse CSS stylesheetandNot implemented: Window's scrollTo() methodnotices; it exited 0.Known gaps: only the requested web check and web test gates ran during this final integration step.
Decisions: no new design decision. The layout sweep retains the 820 px tablet pass and icon-label alignment checks; the AccountRow layout follows
dev's owner-directed whole title-plus-meta centering.Correction to the final report above: the pushed HEAD SHA is
854a49eebfb693c1e6107b4291575a9e44ca317b.Started align-253 from job/align-253 at
a288022dbe(origin/dev base). I am tracing the shared row and icon-label paths before implementing the settings measurements, reusable primitive, and layout sweep updates.Finding while tracing #253: allows 1px for control icon-label pairs and its row check selects only ; it has no numeric assertion for icon circles, trailing switches, or title chips. The Notifications row uses with , a status chip, and a Toggle inside , so those variants need explicit checks. I am reviewing the prior CSS-only static-metric IconLabel implementation for reuse before applying it to the current SettingsRow-based app.
Correction to my previous comment: shell interpolation removed the code identifiers. The accurate finding is:
apps/web/e2e/layout-sweep.mjsallows 1px for control icon-label pairs and its row check selects only.account-row; it has no numeric assertion for.settings-rowicon circles, trailing switches, or title chips. The Notifications row usesSettingsRowwithattention, a status chip, and a Toggle inside.toggle-slot, so those variants need explicit checks. I am reviewing the prior CSS-only static-metric IconLabel implementation for reuse before applying it to the current SettingsRow-based app.Settings-row sweep finding from the preserved pre-change production bundle:
.actionscentre offset of 1.50 px at both 1440 px and 820 px in light theme. The #253 limit is 0.50 px. I am tracing this through the shared SettingsRow layout so any correction stays at the primitive.n/afor icon discs because it did not resolve the rendered card background. I changed it to composite computed card and ancestor backgrounds, and it now fails if it cannot measure the color. The after sweep will provide the contrast result.Baseline browser sweep findings (production bundle before the #253 changes):
n/abecause it could not read the computed theme colors. I updated it to sample the rendered colors through Canvas.I am rerunning the baseline and after production bundles with these probe changes.
The corrected rendered-color probe confirmed a dark-mode visibility regression in the baseline Settings rows: icon-disc contrast reached 1.06:1 against the card in Tokyo Night and only 1.16:1 in Notifications on paper, below the required 1.3:1. I also measured a 28.00 px status-chip offset on 390 px and a 1.50–1.51 px user plugin switch offset at 390/820/1440. The shared icon surface role and SettingsRow layout now address these cases; the after matrix will verify every User/Admin section at both text scales.
The baseline 200% text pass found the same title-chip defect at its widest case: the status-chip centre was 478.50 px from the title cap centre on a 390 px Account row, and 126.50 px on Notifications. The user plugin switch reached 4.01 px from the title-plus-meta centre at 390 px; it remained 0.51 px off at 820/1440. The new SettingsRow first-line chip alignment and tooltip-switch slot changes target these shared row behaviors.
Update on 2026-09-29: fetched and merged
origin/devas required before final gates. The merge commit is00b862dcf74c1dc24ae10b66f420a8ed2bea9142; I resolvedPhotoTimeline.svelteby retaining the new Checkbox and the #253 IconLabel imports, andFileCollection.svelteby retaining both the #253 geometry note and #406 selection-track invariant.Post-merge web gates so far:
bun run check:svelte-check found 0 errors and 0 warningsTMPDIR=/home/kayg/Developer/calternal-wt/align-253/target/tmp bun run test:Test Files 132 passed (132); Tests 831 passed (831)bun run build: passed (✓ built in 37.06s,Wrote site to "build",✔ done); Vite emitted its existing vendoredMODULE_LEVEL_DIRECTIVEwarnings.Screenshot proof remains in progress. The shared Chromium slot has been occupied by active Playwright sessions in
preview-421,blur-436, and earlierpaste-467; I stopped our runs when another worktree started. Thebefore/directory currently has 35 of the required 42 crops; reminder-chip crops are missing and menu-row 390px dark is missing. Theafter/set is not complete yet. I will resume serialized captures when the slot clears.Baseline evidence from the real server and pre-change production bundle (focused on the requested rows): at 390px / 100%, the Notifications state chip measured 28.00px off the title cap centre; its disc contrast measured 1.15:1 in paper and 1.30:1 in Tokyo Night. Apps-row disc contrast measured 1.09:1 in paper and 1.12:1 in Tokyo Night. These fail the #253 thresholds.
The first crop pass also showed that the reminder crop ran after the independent block-action probe. That probe timed out before crop capture on two baseline routes, leaving the real reminder chip out of the evidence. I moved the reminder crop earlier, after the Note API fixture loads and before the editor interaction. The fixture creates the reminder through the real Notes API.
Resuming the same #253 job after a usage-limit interruption. Branch
job/align-253is clean at640abc675caa51132be957ef22d842709337e9d5; the integratedorigin/devbase is369ab6a2f9fc673e3564b94857fbecfeb04df404(merge00b862dcf74c1dc24ae10b66f420a8ed2bea9142). The row alignment implementation and measurement changes are committed. The remaining work is the production proof pass: the preserved before set has 36 of 42 requested crops and the after set is empty.Finding during the final production proof: a Chromium session from the
blur-436worktree was already active on the shared host when the #253 layout sweep started. To avoid cross-job browser interference, I terminated the #253 sweep and its local server before it wrote any proof crops. I will run the single final sweep after the active browser exits; meanwhile I am running the non-browser web gates.Finding in the previous #253 sweep assertion: it compared the SVG element rectangle with the label cap centre. An SVG box can be centred while its path geometry is not, so that did not prove the issue's painted-glyph requirement. I updated
apps/web/e2e/layout-sweep.mjsto transform each SVG shape's painted bounds into viewport coordinates and compare the resulting glyph centre with the cap centre at the existing 0.50px threshold. This measurement runs only in the E2E sweep; the app still uses generated CSS metrics.Resuming #253 after a usage-limit interruption on branch
job/align-253. The prior integrated base is369ab6a2f9fc673e3564b94857fbecfeb04df404(origin/devmerge00b862dcf74c1dc24ae10b66f420a8ed2bea9142). Resumed at700d8ca7ff026dc9e0b899a65287f7c9f1cc0d89, committed the sound painted-glyph measurement comment as6c7a27dd1. The remaining work is completing the real-production before/after proof set and final verification; the existing implementation and row checks are already committed.Merge finding after the required
origin/devsync: the new Calendar task overlays add task time/continuation text beside their state icons, and the Sidebar adds count badges beside the route icon and label. Those new fields overlap the existing #253 wrappers. I am keeping the merged feature data and placing each leading icon/title pair insideIconLabel, with time and badges outside that pair so its alignment target remains the actual label.Full web test finding after the origin/dev merge:
bun run testexited 1 with 132 files passing and 2 failures out of 851 tests.src/lib/fontsGroup.svelte.test.tsandsrc/lib/components/KeyboardShortcutsCard.svelte.test.tseach hit their existing 5,000 ms timeout; neither assertion failed. The suite took 229.04 s versus the preserved pre-merge run's 71.07 s, while two other worktrees were running Vitest and another Svelte check. I am keeping the test expectations unchanged and will run the two timed-out tests in isolation to distinguish host load from a behavioral regression.Resuming Forgejo #253 after a usage-limit interruption. Worktree
job/align-253is clean at0a4f7b5d0b64d84105ee31105f3701f9c5bf2810; itsorigin/devmerge parent is55a2f90feb8a52d91f8bbc1347889b509af02260. The implementation and painted-glyph sweep are committed. I am continuing from the outstanding production proof and isolated-test verification, then I will report the final head SHA and exact gate output.Test follow-up: the two files that timed out in the shared full suite pass in isolation.
src/lib/fontsGroup.svelte.test.tspasses 1/1 in 40.14s, andsrc/lib/components/KeyboardShortcutsCard.svelte.test.tspasses 1/1 in 29.61s. This supports the earlier host-contention explanation; I will still rerun the complete web suite once for the final gate.Finding in the committed sweep:
measureSettingsRows()runs for both themes, but the app-widemeasure()call atapps/web/e2e/layout-sweep.mjs:2808is guarded bytheme === THEMES[0]. Thus dark-theme IconLabel glyph geometry is not checked even when both themes are captured. I am adding a regression check for the theme-independent measurement decision and removing that guard.E2E finding: Chromium 153.0.8010.12 blocked each of the three same-origin Note collaboration WebSocket attempts with
net::ERR_BLOCKED_BY_LOCAL_NETWORK_ACCESS_CHECKS. The Notes API still created and returned the seeded Note, but the UI switched to “Not live” and the live-editor reminder crop could not render. Playwright 1.63 supports a scopedlocal-network-accessgrant and maps it to Chromium's current split permissions. I added that grant to the layout sweep's local-server contexts; the targeted Note run will verify it before I proceed with the full crop sweep.Full production sweep finding: fixture seeding stopped before route measurements. The real Photos upload request for
BURST_004.JPGreturned HTTP 503; the harness expected 201 and aborted atapps/web/e2e/layout-sweep.mjs:732. No after-crops were produced by that run. A follow-up host load sample was 55.05 / 62.27 / 73.55 (1/5/15-minute averages); this sample was taken after the abort, so it does not establish the 503 cause. I am running the focused production matrix for all Settings sections and the seven requested icon-label examples, then will report the full-sweep gap separately.Settings contrast finding: the focused production sweep measured the Notifications icon disc at 1.10:1 against its card in paper mode at 820 px.
SettingsRow.sveltedocuments the shared--settings-icon-surfacerole as at least 1.3:1, and the layout sweep currently enforces that threshold. The 1440 px Tokyo Night sample measured 1.76:1. The complete matrix is still running; I will adjust the shared role token if the other measurements confirm the same gap.Settings chip finding:
settings-notificationsat 390 px in paper mode reports max state-chip centre delta 0.69 px from the title cap centre. The #253 acceptance limit is 0.5 px. Its shared rule is inSettingsRow.svelte; I am collecting the remaining widths, themes and 200% text-scale measurements before adjusting that primitive.Screenshot evidence finding: the production after-crop matrix currently has 35 of 42 expected files. The seven missing are
notifications-row-390-paper,account-row-chip-390-paper,account-row-chip-390-tokyo-night, bothsidebar-row-390themes and bothtoast-390themes. The route sweep captured Settings rows before scrolling them into view, did not open the phone sidebar before its crop, and the phone toast expired before capture. I will update the sweep to prepare these real UI states before taking the 3× crops and rerun the crop pass.The post-fix Settings matrix found one remaining threshold miss at 390 px in Paper mode:
/settings/accountreports a maximum state-chip centre delta of 0.79 px from the title cap centre (limit 0.50 px). The SettingsRow CSS needs another shared-primitive correction. The focused matrix is still running; I will report the exact row and verification result after it finishes.The same 390 px Paper pass reports the Notifications row trailing control 0.51 px from the title-plus-meta block centre (limit 0.50 px). I will keep the shared row alignment assertion unchanged and check the row geometry after the current matrix reports its per-row failure details.
The 200% text pass found the same primitive issue on Admin Users at 820 px in Paper mode: its state chip is 0.84 px off the title cap centre. This scales beyond the 0.50 px limit and confirms the generated 1.4 cap-ratio estimate is insufficient for the active font. The shared SettingsRow now uses the browser's cap/em ratio where supported; the current production sweep predates that source change.
Owner report (2026-09-30, screenshot, Files sidebar on calternal.cloud at
99288dcfe): the folder icons in 'Notes' / 'Photos' rows sit visibly LOWER than the label text (icon optical centre below the x-height centre). Include the Files sidebar folder rows in the alignment fix and in the icon/label glyph-bounds assertion; zoom-check at 390 and 1440, both themes.The pre-fix production sweep also reported
.fc-itemicon/label failures and acreateTreeWalker(null)crash at 390 px. The FileCollection's file icon/name pair already uses IconLabel; the unmatched glyphs were separate selection-check controls inside the same option. The sweep now excludes those control marks and reports a missing responsive label target without aborting the viewport pass. Commit06471e46fincludes this harness correction, the cap-unit SettingsRow fix, and removal of the Notifications row's existing-1pxaction nudge.Finished implementation on
job/align-253atc968d1ff518aa6b9c58a7df52af60552d1eb7070.Built
IconLabelprimitive and static font metric helpers. Migrated icon and label pairs across the web app and shared UI/editor components.apps/web/e2e/layout-sweep.mjsto check painted icon centres against label cap centres at a 0.5 px limit, fail when a labelled control bypassesIconLabel, exercise both themes, and measure User/Admin Settings row icon, control, chip and dark icon-disc contrast at 390/820/1440 px and 100%/200% text.SettingsRowcap alignment using the inherited font's1cap / 1emratio where supported, with generated font metrics as fallback. Removed the Notifications row's vertical action nudge.Key files
packages/ui/src/components/IconLabel.svelte,packages/ui/src/iconLabelGeometry.ts,packages/ui/src/iconLabelElement.ts,scripts/icon-label-font-metrics.mjs,apps/web/e2e/layout-sweep.mjs,apps/web/e2e/layout-sweep-policy.mjs,apps/web/src/routes/settings/parts/SettingsRow.svelte,apps/web/src/routes/settings/notifications/NotificationsSection.svelte, and migrated icon-label sites inapps/web/src/lib,apps/web/src/routes,packages/ui, andpackages/editor.Verification
bun run check:Focused UI tests after the latest Settings fix:
bun run buildexited 0; final build output:The build log includes existing bundler warnings.
Full
bun run testresult:The failures were timeout/hook-timeout failures under shared-host load. I did not change existing test expectations.
git diff --check && node --check apps/web/e2e/layout-sweep.mjspassed.Known gaps
The post-fix production screenshot matrix is incomplete.
artifacts/align-253/beforehas 42 crops;artifacts/align-253/afterhas 37 crops captured before the latest cap correction. The focused production sweep also ran before that correction and reported the chip/control deltas recorded in the earlier comments. I could not rerun the post-fix production matrix while the single browser slot was occupied and the job timebox was reached. These incomplete after-crops are not final proof and were not attached to this issue.Decisions
1cap / 1emfor the chip's font-relative cap geometry when supported, while retaining the generated metric token for older engines.cargo cleancompleted. Web build output was removed. No Rust files changed.Owner report 2026-09-30: the Calendar sidebar scrolled sideways (content shifted ~10 px left; mini-calendar and labels clipped). Stop-gap
5896bf2bcclips the x axis. As part of #253, find which row is wider than the sidebar (the today ring, a selected pill, the tag count?) and fix its width, and add an e2e assertion that every sidebar scroll area has scrollWidth <= clientWidth at 390/820/1440 in both themes.The #652 send-back review found a remaining Search Files row alignment issue: the title cap height is not centred against the file-kind glyph in two-line phone rows. The #652 job is leaving this for the app-wide icon + label primitive and automated alignment check tracked here, as the reviewer directed. Evidence:
search-files-light-390.pngfrom the #652 screenshot set.During the #652 send-back, two-line Search result rows with snippets still center the file glyph against the combined title-and-snippet stack. The first title cap therefore sits above the glyph centre. Please include this Search file-result case in the #253 cap-height matrix.
Evidence from the latest production build, macOS-emulated at 390 px in Light: Search and Files row crops, 3×.