feat(sidebar): flatten the top-level nav heading

Merged
MH
3mo ago
+180-19

Closes #65

Summary

The sidebar rendered its top-level heading as a collapsible <details> wrapping every subgroup, so a one-registry site (every current consuming site) got a redundant chevroned "DOCUMENTATION" level and one extra indent on the whole nav.

Now subgroups are the top level of the menu:

  • One non-empty heading → the heading is not rendered at all; "Getting started", "REST API", … sit directly in the menu.
  • Several headings → each renders as a static daisyUI menu-title label with mt-4 first:mt-0 breathing room — no fold; only subgroups collapse.
  • Landing doc index gets the same rule: the per-group h3 is skipped when there is exactly one heading, fixing the stacked "DOCUMENTATION / DOCUMENTATION" duplicate under the hero.

Intentionally unconditional (settled in the plan interview): no config knob — all consuming sites get the flattened chrome on upgrade.

What did NOT change

  • nav_groups keeps its { "Heading" => { "Subgroup" => [NavItem] } } shape — explicit c.nav lambdas work verbatim.
  • No JS change: docs-nav keys collapse persistence by summary text, so the removed top-level <details> just orphans one harmless localStorage key.
  • No CSS-contract change: menu-title was already @source inline'd in the generated stylesheet, and the new utilities are literals in gem Ruby scanned by the bin/build-css globs.

Test Coverage

  • spec/docs_ui/sidebar_spec.rb — single heading dropped; subgroups render as top-level <details open>; multiple headings render as menu-title <li>s (never <summary>s) with the spacing classes; empty headings excluded from the count; every <details> stays open (JS-off regression guard).
  • spec/docs_ui/landing_spec.rb (new) — single heading: "Documentation" appears once, no h3; multiple headings: both h3 labels render.

Verification

  • bundle exec rspec — 806 examples, 0 failures (94.0% line coverage)
  • bundle exec rubocop — no offenses
  • CSS contract checked by inspection — no new @source needed

Deviations & judgment calls

  • Sidebar single-heading count: an explicit c.nav lambda can contain empty headings (e.g. { "Docs" => {…}, "Empty" => {} }), which the registry-derived path never produces. Empty headings are excluded from the count, so one real + one empty heading still gets the single-heading rule (heading dropped). Covered by a spec.
  • Landing spacing: when the per-group h3 is skipped, the column ul also drops its mt-3 top margin — otherwise the links float an extra 12px below the h2 for no reason. Both class lists stay fully literal for the Tailwind scanner.
  • Discovery: the plan's prediction held — menu-title and all utilities were already covered by the generated CSS contract, so no generator/template change was needed.