Web UI: Jarl logo and wordmark

Add the logo, a Viking jarl drawn like a woodcut whose spear-pointed crown
holds a DNS tree. It is generated by scripts/logo.py, which mirrors the
left half so the figure is exactly symmetrical, as logo.svg (white on a
Nordic-blue disc) and logo-white.svg. It links home in the header, sits on
the login page and is the favicon.

The login page shows an angular "JARL" wordmark drawn as SVG paths over a
rule with a "DNS" node, coloured from the theme. The header's "Jarl" uses
a self-hosted Cinzel Bold subset (SIL OFL); jarld serves .woff2 as
font/woff2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-08 06:19:51 +00:00
1 parent bf7f99874b
commit a7d389be27
13 files changed
+336 -8

No files matched your search

+7 -2
View File
@@ -1,5 +1,6 @@
// Jarl web UI: router, layout and login.
import { h, mount, toast, errorBox, plural } from "./ui.js";
import { wordmark } from "./brand.js";
import { api, get, state, loadDraft, onChange, setUnauthorizedHandler, setImmediate } from "./api.js";
import * as dashboard from "./pages/dashboard.js";
import * as zones from "./pages/zones.js";
@@ -74,7 +75,8 @@ function renderLogin(message) {
}
} }, user, pass, h("button", { class: "primary", type: "submit" }, "Log in"), err);
mount(app, h("div", { class: "login panel" },
h("h1", null, "Jarl"),
h("img", { class: "login-logo", src: "/assets/logo.svg", alt: "", width: 104, height: 104 }),
h("h1", null, wordmark()),
h("div", { class: "tag" }, "Rule your DNS holds"),
message ? h("div", { class: "msg warn" }, message) : null,
form));
@@ -88,7 +90,10 @@ function layout() {
const q = h("input", { type: "search", placeholder: "Search names, data…", "aria-label": "Search" });
const me = state.me;
const header = h("header", { class: "top" },
h("a", { class: "brand", href: "#/" }, "Jarl", h("span", null, me.version)),
h("a", { class: "brand", href: "#/", title: "Dashboard" },
h("img", { src: "/assets/logo.svg", alt: "", width: 30, height: 30 }),
h("span", { class: "brand-name" }, "Jarl"),
h("span", { class: "brand-version" }, me.version)),
navEl,
h("div", { class: "spacer" }),
h("form", { class: "search", onsubmit: (e) => { e.preventDefault(); if (q.value.trim()) location.hash = "#/search?q=" + encodeURIComponent(q.value.trim()); } }, q),