package theme import "strings" // GoogleFont represents a curated Google Font type GoogleFont struct { Family string `json:"family"` Category string `json:"category"` Variants []string `json:"variants"` } // CuratedGoogleFonts returns the list of popular Google Fonts // These are embedded in the binary - no API key needed func CuratedGoogleFonts() []GoogleFont { return []GoogleFont{ // Sans-serif {Family: "Inter", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Roboto", Category: "sans-serif", Variants: []string{"400", "500", "700", "400i", "700i"}}, {Family: "Open Sans", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Lato", Category: "sans-serif", Variants: []string{"400", "700", "400i", "700i"}}, {Family: "Montserrat", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Poppins", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Source Sans 3", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Nunito", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Raleway", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Work Sans", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "DM Sans", Category: "sans-serif", Variants: []string{"400", "500", "700", "400i", "700i"}}, {Family: "Manrope", Category: "sans-serif", Variants: []string{"400", "500", "600", "700"}}, {Family: "Plus Jakarta Sans", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Outfit", Category: "sans-serif", Variants: []string{"400", "500", "600", "700"}}, {Family: "Lexend", Category: "sans-serif", Variants: []string{"400", "500", "600", "700"}}, {Family: "Figtree", Category: "sans-serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Geist", Category: "sans-serif", Variants: []string{"400", "500", "600", "700"}}, // Serif {Family: "Playfair Display", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Merriweather", Category: "serif", Variants: []string{"400", "700", "400i", "700i"}}, {Family: "Lora", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "PT Serif", Category: "serif", Variants: []string{"400", "700", "400i", "700i"}}, {Family: "Source Serif 4", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Libre Baskerville", Category: "serif", Variants: []string{"400", "700", "400i"}}, {Family: "Crimson Text", Category: "serif", Variants: []string{"400", "600", "700", "400i", "600i", "700i"}}, {Family: "EB Garamond", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Bitter", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Cormorant", Category: "serif", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, // Monospace {Family: "JetBrains Mono", Category: "monospace", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Fira Code", Category: "monospace", Variants: []string{"400", "500", "600", "700"}}, {Family: "Source Code Pro", Category: "monospace", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "IBM Plex Mono", Category: "monospace", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Roboto Mono", Category: "monospace", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Space Mono", Category: "monospace", Variants: []string{"400", "700", "400i", "700i"}}, {Family: "Inconsolata", Category: "monospace", Variants: []string{"400", "500", "600", "700"}}, // Display {Family: "Oswald", Category: "display", Variants: []string{"400", "500", "600", "700"}}, {Family: "Bebas Neue", Category: "display", Variants: []string{"400"}}, {Family: "Anton", Category: "display", Variants: []string{"400"}}, {Family: "Archivo Black", Category: "display", Variants: []string{"400"}}, {Family: "Righteous", Category: "display", Variants: []string{"400"}}, {Family: "Rubik", Category: "display", Variants: []string{"400", "500", "600", "700", "400i", "700i"}}, {Family: "Space Grotesk", Category: "display", Variants: []string{"400", "500", "600", "700"}}, {Family: "Sora", Category: "display", Variants: []string{"400", "500", "600", "700"}}, // Handwriting {Family: "Dancing Script", Category: "handwriting", Variants: []string{"400", "500", "600", "700"}}, {Family: "Pacifico", Category: "handwriting", Variants: []string{"400"}}, {Family: "Caveat", Category: "handwriting", Variants: []string{"400", "500", "600", "700"}}, {Family: "Satisfy", Category: "handwriting", Variants: []string{"400"}}, } } // BuildGoogleFontURL generates a Google Fonts CSS URL func BuildGoogleFontURL(family string, variants []string) string { // Format: https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap // For italic: family=Inter:ital,wght@0,400;0,700;1,400;1,700 hasItalic := false weights := []string{} for _, v := range variants { if len(v) > 0 && v[len(v)-1] == 'i' { hasItalic = true weights = append(weights, v[:len(v)-1]) } else { weights = append(weights, v) } } // Deduplicate weights seen := make(map[string]bool) uniqueWeights := []string{} for _, w := range weights { if !seen[w] { seen[w] = true uniqueWeights = append(uniqueWeights, w) } } familyEncoded := family // Replace spaces with + for i := 0; i < len(familyEncoded); i++ { if familyEncoded[i] == ' ' { familyEncoded = familyEncoded[:i] + "+" + familyEncoded[i+1:] } } if hasItalic { // Use ital,wght axis var specs []string for _, w := range uniqueWeights { specs = append(specs, "0,"+w) // normal } for _, w := range uniqueWeights { specs = append(specs, "1,"+w) // italic } return "https://fonts.googleapis.com/css2?family=" + familyEncoded + ":ital,wght@" + joinStrings(specs, ";") + "&display=swap" } return "https://fonts.googleapis.com/css2?family=" + familyEncoded + ":wght@" + joinStrings(uniqueWeights, ";") + "&display=swap" } func joinStrings(strs []string, sep string) string { if len(strs) == 0 { return "" } var result strings.Builder result.WriteString(strs[0]) for i := 1; i < len(strs); i++ { result.WriteString(sep + strs[i]) } return result.String() }