{# Layout: sidebar + topbar + content slot La host app debe definir/override: - block title - block sidebar_nav → links (usar core/components/sidebar_link.html) - block breadcrumbs - block topbar_actions → CTA extra (notificaciones, etc.) - block content - block footer → contenido extra del footer global - block extra_head, block extra_body Variables esperadas (opcionales): - user.email, user.is_admin - tokens_override_href → al CSS de tokens del cliente - logo_html → snippet HTML para sustituir el logotipo por defecto #} {# viewport-fit=cover activa env(safe-area-inset-*) en iOS con notch #} {% block title %}Datagrowth{% endblock %} {# FOUC guard: aplica el tema antes del primer paint para evitar el parpadeo light->dark en usuarios con preferencia "dark" guardada. Se ejecuta síncronamente (sin defer) y SOLO toca data-theme; el resto del API (toggle, sync de icons/aria) vive en theme.js. #} {# Una sola carga de DM Sans con italic + opsz para evitar doble fetch cuando el host la re-importa en app.css. Los hosts NO deben re-importar esta fuente; deben eliminar su @import de DM Sans del CSS local. #} {# Fallback a `request.state.dg_tokens_override_href` para handlers que no pasan `tokens_override_href` en el context Jinja (caso: routers del paquete que renderizan con context propio). La host app puebla el state via `populate_template_state` con la URL al CSS del `BrandDesign` que viste al host. #} {% set _tokens_override_href = tokens_override_href or (request.state.dg_tokens_override_href if request.state else None) %} {% if _tokens_override_href %} {% endif %} {# CSRF double-submit wiring. SIN `defer`: el monkey-patch de `fetch` debe estar activo antes de cualquier ` {% block extra_head %}{% endblock %} {% set user = user or (request.state.dg_user if request.state else None) %} {# ── Topbar ──────────────────────────────────────────────── #}
{# Botón hamburguesa: solo visible en mobile (< md) #} {% block logo %} datagrowth {% endblock %}
{% block topbar_actions %}{% endblock %} {% from "core/components/theme_toggle.html" import theme_toggle %} {{ theme_toggle() }} {% if user %} {% if user.is_admin %} {% endif %} {% from "core/components/csrf.html" import csrf_input %} {% endif %}
{# Overlay semitransparente que aparece detrás del drawer en mobile. Click en él cierra el drawer. #}
{# ── Sidebar (desktop: estático; mobile: drawer deslizante) ── #} {# ── Contenido principal ─────────────────────────────────── #}
{% block content %}{% endblock %}
{# Footer global: links legales y espacio para que el host inyecte extras. #} {# Región de toasts. `register_error_handlers` inyecta un toast inicial server-side vía el bloque `toast_initial`. El mismo contenedor recibe OOB swaps de HTMX y toasts client-side. #}
{% block toast_initial %}{% endblock %}
{% block extra_body %}{% endblock %}