diff options
Diffstat (limited to 'internal/web/theme.go')
| -rw-r--r-- | internal/web/theme.go | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/internal/web/theme.go b/internal/web/theme.go index 66ac7ad..d686c4e 100644 --- a/internal/web/theme.go +++ b/internal/web/theme.go @@ -79,6 +79,11 @@ type Theme struct { // 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"` + // PageWidth caps the columns' box at so many pixels (0 = the screen), + // centred or at the left per PageAlign; the header's and footer's + // content follows it when AlignToMain is on, their backgrounds never. + PageWidth int `json:"page_width"` + PageAlign string `json:"page_align"` // center | left } // Areas of the blog page, in page order. above/below sit inside the main column. @@ -88,6 +93,8 @@ const ( minColumnWidth = 15 maxColumnWidth = 40 maxColumnsSum = 70 // main keeps at least 30% + minPageWidth = 400 + maxPageWidth = 3000 ) // postsPerPageChoices are the listing sizes the design form offers; the first is the default. @@ -104,7 +111,7 @@ func DefaultTheme() Theme { ShowDates: true, DateFormat: "long", PostsPerPage: postsPerPageChoices[0], ListStyle: "full", PostImage: "top", TitleRule: "none", FooterBg: "#2f3a4a", FooterColor: "#d0d5dc", FooterAlign: "center", LogoSize: "medium", - HeaderOn: true, LeftOn: true, RightOn: true, FooterOn: true, LeftWidth: 25, RightWidth: 25, + HeaderOn: true, LeftOn: true, RightOn: true, FooterOn: true, LeftWidth: 25, RightWidth: 25, PageAlign: "center", } } @@ -202,6 +209,10 @@ func (t *Theme) normalize() { if t.LeftWidth+t.RightWidth > maxColumnsSum { // the right column gives way t.RightWidth = maxColumnsSum - t.LeftWidth } + if t.PageWidth != 0 { + t.PageWidth = clampInt(t.PageWidth, minPageWidth, maxPageWidth) + } + t.PageAlign = pick(t.PageAlign, "center", "left") } func clampInt(v, lo, hi int) int { @@ -272,6 +283,8 @@ func ThemeFromForm(cur Theme, f url.Values) Theme { t.LeftWidth, t.RightWidth = num("left_width"), num("right_width") t.KeepColumns = on("keep_columns") t.AlignToMain = on("align_to_main") + t.PageWidth = num("page_width") + t.PageAlign = get("page_align") t.normalize() return t } @@ -294,6 +307,29 @@ func (t Theme) AreaOn(area string) bool { // MainWidth is what the main column gets when both side columns are shown. func (t Theme) MainWidth() int { return 100 - t.LeftWidth - t.RightWidth } +// PageMargin centres the capped page box or leaves it at the left. +func (t Theme) PageMargin() string { + if t.PageAlign == "left" { + return "0" + } + return "0 auto" +} + +// Inset is the padding-<side> declarations (AlignToMain) that put the header's +// and footer's text at a side column's inner edge: the wrap's own 1em plus +// the column's share of the wrap's content box, which is the wrap less its two +// 1em paddings — so (1 - 2w/100)em + w% of the wrap. With a page width the +// wrap is that width or the screen, whichever is narrower: min() where the +// browser has it, the page width alone (the wide-screen case) before it. +func (t Theme) Inset(side string, w int) string { + em := strconv.FormatFloat(1-float64(2*w)/100, 'f', -1, 64) + "em" + if t.PageWidth == 0 { + return "padding-" + side + ":calc(" + em + " + " + strconv.Itoa(w) + "%);" + } + px := strconv.Itoa(w*t.PageWidth/100) + "px" + return "padding-" + side + ":calc(" + em + " + " + px + "); padding-" + side + ":calc(" + em + " + min(" + strconv.Itoa(w) + "%, " + px + "));" +} + // pickFile applies an imagepick or fontpick select: "none" clears, an id // selects, and an empty value (no select in the form) keeps what was there. func pickFile(cur, v string) string { @@ -515,6 +551,12 @@ a:hover {{"{"}}{{if .LinkHover}} color:{{.LinkHover}};{{end}}{{if eq .LinkUnderl .page-title, .post-title { border-color:{{.LinkColor}}; } .page-title::after, .post-title::after { color:{{.LinkColor}}; } {{- end}} .logo { max-height:{{.LogoMaxHeight}}; } +{{- if .PageWidth}} +.wrap.body-wrap { max-width:{{.PageWidth}}px; margin:{{.PageMargin}}; } +{{- if .AlignToMain}} +.site-header .wrap, .site-footer .wrap { max-width:{{.PageWidth}}px; margin:{{.PageMargin}}; } +{{- end}} +{{- end}} @media (min-width: 701px) { .col-left { width:{{.LeftWidth}}%; } .col-right { width:{{.RightWidth}}%; } body.has-left.has-right .col-main { width:{{.MainWidth}}%; } @@ -524,8 +566,8 @@ a:hover {{"{"}}{{if .LinkHover}} color:{{.LinkHover}};{{end}}{{if eq .LinkUnderl .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}}%); } + body.has-left .site-header .wrap, body.has-left .site-footer .wrap { {{.Inset "left" .LeftWidth}} } + body.has-right .site-header .wrap, body.has-right .site-footer .wrap { {{.Inset "right" .RightWidth}} } {{- end}} } {{- with .PaddingPhone}} |
