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
|
||||
|
||||
- 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
|
||||
([docs/plans/preflight.md](docs/plans/preflight.md)) — instance-level
|
||||
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 | — | — |
|
||||
| structured-logging | `log/slog` with levels and fields | 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** | — | — |
|
||||
|
||||
**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
|
||||
|
||||
**Goal:** when 2.x is cut, stop shipping the 1.x migration chain in the binary
|
||||
|
||||
Reference in New Issue
Block a user