Files
steward/internal/web/static/assets/brand.js
T
abrown38andClaude Opus 5.5 3c0808591f Steward 0.1.0 (in development): a DHCPv4 server with a web UI, API and CLI
Subnets served per interface, pools, reservations, options at three
levels, leases in SQLite saved before the ACK, ping and decline conflict
checks, validated changes with history and undo, users and tokens, an
audit log, a Kea importer, a sandboxed systemd unit and Debian packages.
Tested with real udhcpc and dhcpcd clients in Docker (scripts/e2e.sh).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:14:07 +01:00

68 lines
3.6 KiB
JavaScript

// The Steward logo and the login page's wordmark, drawn as inline SVG so
// they appear together with the page instead of loading afterwards.
//
// The "STEWARD" wordmark: the angular letters of Jarl's wordmark (its A
// and R are the same), drawn as paths so no font is needed. The login page
// shows it over a rule with a DHCP node; the header shows the letters
// alone. The letters use the text color and the rule the accent color.
import * as logoData from "./logo.js";
const NS = "http://www.w3.org/2000/svg";
function el(tag, attrs, ...children) {
const e = document.createElementNS(NS, tag);
for (const [k, v] of Object.entries(attrs || {})) e.setAttribute(k, v);
for (const c of children) e.append(c instanceof Node ? c : document.createTextNode(c));
return e;
}
// Letters on a 100-unit cap height; counters are cut with evenodd.
const letters = [
// S: squared, with a downward barb on the top bar and an upward one on
// the bottom bar (Jarl's L turned around)
{ x: 0, d: "M10 0 H54 L58 30 L40 16 H18 V41 H48 L58 51 V90 L48 100 H4 L0 70 L18 84 H40 V59 H10 L0 49 V10 Z" },
// T: a bar with barbed ends
{ x: 70, d: "M0 0 H70 L66 22 L52 18 H44 V100 H26 V18 H18 L4 22 Z" },
// E: the bottom arm ends in Jarl's L spike
{ x: 152, d: "M0 0 H52 L56 18 H18 V41 H44 V59 H18 V82 H40 L58 70 L54 100 H0 Z" },
// W: flat-topped middle peak
{ x: 222, d: "M0 0 H18 L28 66 L38 30 H52 L62 66 L72 0 H90 L76 100 H54 L45 66 L36 100 H14 Z" },
// A and R: Jarl's
{ x: 324, d: "M35 0 L46 10 L64 90 L70 100 H50 L44 76 L35 62 L26 76 L20 100 H0 L6 90 L24 10 Z M35 26 L41 40 L35 54 L29 40 Z" },
{ x: 406, d: "M0 0 H48 L60 12 V40 L50 50 L66 100 H46 L34 60 H18 V100 H0 Z M18 14 H41 L46 19 V35 L41 46 H18 Z" },
// D: cut corners, like R's bowl
{ x: 484, d: "M0 0 H44 L64 20 V80 L44 100 H0 Z M18 16 H38 L46 24 V76 L38 84 H18 Z" },
];
const W = 548; // the letters' width
// wordmark returns the login page's wordmark, letters over the rule.
// wordmark({ rule: false, height: 19, cls: "brand-name" }) returns just the
// letters, height pixels tall.
export function wordmark({ rule = true, height, cls = "wordmark" } = {}) {
const letterG = el("g", { fill: "currentColor", "fill-rule": "evenodd" },
...letters.map((l) => el("path", { d: l.d, transform: `translate(${l.x} 0)` })));
if (!rule) {
const attrs = { class: cls, viewBox: `-2 -2 ${W + 4} 104`, role: "img", "aria-label": "Steward" };
if (height) Object.assign(attrs, { height, width: Math.round(height * (W + 4) / 104) });
return el("svg", attrs, el("title", null, "Steward"), letterG);
}
const c = W / 2;
const ruleG = el("g", { class: "wordmark-rule", transform: "translate(0 118)" },
el("path", { d: `M2 18 L${c - 60} 15.6 V20.4 Z M${W - 2} 18 L${c + 60} 15.6 V20.4 Z`, class: "fill" }),
el("circle", { cx: c - 52, cy: 18, r: 4.6, class: "line" }),
el("path", { d: `M${c - 47.4} 18 H${c - 30} M${c + 30} 18 H${c + 47.4}`, class: "line" }),
el("circle", { cx: c, cy: 18, r: 30, class: "line" }),
el("circle", { cx: c + 52, cy: 18, r: 4.6, class: "line" }),
el("text", { x: c, y: 24.5, "text-anchor": "middle", class: "tag" }, "DHCP"));
return el("svg", { class: cls, viewBox: `-4 -4 ${W + 8} 162`, role: "img", "aria-label": "Steward" },
el("title", null, "Steward"), letterG, ruleG);
}
// logo returns the Steward logo (artwork/logo.svg, via scripts/logo.py) as
// an SVG of the given size in pixels.
export function logo(size, cls) {
return el("svg", { class: cls || "logo", width: size, height: size, viewBox: logoData.viewBox, "aria-hidden": "true", focusable: "false" },
...logoData.shapes.map(([tag, attrs]) => el(tag, attrs)));
}