{#- chirp-ui: App shell layout — extends chirp shell with ChirpUI structure Use this for persistent app shells (topbar, sidebar). Extends chirp/layouts/shell.html and fills in ChirpUI CSS, toast, and app shell structure. Usage: {% extends "chirpui/app_shell_layout.html" %} {% block brand %}My App{% end %} {% block sidebar %} {% from "chirpui/sidebar.html" import sidebar, sidebar_link, sidebar_section, shell_brand_link %} {% call sidebar() %} {% call sidebar_section("Main") %} {{ sidebar_link("/", "Home") }} {{ sidebar_link("/dashboard", "Dashboard") }} {% end %} {% end %} {% end %} {% block content %} Page content here {% end %} This layout keeps the sidebar non-collapsible by default. Use a custom shell or the `app_shell()` macro when you need the resizable/collapsible sidebar variant. Mobile nav drawer: pass nav_drawer=True in the render context (e.g. Template("page.html", nav_drawer=True)) to add a topbar hamburger that opens the sidebar as an accessible off-canvas slide-over below 48rem (focus trap, ESC, scrim, scroll lock, focus return — wired by shell_runtime_script()). With context_rail=True a second trigger opens the rail; override its label with nav_drawer_rail_label. Opt-in and additive — unset leaves the horizontal-strip fallback unchanged. See docs/patterns/navigation.md. Custom brand (override brand_link for non-/ href or custom markup): {% block brand_link %}{% call shell_brand_link(href="/projects") %}My App{% end %}{% end %} Topbar leading affordance (non-anchor, left of the brand — for a hamburger/ back/menu; do NOT put interactive content in {% block brand %}, it nests inside the brand ): {% block topbar_leading %}{{ icon_btn("list", aria_label="Open menu") }}{% end %} Alpine: Chirp injects Alpine.js via AppConfig(alpine=True) or automatically when use_chirp_ui() is called. This layout does NOT load Alpine itself — Chirp is the single authority for Alpine injection. Default app shells use document scrolling. Full-height main (chat, maps): override {% block main_shell_class %} with chirpui-app-shell__main--fill so #page-content receives flex:1 and inner panels handle bounded scroll below the sticky topbar. See docs/fundamentals/layout-vertical.md. HTMX: primary #main swap runs before out-of-band fragments. shell_runtime_script() (emitted by every shell entry point) resets the route-scoped shell regions (#chirp-shell-actions, #chirpui-context-rail) on each boosted #main navigation, so OOB fragments land in clean nodes and a route that ships no fragment for a region empties it rather than stranding the prior route's content. page_scripts block: renders after toast/CSRF/nav scripts near . Only templates that {% extends %} this layout can override it (inner _layout.html files). Filesystem page templates (page.html) are injected via render_with_blocks into {% block content %} — they CANNOT override page_scripts or any other sibling block. If a page needs an inline {% block head_extra %}{% end %} {% end %} {% block shell %} {% from "chirpui/sidebar.html" import sidebar, sidebar_toggle, shell_brand_link %} {% from "chirpui/shell_frame.html" import shell_outlet, shell_outlet_attrs, shell_runtime_script %} {% from "chirpui/shell_actions.html" import shell_actions_bar %} {% from "chirpui/toast.html" import toast_container %} {% set _sidebar_collapsible = "false" %} {% set _collapsible = _sidebar_collapsible and _sidebar_collapsible != "false" %} {% set _shell_actions = shell_actions | default(none) %} {% set _shell_actions_target = _shell_actions.target if _shell_actions else "chirp-shell-actions" %} {% set _context_rail = context_rail | default(false) %} {% set _rail_shell_class = " chirpui-app-shell--has-context-rail" if _context_rail else "" %} {% set _nav_drawer = nav_drawer | default(false) %} {% set _nav_drawer_label = nav_drawer_label | default("Navigation") %} {% set _nav_drawer_class = " chirpui-app-shell--nav-drawer" if _nav_drawer else "" %}
{#- Always-available non-anchor leading zone (left of the brand ): fill {% block topbar_leading %} with a hamburger/back/menu so interactive content is NOT nested inside the brand anchor (#220). The built-in nav_drawer hamburger + collapsible sidebar_toggle render in the same wrapper. Always rendered (empty topbar-start is flex-shrink:0, inert). -#}
{% block topbar_leading %}{% end %}{% if _nav_drawer %}{% endif %}{% if _collapsible %}{{ sidebar_toggle() }}{% endif %}
{% block brand_link %} {% call shell_brand_link() %} {% block brand %}{% end %} {% end %} {% end %}
{% block topbar_center %}{% end %}
{% if _nav_drawer and _context_rail %}{% endif %}
{% if _shell_actions %} {{ shell_actions_bar(_shell_actions) }} {% end %}
{% block topbar_end %}{% end %}
{% call shell_outlet(include_boost_attrs=false) %} {% block content %}{% end %} {% end %}
{% if _context_rail %} {#- Route-context rail outlet — same contract as app_shell(): a route response updates it via an OOB fragment targeting #chirpui-context-rail (context_rail_oob). -#} {% end %} {% if _nav_drawer %}{% endif %}
{{ shell_runtime_script() }} {{ toast_container() }} {% block page_scripts %}{% end %} {% end %}