From b88a7d22c8d79b30de3403c631dd2c5f67669537 Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 19:17:17 +0300 Subject: Make the menu link styles switches that combine The one "Link style" select (normal, uppercase, plain) could not give uppercase links without the bold, or underlined bold links. Bold, uppercase and the underline (never, on hover, always) are now separate switches, drawn by blog.css from body classes (Theme.NavClass) so the theme CSS only colours the links. A stored nav_style is mapped onto the switches when the theme is read and dropped on the next save; themes without either keep the bold, underline-on-hover look blog.css always had. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/web/theme.go | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) (limited to 'internal/web/theme.go') diff --git a/internal/web/theme.go b/internal/web/theme.go index 0437f51..9eae856 100644 --- a/internal/web/theme.go +++ b/internal/web/theme.go @@ -43,10 +43,17 @@ type Theme struct { HeaderRule string `json:"header_rule"` // none | line | dots | stripes, drawn beside the title/logo modules in the header NavAlign string `json:"nav_align"` // left | center | right (header menu) - NavStyle string `json:"nav_style"` // normal | uppercase | plain (regular weight, underlined) - NavBg string `json:"nav_bg"` - NavText string `json:"nav_text"` - NavHover string `json:"nav_hover"` // "" = same as NavText + // The link styles are switches that combine; blog.css draws them from + // the body classes NavClass makes. NavStyle is the select they replaced + // (normal | uppercase | plain): still decoded from themes saved before, + // turned into the switches by ParseTheme and never written again. + NavStyle string `json:"nav_style,omitempty"` + NavBold bool `json:"nav_bold"` + NavUpper bool `json:"nav_upper"` // uppercase, letter-spaced + NavUnderline string `json:"nav_underline"` // hover | never | always + NavBg string `json:"nav_bg"` + NavText string `json:"nav_text"` + NavHover string `json:"nav_hover"` // "" = same as NavText ShowDates bool `json:"show_dates"` DateFormat string `json:"date_format"` // long | short | iso (rendered by i18n.FormatDate) @@ -108,7 +115,7 @@ func DefaultTheme() Theme { ContentStyle: "card", ContentPadding: "normal", Font: "sans", HeadingFont: "same", FontSize: "normal", HeaderImageHeight: "medium", HeaderAlign: "left", HeaderBg: "#2f3a4a", HeaderText: "#ffffff", TitleSize: "normal", HeaderRule: "none", - NavAlign: "left", NavStyle: "normal", NavBg: "#ffffff", NavText: "#222222", + NavAlign: "left", NavBold: true, NavUnderline: "hover", NavBg: "#ffffff", NavText: "#222222", ShowDates: true, DateFormat: "long", PostsPerPage: postsPerPageChoices[0], ListStyle: "full", PostImage: "top", TitleRule: "none", PostNotices: true, FooterBg: "#2f3a4a", FooterColor: "#d0d5dc", FooterAlign: "center", LogoSize: "medium", @@ -122,6 +129,15 @@ func ParseTheme(raw json.RawMessage) Theme { if len(raw) > 0 { _ = json.Unmarshal(raw, &t) } + // Themes saved with the old link-style select: it becomes the switches + // it stood for, and is dropped from the JSON on the next save. + switch t.NavStyle { + case "uppercase": + t.NavUpper = true + case "plain": + t.NavBold, t.NavUnderline = false, "always" + } + t.NavStyle = "" t.normalize() return t } @@ -190,7 +206,7 @@ func (t *Theme) normalize() { t.TitleSize = pick(t.TitleSize, "normal", "small", "large") t.HeaderRule = pick(t.HeaderRule, "none", "line", "dots", "stripes") t.NavAlign = pick(t.NavAlign, "left", "center", "right") - t.NavStyle = pick(t.NavStyle, "normal", "uppercase", "plain") + t.NavUnderline = pick(t.NavUnderline, "hover", "never", "always") t.NavBg = color(t.NavBg, d.NavBg) t.NavText = color(t.NavText, d.NavText) t.NavHover = optColor(t.NavHover) @@ -259,7 +275,9 @@ func ThemeFromForm(cur Theme, f url.Values) Theme { t.TitleSize = get("title_size") t.HeaderRule = get("header_rule") t.NavAlign = get("nav_align") - t.NavStyle = get("nav_style") + t.NavBold = on("nav_bold") + t.NavUpper = on("nav_upper") + t.NavUnderline = get("nav_underline") t.NavBg = get("nav_bg") t.NavText = get("nav_text") t.NavHover = hover("nav_hover") @@ -504,6 +522,19 @@ func (t Theme) RuleClass() string { return "ruled rule-" + t.HeaderRule } +// NavClass is the body classes blog.css draws the menu links from (weight, +// case, underline); the theme CSS only colours them. +func (t Theme) NavClass() string { + c := []string{"nav-ul-" + t.NavUnderline} + if t.NavBold { + c = append(c, "nav-bold") + } + if t.NavUpper { + c = append(c, "nav-upper") + } + return strings.Join(c, " ") +} + // TitleRuleClass is the body class blog.css draws the rule under page and // post titles from; the theme CSS only colours it. func (t Theme) TitleRuleClass() string { @@ -551,7 +582,7 @@ a:hover {{"{"}}{{if .LinkHover}} color:{{.LinkHover}};{{end}}{{if eq .LinkUnderl .header-image img { max-height:{{.HeaderImageMaxHeight}}; } .site-nav { background:{{.NavBg}}; } .nav-inner { text-align:{{.NavAlign}}; } -.site-nav a { color:{{.NavText}};{{if eq .NavStyle "uppercase"}} text-transform:uppercase; letter-spacing:0.05em; font-size:0.9em;{{else if eq .NavStyle "plain"}} font-weight:normal; text-decoration:underline;{{end}} } +.site-nav a { color:{{.NavText}}; } {{- if .NavHover}} .site-nav a:hover { color:{{.NavHover}}; } {{- end}} -- cgit v1.2.3