{{define "layout"}} {{.PageTitle}} — Breadbox {{if .CSRFToken}}{{end}}
{{if .Embed}} {{- /* Embed shell — bare HTML for an iframe target. No top bar, no sidebar, no flash, no footer. Only the global scripts (Alpine, Lucide, styles.css) so the embedded demo's interactivity continues to work. Used by /design/c/{slug}?embed=1 loaded inside the sandbox's viewport-toggle iframe. */ -}}
{{if .TemplContent}}{{.TemplContent}}{{else}}{{block "content" .}}{{end}}{{end}}
{{else if .Standalone}} {{- /* Standalone shell — no sidebar, no mobile drawer. Used by the /design sandbox so reviewers can focus on the component under test. The page body still gets all the global head + footer scripts (Alpine, Lucide, cmdk, shortcuts) so x-data interactivity continues to work. */ -}}
{{template "flash" .}} {{if .TemplContent}}{{.TemplContent}}{{else}}{{block "content" .}}{{end}}{{end}}
{{else}}
{{/* Single location trail — Section / parent / … / current. The layout owns this; pages no longer render their own breadcrumb. */}} {{renderComponent "TopbarBreadcrumb" .}}
{{renderComponent "ThemeToggle" nil}} {{if .AdminUsername}}{{renderComponent "TopbarUserMenu" .}}{{end}}
{{ brand "breadbox" "w-4 h-4 shrink-0 opacity-70" }} Breadbox
{{if .AdminUsername}}{{renderComponent "TopbarUserMenu" .}}{{end}}
{{/* Mobile location trail — the desktop topbar is hidden < lg, so the breadcrumb rides below the mobile navbar (scrollable) on detail pages. Only rendered when the page supplies a trail. */}} {{if .Breadcrumbs}}
{{renderComponent "TopbarBreadcrumbMobile" .}}
{{end}}
{{template "flash" .}} {{- /* TemplContent slot — set by TemplateRenderer.RenderWithTempl to render a templ-generated body inside the base layout without migrating base.html wholesale. Pages migrated to templ drop their old {{define "content"}} block and pass a templ.Component instead. See issue #462. */ -}} {{if .TemplContent}}{{.TemplContent}}{{else}}{{block "content" .}}{{end}}{{end}}
{{- /* App-wide connect-bank slide-over. Rendered once here (not per page) so every "Connect a bank" entry point across the app opens the same drawer via $store.drawers.open('connect-bank'). Props (provider availability + household members) are resolved at render time by the connectDrawer resolver. */ -}} {{if .ConnectBankDrawer}}{{renderComponent "ConnectBankDrawer" .ConnectBankDrawer}}{{end}}
{{end}} {{- /* Settings is a full page now (/settings/*), not a modal overlay. Any legacy `open-settings` dispatch — the sidebar user menu, the cmdk palette, keyboard shortcuts — navigates to the corresponding /settings/ page instead of opening a dialog. One global listener keeps every existing dispatcher working untouched. */ -}} {{if .IsEditor}} {{end}}
{{- /* Global prompt modal — preview / edit Markdown, mounted once and opened from anywhere via a window event: $dispatch('bb-prompt-modal', { mode, edit, title, subtitle, value, target, onSaved }) Logic lives in static/js/admin/components/prompt_modal.js. */ -}}
{{if .DevModeEnabled}}
{{end}}
{{ lucide "tag" "" }}
esc
{{ lucide "tag" "" }}
esc
{{ lucide "receipt" "" }}
{{ lucide "plus" "" }} to add {{ lucide "x" "" }} to remove
{{ lucide "search" "" }}
esc
{{end}}