From f16bd0f79c624665580615321b5d13b4be27cdad Mon Sep 17 00:00:00 2001 From: grm Date: Thu, 17 Sep 2026 17:09:01 +0300 Subject: Add the Gizmo look for announcements: framed boxes that share a row gizmolab's "next event" and "next assembly" boxes were hand-written HTML in the home page's intro; they are announcements by nature, so now they can be: a fourth style, a box framed in the theme's link colour with an italic heading. Headed Gizmo boxes in the same spot share a row on wide screens (the notices wrapper is a flex row, stacking on old browsers like .cols); one without a heading takes the whole row, which is how a banner line goes above a pair of boxes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/db/migrations/blog/00009_gizmo_style.sql | 10 ++++++++++ internal/i18n/el.go | 4 +++- internal/store/sections.go | 2 +- internal/web/handlers_sections.go | 2 +- internal/web/static/blog.css | 14 +++++++++++++- internal/web/templates/dashboard/section_form.html | 2 ++ internal/web/templates/dashboard/sections.html | 2 +- internal/web/templates/layouts/blog.html | 4 +++- internal/web/theme.go | 1 + 9 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 internal/db/migrations/blog/00009_gizmo_style.sql (limited to 'internal') diff --git a/internal/db/migrations/blog/00009_gizmo_style.sql b/internal/db/migrations/blog/00009_gizmo_style.sql new file mode 100644 index 0000000..1af8733 --- /dev/null +++ b/internal/db/migrations/blog/00009_gizmo_style.sql @@ -0,0 +1,10 @@ +-- +goose Up +-- A fourth look for announcements: a box framed in the link colour, with +-- boxes in the same spot sitting side by side on wide screens. +ALTER TABLE sections DROP CONSTRAINT sections_style_check; +ALTER TABLE sections ADD CONSTRAINT sections_style_check CHECK (style IN ('plain', 'note', 'warning', 'gizmo')); + +-- +goose Down +UPDATE sections SET style = 'plain' WHERE style = 'gizmo'; +ALTER TABLE sections DROP CONSTRAINT sections_style_check; +ALTER TABLE sections ADD CONSTRAINT sections_style_check CHECK (style IN ('plain', 'note', 'warning')); diff --git a/internal/i18n/el.go b/internal/i18n/el.go index 88610b4..1fd6ceb 100644 --- a/internal/i18n/el.go +++ b/internal/i18n/el.go @@ -142,7 +142,9 @@ var el = map[string]string{ "Highlighted": "Τονισμένη", "Warning": "Προειδοποίηση", "Plain": "Απλή", - "Next meeting": "Επόμενη συνάντηση", + "Gizmo": "Gizmo", + "Gizmo is a box framed in your link colour. Gizmo boxes with a heading share a row on wide screens (up to three); one without a heading takes the whole row.": "Το Gizmo είναι ένα πλαίσιο στο χρώμα των συνδέσμων σου. Τα πλαίσια Gizmo με επικεφαλίδα μοιράζονται μια σειρά σε φαρδιές οθόνες (μέχρι τρία)· ένα χωρίς επικεφαλίδα πιάνει όλη τη σειρά.", + "Next meeting": "Επόμενη συνάντηση", "“Top” of a side column means above its modules, “bottom” below them. If that column is not shown on your blog (see": "«Κορυφή» μιας πλαϊνής στήλης σημαίνει πάνω από τα στοιχεία της, «τέλος» κάτω από αυτά. Αν η στήλη δεν εμφανίζεται στο ιστολόγιό σας (βλ.", "the announcement moves to the main content instead.": "η ανακοίνωση μεταφέρεται στο κύριο περιεχόμενο.", "Shown on the blog": "Εμφανίζεται στο ιστολόγιο", diff --git a/internal/store/sections.go b/internal/store/sections.go index 2d72ea4..95c6a00 100644 --- a/internal/store/sections.go +++ b/internal/store/sections.go @@ -14,7 +14,7 @@ type Section struct { BodyHTML string Format string // FormatMarkdown | FormatHTML: how BodyMD is read Placement string // -: left|main|right - top|bottom - Style string // plain | note | warning + Style string // plain | note | warning | gizmo Enabled bool SortOrder int CreatedAt time.Time diff --git a/internal/web/handlers_sections.go b/internal/web/handlers_sections.go index 8d6c9c9..04ba764 100644 --- a/internal/web/handlers_sections.go +++ b/internal/web/handlers_sections.go @@ -53,7 +53,7 @@ func (s *Server) handleSectionForm(w http.ResponseWriter, r *http.Request) { // normalizeSection clamps the enum fields so templates and CSS class names can trust them. func normalizeSection(sec *store.Section) { sec.Placement = pick(sec.Column(), "main", "left", "right") + "-" + pick(sec.Position(), "top", "bottom") - sec.Style = pick(sec.Style, "note", "plain", "warning") + sec.Style = pick(sec.Style, "note", "plain", "warning", "gizmo") } func (s *Server) handleSectionSave(w http.ResponseWriter, r *http.Request) { diff --git a/internal/web/static/blog.css b/internal/web/static/blog.css index ace5e5a..eabb314 100644 --- a/internal/web/static/blog.css +++ b/internal/web/static/blog.css @@ -48,7 +48,19 @@ img { max-width: 100%; height: auto; } .notice-title { margin: 0 0 0.3em; font-size: 1em; text-transform: uppercase; letter-spacing: 0.05em; } .notice-body > :first-child { margin-top: 0; } .notice-body > :last-child { margin-bottom: 0; } -.content > .notice:last-child { margin: 1.5em 0 0; } +.content > .notices:last-child { margin-top: 1.5em; } +.content > .notices:last-child > .notice:last-child { margin-bottom: 0; } +/* gizmo: a box framed in the link colour (set by the theme CSS). On wide + screens the flex wrapper puts up to three headed gizmo boxes in one row + (they start at 30% and grow); a gizmo box without a heading, like every + plain/note/warning box, takes the whole row */ +.notice-gizmo { padding: 1em 1.2em; border: 1px solid; background: none; } +.notice-gizmo .notice-title { font-size: 1.15em; font-style: italic; text-transform: none; letter-spacing: 0; margin-bottom: 0.6em; } +@media (min-width: 701px) { + .notices { display: flex; flex-wrap: wrap; gap: 0 1em; align-items: stretch; } + .notices .notice { flex: 1 1 100%; } + .notices .notice-gizmo.headed { flex: 1 1 30%; } +} .pager { margin-top: 2em; padding-top: 1em; border-top: 1px solid rgba(0,0,0,0.1); text-align: center; } .pager a, .pager span { margin: 0 0.8em; } .muted { opacity: 0.7; } diff --git a/internal/web/templates/dashboard/section_form.html b/internal/web/templates/dashboard/section_form.html index 0171aaa..0a0e4ea 100644 --- a/internal/web/templates/dashboard/section_form.html +++ b/internal/web/templates/dashboard/section_form.html @@ -20,8 +20,10 @@ + +

