UI consistency sweep: no rings on mouse or shortcut focus, button sizes, alignment, plus a guard #1385

Closed
opened 2026-10-07 17:24:29 +00:00 by kayg · 19 comments
Owner

Owner rule (2026-10-07)

"please, please, please make any extension of the UI match our existing aesthetic. buttons are properly sized, no selection rings for mouse actions, no automatic selection rings for keyboard shortcuts. proper alignment, etc. the more features we add, the more slop we are adding"

This rule is now in the shared job preamble for every job. This issue is the sweep of what already shipped, plus a guard so it does not come back.

Rule

  • Reuse packages/ui components and role tokens; copy the closest existing screen. No one-off styles, no numeric literals.
  • Buttons use the shared size variants and match their neighbours and the same action elsewhere.
  • A click or tap never leaves a focus or selection ring. A keyboard shortcut that opens UI and moves focus does not show a ring. The ring shows only after Tab or arrow-key navigation (:focus-visible plus the shared data-input flag). Selected and current state is a tinted fill, never a border or outline.
  • Icons and labels align on one center line; edges line up with the surrounding UI.

Work

  1. Guard (automated): a Playwright sweep over every route and every overlay (Search, Cmd+K, Cmd+I inspector, menus, sheets, pickers, toasts, dialogs) in light and dark, desktop and phone width. It (a) clicks each interactive control with the mouse and asserts no visible outline, ring or box-shadow focus style; (b) opens each overlay with its shortcut and asserts no ring on the auto-focused element; (c) presses Tab and asserts the ring does show. Extend scripts/check-focus-tokens.mjs (or the closest existing guard) to fail on :focus (not :focus-visible) ring styles and on programmatic focus() calls without the shared no-ring helper.
  2. Sweep and fix every violation the guard finds, plus button-size drift (same action, different size), and icon+label misalignment found in actual-size screenshots. Fix at the shared primitive when the cause is shared; otherwise in the component.
  3. Report: a table of each fix (route, before/after screenshot at actual size, cause). Anything that needs a design decision is listed, not guessed.
## Owner rule (2026-10-07) "please, please, please make any extension of the UI match our existing aesthetic. buttons are properly sized, no selection rings for mouse actions, no automatic selection rings for keyboard shortcuts. proper alignment, etc. the more features we add, the more slop we are adding" This rule is now in the shared job preamble for every job. This issue is the sweep of what already shipped, plus a guard so it does not come back. ## Rule - Reuse `packages/ui` components and role tokens; copy the closest existing screen. No one-off styles, no numeric literals. - Buttons use the shared size variants and match their neighbours and the same action elsewhere. - A click or tap never leaves a focus or selection ring. A keyboard shortcut that opens UI and moves focus does not show a ring. The ring shows only after Tab or arrow-key navigation (`:focus-visible` plus the shared `data-input` flag). Selected and current state is a tinted fill, never a border or outline. - Icons and labels align on one center line; edges line up with the surrounding UI. ## Work 1. **Guard (automated):** a Playwright sweep over every route and every overlay (Search, Cmd+K, Cmd+I inspector, menus, sheets, pickers, toasts, dialogs) in light and dark, desktop and phone width. It (a) clicks each interactive control with the mouse and asserts no visible outline, ring or box-shadow focus style; (b) opens each overlay with its shortcut and asserts no ring on the auto-focused element; (c) presses Tab and asserts the ring does show. Extend `scripts/check-focus-tokens.mjs` (or the closest existing guard) to fail on `:focus` (not `:focus-visible`) ring styles and on programmatic `focus()` calls without the shared no-ring helper. 2. **Sweep and fix** every violation the guard finds, plus button-size drift (same action, different size), and icon+label misalignment found in actual-size screenshots. Fix at the shared primitive when the cause is shared; otherwise in the component. 3. **Report:** a table of each fix (route, before/after screenshot at actual size, cause). Anything that needs a design decision is listed, not guessed.
Author
Owner

Part 0, first and highest priority: typing in the Notes editor shows rings (owner, 2026-10-07)

"right now just typing in the block editor causes selection rings, they should only be caused by explicit keyboard navigation! please do a proper audit of those."

Reproduce it in a real browser (Chromium and WebKit, macOS rendering): open a Note, click into text, type, use Enter, Backspace, arrows, Home/End, Cmd+B and other editor shortcuts, the slash menu, Escape. Screenshot every ring that appears and name the element and the CSS rule that draws it.

Starting point (orchestrator reading of the code; verify it): apps/web/src/lib/a11y/inputModality.ts keydownSignalsKeyboardModality flips <html data-input> to "keyboard" for arrows, Home/End, PageUp/PageDown and Escape even when the target is an editable (caret movement while writing), for Cmd/Ctrl+K, and for ANY key on a non-editable target (every single-key shortcut). +layout.svelte openSettingsFromKeyboard sets it to "keyboard" directly. With the flag on "keyboard", calternal-app.css stops suppressing :focus-visible, so any focused element (the editor, block handles, the title, toolbar, menus opened by shortcut) rings.

Target rule (owner): a ring shows only after explicit keyboard NAVIGATION that moves focus between controls: Tab / Shift+Tab, and arrow keys that move focus between controls outside a text field (menus, lists, grids, toolbars with roving focus). NOT for: typing or caret movement in any editable (arrows, Home/End, PageUp/PageDown, Enter, Backspace, Escape inside the editor), keyboard shortcuts (Cmd+K, Cmd+I, single-key shortcuts, Settings shortcut) or focus that code moves when a shortcut opens UI. A combobox keeps its active option as a tinted row, not a focus ring. After a shortcut opens an overlay, the first Tab or arrow navigation inside it shows the ring.

Do a proper audit, not one patch: list every place that sets data-input, every :focus-visible / :focus / outline / focus box-shadow rule (packages/ui, packages/editor, apps/web), and every programmatic focus() call, and state for each whether it follows the rule. Fix the modality predicate once (shared primitive), update its module doc and unit tests (typing and caret keys in contenteditable, input and textarea keep modality; shortcuts keep modality; Tab flips; arrows on a roving-focus menu flip). The Playwright guard must include the editor typing flow and assert zero rings. DESIGN.md §34 and CLAUDE.md "Motion and loading" mention keyboard focus rings: update their wording to this rule (rings only for keyboard navigation) in ASD-STE100 style.

