mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-13 14:15:47 +00:00
feat(typography): extend typography system with Google Fonts and font packs
This commit is contained in:
1 parent
4fcfe33cc0
commit
24dc36f6d7
28 files changed
+669
-127
No files matched your search
File renamed without changes.
@@ -85,7 +85,7 @@
|
||||
.hb-ruby-name rt {
|
||||
/* Elegant, subdued annotation text */
|
||||
font-size: 0.45em;
|
||||
font-weight: 500;
|
||||
font-weight: var(--hb-font-weight-body-bold, 600);
|
||||
letter-spacing: 0.08em;
|
||||
line-height: 1.2;
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
/* Latin-optimized variant - smaller annotation for romanization over CJK */
|
||||
.hb-ruby-name--latin rt {
|
||||
font-size: 0.4em;
|
||||
font-weight: 500;
|
||||
font-weight: var(--hb-font-weight-body-bold, 600);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
@@ -278,4 +278,4 @@
|
||||
background-image:
|
||||
radial-gradient(at 20% 25%, hsla(212, 35%, 15%, 1) 0px, transparent 50%), radial-gradient(at 75% 30%, hsla(160, 35%, 15%, 1) 0px, transparent 50%),
|
||||
radial-gradient(at 25% 80%, hsla(280, 35%, 15%, 1) 0px, transparent 50%), radial-gradient(at 75% 85%, hsla(30, 35%, 15%, 1) 0px, transparent 50%);
|
||||
}
|
||||
}
|
||||
@@ -322,8 +322,27 @@
|
||||
|
||||
/* Note: Providing these palettes in @theme ensures theme-pack mappings like var(--color-rose-600) always resolve */
|
||||
|
||||
/* Font family */
|
||||
/* Font families */
|
||||
--font-family-sans: inter var, ui-sans-serif, system-ui, sans-serif, apple color emoji, segoe ui emoji, segoe ui symbol, noto color emoji;
|
||||
--font-heading: var(--font-family-sans);
|
||||
--font-body: var(--font-family-sans);
|
||||
--font-code: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--font-nav: var(--font-heading);
|
||||
|
||||
/* Font weights */
|
||||
--font-weight-heading: 700;
|
||||
--font-weight-body: 400;
|
||||
--hb-font-weight-body-bold: 600;
|
||||
|
||||
/* Line heights */
|
||||
--font-leading-heading: 1.2;
|
||||
--font-leading-body: 1.6;
|
||||
--font-leading-code: 1.5;
|
||||
|
||||
/* Letter spacing */
|
||||
--font-tracking-heading: -0.02em;
|
||||
--font-tracking-body: 0;
|
||||
--font-tracking-caps: 0.05em;
|
||||
|
||||
/* Font sizes */
|
||||
--font-size-xs: 0.75rem;
|
||||
@@ -336,4 +355,4 @@
|
||||
--font-size-4xl: 2.25rem;
|
||||
--font-size-5xl: 3rem;
|
||||
--font-size-6xl: 4rem;
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,40 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizelegibility;
|
||||
/* Apply the runtime font vars injected by Hugo's typography partial */
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
body {
|
||||
line-height: 1.5;
|
||||
font-family: var(--font-body);
|
||||
font-weight: var(--font-weight-body, 400);
|
||||
line-height: var(--font-leading-body, 1.6);
|
||||
letter-spacing: var(--font-tracking-body, 0);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: var(--font-weight-heading, 700);
|
||||
line-height: var(--font-leading-heading, 1.2);
|
||||
letter-spacing: var(--font-tracking-heading, -0.02em);
|
||||
}
|
||||
|
||||
code,
|
||||
pre,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--font-code);
|
||||
}
|
||||
}
|
||||
|
||||
/* Gradient Mesh Animations */
|
||||
@keyframes float {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0) translateX(0);
|
||||
@@ -61,7 +86,7 @@
|
||||
.prose a {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--color-primary-300);
|
||||
font-weight: 500;
|
||||
font-weight: var(--hb-font-weight-body-bold, 600);
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
@@ -100,4 +125,4 @@
|
||||
.prose code:not(:where(pre *))::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
/* Hugo Blox Framework Custom Classes */
|
||||
@layer components {
|
||||
|
||||
/* Section styles for Hugo Blox blocks */
|
||||
.hbb-section {
|
||||
padding-top: 6rem;
|
||||
@@ -8,7 +9,7 @@
|
||||
|
||||
.section-subheading {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
font-weight: var(--font-weight-heading, 700);
|
||||
}
|
||||
|
||||
/* Home section background styles */
|
||||
@@ -129,4 +130,4 @@
|
||||
section.dark {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
.search-result mark {
|
||||
background-color: rgb(254 240 138);
|
||||
color: rgb(17 24 39);
|
||||
font-weight: 600;
|
||||
font-weight: var(--hb-font-weight-body-bold, 600);
|
||||
padding: 0.05rem 0.2rem;
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
@@ -83,4 +83,4 @@
|
||||
.search-result:focus-visible {
|
||||
outline: 2px solid rgb(59 130 246);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
- --hb-spacing-base: Base spacing unit
|
||||
- --hb-spacing-section: Section padding
|
||||
- --hb-font-size-base: Base font size
|
||||
- --hb-font-size-scale: Modular scale ratio
|
||||
*/
|
||||
|
||||
/* Border radius utility - use instead of hardcoded rounded-* classes */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Academic"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
style: "serif"
|
||||
description: "Traditional serif pairing for academic, research, and institutional sites"
|
||||
|
||||
families:
|
||||
heading: "Lora"
|
||||
body: "Source Serif 4"
|
||||
code: "Source Code Pro"
|
||||
|
||||
weights:
|
||||
heading: [500, 600, 700]
|
||||
body: [400, 600]
|
||||
code: [400]
|
||||
|
||||
leading:
|
||||
heading: 1.2
|
||||
body: 1.7
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "0"
|
||||
body: "0"
|
||||
|
||||
variable:
|
||||
heading: true
|
||||
body: true
|
||||
code: false
|
||||
@@ -0,0 +1,35 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Developer"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
description: "Technical, code-forward pairing for developer portfolios and docs"
|
||||
|
||||
families:
|
||||
heading: "Space Grotesk"
|
||||
body: "IBM Plex Sans"
|
||||
code: "Fira Code"
|
||||
|
||||
weights:
|
||||
heading: [500, 700]
|
||||
body: [400, 500, 600]
|
||||
code: [400, 700]
|
||||
|
||||
leading:
|
||||
heading: 1.2
|
||||
body: 1.6
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "-0.02em"
|
||||
body: "0"
|
||||
|
||||
variable:
|
||||
heading: true
|
||||
body: false
|
||||
code: true
|
||||
@@ -0,0 +1,36 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Editorial"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
style: "serif"
|
||||
description: "Elegant serif pairing for magazines, blogs, and long-form content"
|
||||
|
||||
families:
|
||||
heading: "Playfair Display"
|
||||
body: "Merriweather"
|
||||
code: "Source Code Pro"
|
||||
|
||||
weights:
|
||||
heading: [600, 700, 800]
|
||||
body: [300, 400, 700]
|
||||
code: [400]
|
||||
|
||||
leading:
|
||||
heading: 1.15
|
||||
body: 1.7
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "-0.01em"
|
||||
body: "0"
|
||||
|
||||
variable:
|
||||
heading: true
|
||||
body: false
|
||||
code: false
|
||||
@@ -0,0 +1,30 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Geometric"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
description: "Bold, modern geometric sans-serif for startups and SaaS landing pages"
|
||||
|
||||
families:
|
||||
heading: "Montserrat"
|
||||
body: "Poppins"
|
||||
code: "JetBrains Mono"
|
||||
|
||||
weights:
|
||||
heading: [600, 700, 800]
|
||||
body: [300, 400, 500, 600]
|
||||
code: [400]
|
||||
|
||||
leading:
|
||||
heading: 1.15
|
||||
body: 1.6
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "-0.01em"
|
||||
body: "0"
|
||||
@@ -0,0 +1,35 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Humanist"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
description: "Friendly, approachable rounded sans-serif for community and personal sites"
|
||||
|
||||
families:
|
||||
heading: "Nunito"
|
||||
body: "Open Sans"
|
||||
code: "Ubuntu Mono"
|
||||
|
||||
weights:
|
||||
heading: [600, 700, 800]
|
||||
body: [400, 600]
|
||||
code: [400, 700]
|
||||
|
||||
leading:
|
||||
heading: 1.25
|
||||
body: 1.65
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "0"
|
||||
body: "0"
|
||||
|
||||
variable:
|
||||
heading: true
|
||||
body: true
|
||||
code: false
|
||||
@@ -0,0 +1,35 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "Modern"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
description: "Clean, modern sans-serif pairing — the default HugoBlox look"
|
||||
|
||||
families:
|
||||
heading: "Inter"
|
||||
body: "Inter"
|
||||
code: "JetBrains Mono"
|
||||
|
||||
weights:
|
||||
heading: [600, 700]
|
||||
body: [400, 500]
|
||||
code: [400]
|
||||
|
||||
leading:
|
||||
heading: 1.2
|
||||
body: 1.6
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "-0.02em"
|
||||
body: "0"
|
||||
|
||||
variable:
|
||||
heading: true
|
||||
body: true
|
||||
code: true
|
||||
@@ -0,0 +1,30 @@
|
||||
# HugoBlox Font Pack
|
||||
# https://docs.hugoblox.com
|
||||
|
||||
hugoblox:
|
||||
meta:
|
||||
format: "hugoblox-font@1"
|
||||
name: "System"
|
||||
vendor: "hugoblox"
|
||||
version: "1.0.0"
|
||||
license: "MIT"
|
||||
description: "System fonts only — zero network requests, maximum performance"
|
||||
|
||||
families:
|
||||
heading: "system-sans"
|
||||
body: "system-sans"
|
||||
code: "system-mono"
|
||||
|
||||
weights:
|
||||
heading: [600, 700]
|
||||
body: [400, 500]
|
||||
code: [400]
|
||||
|
||||
leading:
|
||||
heading: 1.2
|
||||
body: 1.6
|
||||
code: 1.5
|
||||
|
||||
tracking:
|
||||
heading: "-0.02em"
|
||||
body: "0"
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
{{ if $palette }}
|
||||
{{ range $shade, $value := $palette }}
|
||||
/* Note: converting OKLCH to raw RGB CSS vars requires Tailwind 4's internal conversion
|
||||
{{/* Note: converting OKLCH to raw RGB CSS vars requires Tailwind 4's internal conversion
|
||||
or we output the raw color value directly if theme.css supports it.
|
||||
|
||||
The existing system expects --hb-primary-50-rgb to be a comma-less RGB triplet "255 255 255".
|
||||
@@ -110,15 +110,15 @@
|
||||
|
||||
Since we cannot easily calculate OKLCH -> RGB in Hugo templates, we should
|
||||
OUTPUT THE HIGHER LEVEL VARIABLES directly here to override theme.css.
|
||||
*/
|
||||
*/}}
|
||||
--color-{{$name}}-{{$shade}}: {{ $value }};
|
||||
{{ end }}
|
||||
|
||||
/* Also set the legacy RGB vars to a fallback (500 shade) to prevent breakage
|
||||
in case something accesses the raw -rgb var directly, though unlikely */
|
||||
{{/* Also set the legacy RGB vars to a fallback (500 shade) to prevent breakage
|
||||
in case something accesses the raw -rgb var directly, though unlikely */}}
|
||||
--hb-{{$name}}-500-rgb: 128 128 128;
|
||||
{{ else }}
|
||||
/* Unknown palette '{{$colorInput}}', falling back to Indigo-like default */
|
||||
{{/* Unknown palette '{{$colorInput}}', falling back to Indigo-like default */}}
|
||||
{{ partial "functions/generate_color_scale" (dict "name" $name "color" "#6366f1") }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -56,11 +56,90 @@
|
||||
|
||||
{{/* ═══════════════════════════════════════════════════════════════════════════
|
||||
TYPOGRAPHY
|
||||
|
||||
Font packs are loaded from data/fonts/ and provide:
|
||||
- families: { heading, body, code, nav }
|
||||
- weights: { heading, body, code, nav } (arrays)
|
||||
- sizes: { base, sm, lg }
|
||||
- leading: { heading, body, code }
|
||||
- tracking: { heading, body, caps }
|
||||
|
||||
Legacy support: typography.font (sans/serif/native) maps to pack names
|
||||
═══════════════════════════════════════════════════════════════════════════ */}}
|
||||
{{ $hb_typography := $hb.typography | default dict }}
|
||||
{{ $typography := dict
|
||||
"font" ($hb_typography.font | default "sans")
|
||||
"size" ($hb_typography.size | default "md")
|
||||
|
||||
{{/* Resolve font pack name */}}
|
||||
{{ $font_pack_name := $hb_typography.pack | default "modern" }}
|
||||
|
||||
{{/* Load the font pack */}}
|
||||
{{ $font_pack := partial "functions/load_font_pack" $font_pack_name }}
|
||||
|
||||
{{/* Resolve families — inline overrides take priority */}}
|
||||
{{ $pack_families := $font_pack.families | default dict }}
|
||||
{{ $inline_families := $hb_typography.families | default dict }}
|
||||
{{ $families := dict
|
||||
"heading" ($inline_families.heading | default $pack_families.heading | default "Inter")
|
||||
"body" ($inline_families.body | default $pack_families.body | default "Inter")
|
||||
"code" ($inline_families.code | default $pack_families.code | default "JetBrains Mono")
|
||||
"nav" ($inline_families.nav | default $pack_families.nav | default "")
|
||||
}}
|
||||
|
||||
{{/* Resolve weights — inline overrides take priority */}}
|
||||
{{ $pack_weights := $font_pack.weights | default dict }}
|
||||
{{ $inline_weights := $hb_typography.weights | default dict }}
|
||||
{{ $weights := dict
|
||||
"heading" ($inline_weights.heading | default $pack_weights.heading | default (slice 700))
|
||||
"body" ($inline_weights.body | default $pack_weights.body | default (slice 400))
|
||||
"code" ($inline_weights.code | default $pack_weights.code | default (slice 400))
|
||||
"nav" ($inline_weights.nav | default $pack_weights.nav | default (slice))
|
||||
}}
|
||||
|
||||
{{/* Resolve sizes — inline overrides take priority */}}
|
||||
{{ $pack_sizes := $font_pack.sizes | default dict }}
|
||||
{{ $inline_sizes := $hb_typography.sizes | default dict }}
|
||||
{{ $sizes := dict
|
||||
"base" ($inline_sizes.base | default $pack_sizes.base | default "1rem")
|
||||
"sm" ($inline_sizes.sm | default $pack_sizes.sm | default "0.875rem")
|
||||
"lg" ($inline_sizes.lg | default $pack_sizes.lg | default "1.125rem")
|
||||
}}
|
||||
|
||||
{{/* Resolve leading (line-height) */}}
|
||||
{{ $pack_leading := $font_pack.leading | default dict }}
|
||||
{{ $inline_leading := $hb_typography.leading | default dict }}
|
||||
{{ $leading := dict
|
||||
"heading" ($inline_leading.heading | default $pack_leading.heading | default 1.2)
|
||||
"body" ($inline_leading.body | default $pack_leading.body | default 1.6)
|
||||
"code" ($inline_leading.code | default $pack_leading.code | default 1.5)
|
||||
}}
|
||||
|
||||
{{/* Resolve tracking (letter-spacing) */}}
|
||||
{{ $pack_tracking := $font_pack.tracking | default dict }}
|
||||
{{ $inline_tracking := $hb_typography.tracking | default dict }}
|
||||
{{ $tracking := dict
|
||||
"heading" ($inline_tracking.heading | default $pack_tracking.heading | default "-0.02em")
|
||||
"body" ($inline_tracking.body | default $pack_tracking.body | default "0")
|
||||
"caps" ($inline_tracking.caps | default $pack_tracking.caps | default "0.05em")
|
||||
}}
|
||||
|
||||
{{/* Resolve variable font flags */}}
|
||||
{{ $pack_variable := $font_pack.variable | default dict }}
|
||||
{{ $inline_variable := $hb_typography.variable | default dict }}
|
||||
{{ $variable := dict
|
||||
"heading" ($inline_variable.heading | default $pack_variable.heading | default false)
|
||||
"body" ($inline_variable.body | default $pack_variable.body | default false)
|
||||
"code" ($inline_variable.code | default $pack_variable.code | default false)
|
||||
"nav" ($inline_variable.nav | default $pack_variable.nav | default false)
|
||||
}}
|
||||
|
||||
{{ $typography := dict
|
||||
"pack" $font_pack_name
|
||||
"style" ($font_pack.meta.style | default "sans")
|
||||
"families" $families
|
||||
"weights" $weights
|
||||
"variable" $variable
|
||||
"sizes" $sizes
|
||||
"leading" $leading
|
||||
"tracking" $tracking
|
||||
}}
|
||||
|
||||
{{/* ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{{/*
|
||||
HugoBlox Theme Engine: Layout Tokens
|
||||
|
||||
Resolves layout design tokens (radius, spacing) from the theme config
|
||||
and emits them as CSS custom properties.
|
||||
|
||||
Input: Theme config dict from get_theme_config:
|
||||
- layout: { radius, spacing }
|
||||
|
||||
Output: <style> (CSS custom properties for --hb-radius, --hb-spacing-*)
|
||||
*/}}
|
||||
|
||||
{{/* Map radius tokens to CSS values */}}
|
||||
{{ $radius := .layout.radius | default "md" }}
|
||||
{{ $radius_value := "0.5rem" }}
|
||||
{{ if eq $radius "none" }}
|
||||
{{ $radius_value = "0" }}
|
||||
{{ else if eq $radius "sm" }}
|
||||
{{ $radius_value = "0.25rem" }}
|
||||
{{ else if eq $radius "md" }}
|
||||
{{ $radius_value = "0.5rem" }}
|
||||
{{ else if eq $radius "lg" }}
|
||||
{{ $radius_value = "0.75rem" }}
|
||||
{{ else if eq $radius "full" }}
|
||||
{{ $radius_value = "9999px" }}
|
||||
{{ else }}
|
||||
{{ warnf "LAYOUT TOKEN WARNING: Unknown radius value '%s'. Expected: none | sm | md | lg | full. Falling back to 'md' (0.5rem)." $radius }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Map spacing tokens to CSS values */}}
|
||||
{{ $spacing := .layout.spacing | default "comfortable" }}
|
||||
{{ $spacing_base := "1rem" }}
|
||||
{{ $spacing_section := "4rem" }}
|
||||
{{ if eq $spacing "compact" }}
|
||||
{{ $spacing_base = "0.75rem" }}
|
||||
{{ $spacing_section = "2.5rem" }}
|
||||
{{ else if eq $spacing "comfortable" }}
|
||||
{{ $spacing_base = "1rem" }}
|
||||
{{ $spacing_section = "4rem" }}
|
||||
{{ else if eq $spacing "spacious" }}
|
||||
{{ $spacing_base = "1.25rem" }}
|
||||
{{ $spacing_section = "6rem" }}
|
||||
{{ else }}
|
||||
{{ warnf "LAYOUT TOKEN WARNING: Unknown spacing value '%s'. Expected: compact | comfortable | spacious. Falling back to 'comfortable'." $spacing }}
|
||||
{{ end }}
|
||||
|
||||
<style>
|
||||
:root {
|
||||
/* Layout — Border Radius */
|
||||
--hb-radius: {{ $radius_value | safeCSS }};
|
||||
|
||||
/* Layout — Spacing */
|
||||
--hb-spacing-base: {{ $spacing_base | safeCSS }};
|
||||
--hb-spacing-section: {{ $spacing_section | safeCSS }};
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
{{/*
|
||||
HugoBlox Theme Engine: Loads and validates a HugoBlox font pack
|
||||
|
||||
Input: Font pack name (string), e.g. "modern-sans" or "acme/editorial"
|
||||
|
||||
Returns: The font pack data object (families, weights, sizes, leading, tracking) or empty dict if invalid
|
||||
|
||||
Validates:
|
||||
- Font pack exists in data/fonts/
|
||||
- Required 'hugoblox' wrapper is present
|
||||
- Required 'meta.format' starts with 'hugoblox-font@'
|
||||
*/}}
|
||||
|
||||
{{ $pack_name := . }}
|
||||
{{ $result := dict }}
|
||||
|
||||
{{/* Parse vendor/name format */}}
|
||||
{{ $vendor := "" }}
|
||||
{{ $name := $pack_name }}
|
||||
|
||||
{{ if strings.Contains $pack_name "/" }}
|
||||
{{ $parts := split $pack_name "/" }}
|
||||
{{ $vendor = index $parts 0 }}
|
||||
{{ $name = index $parts 1 }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Load raw font pack data */}}
|
||||
{{ $raw_pack := dict }}
|
||||
|
||||
{{ if $vendor }}
|
||||
{{/* Third-party font pack: data/fonts/vendor/name.yaml */}}
|
||||
{{ $vendor_fonts := index site.Data.fonts $vendor }}
|
||||
{{ if $vendor_fonts }}
|
||||
{{ $raw_pack = index $vendor_fonts $name }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{/* Built-in or user font pack: data/fonts/name.yaml */}}
|
||||
{{ with index site.Data.fonts $name }}
|
||||
{{ $raw_pack = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Validate font pack structure */}}
|
||||
{{ if not $raw_pack }}
|
||||
{{ warnf "FONT PACK ERROR: Font pack '%s' not found in data/fonts/" $pack_name }}
|
||||
{{ else if not $raw_pack.hugoblox }}
|
||||
{{ warnf "FONT PACK ERROR: Font pack '%s' is not a valid HugoBlox font pack (missing 'hugoblox' key)" $pack_name }}
|
||||
{{ else }}
|
||||
{{ $pack := $raw_pack.hugoblox }}
|
||||
|
||||
{{ if not $pack.meta }}
|
||||
{{ warnf "FONT PACK ERROR: Font pack '%s' is missing the required 'meta' block" $pack_name }}
|
||||
{{ else }}
|
||||
{{/* Validate format version */}}
|
||||
{{ $format := $pack.meta.format | default "" }}
|
||||
{{ if not (hasPrefix $format "hugoblox-font@") }}
|
||||
{{ warnf "FONT PACK ERROR: Font pack '%s' has invalid format '%s'. Expected 'hugoblox-font@1'" $pack_name $format }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Return the validated font pack data (without the hugoblox wrapper) */}}
|
||||
{{ $result = $pack }}
|
||||
{{ end }}
|
||||
|
||||
{{ return $result }}
|
||||
@@ -0,0 +1,147 @@
|
||||
{{/*
|
||||
HugoBlox Theme Engine: Typography Rendering
|
||||
|
||||
Resolves font families, emits Google Fonts <link> tags,
|
||||
and generates CSS custom properties for typography.
|
||||
|
||||
Input: Theme config dict from get_theme_config:
|
||||
- typography: { families, weights, sizes, leading, tracking }
|
||||
|
||||
Output: <link> (Google Fonts) + <style> (CSS custom properties)
|
||||
*/}}
|
||||
|
||||
{{ $typo := .typography }}
|
||||
{{ $pack_style := $typo.style | default "sans" }}
|
||||
{{ $families := $typo.families }}
|
||||
{{ $weights := $typo.weights }}
|
||||
{{ $variable := $typo.variable | default dict }}
|
||||
{{ $leading := $typo.leading }}
|
||||
{{ $tracking := $typo.tracking }}
|
||||
{{ $sizes := $typo.sizes }}
|
||||
|
||||
{{/* System font stacks */}}
|
||||
{{ $system_sans := "ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'" }}
|
||||
{{ $system_serif := "ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif" }}
|
||||
{{ $system_mono := "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace" }}
|
||||
|
||||
{{/* Map font names to CSS values and collect Google Font families */}}
|
||||
{{ $resolve_family := dict }}
|
||||
{{ $roles := slice "heading" "body" "code" "nav" }}
|
||||
{{ range $role := $roles }}
|
||||
{{ $name := index $families $role }}
|
||||
{{ if not $name }}
|
||||
{{ continue }}
|
||||
{{ end }}
|
||||
{{ $css := "" }}
|
||||
{{ $is_google := false }}
|
||||
{{ if eq $name "system-sans" }}
|
||||
{{ $css = $system_sans }}
|
||||
{{ else if eq $name "system-serif" }}
|
||||
{{ $css = $system_serif }}
|
||||
{{ else if eq $name "system-mono" }}
|
||||
{{ $css = $system_mono }}
|
||||
{{ else }}
|
||||
{{/* Google Font — pick the right generic fallback based on pack style and role */}}
|
||||
{{ if eq $role "code" }}
|
||||
{{ $css = printf "'%s', %s" $name $system_mono }}
|
||||
{{ else if eq $pack_style "serif" }}
|
||||
{{ $css = printf "'%s', %s" $name $system_serif }}
|
||||
{{ else }}
|
||||
{{ $css = printf "'%s', %s" $name $system_sans }}
|
||||
{{ end }}
|
||||
{{ $is_google = true }}
|
||||
{{ end }}
|
||||
{{ $resolve_family = merge $resolve_family (dict $role (dict "css" $css "google" $is_google "name" $name)) }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Build Google Fonts URL */}}
|
||||
{{ $gf_families := slice }}
|
||||
{{ $seen := dict }}
|
||||
{{ range $role := $roles }}
|
||||
{{ with index $resolve_family $role }}
|
||||
{{ if .google }}
|
||||
{{ $name := .name }}
|
||||
{{ $is_variable := index $variable $role | default false }}
|
||||
{{/* Use full range for variable fonts, discrete weights for static */}}
|
||||
{{ $weight_spec := "" }}
|
||||
{{ if $is_variable }}
|
||||
{{ $weight_spec = "100..900" }}
|
||||
{{ else }}
|
||||
{{ $role_weights := index $weights $role | default (slice 400) }}
|
||||
{{ $weight_spec = delimit $role_weights "," }}
|
||||
{{ end }}
|
||||
{{/* Dedup key includes weight spec so the same font can appear as both static and variable if needed */}}
|
||||
{{ $dedup_key := printf "%s|%s" $name $weight_spec }}
|
||||
{{ if not (index $seen $dedup_key) }}
|
||||
{{ $seen = merge $seen (dict $dedup_key true) }}
|
||||
{{ $encoded := replace $name " " "+" }}
|
||||
{{ $gf_families = $gf_families | append (printf "%s:wght@%s" $encoded $weight_spec) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if gt (len $gf_families) 0 }}
|
||||
{{ $gf_url := printf "https://fonts.googleapis.com/css2?%s&display=swap" (delimit (apply $gf_families "printf" "family=%s" ".") "&") }}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="{{ $gf_url }}" rel="stylesheet" />
|
||||
{{ end }}
|
||||
|
||||
{{/* Font size vars */}}
|
||||
{{ $font_size_base := $sizes.base | default "1rem" }}
|
||||
{{ $font_size_sm := $sizes.sm | default "0.875rem" }}
|
||||
{{ $font_size_lg := $sizes.lg | default "1.125rem" }}
|
||||
|
||||
{{/* Resolve CSS font-family values */}}
|
||||
{{ $heading_css := $system_sans }}
|
||||
{{ with index $resolve_family "heading" }}{{ $heading_css = .css }}{{ end }}
|
||||
{{ $body_css := $system_sans }}
|
||||
{{ with index $resolve_family "body" }}{{ $body_css = .css }}{{ end }}
|
||||
{{ $code_css := $system_mono }}
|
||||
{{ with index $resolve_family "code" }}{{ $code_css = .css }}{{ end }}
|
||||
{{ $nav_css := $heading_css }}
|
||||
{{ with index $resolve_family "nav" }}{{ if .css }}{{ $nav_css = .css }}{{ end }}{{ end }}
|
||||
|
||||
{{/* Get primary weight for each role (first in array) */}}
|
||||
{{ $heading_weight := 700 }}
|
||||
{{ with $weights.heading }}{{ $heading_weight = index . 0 }}{{ end }}
|
||||
{{ $body_weight := 400 }}
|
||||
{{ with $weights.body }}{{ $body_weight = index . 0 }}{{ end }}
|
||||
|
||||
{{/* Bold body weight: second value in array, or primary + 200 (capped at 900) */}}
|
||||
{{ $body_weight_bold := math.Min 900 (add $body_weight 200) }}
|
||||
{{ with $weights.body }}{{ if ge (len .) 2 }}{{ $body_weight_bold = index . 1 }}{{ end }}{{ end }}
|
||||
|
||||
<style>
|
||||
:root {
|
||||
/* Typography — Font Families */
|
||||
--font-heading: {{ $heading_css | safeCSS }};
|
||||
--font-body: {{ $body_css | safeCSS }};
|
||||
--font-code: {{ $code_css | safeCSS }};
|
||||
--font-nav: {{ $nav_css | safeCSS }};
|
||||
|
||||
/* Typography — Font Weights */
|
||||
--font-weight-heading: {{ $heading_weight }};
|
||||
--font-weight-body: {{ $body_weight }};
|
||||
--hb-font-weight-body-bold: {{ $body_weight_bold }};
|
||||
|
||||
/* Typography — Line Heights */
|
||||
--font-leading-heading: {{ $leading.heading }};
|
||||
--font-leading-body: {{ $leading.body }};
|
||||
--font-leading-code: {{ $leading.code }};
|
||||
|
||||
/* Typography — Letter Spacing */
|
||||
--font-tracking-heading: {{ $tracking.heading | safeCSS }};
|
||||
--font-tracking-body: {{ $tracking.body | safeCSS }};
|
||||
--font-tracking-caps: {{ $tracking.caps | safeCSS }};
|
||||
|
||||
/* Typography — Font Sizes */
|
||||
--hb-font-size-base: {{ $font_size_base | safeCSS }};
|
||||
--hb-font-size-sm: {{ $font_size_sm | safeCSS }};
|
||||
--hb-font-size-lg: {{ $font_size_lg | safeCSS }};
|
||||
|
||||
/* Typography — Font Family Alias (Tailwind v4 compat) */
|
||||
--font-family-sans: {{ $body_css | safeCSS }};
|
||||
}
|
||||
</style>
|
||||
@@ -202,82 +202,10 @@
|
||||
|
||||
<title>{{$title}}</title>
|
||||
|
||||
{{/* Load font theme - uses typography config from get_theme_config */}}
|
||||
{{/* Supported values: 'sans' (Inter, default), 'serif' (Roboto Slab), 'native' (system fonts) */}}
|
||||
{{ $font_setting := $theme_config.typography.font | default "sans" }}
|
||||
{{ $use_custom_font := true }}
|
||||
{{ $font_family := "Inter var" }}
|
||||
{{ $font_file := "Inter.var.woff2" }}
|
||||
{{ $font_type := "woff2" }}
|
||||
|
||||
{{ if eq $font_setting "native" }}
|
||||
{{/* Native system font stack - no custom font loaded */}}
|
||||
{{ $use_custom_font = false }}
|
||||
{{ else if eq $font_setting "serif" }}
|
||||
{{ $font_file = "RobotoSlab-VariableFont_wght.ttf" }}
|
||||
{{ $font_type = "truetype" }}
|
||||
{{ end }}
|
||||
{{/* Default case (sans or any unrecognized value) uses Inter */}}
|
||||
|
||||
{{/* Map size tokens to CSS custom properties */}}
|
||||
{{ $font_size_base := "1rem" }}
|
||||
{{ $font_size_scale := "1.25" }}
|
||||
{{ if eq $theme_config.typography.size "sm" }}
|
||||
{{ $font_size_base = "0.875rem" }}
|
||||
{{ $font_size_scale = "1.2" }}
|
||||
{{ else if eq $theme_config.typography.size "lg" }}
|
||||
{{ $font_size_base = "1.125rem" }}
|
||||
{{ $font_size_scale = "1.333" }}
|
||||
{{ else if eq $theme_config.typography.size "xl" }}
|
||||
{{ $font_size_base = "1.25rem" }}
|
||||
{{ $font_size_scale = "1.414" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Map radius tokens to CSS values */}}
|
||||
{{ $radius_value := "0.5rem" }}
|
||||
{{ if eq $theme_config.layout.radius "none" }}
|
||||
{{ $radius_value = "0" }}
|
||||
{{ else if eq $theme_config.layout.radius "sm" }}
|
||||
{{ $radius_value = "0.25rem" }}
|
||||
{{ else if eq $theme_config.layout.radius "lg" }}
|
||||
{{ $radius_value = "0.75rem" }}
|
||||
{{ else if eq $theme_config.layout.radius "full" }}
|
||||
{{ $radius_value = "9999px" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Map spacing tokens to CSS values */}}
|
||||
{{ $spacing_base := "1rem" }}
|
||||
{{ $spacing_section := "4rem" }}
|
||||
{{ if eq $theme_config.layout.spacing "compact" }}
|
||||
{{ $spacing_base = "0.75rem" }}
|
||||
{{ $spacing_section = "2.5rem" }}
|
||||
{{ else if eq $theme_config.layout.spacing "spacious" }}
|
||||
{{ $spacing_base = "1.25rem" }}
|
||||
{{ $spacing_section = "6rem" }}
|
||||
{{ end }}
|
||||
|
||||
<style>
|
||||
:root {
|
||||
/* Typography tokens */
|
||||
--hb-font-size-base: {{ $font_size_base }};
|
||||
--hb-font-size-scale: {{ $font_size_scale }};
|
||||
|
||||
/* Layout tokens */
|
||||
--hb-radius: {{ $radius_value }};
|
||||
--hb-spacing-base: {{ $spacing_base }};
|
||||
--hb-spacing-section: {{ $spacing_section }};
|
||||
}
|
||||
{{ if $use_custom_font }}
|
||||
{{ $font_resource := resources.Get (printf "dist/font/%s" $font_file) }}
|
||||
@font-face {
|
||||
font-family: '{{$font_family}}';
|
||||
font-style: normal;
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
src: url({{ $font_resource.RelPermalink }}) format({{$font_type}});
|
||||
}
|
||||
{{ end }}
|
||||
</style>
|
||||
{{/* Typography: font loading, font families, and sizes */}}
|
||||
{{ partial "functions/typography.html" $theme_config }}
|
||||
{{/* Layout tokens: radius and spacing */}}
|
||||
{{ partial "functions/layout_tokens.html" $theme_config }}
|
||||
|
||||
{{ if .Params.design.background.image.filename }}
|
||||
{{/* See Hugo note on linking assets in styles: https://github.com/gohugoio/hugoThemes#common-permalink-issues */}}
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "academic"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "humanist"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "developer"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "modern"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "modern"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -34,8 +34,8 @@ hugoblox:
|
||||
# TYPOGRAPHY
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
font: "sans" # sans | serif | native
|
||||
size: "md" # sm | md | lg | xl
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "modern"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -59,10 +59,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "modern"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
@@ -69,10 +69,8 @@ hugoblox:
|
||||
# Font and text sizing
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
typography:
|
||||
# Font: 'sans' (Inter), 'serif' (Roboto Slab), or 'native' (system fonts)
|
||||
font: "sans"
|
||||
# Size: 'sm', 'md', 'lg', or 'xl' (affects base font size and scale)
|
||||
size: "md"
|
||||
# Theme pack: name from data/fonts/
|
||||
pack: "geometric"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# LAYOUT
|
||||
|
||||
Reference in new issue
Block a user