{% from "admin/components/icons.html" import icon %} {% from "admin/components/ui/theme-toggle.html" import theme_toggle %} {% from "admin/components/ui/locale-switcher.html" import locale_switcher_menu %} {% from "admin/components/ui/calendar.html" import calendar_script %} {% from "admin/components/ui/select.html" import select_script %} {% from "admin/components/ui/multi-select.html" import multi_select_script %} {# The CSRF token, for JS. The cookie is HttpOnly precisely so that scripts read it here instead -- see theme.html's htmx:configRequest listener. #} {% block title %}{{ _(site_title) }}{% endblock %} · {{ _(site_title) }} {% if favicon_url %}{% endif %} {# Design tokens, Tailwind config, Alpine + plugins, HTMX, and the theme store -- all of it, in the one load order that works. See admin/theme.html. #} {% include "admin/theme.html" %} {{ calendar_script() }} {{ select_script() }} {{ multi_select_script() }} {% block extra_head %}{% endblock %} {# These resolve through theme.html's variables, so the admin follows the active theme without a template knowing which one it is. #} {# SidebarProvider, from shadcn's sidebar-07 block. Its state lives on so every descendant can read it: `collapsed` is the desktop icon-rail mode, seeded from the sidebar_state cookie so the choice survives a reload; `sidebarOpen` is the mobile sheet, a separate axis, because a phone has an overlay rather than a rail. Cmd/Ctrl+B is wired here too, so it works wherever focus is. #} {% include "admin/components/toasts.html" %} {% include "admin/components/action_confirm_modal.html" %}
{# Below md the sidebar *is* a sheet -- overlay plus off-canvas panel -- and from md up it becomes the static collapsible column. One element does both jobs, so there is no duplicate nav markup to keep in sync. #}
{% include "admin/components/ui/sidebar.html" %} {# SidebarInset. #}
{# The block's header: trigger, a short vertical rule, then the breadcrumbs. h-16 fixed, so it doesn't jump as content changes. #}
{% if breadcrumbs %} {% from "admin/components/ui/breadcrumb.html" import render_breadcrumbs %} {{ render_breadcrumbs(breadcrumbs, base_path) }} {% endif %}
{% if locale_switcher %}{{ locale_switcher_menu(locale_switcher, base_path, csrf_token) }}{% endif %} {{ theme_toggle() }}
{# The page itself scrolls -- one scrollbar, the browser's own, for however long the table is. So main must NOT become a scroll container: overflow-x-clip clips a wide table without the side effect overflow-x-hidden has, which is to turn the other axis into auto and box the content in a scrollbar of its own. The sidebar stays put by being sticky, not by the content scrolling underneath it. #}
{% block content %}{% endblock %}