## Part 0, first and highest priority: typing in the Notes editor shows rings (owner, 2026-10-07) "right now just typing in the block editor causes selection rings, they should only be caused by explicit keyboard navigation! please do a proper audit of those." Reproduce it in a real browser (Chromium and WebKit, macOS rendering): open a Note, click into text, type, use Enter, Backspace, arrows, Home/End, Cmd+B and other editor shortcuts, the slash menu, Escape. Screenshot every ring that appears and name the element and the CSS rule that draws it. Starting point (orchestrator reading of the code; verify it): `apps/web/src/lib/a11y/inputModality.ts` `keydownSignalsKeyboardModality` flips `<html data-input>` to "keyboard" for arrows, Home/End, PageUp/PageDown and Escape even when the target is an editable (caret movement while writing), for Cmd/Ctrl+K, and for ANY key on a non-editable target (every single-key shortcut). `+layout.svelte` `openSettingsFromKeyboard` sets it to "keyboard" directly. With the flag on "keyboard", `calternal-app.css` stops suppressing `:focus-visible`, so any focused element (the editor, block handles, the title, toolbar, menus opened by shortcut) rings. Target rule (owner): a ring shows only after explicit keyboard NAVIGATION that moves focus between controls: Tab / Shift+Tab, and arrow keys that move focus between controls outside a text field (menus, lists, grids, toolbars with roving focus). NOT for: typing or caret movement in any editable (arrows, Home/End, PageUp/PageDown, Enter, Backspace, Escape inside the editor), keyboard shortcuts (Cmd+K, Cmd+I, single-key shortcuts, Settings shortcut) or focus that code moves when a shortcut opens UI. A combobox keeps its active option as a tinted row, not a focus ring. After a shortcut opens an overlay, the first Tab or arrow navigation inside it shows the ring. Do a proper audit, not one patch: list every place that sets `data-input`, every `:focus-visible` / `:focus` / `outline` / focus `box-shadow` rule (packages/ui, packages/editor, apps/web), and every programmatic `focus()` call, and state for each whether it follows the rule. Fix the modality predicate once (shared primitive), update its module doc and unit tests (typing and caret keys in contenteditable, input and textarea keep modality; shortcuts keep modality; Tab flips; arrows on a roving-focus menu flip). The Playwright guard must include the editor typing flow and assert zero rings. DESIGN.md §34 and CLAUDE.md "Motion and loading" mention keyboard focus rings: update their wording to this rule (rings only for keyboard navigation) in ASD-STE100 style.
Author
Owner

Starting #1385 on job/slop-1385, based on 0419170fe456b58614befcb12d9c9d255118f1c0 (origin/dev). I have read the issue and the required project contract, glossary, and DESIGN sections. I am auditing the focus sources and existing browser guards first; I will add the guard before applying fixes.

Starting #1385 on `job/slop-1385`, based on `0419170fe456b58614befcb12d9c9d255118f1c0` (`origin/dev`). I have read the issue and the required project contract, glossary, and DESIGN sections. I am auditing the focus sources and existing browser guards first; I will add the guard before applying fixes.
Author
Owner

#1385 guard pre-fix violation list

The extended static guard found 133 direct DOM focus calls without the shared focusWithPolicy helper. It found no local CSS ring declaration violations; the current ring declarations use shared tokens or the exact temporary owner baselines.

Locations (path: line numbers):

  • apps/web/src/lib/a11y/focusTrap.ts: 198, 206, 209, 213
  • apps/web/src/lib/a11y/focusWithKeyboard.ts: 218
  • apps/web/src/lib/actions/rovingToolbar.ts: 58
  • apps/web/src/lib/auth/components/RecoveryKeyEntry.svelte: 91, 129, 148, 169, 205
  • apps/web/src/lib/canvas/CanvasReact.tsx: 299, 311
  • apps/web/src/lib/canvas/DrawingBlock.svelte: 76, 207, 234, 237, 248
  • apps/web/src/lib/components/AppToaster.svelte: 269, 271, 313
  • apps/web/src/lib/components/TagEditor.svelte: 101, 174
  • apps/web/src/lib/components/TagRow.svelte: 69, 78
  • apps/web/src/lib/components/analytics/BklitKeyboardMarks.tsx: 115
  • apps/web/src/lib/components/analytics/RangePicker.svelte: 247
  • apps/web/src/lib/components/money/AssignedCell.svelte: 41
  • apps/web/src/lib/components/money/MoneyImport.svelte: 115
  • apps/web/src/lib/components/money/TransactionForm.svelte: 70, 100
  • apps/web/src/lib/components/search-dialog.svelte: 330, 549, 664, 860, 866, 1135
  • apps/web/src/lib/editor/format/BlockHoverActions.svelte: 224
  • apps/web/src/lib/editor/format/EditorFormatBar.svelte: 57
  • apps/web/src/lib/editor/format/FormatCommandRow.svelte: 127
  • apps/web/src/lib/files/FilesBrowser.svelte: 355, 484, 801, 823, 1736, 1842
  • apps/web/src/lib/files/InfoPanel.svelte: 275
  • apps/web/src/lib/files/PublicLinkPage.svelte: 141
  • apps/web/src/lib/files/RecentView.svelte: 398, 416
  • apps/web/src/lib/mail/MailView.svelte: 1027
  • apps/web/src/lib/notes/NoteContents.svelte: 120
  • apps/web/src/lib/notes/NoteEditorSurface.svelte: 232
  • apps/web/src/lib/notes/NotesExplorer.svelte: 380
  • apps/web/src/lib/notes/TaskHeader.svelte: 72
  • apps/web/src/lib/notes/editorHost.ts: 186
  • apps/web/src/lib/notifications/InboxPanel.svelte: 211, 223, 291
  • apps/web/src/lib/photos/PhotoTimeline.svelte: 325
  • apps/web/src/lib/search/SearchField.svelte: 151, 283, 289
  • apps/web/src/lib/tasks/TaskBoard.svelte: 144
  • apps/web/src/routes/+layout.svelte: 869
  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte: 2067
  • apps/web/src/routes/login/+page.svelte: 117
  • apps/web/src/routes/money/[budget]/[month]/+page.svelte: 147
  • apps/web/src/routes/money/[budget]/accounts/account/+page.svelte: 129
  • apps/web/src/routes/settings/[...path]/+page.svelte: 285
  • apps/web/src/routes/settings/account/AppPasswordsGroup.svelte: 235
  • apps/web/src/routes/settings/account/LocationGroup.svelte: 56, 119, 133, 142, 168
  • apps/web/src/routes/settings/account/PasskeysGroup.svelte: 56, 62, 70, 81, 102, 109
  • apps/web/src/routes/settings/account/ProfileGroup.svelte: 50, 57, 67
  • apps/web/src/routes/settings/admin/ConfigGroup.svelte: 150
  • apps/web/src/routes/settings/ai/AiSection.svelte: 86
  • apps/web/src/routes/settings/api.svelte.ts: 174
  • apps/web/src/routes/settings/appearance/BackgroundGroup.svelte: 471, 496, 863
  • apps/web/src/routes/settings/calendars/CalendarsSection.svelte: 168
  • apps/web/src/routes/settings/connected-accounts/ConnectedAccountsSection.svelte: 521
  • packages/ui/src/clipboard.ts: 46
  • packages/ui/src/components/DateStrip.svelte: 99
  • packages/ui/src/components/FloatingSidebar.svelte: 281
  • packages/ui/src/components/ModeHeader.svelte: 216
  • packages/ui/src/components/PopoverSurface.svelte: 124, 185
  • packages/ui/src/components/SegmentedControl.svelte: 557
  • packages/ui/src/components/TabBar.svelte: 159, 310, 312, 315, 316
  • packages/ui/src/components/calendar/AgendaList.svelte: 467, 588, 661, 947
  • packages/ui/src/components/calendar/AttachmentDeck.svelte: 332, 610, 639
  • packages/ui/src/components/calendar/ItemPreview.svelte: 175
  • packages/ui/src/components/calendar/LogEntryEditor.svelte: 38
  • packages/ui/src/components/calendar/MonthGrid.svelte: 210
  • packages/ui/src/components/calendar/TimeGrid.svelte: 459, 460
  • packages/ui/src/components/composer/ModePill.svelte: 65, 78
  • packages/ui/src/components/composer/VoiceRecordingPill.svelte: 119, 124, 135
  • packages/ui/src/components/files/FileCollection.svelte: 375, 451
  • packages/ui/src/components/menu/FloatingSurface.svelte: 385
  • packages/ui/src/components/menu/Menu.svelte: 180, 196, 217, 223, 228, 272, 503
  • packages/ui/src/components/notes/NoteProperties.svelte: 104

