docs: panel header, type scale, account e-mail and the fixes of a full review
test / test (push) Canceled after 0s

Header. The navbar carries the wordmark instead of the stamp - at nav
size the stamp dissolves into the brick bar - and under the bar runs the
stamp's paper margin with the perforation along its outer edge: round
holes, flat teeth, ink hairline, 8px under a 60px bar as on the mark.
docs/assets/selfpost-wordmark.svg and -ink are the stamp's own outlines;
the header comparison sheet is removed.

Type and tables. About 40 % of the text measured under 13px. Body is
16px with two tokens under it, 14px for supporting text and 13px for
upper-case mono labels and tags; Bulma's small sizes are overridden or
dropped (sp-small replaces is-size-7, is-small only on a progress bar).
Every table sits in a table-container, addresses break after the @, and
all screens fit without scrolling at 1280px and up.

Contrast. A sweep of every text node against its real ground found the
solid red button at 3:1, the muted grey at 4.1:1, the amber postmark and
Bulma's menu labels just under 4.5. All 33 screens now measure 4.5:1 or
better. The sweep is kept as panel/audit.js and its clean output joins
the evidence a restyle step must show.

Account and roles. A user's e-mail is its own field (users.email in the
2.0 baseline), edited under Account and seen by the global user on the
user form and list. The DMARC default report address is a user's
setting, not the server's: it moves to Account with its authorization
record, and a domain follows the default of a named user. Roles are
labelled by reach - global, domain - and each assignment list gains All,
a per-user flag that covers domains added later. Account gains
Notifications for the new roadmap candidate panel-notifications;
password-reset is added as a candidate too.

Also: the inbound spam filter is visible where a domain user looks (a
box on each inbound domain, a tag on the list, its socket on Health);
the health row is compact again and splits 6/3/2; a read-only value is
as tall as its Copy button; Preflight and Account lose boxes that only
filled space, and the plan's layout rule and contract now forbid them.

check.py gains the type floor, the table-container rule and the is-small
rule, and no longer counts min-width: 0 as setting a width.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
mix
2026-09-22 00:05:32 +03:00
co-authored by Claude Fable 5.1
parent 4921416384
commit d83d326217
72 changed files with 970 additions and 624 deletions
+16 -4
View File
@@ -25,9 +25,9 @@ VOID = {"input", "img", "br", "hr", "meta", "link"}
BULMA = set("""
box button buttons checkbox column columns control field file file-cta file-input file-label
file-name has-addons has-icons-left has-name has-text-danger help icon input label menu menu-label
menu-list notification progress select table tag textarea title
menu-list notification progress select table table-container tag textarea title
is-3 is-4 is-5 is-6 is-7 is-9 is-active is-danger is-expanded is-fullwidth is-gapless is-half
is-hoverable is-left is-light is-primary is-size-7 is-small is-success is-text is-warning
is-hoverable is-left is-light is-primary is-small is-success is-text is-warning
mb-1 mb-2 mb-3 mb-4 ml-2 mt-4 mt-5
""".split())
# Classes the shell (build.py, later layout.html) owns; screens never write them.
@@ -111,7 +111,7 @@ def box_kind(box):
kinds.append(name)
break
else:
kinds.append("table" if k.tag == "table" else k.tag)
kinds.append("table" if k.tag == "table" or k.has("table-container") else k.tag)
out = []
for k in kinds:
if not out or out[-1].split("×")[0] != k:
@@ -159,7 +159,14 @@ def main():
errors.append(f"theme.css: !important in `{sel}`")
if re.search(r"(^|[\s,>+~])#[a-z]", sel):
errors.append(f"theme.css: id selector `{sel}` — style components, not one element on one page")
if re.search(r"(?<![-\w])(max-width|min-width|width)\s*:", body) and not any(sel.startswith(w) or w in sel for w in WIDTH_OK):
# The type floor: 13px. Icons (.ti) and the postmark imprint are not running text.
for value, unit in re.findall(r"font-size\s*:\s*([\d.]+)(rem|em|px)", body):
floor = {"rem": 0.8125, "em": 0.85, "px": 13}[unit]
if float(value) < floor and ".sp-postmark" not in sel and ".ti" not in sel:
errors.append(f"theme.css: `{sel}` sets font-size {value}{unit}, below the 13px floor — use --sp-fs-xs or --sp-fs-s")
# `min-width: 0` sizes nothing: it only lets a flex or grid child shrink so its text can be clipped.
sized = re.sub(r"min-width\s*:\s*0\s*(;|$)", "", body)
if re.search(r"(?<![-\w])(max-width|min-width|width)\s*:", sized) and not any(sel.startswith(w) or w in sel for w in WIDTH_OK):
errors.append(f"theme.css: `{sel}` sets a width; widths come from the page layout (WIDTH_OK)")
used, documented, outlines = set(), set(), {}
@@ -181,6 +188,8 @@ def main():
errors.append(f"{name}: `{c}` is not defined in theme.css")
elif c == "ti" or c.startswith("ti-"):
continue
elif c == "is-small" and "progress" not in n.classes:
errors.append(f"{name}: `is-small` on <{n.tag}> — controls keep the normal size; only a progress bar is small")
elif c not in BULMA:
errors.append(f"{name}: class `{c}` is neither in the Bulma subset nor an sp- component")
if bare:
@@ -193,6 +202,9 @@ def main():
marks = root.find("sp-postmark")
if name != "components" and (len(marks) > 1 or any(not m.parent.has("sp-head") for m in marks)):
errors.append(f"{name}: at most one postmark, inside page_head")
for tbl in (n for n in root.walk() if n.tag == "table"):
if not tbl.parent.has("table-container"):
errors.append(f"{name}: a <table> must sit in a table-container so a wide one can scroll")
for box in root.find("box"):
if not box.kids or not box.kids[0].has("sp-box-head"):
errors.append(f"{name}: a box must start with box_head")