panel-redesign: vendor Bulma 1.0.4 and the Tabler icon subset

Stage 0, step 1. Nothing links the new files yet; the old panel is unchanged.

  - static/bulma.min.css: css/bulma.min.css of npm bulma@1.0.4, byte for
    byte (tarball integrity checked against the registry;
    sha256 67fa26df1ca9e95d8f2adc7c04fa1b15fa3d24257470ebc10cc68b9aab914bee).
  - static/tabler-icons.css + tabler-icons.woff2: @tabler/icons-webfont
    3.49.0 subset with fontTools pyftsubset to the 57 icons the mockups use
    (the plan said 58; the count is 57).
  - NOTICE and development.md § Vendored front-end list both, MIT.
  - Roadmap: panel-redesign is agreed (owner, 2026-10-09), 6/27; the entry no
    longer says 29 screens or that Health is undecided.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-09 17:19:23 +03:00
co-authored by Claude Haiku 5.5
parent 049d8c781c
commit 2fa0bef6c2
9 changed files with 294 additions and 11 deletions
+11
View File
@@ -5,6 +5,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
## [Unreleased]
### Added
- panel-redesign (stage 0): Bulma 1.0.4 and a 57-icon subset of Tabler Icons
3.49.0 are vendored under `/static` (MIT, listed in NOTICE). Nothing links
them yet; the old panel is unchanged.
### Changed
- panel-redesign is `agreed` (owner, 2026-10-09); work starts with stage 0,
the component kit.
## [1.9.5] - 2026-10-09
CI-only cut: the release workflow no longer breaks the multi-arch image it
+16
View File
@@ -21,6 +21,22 @@ htmx 2.0.4 (internal/web/view/static/htmx.min.js)
Licence: Zero-Clause BSD (0BSD)
https://github.com/bigskysoftware/htmx
Bulma 1.0.4 (internal/web/view/static/bulma.min.css)
Copyright (c) 2023 Jeremy Thomas
Licence: MIT
https://github.com/jgthms/bulma
Distributed unmodified.
Tabler Icons 3.49.0
Copyright (c) 2020-2026 Paweł Kuna
Licence: MIT
https://github.com/tabler/tabler-icons
Distributed as two files:
internal/web/view/static/tabler-icons.css
internal/web/view/static/tabler-icons.woff2
Webfont subset to the 57 icons the panel uses, made with fontTools
pyftsubset.
IBM Plex Sans / IBM Plex Mono
Copyright © 2017 IBM Corp., with Reserved Font Name "Plex"
Licence: SIL Open Font License 1.1
+3
View File
@@ -180,6 +180,9 @@ the tree are AGPL-3.0-compatible.
|---|---|---|---|
| `internal/web/view/static/htmx.min.js` | 2.0.4 | <https://github.com/bigskysoftware/htmx> | 0BSD |
| `internal/web/view/static/ibm-plex-*.woff2` | latin subset | <https://github.com/IBM/plex> | SIL OFL 1.1 (`OFL.txt` beside the files) |
| `internal/web/view/static/bulma.min.css` | 1.0.4 | <https://github.com/jgthms/bulma> | MIT |
| `internal/web/view/static/tabler-icons.css` | 3.49.0 (57-icon subset) | <https://github.com/tabler/tabler-icons> | MIT |
| `internal/web/view/static/tabler-icons.woff2` | 3.49.0 (57-icon subset) | <https://github.com/tabler/tabler-icons> | MIT |
### E2e module (`test/e2e/go.mod`)
+4 -4
View File
@@ -1,6 +1,6 @@
# Plan: panel-redesign
**Status:** candidate
**Status:** agreed (owner, 2026-10-09)
**Date:** 2026-09-21 (replaces the 2026-09-08 givens)
**Version:** `2.0.0` MAJOR — a breaking release by the owner's decision (§ No compatibility).
@@ -300,7 +300,7 @@ The kit is, in `internal/web/view`:
| `components.go` | the typed input of every partial — `Head{Kicker, Title, Lead, Postmark, Actions}`, `Box{No, Title, End, Help, Variant}`, `Record{Name, Status, Host, Type, Value, InDNS}`, `Fact{Label, Value, Big}`, `SideMenu{Groups}`, `Tag{Status, Label}`, … — so a page passes a struct, not a `dict` it composes itself. A partial's input is part of its definition: components.html documents the look, `components.go` the data |
| template functions | `status_tag` (status → `tag is-… is-light`), `copy_field`, `wbr_at` (`<wbr>` after `@` in table cells) |
| `static/panel.css` | `panel/theme.css` + `shared/brand.css`, no rule the mockups do not have; the old stylesheet is deleted at the end, not merged |
| `static/bulma.min.css`, icons | vendored, pinned by checksum; the icon font subset to the icons the mockups use (58 today), not the whole Tabler set |
| `static/bulma.min.css`, icons | vendored, pinned by checksum; the icon font subset to the icons the mockups use (57 today), not the whole Tabler set |
| `layout.html` | the shell: navbar with the wordmark, perforated edge, sibling strip, user menu, footer; the signed-out variant |
| `GET /server/components` | the kit page, global role only: every partial in every state rendered from fixtures — the mockup `components.html` rebuilt by the real templates |
@@ -461,7 +461,7 @@ reviewed.
- Weakening CSP; a JS bundler; a front-end framework.
## Implementation checklist
No code until the roadmap status is **agreed**. Stages are ordered for
Roadmap status: **agreed** (owner, 2026-10-09). Stages are ordered for
development, not by feature priority (owner, 2026-10-09). Stage 1 touches no
file under `internal/web/view` and may run alongside stage 0; stage 2 starts
only when both are done. Every stage-2 step ends with the evidence of
@@ -477,7 +477,7 @@ only when both are done. Every stage-2 step ends with the evidence of
**Stage 0 — foundation and component kit** (§ Component kit)
- [ ] Vendor Bulma 1.0.4 and the icon subset (MIT) under `/static`, NOTICE and development.md rows, pinned checksum — **Haiku**
- [x] Vendor Bulma 1.0.4 and the icon subset (MIT) under `/static`, NOTICE and development.md rows, pinned checksum — **Haiku**
- [ ] Guard tests from § Enforcement and `TestRoutesFollowNavigation`, red against today's panel where expected, with the `legacyPages` ratchet listing every page; `design-first` CI step including the ratchet and the history-blob check (§ Against resurrected pages) — **Opus**
- [ ] `panel.css` from `panel/theme.css` + `shared/brand.css`; `components.html` partials with typed inputs in `components.go`; `status_tag`, `copy_field`, `wbr_at` — **Sonnet**
- [ ] `layout.html`: navbar, perforated edge, sibling strip, user menu, footer, visibility flags; the signed-out shell — **Sonnet**
+9 -7
View File
@@ -39,7 +39,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
| panel-notifications | E-mail notifications about important events | candidate | — | — |
| delivery-log-storage | Keep a message's `mail.log` lines with its send-log row | candidate | — | — |
| password-reset | Password reset by e-mail | candidate | — | — |
| panel-redesign | Panel redesign (accepted mockups, design contract) | candidate | 5/27 | [plans/panel-redesign.md](plans/panel-redesign.md) |
| panel-redesign | Panel redesign (accepted mockups, design contract) | agreed | 6/27 | [plans/panel-redesign.md](plans/panel-redesign.md) |
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
**Recommended order** (not binding; owner, 2026-10-09: feature order follows
@@ -408,7 +408,7 @@ Not drawn yet.
**Goal:** a panel an operator opens a few times a year and still finds their
way in — the important things in view, details one or two clicks away, one
visual language, no page tuning its own width. The design is drawn and
accepted: 29 screens and a components page under
accepted: 33 screens and a components page under
[docs/assets/panel-redesign/](assets/panel-redesign/index.html) (Bulma base,
postmark / numbered heads / perforated edge, icons). Contract and checklist:
[plans/panel-redesign.md](plans/panel-redesign.md).
@@ -419,10 +419,12 @@ Server) and paths are renamed to match. **No compatibility is kept** (owner,
2026-09-21): no redirects, the migration chain is replaced by one baseline
and 2.0 starts from an empty data directory — the only live installation is
the owner's. Inbound domains become delegable to a
domain administrator, separately from outbound. Four things the mockups show need their own decision and are not
smuggled in with the restyle: queue as a table (`postqueue -j`), inbound
queue, inbound log (rides on inbound-antispam-panel), Server › Health. No CSP
change, no JavaScript widgets, no second framework.
domain administrator, separately from outbound. Server › Health is built
with the restyle (decided 2026-10-09). Three things the mockups show keep
their own decision and are not smuggled in: queue as a table
(`postqueue -j`), inbound queue, inbound log (rides on
inbound-antispam-panel) — until then both queues ship the old content in the
new shell. No CSP change, no JavaScript widgets, no second framework.
**Done when:** every page renders with the component skeleton recorded in
`panel/outlines.json`, the guard tests in the plan are green with no
@@ -434,7 +436,7 @@ of their own — answered by guard tests that land before the templates, a
`design-first` CI step and evidence-based acceptance (plan § Enforcement).
Competes with feature work; Bulma is ~680 kB minified, embedded once.
**Version:** `2.0.0` MAJOR; `candidate` until agreed.
**Version:** `2.0.0` MAJOR; `agreed` (owner, 2026-10-09).
---
File diff suppressed because one or more lines are too long
+247
View File
@@ -0,0 +1,247 @@
/* Tabler Icons 3.49.0 — MIT, https://github.com/tabler/tabler-icons — subset to the 57 icons the panel uses; regenerate, do not hand-edit */
@font-face {
font-family: "tabler-icons";
font-style: normal;
font-weight: 400;
src: url("tabler-icons.woff2") format("woff2");
}
.ti {
font-family: "tabler-icons" !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.ti-activity-heartbeat:before {
content: "\f0db";
}
.ti-address-book:before {
content: "\f021";
}
.ti-alert-circle:before {
content: "\ea05";
}
.ti-alert-triangle:before {
content: "\ea06";
}
.ti-apps:before {
content: "\ebb6";
}
.ti-archive:before {
content: "\ea0b";
}
.ti-arrow-forward-up:before {
content: "\eb78";
}
.ti-arrow-left:before {
content: "\ea19";
}
.ti-arrow-right:before {
content: "\ea1f";
}
.ti-arrows-exchange:before {
content: "\f1f4";
}
.ti-certificate:before {
content: "\ed76";
}
.ti-checklist:before {
content: "\f074";
}
.ti-circle-check:before {
content: "\ea67";
}
.ti-copy:before {
content: "\ea7a";
}
.ti-cpu:before {
content: "\ef8e";
}
.ti-download:before {
content: "\ea96";
}
.ti-eye:before {
content: "\ea9a";
}
.ti-file-export:before {
content: "\ede9";
}
.ti-file-import:before {
content: "\edea";
}
.ti-file-text:before {
content: "\eaa2";
}
.ti-filter:before {
content: "\eaa5";
}
.ti-gauge:before {
content: "\eab1";
}
.ti-heartbeat:before {
content: "\ef92";
}
.ti-help:before {
content: "\eabf";
}
.ti-help-circle:before {
content: "\f91d";
}
.ti-inbox:before {
content: "\eac4";
}
.ti-key:before {
content: "\eac7";
}
.ti-list-details:before {
content: "\ef40";
}
.ti-lock:before {
content: "\eae2";
}
.ti-logout:before {
content: "\eba8";
}
.ti-mail:before {
content: "\eae5";
}
.ti-mail-forward:before {
content: "\eeac";
}
.ti-mail-pause:before {
content: "\f941";
}
.ti-plug:before {
content: "\ebd9";
}
.ti-plug-connected:before {
content: "\f00a";
}
.ti-plug-connected-x:before {
content: "\f0a0";
}
.ti-plus:before {
content: "\eb0b";
}
.ti-refresh:before {
content: "\eb13";
}
.ti-reload:before {
content: "\f3ae";
}
.ti-report-analytics:before {
content: "\eecb";
}
.ti-send:before {
content: "\eb1e";
}
.ti-server-2:before {
content: "\f07c";
}
.ti-server-cog:before {
content: "\f321";
}
.ti-settings:before {
content: "\eb20";
}
.ti-shield-check:before {
content: "\eb22";
}
.ti-stack-2:before {
content: "\eef7";
}
.ti-trash:before {
content: "\eb41";
}
.ti-upload:before {
content: "\eb47";
}
.ti-user:before {
content: "\eb4d";
}
.ti-user-circle:before {
content: "\ef68";
}
.ti-user-plus:before {
content: "\eb4b";
}
.ti-users:before {
content: "\ebf2";
}
.ti-world:before {
content: "\eb54";
}
.ti-world-check:before {
content: "\f9db";
}
.ti-world-download:before {
content: "\ef8a";
}
.ti-world-upload:before {
content: "\ef8b";
}
.ti-x:before {
content: "\eb55";
}
Binary file not shown.
+1
View File
@@ -29,6 +29,7 @@ func TestStaticAssetsCarryETag(t *testing.T) {
// thing the panel serves and the ones a browser is most willing to keep.
"ibm-plex-sans.woff2", "ibm-plex-mono-400.woff2", "ibm-plex-mono-600.woff2",
"OFL.txt",
"bulma.min.css", "tabler-icons.css", "tabler-icons.woff2",
} {
rec := serveStatic("/static/"+name, nil)
if rec.Code != http.StatusOK {