diff --git a/CHANGELOG.md b/CHANGELOG.md index fd2f209..e001c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,15 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version - panel: `/` redirects domain administrators to `/domains`; global users still land on `/status`. Navigation hides global-only sections for domain administrators. +- panel: **visual style** brought in line with the SelfPost mark — brick accent + and warm paper in place of the blue-on-cool-grey defaults, IBM Plex Sans and + IBM Plex Mono served by the panel itself, squarer corners, and column + headings, status badges and small labels set in the mono face. Light and dark + schemes both keep their contrast; no page, control or workflow changed. A page + whose only card is the narrow one now takes its heading and footer down to the + card's width instead of splitting them across the wider measure. The three + WOFF2 files add ~76 KB to the image and are served from the panel's own origin, + so the Content-Security-Policy is unchanged (`default-src 'self'`). ## [1.1.0] - 2026-08-10 diff --git a/NOTICE b/NOTICE index a38602b..ca4c34c 100644 --- a/NOTICE +++ b/NOTICE @@ -11,21 +11,28 @@ Corresponding Source for the published releases is at: If you modify SelfPost and let users interact with it over a network, AGPL-3.0 §13 requires you to offer them the Corresponding Source of your modified version. Update the Source link in the panel footer -(internal/web/templates/layout.html) so it points at your sources. +(internal/web/view/templates/layout.html) so it points at your sources. Third-party software included in this distribution ---------------------------------------------------- -htmx 2.0.4 (internal/web/static/htmx.min.js) +htmx 2.0.4 (internal/web/view/static/htmx.min.js) Copyright (c) Big Sky Software Licence: Zero-Clause BSD (0BSD) https://github.com/bigskysoftware/htmx -IBM Plex Sans / IBM Plex Mono (wordmarks in SVG logos only) +IBM Plex Sans / IBM Plex Mono Copyright © 2017 IBM Corp., with Reserved Font Name "Plex" Licence: SIL Open Font License 1.1 - The shipped SVGs use outlined paths derived from these fonts; the - font files themselves are not distributed. + https://github.com/IBM/plex + Distributed unmodified as three latin-subset WOFF2 files served by the + control panel: + internal/web/view/static/ibm-plex-sans.woff2 (variable, 100-700) + internal/web/view/static/ibm-plex-mono-400.woff2 + internal/web/view/static/ibm-plex-mono-600.woff2 + Subsetting and WOFF2 packaging by Fontsource (fontsource.org), from + the Google Fonts distribution. The wordmarks in the shipped SVG logos + are outlined paths derived from the same fonts. Go module dependencies are listed with their licences in docs/development.md § External libraries. Debian packages in the diff --git a/internal/web/view/static/ibm-plex-mono-400.woff2 b/internal/web/view/static/ibm-plex-mono-400.woff2 new file mode 100644 index 0000000..a6c77d6 Binary files /dev/null and b/internal/web/view/static/ibm-plex-mono-400.woff2 differ diff --git a/internal/web/view/static/ibm-plex-mono-600.woff2 b/internal/web/view/static/ibm-plex-mono-600.woff2 new file mode 100644 index 0000000..59e4f52 Binary files /dev/null and b/internal/web/view/static/ibm-plex-mono-600.woff2 differ diff --git a/internal/web/view/static/ibm-plex-sans.woff2 b/internal/web/view/static/ibm-plex-sans.woff2 new file mode 100644 index 0000000..b757bc5 Binary files /dev/null and b/internal/web/view/static/ibm-plex-sans.woff2 differ diff --git a/internal/web/view/static/panel.css b/internal/web/view/static/panel.css index e2e155c..722819e 100644 --- a/internal/web/view/static/panel.css +++ b/internal/web/view/static/panel.css @@ -2,7 +2,36 @@ layout so the panel's Content-Security-Policy can be a plain "default-src 'self'" with no inline-style exemption. Any rule added here must therefore stay here: an inline style="..." attribute in a - template is blocked by that policy and silently does nothing. */ + template is blocked by that policy and silently does nothing. + + The palette and the type below come from the mark's own approval sheet + (docs/assets/selfpost-proof.html) — brick, warm paper, IBM Plex. Before it + the panel was a default blue-on-cool-grey admin theme with the stamp pasted + onto it, and the two read as different pieces of work. */ + +/* The fonts ship with the panel rather than being asked for by name. Two + reasons, and the second is the one that matters. A named family resolves to + whatever the operator's machine has, so the mark — Plex converted to + outlines — would be the only Plex on the page. And ui-monospace is Consolas, + SF Mono or DejaVu Sans Mono depending on that machine, each with its own + advance width: the six-column send log and the mail.log tables would lay out + differently for every operator. Served from the panel's own origin, so the + policy above already covers them and needs no font-src of its own. + One variable file for the sans (100–700 in 46 KB, less than the four static + weights it replaces); mono has no variable release, so it is two files. + swap, so a cold load shows the system stack for a frame instead of nothing. */ +@font-face { + font-family: "IBM Plex Sans"; font-style: normal; font-weight: 100 700; + font-display: swap; src: url("/static/ibm-plex-sans.woff2") format("woff2"); +} +@font-face { + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; + font-display: swap; src: url("/static/ibm-plex-mono-400.woff2") format("woff2"); +} +@font-face { + font-family: "IBM Plex Mono"; font-style: normal; font-weight: 600; + font-display: swap; src: url("/static/ibm-plex-mono-600.woff2") format("woff2"); +} /* Colour tokens. Light values live on :root; the dark media query below reassigns the same names rather than re-declaring every rule that uses @@ -10,39 +39,57 @@ property already carries the right value for the active scheme. */ :root { color-scheme: light dark; - --bg: #f6f7f9; --fg: #1b1f24; + --font-sans: "IBM Plex Sans", system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + --bg: #f4f2ed; --fg: #12161c; + --muted: #6b7684; /* secondary text: .muted, th, timestamps, footers */ --card-bg: #fff; - --border: #e2e5e9; /* dividers: card, table, app, nav, code, encrypt-fields */ - --control-border: #cfd4da; /* input, select, textarea, action buttons */ + --border: #dedcd7; /* dividers: card, table, app, nav, code, encrypt-fields */ + --control-border: #cbc8c1; /* input, select, textarea, action buttons */ --input-bg: #fff; - --code-bg: #f0f2f4; - --surface-bg: #eef1f5; --surface-bg-hover: #e2e7ee; --surface-open-bg: #dde3ec; - --nav-active-bg: #e6ebf5; - --flash-bg: #ecfdf3; --flash-border: #abefc6; --flash-fg: #067647; - --credential-bg: #fffbeb; --credential-border: #f5c518; - --danger-bg: #fef3f2; --danger-border: #fecdca; --danger-fg: #b42318; --danger-bg-hover: #fee4e2; - --st-ok-bg: #ecfdf3; --st-ok-fg: #067647; --st-ok-border: #abefc6; - --st-warn-bg: #fffaeb; --st-warn-fg: #b54708; --st-warn-border: #fedf89; - --st-error-bg: #fef3f2; --st-error-fg: #b42318; --st-error-border: #fecdca; - --st-unknown-bg: #f0f2f4; --st-unknown-fg: #6b7280; --st-unknown-border: #e2e5e9; + --code-bg: #efede9; + --surface-bg: #eae7e0; --surface-bg-hover: #e0dcd3; --surface-open-bg: #d8d3c8; + --nav-active-bg: #ede4de; + /* Brick, the mark's own colour, is the panel's accent — but as two values, + not one. A filled control carries --accent-fill with white on it; a link or + an active entry carries --accent-text, because the fill on a page + background is a colour to sit behind text, not to be text. In the light + scheme the two coincide. */ + --accent-fill: #7a3b2e; --accent-fill-hover: #632f25; --accent-text: #7a3b2e; + --on-accent: #fff; + --flash-bg: #edf6ef; --flash-border: #c2e0cc; --flash-fg: #2c6b43; + --credential-bg: #fcf6e4; --credential-border: #e0c874; + --danger-fill: #b42318; --danger-fill-hover: #912018; + --danger-bg: #fbedea; --danger-border: #efccc4; --danger-fg: #b42318; --danger-bg-hover: #f7dfda; + --st-ok-bg: #edf6ef; --st-ok-fg: #2c6b43; --st-ok-border: #c2e0cc; + --st-warn-bg: #fbf2e2; --st-warn-fg: #8a5510; --st-warn-border: #ebd5a6; + --st-error-bg: #fbedea; --st-error-fg: #b42318; --st-error-border: #efccc4; + --st-unknown-bg: #efede9; --st-unknown-fg: #6b7684; --st-unknown-border: #dedcd7; } @media (prefers-color-scheme: dark) { :root { - --bg: #14171a; --fg: #e6e8eb; - --card-bg: #1d2125; - --border: #2b3138; - --control-border: #2b3138; - --input-bg: #14171a; - --code-bg: #14171a; - --surface-bg: #22262b; --surface-bg-hover: #2b3138; --surface-open-bg: #313841; - --nav-active-bg: #22303f; - --flash-bg: #0d2818; --flash-border: #1a5336; --flash-fg: #75d99b; - --credential-bg: #2a2408; --credential-border: #6b5a10; - --danger-bg: #2d1211; --danger-border: #6b201a; --danger-fg: #f5a29b; --danger-bg-hover: #3d1a18; - --st-ok-bg: #0d2818; --st-ok-fg: #75d99b; --st-ok-border: #1a5336; - --st-warn-bg: #2e2308; --st-warn-fg: #f5c86b; --st-warn-border: #6b5210; - --st-error-bg: #2d1211; --st-error-fg: #f5a29b; --st-error-border: #6b201a; - --st-unknown-bg: #22262b; --st-unknown-fg: #9aa3ad; --st-unknown-border: #2b3138; + --bg: #16181b; --fg: #e9e6e0; + --muted: #9aa1a9; + --card-bg: #1d2024; + --border: #2c2f34; + --control-border: #3a3e44; + --input-bg: #14161a; + --code-bg: #14161a; + --surface-bg: #23262b; --surface-bg-hover: #2c3036; --surface-open-bg: #343941; + --nav-active-bg: #2a1f1b; + /* Brick as drawn is about 2:1 against this background — a link nobody can + read. The fill lightens just enough to keep white on it above 4.5:1 + (6.9:1 as set), and the text value lightens further to clear the same bar + as text on the page. This is where the two values earn their keep. */ + --accent-fill: #8e4535; --accent-fill-hover: #a0503e; --accent-text: #ce7b66; + --flash-bg: #132318; --flash-border: #22452f; --flash-fg: #7fcb9b; + --credential-bg: #26210d; --credential-border: #5e5013; + --danger-fill: #9b2c22; --danger-fill-hover: #b0342a; + --danger-bg: #2a1412; --danger-border: #5e2721; --danger-fg: #eb9b92; --danger-bg-hover: #381a17; + --st-ok-bg: #132318; --st-ok-fg: #7fcb9b; --st-ok-border: #22452f; + --st-warn-bg: #2a2109; --st-warn-fg: #e5be72; --st-warn-border: #5e4b12; + --st-error-bg: #2a1412; --st-error-fg: #eb9b92; --st-error-border: #5e2721; + --st-unknown-bg: #23262b; --st-unknown-fg: #9aa1a9; --st-unknown-border: #2c2f34; } } * { box-sizing: border-box; } @@ -54,7 +101,7 @@ read as the layout jumping. */ html { scrollbar-gutter: stable; } body { - font: 15px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + font: 400 15px/1.5 var(--font-sans); margin: 0; padding: 2rem 1rem; background: var(--bg); color: var(--fg); } /* The two columns — navigation on the left, page on the right — centred as a @@ -93,7 +140,11 @@ main.wide > * { max-width: none; } /* Longhands, not the margin shorthand: the shorthand would re-zero the auto side margins above and pin the heading to the column's edge, a measure's worth to the left of the card under it. */ -h1 { font-size: 1.4rem; margin-top: 0; margin-bottom: 1rem; } +/* Light rather than bold, which is the sheet's own setting for the name and + the one thing a variable font makes free. It is the page's title, not an + alarm: the size already says which line is the heading, and at 700 it shouted + over the card under it. */ +h1 { font-size: 1.5rem; font-weight: 300; letter-spacing: -0.01em; margin-top: 0; margin-bottom: 1rem; } /* The full mark, on the two pages that have no navigation bar to carry the compact one. It takes the column's width so its edges line up with the card below it, capped at that column's own 24rem; height stays automatic because @@ -101,15 +152,19 @@ h1 { font-size: 1.4rem; margin-top: 0; margin-bottom: 1rem; } The subtitle stops resolving below 280px, which a 320px viewport still clears once the body's padding is taken off. */ .mark { display: block; width: 100%; max-width: 24rem; height: auto; margin-bottom: 1.4rem; } +/* The radius comes down from 10px across the panel: the mark is a stamp with a + square edge and a hairline, and a card rounded like a phone widget sat in + front of it as a different kind of object. 6px on the containers, 5px on the + controls inside them. */ .card { - background: var(--card-bg); border: 1px solid var(--border); border-radius: 10px; + background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; padding: 1.5rem; margin: 0 auto; } .card.narrow { max-width: 24rem; } label { display: block; font-weight: 600; margin: 0.9rem 0 0.3rem; } input { - width: 100%; padding: 0.55rem 0.7rem; font-size: 1rem; - border: 1px solid var(--control-border); border-radius: 6px; background: var(--input-bg); color: inherit; + width: 100%; padding: 0.55rem 0.7rem; font-family: inherit; font-size: 1rem; + border: 1px solid var(--control-border); border-radius: 5px; background: var(--input-bg); color: inherit; } /* One vocabulary for actions. Anything that performs an action looks like a button: a