aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-12 12:21:10 +0300
committergrm <grm@eyesin.space>2026-09-12 12:21:10 +0300
commit336199c65d910bbd45a9c5dcbc3beae214d75b98 (patch)
tree914b9259f96f21939305f9b4990b725cea72ca47
parente0768c2aacd72bd4e62d0847dc2231644664d08d (diff)
downloadblogspace-336199c65d910bbd45a9c5dcbc3beae214d75b98.tar.gz
blogspace-336199c65d910bbd45a9c5dcbc3beae214d75b98.tar.bz2
blogspace-336199c65d910bbd45a9c5dcbc3beae214d75b98.zip
Redesign the dashboard: paper & ink
Flat colours, 2px rules, hard offset shadows and square corners; mono headings and labels; the blog nav is a tab strip that scrolls sideways on phones instead of stacking; list pages get a page header with the primary action on the right; the Markdown cheat-sheet collapses into a details element so forms stay calm. Plain CSS only, no variables or flex, so it still renders on old browsers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
-rw-r--r--internal/web/static/dashboard.css177
-rw-r--r--internal/web/templates/admin/delete_user.html4
-rw-r--r--internal/web/templates/admin/index.html5
-rw-r--r--internal/web/templates/admin/new_user.html4
-rw-r--r--internal/web/templates/auth/login.html5
-rw-r--r--internal/web/templates/dashboard/confirm.html4
-rw-r--r--internal/web/templates/dashboard/design.html2
-rw-r--r--internal/web/templates/dashboard/images.html8
-rw-r--r--internal/web/templates/dashboard/page_form.html2
-rw-r--r--internal/web/templates/dashboard/pages.html7
-rw-r--r--internal/web/templates/dashboard/password.html2
-rw-r--r--internal/web/templates/dashboard/post_form.html2
-rw-r--r--internal/web/templates/dashboard/posts.html5
-rw-r--r--internal/web/templates/dashboard/settings.html2
-rw-r--r--internal/web/templates/partials/mdhelp.html10
15 files changed, 152 insertions, 87 deletions
diff --git a/internal/web/static/dashboard.css b/internal/web/static/dashboard.css
index 455daec..e0aee73 100644
--- a/internal/web/static/dashboard.css
+++ b/internal/web/static/dashboard.css
@@ -1,89 +1,142 @@
-/* Blogspace dashboard — plain CSS, no JS required, works down to ~320px. */
+/* Blogspace dashboard — "paper & ink": flat colours, 2px rules, hard shadows,
+ square corners. Plain CSS (no variables, no flex for layout) so it renders on
+ old browsers; works down to ~320px. */
* { box-sizing: border-box; }
-body { margin: 0; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #222; background: #f3f4f6; }
-a { color: #1a5fb4; }
-h1 { font-size: 1.6em; margin: 0.4em 0 0.6em; }
-h2 { font-size: 1.2em; margin: 0 0 0.6em; }
-h3 { font-size: 1.05em; margin: 0 0 0.4em; }
-code { background: #eef; padding: 0 0.25em; border-radius: 3px; font-size: 0.95em; }
-.muted { color: #666; }
+html { background: #f4efe4; }
+body { margin: 0; font-family: -apple-system, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #1d1a17; background: #f4efe4; }
+a { color: #c8541a; }
+a:hover { color: #a3420f; }
+h1, h2, h3, .mono { font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; letter-spacing: -0.01em; }
+h1 { font-size: 1.5em; margin: 0.2em 0 0.6em; line-height: 1.2; }
+h2 { font-size: 1.05em; margin: 0 0 0.8em; text-transform: uppercase; letter-spacing: 0.06em; }
+h3 { font-size: 0.95em; margin: 0 0 0.4em; text-transform: uppercase; letter-spacing: 0.06em; }
+code { font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; background: #ebe5d6; padding: 0 0.3em; font-size: 0.92em; }
+p { margin: 0.6em 0; }
+.muted { color: #6b655c; }
.nowrap { white-space: nowrap; }
.narrow { max-width: 480px; }
+.center { margin-left: auto; margin-right: auto; }
.small { font-size: 0.9em; }
-.topbar { background: #1f2933; color: #fff; }
-.topbar a { color: #fff; text-decoration: none; }
-.topbar-inner { max-width: 1100px; margin: 0 auto; padding: 0.6em 1em; overflow: hidden; }
-.brand { font-weight: bold; font-size: 1.1em; margin-right: 1em; }
-.who { color: #b8c2cc; }
-.topbar .links { float: right; }
-.topbar .links a, .topbar .links form { margin-left: 1em; }
-.blogbar { background: #fff; border-bottom: 1px solid #d9dde3; }
-.blogbar .topbar-inner { padding: 0.5em 1em; }
-.blogbar a { display: inline-block; padding: 0.3em 0.6em; text-decoration: none; color: #333; border-radius: 4px; }
-.blogbar a:hover { background: #eef1f5; }
-.blogbar a.active { background: #1f2933; color: #fff; }
-.topbar .links a.active { text-decoration: underline; }
-.blogbar .blogname { margin-right: 0.6em; }
-.blogbar .view { float: right; color: #1a5fb4; font-weight: bold; }
+/* ---- bars ---------------------------------------------------------------- */
+.topbar { background: #1d1a17; color: #f4efe4; }
+.topbar a { color: #f4efe4; text-decoration: none; }
+.topbar-inner { max-width: 1100px; margin: 0 auto; padding: 0.55em 1em; overflow: hidden; }
+.brand { font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-weight: bold; text-transform: uppercase; letter-spacing: 0.12em; margin-right: 1.2em; }
+.who { color: #b8b0a3; font-size: 0.9em; }
+.topbar .links { float: right; font-size: 0.9em; }
+.topbar .links a, .topbar .links form { margin-left: 1.2em; }
+.topbar .links a:hover, .topbar .links .linkbtn:hover { color: #f2a67c; }
+.topbar .links a.active { border-bottom: 2px solid #f2a67c; }
+.blogbar { background: #fffdf8; border-bottom: 2px solid #1d1a17; }
+.blogbar .topbar-inner { padding: 0.5em 1em 0; }
+.blogbar .blogname { display: inline-block; margin: 0 1em 0.5em 0; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; }
+.blogbar .tabs { display: inline-block; vertical-align: bottom; }
+.blogbar .tabs a { display: inline-block; padding: 0.35em 0.8em; margin: 0 0.15em 0 0; text-decoration: none; color: #1d1a17; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.9em; border: 2px solid transparent; border-bottom: 0; }
+.blogbar .tabs a:hover { border-color: #1d1a17; }
+.blogbar .tabs a.active { background: #1d1a17; color: #f4efe4; border-color: #1d1a17; }
+.blogbar .view { float: right; margin: 0 0 0.5em; padding: 0.25em 0.7em; border: 2px solid #1d1a17; color: #1d1a17; text-decoration: none; font-size: 0.85em; font-weight: bold; background: #fffdf8; }
+.blogbar .view:hover { background: #1d1a17; color: #f4efe4; }
-.main { max-width: 1100px; margin: 0 auto; padding: 1em; }
-.card { background: #fff; border: 1px solid #d9dde3; border-radius: 6px; padding: 1em 1.2em; margin-bottom: 1em; }
+/* ---- page ---------------------------------------------------------------- */
+.main { max-width: 1100px; margin: 0 auto; padding: 1.4em 1em 3em; }
+.pagehead { overflow: hidden; margin-bottom: 0.8em; }
+.pagehead h1 { float: left; margin: 0.1em 1em 0.3em 0; }
+.pagehead .actions { float: right; margin-top: 0.1em; }
+.pagehead .lead { clear: both; margin: 0; }
+.card { background: #fffdf8; border: 2px solid #1d1a17; box-shadow: 4px 4px 0 #1d1a17; padding: 1.1em 1.3em; margin: 0 4px 1.6em 0; }
+.card > :last-child { margin-bottom: 0; }
.cols { overflow: hidden; }
-.cols .card { float: left; width: 49%; margin-right: 2%; }
-.cols .card + .card { margin-right: 0; }
-.flash { padding: 0.7em 1em; border-radius: 6px; margin-bottom: 1em; }
-.flash.ok { background: #e3f6e8; border: 1px solid #9ad3a8; color: #1c5a2a; }
-.flash.err { background: #fdecec; border: 1px solid #f0a5a5; color: #7a1c1c; }
-.tag { display: inline-block; font-size: 0.75em; background: #eef1f5; color: #555; padding: 0 0.5em; border-radius: 3px; margin-left: 0.4em; vertical-align: middle; }
+.cols .card { float: left; width: 48.5%; margin-right: 3%; }
+.cols .card + .card { margin-right: 4px; }
+.flash { padding: 0.6em 1em; border: 2px solid #1d1a17; margin: 0 4px 1.2em 0; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.9em; }
+.flash.ok { background: #e4f0dc; }
+.flash.err { background: #f6dfd9; }
+.tag { display: inline-block; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.7em; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid #1d1a17; color: #1d1a17; padding: 0 0.45em; margin-left: 0.5em; vertical-align: middle; line-height: 1.6; }
+.tag.off { color: #6b655c; border-color: #b8b0a3; }
-label { display: block; margin: 0.6em 0; }
+/* ---- forms --------------------------------------------------------------- */
+label { display: block; margin: 0.8em 0; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.8em; text-transform: uppercase; letter-spacing: 0.05em; }
+label .muted { text-transform: none; letter-spacing: 0; }
+label code { text-transform: none; letter-spacing: 0; }
input[type=text], input[type=password], input:not([type]), input[type=file], select, textarea {
- width: 100%; max-width: 100%; padding: 0.45em 0.5em; border: 1px solid #b9c0c9; border-radius: 4px; font: inherit; background: #fff; }
-input[type=color] { width: 4em; height: 2.2em; padding: 0.1em; border: 1px solid #b9c0c9; border-radius: 4px; background: #fff; }
-textarea { font-family: "Courier New", Courier, monospace; font-size: 0.95em; line-height: 1.45; }
-textarea.editor { min-height: 20em; }
-label.check { display: block; }
-label.check input { width: auto; margin-right: 0.4em; }
+ width: 100%; max-width: 100%; padding: 0.5em 0.6em; margin-top: 0.25em; border: 2px solid #1d1a17; font: inherit; font-family: -apple-system, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; text-transform: none; letter-spacing: 0; background: #fffdf8; color: #1d1a17; }
+input:focus, select:focus, textarea:focus, button:focus, .btn:focus { outline: 2px solid #c8541a; outline-offset: 1px; }
+input[type=color] { width: 4em; height: 2.3em; padding: 0.1em; margin-top: 0.25em; border: 2px solid #1d1a17; background: #fffdf8; }
+input[type=file] { padding: 0.35em; }
+select { -webkit-appearance: none; appearance: none; padding-right: 1.8em; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231d1a17' stroke-width='2'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 0.6em center; }
+textarea { font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.95em; line-height: 1.5; }
+textarea.editor { min-height: 28em; }
+label.check { text-transform: none; letter-spacing: 0; font-family: -apple-system, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1em; }
+label.check input { width: auto; margin: 0 0.4em 0 0; vertical-align: middle; }
.row { overflow: hidden; }
.row label { float: left; margin-right: 1.5em; min-width: 12em; }
.row label:last-child { margin-right: 0; }
-button, .btn { display: inline-block; font: inherit; padding: 0.45em 1em; border-radius: 4px; border: 1px solid #164b8f; background: #1a5fb4; color: #fff; cursor: pointer; text-decoration: none; }
-button:hover, .btn:hover { background: #164b8f; }
-.btn.secondary, button.secondary { background: #fff; color: #1a5fb4; }
-.btn.small { font-size: 0.75em; vertical-align: middle; }
-button.mini, .mini { font-size: 0.8em; padding: 0.15em 0.5em; background: #fff; color: #333; border-color: #b9c0c9; }
-button.mini:hover { background: #eef1f5; }
-button.danger { background: #b42318; border-color: #8a1a12; }
-a.danger, button.mini.danger { color: #b42318; }
-button.linkbtn { background: none; border: 0; padding: 0; color: inherit; font: inherit; cursor: pointer; text-decoration: none; }
+button, .btn { display: inline-block; font: inherit; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.9em; font-weight: bold; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.5em 1.1em; border: 2px solid #1d1a17; background: #c8541a; color: #fffdf8; cursor: pointer; text-decoration: none; box-shadow: 3px 3px 0 #1d1a17; vertical-align: middle; }
+button:hover, .btn:hover { background: #a3420f; color: #fffdf8; }
+button:active, .btn:active { box-shadow: none; position: relative; top: 3px; left: 3px; }
+.btn.secondary, button.secondary { background: #fffdf8; color: #1d1a17; }
+.btn.secondary:hover, button.secondary:hover { background: #ebe5d6; color: #1d1a17; }
+.btn.small { font-size: 0.75em; padding: 0.4em 0.9em; }
+button.mini, .mini { font-size: 0.72em; padding: 0.15em 0.55em; background: #fffdf8; color: #1d1a17; border-width: 1px; box-shadow: none; font-weight: normal; }
+button.mini:hover, .mini:hover { background: #1d1a17; color: #fffdf8; }
+button.mini:active { top: 0; left: 0; }
+button.danger { background: #a12b1e; }
+button.danger:hover { background: #7d1f14; }
+a.danger { color: #a12b1e; }
+button.mini.danger { color: #a12b1e; border-color: #a12b1e; background: #fffdf8; }
+button.mini.danger:hover { background: #a12b1e; color: #fffdf8; }
+button.linkbtn { background: none; border: 0; padding: 0; color: inherit; font: inherit; cursor: pointer; text-decoration: none; box-shadow: none; text-transform: none; letter-spacing: 0; }
+button.linkbtn:active { top: 0; left: 0; }
form.inline, .inline { display: inline; }
details.inline summary { display: inline; cursor: pointer; }
+summary.mini { display: inline-block; list-style: none; }
.filter { margin: 0 0 1em; }
-.filter select { width: auto; }
+.filter label { display: inline-block; margin: 0; }
+.filter select { width: auto; margin: 0 0 0 0.4em; }
+.actionrow { margin: 1.2em 0 0; }
+.actionrow a { margin-left: 0.8em; }
+/* ---- tables & lists ------------------------------------------------------ */
table { border-collapse: collapse; width: 100%; }
-th, td { text-align: left; padding: 0.45em 0.5em; border-bottom: 1px solid #e5e8ec; vertical-align: middle; }
-th { font-size: 0.85em; text-transform: uppercase; color: #666; letter-spacing: 0.03em; }
-tr.disabled td { color: #999; }
+th, td { text-align: left; padding: 0.5em 0.6em; border-bottom: 1px solid #ddd6c7; vertical-align: middle; }
+th { font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.75em; text-transform: uppercase; letter-spacing: 0.08em; color: #1d1a17; border-bottom: 2px solid #1d1a17; }
+tr:nth-child(even) td { background: #f9f6ee; }
+tr:hover td { background: #f1ecdf; }
+tr.disabled td { color: #8f887c; }
ul.plain { list-style: none; padding: 0; margin: 0; }
-ul.plain li { padding: 0.2em 0; }
-.help { background: #fbfbf4; }
-.cheat td { border: 0; padding: 0.2em 1em 0.2em 0; }
+ul.plain li { padding: 0.3em 0; border-bottom: 1px solid #ddd6c7; }
+ul.plain li:last-child { border-bottom: 0; }
+details.help { margin: 0.8em 4px 1.6em 0; border: 2px dashed #1d1a17; padding: 0.5em 1em; background: #fbf8f0; }
+details.help summary { cursor: pointer; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.85em; text-transform: uppercase; letter-spacing: 0.06em; }
+details.help[open] summary { margin-bottom: 0.5em; }
+.cheat td { border: 0; padding: 0.2em 1em 0.2em 0; background: none; }
+/* ---- images -------------------------------------------------------------- */
.gallery { overflow: hidden; }
-.thumb { float: left; width: 200px; margin: 0 1em 1em 0; padding: 0.6em; text-align: center; }
-.thumb img { max-width: 100%; max-height: 140px; }
-.thumb .meta { font-size: 0.85em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.thumb .copy { font-size: 0.8em; margin: 0.4em 0; font-family: "Courier New", monospace; }
-.imagepick { margin-top: 0.5em; padding-top: 0.5em; border-top: 1px dashed #d9dde3; }
-.imagepick img.preview { display: block; max-width: 240px; max-height: 120px; margin-top: 0.4em; border: 1px solid #d9dde3; }
+.thumb { float: left; width: 200px; margin: 0 1.2em 1.6em 0; padding: 0.6em; text-align: center; }
+.thumb img { max-width: 100%; max-height: 140px; border: 1px solid #ddd6c7; }
+.thumb .meta { font-size: 0.85em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 0.4em; }
+.thumb .copy { font-size: 0.75em; margin: 0.4em 0; padding: 0.3em; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; }
+.imagepick { margin-top: 0.5em; padding-top: 0.5em; border-top: 2px dashed #ddd6c7; }
+.imagepick img.preview { display: block; max-width: 240px; max-height: 120px; margin-top: 0.4em; border: 2px solid #1d1a17; }
.imagepick select { width: auto; max-width: 100%; }
+/* ---- login --------------------------------------------------------------- */
+.login-brand { text-align: center; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; text-transform: uppercase; letter-spacing: 0.2em; margin: 2em 0 1em; }
+
@media (max-width: 700px) {
- .cols .card, .row label, .thumb { float: none; width: auto; margin-right: 0; }
- .topbar .links, .blogbar .view { float: none; display: block; margin-top: 0.3em; }
+ .cols .card, .row label, .thumb { float: none; width: auto; margin-right: 4px; }
+ .cols .card + .card { margin-right: 4px; }
+ .topbar .links { float: none; display: block; margin-top: 0.3em; }
.topbar .links a, .topbar .links form { margin-left: 0; margin-right: 1em; }
+ .blogbar .topbar-inner { overflow: visible; }
+ .blogbar .blogname { display: block; margin-right: 0; }
+ .blogbar .view { float: none; display: inline-block; margin: 0 0 0.6em; }
+ .blogbar .tabs { display: block; overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; margin: 0 -1em; padding: 0 1em; }
+ .pagehead h1, .pagehead .actions { float: none; }
+ .pagehead .actions { margin-bottom: 0.6em; }
+ .card { padding: 0.9em 1em; overflow-x: auto; }
table { font-size: 0.9em; }
th, td { padding: 0.35em 0.3em; }
}
diff --git a/internal/web/templates/admin/delete_user.html b/internal/web/templates/admin/delete_user.html
index 34d3fb9..e673b64 100644
--- a/internal/web/templates/admin/delete_user.html
+++ b/internal/web/templates/admin/delete_user.html
@@ -5,8 +5,8 @@
<p>This permanently deletes the user <strong>{{.Data.target.Username}}</strong>, their blog, and every page, post and image in it.</p>
<form method="post" action="/admin/users/{{.Data.target.ID}}/delete">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
- <button type="submit" class="danger">Yes, delete everything</button>
- <a class="btn secondary" href="/admin/">Cancel</a>
+ <p class="actionrow"><button type="submit" class="danger">Yes, delete everything</button>
+ <a class="btn secondary" href="/admin/">Cancel</a></p>
</form>
</div>
{{end}}
diff --git a/internal/web/templates/admin/index.html b/internal/web/templates/admin/index.html
index bea9266..ecb0112 100644
--- a/internal/web/templates/admin/index.html
+++ b/internal/web/templates/admin/index.html
@@ -1,6 +1,9 @@
{{define "title"}}Admin · Blogspace{{end}}
{{define "content"}}
-<h1>Users &amp; blogs <a class="btn small" href="/admin/users/new">+ New blogger</a></h1>
+<div class="pagehead">
+ <h1>Users &amp; blogs</h1>
+ <div class="actions"><a class="btn" href="/admin/users/new">+ New blogger</a></div>
+</div>
<div class="card">
<table>
<tr><th>User</th><th>Role</th><th>Blog</th><th>Since</th><th>Actions</th></tr>
diff --git a/internal/web/templates/admin/new_user.html b/internal/web/templates/admin/new_user.html
index 87d1186..3d93dbd 100644
--- a/internal/web/templates/admin/new_user.html
+++ b/internal/web/templates/admin/new_user.html
@@ -7,9 +7,9 @@
<label>Username<br><input name="username" value="{{.Data.username}}" required pattern="[a-zA-Z0-9_.-]{2,40}" autofocus></label>
<label>Password <span class="muted">(min 8 characters; they can change it later)</span><br><input type="password" name="password" required minlength="8"></label>
<label>Subdomain <span class="muted">(defaults to the username)</span><br>
- <span class="nowrap"><input name="subdomain" value="{{.Data.subdomain}}" pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?" maxlength="63" size="14">.{{.Data.cfg.BaseDomain}}</span></label>
+ <span class="nowrap"><input name="subdomain" value="{{.Data.subdomain}}" pattern="[a-z0-9]([a-z0-9-]*[a-z0-9])?" maxlength="63" size="14" style="width:auto">.{{.Data.cfg.BaseDomain}}</span></label>
<label>Blog title<br><input name="title" value="{{.Data.title}}" maxlength="120"></label>
- <button type="submit">Create</button> <a href="/admin/">Cancel</a>
+ <p class="actionrow"><button type="submit">Create</button> <a href="/admin/">Cancel</a></p>
</form>
</div>
{{end}}
diff --git a/internal/web/templates/auth/login.html b/internal/web/templates/auth/login.html
index 2aec3b1..3f66f3f 100644
--- a/internal/web/templates/auth/login.html
+++ b/internal/web/templates/auth/login.html
@@ -1,12 +1,13 @@
{{define "title"}}Log in · Blogspace{{end}}
{{define "content"}}
-<div class="card narrow">
+<p class="login-brand">Blogspace</p>
+<div class="card narrow center">
<h1>Log in</h1>
<form method="post" action="/webadmin">
<input type="hidden" name="next" value="{{.Data.next}}">
<label>Username<br><input name="username" value="{{.Data.username}}" required autofocus autocomplete="username"></label>
<label>Password<br><input type="password" name="password" required autocomplete="current-password"></label>
- <button type="submit">Log in</button>
+ <p class="actionrow"><button type="submit">Log in</button></p>
</form>
</div>
{{end}}
diff --git a/internal/web/templates/dashboard/confirm.html b/internal/web/templates/dashboard/confirm.html
index eefa1da..8dd0b3a 100644
--- a/internal/web/templates/dashboard/confirm.html
+++ b/internal/web/templates/dashboard/confirm.html
@@ -9,8 +9,8 @@
<p>You are about to permanently delete {{.Data.what}}. This cannot be undone.</p>
<form method="post" action="{{.Data.action}}">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
- <button type="submit" class="danger">Yes, delete</button>
- <a class="btn secondary" href="{{.Data.back}}">Cancel</a>
+ <p class="actionrow"><button type="submit" class="danger">Yes, delete</button>
+ <a class="btn secondary" href="{{.Data.back}}">Cancel</a></p>
</form>
{{end}}
</div>
diff --git a/internal/web/templates/dashboard/design.html b/internal/web/templates/dashboard/design.html
index 27ce3c7..09ea1fe 100644
--- a/internal/web/templates/dashboard/design.html
+++ b/internal/web/templates/dashboard/design.html
@@ -82,6 +82,6 @@
</div>
</div>
-<p><button type="submit">Save design</button></p>
+<p class="actionrow"><button type="submit">Save design</button></p>
</form>
{{end}}
diff --git a/internal/web/templates/dashboard/images.html b/internal/web/templates/dashboard/images.html
index 5383e73..b44289e 100644
--- a/internal/web/templates/dashboard/images.html
+++ b/internal/web/templates/dashboard/images.html
@@ -1,14 +1,16 @@
{{define "title"}}Images · {{.Blog.Title}}{{end}}
{{define "content"}}
-<h1>Images</h1>
+<div class="pagehead">
+ <h1>Images</h1>
+ <p class="lead muted">Images are stored with your blog. Put one in a post with <em>Insert image</em> in the editor, or copy its line from below.</p>
+</div>
<div class="card">
<form method="post" action="/b/{{.Blog.Subdomain}}/images/upload" enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label>Upload an image <span class="muted">(PNG, JPEG, GIF or WebP)</span><br><input type="file" name="file" accept="image/*" required></label>
- <button type="submit">Upload</button>
+ <p class="actionrow"><button type="submit">Upload</button></p>
</form>
</div>
-<p class="muted">To put an image in a post, copy its Markdown line into the post text.</p>
<div class="gallery">
{{range .Data.images}}
<div class="card thumb">
diff --git a/internal/web/templates/dashboard/page_form.html b/internal/web/templates/dashboard/page_form.html
index df94c7e..38aac71 100644
--- a/internal/web/templates/dashboard/page_form.html
+++ b/internal/web/templates/dashboard/page_form.html
@@ -10,7 +10,7 @@
<label>Intro text <span class="muted">(Markdown, shown above the posts; optional)</span><br>
<textarea name="intro" rows="8">{{.Data.page.IntroMD}}</textarea></label>
<label class="check"><input type="checkbox" name="show_in_nav"{{if .Data.page.ShowInNav}} checked{{end}}> Show in menu</label>
- <p>
+ <p class="actionrow">
<button type="submit">Save</button>
{{if .Data.page.ID}}<a class="btn secondary" href="{{.BlogURL}}{{if .Data.page.IsHome}}/{{else}}/{{.Data.page.Slug}}{{end}}" target="_blank">View page &#8599;</a>{{end}}
<a href="/b/{{.Blog.Subdomain}}/pages">Back to pages</a>
diff --git a/internal/web/templates/dashboard/pages.html b/internal/web/templates/dashboard/pages.html
index 6703e7f..5e9aca5 100644
--- a/internal/web/templates/dashboard/pages.html
+++ b/internal/web/templates/dashboard/pages.html
@@ -1,7 +1,10 @@
{{define "title"}}Pages · {{.Blog.Title}}{{end}}
{{define "content"}}
-<h1>Pages <a class="btn small" href="/b/{{.Blog.Subdomain}}/pages/new">+ New page</a></h1>
-<p class="muted">Pages appear in your blog's menu in this order. Each page holds its own list of posts.</p>
+<div class="pagehead">
+ <h1>Pages</h1>
+ <div class="actions"><a class="btn" href="/b/{{.Blog.Subdomain}}/pages/new">+ New page</a></div>
+ <p class="lead muted">Pages appear in your blog's menu in this order. Each page holds its own list of posts.</p>
+</div>
<div class="card">
<table>
<tr><th>Menu order</th><th>Title</th><th>Address</th><th>Posts</th><th></th></tr>
diff --git a/internal/web/templates/dashboard/password.html b/internal/web/templates/dashboard/password.html
index f333bd7..3e28744 100644
--- a/internal/web/templates/dashboard/password.html
+++ b/internal/web/templates/dashboard/password.html
@@ -7,7 +7,7 @@
<label>Current password<br><input type="password" name="current" required autocomplete="current-password"></label>
<label>New password<br><input type="password" name="password" required minlength="8" autocomplete="new-password"></label>
<label>Repeat new password<br><input type="password" name="password2" required minlength="8" autocomplete="new-password"></label>
- <button type="submit">Change password</button>
+ <p class="actionrow"><button type="submit">Change password</button></p>
</form>
</div>
{{end}}
diff --git a/internal/web/templates/dashboard/post_form.html b/internal/web/templates/dashboard/post_form.html
index 520b9e6..7e6f00f 100644
--- a/internal/web/templates/dashboard/post_form.html
+++ b/internal/web/templates/dashboard/post_form.html
@@ -15,7 +15,7 @@
<label>Content <span class="muted">(Markdown)</span><br>
<textarea name="body" rows="24" class="editor">{{.Data.post.BodyMD}}</textarea></label>
<label class="check"><input type="checkbox" name="published"{{if .Data.post.Published}} checked{{end}}> Visible on the blog</label>
- <p>
+ <p class="actionrow">
<button type="submit">Save</button>
{{if .Data.post.ID}}<a class="btn secondary" href="{{.BlogURL}}/{{.Data.post.PageSlug}}/{{.Data.post.Slug}}" target="_blank">View post &#8599;</a>{{end}}
<a href="/b/{{.Blog.Subdomain}}/posts">Back to posts</a>
diff --git a/internal/web/templates/dashboard/posts.html b/internal/web/templates/dashboard/posts.html
index 24e86bb..c0b7112 100644
--- a/internal/web/templates/dashboard/posts.html
+++ b/internal/web/templates/dashboard/posts.html
@@ -1,6 +1,9 @@
{{define "title"}}Posts · {{.Blog.Title}}{{end}}
{{define "content"}}
-<h1>Posts <a class="btn small" href="/b/{{.Blog.Subdomain}}/posts/new{{if .Data.pageID}}?page={{.Data.pageID}}{{end}}">+ New post</a></h1>
+<div class="pagehead">
+ <h1>Posts</h1>
+ <div class="actions"><a class="btn" href="/b/{{.Blog.Subdomain}}/posts/new{{if .Data.pageID}}?page={{.Data.pageID}}{{end}}">+ New post</a></div>
+</div>
<form method="get" class="filter">
<label>Page:
<select name="page" onchange="this.form.submit()">
diff --git a/internal/web/templates/dashboard/settings.html b/internal/web/templates/dashboard/settings.html
index 9b2b325..53a24c4 100644
--- a/internal/web/templates/dashboard/settings.html
+++ b/internal/web/templates/dashboard/settings.html
@@ -7,7 +7,7 @@
<label>Blog title<br><input name="title" value="{{.Blog.Title}}" required maxlength="120"></label>
<label>Tagline <span class="muted">(shown under the title)</span><br><input name="tagline" value="{{.Blog.Tagline}}" maxlength="300"></label>
<p class="muted">Address: <code>{{.BlogURL}}</code>{{if ne .Blog.Subdomain "www"}} — only the administrator can change this{{end}}.</p>
- <button type="submit">Save</button>
+ <p class="actionrow"><button type="submit">Save</button></p>
</form>
</div>
{{end}}
diff --git a/internal/web/templates/partials/mdhelp.html b/internal/web/templates/partials/mdhelp.html
index 878130e..4623c07 100644
--- a/internal/web/templates/partials/mdhelp.html
+++ b/internal/web/templates/partials/mdhelp.html
@@ -1,9 +1,9 @@
-{{define "mdhelp"}}<div class="card help">
- <h3>Formatting cheat-sheet</h3>
+{{define "mdhelp"}}<details class="help">
+ <summary>Formatting cheat-sheet</summary>
<table class="cheat">
<tr><td><code># Heading</code>, <code>## Smaller heading</code></td><td><code>**bold**</code>, <code>*italic*</code></td></tr>
- <tr><td><code>[link text](https://example.org)</code></td><td><code>![description](/media/…)</code> — copy the address from <em>Images</em></td></tr>
+ <tr><td><code>[link text](https://example.org)</code></td><td><code>![description](/media/…)</code> — or use <em>Insert image</em></td></tr>
<tr><td><code>- list item</code> / <code>1. numbered</code></td><td><code>&gt; quote</code>, <code>`code`</code>, <code>---</code> for a line</td></tr>
</table>
- <p class="muted">Blank line = new paragraph. Press Enter once for a line break.</p>
-</div>{{end}}
+ <p class="muted small">Blank line = new paragraph. Press Enter once for a line break.</p>
+</details>{{end}}