The checker excludes editor command-chain focus (Tiptap selection restore), Note prefetch intent focus, WindowClient.focus and the OIDC popup Window.focus because these are not DOM element focus calls.

#1385 guard pre-fix violation list The extended static guard found 133 direct DOM focus calls without the shared focusWithPolicy helper. It found no local CSS ring declaration violations; the current ring declarations use shared tokens or the exact temporary owner baselines. Locations (path: line numbers): - apps/web/src/lib/a11y/focusTrap.ts: 198, 206, 209, 213 - apps/web/src/lib/a11y/focusWithKeyboard.ts: 218 - apps/web/src/lib/actions/rovingToolbar.ts: 58 - apps/web/src/lib/auth/components/RecoveryKeyEntry.svelte: 91, 129, 148, 169, 205 - apps/web/src/lib/canvas/CanvasReact.tsx: 299, 311 - apps/web/src/lib/canvas/DrawingBlock.svelte: 76, 207, 234, 237, 248 - apps/web/src/lib/components/AppToaster.svelte: 269, 271, 313 - apps/web/src/lib/components/TagEditor.svelte: 101, 174 - apps/web/src/lib/components/TagRow.svelte: 69, 78 - apps/web/src/lib/components/analytics/BklitKeyboardMarks.tsx: 115 - apps/web/src/lib/components/analytics/RangePicker.svelte: 247 - apps/web/src/lib/components/money/AssignedCell.svelte: 41 - apps/web/src/lib/components/money/MoneyImport.svelte: 115 - apps/web/src/lib/components/money/TransactionForm.svelte: 70, 100 - apps/web/src/lib/components/search-dialog.svelte: 330, 549, 664, 860, 866, 1135 - apps/web/src/lib/editor/format/BlockHoverActions.svelte: 224 - apps/web/src/lib/editor/format/EditorFormatBar.svelte: 57 - apps/web/src/lib/editor/format/FormatCommandRow.svelte: 127 - apps/web/src/lib/files/FilesBrowser.svelte: 355, 484, 801, 823, 1736, 1842 - apps/web/src/lib/files/InfoPanel.svelte: 275 - apps/web/src/lib/files/PublicLinkPage.svelte: 141 - apps/web/src/lib/files/RecentView.svelte: 398, 416 - apps/web/src/lib/mail/MailView.svelte: 1027 - apps/web/src/lib/notes/NoteContents.svelte: 120 - apps/web/src/lib/notes/NoteEditorSurface.svelte: 232 - apps/web/src/lib/notes/NotesExplorer.svelte: 380 - apps/web/src/lib/notes/TaskHeader.svelte: 72 - apps/web/src/lib/notes/editorHost.ts: 186 - apps/web/src/lib/notifications/InboxPanel.svelte: 211, 223, 291 - apps/web/src/lib/photos/PhotoTimeline.svelte: 325 - apps/web/src/lib/search/SearchField.svelte: 151, 283, 289 - apps/web/src/lib/tasks/TaskBoard.svelte: 144 - apps/web/src/routes/+layout.svelte: 869 - apps/web/src/routes/calendar/[view]/[date]/+page.svelte: 2067 - apps/web/src/routes/login/+page.svelte: 117 - apps/web/src/routes/money/[budget]/[month]/+page.svelte: 147 - apps/web/src/routes/money/[budget]/accounts/[[account]]/+page.svelte: 129 - apps/web/src/routes/settings/[...path]/+page.svelte: 285 - apps/web/src/routes/settings/account/AppPasswordsGroup.svelte: 235 - apps/web/src/routes/settings/account/LocationGroup.svelte: 56, 119, 133, 142, 168 - apps/web/src/routes/settings/account/PasskeysGroup.svelte: 56, 62, 70, 81, 102, 109 - apps/web/src/routes/settings/account/ProfileGroup.svelte: 50, 57, 67 - apps/web/src/routes/settings/admin/ConfigGroup.svelte: 150 - apps/web/src/routes/settings/ai/AiSection.svelte: 86 - apps/web/src/routes/settings/api.svelte.ts: 174 - apps/web/src/routes/settings/appearance/BackgroundGroup.svelte: 471, 496, 863 - apps/web/src/routes/settings/calendars/CalendarsSection.svelte: 168 - apps/web/src/routes/settings/connected-accounts/ConnectedAccountsSection.svelte: 521 - packages/ui/src/clipboard.ts: 46 - packages/ui/src/components/DateStrip.svelte: 99 - packages/ui/src/components/FloatingSidebar.svelte: 281 - packages/ui/src/components/ModeHeader.svelte: 216 - packages/ui/src/components/PopoverSurface.svelte: 124, 185 - packages/ui/src/components/SegmentedControl.svelte: 557 - packages/ui/src/components/TabBar.svelte: 159, 310, 312, 315, 316 - packages/ui/src/components/calendar/AgendaList.svelte: 467, 588, 661, 947 - packages/ui/src/components/calendar/AttachmentDeck.svelte: 332, 610, 639 - packages/ui/src/components/calendar/ItemPreview.svelte: 175 - packages/ui/src/components/calendar/LogEntryEditor.svelte: 38 - packages/ui/src/components/calendar/MonthGrid.svelte: 210 - packages/ui/src/components/calendar/TimeGrid.svelte: 459, 460 - packages/ui/src/components/composer/ModePill.svelte: 65, 78 - packages/ui/src/components/composer/VoiceRecordingPill.svelte: 119, 124, 135 - packages/ui/src/components/files/FileCollection.svelte: 375, 451 - packages/ui/src/components/menu/FloatingSurface.svelte: 385 - packages/ui/src/components/menu/Menu.svelte: 180, 196, 217, 223, 228, 272, 503 - packages/ui/src/components/notes/NoteProperties.svelte: 104 The checker excludes editor command-chain focus (Tiptap selection restore), Note prefetch intent focus, WindowClient.focus and the OIDC popup Window.focus because these are not DOM element focus calls.
Author
Owner

