aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/static/dashboard.css
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-16 19:10:13 +0300
committergrm <grm@eyesin.space>2026-09-16 19:10:13 +0300
commit4a6f987bf7f9d73e9dd33d225fd2d73e9bf69a88 (patch)
treecc39ea660046d9c977647e7c9c36349c126be214 /internal/web/static/dashboard.css
parent6b6b5d2e35ff182a0732da245f4eb37c8afa0564 (diff)
downloadblogspace-4a6f987bf7f9d73e9dd33d225fd2d73e9bf69a88.tar.gz
blogspace-4a6f987bf7f9d73e9dd33d225fd2d73e9bf69a88.tar.bz2
blogspace-4a6f987bf7f9d73e9dd33d225fd2d73e9bf69a88.zip
Merge the Layout and Design tabs into one form with a single Save
The look and the layout of a blog were split over two tabs that cross-linked each other, and the Layout tab saved every toggle, move and add straight away through a dozen tiny forms. Now one Design tab holds it all, organised by part of the blog (colours & fonts, header, menu, content, side columns, footer, logo & icon) behind a sticky side index, and one Save stores the theme, every module and the menu atomically (BlogStore.SaveDesign). Module settings are edited inline; Discard changes throws the edits away; a validation error shows the form again exactly as it was sent. The image pickers no longer render the whole library four times over: a select of the newest images, and a script-driven panel that fetches the library page by page (GET /files as JSON) with lazy thumbnails. New options: posts per page, site title size, footer alignment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/static/dashboard.css')
-rw-r--r--internal/web/static/dashboard.css106
1 files changed, 75 insertions, 31 deletions
diff --git a/internal/web/static/dashboard.css b/internal/web/static/dashboard.css
index 3ef96c2..32561bf 100644
--- a/internal/web/static/dashboard.css
+++ b/internal/web/static/dashboard.css
@@ -195,28 +195,25 @@ table.files td.name { word-break: break-all; }
table.files .copy { width: 100%; min-width: 11em; padding: 0.3em 0.4em; margin: 0; font-family: var(--mono); font-size: 0.75em; }
.pager { margin: 0 0 1.4em; text-align: center; font-size: 0.92em; }
.pager a, .pager span { margin: 0 0.8em; }
-.imagepick { margin-top: 0.8em; padding-top: 0.6em; border-top: 1px solid var(--border); }
-.imagepick > label { margin-bottom: 0.3em; }
-.imagepick .picks { display: flex; flex-wrap: wrap; gap: 0.7em; margin: 0.4em 0 0; }
-.imagepick .pick { width: 120px; margin: 0; padding: 0.4em; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); text-align: center; font-size: 0.75em; font-weight: normal; cursor: pointer; }
-.imagepick .pick:hover { border-color: var(--border-strong); background: var(--surface-2); }
-.imagepick .pick input { display: block; width: auto; margin: 0 auto 0.3em; }
-.imagepick .pick img, .imagepick .pick .swatch { display: block; width: 100%; height: 64px; margin: 0 auto 0.3em; border: 1px solid var(--border); border-radius: 4px; background: var(--surface-2); }
-.imagepick .pick img { object-fit: contain; }
-.imagepick .pick .swatch { line-height: 64px; font-size: 1.6em; color: var(--muted); }
-.imagepick .pick .name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.imagepick .pick input:checked ~ img, .imagepick .pick input:checked ~ .swatch { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
-
-/* ---- design page ---------------------------------------------------------- */
-.jumps { display: flex; flex-wrap: wrap; gap: 0.4em; margin: 0 0 1.2em; font-size: 0.85em; }
-.jumps a { display: inline-block; padding: 0.2em 0.75em; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); text-decoration: none; }
-.jumps a:hover { border-color: var(--accent); color: var(--accent); }
+/* ---- design page: side index, one form, module rows, image pickers ---------- */
+.design { display: grid; grid-template-columns: 11em minmax(0, 1fr); gap: 0 1.4em; align-items: start; }
+.sidenav { position: sticky; top: 1em; display: flex; flex-direction: column; gap: 0.15em; font-size: 0.9em; }
+.sidenav a { padding: 0.35em 0.7em; border-radius: var(--radius-sm); color: var(--muted); text-decoration: none; font-weight: 500; }
+.sidenav a:hover { background: var(--surface); color: var(--text); }
+.sidenav a.active { background: var(--accent-soft); color: var(--accent-hover); }
+.design .card h3 { margin-top: 1.4em; padding-top: 0.9em; border-top: 1px solid var(--border); }
+.design .card h3:first-of-type { margin-top: 0.8em; }
+.design .card h2 + h3 { margin-top: 0; padding-top: 0; border-top: 0; }
+.design h4 { font-size: 0.9em; font-weight: 600; margin: 1em 0 0.2em; }
+/* what only makes sense with, or without, the script */
+form.js .nojs { display: none !important; }
+form:not(.js) .jsonly { display: none !important; }
.presets { display: flex; flex-wrap: wrap; gap: 0.8em; }
.presets .preset { width: 8.5em; margin: 0; padding: 0.55em 0.45em; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-sm); font-size: 0.8em; font-weight: 500; text-align: center; }
.presets .preset:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.presets .sw { display: inline-block; width: 22%; height: 2.2em; margin: 0 1%; border: 1px solid rgba(0,0,0,0.15); border-radius: 3px; vertical-align: middle; }
.presets .pname { display: block; margin-top: 0.4em; }
-/* layout page: the column diagram, area cards with their module tables */
+/* the column diagram */
.lay { margin: 0 0 1em; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface-2); font-size: 0.75em; font-weight: 500; text-align: center; overflow: hidden; }
.lay-row { padding: 0.5em; background: var(--text); color: #fff; }
.lay-cols { display: flex; padding: 0.5em; gap: 0.3em; }
@@ -226,38 +223,85 @@ table.files .copy { width: 100%; min-width: 11em; padding: 0.3em 0.4em; margin:
.lay-col.off { text-decoration: none; }
.lay-col.empty { background: transparent; }
.row input[type=number] { width: 6em; }
-.cardhead { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5em 1em; }
-.cardhead h2 { margin: 0 0 0.6em; }
-.cardhead .switch { margin: 0 0 0.6em auto; }
-.card.area-off { border-style: dashed; box-shadow: none; }
-.card.area-off .modules, .card.area-off .addmod { opacity: 0.6; }
-table.modules { margin: 0.5em 0 1em; }
-table.modules td.order, table.modules td.actions { width: 1%; }
-table.modules th { width: 10em; }
-.addmod { border-top: 1px solid var(--border); padding-top: 0.6em; }
-.addmod label, .add label { display: inline-block; margin: 0 0.8em 0.4em 0; min-width: 14em; }
-.addmod select, .add select, .add input { width: auto; min-width: 14em; }
-.addmod button, .add button { vertical-align: bottom; margin-bottom: 0.4em; }
+/* module and menu rows */
+.arealist { margin: 0.6em 0 0; }
+.arealist.area-off .modlist, .arealist.area-off .addmod { opacity: 0.55; }
+.modlist { margin: 0.4em 0; }
+.modlist:empty { display: none; }
+.modrow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4em 0.7em; padding: 0.45em 0.6em; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); margin: 0 0 0.4em; }
+.modrow .order { display: inline-flex; gap: 0.2em; }
+.modrow .order .pos { width: 3.6em; margin: 0; padding: 0.2em 0.3em; font-size: 0.85em; }
+.modrow .modname { font-weight: 500; }
+.modrow .tag { margin-left: 0; }
+.modrow .modact { margin-left: auto; }
+.modrow .modact label.check { display: inline; margin: 0; font-size: 0.85em; }
+.modrow .modset { flex-basis: 100%; font-size: 0.95em; }
+.modrow .modset summary { cursor: pointer; color: var(--muted); font-size: 0.9em; }
+.modrow .modset summary .modsum { font-style: italic; }
+.modrow .modset[open] summary { color: var(--text); }
+.modrow .modset label { margin: 0.5em 0; }
+.modrow .modset .row { gap: 0 1em; }
+.modrow .modset .row label { flex: 0 1 14em; min-width: 10em; }
+.modrow .modset p:last-child { margin-bottom: 0.3em; }
+.modrow .linkfields { display: inline-flex; flex-wrap: wrap; gap: 0.4em; flex: 1 1 14em; }
+.modrow .linkfields input { flex: 1 1 8em; width: auto; margin: 0; padding: 0.25em 0.45em; font-size: 0.9em; }
+.addmod label { display: inline-block; margin: 0.3em 0 0.4em; min-width: 14em; }
+.addmod select { width: auto; min-width: 14em; }
.addrow { margin-top: 1em; border-top: 1px solid var(--border); padding-top: 0.6em; margin-bottom: 0; }
+.addrow h3 { margin: 0.5em 0 0.2em; padding: 0; border: 0; }
+.addrow select, .addrow input { width: auto; min-width: 14em; }
+.addrow button { vertical-align: bottom; margin-bottom: 0.4em; }
textarea.mono { font-size: 0.9em; }
.hoverpick { margin: 0.8em 0; }
.hoverpick > input { width: auto; margin: 0 0.4em 0 0; vertical-align: middle; }
.hoverpick > label.check { display: inline; margin: 0; }
.hoverpick .hover-color { margin: 0.4em 0 0 1.6em; }
.hoverpick > input:not(:checked) ~ .hover-color { display: none; }
-.savebar { position: sticky; bottom: 0; margin: 0; padding: 0.8em 0; background: var(--bg); border-top: 1px solid var(--border-strong); display: flex; gap: 0.8em; align-items: center; }
+/* image fields: the chosen image, the select (or the library button) and an upload */
+.imagepick { margin: 0.9em 0 0; }
+.imagepick > label { margin-bottom: 0.3em; }
+.pickrow { display: flex; gap: 1em; align-items: flex-start; }
+.pickrow .preview { flex: 0 0 auto; }
+.pickrow .preview img, .pickrow .preview .swatch { display: block; width: 96px; height: 72px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface-2); object-fit: contain; }
+.pickrow .preview .swatch { line-height: 72px; text-align: center; font-size: 1.6em; color: var(--muted); }
+.pickrow .pickctl { flex: 1 1 auto; min-width: 0; }
+.pickrow .pickctl select { max-width: 24em; margin: 0 0 0.5em; }
+.pickrow .pickctl .upload { margin: 0.5em 0 0; font-size: 0.85em; }
+.pickrow .pickctl .upload input { max-width: 24em; }
+.picker { position: fixed; inset: 0; z-index: 20; background: rgba(16, 24, 40, 0.45); padding: 3vh 1em; overflow: auto; }
+.picker-box { max-width: 860px; margin: 0 auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 8px 30px rgba(16, 24, 40, 0.25); padding: 1em 1.2em; }
+.picker-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6em; margin-bottom: 0.8em; }
+.picker-head input { flex: 1 1 12em; width: auto; margin: 0; padding: 0.4em 0.6em; }
+.picker .picks { display: flex; flex-wrap: wrap; gap: 0.7em; }
+.picker .pick { width: 120px; margin: 0; padding: 0.4em; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); text-align: center; font-size: 0.75em; font-weight: normal; cursor: pointer; }
+.picker .pick:hover { border-color: var(--accent); background: var(--surface-2); color: var(--text); }
+.picker .pick img { display: block; width: 100%; height: 64px; margin: 0 auto 0.3em; border: 1px solid var(--border); border-radius: 4px; background: var(--surface-2); object-fit: contain; }
+.picker .pick .name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.picker .pager { margin: 0.8em 0 0; }
+.picker .pager button[disabled] { opacity: 0.4; cursor: default; }
+.savebar { position: sticky; bottom: 0; z-index: 5; margin: 0; padding: 0.8em 0; background: var(--bg); border-top: 1px solid var(--border-strong); display: flex; flex-wrap: wrap; gap: 0.8em; align-items: center; }
+.savebar .unsaved { font-size: 0.85em; font-weight: 600; color: #92400e; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 999px; padding: 0.15em 0.7em; }
+.savebar .viewlink { margin-left: auto; font-size: 0.9em; }
details.reset { margin-top: 1.6em; }
/* ---- login --------------------------------------------------------------- */
.login-brand { text-align: center; font-weight: 700; font-size: 1.2em; letter-spacing: -0.01em; margin: 2.5em 0 1em; }
+@media (max-width: 900px) {
+ .design { display: block; }
+ .sidenav { position: static; flex-direction: row; flex-wrap: wrap; margin: 0 0 1em; gap: 0.3em; }
+ .sidenav a { border: 1px solid var(--border); background: var(--surface); }
+}
@media (max-width: 700px) {
body { font-size: 15px; }
.cols { grid-template-columns: 1fr; gap: 1.2em; }
.row { gap: 0; }
.row label { flex-basis: 100%; }
- .imagepick .pick { width: 46%; }
+ .picker .pick { width: 46%; }
.presets .preset { width: 46%; }
+ .sidenav { flex-wrap: nowrap; overflow-x: auto; white-space: nowrap; -webkit-overflow-scrolling: touch; margin: 0 -1em 1em; padding: 0 1em; }
+ .pickrow { flex-wrap: wrap; }
+ .modrow .modact label.check { font-size: 0.8em; }
.topbar .links { margin-left: 0; flex-basis: 100%; gap: 1em; }
.blogbar .topbar-inner { display: block; }
.blogbar .blogname { display: block; max-width: none; padding-bottom: 0.3em; }