fix(web,ui): after a key nothing moves again, named per file (train #1056) #1061

Closed
kayg wants to merge 0 commits from fix/instant-keys-1056 into dev
Owner

Fixes the reds of today's train (#1056): tree.spec.ts:299 (10/10, all four main projects) and keys-no-motion.spec.ts:53 (webkit-1440, 3/10). Base dev 4274dafb4.

Cause (bisected)

843c2aac9 (#1044, merged as fe2efbf11) replaced the whole-page :root[data-last-input="keyboard"] * rule with per-element data-instant, but only six components asked. Every other transition a key can set off animated again.

  • tree.spec.ts:299 + keys-no-motion.spec.ts:53 with --repeat-each=3 at 843c2aac9: tree fails 3/3 (chromium-1440 and webkit-1440); at its parent 7c50123c3: 15/15 pass.
  • Tree: tree.css gates its fade on :root:not([data-input="keyboard"]). data-input is the focus-ring signal, which arrows inside the note never set, so only the old * rule stopped the fade.
  • Settings capsule: data-instant sits on .selector, but the transition is on its child .capsule.

Fix

  • tree.css: the fade also needs :not([data-last-input="keyboard"]), so it snaps after any acting key.
  • Audit: every transition in apps/web/src, packages/ui/src, packages/editor/src. That's 72 files; 66 declare a moving transition, giving 143 selectors. Each is now named under :root[data-last-input="keyboard"] (with ::before/::after only where the transition is on one). A key silences exactly what the old rule did, and a flip of the attribute restyles only these elements, never every element.
    • Plain .css files name their own rules next to their transitions: base, chrome, bar, sheet, tooltip, tree, cm, editor, code-block, cover, links, note-bars, note-bars-lazy, panes-motion, press.
    • Svelte components keep their <style> exactly as on dev. Their rules live in apps/web/src/styles/keys-still.css, and Tasks' rules in tasks/tasks.css (lazy). Reason: a component's scoping class is a hash of its CSS, so editing 52 <style> blocks renamed classes all through the first-paint JS. That was +0.46 KB brotli of new hash strings for byte-identical code.
    • Two bare tag selectors get their parent class (.board section, .idx span). Elsewhere, class selectors can match same-named classes in other components; those elements were zeroed by the old rule too.
    • The full per-file, per-selector list is in the comment below.
  • New guard, keys-no-motion.spec.ts: after a key, nothing visible may keep a transition (duration or delay, element or pseudo-element) on the note screen (tree, list, bars, panes), Settings, the palette, the Style and Info sheets, Tasks and Contacts. On dev it fails all seven, listing 33 moving elements on the note screen alone.
  • The flip guard ("a flip changes no element that did not ask") now reads which elements asked from the stylesheets: any non-universal rule under the keyboard attribute. Before, it used a hand list. The no-universal-rule test is unchanged. All other existing tests are unchanged.

Evidence follows as a comment.

🤖 Generated with Claude Code

Fixes the reds of today's train (#1056): `tree.spec.ts:299` (10/10, all four main projects) and `keys-no-motion.spec.ts:53` (webkit-1440, 3/10). Base `dev` `4274dafb4`. ## Cause (bisected) `843c2aac9` (#1044, merged as `fe2efbf11`) replaced the whole-page `:root[data-last-input="keyboard"] *` rule with per-element `data-instant`, but only six components asked. Every other transition a key can set off animated again. - `tree.spec.ts:299` + `keys-no-motion.spec.ts:53` with `--repeat-each=3` at `843c2aac9`: tree fails 3/3 (chromium-1440 and webkit-1440); at its parent `7c50123c3`: 15/15 pass. - Tree: `tree.css` gates its fade on `:root:not([data-input="keyboard"])`. `data-input` is the focus-ring signal, which arrows inside the note never set, so only the old `*` rule stopped the fade. - Settings capsule: `data-instant` sits on `.selector`, but the transition is on its child `.capsule`. ## Fix - `tree.css`: the fade also needs `:not([data-last-input="keyboard"])`, so it snaps after any acting key. - **Audit: every transition in `apps/web/src`, `packages/ui/src`, `packages/editor/src`.** That's 72 files; 66 declare a moving transition, giving 143 selectors. Each is now named under `:root[data-last-input="keyboard"]` (with `::before`/`::after` only where the transition is on one). A key silences exactly what the old rule did, and a flip of the attribute restyles only these elements, never every element. - Plain `.css` files name their own rules next to their transitions: base, chrome, bar, sheet, tooltip, tree, cm, editor, code-block, cover, links, note-bars, note-bars-lazy, panes-motion, press. - Svelte components keep their `<style>` exactly as on dev. Their rules live in `apps/web/src/styles/keys-still.css`, and Tasks' rules in `tasks/tasks.css` (lazy). Reason: a component's scoping class is a hash of its CSS, so editing 52 `<style>` blocks renamed classes all through the first-paint JS. That was +0.46 KB brotli of new hash strings for byte-identical code. - Two bare tag selectors get their parent class (`.board section`, `.idx span`). Elsewhere, class selectors can match same-named classes in other components; those elements were zeroed by the old rule too. - The full per-file, per-selector list is in the comment below. - **New guard, `keys-no-motion.spec.ts`:** after a key, nothing visible may keep a transition (duration or delay, element or pseudo-element) on the note screen (tree, list, bars, panes), Settings, the palette, the Style and Info sheets, Tasks and Contacts. On dev it fails all seven, listing 33 moving elements on the note screen alone. - **The flip guard** ("a flip changes no element that did not ask") now reads which elements asked from the stylesheets: any non-universal rule under the keyboard attribute. Before, it used a hand list. The no-universal-rule test is unchanged. All other existing tests are unchanged. Evidence follows as a comment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The whole-page rule zeroed every transition after a key; per element, the tree's lit branch and
the Settings tab capsule were missed and only the train caught them. The sweep checks the same
promise element by element (and ::before/::after) on the note screen, Settings, the palette, the
Style and Info sheets, Tasks and Contacts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1044 replaced the whole-page `:root[data-last-input=keyboard] *` rule with per-element
`data-instant`, but only six components asked, so every other transition a key can set off
animated again: the tree's lit branch (tree:299, 10/10 red) and the Settings tab capsule
(keys-no-motion:53) on the train, and, as the new sweep shows, the panes, bars, editor tasks,
tags, scroller, backdrop, list rows, sidebar chevrons and more.

Each of the 66 files that declares a transition now names its own transitioning selectors
(143) under :root[data-last-input=keyboard] (pseudo-elements only where the transition is on
one), so a key silences exactly what the old rule did while a flip restyles only those
elements, never every element. tree.css gates its fade on both signals. The flip guard reads
which elements asked from the stylesheets instead of a hand list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Svelte component's scoping class is a hash of its CSS, so naming each component's transitions
inside its own <style> renamed classes all through the first-paint JS (identical code, +0.46 KB
brotli of new hash strings). styles/keys-still.css names the same 52 components' transitions
(plain class selectors; Board and IndexStrip get their parent class) and their <style> blocks are
as on dev. Plain .css files keep their rule next to their transitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(web): Contacts' keyboard rules stay in the global sheet (its lazy chunk keeps its size)
Some checks are pending
CI / server (pull_request) Waiting to run
CI / rust (pull_request) Waiting to run
CI / web (pull_request) Waiting to run
Markdown extraction contract / markdown-native-contract (pull_request) Waiting to run
Markdown extraction contract / markdown-editor-contract (pull_request) Waiting to run
4318101d10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Gates (dev-01, x86, --workers=1; server built from dev 4274dafb4's crates), head 4318101d1

gate result
tree.spec.ts:299 + keys-no-motion.spec.ts (16 tests incl. the 7 new sweeps) --repeat-each=10 chromium-1440 160/160, chromium-390 10/10 (tree; the keys file is wide-only), webkit-1440 160/160, webkit-390 10/10: 0 failures
contrast project 10 passed, 0 failed
svelte-check (at 05bc492cb, before the CSS moved; components are now as on dev) 0 errors; warnings equal to dev's
vitest web / ui 238/238, 96/96
bisect 843c2aac9: tree fails 3/3 (chromium and webkit 1440); parent 7c50123c3: 15/15 pass

One environment note: an intermediate head (3c450a930) had chromium-390 tree:299 fail 10/10 with "Target crashed", while WebKit and other lanes were loading the host. The final head reran 3/3 and then 10/10 green, and 05bc492cb 3/3 green.

Keyboard-arrow frames (rAF deltas; 30 arrow presses, 10 runs per arm, ABAB, chromium-1440)

arm rAF p50 (median of runs) p95 (median of runs)
dev 4274dafb4 166.7 ms 275 ms
fix 166.7 ms 283 ms

Same p50. The p95 values overlap: dev 183–517 ms, fix 167–350 ms. These are headless frames on a GPU-less build host, so they show the arrow walk is not slower, not real frame times. One fix run is missing from the 10 (the probe run that overlapped the host crash).

Bundle gate (perf/web.sh --bundle-only, budgets.json untouched)

dev 4274dafb4 fix 4318101d1 delta
first-screen JS, raw 783.610 KB 783.610 KB 0
first-screen JS, brotli (chunk.index) 222.546 222.477 −0.069 (line 222.71: ok)
first-screen CSS, brotli 17.821 18.417 +0.596 (line 22.1: ok)
first-screen CSS, raw 99.313 104.949 +5.636
chunk.ContactsScreen, brotli 14.132 14.152 +0.020: over its 14.14 line by 12 B
tasks_css, brotli 3.645 3.723 +0.078

Not bumped, needs kayg:

  1. First-screen CSS +0.596 KB brotli. This is what naming the 143 transitions costs; the old * rule was one line. It is the price of restoring "keys never animate" without the whole-page restyle (40–60 ms per flip in Safari, #1031).
  2. ContactsScreen +12 B over its line. Its code is byte-identical: with asset and Svelte hashes normalised, both builds are 44,431 B raw and 14,140 B brotli. The lazy chunk imports the index chunk by its hashed file name, and any first-paint CSS change renames it. This can't be avoided while (1) changes first-paint CSS at all.

The earlier version put the rules inside each component. That made the first-screen JS +0.46 KB brotli of renamed classes with identical code, so the rules moved to one global sheet. A budget bump I had committed for that is reverted.

## Gates (dev-01, x86, `--workers=1`; server built from dev `4274dafb4`'s crates), head `4318101d1` | gate | result | |---|---| | `tree.spec.ts:299` + `keys-no-motion.spec.ts` (16 tests incl. the 7 new sweeps) `--repeat-each=10` | chromium-1440 **160/160**, chromium-390 **10/10** (tree; the keys file is wide-only), webkit-1440 **160/160**, webkit-390 **10/10**: 0 failures | | contrast project | **10 passed**, 0 failed | | svelte-check (at `05bc492cb`, before the CSS moved; components are now as on dev) | 0 errors; warnings equal to dev's | | vitest web / ui | 238/238, 96/96 | | bisect | `843c2aac9`: tree fails 3/3 (chromium and webkit 1440); parent `7c50123c3`: 15/15 pass | One environment note: an intermediate head (`3c450a930`) had chromium-390 tree:299 fail 10/10 with "Target crashed", while WebKit and other lanes were loading the host. The final head reran 3/3 and then 10/10 green, and `05bc492cb` 3/3 green. ### Keyboard-arrow frames (rAF deltas; 30 arrow presses, 10 runs per arm, ABAB, chromium-1440) | arm | rAF p50 (median of runs) | p95 (median of runs) | |---|---|---| | dev `4274dafb4` | 166.7 ms | 275 ms | | fix | 166.7 ms | 283 ms | Same p50. The p95 values overlap: dev 183–517 ms, fix 167–350 ms. These are headless frames on a GPU-less build host, so they show the arrow walk is not slower, not real frame times. One fix run is missing from the 10 (the probe run that overlapped the host crash). ### Bundle gate (`perf/web.sh --bundle-only`, `budgets.json` untouched) | | dev `4274dafb4` | fix `4318101d1` | delta | |---|---|---|---| | first-screen JS, raw | 783.610 KB | 783.610 KB | **0** | | first-screen JS, brotli (`chunk.index`) | 222.546 | 222.477 | −0.069 (line 222.71: ok) | | first-screen CSS, brotli | 17.821 | 18.417 | **+0.596** (line 22.1: ok) | | first-screen CSS, raw | 99.313 | 104.949 | +5.636 | | `chunk.ContactsScreen`, brotli | 14.132 | 14.152 | **+0.020: over its 14.14 line by 12 B** | | `tasks_css`, brotli | 3.645 | 3.723 | +0.078 | **Not bumped, needs kayg:** 1. **First-screen CSS +0.596 KB brotli.** This is what naming the 143 transitions costs; the old `*` rule was one line. It is the price of restoring "keys never animate" without the whole-page restyle (40–60 ms per flip in Safari, #1031). 2. **`ContactsScreen` +12 B over its line.** Its code is byte-identical: with asset and Svelte hashes normalised, both builds are 44,431 B raw and 14,140 B brotli. The lazy chunk imports the index chunk by its hashed file name, and any first-paint CSS change renames it. This can't be avoided while (1) changes first-paint CSS at all. The earlier version put the rules inside each component. That made the first-screen JS +0.46 KB brotli of renamed classes with identical code, so the rules moved to one global sheet. A budget bump I had committed for that is reverted.
Author
Owner

Audit: every transition the old * rule covered, now named (file → selectors)

  • web/chrome/ListDock.svelte: .dock, .dock.away
  • web/chrome/PageToolbar.svelte: :global(.pt), :global(.pt)::after, :global(.pt.tb .gb), .check
  • web/chrome/PhoneFoot.svelte: .tab, .thumb
  • web/chrome/ShareSheet.svelte: :global([data-menu-open] > *)
  • web/chrome/TopPills.svelte: .tp :global(.pill.back), .face, .line, .line.springy, .line :global(.float-glass), .words
  • web/components/ArtStack.svelte: .layer
  • web/components/Backdrop.svelte: .surface:global([data-linked]), .backdrop, .backdrop::after, .baked .scene, .baked .grain, .soft, .floor, .scrim, .picture
  • web/components/BackdropCredit.svelte: .credit
  • web/components/BackdropPicker.svelte: .x
  • web/components/InfoSheet.svelte: .backlinks .item
  • web/components/InlinePicker.svelte: .lk-picker.settling
  • web/components/NotePreview.svelte: :global(.dockbox .dock)
  • web/components/PhoneStack.svelte: .pills.gone, .edge, .edge.off
  • web/components/PropRow.svelte: .v, .text, .badge
  • web/components/Settings.svelte: .accent .dot
  • web/components/Shell.svelte: .shell, .frame-fill, .frame-shadow, .viewport, .note-swap, .pane, .focus .pane, .focus .resizer, .side-hidden .side-pane
  • web/components/Sidebar.svelte: .chev, ul.dragging li, .row
  • web/components/SignIn.svelte: .frost, .card, .go
  • web/components/appearance/BackdropGrid.svelte: .tile
  • web/components/appearance/TickSlider.svelte: .val, .chev, .turn
  • web/components/settings/CopyLink.svelte: :global(.cl-text)::after, .copylink
  • web/components/style/BlockStyle.svelte: .sw
  • web/components/style/Swatches.svelte: .ring.glide
  • web/composer/KindLine.svelte: .rest, .kmenu > :is(:global(.kglass), .kopts)
  • web/composer/PullHint.svelte: .hint, .icon
  • web/contacts/ContactRow.svelte: .row
  • web/contacts/FamilyTree.svelte: .node.node, .pic, .node .pic
  • web/contacts/IndexStrip.svelte: span
  • web/contacts/MergeSheet.svelte: .opt, .tick
  • web/press/press.css: [data-press-src]
  • web/rows/BulkBar.svelte: .bulk, .bulk.away, .act
  • web/rows/RowActions.svelte: .ra-act, .ra-act.outer::before
  • web/styles/cm.css: .page .cm-scroller, .page .cm-paper .cm-sheet, .page[data-springing] .cm-paper .cm-sheet, .page .cm-task, .page .cm-task::before, .page .cm-task-tick, .page .cm-task-dash, .page .cm-task-done .cm-task-tick, .page .cm-task-cancelled .cm-task-dash, .page .cm-editor[data-task-press] .cm-li-task, .page .tag-chip, .page .tag-x, .page .tag-add
  • web/styles/code-block.css: .page .cm-code-lang
  • web/styles/cover.css: .cover.ready .cover-img, .cover-tools
  • web/styles/editor.css: .page .paper
  • web/styles/links.css: .page .cm-link:hover
  • web/styles/note-bars-lazy.css: .nb-act, .nb-strip
  • web/styles/note-bars.css: .nb-btn
  • web/styles/panes-motion.css: .stack > .screen, .stack > .screen[data-place="front"]
  • web/tasks/Board.svelte: section
  • web/tasks/DragCard.svelte: .card
  • web/tasks/DragRow.svelte: .dr, .grip
  • web/tasks/Props.svelte: .prop
  • web/tasks/TaskList.svelte: .grp
  • web/tasks/when/Grow.svelte: .grow
  • web/tasks/when/Sentence.svelte: .w
  • web/tasks/when/Suggest.svelte: .c
  • web/tasks/when/WhenField.svelte: .field
  • web/tasks/when/WordMenu.svelte: .o
  • ui/components/ColorPicker.svelte: .sw
  • ui/components/ContextMenu.svelte: :global([data-menu-open] > *)
  • ui/components/EmptyState.svelte: .act
  • ui/components/Morph.svelte: .morph.ready, .morph-halo.ready, .morph.sheet.ready, :global(.morph-glass), .morph-clip, .morph-halo i
  • ui/components/Pill.svelte: .pill
  • ui/primitives/check/SpringCheckBox.svelte: .sc-box, .sc-fill, .sc-tick, .on .sc-tick
  • ui/primitives/selector/Selector.svelte: .capsule, .capsule.reduced, .opt
  • ui/primitives/switch/SquishSwitch.svelte: .squish
  • ui/primitives/toast/ToastCapsule.svelte: .inner
  • ui/primitives/toast/ToastRow.svelte: .act
  • ui/primitives/toast/Toasts.svelte: .item
  • ui/styles/bar.css: .bar-wrap, .bar-wrap.phone
  • ui/styles/base.css: .press, .bounce
  • ui/styles/chrome.css: .ib, .seg button
  • ui/styles/sheet.css: .lifted-sheet
  • ui/styles/tooltip.css: .warm-tip, .warm-tip[data-motion="travel"]
  • ui/styles/tree.css: .tree, .tree-lit, .tree .cm-bullet::before, .tree-lit .cm-bullet::before, .tree-grow::after (fade gated on both keyboard signals)

Already keyboard-aware and left as they were: contacts/contacts.css (named rule), Backdrop.svelte .shade (:not([data-last-input])), TickSlider .turn (data-input), Shell/bar/editor .instant and .resizing rules, App.svelte .no-transitions *. Dead selectors not copied: Settings.svelte .toggle input (no such element since SquishSwitch).

## Audit: every transition the old `*` rule covered, now named (file → selectors) - `web/chrome/ListDock.svelte`: `.dock`, `.dock.away` - `web/chrome/PageToolbar.svelte`: `:global(.pt)`, `:global(.pt)::after`, `:global(.pt.tb .gb)`, `.check` - `web/chrome/PhoneFoot.svelte`: `.tab`, `.thumb` - `web/chrome/ShareSheet.svelte`: `:global([data-menu-open] > *)` - `web/chrome/TopPills.svelte`: `.tp :global(.pill.back)`, `.face`, `.line`, `.line.springy`, `.line :global(.float-glass)`, `.words` - `web/components/ArtStack.svelte`: `.layer` - `web/components/Backdrop.svelte`: `.surface:global([data-linked])`, `.backdrop`, `.backdrop::after`, `.baked .scene`, `.baked .grain`, `.soft`, `.floor`, `.scrim`, `.picture` - `web/components/BackdropCredit.svelte`: `.credit` - `web/components/BackdropPicker.svelte`: `.x` - `web/components/InfoSheet.svelte`: `.backlinks .item` - `web/components/InlinePicker.svelte`: `.lk-picker.settling` - `web/components/NotePreview.svelte`: `:global(.dockbox .dock)` - `web/components/PhoneStack.svelte`: `.pills.gone`, `.edge`, `.edge.off` - `web/components/PropRow.svelte`: `.v`, `.text`, `.badge` - `web/components/Settings.svelte`: `.accent .dot` - `web/components/Shell.svelte`: `.shell`, `.frame-fill`, `.frame-shadow`, `.viewport`, `.note-swap`, `.pane`, `.focus .pane`, `.focus .resizer`, `.side-hidden .side-pane` - `web/components/Sidebar.svelte`: `.chev`, `ul.dragging li`, `.row` - `web/components/SignIn.svelte`: `.frost`, `.card`, `.go` - `web/components/appearance/BackdropGrid.svelte`: `.tile` - `web/components/appearance/TickSlider.svelte`: `.val`, `.chev`, `.turn` - `web/components/settings/CopyLink.svelte`: `:global(.cl-text)::after`, `.copylink` - `web/components/style/BlockStyle.svelte`: `.sw` - `web/components/style/Swatches.svelte`: `.ring.glide` - `web/composer/KindLine.svelte`: `.rest`, `.kmenu > :is(:global(.kglass), .kopts)` - `web/composer/PullHint.svelte`: `.hint`, `.icon` - `web/contacts/ContactRow.svelte`: `.row` - `web/contacts/FamilyTree.svelte`: `.node.node`, `.pic`, `.node .pic` - `web/contacts/IndexStrip.svelte`: `span` - `web/contacts/MergeSheet.svelte`: `.opt`, `.tick` - `web/press/press.css`: `[data-press-src]` - `web/rows/BulkBar.svelte`: `.bulk`, `.bulk.away`, `.act` - `web/rows/RowActions.svelte`: `.ra-act`, `.ra-act.outer::before` - `web/styles/cm.css`: `.page .cm-scroller`, `.page .cm-paper .cm-sheet`, `.page[data-springing] .cm-paper .cm-sheet`, `.page .cm-task`, `.page .cm-task::before`, `.page .cm-task-tick`, `.page .cm-task-dash`, `.page .cm-task-done .cm-task-tick`, `.page .cm-task-cancelled .cm-task-dash`, `.page .cm-editor[data-task-press] .cm-li-task`, `.page .tag-chip`, `.page .tag-x`, `.page .tag-add` - `web/styles/code-block.css`: `.page .cm-code-lang` - `web/styles/cover.css`: `.cover.ready .cover-img`, `.cover-tools` - `web/styles/editor.css`: `.page .paper` - `web/styles/links.css`: `.page .cm-link:hover` - `web/styles/note-bars-lazy.css`: `.nb-act`, `.nb-strip` - `web/styles/note-bars.css`: `.nb-btn` - `web/styles/panes-motion.css`: `.stack > .screen`, `.stack > .screen[data-place="front"]` - `web/tasks/Board.svelte`: `section` - `web/tasks/DragCard.svelte`: `.card` - `web/tasks/DragRow.svelte`: `.dr`, `.grip` - `web/tasks/Props.svelte`: `.prop` - `web/tasks/TaskList.svelte`: `.grp` - `web/tasks/when/Grow.svelte`: `.grow` - `web/tasks/when/Sentence.svelte`: `.w` - `web/tasks/when/Suggest.svelte`: `.c` - `web/tasks/when/WhenField.svelte`: `.field` - `web/tasks/when/WordMenu.svelte`: `.o` - `ui/components/ColorPicker.svelte`: `.sw` - `ui/components/ContextMenu.svelte`: `:global([data-menu-open] > *)` - `ui/components/EmptyState.svelte`: `.act` - `ui/components/Morph.svelte`: `.morph.ready`, `.morph-halo.ready`, `.morph.sheet.ready`, `:global(.morph-glass)`, `.morph-clip`, `.morph-halo i` - `ui/components/Pill.svelte`: `.pill` - `ui/primitives/check/SpringCheckBox.svelte`: `.sc-box`, `.sc-fill`, `.sc-tick`, `.on .sc-tick` - `ui/primitives/selector/Selector.svelte`: `.capsule`, `.capsule.reduced`, `.opt` - `ui/primitives/switch/SquishSwitch.svelte`: `.squish` - `ui/primitives/toast/ToastCapsule.svelte`: `.inner` - `ui/primitives/toast/ToastRow.svelte`: `.act` - `ui/primitives/toast/Toasts.svelte`: `.item` - `ui/styles/bar.css`: `.bar-wrap`, `.bar-wrap.phone` - `ui/styles/base.css`: `.press`, `.bounce` - `ui/styles/chrome.css`: `.ib`, `.seg button` - `ui/styles/sheet.css`: `.lifted-sheet` - `ui/styles/tooltip.css`: `.warm-tip`, `.warm-tip[data-motion="travel"]` - `ui/styles/tree.css`: `.tree`, `.tree-lit`, `.tree .cm-bullet::before`, `.tree-lit .cm-bullet::before`, `.tree-grow::after` (fade gated on both keyboard signals) Already keyboard-aware and left as they were: `contacts/contacts.css` (named rule), `Backdrop.svelte` `.shade` (`:not([data-last-input])`), `TickSlider` `.turn` (`data-input`), Shell/bar/editor `.instant` and `.resizing` rules, `App.svelte` `.no-transitions *`. Dead selectors not copied: `Settings.svelte` `.toggle input` (no such element since SquishSwitch).
Author
Owner

Merged into dev (66867c24b). kayg OK on CSS +0.596 KB brotli and the Contacts +12 B hash artefact (11-10). Closing.

Merged into dev (66867c24b). kayg OK on CSS +0.596 KB brotli and the Contacts +12 B hash artefact (11-10). Closing.
kayg closed this pull request 2026-11-10 15:51:42 +00:00
Some checks are pending
CI / server (pull_request) Waiting to run
CI / rust (pull_request) Waiting to run
CI / web (pull_request) Waiting to run
Markdown extraction contract / markdown-native-contract (pull_request) Waiting to run
Markdown extraction contract / markdown-editor-contract (pull_request) Waiting to run

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!1061
No description provided.