Production Chromium baseline finding for #1385:

  • Clicking into the real Note editor starts with data-input="pointer" and no ring.
  • Pressing Escape or an editor caret key changes the root to data-input="keyboard" and paints a ring on div.tiptap.ProseMirror.cal-prose[role="textbox"].
  • Chromium reports rgb(61, 90, 196) solid 2px. The ring is drawn by :focus-visible:not([data-focus-ring="none"]) in packages/ui/src/tokens.css (outline: var(--focus-ring)); the editor surface's local box-shadow: none rule does not remove that outline.
  • The production screenshot and CSS rule report are in artifacts/focus-1385/before/Chromium/.
Production Chromium baseline finding for #1385: - Clicking into the real Note editor starts with `data-input="pointer"` and no ring. - Pressing Escape or an editor caret key changes the root to `data-input="keyboard"` and paints a ring on `div.tiptap.ProseMirror.cal-prose[role="textbox"]`. - Chromium reports `rgb(61, 90, 196) solid 2px`. The ring is drawn by `:focus-visible:not([data-focus-ring="none"])` in `packages/ui/src/tokens.css` (`outline: var(--focus-ring)`); the editor surface's local `box-shadow: none` rule does not remove that outline. - The production screenshot and CSS rule report are in `artifacts/focus-1385/before/Chromium/`.
Author
Owner

Pre-fix browser findings for #1385 (production app, macOS platform emulation):

  • Chromium and WebKit both show a 2 px focus outline on div.tiptap.ProseMirror.cal-prose[role=textbox][contenteditable=true] after Escape, caret arrows, Home, End, PageUp, PageDown, Cmd+B, Cmd+I and the slash menu. The ring remains when typing after caret movement.
  • WebKit also shows the ring when Enter moves focus from block selection into the Note editor, and when Escape restores focus to the block-selection surface.
  • The ring comes from :focus-visible:not([data-focus-ring="none"]) in packages/ui/src/tokens.css (outline: var(--focus-ring)). NoteEditorSurface.svelte sets box-shadow: none; that does not remove the shared outline.
  • A pointer click starts without a ring. Tab into Search shows a ring after the overlay opens.

The before captures are in artifacts/focus-1385-review6/before/Chromium/ and artifacts/focus-1385-review6/before/WebKit/. Each engine has light and dark captures at 390, 820 and 1440 CSS px. The editor key captures include the focused element and matched CSS rules in the probe output. The pre-fix guard reports 50 expected violations across both engines.

Pre-fix browser findings for #1385 (production app, macOS platform emulation): - Chromium and WebKit both show a 2 px focus outline on `div.tiptap.ProseMirror.cal-prose[role=textbox][contenteditable=true]` after Escape, caret arrows, Home, End, PageUp, PageDown, Cmd+B, Cmd+I and the slash menu. The ring remains when typing after caret movement. - WebKit also shows the ring when Enter moves focus from block selection into the Note editor, and when Escape restores focus to the block-selection surface. - The ring comes from `:focus-visible:not([data-focus-ring="none"])` in `packages/ui/src/tokens.css` (`outline: var(--focus-ring)`). `NoteEditorSurface.svelte` sets `box-shadow: none`; that does not remove the shared outline. - A pointer click starts without a ring. Tab into Search shows a ring after the overlay opens. The before captures are in `artifacts/focus-1385-review6/before/Chromium/` and `artifacts/focus-1385-review6/before/WebKit/`. Each engine has light and dark captures at 390, 820 and 1440 CSS px. The editor key captures include the focused element and matched CSS rules in the probe output. The pre-fix guard reports 50 expected violations across both engines.
Author
Owner

Performance-lint finding from the post-merge bun run check: removing the empty .attachment-row:focus {} block in packages/ui/src/components/calendar/AttachmentDeck.svelte changed the exact parsed list hash for each:99612e0541854137:1 from 32ce8d34fe081e970cad823badcde9be2483b3a4e29d70919836cd68c19a3dea to b8a32a9f0f39273c91af6e88c24036fc15487c5ddd95e799ea050613e6d03d84. I reviewed the diff: the renderedRefs loop, its behavior, and its existing bounds did not change. I will refresh only that exact source hash in its registry and current pins; no limit or ratchet changes.

Performance-lint finding from the post-merge `bun run check`: removing the empty `.attachment-row:focus {}` block in `packages/ui/src/components/calendar/AttachmentDeck.svelte` changed the exact parsed list hash for `each:99612e0541854137:1` from `32ce8d34fe081e970cad823badcde9be2483b3a4e29d70919836cd68c19a3dea` to `b8a32a9f0f39273c91af6e88c24036fc15487c5ddd95e799ea050613e6d03d84`. I reviewed the diff: the `renderedRefs` loop, its behavior, and its existing bounds did not change. I will refresh only that exact source hash in its registry and current pins; no limit or ratchet changes.
Author
Owner

