Skip to main content

max / makenotwork

Give the dashboard headings their classes, and say what the bare-heading exception covers
Author: Max Johnson <me@maxj.phd> · 2026-08-02 21:33 UTC
Signed with PGP, not checked
Commit: 1e8a2701b080a34aa25ad1461fc4b4a0885581fd
Parent: fb148be
4 files changed, +8 insertions, -7 deletions
@@ -178,7 +178,7 @@
178 178 8. **Spacing values come from `--gap-*` tokens.** Pick the one that names what the rule separates: `bound` inside a thing, `peer` between siblings, `group` between related controls, `section` between parts of a pane, `pane` between panes, `page` at page level. Raw values and off-scale values are bugs. The tokens are generated from makeover-geometry and shift under a coarse pointer, so a hardcoded value silently opts that rule out of touch density.
179 179 9. **Font sizes come from `--text-*` tokens.** Pick the one that names what the text is: `fine` for timestamps and badges, `note` for metadata and table cells, `body` for running copy, `lead` for emphasised copy and card titles, `subhead` / `head` / `title` for the three heading levels, `display` and `hero` above the hierarchy. Raw values and off-scale values are bugs, including a value that differs from a tier by a fraction of a pixel. Nothing sits below `fine`: text that should recede does it with colour or weight.
180 180 10. **Page-scoped CSS is a last resort.** Default to composing layout + component primitives. Page-scoped sections exist for genuinely page-specific layout (grids, long-form typography, marketing heroes), not for shapes that are cards or list rows in disguise.
181 - 11. **No bare `<h1>` / `<h2>` in templates.** Pick a heading class: `.brand-h1` (wordmark), `.page-title` (page h1), `.subtitle-h2` (page subtitle on auth/wizards), `.subsection-title` (default h2 inside dashboards/tabs/partials), or `.section-header` (prose sub-section with bottom border). Bare tags inside server-rendered markdown / user content (e.g. `partials/item_text_editor.html` JS preview) are the only exception.
181 + 11. **No bare `<h1>` / `<h2>` in templates.** Pick a heading class: `.brand-h1` (wordmark), `.page-title` (page h1), `.subtitle-h2` (page subtitle on auth/wizards), `.subsection-title` (default h2 inside dashboards/tabs/partials), or `.section-header` (prose sub-section with bottom border). Two exceptions, both because the heading classes are the wrong tool rather than because the rule is optional. **Prose containers** style their own descendant headings — `.doc-body`, `.article-body`, `.markdown-preview` — so a heading inside one stays bare whether the prose was rendered from markdown or hand-written in the template (`pages/economics.html`). **Standalone creator pages** (`templates/custom/*.html`, `templates/embed/*.html`) do not load `style.css` at all, so no heading class exists to pick.
182 182 12. **Button class is `.btn-primary` / `.btn-secondary` / `.btn-danger`.** Works on both `<button>` and `<a>` (the `<a><button>` antipattern is banned. Use `<a class="btn-primary">…</a>`). The bare `button.primary` shorthand has been retired.
183 183 13. **Class names use kebab-case only.** No BEM `__` separator (retired 2026-05-20, was 112 sites across 20 prefixes, now flattened to single-dash). Modifier `--` (e.g. `.card--bordered`) is still allowed.
184 184
@@ -4115,9 +4115,10 @@
4115 4115 gap: var(--gap-section);
4116 4116 }
4117 4117
4118 + /* Typeface comes from .subsection-title on the heading itself; this sets only
4119 + what the flex row needs. */
4118 4120 .admin-section-header h2 {
4119 4121 font-size: var(--text-lead);
4120 - font-family: var(--font-mono);
4121 4122 margin: 0;
4122 4123 }
4123 4124
@@ -5,7 +5,7 @@
5 5 {% block content %}
6 6 <div class="cp-editor">
7 7 <header class="cp-editor-head">
8 - <h1>Custom page: {{ heading }}</h1>
8 + <h1 class="page-title">Custom page: {{ heading }}</h1>
9 9 <nav class="cp-editor-actions">
10 10 <a href="{{ live_url }}" target="_blank" rel="noopener">View live</a>
11 11 <a href="/docs/custom-pages" target="_blank" rel="noopener">Guide</a>
@@ -13,7 +13,7 @@
13 13
14 14 <section class="admin-scan-section">
15 15 <div class="admin-section-header">
16 - <h2>Pipeline Health</h2>
16 + <h2 class="subsection-title">Pipeline Health</h2>
17 17 <span class="dimmed text-sm">last 24h</span>
18 18 </div>
19 19 <div class="layer-health-grid">
@@ -39,7 +39,7 @@
39 39
40 40 <section class="admin-scan-section">
41 41 <div class="admin-section-header">
42 - <h2>Active Queue</h2>
42 + <h2 class="subsection-title">Active Queue</h2>
43 43 <span class="dimmed text-sm">auto-refresh every 10s</span>
44 44 </div>
45 45 <div class="stats-row"
@@ -59,7 +59,7 @@
59 59
60 60 <section class="admin-scan-section">
61 61 <div class="admin-section-header">
62 - <h2>Audit Log</h2>
62 + <h2 class="subsection-title">Audit Log</h2>
63 63 <a href="/admin/uploads/audit" class="text-sm">View full log</a>
64 64 </div>
65 65 <p class="dimmed text-sm">Every promote, quarantine, and re-scan is recorded.</p>
@@ -67,7 +67,7 @@
67 67
68 68 <section class="admin-scan-section">
69 69 <div class="admin-section-header">
70 - <h2>Held for Review</h2>
70 + <h2 class="subsection-title">Held for Review</h2>
71 71 <div>
72 72 <span class="dimmed text-sm">{{ total_held }} held</span>
73 73 {% if total_held > 0 %}