mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-20 09:46:01 -05:00
hscontrol/templates: refactor to use CSS classes and embedded files
Refactor template system to use go:embed for external assets and CSS classes for styling instead of inline styles: - general.go: Add go:embed directives for style.css and headscale.svg, replace inline styles with CSS classes (H1, H2, H3, P, etc.), add mdTypesetBody wrapper with Material for MkDocs styling - apple.go, oidc_callback.go, register_web.go, windows.go: Update to use new CSS-based helper functions (H1, H2, P, etc.) and mdTypesetBody for consistent layout This separates content from presentation, making templates easier to maintain and update. All styling is now centralized in style.css with Material for MkDocs design system.
This commit is contained in:
committed by
Kristoffer Dalby
parent
285c4e46a9
commit
3ed1067a95
@@ -1,46 +1,176 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/chasefleming/elem-go"
|
||||
"github.com/chasefleming/elem-go/attrs"
|
||||
"github.com/chasefleming/elem-go/styles"
|
||||
)
|
||||
|
||||
// bodyStyle provides consistent body styling across all templates with
|
||||
// a centered, readable layout and appropriate spacing.
|
||||
var bodyStyle = styles.Props{
|
||||
styles.Margin: "40px auto",
|
||||
styles.MaxWidth: "800px",
|
||||
styles.LineHeight: "1.5",
|
||||
styles.FontSize: "16px",
|
||||
styles.Color: "#444",
|
||||
styles.Padding: "0 10px",
|
||||
styles.FontFamily: "sans-serif",
|
||||
//go:embed style.css
|
||||
var headscaleCSS string
|
||||
|
||||
//go:embed headscale.svg
|
||||
var headscaleSVG string
|
||||
|
||||
|
||||
|
||||
// mdTypesetBody creates a body element with md-typeset styling
|
||||
// that matches the official Headscale documentation design.
|
||||
// Uses CSS classes with styles defined in headscaleCSS.
|
||||
func mdTypesetBody(children ...elem.Node) *elem.Element {
|
||||
return elem.Body(attrs.Props{
|
||||
attrs.Style: styles.Props{
|
||||
styles.MinHeight: "100vh",
|
||||
styles.Display: "flex",
|
||||
styles.FlexDirection: "column",
|
||||
styles.AlignItems: "center",
|
||||
styles.BackgroundColor: "#ffffff",
|
||||
styles.Padding: "3rem 1.5rem",
|
||||
}.ToInline(),
|
||||
"translate": "no",
|
||||
},
|
||||
elem.Div(attrs.Props{
|
||||
attrs.Class: "md-typeset",
|
||||
attrs.Style: styles.Props{
|
||||
styles.MaxWidth: "min(800px, 90vw)",
|
||||
styles.Width: "100%",
|
||||
}.ToInline(),
|
||||
}, children...),
|
||||
)
|
||||
}
|
||||
|
||||
// headerStyle provides consistent header styling with improved line height
|
||||
var headerStyle = styles.Props{
|
||||
styles.LineHeight: "1.2",
|
||||
// Styled Element Wrappers
|
||||
// These functions wrap elem-go elements using CSS classes.
|
||||
// Styling is handled by the CSS in headscaleCSS.
|
||||
|
||||
// H1 creates a H1 element styled by .md-typeset h1
|
||||
func H1(children ...elem.Node) *elem.Element {
|
||||
return elem.H1(nil, children...)
|
||||
}
|
||||
|
||||
// headerOne creates a level 1 heading with consistent styling
|
||||
// H2 creates a H2 element styled by .md-typeset h2
|
||||
func H2(children ...elem.Node) *elem.Element {
|
||||
return elem.H2(nil, children...)
|
||||
}
|
||||
|
||||
// H3 creates a H3 element styled by .md-typeset h3
|
||||
func H3(children ...elem.Node) *elem.Element {
|
||||
return elem.H3(nil, children...)
|
||||
}
|
||||
|
||||
// P creates a paragraph element styled by .md-typeset p
|
||||
func P(children ...elem.Node) *elem.Element {
|
||||
return elem.P(nil, children...)
|
||||
}
|
||||
|
||||
// Ol creates an ordered list element styled by .md-typeset ol
|
||||
func Ol(children ...elem.Node) *elem.Element {
|
||||
return elem.Ol(nil, children...)
|
||||
}
|
||||
|
||||
// Ul creates an unordered list element styled by .md-typeset ul
|
||||
func Ul(children ...elem.Node) *elem.Element {
|
||||
return elem.Ul(nil, children...)
|
||||
}
|
||||
|
||||
// A creates a link element styled by .md-typeset a
|
||||
func A(href string, children ...elem.Node) *elem.Element {
|
||||
return elem.A(attrs.Props{attrs.Href: href}, children...)
|
||||
}
|
||||
|
||||
// Code creates an inline code element styled by .md-typeset code
|
||||
func Code(children ...elem.Node) *elem.Element {
|
||||
return elem.Code(nil, children...)
|
||||
}
|
||||
|
||||
// Pre creates a preformatted text block styled by .md-typeset pre
|
||||
func Pre(children ...elem.Node) *elem.Element {
|
||||
return elem.Pre(nil, children...)
|
||||
}
|
||||
|
||||
// PreCode creates a code block inside Pre styled by .md-typeset pre > code
|
||||
func PreCode(code string) *elem.Element {
|
||||
return elem.Code(nil, elem.Text(code))
|
||||
}
|
||||
|
||||
// Deprecated: use H1, H2, H3 instead
|
||||
func headerOne(text string) *elem.Element {
|
||||
return elem.H1(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||
return H1(elem.Text(text))
|
||||
}
|
||||
|
||||
// headerTwo creates a level 2 heading with consistent styling
|
||||
// Deprecated: use H1, H2, H3 instead
|
||||
func headerTwo(text string) *elem.Element {
|
||||
return elem.H2(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||
return H2(elem.Text(text))
|
||||
}
|
||||
|
||||
// headerThree creates a level 3 heading with consistent styling
|
||||
// Deprecated: use H1, H2, H3 instead
|
||||
func headerThree(text string) *elem.Element {
|
||||
return elem.H3(attrs.Props{attrs.Style: headerStyle.ToInline()}, elem.Text(text))
|
||||
return H3(elem.Text(text))
|
||||
}
|
||||
|
||||
// contentContainer wraps page content with proper width.
|
||||
// Content inside is left-aligned by default.
|
||||
func contentContainer(children ...elem.Node) *elem.Element {
|
||||
containerStyle := styles.Props{
|
||||
styles.MaxWidth: "720px",
|
||||
styles.Width: "100%",
|
||||
styles.Display: "flex",
|
||||
styles.FlexDirection: "column",
|
||||
styles.AlignItems: "flex-start", // Left-align all children
|
||||
}
|
||||
|
||||
return elem.Div(attrs.Props{attrs.Style: containerStyle.ToInline()}, children...)
|
||||
}
|
||||
|
||||
// headscaleLogo returns the Headscale SVG logo for consistent branding across all pages.
|
||||
// The logo is styled by the .headscale-logo CSS class.
|
||||
func headscaleLogo() elem.Node {
|
||||
// Return the embedded SVG as-is
|
||||
return elem.Raw(headscaleSVG)
|
||||
}
|
||||
|
||||
// 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()},
|
||||
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")),
|
||||
)
|
||||
}
|
||||
|
||||
// listStyle provides consistent styling for ordered and unordered lists
|
||||
// EXTRACTED FROM: .md-typeset ol, .md-typeset ul CSS rules
|
||||
var listStyle = styles.Props{
|
||||
styles.LineHeight: lineHeightBase, // 1.6 - From .md-typeset
|
||||
styles.MarginTop: "1em", // From CSS: margin-top: 1em
|
||||
styles.MarginBottom: "1em", // From CSS: margin-bottom: 1em
|
||||
styles.PaddingLeft: "clamp(1.5rem, 5vw, 2.5rem)", // Responsive indentation
|
||||
}
|
||||
|
||||
// HtmlStructure creates a complete HTML document structure with proper meta tags
|
||||
// and semantic HTML5 structure. The head and body elements are passed as parameters
|
||||
// to allow for customization of each page.
|
||||
// Styling is provided via a CSS stylesheet (Material for MkDocs design system) with
|
||||
// minimal inline styles for layout and positioning.
|
||||
func HtmlStructure(head, body *elem.Element) *elem.Element {
|
||||
return elem.Html(attrs.Props{attrs.Lang: "en"},
|
||||
elem.Head(nil,
|
||||
@@ -56,9 +186,21 @@ func HtmlStructure(head, body *elem.Element) *elem.Element {
|
||||
attrs.Content: "width=device-width, initial-scale=1.0",
|
||||
}),
|
||||
elem.Link(attrs.Props{
|
||||
attrs.Rel: "icon",
|
||||
attrs.Rel: "icon",
|
||||
attrs.Href: "/favicon.ico",
|
||||
}),
|
||||
// Google Fonts for Roboto and Roboto Mono
|
||||
elem.Link(attrs.Props{
|
||||
attrs.Rel: "preconnect",
|
||||
attrs.Href: "https://fonts.gstatic.com",
|
||||
"crossorigin": "",
|
||||
}),
|
||||
elem.Link(attrs.Props{
|
||||
attrs.Rel: "stylesheet",
|
||||
attrs.Href: "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;700&display=swap",
|
||||
}),
|
||||
// Material for MkDocs CSS styles
|
||||
elem.Style(attrs.Props{attrs.Type: "text/css"}, elem.Raw(headscaleCSS)),
|
||||
head,
|
||||
),
|
||||
body,
|
||||
|
||||
Reference in New Issue
Block a user