{#- chirp-ui: App Shell component Full-page shell with topbar, sidebar, and main content area. Navigation is driven by explicit hx-boost on sidebar_link/nav_link, targeting #main to keep the sidebar and topbar persistent. In filesystem apps, the route/layout tree should declare matching Chirp domain metadata plus an outlet of main so route-aware links and boosted responses resolve to this shell consistently. Named slots: topbar_leading (always — non-anchor leading zone left of the brand, for a hamburger/back/menu affordance), brand (when brand_slot=true), topbar, topbar_end, sidebar, context_rail (when context_rail=true), default (main content). For routed apps, wrap the main slot with shell_outlet() so custom shells use the same #page-content contract as app_shell_layout.html. Route-context rail (context_rail=true): adds an optional secondary region (#chirpui-context-rail) on the trailing side, for an inspector/detail panel that updates with the current route. The update protocol mirrors shell actions: a route response includes an out-of-band fragment targeting the rail outlet — use context_rail_oob() from chirpui/oob.html, e.g. {% call context_rail_oob() %}

Details for {{ item.name }}

{% end %} Works standalone with plain htmx OOB; under Chirp, boosted nav carries the rail fragment in the same response (route_link_attrs already targets #main). Below 72rem the rail stacks under main; below 48rem it joins the single column. Mobile nav drawer (nav_drawer=true): adds a topbar hamburger that opens the sidebar as an accessible off-canvas slide-over below the 48rem breakpoint (focus trap, ESC, scrim, scroll lock, focus return — all vanilla JS in shell_runtime_script(), no Alpine dependency). With context_rail=true a second topbar trigger opens the rail as a right-side slide-over. Opt-in and additive: shells that do not set nav_drawer keep the horizontal-strip fallback unchanged. See docs/patterns/navigation.md § Mobile nav drawer. Usage: import app_shell plus shell_outlet provide brand / topbar / topbar_end / sidebar slots fill topbar_leading for a non-anchor leading affordance (hamburger/back/ menu) — it escapes the brand-anchor trap, so put interactive controls there, never inside the brand slot (which nests inside the brand ) wrap the main slot with shell_outlet() for routed shells enable sidebar_collapsible=true for the resizable shell variant enable context_rail=true and fill the context_rail slot for the rail enable nav_drawer=true for the mobile hamburger slide-over -#} {% from "chirpui/shell_actions.html" import shell_actions_bar %} {% from "chirpui/shell_frame.html" import shell_runtime_script %} {% def app_shell(brand="", brand_url="/", brand_slot=false, brand_boost=true, sidebar_collapsible=false, topbar_variant="default", sidebar_variant="default", shell_actions=none, context_rail=false, context_rail_label="Context", context_rail_variant="default", nav_drawer=false, nav_drawer_label="Navigation", cls="") %} {% set topbar_variant = topbar_variant | validate_variant(("default","glass","gradient"), "default") %} {% set sidebar_variant = sidebar_variant | validate_variant(("default","glass","muted"), "default") %} {% set _context_rail_variant = context_rail_variant | validate_variant(("default","muted"), "default") %} {% set topbar_class = " chirpui-app-shell__topbar--" ~ topbar_variant if topbar_variant != "default" else "" %} {% set sidebar_class = " chirpui-app-shell__sidebar--" ~ sidebar_variant if sidebar_variant != "default" else "" %} {% set rail_shell_class = " chirpui-app-shell--has-context-rail" if context_rail else "" %} {% set nav_drawer_class = " chirpui-app-shell--nav-drawer" if nav_drawer else "" %} {% set rail_variant_class = " chirpui-app-shell__context-rail--" ~ _context_rail_variant if _context_rail_variant != "default" else "" %} {% set shell_actions_target = shell_actions.target if shell_actions else "chirp-shell-actions" %} {% if sidebar_collapsible %}