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/web_test.go | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'internal/web/web_test.go') diff --git a/internal/web/web_test.go b/internal/web/web_test.go index 2f3bc4f..e12f9e0 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -101,17 +101,17 @@ func TestThemeNormalizeAndCSS(t *testing.T) { t.Errorf("old theme content defaults: %+v", th) } f := url.Values{"bg_color": {"#ABCDEF"}, "bg_image": {"not-a-uuid"}, "logo_size": {"large"}, - "link_hover": {"#ff0000"}, "nav_hover": {"#00ff00"}, "nav_hover_custom": {"on"}, "nav_style": {"uppercase"}, "nav_align": {"center"}, + "link_hover": {"#ff0000"}, "nav_hover": {"#00ff00"}, "nav_hover_custom": {"on"}, "nav_upper": {"on"}, "nav_align": {"center"}, "link_underline": {"hover"}, "heading_font": {"serif"}, "content_style": {"flat"}, "content_padding": {"roomy"}, "date_format": {"iso"}, "header_rule": {"stripes"}} th = ThemeFromForm(DefaultTheme(), f) if th.BgColor != "#abcdef" || th.BgImage != "" || th.LogoSize != "large" || th.RuleClass() != "ruled rule-stripes" { t.Errorf("from form: %+v", th) } - if th.LinkHover != "" || th.NavHover != "#00ff00" || th.ShowDates || th.DateFormat != "iso" { + if th.LinkHover != "" || th.NavHover != "#00ff00" || th.ShowDates || th.DateFormat != "iso" || th.NavBold || !th.NavUpper { t.Errorf("hover checkbox / unticked checkboxes: %+v", th) } css := th.CSS() - for _, want := range []string{"background-color:#abcdef", "text-transform:uppercase", ".site-nav a:hover { color:#00ff00", "a:hover { text-decoration:underline", + for _, want := range []string{"background-color:#abcdef", ".site-nav a:hover { color:#00ff00", "a:hover { text-decoration:underline", "h1, h2, h3, h4, h5, h6 { font-family:Georgia", "text-align:center", "border-radius:0", "padding:2.2em 3em", "@media (max-width: 700px)", ".logo { max-height:140px"} { if !strings.Contains(css, want) { t.Errorf("css missing %q:\n%s", want, css) @@ -124,10 +124,10 @@ func TestThemeNormalizeAndCSS(t *testing.T) { if css := DefaultTheme().CSS(); strings.Contains(css, "padding:") || strings.Contains(css, "a:hover") { t.Errorf("default css should not set padding or hover: %s", css) } - // Plain menu links: regular weight and underlined, instead of blog.css's bold. - css = ThemeFromForm(DefaultTheme(), url.Values{"nav_style": {"plain"}}).CSS() - if !strings.Contains(css, ".site-nav a { color:#222222; font-weight:normal; text-decoration:underline; }") { - t.Errorf("plain nav css: %s", css) + // The menu links' weight, case and underline are body classes blog.css draws; the theme CSS only colours them. + css = DefaultTheme().CSS() + if !strings.Contains(css, ".site-nav a { color:#222222; }") || strings.Contains(css, "font-weight") || strings.Contains(css, "text-transform") { + t.Errorf("nav css: %s", css) } if css := DefaultTheme().CSS(); strings.Contains(css, "@font-face") || strings.Contains(css, "h1, h2") { t.Errorf("default css should declare no font faces or heading rule: %s", css) @@ -139,6 +139,33 @@ func TestThemeNormalizeAndCSS(t *testing.T) { } } +// The menu link styles are switches; the select they replaced is migrated +// from stored themes (bold + hover underline is what blog.css always drew). +func TestNavStyles(t *testing.T) { + th := ParseTheme([]byte(`{"nav_style":"uppercase"}`)) + if !th.NavUpper || !th.NavBold || th.NavUnderline != "hover" || th.NavStyle != "" || strings.Contains(string(th.JSON()), "nav_style") { + t.Errorf("uppercase migration: %+v %s", th, th.JSON()) + } + th = ParseTheme([]byte(`{"nav_style":"plain"}`)) + if th.NavBold || th.NavUpper || th.NavUnderline != "always" { + t.Errorf("plain migration: %+v", th) + } + for _, raw := range []string{`{}`, `{"nav_style":"normal"}`, `{"nav_underline":"wavy"}`} { + th = ParseTheme([]byte(raw)) + if !th.NavBold || th.NavUpper || th.NavUnderline != "hover" || th.NavClass() != "nav-ul-hover nav-bold" { + t.Errorf("%s: %+v %q", raw, th, th.NavClass()) + } + } + // New saves carry the switches: a stored false stays false, and unticked boxes are off. + if th = ParseTheme([]byte(`{"nav_bold":false,"nav_upper":true,"nav_underline":"never"}`)); th.NavBold || !th.NavUpper || th.NavUnderline != "never" { + t.Errorf("stored switches: %+v", th) + } + th = ThemeFromForm(DefaultTheme(), url.Values{"nav_underline": {"always"}}) + if th.NavBold || th.NavUpper || th.NavClass() != "nav-ul-always" { + t.Errorf("from form: %+v %q", th, th.NavClass()) + } +} + // Announcements on post pages are on by default (the look blogs had before // the switch) and, like every checkbox, off when the box is unticked; stored // themes from before the option get the default too. -- cgit v1.2.3