Files
selfpost/internal/web/templates/layout.html
T
mix 610f74d6a4 panel: give each nav entry an icon
The bar was six similar-length words; nothing distinguished them at a
glance. Each entry now leads with a 16px inline SVG.

Inline rather than an icon font or a sprite file: the icons inherit the
entry's colour through currentColor, so the active pill's darker text and
a link's blue need no second rule, they cost no request, and they need no
exemption from the panel's default-src 'self' policy. Each is aria-hidden
because the entry's text is already its accessible name.

Account is included -- it is a page like the others and would otherwise
be the one bare word left in the bar -- so the highlight and padding
rules now cover the session block too, and the bar centres its items
instead of aligning them on the text baseline now that an entry is an
icon-plus-label row rather than a run of text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 22:11:51 +03:00

69 lines
5.8 KiB
HTML

{{define "layout.html"}}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.Title}}</title>
<link rel="icon" href="/static/favicon.png" type="image/png">
<link rel="stylesheet" href="/static/panel.css">
{{/* Unless told otherwise, htmx injects a stylesheet element of its own into
the head for the request-indicator classes. The panel uses no
hx-indicator, and that injected element would be the one thing the
Content-Security-Policy (phase 14.A) has to make an exception for, so it
is switched off here. */}}
<meta name="htmx-config" content='{"includeIndicatorStyles":false}'>
<script src="/static/htmx.min.js" defer></script>
<script src="/static/panel.js" defer></script>
</head>
<body>
<main>
{{if .User}}{{template "nav" .}}{{end}}
{{template "content" .}}
{{/* The running version, on every authenticated page: it is what a backup
manifest is checked against on restore and the first thing to establish
when something behaves unexpectedly. Only for signed-in administrators —
the login and setup pages must not advertise it to the internet. */}}
{{if .User}}<footer class="version">SelfPost {{.Version}}</footer>{{end}}
</main>
</body>
</html>{{end}}
{{/* nav is the panel's navigation bar. It is rendered here, from the layout, so
every authenticated page has it without the page's own template having to
remember to include it; .Active names the current page so it is highlighted
instead of linking to itself. Unauthenticated pages (login, setup) carry no
.User and get no nav. */}}
{{define "nav"}}
<nav class="nav">
<div class="links">
{{if eq .Active "status"}}<span aria-current="page">{{template "icon-status"}}Status</span>{{else}}<a href="/status">{{template "icon-status"}}Status</a>{{end}}
{{if eq .Active "domains"}}<span aria-current="page">{{template "icon-domains"}}Domains</span>{{else}}<a href="/domains">{{template "icon-domains"}}Domains</a>{{end}}
{{if eq .Active "deliveries"}}<span aria-current="page">{{template "icon-deliveries"}}Deliveries</span>{{else}}<a href="/deliveries">{{template "icon-deliveries"}}Deliveries</a>{{end}}
{{if eq .Active "mail_queue"}}<span aria-current="page">{{template "icon-mail-queue"}}Mail queue</span>{{else}}<a href="/mail-queue">{{template "icon-mail-queue"}}Mail queue</a>{{end}}
{{if eq .Active "system_log"}}<span aria-current="page">{{template "icon-system-log"}}System log</span>{{else}}<a href="/system-log">{{template "icon-system-log"}}System log</a>{{end}}
{{if eq .Active "backup"}}<span aria-current="page">{{template "icon-backup"}}Backup</span>{{else}}<a href="/backup">{{template "icon-backup"}}Backup</a>{{end}}
</div>
<div class="session">
<span class="muted">{{.User}}</span>
{{if eq .Active "account"}}<span aria-current="page">{{template "icon-account"}}Account</span>{{else}}<a href="/account">{{template "icon-account"}}Account</a>{{end}}
<form class="inline" method="post" action="/logout">
<button type="submit" class="danger">Sign out</button>
</form>
</div>
</nav>
{{end}}
{{/* Navigation icons. Inline SVG rather than an icon font or sprite file: they
inherit the link's colour through currentColor, cost no extra request, and
need no exemption from the panel's "default-src 'self'" policy. Each is
aria-hidden because the entry's own text is already the accessible name;
the icon is a landmark for the eye, not a second label. Kept as separate
templates so the nav above stays one readable line per page. */}}
{{define "icon-status"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.25 8.5h2.9L6.2 3.4l3.1 9.4 1.9-4.3h3.55"/></svg>{{end}}
{{define "icon-domains"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M1.9 8h12.2"/><path d="M8 1.75c1.85 1.8 2.8 4 2.8 6.25S9.85 12.45 8 14.25C6.15 12.45 5.2 10.25 5.2 8S6.15 3.55 8 1.75Z"/></svg>{{end}}
{{define "icon-deliveries"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.25 1.75 1.6 6.6l5 2.05 2.05 5z"/><path d="M14.25 1.75 6.6 8.65"/></svg>{{end}}
{{define "icon-mail-queue"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.75 9.5h3.3l1 1.75h3.9l1-1.75h3.3v3.05a1.2 1.2 0 0 1-1.2 1.2H2.95a1.2 1.2 0 0 1-1.2-1.2z"/><path d="M1.75 9.5 3.4 3.2a1.25 1.25 0 0 1 1.2-.95h6.8a1.25 1.25 0 0 1 1.2.95l1.65 6.3"/></svg>{{end}}
{{define "icon-system-log"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.75 1.75h5.1l3.4 3.4v8.05a1.05 1.05 0 0 1-1.05 1.05H3.75a1.05 1.05 0 0 1-1.05-1.05V2.8a1.05 1.05 0 0 1 1.05-1.05Z"/><path d="M8.85 1.75v3.4h3.4"/><path d="M5.35 8.6h5.3M5.35 11.1h3.5"/></svg>{{end}}
{{define "icon-backup"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.75 3.1h12.5v2.8H1.75z"/><path d="M3 5.9v7.05a1.05 1.05 0 0 0 1.05 1.05h7.9A1.05 1.05 0 0 0 13 12.95V5.9"/><path d="M6.5 8.6h3"/></svg>{{end}}
{{define "icon-account"}}<svg class="icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="5.4" r="2.75"/><path d="M2.9 14.25a5.1 5.1 0 0 1 10.2 0"/></svg>{{end}}