think
16px
820px

Admin Side-Nav — Design

Date: 2026-07-06 · Status: Approved (brainstorm; user picked side-nav over two-level tabs / merging)
Scope: web only — a presentation refactor of the Administration page. No backend, no routes, no i18n leaf-label changes.

Problem

Admin has grown to 13 flat Carbon tabs (Users … Backups). Past ~7 a flat TabList overflows
into scroll arrows and finding a setting means scanning 13 labels. Every tab is functionally
justified (SCIM was already folded into Directory), so nothing is deleted — the fix is
grouped presentation that scales with future features.

Design

Replace the Tabs/TabList/TabPanels in AdminPage.tsx with a left rail + content pane:

  • Rail: a <nav aria-label> with four group headers and the 13 items beneath them.
    Groups (header → items):
  • People & access → Users, Org & positions, Roles & permissions, Directory
  • Documents → Classifications, Document IDs, Retention
  • Correspondence → Correspondence, Letterhead
  • System → Licensing, AI, Observability, Backups
  • Selection: useState<ItemKey>('users') (matches today's default of the first tab; the
    current Tabs keep no URL state either, so parity is preserved). Items are <button>s with
    aria-current="page" on the active one.
  • Content: the selected item's existing component renders unchanged on the right. The 13
    leaf components (ClassificationsTabBackupsTab) are NOT touched; the inline Users
    panel stays inline in AdminPage.
  • A data-driven NAV_GROUPS array ({titleKey, items:[{key, labelKey, render}]}) replaces
    the hand-listed Tab/TabPanel pairs — adding a future page is one array entry.

Styling

New .admin-nav block in app.css using Carbon theme tokens (--cds-text-secondary group
headers in caps, --cds-layer-* hover/active, --cds-link-primary/border active accent) so
light/dark themes both work — same approach as the existing .obs-*/.admin-split blocks.
Rail is fixed-width (~13rem) and sticky; below the existing 900px breakpoint it collapses to
a wrapping horizontal list above the content (no horizontal scroll on mobile).

i18n

Reuse the existing admin.tabs.* keys for item labels (en/id already translated). Add only
four group-header keys admin.nav.people|documents|correspondence|system (en + id).

Testing

npx tsc --noEmit + npx vite build; deploy web; click-through: default lands on Users,
every one of the 13 items renders its page, active state + group headers visible in light
and dark, narrow viewport stacks. Demo untouched (no backend change).

Out of scope (YAGNI)

URL/deep-link state for the selected item (the tabs never had it; can add later), collapsing
groups, icons per item, touching any leaf tab component, app-shell SideNav integration.