The exact source comparison found 18 existing material.local-filter pins in packages/ui/src/tokens.css whose syntax hashes changed when #1385 added the shared focus suppression rule. Each live finding still has actual='active local filter', matching its existing limit. I reviewed the token diff: no Glass or backdrop-filter declaration changed. I will refresh only these 18 source hashes. No rule limit, exception count or ratchet changes.

The exact source comparison found 18 existing `material.local-filter` pins in `packages/ui/src/tokens.css` whose syntax hashes changed when #1385 added the shared focus suppression rule. Each live finding still has `actual='active local filter'`, matching its existing limit. I reviewed the token diff: no Glass or backdrop-filter declaration changed. I will refresh only these 18 source hashes. No rule limit, exception count or ratchet changes.
Author
Owner

Evidence from the #1371 production replay (WebKit and Chromium, light theme, artifacts/wklink/*-light-adjacent-undo.png in worktree wklink-1371): after selecting with Shift+Arrow and pressing Cmd+Z, the whole Note editor surface gets a dark, square-cornered focus ring (x 450–1140, y 95–353). This is the "typing in the editor causes rings" case.

Required result for Part 0: caret movement, Shift+Arrow selection, Undo/Redo and any typing inside the editor never ring the editor surface. The editor surface is a text field with a caret; it never needs a ring at all. Rings stay for Tab/arrow navigation between controls outside text editing.

Evidence from the #1371 production replay (WebKit and Chromium, light theme, `artifacts/wklink/*-light-adjacent-undo.png` in worktree wklink-1371): after selecting with Shift+Arrow and pressing Cmd+Z, the whole Note editor surface gets a dark, square-cornered focus ring (x 450–1140, y 95–353). This is the "typing in the editor causes rings" case. Required result for Part 0: caret movement, Shift+Arrow selection, Undo/Redo and any typing inside the editor never ring the editor surface. The editor surface is a text field with a caret; it never needs a ring at all. Rings stay for Tab/arrow navigation between controls outside text editing.
Author
Owner

The full focus audit found a second active-descendant case in packages/ui/src/components/files/FileCollection.svelte: the Files listbox keeps DOM focus on its owner and paints the active option through the shared data-focus-ring-owner / data-focus-ring-target hook. The broad active-descendant exclusion suppressed that ring, unlike the existing listbox contract. I narrowed the exception to comboboxes, added listbox unit coverage and a Chromium/WebKit Playwright flow with a real Files API row, and clarified DESIGN §34. Focused tests are running now.

The full focus audit found a second active-descendant case in `packages/ui/src/components/files/FileCollection.svelte`: the Files listbox keeps DOM focus on its owner and paints the active option through the shared `data-focus-ring-owner` / `data-focus-ring-target` hook. The broad active-descendant exclusion suppressed that ring, unlike the existing listbox contract. I narrowed the exception to comboboxes, added listbox unit coverage and a Chromium/WebKit Playwright flow with a real Files API row, and clarified DESIGN §34. Focused tests are running now.
Author
Owner

The Files listbox check also exposed a Tab gap: before an option is active, its shared owner has no child ring target, so Tab could focus the listbox with no visible focus ring. I added a shared token fallback that paints the owner while no active target exists; after ArrowDown, the existing shared rule paints the active row. The production Playwright guard asserts both states in Chromium and WebKit.

The Files listbox check also exposed a Tab gap: before an option is active, its shared owner has no child ring target, so Tab could focus the listbox with no visible focus ring. I added a shared token fallback that paints the owner while no active target exists; after ArrowDown, the existing shared rule paints the active row. The production Playwright guard asserts both states in Chromium and WebKit.
Author
Owner

The first final bun run check found one changed exact material.local-filter adoption hash for the shared packages/ui/src/tokens.css stylesheet. This followed the added shared focus-ring fallback rule. I am refreshing that exact source pin with the repository's adoption command; I will keep the existing performance limits unchanged.

The first final `bun run check` found one changed exact `material.local-filter` adoption hash for the shared `packages/ui/src/tokens.css` stylesheet. This followed the added shared focus-ring fallback rule. I am refreshing that exact source pin with the repository's adoption command; I will keep the existing performance limits unchanged.
Author
Owner

Bench finding: the reused combined kbd-motion-527 runner stopped before its profile collection at the unchanged Search overlay open assertion. Chromium observed keyboard durations [200] and pointer durations [200, 240] at apps/web/e2e/kbd-motion-527.mjs:485. I kept the motion expectation unchanged. I added a profile-only entry point for the same real 1,000-file listbox workload so this issue can still record p50/p95, CPU and RSS; the focused #1385 Playwright guard passes in Chromium and WebKit.

Bench finding: the reused combined `kbd-motion-527` runner stopped before its profile collection at the unchanged Search overlay open assertion. Chromium observed keyboard durations `[200]` and pointer durations `[200, 240]` at `apps/web/e2e/kbd-motion-527.mjs:485`. I kept the motion expectation unchanged. I added a profile-only entry point for the same real 1,000-file listbox workload so this issue can still record p50/p95, CPU and RSS; the focused #1385 Playwright guard passes in Chromium and WebKit.
Author
Owner

Large-list focus finding: the real 1,000-file profile reached its owner-ring check after Tab. The listbox had :focus-visible, data-input="keyboard", no data-focus-ring-suppressed flag and no aria-activedescendant, but its computed outline-style was none; the profile stopped before measurements. The 4-item Files guard did not expose this state. I am changing the shared owner fallback to follow the listbox's aria-activedescendant state, then I will rerun the production-browser guard and the focused profile.

Large-list focus finding: the real 1,000-file profile reached its owner-ring check after Tab. The listbox had `:focus-visible`, `data-input="keyboard"`, no `data-focus-ring-suppressed` flag and no `aria-activedescendant`, but its computed `outline-style` was `none`; the profile stopped before measurements. The 4-item Files guard did not expose this state. I am changing the shared owner fallback to follow the listbox's `aria-activedescendant` state, then I will rerun the production-browser guard and the focused profile.
Author
Owner

Large-list browser finding: after 1,000 real Files API uploads, Chromium reached the Files listbox by Tab with data-input=keyboard, no data-focus-ring-suppressed, :focus-visible=true, and no aria-activedescendant. The owner fallback selector matched, but computed outline-style was none and width was 3px. FileCollection always set data-focus-ring="none", so the shared base ring stayed disabled before an active row existed. I changed the shared FileCollection owner to set that opt-out only after it has an active descendant. The browser guard now checks both the owner ring before selection and the active-row ring after ArrowDown.

