123 lines
5.0 KiB
HTML
123 lines
5.0 KiB
HTML
{#
|
|
The shell every full page extends. A page template overrides `title`,
|
|
optionally `head` (extra scripts) and `body_class`, and always `content`.
|
|
|
|
Every page struct must carry a `nav: crate::ui::Nav` field, because the
|
|
navbar component below reads it.
|
|
#}
|
|
<!doctype html>
|
|
<html lang="{{ nav.language() }}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{% block title %}Komp Accounting{% endblock %} · Komp Accounting</title>
|
|
{#
|
|
Cascade order, declared before anything ships CSS so that it is this line
|
|
that decides it. `app` is static/app.css, which is wrapped in that layer:
|
|
it sits above Tailwind's preflight — so the reset cannot touch the site's
|
|
own look — and below Tailwind's utilities, so a utility class put on an
|
|
element still wins. That is what makes Tailwind usable here one element at
|
|
a time, without converting the stylesheet.
|
|
#}
|
|
<style>@layer theme, base, app, components, utilities;</style>
|
|
<link rel="stylesheet" href="/static/app.css">
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2/dist/htmx.min.js"></script>
|
|
|
|
{#
|
|
Penguin UI (https://www.penguinui.com) is Tailwind + Alpine, and every
|
|
component on this site is one of its components: the navbar, the failure
|
|
alert and modal, and the toast, which is included straight out of
|
|
penguinui-components. The focus plugin is what x-trap in the modal needs,
|
|
and it has to load before Alpine itself.
|
|
#}
|
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
<style type="text/tailwindcss">
|
|
/*
|
|
The theme penguinui-components/style.css declares, with this app's palette
|
|
in the light slots. The names are the library's and are not renamed: the
|
|
component markup here is the library's markup, so it asks for
|
|
`text-on-surface` and `rounded-radius` and has to find them.
|
|
*/
|
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
|
|
@theme {
|
|
/* Light theme — this app's palette. */
|
|
--color-surface: #ffffff;
|
|
--color-surface-alt: #f3f5f7;
|
|
--color-on-surface: #465267;
|
|
--color-on-surface-strong: #17202a;
|
|
--color-primary: #2563eb;
|
|
--color-on-primary: #ffffff;
|
|
--color-secondary: #152238;
|
|
--color-on-secondary: #ffffff;
|
|
--color-outline: #d9dfe7;
|
|
--color-outline-strong: #8f9bad;
|
|
|
|
/*
|
|
Dark theme. Nothing sets the `.dark` class the custom variant above
|
|
scopes these to, so they are inert — they exist because the library's
|
|
markup carries `dark:` classes and is used as it ships. Scoping the
|
|
variant to a class rather than leaving Tailwind's prefers-color-scheme
|
|
default is what keeps them from firing on an OS set to dark.
|
|
*/
|
|
--color-surface-dark: #0f1722;
|
|
--color-surface-dark-alt: #152238;
|
|
--color-on-surface-dark: #cbd7e8;
|
|
--color-on-surface-dark-strong: #ffffff;
|
|
--color-primary-dark: #68a4ff;
|
|
--color-on-primary-dark: #0f1722;
|
|
--color-outline-dark: #33415a;
|
|
--color-outline-dark-strong: #cbd7e8;
|
|
|
|
/* Shared colours. */
|
|
--color-info: #1d4e89;
|
|
--color-on-info: #ffffff;
|
|
--color-success: #21643a;
|
|
--color-on-success: #ffffff;
|
|
--color-warning: #8a5a08;
|
|
--color-on-warning: #ffffff;
|
|
--color-danger: #9d342d;
|
|
--color-on-danger: #ffffff;
|
|
|
|
/* Border radius. */
|
|
--radius-radius: 6px;
|
|
}
|
|
</style>
|
|
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/focus@3/dist/cdn.min.js"></script>
|
|
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js"></script>
|
|
|
|
<script>
|
|
// htmx only swaps 2xx responses. Every form in this app answers a failure
|
|
// with a status that says so — 422 for a rejected draft, 403 for a lost
|
|
// session, 502 for an unreachable backend — so the markup explaining the
|
|
// failure was rendered, returned, and then thrown away, and the page did
|
|
// nothing at all. Swap it instead; the response body is the explanation.
|
|
document.addEventListener('htmx:beforeSwap', function (event) {
|
|
if (event.detail.xhr.status >= 400) {
|
|
event.detail.shouldSwap = true;
|
|
event.detail.isError = false;
|
|
}
|
|
});
|
|
</script>
|
|
{% block head %}{% endblock %}
|
|
</head>
|
|
<body>
|
|
{% include "ui/navbar.html" %}
|
|
{% block content %}{% endblock %}
|
|
{#
|
|
Penguin UI's stacking toast notification, straight out of the library and
|
|
unmodified — askama.toml puts penguinui-components on the template path, so
|
|
this is the library's file, not a copy of it.
|
|
|
|
Mounted once per page and deliberately outside the block every fragment
|
|
swaps into: a success is announced by a fragment dispatching `notify` (see
|
|
ui/toast.html), and the toast showing it has to outlive the swap that
|
|
follows. The component file also ships the demo buttons that trigger it in
|
|
the library's own preview; `.penguin-toast > button` in static/app.css is
|
|
what keeps those off the page.
|
|
#}
|
|
<div class="penguin-toast">{% include "toast-notification/stacking-toast-notification.html" %}</div>
|
|
</body>
|
|
</html>
|