diff options
| -rw-r--r-- | AGENTS.md | 4 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | internal/i18n/el.go | 4 | ||||
| -rw-r--r-- | internal/web/templates/dashboard/design.html | 1 | ||||
| -rw-r--r-- | internal/web/theme.go | 9 | ||||
| -rw-r--r-- | internal/web/web_test.go | 13 |
6 files changed, 30 insertions, 4 deletions
@@ -211,7 +211,9 @@ internal/web/ server.go (host router, middleware, render helpers) classes; column widths in the theme CSS under `@media (min-width: 701px)`). The page is always full width — there is no content max-width; the column percentages divide the page and `keep_columns` is how a blogger - gets a narrower, centred main column; + gets a narrower, centred main column, with `align_to_main` insetting the + header's and footer's `.wrap`s by the column widths (`calc(1em + N%)`, + wide screens only, backgrounds untouched) so they line up with it; below 700px `.cols` becomes block flow so the order is main → left → right → footer. Everything is rendered by the `module` template in `layouts/blog.html`. New blogs get `insertDefaultModules` (title + menu @@ -29,7 +29,8 @@ Server-rendered HTML, no JavaScript required, works on old browsers and phones. tags (with counts), tag cloud, a search box (finds posts by title and text, any case; several words match in order with anything in between), custom HTML, footer text, RSS link, site map. Column widths are percentages; on phones the columns - stack under the posts. The **menu** mixes blog pages and outside links. Posts per + stack under the posts; the header and footer can follow the main column's edges + for a centred, single-column look. The **menu** mixes blog pages and outside links. Posts per page, the date format, the site title size and the footer alignment live here too, as does whether listings show whole posts or **summaries** with a "Read more" link (a post's summary ends at a `<!--more-->` line, or after its first paragraphs). diff --git a/internal/i18n/el.go b/internal/i18n/el.go index 3c70fb2..88610b4 100644 --- a/internal/i18n/el.go +++ b/internal/i18n/el.go @@ -474,7 +474,9 @@ var el = map[string]string{ "A page or link filled in here is added when you save.": "Μια σελίδα ή ένας σύνδεσμος που συμπληρώνετε εδώ προστίθεται όταν αποθηκεύσετε.", "How the menu looks": "Πώς φαίνεται το μενού", "The box your posts and pages are shown in. It spans whatever the side columns leave.": "Το πλαίσιο στο οποίο εμφανίζονται οι δημοσιεύσεις και οι σελίδες σας. Απλώνεται σε ό,τι αφήνουν οι πλαϊνές στήλες.", - "Posts per page": "Δημοσιεύσεις ανά σελίδα", + "Posts per page": "Δημοσιεύσεις ανά σελίδα", + "Header and footer follow the main column": "Η κεφαλίδα και το υποσέλιδο ακολουθούν την κύρια στήλη", + "their text lines up with the main content instead of the page edges; the backgrounds stay full width": "το κείμενό τους ευθυγραμμίζεται με το κύριο περιεχόμενο αντί για τις άκρες της σελίδας· τα φόντα μένουν σε όλο το πλάτος", "On listings show": "Στις λίστες δείξε", "The whole post": "Ολόκληρη τη δημοσίευση", "A summary and a “Read more” link": "Περίληψη και σύνδεσμο «Διάβασε περισσότερα»", diff --git a/internal/web/templates/dashboard/design.html b/internal/web/templates/dashboard/design.html index 71597e8..c22a35e 100644 --- a/internal/web/templates/dashboard/design.html +++ b/internal/web/templates/dashboard/design.html @@ -216,6 +216,7 @@ </div> <p class="muted small">{{t "Between 15% and 40% each; the main content takes the rest. A column only appears when it has at least one module; without columns the posts take the whole width. On phones the columns stack: main content first, then the left and right columns, then the footer."}}</p> <label class="check"><input type="checkbox" name="keep_columns"{{if $t.KeepColumns}} checked{{end}}> {{t "Always keep the columns' space, even when a column is empty or hidden"}} <span class="muted">({{t "the main content then keeps its width and stays centred"}})</span></label> + <label class="check"><input type="checkbox" name="align_to_main"{{if $t.AlignToMain}} checked{{end}}> {{t "Header and footer follow the main column"}} <span class="muted">({{t "their text lines up with the main content instead of the page edges; the backgrounds stay full width"}})</span></label> <h3>{{t "Left column"}}</h3> <label class="check"><input type="checkbox" name="left_on" data-area="left"{{if $t.LeftOn}} checked{{end}}> {{t "Show the left column"}}</label> {{template "arealist" (dict "area" "left" "root" . "hint" (t "A narrow column beside your posts. Good for the menu, an archive or recent posts."))}} diff --git a/internal/web/theme.go b/internal/web/theme.go index 70b218e..1fc78ad 100644 --- a/internal/web/theme.go +++ b/internal/web/theme.go @@ -67,6 +67,10 @@ type Theme struct { // KeepColumns lays the side columns out even when empty or hidden, so // the main content keeps the same width on every page. KeepColumns bool `json:"keep_columns"` + // AlignToMain insets the header's and footer's content to the main + // column's edges (the backgrounds stay full width), so a blog with + // KeepColumns and empty columns reads as one centred column. + AlignToMain bool `json:"align_to_main"` } // Areas of the blog page, in page order. above/below sit inside the main column. @@ -250,6 +254,7 @@ func ThemeFromForm(cur Theme, f url.Values) Theme { t.HeaderOn, t.LeftOn, t.RightOn, t.FooterOn = on("header_on"), on("left_on"), on("right_on"), on("footer_on") t.LeftWidth, t.RightWidth = num("left_width"), num("right_width") t.KeepColumns = on("keep_columns") + t.AlignToMain = on("align_to_main") t.normalize() return t } @@ -452,6 +457,10 @@ a:hover {{"{"}}{{if .LinkHover}} color:{{.LinkHover}};{{end}}{{if eq .LinkUnderl {{- with .TitleSizeCSS}} .site-title { font-size:{{.}}; } {{- end}} +{{- if .AlignToMain}} + body.has-left .site-header .wrap, body.has-left .site-footer .wrap { padding-left:calc(1em + {{.LeftWidth}}%); } + body.has-right .site-header .wrap, body.has-right .site-footer .wrap { padding-right:calc(1em + {{.RightWidth}}%); } +{{- end}} } {{- with .PaddingPhone}} @media (max-width: 700px) { .content { padding:{{.}}; } } diff --git a/internal/web/web_test.go b/internal/web/web_test.go index a13697c..b707339 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -129,7 +129,7 @@ func TestLayoutTheme(t *testing.T) { // The layout switches ride in the design form: the areas are checkboxes, so an unticked one is off. th := ThemeFromForm(DefaultTheme(), url.Values{"left_width": {"40"}, "right_width": {"40"}, "keep_columns": {"on"}, "header_on": {"on"}, "left_on": {"on"}, "right_on": {"on"}, "posts_per_page": {"20"}, "title_size": {"large"}, "footer_align": {"left"}}) - if th.LeftWidth != 40 || th.RightWidth != 30 || th.MainWidth() != 30 || !th.KeepColumns { + if th.LeftWidth != 40 || th.RightWidth != 30 || th.MainWidth() != 30 || !th.KeepColumns || th.AlignToMain { t.Errorf("the right column should give way: %+v", th) } if th.FooterOn || !th.HeaderOn || th.PostsPerPage != 20 || th.TitleSize != "large" || th.FooterAlign != "left" { @@ -149,6 +149,17 @@ func TestLayoutTheme(t *testing.T) { t.Errorf("css missing %q:\n%s", want, css) } } + if strings.Contains(css, ".site-header .wrap") { + t.Error("header insets emitted without align_to_main") + } + th = ThemeFromForm(th, url.Values{"left_width": {"15"}, "right_width": {"20"}, "align_to_main": {"on"}}) + css = th.CSS() + for _, want := range []string{"body.has-left .site-header .wrap, body.has-left .site-footer .wrap { padding-left:calc(1em + 15%); }", + "body.has-right .site-header .wrap, body.has-right .site-footer .wrap { padding-right:calc(1em + 20%); }"} { + if !strings.Contains(css, want) { + t.Errorf("css missing %q:\n%s", want, css) + } + } th.LeftOn = false if th.AreaOn("left") || !th.AreaOn("above") || !th.AreaOn("bogus") { t.Errorf("area switches: %+v", th) |