{{t "Gizmo is a box framed in your link colour. Gizmo boxes with a heading share a row on wide screens (up to three); one without a heading takes the whole row."}}

{{t "“Top” of a side column means above its modules, “bottom” below them. If that column is not shown on your blog (see"}} {{t "Design"}}) {{t "the announcement moves to the main content instead."}}

{{template "editor" (dict "name" "body" "value" $s.BodyMD "format" $s.Format "rows" 10 "tall" false "upload" (print "/b/" .Blog.Subdomain "/files/upload") "preview" (print "/b/" .Blog.Subdomain "/preview") "csrf" .CSRF)}} diff --git a/internal/web/templates/dashboard/sections.html b/internal/web/templates/dashboard/sections.html index 4d62f29..9f0e314 100644 --- a/internal/web/templates/dashboard/sections.html +++ b/internal/web/templates/dashboard/sections.html @@ -17,7 +17,7 @@ {{if .Title}}{{.Title}}{{else}}({{t "untitled"}}){{end}}{{if not .Enabled}}{{t "hidden"}}{{end}} {{if eq .Column "main"}}{{t "Main content"}}{{else if eq .Column "left"}}{{t "Left column"}}{{else}}{{t "Right column"}}{{end}}, {{t .Position}} - {{if eq .Style "note"}}{{t "Highlighted"}}{{else if eq .Style "warning"}}{{t "Warning"}}{{else}}{{t "Plain"}}{{end}} + {{if eq .Style "note"}}{{t "Highlighted"}}{{else if eq .Style "warning"}}{{t "Warning"}}{{else if eq .Style "gizmo"}}{{t "Gizmo"}}{{else}}{{t "Plain"}}{{end}}
{{t "delete"}} diff --git a/internal/web/templates/layouts/blog.html b/internal/web/templates/layouts/blog.html index 6e8b819..76475b9 100644 --- a/internal/web/templates/layouts/blog.html +++ b/internal/web/templates/layouts/blog.html @@ -43,10 +43,12 @@ {{end}} -{{define "notices"}}{{range .}}