Hive Hive
Sign in

fix(noora): accessible sidebar and breadcrumbs focus + tab menu color states

GitHub issue · Closed

Metadata
Source
tuist/tuist #11389
Updated
Jun 24, 2026
Details

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-visible outlines to the group link, the chevron/trigger, and leaf items.
  • Tab menu color states. Text and left icon are now surface/label/secondary by default and surface/label/primary on hover/selected. Hover background moved from surface/background/tertiary to surface/background/secondary.
  • Chevron indicator is surface/label/tertiary by default and surface/label/secondary on 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.

Comments
P
pepicrft Jun 19, 2026

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.

E
esnunes Jun 20, 2026

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-utils imports @scalar/object-utils/merge. They only use /nested and /arrays.
  • The standalone.js bundle (3.9 MB) contains zero ts-deepmerge code (no ts-deepmerge string, no array-guard message, no allowUndefinedOverrides).
  • Note: api-reference has its own local deepMerge in helpers/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.

TA
tuist-atlas[bot] Jun 21, 2026

The accessibility improvements for sidebar focus states and tab menu colors from this pull request are now available in noora@0.82.6. Update to this version to pick up the fix.