aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/static
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-13 00:38:24 +0300
committergrm <grm@eyesin.space>2026-09-13 00:38:24 +0300
commitd11e4c9544f544b3a024c7ac705a674b4096cf50 (patch)
treea60be98a37b764dfcb655b2738c8a991a248103b /internal/web/static
parente974eb6f3feb06c8951f85c1d34161938905f807 (diff)
downloadblogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.tar.gz
blogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.tar.bz2
blogspace-d11e4c9544f544b3a024c7ac705a674b4096cf50.zip
Add the Layout tab: five areas with modules, columns and a menu editor
A blog page is now header, left column, main content, right column and footer, each holding an ordered list of modules (blog title, logo, menu, archive by year/month, recent posts, custom HTML, footer text, RSS link, site map). Side columns have percentage widths, can be hidden, and can keep their space when empty; on phones they stack under the posts. The menu becomes its own ordered list mixing pages and outside links. Announcements are placed per column at its top or bottom. Modules and menu items get tables; the migration derives them from each blog's old theme (nav position, footer text, show-in-nav pages) so existing blogs look the same. Custom HTML is deliberately stored and served as-is (owner's decision; see AGENTS.md for why the blast radius is the blogger's own origin). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/static')
-rw-r--r--internal/web/static/blog.css50
-rw-r--r--internal/web/static/dashboard.css45
2 files changed, 68 insertions, 27 deletions
diff --git a/internal/web/static/blog.css b/internal/web/static/blog.css
index 778c169..c785ca6 100644
--- a/internal/web/static/blog.css
+++ b/internal/web/static/blog.css
@@ -5,6 +5,8 @@ img { max-width: 100%; height: auto; }
.site-header { padding: 0; }
.header-image img { display: block; width: 100%; max-height: 320px; object-fit: cover; }
.header-text { padding: 1.4em 1em; }
+.header-logo { padding: 1em; }
+.header-logo img.logo { display: inline-block; width: auto; }
.site-title { margin: 0; font-size: 2em; line-height: 1.2; }
.site-title a { text-decoration: none; }
.tagline { margin: 0.3em 0 0; opacity: 0.85; }
@@ -12,7 +14,7 @@ img { max-width: 100%; height: auto; }
.nav-inner { padding: 0.3em 1em; }
.site-nav a { display: inline-block; padding: 0.5em 0.8em; text-decoration: none; font-weight: bold; }
.site-nav a.active, .site-nav a:hover { text-decoration: underline; }
-.body-wrap { padding: 1.5em 1em; overflow: hidden; }
+.body-wrap { padding: 1.5em 1em; }
.content { padding: 1.5em 2em; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
.page-title { margin-top: 0; }
.intro { padding-bottom: 1em; margin-bottom: 1.5em; border-bottom: 1px solid rgba(0,0,0,0.1); }
@@ -43,19 +45,43 @@ img { max-width: 100%; height: auto; }
.site-footer { margin-top: 1em; padding: 1.5em 0; text-align: center; }
.site-footer p { margin: 0.3em 0; }
.site-footer .small { font-size: 0.85em; opacity: 0.8; }
+.site-footer .mod { background: none; box-shadow: none; padding: 0; margin: 0 0 0.8em; }
+.site-footer .mod-title { font-size: 0.95em; margin: 0 0 0.3em; }
+.site-footer ul { list-style: none; margin: 0; padding: 0; }
+.site-footer .mod-sitemap li { display: inline-block; margin: 0 0.6em; }
+.custom-html { margin: 0 0 1.5em; }
+.custom-html-below { margin: 1.5em 0 0; }
-/* left sidebar layout: float the menu beside the content */
-.nav-left-sidebar .body-wrap .site-nav { float: left; width: 180px; border: 0; border-radius: 6px; padding: 0.5em 0; }
-.nav-left-sidebar .body-wrap .site-nav a { display: block; padding: 0.4em 1em; }
-.nav-left-sidebar .body-wrap .site-nav .nav-inner { padding: 0; text-align: left; } /* alignment is a horizontal-menu option */
-.nav-left-sidebar .body-wrap .content { margin-left: 200px; }
-.nav-left-sidebar .body-wrap .sidebar-notices { float: left; clear: left; width: 180px; margin-top: 1em; }
-.nav-left-sidebar .body-wrap .sidebar-notices .notice { font-size: 0.9em; padding: 0.6em 0.8em; }
+/* columns: main first in the HTML so it comes first on phones; on wide screens
+ flexbox puts the left column before it. Widths come from the theme CSS.
+ Browsers without flexbox see the columns stacked, which still reads fine. */
+.cols { display: flex; flex-wrap: wrap; align-items: flex-start; }
+body.has-left .wrap, body.has-right .wrap { max-width: none; } /* side columns: use the whole page */
+.col { width: 100%; }
+.col-left { order: -1; padding-right: 1.5em; }
+.col-right { padding-left: 1.5em; }
+/* side-column and footer modules */
+.mod { margin: 0 0 1.5em; padding: 1em 1.2em; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.15); font-size: 0.95em; }
+.mod > :last-child { margin-bottom: 0; }
+.mod-title { margin: 0 0 0.6em; font-size: 1em; text-transform: uppercase; letter-spacing: 0.05em; }
+.mod ul { margin: 0; padding-left: 1.2em; }
+.mod li { margin: 0.25em 0; }
+.mod-logo { text-align: center; }
+.mod-logo img.logo { width: auto; }
+.col .site-nav { border: 0; padding: 0.5em 0; }
+.col .site-nav .mod-title { padding: 0 1em; }
+.col .site-nav .nav-inner { padding: 0; }
+.col .site-nav a { display: block; padding: 0.4em 1em; }
+.mod-archive details { margin: 0.2em 0; }
+.mod-archive details details { margin-left: 1em; }
+.mod-archive summary { cursor: pointer; padding: 0.15em 0; }
+.mod-archive ul { margin: 0.2em 0 0.4em; }
+.col .notice { font-size: 0.9em; padding: 0.6em 0.8em; }
@media (max-width: 700px) {
.content { padding: 1em; }
.site-title { font-size: 1.5em; }
- .nav-left-sidebar .body-wrap .site-nav { float: none; width: auto; margin-bottom: 1em; }
- .nav-left-sidebar .body-wrap .site-nav a { display: inline-block; }
- .nav-left-sidebar .body-wrap .content { margin-left: 0; }
- .nav-left-sidebar .body-wrap .sidebar-notices { float: none; width: auto; margin: 0 0 1em; }
+ .cols { display: block; }
+ .col-left, .col-right { padding: 0; margin-top: 1.5em; }
+ .col-left { order: 0; }
+ .col-empty { display: none; } /* a reserved-but-empty column has nothing to stack */
}
diff --git a/internal/web/static/dashboard.css b/internal/web/static/dashboard.css
index 4ea0d43..7a66ba5 100644
--- a/internal/web/static/dashboard.css
+++ b/internal/web/static/dashboard.css
@@ -59,7 +59,7 @@ p { margin: 0.6em 0; }
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 {
+input[type=text], input[type=password], input[type=number], input:not([type]), input[type=file], select, textarea {
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; }
@@ -145,19 +145,32 @@ details.help[open] summary { margin-bottom: 0.5em; }
.presets .preset:hover { background: #ebe5d6; color: #1d1a17; }
.presets .sw { display: inline-block; width: 22%; height: 2.2em; margin: 0 1%; border: 1px solid rgba(0,0,0,0.25); vertical-align: middle; }
.presets .pname { display: block; margin-top: 0.4em; }
-/* menu position tiles + options that only apply to one layout; the show/hide is
- pure CSS (:checked ~), and browsers without it simply show everything */
-.navpos { overflow: hidden; }
-.navpos > input[type=radio] { position: absolute; opacity: 0; width: 1px; height: 1px; }
-.navpos .tile { float: left; width: 8.5em; margin: 0.3em 0.9em 0.9em 0; padding: 0.5em 0.4em 0.4em; border: 2px solid #ddd6c7; background: #fffdf8; text-align: center; cursor: pointer; font-size: 0.75em; }
-.navpos .tile svg { display: block; width: 80px; height: 56px; margin: 0 auto 0.4em; border: 1px solid #ddd6c7; }
-.navpos .tile span { display: block; }
-.navpos .tile:hover { border-color: #1d1a17; }
-.navpos > input:checked + .tile { border-color: #c8541a; box-shadow: 3px 3px 0 #c8541a; }
-.navpos > input:focus + .tile { outline: 2px solid #c8541a; outline-offset: 1px; }
-.navpos .only-horizontal, .navpos .only-sidebar { clear: both; }
-#np-side:checked ~ .only-horizontal { display: none; }
-#np-below:checked ~ .only-sidebar, #np-top:checked ~ .only-sidebar { display: none; }
+/* layout page: the column diagram, area cards with their module tables */
+.lay { margin: 0 0 1em; border: 2px solid #1d1a17; background: #f4efe4; font-family: "IBM Plex Mono", "SF Mono", Menlo, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 0.75em; text-transform: uppercase; letter-spacing: 0.05em; text-align: center; }
+.lay-row { padding: 0.5em; background: #1d1a17; color: #f4efe4; }
+.lay-cols { overflow: hidden; padding: 0.5em 0; }
+.lay-col { float: left; padding: 1.6em 0.3em; border: 1px dashed #1d1a17; background: #fffdf8; }
+.lay-col.lay-main { background: #ebe5d6; }
+.lay .off { opacity: 0.4; text-decoration: line-through; }
+.lay-col.off { text-decoration: none; }
+.lay-col.empty { background: transparent; }
+.row input[type=number] { width: 6em; }
+.cardhead { overflow: hidden; }
+.cardhead h2 { float: left; margin-right: 1em; }
+.cardhead .switch { float: right; margin-top: -0.2em; }
+.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: 2px dashed #ddd6c7; 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; }
+.addrow { margin-top: 1em; border-top: 2px dashed #ddd6c7; padding-top: 0.6em; }
+.addrow .add { float: left; width: 48%; margin-right: 4%; }
+.addrow .add + .add { margin-right: 0; }
+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; }
@@ -174,7 +187,9 @@ details.reset { margin-top: 1.6em; }
.cols .card, .row label, .thumb { float: none; width: auto; margin-right: 4px; }
.imagepick .pick { width: 46%; margin-right: 4%; }
.presets .preset { width: 46%; margin-right: 4%; }
- .navpos .tile { width: 30%; margin-right: 3%; }
+ .addrow .add { float: none; width: auto; margin-right: 0; }
+ .cardhead h2, .cardhead .switch { float: none; margin-top: 0; }
+ .cardhead .switch { display: block; margin-bottom: 0.6em; }
.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; }