diff --git a/hugo-blox-builder.code-workspace b/hugoblox.code-workspace similarity index 100% rename from hugo-blox-builder.code-workspace rename to hugoblox.code-workspace diff --git a/modules/blox/assets/css/blox/biography.css b/modules/blox/assets/css/blox/biography.css index 064b3aaa..4f1186e5 100644 --- a/modules/blox/assets/css/blox/biography.css +++ b/modules/blox/assets/css/blox/biography.css @@ -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%); -} +} \ No newline at end of file diff --git a/modules/blox/assets/css/config/theme.css b/modules/blox/assets/css/config/theme.css index 371313b0..f00215d4 100644 --- a/modules/blox/assets/css/config/theme.css +++ b/modules/blox/assets/css/config/theme.css @@ -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; -} +} \ No newline at end of file diff --git a/modules/blox/assets/css/framework/base.css b/modules/blox/assets/css/framework/base.css index 7b31f1a9..ca1fd97b 100644 --- a/modules/blox/assets/css/framework/base.css +++ b/modules/blox/assets/css/framework/base.css @@ -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; } -} +} \ No newline at end of file diff --git a/modules/blox/assets/css/framework/components.css b/modules/blox/assets/css/framework/components.css index cef13d01..b0661d2b 100644 --- a/modules/blox/assets/css/framework/components.css +++ b/modules/blox/assets/css/framework/components.css @@ -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; } -} +} \ No newline at end of file diff --git a/modules/blox/assets/css/hb-search.css b/modules/blox/assets/css/hb-search.css index 434b66f0..2a121a4a 100644 --- a/modules/blox/assets/css/hb-search.css +++ b/modules/blox/assets/css/hb-search.css @@ -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; -} +} \ No newline at end of file diff --git a/modules/blox/assets/css/layout-utilities.css b/modules/blox/assets/css/layout-utilities.css index 23ecae57..e6f0f6d3 100644 --- a/modules/blox/assets/css/layout-utilities.css +++ b/modules/blox/assets/css/layout-utilities.css @@ -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 */ diff --git a/modules/blox/data/fonts/academic.yaml b/modules/blox/data/fonts/academic.yaml new file mode 100644 index 00000000..a53a1715 --- /dev/null +++ b/modules/blox/data/fonts/academic.yaml @@ -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 diff --git a/modules/blox/data/fonts/developer.yaml b/modules/blox/data/fonts/developer.yaml new file mode 100644 index 00000000..03d22889 --- /dev/null +++ b/modules/blox/data/fonts/developer.yaml @@ -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 diff --git a/modules/blox/data/fonts/editorial.yaml b/modules/blox/data/fonts/editorial.yaml new file mode 100644 index 00000000..8080ec74 --- /dev/null +++ b/modules/blox/data/fonts/editorial.yaml @@ -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 diff --git a/modules/blox/data/fonts/geometric.yaml b/modules/blox/data/fonts/geometric.yaml new file mode 100644 index 00000000..9a64207e --- /dev/null +++ b/modules/blox/data/fonts/geometric.yaml @@ -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" diff --git a/modules/blox/data/fonts/humanist.yaml b/modules/blox/data/fonts/humanist.yaml new file mode 100644 index 00000000..d6db4ddd --- /dev/null +++ b/modules/blox/data/fonts/humanist.yaml @@ -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 diff --git a/modules/blox/data/fonts/modern.yaml b/modules/blox/data/fonts/modern.yaml new file mode 100644 index 00000000..c0355a0a --- /dev/null +++ b/modules/blox/data/fonts/modern.yaml @@ -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 diff --git a/modules/blox/data/fonts/system.yaml b/modules/blox/data/fonts/system.yaml new file mode 100644 index 00000000..2797716b --- /dev/null +++ b/modules/blox/data/fonts/system.yaml @@ -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" diff --git a/modules/blox/layouts/_partials/functions/generate_color_scale.html b/modules/blox/layouts/_partials/functions/generate_color_scale.html index e8228bed..ad696822 100644 --- a/modules/blox/layouts/_partials/functions/generate_color_scale.html +++ b/modules/blox/layouts/_partials/functions/generate_color_scale.html @@ -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 }} diff --git a/modules/blox/layouts/_partials/functions/get_theme_config.html b/modules/blox/layouts/_partials/functions/get_theme_config.html index 817e9424..9e9d7114 100644 --- a/modules/blox/layouts/_partials/functions/get_theme_config.html +++ b/modules/blox/layouts/_partials/functions/get_theme_config.html @@ -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 }} {{/* ═══════════════════════════════════════════════════════════════════════════ diff --git a/modules/blox/layouts/_partials/functions/layout_tokens.html b/modules/blox/layouts/_partials/functions/layout_tokens.html new file mode 100644 index 00000000..3c48bf67 --- /dev/null +++ b/modules/blox/layouts/_partials/functions/layout_tokens.html @@ -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: diff --git a/modules/blox/layouts/_partials/functions/load_font_pack.html b/modules/blox/layouts/_partials/functions/load_font_pack.html new file mode 100644 index 00000000..c036013e --- /dev/null +++ b/modules/blox/layouts/_partials/functions/load_font_pack.html @@ -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 }} diff --git a/modules/blox/layouts/_partials/functions/typography.html b/modules/blox/layouts/_partials/functions/typography.html new file mode 100644 index 00000000..6544a72a --- /dev/null +++ b/modules/blox/layouts/_partials/functions/typography.html @@ -0,0 +1,147 @@ +{{/* + HugoBlox Theme Engine: Typography Rendering + + Resolves font families, emits Google Fonts tags, + and generates CSS custom properties for typography. + + Input: Theme config dict from get_theme_config: + - typography: { families, weights, sizes, leading, tracking } + + Output: (Google Fonts) + diff --git a/modules/blox/layouts/_partials/site_head.html b/modules/blox/layouts/_partials/site_head.html index 91e76732..3fd9431b 100644 --- a/modules/blox/layouts/_partials/site_head.html +++ b/modules/blox/layouts/_partials/site_head.html @@ -202,82 +202,10 @@