Capture the layout and component decisions for a future panel restyle so mockups start from a frozen vocabulary instead of per-page widths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,6 +23,10 @@ document is not part of the release.
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Roadmap item and givens for a panel visual redesign
|
||||||
|
([docs/plans/panel-redesign.md](docs/plans/panel-redesign.md)) — layout
|
||||||
|
grammar (`measure` / `fill` plus Web Awesome layout CSS), no JS component
|
||||||
|
library, new mockups rather than `docs/assets/panel-ui/`. Status: candidate.
|
||||||
- Roadmap item and plan for `/preflight` installation check page
|
- Roadmap item and plan for `/preflight` installation check page
|
||||||
([docs/plans/preflight.md](docs/plans/preflight.md)) — instance-level
|
([docs/plans/preflight.md](docs/plans/preflight.md)) — instance-level
|
||||||
infrastructure verification (rDNS, TLS, ports, HELO banner, proxy headers,
|
infrastructure verification (rDNS, TLS, ports, HELO banner, proxy headers,
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# Plan: panel-redesign
|
||||||
|
|
||||||
|
**Status:** candidate
|
||||||
|
**Date:** 2026-09-08
|
||||||
|
**Version:** `1.x` MINOR once implementation is agreed; this file is givens, not a
|
||||||
|
build plan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What this file is
|
||||||
|
|
||||||
|
Source data for a visual redesign of the control panel. Mockups have not been
|
||||||
|
drawn yet. Implementation in `internal/web` waits on those mockups and on
|
||||||
|
roadmap status **agreed**.
|
||||||
|
|
||||||
|
HTML click-throughs in [docs/assets/panel-ui/](../assets/panel-ui/index.html)
|
||||||
|
are **not** the brief. They were an earlier layout experiment; new screens are
|
||||||
|
drawn from this file's vocabulary, not copied from that folder.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Stop hand-tuning every card on every page to hide meaningless empty space.
|
||||||
|
Operators should get a panel whose width rules are the same on Status, a
|
||||||
|
delete-confirm, and a six-column log: a form stays at a reading measure, ops
|
||||||
|
content uses the remaining column, and a card fills its cell.
|
||||||
|
|
||||||
|
This is a visual and CSS-grammar change. Routes, POST behaviour, and htmx
|
||||||
|
fragments stay as they are unless a later mockup proves a control must move.
|
||||||
|
|
||||||
|
## Pain (why)
|
||||||
|
|
||||||
|
[panel.css](../../internal/web/view/static/panel.css) already juggles several
|
||||||
|
widths (`64rem` column, `48rem` measure, `main.wide`, `.card.narrow`,
|
||||||
|
`page-login`) and two names for one pairing (`.split` everywhere, `.pair` on
|
||||||
|
DMARC). Each new screen picks a width by eye. On a wide monitor that leaves
|
||||||
|
voids; stretching a single input to the window is unreadable. The fix is a
|
||||||
|
layout grammar, not another button library.
|
||||||
|
|
||||||
|
## Delivery constraints (fixed unless product says otherwise)
|
||||||
|
|
||||||
|
The panel is not an SPA. Changing any of these needs an explicit decision, not
|
||||||
|
a mockup flourish:
|
||||||
|
|
||||||
|
| Constraint | Where |
|
||||||
|
|---|---|
|
||||||
|
| Go `html/template`, server-rendered pages | [internal/web/view/](../../internal/web/view/) |
|
||||||
|
| Vendored htmx 2.x for polling fragments | [layout.html](../../internal/web/view/templates/layout.html), [development.md](../development.md) § Vendored front-end |
|
||||||
|
| Assets embedded (`//go:embed`), no CDN at runtime | [view.go](../../internal/web/view/view.go) |
|
||||||
|
| CSP `default-src 'self'` — no inline script or `style=""` | [security.go](../../internal/web/security.go), `TestNoTemplateUsesInlineScriptOrStyle` |
|
||||||
|
| State changes are native HTML POST | templates under `internal/web/view/templates/` |
|
||||||
|
| AGPL-3.0; new front-end must be MIT-or-better and listed in NOTICE | [NOTICE](../../NOTICE), [development.md](../development.md) |
|
||||||
|
|
||||||
|
JavaScript is not a product requirement. [security.md](../security.md) records
|
||||||
|
progressive enhancement so **authorization does not depend on JS** (origin
|
||||||
|
check, session, RBAC). `data-confirm` is deliberately not a security boundary.
|
||||||
|
The redesign may use JS for chrome (copy, `<dialog>`). It must not make a
|
||||||
|
script the only way to submit a form.
|
||||||
|
|
||||||
|
Brand carriers today: stamp, brick, IBM Plex — [product.md](../product.md) and
|
||||||
|
[docs/assets/selfpost-proof.html](../assets/selfpost-proof.html). New mockups
|
||||||
|
may restyle; they must not silently drop the mark or invent a second product
|
||||||
|
look.
|
||||||
|
|
||||||
|
## Screens in scope (information architecture)
|
||||||
|
|
||||||
|
Same pages as the live panel. Visibility flags (inbound, DMARC, global vs
|
||||||
|
domain-admin) stay as in [web.go](../../internal/web/web.go).
|
||||||
|
|
||||||
|
| Kind | Pages |
|
||||||
|
|---|---|
|
||||||
|
| Signed-out | login, setup |
|
||||||
|
| Global ops | Status (plus polled fragment), mail queue, system log, backup, users |
|
||||||
|
| Shared | domains, domain detail, deliveries, delivery, help, settings |
|
||||||
|
| Optional | inbound (+ domain, delete), DMARC hub / domain / report |
|
||||||
|
| Confirms / forms | domain delete, inbound delete, user create/edit/delete |
|
||||||
|
|
||||||
|
## Layout layer (decided)
|
||||||
|
|
||||||
|
**Hybrid.** Page-level regions are ours. In-page composition is vendored Web
|
||||||
|
Awesome **layout CSS only** (MIT Core), not the component library and not the
|
||||||
|
full `utilities.css` barrel.
|
||||||
|
|
||||||
|
Every Layout ([every-layout.dev](https://every-layout.dev/)) is the *school*
|
||||||
|
(Stack, Cluster, Sidebar, Grid). It is a paid book; redistribution is
|
||||||
|
forbidden. Do not vendor it, do not buy it for the image, do not put their
|
||||||
|
custom elements in the tree. Web Awesome `layout.css` is the MIT packaging of
|
||||||
|
those recipes (`wa-stack` ≈ Stack, `wa-cluster` ≈ Cluster, `wa-flank` ≈
|
||||||
|
Sidebar, `wa-grid` ≈ Grid/Switcher).
|
||||||
|
|
||||||
|
### Regions (our CSS)
|
||||||
|
|
||||||
|
Shell: navigation column plus content that takes the rest (today's `.shell`,
|
||||||
|
without a centred `64rem` well that leaves side voids).
|
||||||
|
|
||||||
|
Inside the content column, **two modes only**. A page must not invent a third
|
||||||
|
width:
|
||||||
|
|
||||||
|
- **`measure`** — one readable form (login, setup, delete confirm, user form).
|
||||||
|
Cap around `42rem`, **left-aligned**. Fields fill the measure, not the
|
||||||
|
window. Empty space to the right of a form on a wide monitor is acceptable.
|
||||||
|
- **`fill`** — ops: Status, tables, logs, queue, DNS, paired cards. One column
|
||||||
|
cap for all fill pages (pick a single value in mockups, e.g. ~`90rem`; do
|
||||||
|
not keep both `48rem` and `64rem`). A card fills its grid cell.
|
||||||
|
|
||||||
|
There is no `main.wide` or `.card.narrow` as a width exception. A narrow card
|
||||||
|
lives in `measure`. A wide table lives in `fill`.
|
||||||
|
|
||||||
|
### In-page (vendored WA CSS)
|
||||||
|
|
||||||
|
Embed under `/static`, same as htmx — never a runtime CDN.
|
||||||
|
|
||||||
|
| Take | Do not take |
|
||||||
|
|---|---|
|
||||||
|
| `utilities/layout.css` | barrel `utilities.css` |
|
||||||
|
| `utilities/gap.css` | `themes/default.css`, `native.css` |
|
||||||
|
| `align-items.css`, `justify-content.css`, `flex-wrap.css` if a mockup needs them | variants, prose, FOUCE, JS, icons, Pro |
|
||||||
|
| A short `:root` stub for `--wa-space-*` only | their `layers.css` (it styles `wa-page`) |
|
||||||
|
|
||||||
|
Declare `@layer wa-utilities` locally. One spacing scale for the whole layer
|
||||||
|
(map to today's `1.2rem` gaps if that looks right; do not keep a second scale).
|
||||||
|
|
||||||
|
`.card` with slots (head / body / actions) stays ours so a `wa-grid` cell can
|
||||||
|
stretch. Do not use `wa-card`.
|
||||||
|
|
||||||
|
### Class vocabulary
|
||||||
|
|
||||||
|
**Paired cards are `wa-grid`, not `wa-split`.** In Web Awesome, `wa-split` is
|
||||||
|
space-between (title ‖ action). `wa-grid` is `auto-fit` + `--min-column-size`,
|
||||||
|
which is today's `.split { minmax(22rem, 1fr) }`. Freeze `--min-column-size`
|
||||||
|
once (same order as `22rem`); do not retune it per page.
|
||||||
|
|
||||||
|
| Class | Use |
|
||||||
|
|---|---|
|
||||||
|
| `measure` / `fill` | page region only, never inside a card |
|
||||||
|
| `wa-stack` | form or card body; children stretch on the block axis |
|
||||||
|
| `wa-cluster` | button rows, tags, Copy beside a value |
|
||||||
|
| `wa-grid` | two or more equal cards or field columns |
|
||||||
|
| `wa-split` | card header: title ‖ help/action; label ‖ value |
|
||||||
|
| `wa-flank` | rare: fixed + fluid (icon beside a field) |
|
||||||
|
| `wa-gap-*` | change a gap; not a substitute for a region |
|
||||||
|
|
||||||
|
`wa-frame` is for media boxes; the panel has none — omit from mockups.
|
||||||
|
|
||||||
|
One child in `wa-grid` occupies the row (`auto-fit`, not `auto-fill`): a
|
||||||
|
domain-admin Settings card must not leave an empty second column.
|
||||||
|
|
||||||
|
**Forbidden in mockups and later in templates:** a one-off `max-width` “for
|
||||||
|
this page”; a second name for the same pairing (today `.pair` vs `.split`);
|
||||||
|
`margin-inline: auto` on a card inside a grid (that already collapsed `.split`
|
||||||
|
tracks).
|
||||||
|
|
||||||
|
**Ready when:** Status (`fill` + `wa-grid` pairs) and login (`measure` +
|
||||||
|
`wa-stack`) can be drawn with no extra width rule. If a third screen wants a
|
||||||
|
new `max-width`, the vocabulary is unfinished.
|
||||||
|
|
||||||
|
## Components (decided)
|
||||||
|
|
||||||
|
**No JavaScript component library.** Pico, Bootstrap JS, DaisyUI, FAST,
|
||||||
|
Shoelace, and Web Awesome custom elements (`wa-button`, `wa-dialog`,
|
||||||
|
`wa-drawer`, `wa-card`) are out. They do not fix empty cards; they add Lit or
|
||||||
|
another runtime, Shadow DOM, CSP risk, and friction with htmx swaps.
|
||||||
|
|
||||||
|
What the live panel actually uses — and what mockups should assume:
|
||||||
|
|
||||||
|
| Need | Source |
|
||||||
|
|---|---|
|
||||||
|
| Button, input, select, textarea, table | native HTML, our CSS (brick / Plex) |
|
||||||
|
| Card | our `.card` + slots |
|
||||||
|
| Flash, status tint | our CSS |
|
||||||
|
| Help / application panels | CSS disclosure (checkbox today); or `<details>` / Popover if a mockup prefers |
|
||||||
|
| Destructive confirm | `data-confirm` / `window.confirm`, plus existing confirm pages for high blast radius |
|
||||||
|
| Overlay, if a mockup truly needs one | HTML [`<dialog>`](https://developer.mozilla.org/docs/Web/HTML/Element/dialog), not `wa-dialog` |
|
||||||
|
| Copy-to-clipboard | existing `panel.js` |
|
||||||
|
|
||||||
|
There is no combobox, date picker, tab set, tree, or chart in the panel. If a
|
||||||
|
future mockup invents a widget native HTML cannot express, choose a library
|
||||||
|
then, for that widget only.
|
||||||
|
|
||||||
|
## Out of scope for this item
|
||||||
|
|
||||||
|
- New panel features, routes, or copy (those are other roadmap rows).
|
||||||
|
- Weakening CSP to accommodate a widget library.
|
||||||
|
- Web Awesome Pro, Font Awesome Kit, a JS bundler for Lit.
|
||||||
|
- Replacing htmx with a front-end framework.
|
||||||
|
- Implementing `docs/assets/panel-ui/` as-is.
|
||||||
|
|
||||||
|
## Next (not this file's job)
|
||||||
|
|
||||||
|
1. Draw new mockups using only the vocabulary above.
|
||||||
|
2. After mockups are accepted and this item is **agreed**: vendor the WA CSS
|
||||||
|
files, NOTICE / development.md rows, then restyle templates without changing
|
||||||
|
behaviour.
|
||||||
|
|
||||||
|
## Implementation checklist
|
||||||
|
|
||||||
|
None until mockups exist and the roadmap status is **agreed**. Do not start
|
||||||
|
vendoring or rewriting `internal/web` from this file alone.
|
||||||
@@ -36,6 +36,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
|
|||||||
| template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — |
|
| template-data-typing | Typed structs for template data instead of `map[string]any` | candidate | — | — |
|
||||||
| structured-logging | `log/slog` with levels and fields | candidate | — | — |
|
| structured-logging | `log/slog` with levels and fields | candidate | — | — |
|
||||||
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | candidate | — | — |
|
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | candidate | — | — |
|
||||||
|
| panel-redesign | Panel visual redesign (layout grammar) | candidate | — | [plans/panel-redesign.md](plans/panel-redesign.md) |
|
||||||
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
|
| schema-squash | Squash SQLite migrations into a 2.x baseline | **2.x** | — | — |
|
||||||
|
|
||||||
**Recommended order** (not binding): the next feature is
|
**Recommended order** (not binding): the next feature is
|
||||||
@@ -294,6 +295,31 @@ and tagging it would only delete cross-platform test coverage.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## panel-redesign
|
||||||
|
|
||||||
|
**Goal:** a panel layout grammar so screens stop inventing per-page widths to
|
||||||
|
hide empty space. Forms stay on a reading measure; ops pages use the remaining
|
||||||
|
column; cards fill their cells. Givens (hybrid WA layout CSS + own `measure` /
|
||||||
|
`fill`, no JS component library, new mockups rather than
|
||||||
|
[docs/assets/panel-ui/](assets/panel-ui/index.html)):
|
||||||
|
[plans/panel-redesign.md](plans/panel-redesign.md).
|
||||||
|
|
||||||
|
**Boundary:** visual CSS and templates after mockups. No new routes or mail-path
|
||||||
|
behaviour. Not a front-end framework, not Web Awesome components, not Pico /
|
||||||
|
Bootstrap. The older HTML mockups in `docs/assets/panel-ui/` are not the brief.
|
||||||
|
|
||||||
|
**Done when:** mockups exist that use only the frozen vocabulary, then — once
|
||||||
|
this item is agreed — the live panel matches them without one-off `max-width`
|
||||||
|
exceptions. The givens file itself is not “done”.
|
||||||
|
|
||||||
|
**Dependencies / risks:** competes with feature work for attention; easy to
|
||||||
|
start restyling `internal/web` before the vocabulary is proven on paper.
|
||||||
|
Vendoring WA CSS needs NOTICE / licence rows. CSP stays `default-src 'self'`.
|
||||||
|
|
||||||
|
**Version:** `1.x` MINOR when implementation is agreed; `candidate` until then.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## schema-squash
|
## schema-squash
|
||||||
|
|
||||||
**Goal:** when 2.x is cut, stop shipping the 1.x migration chain in the binary
|
**Goal:** when 2.x is cut, stop shipping the 1.x migration chain in the binary
|
||||||
|
|||||||
Reference in New Issue
Block a user