6.1 KiB
Continuation — Floating-gap canon + menu focus-ring (2026-06-28)
Where we left off. Spun out of the eidos inherit/radius audit; converged on canonizing the trigger→panel gap of menus and killing the keyboard focus-ring that framed the whole menu panel.
✅ RESOLVED 2026-06-29
The canon-read fix WORKS — verified live: menubar (no sideOffset) and
dropdown show the 4px canonical gap on real interaction (the $derived
returns 4 → Floating UI offset 4). The keyboard focus-ring fix was already live.
Menu demos (dropdown / context / select) cleaned to follow the canon — commit
9c48164f. The canon-read + token are in commit 61ea97b5.
Minor known artifact (demo-only, NOT a real-world bug): the dropdown demo
opens on mount (open: on), so Floating UI positions the panel against the
trigger's TRANSIENT initial-layout rect and doesn't re-measure on the settle
(autoUpdate watches scroll/resize, not arbitrary layout shifts) → it shows ~36px
until you close+reopen. Real menus open on a user click (settled layout) → 4px.
To make the demo pixel-perfect, give Floating UI a layout-shift re-measure on open.
The original (now historical) handoff follows.
✅ DONE + VERIFIED LIVE (CSS — HMR is reliable)
- Archetype
:where()systemic fix (src/uix/eidos/archetypes.css) — every DEFAULT archetype rule wrapped in:where()(specificity 0,0,0) so any component recipe (0,1,0) wins regardless of load order. Kept at full specificity ON PURPOSE: the:has([data-archetype='field-trigger'])padding override + the canonical item highlight. - Overscroll containment —
overscroll-behavior: containadded to the shared ScrollArea viewport (scroll-area.css) + 7 overlay scroll regions (context-menu, dialog, drawer, dropdown-menu, float-panel, popover, tooltip). Fixes scroll chaining to the<body>. Verified on select + dropdown. - Focus-ring fix (
archetypes.css) — the universal[data-archetype]:focus-visiblebox-shadow ring now EXCLUDESitem,option, ANDcontent:- Menu/listbox ROWS keyboard-focus via roving tabindex → drew the thick ring on
keyboard but only a subtle bg on hover (inconsistent). Now rows use the canonical
highlight for BOTH
:hoverAND:focus-visible. - Menu/popover/dialog PANELS (
data-archetype='content') take focus for key capture → drew the ring around the WHOLE float (the thick purple frame the user hated). Now excluded; the panel's own elevation (border + shadow) is the boundary. - Verified live: loaded rule tail =
…:not([item]):not([option]):not([content]); highlight selector includes:focus-visible.
- Menu/listbox ROWS keyboard-focus via roving tabindex → drew the thick ring on
keyboard but only a subtle bg on hover (inconsistent). Now rows use the canonical
highlight for BOTH
⏳ DONE but NEEDS A FRESH-LOAD CHECK (JS — the tab cached the old module)
--floating-gap-menutoken:0px → var(--space-1)(4px) inrender-css.ts+ regeneratedgenerated/base.css. Menus get a small gap now, not flush.- nav-menu wired to the canon (
navigation-menu.css) — both orientations nowcalc(100% + var(--floating-gap, 0px))(was hardcoded--space-1). nav-menu is CSS-positioned (not portaled) so it consumes the canon through CSS. Verified 4px early, before the cache trouble — reliable. - Canon-read FIX (
src/uix/soma/layers/floating/floating.svelte.ts) — THE fix:FloatingContent.canonicalGapPxwas a$statefilled by a one-shot rAF (requestFrame) inside awatch(contentRef). For PORTALED menus that rAF callback never fired (instrumented: watch passes the guard, frame scheduled, never run, not cancelled —requestFrame/resolveWindowatarts/adom/active-dom.svelte.ts:481suspected). SocanonicalGapPxstayednull→ menus fell back tosideOffset(flush). The token change alone never reached them.- Fix:
canonicalGapPxis now a$derivedreading--floating-gapoffcontentRef.currentdirectly (reactive, no rAF). The rAF keeps only the zIndex read.--floating-gapis a style value (no layout) so reading on mount is safe. - Type-clean (svelte-check passed) but NOT visually verified — see CACHE below.
⚠️ CACHE GOTCHA — READ FIRST (it ate most of the session)
- Editing a deeply-imported
.svelte.ts(e.g.floating.svelte.ts) does NOT reliably HMR, AND the connected tab serves that module from a STRONG cache.navigateand a?nocache=query do NOT revalidate the module (it's fetched by path). - Tell-tale: a
gap: 12from a hardcode I'd reverted long ago persisted through edits + a full dev-server restart → the tab was running a frozen old module. - Detect stale: temporarily set
mainAxis: 12in the offset middleware → if the gap isn't 12, the code is fresh. - Only reliable bust: browser HARD reload (Ctrl+Shift+R). Not
navigate. - CSS (
archetypes.css, recipes) DOES hot-update fine — only the.svelte.tsmodule is the problem.
▶️ FIRST ACTIONS TOMORROW
- Hard-reload (Ctrl+Shift+R) the menubar/dropdown page.
- Confirm the canon-read fix: menubar (passes no
sideOffset) should now show a ~4px gap (no longer pegado). If still flush → re-instrument; you're guaranteed fresh. - Clean the menu demos so they follow the canon (now that the read works). These
pass explicit
sideOffsetthat BYPASSES the canon:web/routes/uix/components/dropdown-menu/+page.svelte→sideOffset = $state(4)web/routes/uix/components/context-menu/+page.svelte→sideOffset = $state(0)web/routes/uix/components/select/+page.svelte→ hardcoded<Select.Content sideOffset={6}>Default each toundefinedso the token retunes all. Slider pattern to avoid thenumber|undefinedbind error:value={sideOffset ?? 0} oninput={(e) => (sideOffset = e.currentTarget.valueAsNumber)}+ label{sideOffset === undefined ? 'auto' : sideOffset + 'px'}.
- If the canon-read fix DIDN'T take on fresh load, isolate why
requestFrame's rAF never fires for portaled menu content — but the$derivedshould sidestep it entirely.
Dev server
Restarted fresh on 5199: npm run dev -- --port 5199 --strictPort (the bare dev
script uses the default port, so pass the flags).