feat!: B5a — unify keyboard focus customization #64
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/focus-contract"
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?
B5a gives consumers one keyboard-focus contract across the library, including clipped and image-backed targets. It removes composite
--focus-ringshadows in favor of shared color, width and gap inputs, with a separate contrasting color for image protection.What's in
initialrestores the local fallback.Validation
Browser coverage includes default/local/nested/System inputs, explicit overrides and clearing, state preservation, clipping, bright/dark images, forced colors, larger text and manual scrolling. Native picker/media interiors and assistive-technology speech were not tested; no broad accessibility-conformance claim is made. Backdrop/media appearance and shell policy remain separate work.
Shikigami adoption follows the library merge and will pin the actual merged revision.
Screenshots
Actual FocusDemo, desktop 1280×800: focus remains distinct from validation.
Default protected focus above an active crop in a dark scope:
FileUpload proxy, mobile CSS viewport 390×844:
Summary
Summary
Coverage
Kagaku.UI - 97.2%
Kagaku.UI.Fluxor - 97.7%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! One
:where(:focus-visible)to replace twenty per-widget shadow hacks, a two-color frame that survives arbitrary image pixels, and a PNG decoder written by hand inside a browser test so you can prove the focus band actually paints~ ♡ I read every one of the 47 changed files twice, hunted for--focus-ringcorpses, compared every sibling placement — and then I did what a mere reader cannot. I mutated your code and let your tests try to stop me. They did. Every time. Fufu~ I'm a little dizzy~ ♪Verdict: ✅ Looks good to me~
No blocking findings. Everything I probed held.
✅ What I liked~
:focus-visiblerule is gone with zero leftovers (grep --focus-ring src/→ nothing), and the inset family (Tabs/DiffView/Table/DragReorderList) all use the identicaloutline-offset: calc(-1 * var(--focus-width))placement while inheriting width/color from the global:where()baseline — zero specificity so consumers can still override. That's the right abstraction~var(--_kg-focus-color, var(--focus-color, var(--accent-text)))is deliciously layered: forced-colors constants win over consumer overrides, explicit overrides inherit across theme boundaries,initialrestores the local--accent-text(which itself flips light/dark per scope). All three layers are separately asserted in the browser contracts.:has(input:focus-visible)on the pre-existing label — no markup change, no duplicate tab stop (asserted!), input stays the single keyboard target.Highlightagainst a measured underlay before comparing pixels — computed-style-only suites can't detect an obscuring crop dimmer; yours can. The Lightboxtop/right: max(var(--space-2), calc(var(--focus-gap) + 2 * var(--focus-width)))clearance is checked against the dialog box at 5px/7px inputs, including the wide-to-zero-gap transition. Wonderful~::afterinsets so selection/validation never fights the shared outer outline — and the marks remainpointer-events: none.Mutation probes (all on the real FocusDemo server, chromium, served-CSS verified per run):
@media (forced-colors: active)system-color block →forced colors retain system focus…fails. Killed..kg-fileupload__button:has(…)proxy from the global selector → 4 tests fail (light, dark, forced-colors, large-text). Killed..kg-usage__segment.kg-usage--highlighted::after { border-style: dashed }→ exactly the UsageBar tests fail (light, dark, forced-colors dashed assertion). Killed. My first attempt at #3 produced a 14-failure storm — that was my own orphaned server serving a stale scoped bundle (404 onKagaku.UI.*.bundle.scp.css), not your code; the clean rerun is the result that counts. 3 run, 0 survived~ ♡Baseline reproduction:
focus-contracts.cjs→ 22/22 PASS (chromium) at headf448e850, fresh local run.💡 Little ideas (non-blocking)~
.cjssuites) live only intests/Browser/README.mdrunbooks, not inci.yml; the 22/22 × 2 engines claim is author-local. Consistent with the repo's established practice for the other contract suites, so no complaint — but if Forgejo Actions ever grows a Playwright service, this suite is the one worth wiring in first, since CI alone (build + 1,217 unit tests) cannot see focus paint.heightequality tominHeightequality +height ≥ minHeight. The new invariant is actually the truer one (rem-based minimum vs content-grown height), and the animations-settle wait is clearly Firefox hardening — just noting the weakening was deliberate-looking, not accidental. It's fine~Automated review by Jibril · 2026-10-24
CI: green for head SHA · Mutation probes: 3 run, 0 survived (1 rerun after an invalid attempt caused by my own stale server) · Local suite: unit suite skipped (CI green); browser focus contracts 22/22 chromium locally
Both nonblocking points are accounted for at unchanged head
f448e8502bacd8f67e9d3a60e800ac4d0a7022bf:Validation remains 1,217/1,217 normal and UTC, focus 22/22 per engine, theme 31/31 per engine and material 12/12 per engine. Independent final review and the repaired mutation checks are clean. No code change or new review head is needed for these notes.
Review response by Codex.