feat(ui): build the Nav component with submenus and roving focus

Replaces a scaffold that rendered bare anchors. Flat or nested to three
levels, icons, badges, disabled items, aria-current on the active link,
arrow-key roving focus, and a disclosure arrow that rotates on open.

Three levels rather than arbitrary depth because this template language has
no component recursion, so each level is written out.

On a phone the bar becomes a toggle and submenus stack inline rather than
floating: a hover-opened overlay cannot be reached on touch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 16:47:20 +05:30
co-authored by Claude Opus 5
parent 45ef035bae
commit f4960f2fc5
3 changed files with 436 additions and 7 deletions
@@ -253,6 +253,63 @@ const DATATABLE_ROWS =
'[{"id": 1, "name": "Northwind", "plan": "Scale", "owner": "A. Okafor", "seats": 6, "status": "Active", "statusHtml": "<span class=\\"showcase-pill showcase-pill--success\\">Active</span>"}, {"id": 2, "name": "Acme Industrial", "plan": "Team", "owner": "R. Silva", "seats": 13, "status": "Trial", "statusHtml": "<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>"}, {"id": 3, "name": "Globex", "plan": "Enterprise", "owner": "M. Chen", "seats": 20, "status": "Past due", "statusHtml": "<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>"}, {"id": 4, "name": "Initech", "plan": "Starter", "owner": "J. Dubois", "seats": 27, "status": "Active", "statusHtml": "<span class=\\"showcase-pill showcase-pill--success\\">Active</span>"}, {"id": 5, "name": "Umbrella", "plan": "Scale", "owner": "P. Novak", "seats": 34, "status": "Trial", "statusHtml": "<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>"}, {"id": 6, "name": "Stark Labs", "plan": "Team", "owner": "A. Okafor", "seats": 41, "status": "Past due", "statusHtml": "<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>"}, {"id": 7, "name": "Wayne Foods", "plan": "Enterprise", "owner": "R. Silva", "seats": 48, "status": "Active", "statusHtml": "<span class=\\"showcase-pill showcase-pill--success\\">Active</span>"}, {"id": 8, "name": "Soylent", "plan": "Starter", "owner": "M. Chen", "seats": 55, "status": "Trial", "statusHtml": "<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>"}, {"id": 9, "name": "Hooli", "plan": "Scale", "owner": "J. Dubois", "seats": 62, "status": "Past due", "statusHtml": "<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>"}, {"id": 10, "name": "Vehement", "plan": "Team", "owner": "P. Novak", "seats": 69, "status": "Active", "statusHtml": "<span class=\\"showcase-pill showcase-pill--success\\">Active</span>"}, {"id": 11, "name": "Massive Dynamic", "plan": "Enterprise", "owner": "A. Okafor", "seats": 76, "status": "Trial", "statusHtml": "<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>"}, {"id": 12, "name": "Cyberdyne", "plan": "Starter", "owner": "R. Silva", "seats": 83, "status": "Past due", "statusHtml": "<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>"}, {"id": 13, "name": "Tyrell", "plan": "Scale", "owner": "M. Chen", "seats": 90, "status": "Active", "statusHtml": "<span class=\\"showcase-pill showcase-pill--success\\">Active</span>"}, {"id": 14, "name": "Aperture", "plan": "Team", "owner": "J. Dubois", "seats": 97, "status": "Trial", "statusHtml": "<span class=\\"showcase-pill showcase-pill--info\\">Trial</span>"}, {"id": 15, "name": "Black Mesa", "plan": "Enterprise", "owner": "P. Novak", "seats": 104, "status": "Past due", "statusHtml": "<span class=\\"showcase-pill showcase-pill--danger\\">Past due</span>"}]';
export const componentProfiles = {
Nav: {
demos: [
standard(
"Horizontal links",
"A flat link bar. The active item is marked with aria-current, and the arrow keys move between items.",
{
items: [
{ label: "Home", href: "/", value: "home", icon: "icon-[lucide--house]" },
{ label: "Inbox", href: "/inbox", value: "inbox", badge: "9" },
{ label: "Reports", href: "/reports", value: "reports" },
{ label: "Archive", href: "/archive", value: "archive", disabled: true },
],
active: "inbox",
},
),
advanced(
"Nested submenus",
"An item carrying its own items array opens a submenu on hover or focus, to a maximum of three levels. On a phone the submenus stack inline instead of floating, because a hover-opened overlay is unreachable on touch.",
{
items: [
{ label: "Home", href: "/", value: "home" },
{
label: "Products",
value: "products",
items: [
{ label: "Overview", href: "/p", value: "p-overview" },
{
label: "Platform",
value: "p-platform",
items: [
{ label: "Runtime", href: "/p/runtime", value: "runtime" },
{ label: "Compiler", href: "/p/compiler", value: "compiler" },
],
},
],
},
],
active: "p-overview",
},
),
standard("Vertical rail", "Vertical orientation switches the arrow keys to up and down.", {
items: [
{ label: "Dashboard", href: "/", value: "dash", icon: "icon-[lucide--gauge]" },
{ label: "Team", href: "/team", value: "team", icon: "icon-[lucide--users]" },
{
label: "Settings",
href: "/settings",
value: "settings",
icon: "icon-[lucide--settings]",
},
],
active: "team",
orientation: "vertical",
collapsible: false,
}),
],
},
Stepper: {
demos: [
standard(