Non-blocking note: the focus style still does not make the disclosure-only sidebar group reachable with native keyboard navigation. In the no-link branch, sidebar_group passes data-part=“trigger” to tab_menu_vertical, which renders a plain div, so the element is not naturally tabbable and does not expose native button semantics unless JS patches it later. Since this component is a disclosure control, can we render that branch as a real instead of styling a div? That keeps the sidebar native to the web and gives Space/Enter, focus, and disabled behavior without relying on post-mount JS.
Hive
fix(noora): accessible sidebar and breadcrumbs focus + tab menu color states
GitHub issue · Closed
What changed
- Keyboard focus on sidebar tabs is now visible. A collapsible group header is two tab stops — the navigate link and the chevron toggle — but only the chevron showed the browser default ring, so tabbing onto the actual nav item gave no visible indicator. Added consistent
:focus-visibleoutlines to the group link, the chevron/trigger, and leaf items. - Tab menu color states. Text and left icon are now
surface/label/secondaryby default andsurface/label/primaryon hover/selected. Hover background moved fromsurface/background/tertiarytosurface/background/secondary. - Chevron indicator is
surface/label/tertiaryby default andsurface/label/secondaryon hover, giving the expand/collapse arrow its own (quieter) scale, distinct from the label.
Why
Tabbing through the sidebar highlighted only the chevron, not the whole tab — keyboard users couldn’t tell which nav item was focused. The color tweaks make the inactive/hover/active states read consistently across the sidebar (and the docs nav, which shares the component).
Where
- Generic text/icon coloring + hover background →
noora/css/tab_menu.css(applies to every tab menu). - Focus rings and the chevron indicator coloring →
noora/css/sidebar.css(the chevron is created by the sidebar, not the shared tab-menu component).
Validation
Verified in the browser (light/dark): focus rings appear on Tab for the nav link, chevron, and sub-items; hover/selected states transition text and arrow as expected. CSS only — no markup or component API changes.
Reviewed: ts-deepmerge 7.0.3 → 8.0.0 (CVE-2026-12644)
Verified this bump is safe and behaviorally inert for our usage. ✅
What 8.0.0 changes: Diffed the published tarballs — the only code difference is the blocked-key list in merge. 7.0.3 skips __proto__, constructor, prototype; 8.0.0 adds toString, valueOf, hasOwnProperty, isPrototypeOf, propertyIsEnumerable, toLocaleString. That’s the CVE fix (prototype-method override / DoS). The isObject guard is identical between versions.
How Scalar uses ts-deepmerge: @scalar/object-utils wraps it in one file as a thin pass-through (deepMerge → merge, default options), exposed only via the @scalar/object-utils/merge subpath. It’s the sole consumer of ts-deepmerge in the whole lockfile.
Does api-reference exercise the blocked keys? No — it never calls ts-deepmerge at all:
- Nothing in
@scalar/api-reference,@scalar/api-client, or@scalar/oas-utilsimports@scalar/object-utils/merge. They only use/nestedand/arrays. - The
standalone.jsbundle (3.9 MB) contains zero ts-deepmerge code (nots-deepmergestring, no array-guard message, noallowUndefinedOverrides). - Note: api-reference has its own local
deepMergeinhelpers/openapi.js— separate function, unrelated to ts-deepmerge.
Conclusion: The stricter key-blocking can’t break anything because the function isn’t reachable in the code path we ship. Clean transitive-CVE pin, no runtime impact.