Large-list browser finding: after 1,000 real Files API uploads, Chromium reached the Files listbox by Tab with `data-input=keyboard`, no `data-focus-ring-suppressed`, `:focus-visible=true`, and no `aria-activedescendant`. The owner fallback selector matched, but computed `outline-style` was `none` and width was `3px`. `FileCollection` always set `data-focus-ring="none"`, so the shared base ring stayed disabled before an active row existed. I changed the shared `FileCollection` owner to set that opt-out only after it has an active descendant. The browser guard now checks both the owner ring before selection and the active-row ring after ArrowDown.
Author
Owner

#1385 final report

Head SHA: 84f97bf57f71f6afbff178c9d2965ffbec21ef45
The working tree is clean. No push, deploy, merge, or issue close was done.

Built

  • The shared input-modality predicate now enables rings only when keys move focus between controls: Tab and Shift+Tab, and unmodified arrows, Home, or End in roving widgets or on a menu trigger that opens and focuses its menu.
  • Other keys set the shared root suppression flag before handlers can move focus. This keeps Note typing, caret movement, editor shortcuts, the slash menu, Cmd+K, the Settings shortcut, and code-restored focus ring-free until the next navigation key. Reduced motion and shared motion timing stay independent.
  • The Notes editor keeps its caret paint separate from the shared outline ring. Search combobox arrows keep the active option tinted without a focus ring.
  • Files keeps the shared ring on its listbox owner after Tab until an active option exists. Arrow navigation then gives the shared ring to the active row.
  • The static focus audit checks shared ring rules and classifies every programmatic focus call. The Playwright guard runs the real production app in Chromium and WebKit with macOS platform emulation.
  • docs/DESIGN.md §34 and CLAUDE.md now state the navigation-only ring rule. The generated performance pins changed only exact source hashes; exception counts and limits did not change.

Files

  • apps/web/src/app.html, apps/web/src/calternal-app.css, apps/web/src/lib/a11y/inputModality.ts, apps/web/src/lib/a11y/inputModality.test.ts, apps/web/src/lib/a11y/focusTrap.ts, apps/web/src/lib/notes/NoteEditorSurface.svelte, and apps/web/src/routes/+layout.svelte
  • packages/ui/src/tokens.css, packages/ui/src/components/files/FileCollection.svelte, and packages/ui/src/components/calendar/AttachmentDeck.svelte
  • apps/web/scripts/check-focus-tokens.mjs, apps/web/e2e/focus-policy-1385.mjs, apps/web/e2e/kbd-motion-527.mjs, apps/web/src/lib/themes.test.ts, and apps/web/package.json
  • bench/kbd-motion-527.mjs, contracts/perf/registry.json, contracts/perf/adoption-1058.json, and contracts/perf/exceptions.json
  • docs/DESIGN.md and CLAUDE.md

Audit

The boot value is pointer in apps/web/src/app.html:3. The two runtime writers are in apps/web/src/routes/+layout.svelte:1260 (pointer or touch) and +layout.svelte:1265 (recognized keyboard navigation). The audit found 109 focus selectors, 51 focus ring rules, 132 DOM focus calls, and 3 data-input writers. The shared root suppression policy covers the focus calls. Each selector and focus call has a classification in the attached source audit.

Gates

cd apps/web && bun run check passed. Verbatim output:

PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22544 scoped exceptions
User browser caches use userStorage; only documented device/public-link exceptions remain.
Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css.
Text sizes and UI shape values use shared role tokens.
Focus policy passes: 51 focus ring rules, 132 DOM focus calls and 3 data-input writers audited.
UI transitions and animation options use shared motion tokens or documented exceptions.
svelte-check found 0 errors and 0 warnings

bunx vitest run src/lib/a11y/inputModality.test.ts src/lib/themes.test.ts --maxWorkers=2 passed:

 Test Files  2 passed (2)
      Tests  90 passed (90)
   Start at  02:17:32
   Duration  75.72s (transform 63%, tests 23%, environment 8%, import 5%)

bun run test:e2e:focus-policy-1385 passed against the production build and shared server:

Version 1.63.0
PASS #1385 focus policy: editor typing and shortcut focus stay quiet; Tab, roving-menu and Files listbox navigation show rings.

git diff --check passed. No Rust source changed. cargo clean output:

Removed 1 file, 356B total

Performance

The local profile loaded 1,000 real Files and measured 100 selection moves. Load average before the run was 28.2, 31.1, 37.91. There is no keyboard-motion baseline in docs/perf/baseline.json, so these are local measurements without a regression comparison.

  • Average selection: p50 10.81 ms, p95 59.24 ms, browser CPU 0.78 s, RSS 621 MB before and 630 MB after.
  • Burst of 100 moves: p50 9.42 ms, p95 19.53 ms, browser CPU 2.26 s, peak RSS 642 MB.
  • The initial owner ring and the active row both used the shared 2px ring. The change checks one owner attribute per interaction; work does not scan the 1,000 items.

The machine-readable profile and 213 actual-size screenshots are in the issue attachments. Screenshots use macOS emulation at 390, 820, and 1440 px, in light and dark themes. Current Files owner and active-row screenshots:

Editor typing screenshots: before, current Chromium, and current WebKit. Shortcut focus screenshots: Cmd+K opens quietly, then the first Tab shows a ring.

UX gaps closed

  • Note typing, Enter, Backspace, caret arrows, Home/End, PageUp/PageDown, Cmd+B, Cmd+I, slash menu, Escape, Cmd+K, and the Settings shortcut stay ring-free in Chromium and WebKit.
  • Tab and Shift+Tab, roving-menu arrows, the first Tab after opening Search or Settings, and Files owner-to-active-row navigation show the shared ring.
  • Pointer focus and code-moved focus do not leave a ring. Keyboard Tab access remains available.

UX gaps left

  • A separate existing motion check still reports the Search overlay duration as keyboard [200] and pointer [200, 240]. I kept its expectation unchanged and recorded this finding in an earlier issue comment; this focus-policy change does not resolve that motion mismatch.
  • The full web suite was not run in this job. The merge round must run cd apps/web && bun run test on the combined branch.

Decision

DESIGN.md §34 did not define the focus handoff for a listbox that keeps DOM focus on its owner. The Files listbox keeps its owner ring until an active option exists, then gives the ring to that option. This keeps Tab focus visible before the first arrow and avoids two rings after selection.

