mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-12 14:14:48 +00:00
222 lines
8.8 KiB
HTML
222 lines
8.8 KiB
HTML
{{/*
|
|
HugoBlox Theme Engine: Typography Rendering
|
|
|
|
Resolves font families, emits @font-face for bundled fonts,
|
|
Google Fonts <link> tags for remote fonts, and CSS custom properties.
|
|
|
|
Font resolution order (per family):
|
|
1. system-sans / system-serif / system-mono → native OS font stack
|
|
2. Bundled file in assets/dist/font/<Name>* → local @font-face
|
|
3. Fallback → Google Fonts CDN <link>
|
|
|
|
Input: Theme config dict from get_theme_config:
|
|
- typography: { families, weights, sizes, leading, tracking, variable }
|
|
|
|
Output: @font-face (bundled) + <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 / bundled 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 }}
|
|
{{ $bundled_res := 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 }}
|
|
{{/* Check for a locally bundled font file in assets/dist/font/ */}}
|
|
{{ $normalized := replace $name " " "" }}
|
|
{{ $bundled_res = resources.GetMatch (printf "dist/font/%s*" $normalized) }}
|
|
{{/* Build CSS font-family value with appropriate generic fallback */}}
|
|
{{ 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 }}
|
|
{{/* Only request from Google Fonts if no local bundle exists */}}
|
|
{{ if not $bundled_res }}
|
|
{{ $is_google = true }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ $resolve_family = merge $resolve_family (dict $role (dict "css" $css "google" $is_google "name" $name "bundled" $bundled_res)) }}
|
|
{{ end }}
|
|
|
|
{{/* Emit @font-face declarations for locally bundled fonts */}}
|
|
{{ $bundled_seen := dict }}
|
|
{{ range $role := $roles }}
|
|
{{ with index $resolve_family $role }}
|
|
{{ if .bundled }}
|
|
{{ $name := .name }}
|
|
{{ if not (index $bundled_seen $name) }}
|
|
{{ $bundled_seen = merge $bundled_seen (dict $name true) }}
|
|
{{ $res := .bundled }}
|
|
{{/* Publish the font file so Hugo copies it to the output directory */}}
|
|
{{ $res.Publish }}
|
|
{{/* Determine the CSS font format from the file extension */}}
|
|
{{ $ext := path.Ext $res.Name }}
|
|
{{ $format := "" }}
|
|
{{ if eq $ext ".woff2" }}
|
|
{{ $format = "woff2" }}
|
|
{{ else if eq $ext ".ttf" }}
|
|
{{ $format = "truetype" }}
|
|
{{ else if eq $ext ".woff" }}
|
|
{{ $format = "woff" }}
|
|
{{ else }}
|
|
{{ warnf "TYPOGRAPHY: Bundled font '%s' has unrecognized extension '%s'. Expected .woff2 or .ttf." $name $ext }}
|
|
{{ end }}
|
|
{{/* Determine weight range: variable font files get full range, static get discrete weights.
|
|
Detect variable fonts from the ".var." convention in the filename (e.g. Inter.var.woff2),
|
|
which is more reliable than the pack's variable flag (used for Google Fonts URL construction). */}}
|
|
{{ $is_var_file := strings.Contains $res.Name ".var." }}
|
|
{{ $weight_val := "400" }}
|
|
{{ if $is_var_file }}
|
|
{{ $weight_val = "100 900" }}
|
|
{{ else }}
|
|
{{/* Collect all discrete weights across roles using this font */}}
|
|
{{ $all_weights := slice }}
|
|
{{ range $r := $roles }}
|
|
{{ $entry := index $resolve_family $r }}
|
|
{{ if and $entry (eq (index $entry "name") $name) }}
|
|
{{ with index $weights $r }}
|
|
{{ range $w := . }}
|
|
{{ $all_weights = $all_weights | append $w }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ if gt (len $all_weights) 0 }}
|
|
{{ $weight_val = delimit ($all_weights | uniq | sort) " " }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ if $format }}
|
|
<style>
|
|
@font-face {
|
|
font-family: '{{ $name }}';
|
|
src: local('{{ $name }}'), url('{{ $res.RelPermalink }}') format('{{ $format }}');
|
|
font-weight: {{ $weight_val }};
|
|
font-display: swap;
|
|
}
|
|
</style>
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ end }}
|
|
{{ 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 */
|
|
--hb-font-heading: {{ $heading_css | safeCSS }};
|
|
--hb-font-body: {{ $body_css | safeCSS }};
|
|
--hb-font-code: {{ $code_css | safeCSS }};
|
|
--hb-font-nav: {{ $nav_css | safeCSS }};
|
|
|
|
/* Typography — Font Weights */
|
|
--hb-font-weight-heading: {{ $heading_weight }};
|
|
--hb-font-weight-body: {{ $body_weight }};
|
|
--hb-font-weight-body-bold: {{ $body_weight_bold }};
|
|
|
|
/* Typography — Line Heights */
|
|
--hb-font-leading-heading: {{ $leading.heading }};
|
|
--hb-font-leading-body: {{ $leading.body }};
|
|
--hb-font-leading-code: {{ $leading.code }};
|
|
|
|
/* Typography — Letter Spacing */
|
|
--hb-font-tracking-heading: {{ $tracking.heading | safeCSS }};
|
|
--hb-font-tracking-body: {{ $tracking.body | safeCSS }};
|
|
--hb-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) */
|
|
--hb-font-family-sans: {{ $body_css | safeCSS }};
|
|
}
|
|
</style>
|