// Navigation tour. Route: /navigation // // One page that wires every navigation component together the way a real // console would, rather than showing each in isolation: // // Navbar the top bar, with a dropdown built from nested children // MegaMenu a wide product panel beside it // Breadcrumb the trail under the bar // Sidebar the left rail, which becomes a Drawer on a phone // Tabs the main content, backed by a tab query parameter // Stepper a wizard inside one tab, gated on a checkbox // Pagination under the list in another tab // Scrollspy a contents rail that follows the reader down the article // Nav the secondary links in the footer // // Object props are held in state and bound. Passing one inline as an // attribute would put a brace at the start of the value, which the compiler // reads as an interpolation. page NavigationTour { seo { title = "Navigation tour" description = "Every navigation component working together in one shell." } state brand = { label: "Acme", description: "Console", href: "/navigation" } state topLinks = [ { label: "Overview", href: "/navigation", value: "overview" }, { label: "Workspace", value: "workspace", columns: 2, description: "Everything your team touches.", children: [ { label: "Projects", children: [ { label: "Active", href: "/navigation", description: "In flight right now." }, { label: "Archived", href: "/navigation", description: "Kept for the record." } ] }, { label: "People", children: [ { label: "Members", href: "/navigation", description: "Who can sign in." }, { label: "Roles", href: "/navigation", description: "What they may do." } ] } ] }, { label: "Reports", href: "/navigation", value: "reports" } ] state topActions = [ { label: "Docs", href: "/about", variant: "link" }, { label: "New project", href: "/navigation", variant: "primary" } ] state megaColumns = [ { heading: "Platform", items: [ { label: "Runtime", href: "/navigation", description: "The browser half.", icon: "icon-[lucide--cpu]" }, { label: "Compiler", href: "/navigation", description: "WRN to JavaScript.", icon: "icon-[lucide--hammer]" } ] }, { heading: "Tooling", items: [ { label: "CLI", href: "/navigation", description: "Scaffold and deploy.", icon: "icon-[lucide--terminal]" }, { label: "Editor", href: "/navigation", description: "Language server.", icon: "icon-[lucide--code]" } ] } ] state crumbs = [ { label: "Home", href: "/" }, { label: "Console", href: "/navigation" }, { label: "Navigation tour" } ] state sideItems = [ { label: "Overview", href: "#overview", value: "overview", icon: "icon-[lucide--gauge]" }, { heading: "Build", items: [ { label: "Projects", href: "#projects", value: "projects", badge: "4" }, { label: "Environments", value: "envs", items: [ { label: "Production", href: "#projects", value: "prod" }, { label: "Staging", href: "#projects", value: "staging" } ] } ] }, { heading: "Account", items: [{ label: "Settings", href: "#settings", value: "settings", icon: "icon-[lucide--settings]" }] } ] state contentTabs = [ { label: "Article", value: "article", title: "Release notes" }, { label: "Wizard", value: "wizard", title: "Create a project" }, { label: "Members", value: "members", title: "Who can sign in" } ] state spyLinks = [ { label: "Overview", href: "#overview" }, { label: "Projects", href: "#projects" }, { label: "Settings", href: "#settings" } ] state wizardSteps = [ { label: "Details", title: "Name the project", content: "Pick something the team will recognise." }, { label: "Access", title: "Who can see it", content: "Invite people now or later." }, { label: "Review", title: "Check and create", content: "Nothing is created until you finish." } ] state footerLinks = [ { label: "Status", href: "/navigation", value: "status" }, { label: "Changelog", href: "/navigation", value: "changelog" }, { label: "Support", href: "/about", value: "support" } ] state members = [ "A. Okafor", "R. Silva", "M. Chen", "J. Dubois", "P. Novak", "T. Ahmed", "L. Rossi", "K. Bauer", "S. Haddad", "N. Ito" ] // Tabs reports the selection and this page owns what is shown. Slot content // and component props are rendered once and do not track page state, so // anything that has to react lives out here in page scope. state section = "article" state sideActive = "overview" state wizardStep = 0 // Not named page: that is the page keyword and shadows the runtime binding. state currentPage = 1 state pageSize = 4 state lastEvent = "Nothing yet. Interact with any control." functions { client function onSideSelect(payload) { sideActive = payload.value || "" lastEvent = "Sidebar select: " + (payload.value || "(none)") } client function onTabChange(payload) { section = payload.value lastEvent = "Tab change: " + payload.value } // The stepper never validates anything itself. This page owns the // checkbox, so it decides when the step may advance. client function onWizardChange(payload) { wizardStep = payload.index lastEvent = "Wizard step: " + (payload.index + 1) } client function onWizardFinish() { lastEvent = "Wizard finished. A real app would create the project here." } client function onWizardSkip(payload) { wizardStep = payload.index lastEvent = "Wizard skipped to step " + (payload.index + 1) } client function onPageChange(payload) { currentPage = payload.page lastEvent = "Page " + payload.page } client function onSpyChange(payload) { lastEvent = "Reading: " + payload.label } client function onMegaSelect(payload) { lastEvent = "Mega menu: " + payload.value } client function onNavSelect(payload) { lastEvent = "Footer nav: " + payload.value } } view {

{lastEvent}

Overview

Scroll this column and the contents rail on the right follows along. The runtime observes these sections against the viewport and moves aria-current to the matching link.

Projects

The rail on the left is the Sidebar. Below the tablet breakpoint it collapses behind a launcher and opens as a Drawer, which is where the focus trap and the body scroll lock come from.

Settings

The tab you are reading is mirrored into the URL. Switch tabs and the address bar follows, then use the browser back button.

Back, Skip and Next each emit an output, shown in the status line above, and Next becomes Create project on the last step. The marker does not advance here: active is a component prop, and props are rendered once rather than tracking page state. Set nextDisabled to hold Next while a form is incomplete.

Showing page {currentPage} of 3. Pagination reports the requested page through its change output and the page decides what to do with it; the counter above is bound to that state. Re-slicing the list itself would need the list re-rendered, which an each block does not do when state changes.

    {#each members as person}
  • {person}
  • {/each}
} style { .tour { display: grid; gap: 1rem; padding-bottom: 3rem; } .tour__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; } /* * Three columns: the rail, the reading column and the contents. The middle * column carries min-width 0 so a wide table or code block inside a tab * cannot push the whole shell sideways. */ .tour__shell { display: grid; grid-template-columns: 15rem minmax(0, 1fr) 12rem; gap: 1.5rem; align-items: start; } .tour__main { min-width: 0; } .tour__rail, .tour__toc { position: sticky; top: 1rem; } .tour__status { margin: 0 0 0.75rem; padding: 0.5rem 0.75rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-soft); color: var(--wire-color-text-muted); font-size: 0.82rem; } /* Tall enough that scrolling actually moves between sections. */ .tour__section { min-height: 60vh; padding-top: 0.5rem; scroll-margin-top: 1rem; } .tour__section h3 { margin: 0 0 0.4rem; font-size: 1rem; font-weight: 700; } .tour__section p { margin: 0; max-width: 46rem; color: var(--wire-color-text-muted); line-height: 1.7; } .tour__members { display: grid; gap: 0.35rem; margin: 0 0 1rem; padding: 0; list-style: none; } .tour__members li { padding: 0.5rem 0.75rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-sm); font-size: 0.88rem; } .tour__footer { padding-top: 1rem; border-top: 1px solid var(--wire-color-border); } /* The rails fold away first; the reading column is what matters. */ @media (max-width: 1023px) { .tour__shell { grid-template-columns: minmax(0, 1fr); } .tour__rail, .tour__toc { position: static; } } } }