# #1385 final report Head SHA: `84f97bf57f71f6afbff178c9d2965ffbec21ef45` The working tree is clean. No push, deploy, merge, or issue close was done. ## Built - The shared input-modality predicate now enables rings only when keys move focus between controls: Tab and Shift+Tab, and unmodified arrows, Home, or End in roving widgets or on a menu trigger that opens and focuses its menu. - Other keys set the shared root suppression flag before handlers can move focus. This keeps Note typing, caret movement, editor shortcuts, the slash menu, Cmd+K, the Settings shortcut, and code-restored focus ring-free until the next navigation key. Reduced motion and shared motion timing stay independent. - The Notes editor keeps its caret paint separate from the shared outline ring. Search combobox arrows keep the active option tinted without a focus ring. - Files keeps the shared ring on its listbox owner after Tab until an active option exists. Arrow navigation then gives the shared ring to the active row. - The static focus audit checks shared ring rules and classifies every programmatic focus call. The Playwright guard runs the real production app in Chromium and WebKit with macOS platform emulation. - `docs/DESIGN.md` §34 and `CLAUDE.md` now state the navigation-only ring rule. The generated performance pins changed only exact source hashes; exception counts and limits did not change. ## Files - `apps/web/src/app.html`, `apps/web/src/calternal-app.css`, `apps/web/src/lib/a11y/inputModality.ts`, `apps/web/src/lib/a11y/inputModality.test.ts`, `apps/web/src/lib/a11y/focusTrap.ts`, `apps/web/src/lib/notes/NoteEditorSurface.svelte`, and `apps/web/src/routes/+layout.svelte` - `packages/ui/src/tokens.css`, `packages/ui/src/components/files/FileCollection.svelte`, and `packages/ui/src/components/calendar/AttachmentDeck.svelte` - `apps/web/scripts/check-focus-tokens.mjs`, `apps/web/e2e/focus-policy-1385.mjs`, `apps/web/e2e/kbd-motion-527.mjs`, `apps/web/src/lib/themes.test.ts`, and `apps/web/package.json` - `bench/kbd-motion-527.mjs`, `contracts/perf/registry.json`, `contracts/perf/adoption-1058.json`, and `contracts/perf/exceptions.json` - `docs/DESIGN.md` and `CLAUDE.md` ## Audit The boot value is `pointer` in `apps/web/src/app.html:3`. The two runtime writers are in `apps/web/src/routes/+layout.svelte:1260` (pointer or touch) and `+layout.svelte:1265` (recognized keyboard navigation). The audit found 109 focus selectors, 51 focus ring rules, 132 DOM focus calls, and 3 `data-input` writers. The shared root suppression policy covers the focus calls. Each selector and focus call has a classification in the attached [source audit](https://git.kayg.org/attachments/91e6f60c-29ca-4c7e-b940-2691ea917ee8). ## Gates `cd apps/web && bun run check` passed. Verbatim output: ```text PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22544 scoped exceptions User browser caches use userStorage; only documented device/public-link exceptions remain. Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css. Text sizes and UI shape values use shared role tokens. Focus policy passes: 51 focus ring rules, 132 DOM focus calls and 3 data-input writers audited. UI transitions and animation options use shared motion tokens or documented exceptions. svelte-check found 0 errors and 0 warnings ``` `bunx vitest run src/lib/a11y/inputModality.test.ts src/lib/themes.test.ts --maxWorkers=2` passed: ```text Test Files 2 passed (2) Tests 90 passed (90) Start at 02:17:32 Duration 75.72s (transform 63%, tests 23%, environment 8%, import 5%) ``` `bun run test:e2e:focus-policy-1385` passed against the production build and shared server: ```text Version 1.63.0 PASS #1385 focus policy: editor typing and shortcut focus stay quiet; Tab, roving-menu and Files listbox navigation show rings. ``` `git diff --check` passed. No Rust source changed. `cargo clean` output: ```text Removed 1 file, 356B total ``` ## Performance The local profile loaded 1,000 real Files and measured 100 selection moves. Load average before the run was `28.2, 31.1, 37.91`. There is no keyboard-motion baseline in `docs/perf/baseline.json`, so these are local measurements without a regression comparison. - Average selection: p50 `10.81 ms`, p95 `59.24 ms`, browser CPU `0.78 s`, RSS `621 MB` before and `630 MB` after. - Burst of 100 moves: p50 `9.42 ms`, p95 `19.53 ms`, browser CPU `2.26 s`, peak RSS `642 MB`. - The initial owner ring and the active row both used the shared `2px` ring. The change checks one owner attribute per interaction; work does not scan the 1,000 items. The machine-readable [profile](https://git.kayg.org/attachments/26ae22f8-001e-4f2d-8af5-a5df07d00f81) and 213 actual-size screenshots are in the issue attachments. Screenshots use macOS emulation at 390, 820, and 1440 px, in light and dark themes. Current Files owner and active-row screenshots: - Phone, light: [owner](https://git.kayg.org/attachments/0880fd69-d08d-447a-a794-b8a22c2c76d3), [active row](https://git.kayg.org/attachments/4edec84c-51f9-47b3-a800-e4518fcbd3e3) - Phone, dark: [owner](https://git.kayg.org/attachments/2683a436-350f-4fca-900d-8369addb5bdf), [active row](https://git.kayg.org/attachments/d2b2cd69-ccfe-4892-8102-c9489ffdeb7e) - Tablet, light: [owner](https://git.kayg.org/attachments/88947bd9-9d65-40e3-825e-51fb12192262), [active row](https://git.kayg.org/attachments/3f21b353-9a8c-4836-8f51-1578e0b2450a) - Tablet, dark: [owner](https://git.kayg.org/attachments/5fe06572-288f-418c-bdaf-25498d2c5665), [active row](https://git.kayg.org/attachments/70d6d39d-97f2-4bc6-876e-beca766e5c71) - Desktop, light: [owner](https://git.kayg.org/attachments/43a99dea-b5cc-4cde-b352-504d7f6d9a56), [active row](https://git.kayg.org/attachments/77f753ba-f02b-4090-8b04-bcf355a771aa) - Desktop, dark: [owner](https://git.kayg.org/attachments/9dc942fd-657f-4f9e-ba71-e986b6d7e8b2), [active row](https://git.kayg.org/attachments/5a86a3fd-3c5e-40cc-bc4a-5ef2b20db589) Editor typing screenshots: [before](https://git.kayg.org/attachments/812ee307-5f13-4f4c-b0ce-ba69231b2f22), current [Chromium](https://git.kayg.org/attachments/de064ae0-db64-448c-9c71-d3be1054ec5e), and current [WebKit](https://git.kayg.org/attachments/d1547f15-44ae-43f2-9ca0-a8b774724d05). Shortcut focus screenshots: [Cmd+K opens quietly](https://git.kayg.org/attachments/c4faf42c-2c50-4232-a03a-d7497c6c4083), then the [first Tab shows a ring](https://git.kayg.org/attachments/b0f5f14c-f561-4fbf-a234-4f2cd47cb048). ## UX gaps closed - Note typing, Enter, Backspace, caret arrows, Home/End, PageUp/PageDown, Cmd+B, Cmd+I, slash menu, Escape, Cmd+K, and the Settings shortcut stay ring-free in Chromium and WebKit. - Tab and Shift+Tab, roving-menu arrows, the first Tab after opening Search or Settings, and Files owner-to-active-row navigation show the shared ring. - Pointer focus and code-moved focus do not leave a ring. Keyboard Tab access remains available. ## UX gaps left - A separate existing motion check still reports the Search overlay duration as keyboard `[200]` and pointer `[200, 240]`. I kept its expectation unchanged and recorded this finding in an earlier issue comment; this focus-policy change does not resolve that motion mismatch. - The full web suite was not run in this job. The merge round must run `cd apps/web && bun run test` on the combined branch. ## Decision `DESIGN.md` §34 did not define the focus handoff for a listbox that keeps DOM focus on its owner. The Files listbox keeps its owner ring until an active option exists, then gives the ring to that option. This keeps Tab focus visible before the first arrow and avoids two rings after selection.
Author
Owner

