From 3918020551eb1a7a44c57dd702df9dc35d8f6baa Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 13 Apr 2026 12:42:10 +0000 Subject: [PATCH] templates: use CSS variables in all shared components Replace hardcoded Go color constants with var(--hs-*) and var(--md-*) CSS custom properties in externalLink, orDivider, card, warningBox, downloadButton, and pageFooter. This ensures all components follow the dark mode theme automatically. Also switch pageFooter from div to semantic footer element and simplify externalLink by letting CSS handle link styling. Updates juanfont/headscale#3182 --- hscontrol/templates/design.go | 42 ++++++++++++---------------------- hscontrol/templates/general.go | 24 ++++++++----------- 2 files changed, 23 insertions(+), 43 deletions(-) diff --git a/hscontrol/templates/design.go b/hscontrol/templates/design.go index 18c98225..e9c81b33 100644 --- a/hscontrol/templates/design.go +++ b/hscontrol/templates/design.go @@ -108,15 +108,11 @@ func responsiveContainer(children ...elem.Node) *elem.Element { func card(title string, children ...elem.Node) *elem.Element { cardContent := children if title != "" { - // Prepend title as H3 if provided cardContent = append([]elem.Node{ elem.H3(attrs.Props{ attrs.Style: styles.Props{ styles.MarginTop: "0", styles.MarginBottom: spaceM, - styles.FontSize: fontSizeH3, - styles.LineHeight: lineHeightH3, // 1.5 - H3 line height - styles.Color: colorTextSecondary, }.ToInline(), }, elem.Text(title)), }, children...) @@ -124,12 +120,12 @@ func card(title string, children ...elem.Node) *elem.Element { return elem.Div(attrs.Props{ attrs.Style: styles.Props{ - styles.Background: colorBackgroundCard, - styles.Border: "1px solid " + colorBorderLight, - styles.BorderRadius: "0.5rem", // 8px rounded corners - styles.Padding: "clamp(1rem, 3vw, 1.5rem)", // Responsive padding + styles.Background: "var(--hs-bg)", + styles.Border: "1px solid var(--hs-border)", + styles.BorderRadius: "0.5rem", + styles.Padding: "clamp(1rem, 3vw, 1.5rem)", styles.MarginBottom: spaceL, - styles.BoxShadow: "0 1px 3px rgba(0,0,0,0.1)", // Subtle shadow + styles.BoxShadow: "0 1px 3px rgba(0,0,0,0.1)", }.ToInline(), }, cardContent...) } @@ -333,6 +329,12 @@ func inlineCode(code string) *elem.Element { // //nolint:unused // Used in apple.go template. func orDivider() *elem.Element { + lineStyle := styles.Props{ + styles.Flex: "1", + styles.Height: "1px", + styles.BackgroundColor: "var(--hs-border)", + }.ToInline() + return elem.Div(attrs.Props{ attrs.Style: styles.Props{ styles.Display: "flex", @@ -343,29 +345,17 @@ func orDivider() *elem.Element { styles.Width: "100%", }.ToInline(), }, - elem.Div(attrs.Props{ - attrs.Style: styles.Props{ - styles.Flex: "1", - styles.Height: "1px", - styles.BackgroundColor: colorBorderLight, - }.ToInline(), - }), + elem.Div(attrs.Props{attrs.Style: lineStyle}), elem.Strong(attrs.Props{ attrs.Style: styles.Props{ - styles.Color: colorTextSecondary, + styles.Color: "var(--md-default-fg-color--light)", styles.FontSize: fontSizeBase, styles.FontWeight: "500", "text-transform": "uppercase", "letter-spacing": "0.05em", }.ToInline(), }, elem.Text("or")), - elem.Div(attrs.Props{ - attrs.Style: styles.Props{ - styles.Flex: "1", - styles.Height: "1px", - styles.BackgroundColor: colorBorderLight, - }.ToInline(), - }), + elem.Div(attrs.Props{attrs.Style: lineStyle}), ) } @@ -526,10 +516,6 @@ func externalLink(href, text string) *elem.Element { attrs.Href: href, attrs.Rel: "noreferrer noopener", attrs.Target: "_blank", - attrs.Style: styles.Props{ - styles.Color: colorPrimaryAccent, // #4051b5 - base link color - styles.TextDecoration: "none", - }.ToInline(), }, elem.Text(text)) } diff --git a/hscontrol/templates/general.go b/hscontrol/templates/general.go index fd104a9c..392f974d 100644 --- a/hscontrol/templates/general.go +++ b/hscontrol/templates/general.go @@ -124,26 +124,20 @@ func headscaleLogo() elem.Node { // pageFooter creates a consistent footer for all pages. func pageFooter() *elem.Element { - footerStyle := styles.Props{ - styles.MarginTop: space3XL, - styles.TextAlign: "center", - styles.FontSize: fontSizeSmall, - styles.Color: colorTextSecondary, - styles.LineHeight: lineHeightBase, - } - - linkStyle := styles.Props{ - styles.Color: colorTextSecondary, - styles.TextDecoration: "underline", - } - - return elem.Div(attrs.Props{attrs.Style: footerStyle.ToInline()}, + return elem.Footer(attrs.Props{ + attrs.Style: styles.Props{ + styles.MarginTop: space3XL, + styles.TextAlign: "center", + styles.FontSize: fontSizeSmall, + styles.Color: "var(--md-default-fg-color--light)", + styles.LineHeight: lineHeightBase, + }.ToInline(), + }, elem.Text("Powered by "), elem.A(attrs.Props{ attrs.Href: "https://github.com/juanfont/headscale", attrs.Rel: "noreferrer noopener", attrs.Target: "_blank", - attrs.Style: linkStyle.ToInline(), }, elem.Text("Headscale")), ) }