Three tools (archive, browse, classifier) independently implemented
an empty-state pattern with three different CSS class naming
conventions and slightly different rules:
archive: .empty-state + .empty-state-content (BEM-less)
browse: .empty-state + .empty-state__inner (BEM)
classifier: .empty-state + .empty-state-content (BEM-less)
Same visual intent ("nothing's loaded yet — here's a welcome card
with instructions"), implemented three times with subtly different
spacing, no shared body styling for h2/p/ul/li, and incompatible
class names that prevented a future tool from copy-pasting the
pattern.
Promote a single consolidated rule set to shared/base.css using
BEM naming throughout:
.empty-state — base (flex centered, padding)
.empty-state--overlay — modifier: position absolute,
top 50px to clear app-header,
z-index 10. Used by archive
and classifier (their empty
states sit OVER the main
layout).
.empty-state__inner — content card (left-aligned,
text-muted, max-width 640)
.empty-state__inner--centered — modifier: tighter max-width
500, centered text, 2rem
padding. Used by tools whose
welcome screen reads as a
centered card.
.empty-state__inner h2/p/ul/ol/li — typography defaults
.empty-state__inner .note — italic small-print
.welcome-list — bullet list with left-aligned
text + auto margins; safe to
nest inside a centered card.
Per-tool changes:
- archive/template.html, archive/js/app.js: rename
.empty-state-content → .empty-state__inner empty-state__inner--centered;
add .empty-state--overlay to the outer .empty-state container.
Also the runtime-injected unsupported-browser markup in
showUnsupportedBrowserMessage() and the showHttpErrorState
selector.
- classifier/template.html: same renames.
- archive/css/layout.css + components.css: delete .empty-state*
and .welcome-list rules (now in shared).
- classifier/css/layout.css: same. Keep .empty-state.drag-over
locally — classifier is the only tool whose empty state acts
as a drop target.
- browse/css/base.css: delete .empty-state* (shared covers it).
browse's template was already using .empty-state__inner so no
template change needed.
LOC: shared/base.css gains ~70 lines; per-tool overrides lose ~85
combined. Net -15. More importantly, future tools can reuse the
pattern by adding two divs and (optionally) the --centered or
--overlay modifiers; no copy-paste required.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five tools (browse, transmittal, landing, form, tables) were rendering
their headers as vertically-stacked blocks — the .app-header flex
container correctly laid out its left/right groups, but those groups
themselves had no display:flex rule, so their children (logo, title,
build label, action button) defaulted to block-level stacking.
Three tools (archive, mdedit, classifier) hid the bug because they
each carried their own copy of the .header-left/.header-right flex
rule in tool-local CSS. Same intent, slightly different gap values:
archive: left gap 0.75rem, right gap 0.5rem
mdedit: both 0.75rem
classifier: both 0.5rem
Promote the rule to shared/base.css alongside .app-header (where the
class is used in every template anyway): left 0.75rem, right 0.5rem
(matching archive — the layout the user pointed at as the reference).
Delete the three local duplicates.
Now all eight tools use the same header chrome contract: logo + title
group + primary action laid out horizontally with consistent gaps,
icon buttons grouped tighter on the right.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ZDDC — Zero Day Document Control. A file-naming convention plus five
single-file HTML tools (archive, transmittal, classifier, mdedit,
landing) and an optional Go HTTP server (zddc-server) with ACL and a
virtual archive index. Self-contained, offline-capable, dependency-free.
See README.md for an overview, AGENTS.md and ARCHITECTURE.md for the
build/release/architecture detail, bootstrap/README.md for the
two-level deployment install pattern, and zddc/README.md for the
HTTP server.