Additional Calendar evidence from bug bash #1394, macOS-emulated Chromium on the real production bundle at 1440 px, light theme:

  1. Open Calendar Week and click the Day radio. The click leaves no :focus-visible target.
  2. Press 2 for the Week view shortcut. The route changes to Week, but the previously clicked Day radio matches :focus-visible and renders outline: rgb(61, 90, 196) solid 2px without Tab or arrow navigation.
  3. In Week at 390 px, click the real +1 overflow chip. Its phone sheet opens with the seeded Log. Press Escape; it closes and restores focus to the chip, which also shows the blue outline in the capture.

The screenshot artifacts/bash3-calendar/calendar-shortcuts-after-keyboard-shortcut-mac.png shows the Day radio ring after the shortcut. The 390 px sheet capture is artifacts/bash3-calendar/calendar-shortcuts-390-light-overflow-mac.png. The direct focus measurement found one focused radio after pressing 2; pointer click had zero :focus-visible targets. This is the shared input-modality/focus behavior already tracked by #1385 and the Quick Look reproduction in #1406, so I did not edit packages/ui in Calendar work.

Additional Calendar evidence from bug bash #1394, macOS-emulated Chromium on the real production bundle at 1440 px, light theme: 1. Open Calendar Week and click the Day radio. The click leaves no `:focus-visible` target. 2. Press `2` for the Week view shortcut. The route changes to Week, but the previously clicked Day radio matches `:focus-visible` and renders `outline: rgb(61, 90, 196) solid 2px` without Tab or arrow navigation. 3. In Week at 390 px, click the real `+1` overflow chip. Its phone sheet opens with the seeded Log. Press Escape; it closes and restores focus to the chip, which also shows the blue outline in the capture. The screenshot `artifacts/bash3-calendar/calendar-shortcuts-after-keyboard-shortcut-mac.png` shows the Day radio ring after the shortcut. The 390 px sheet capture is `artifacts/bash3-calendar/calendar-shortcuts-390-light-overflow-mac.png`. The direct focus measurement found one focused radio after pressing `2`; pointer click had zero `:focus-visible` targets. This is the shared input-modality/focus behavior already tracked by #1385 and the Quick Look reproduction in #1406, so I did not edit `packages/ui` in Calendar work.
Author
Owner

slop2 continuation start: branch job/slop-1385 head 84f97bf57. Items: editor ring guard extensions, button size/alignment guard, #1448 note heading l ring, Calendar toast outline, Search Actions highlight fill.

slop2 continuation start: branch job/slop-1385 head 84f97bf57. Items: editor ring guard extensions, button size/alignment guard, #1448 note heading l ring, Calendar toast outline, Search Actions highlight fill.
Author
Owner

slop2 continuation finished. Head 9477efbaa on job/slop-1385 (origin/dev merged).
Fixes: Menu active row now the shared tinted fill (Search Actions menu and every menu); Task lane Add task pill moved out of the scrolling lane (was clipped and squeezed to 19 px); Tasks layout switcher drops its compact override; Files header sort buttons and rows use shared height/radius tokens.
No code change needed (guarded, passes in Chromium and WebKit): #1448 heading + l, Shift+Arrow, Cmd+Z/Shift+Z, Cmd+A, paste, format toolbar, Quick Look opened with Space. Calendar toast outline is the shared countdown ring (1px accent stroke, all finite toasts), not a focus ring: outline none, activeElement body.
UI metrics guard violations: before 12 distinct (42 hits), after 0.
Screenshots in artifacts/ui-metrics-1385/{before,after}, artifacts/toast-outline-1385, artifacts/slop-1385 (worktree, not committed).

slop2 continuation finished. Head 9477efbaa on job/slop-1385 (origin/dev merged). Fixes: Menu active row now the shared tinted fill (Search Actions menu and every menu); Task lane Add task pill moved out of the scrolling lane (was clipped and squeezed to 19 px); Tasks layout switcher drops its compact override; Files header sort buttons and rows use shared height/radius tokens. No code change needed (guarded, passes in Chromium and WebKit): #1448 heading + l, Shift+Arrow, Cmd+Z/Shift+Z, Cmd+A, paste, format toolbar, Quick Look opened with Space. Calendar toast outline is the shared countdown ring (1px accent stroke, all finite toasts), not a focus ring: outline none, activeElement body. UI metrics guard violations: before 12 distinct (42 hits), after 0. Screenshots in artifacts/ui-metrics-1385/{before,after}, artifacts/toast-outline-1385, artifacts/slop-1385 (worktree, not committed).
kayg referenced this issue from a commit 2026-10-08 10:41:40 +00:00
kayg referenced this issue from a commit 2026-10-08 10:41:40 +00:00
Author
Owner

Deployed to calternal.cloud in merge round 35a (build cc1af3c12, live 2026-10-09 00:12 IST). Closing.

Deployed to calternal.cloud in merge round 35a (build cc1af3c12, live 2026-10-09 00:12 IST). Closing.
kayg closed this issue 2026-10-08 18:41:41 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
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-v0#1385
No description provided.