feat: enhance block infrastructure with dynamic assets & multi-layer backgrounds

- Add dynamic JavaScript loading system for blocks
- Implement multi-layer background support (texture + gradient)
- Add Tailwind color variable conversion in templates
- Support light/dark background color modes
- Prevent framework CSS conflicts with custom styles
This commit is contained in:
George Cushen committed 2025-09-17 00:26:10 +01:00
1 parent 776157d486
commit ef2b77ea92
5 files changed
+294 -31

No files matched your search

@@ -5,12 +5,12 @@
padding-top: 6rem;
padding-bottom: 6rem;
}
.section-subheading {
font-size: 1.25rem;
font-weight: 700;
}
/* Home section background styles */
.home-section-bg {
position: absolute;
@@ -20,13 +20,27 @@
width: 100%;
z-index: -1;
}
.home-section-bg.bg-image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
/* 2025 Glassmorphism texture support - disable conflicting defaults */
.home-section-bg.bg-image[style*='background-size'][style*='background-repeat'] {
background-size: revert !important;
background-repeat: revert !important;
background-position: revert !important;
}
/* Specific glassmorphism texture class override */
.blox-cta-card .home-section-bg.bg-image {
background-size: revert;
background-repeat: revert;
background-position: revert;
}
/* Video background */
.bg-video {
position: absolute;
@@ -38,12 +52,12 @@
object-position: center center;
opacity: 1;
}
/* Flip video horizontally */
.bg-video.flip {
transform: scaleX(-1);
}
/* Container styles */
.universal-wrapper {
margin: 0 auto;
@@ -51,18 +65,18 @@
padding-left: 1rem;
width: 100%;
}
@media (min-width: 1200px) {
.universal-wrapper {
max-width: 1200px;
}
}
.article-container {
max-width: 760px;
margin: 0 auto;
}
/* Button toolbar */
.btn-toolbar {
display: flex;
@@ -70,26 +84,26 @@
justify-content: center;
gap: 0.5rem;
}
/* Documentation specific */
.docs-article-container {
max-width: 760px;
}
/* Powered-by footer styles */
.powered-by {
font-size: 0.75rem;
}
.powered-by a {
color: inherit;
text-decoration: none;
}
.powered-by a:hover {
text-decoration: underline;
}
/* Parallax effect */
.parallax {
background-attachment: fixed;
@@ -98,17 +112,17 @@
background-size: cover;
position: relative;
}
/* When parallax is used with home-section-bg, preserve absolute positioning */
.home-section-bg.parallax {
position: absolute;
}
/* Section color schemes - for backward compatibility */
section.light {
background-color: transparent;
}
section.dark {
background-color: transparent;
}
@@ -0,0 +1,19 @@
{{/*
Get Block Scripts
Discovers and returns a slice of JavaScript files for blocks used on the current page.
*/}}
{{ $block_scripts := slice }}
{{ if .IsPage }}
{{/* Get the list of unique block types used on this page from the Page Store */}}
{{ $block_types := .Page.Store.Get "block_types" | default (slice) | uniq }}
{{ range $block_type := $block_types }}
{{/* Check if a JS file exists for this block type */}}
{{ $script_path := printf "js/hbx/blocks/%s/%s.js" $block_type $block_type }}
{{ with resources.Get $script_path }}
{{ $block_scripts = $block_scripts | append . }}
{{ end }}
{{ end }}
{{ end }}
{{ return $block_scripts }}
@@ -33,12 +33,176 @@
{{ $style_bg := "" }}
{{ if $bg.color }}
{{ $style_bg = printf "background-color: %s;" ($bg.color | default "transparent") }}
{{/* Check if color is a map/dict (light/dark structure) or a simple string */}}
{{ $color_type := printf "%T" $bg.color }}
{{ if eq $color_type "map[string]interface {}" }}
{{/* New nested light/dark structure */}}
{{ if and $bg.color.light $bg.color.dark }}
{{ $light_color := $bg.color.light }}
{{ $dark_color := $bg.color.dark }}
{{/* Convert light color */}}
{{ if not (or (hasPrefix $light_color "var(") (hasPrefix $light_color "rgb(") (hasPrefix $light_color "#") (eq $light_color "transparent")) }}
{{ if hasPrefix $light_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $light_color }}
{{ $light_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $light_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $light_color }}
{{ $light_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $light_color "-" }}
{{ $parts := split $light_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $light_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{/* Convert dark color */}}
{{ if not (or (hasPrefix $dark_color "var(") (hasPrefix $dark_color "rgb(") (hasPrefix $dark_color "#") (eq $dark_color "transparent")) }}
{{ if hasPrefix $dark_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $dark_color }}
{{ $dark_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $dark_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $dark_color }}
{{ $dark_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $dark_color "-" }}
{{ $parts := split $dark_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $dark_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ $style_bg = printf "background-color: %s;" $light_color }}
{{ $style_bg = printf "%s--dark-bg-color: %s;" $style_bg $dark_color }}
{{ else if $bg.color.light }}
{{ $light_color := $bg.color.light }}
{{/* Convert light color */}}
{{ if not (or (hasPrefix $light_color "var(") (hasPrefix $light_color "rgb(") (hasPrefix $light_color "#") (eq $light_color "transparent")) }}
{{ if hasPrefix $light_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $light_color }}
{{ $light_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $light_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $light_color }}
{{ $light_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $light_color "-" }}
{{ $parts := split $light_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $light_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ $style_bg = printf "background-color: %s;" $light_color }}
{{ else if $bg.color.dark }}
{{ $dark_color := $bg.color.dark }}
{{/* Convert dark color */}}
{{ if not (or (hasPrefix $dark_color "var(") (hasPrefix $dark_color "rgb(") (hasPrefix $dark_color "#") (eq $dark_color "transparent")) }}
{{ if hasPrefix $dark_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $dark_color }}
{{ $dark_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $dark_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $dark_color }}
{{ $dark_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $dark_color "-" }}
{{ $parts := split $dark_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $dark_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ $style_bg = printf "--dark-bg-color: %s; background-color: var(--dark-bg-color);" $dark_color }}
{{ end }}
{{ else }}
{{/* Legacy single color support - backwards compatible */}}
{{ $bg_color := $bg.color }}
{{/* Convert single color */}}
{{ if not (or (hasPrefix $bg_color "var(") (hasPrefix $bg_color "rgb(") (hasPrefix $bg_color "#") (eq $bg_color "transparent")) }}
{{ if hasPrefix $bg_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $bg_color }}
{{ $bg_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $bg_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $bg_color }}
{{ $bg_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $bg_color "-" }}
{{ $parts := split $bg_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $bg_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ $style_bg = printf "background-color: %s;" $bg_color }}
{{ end }}
{{ end }}
{{/* Modern layered background support for 2025 glassmorphism */}}
{{ $background_layers := slice }}
{{ $gradient_layer := "" }}
{{/* Process gradients first but don't add to layers yet */}}
{{/* Support legacy gradient syntax */}}
{{ if and $bg.gradient_start $bg.gradient_end }}
{{ $angle := string $bg.gradient_angle | default "90" }}
{{ $style_bg = printf "%sbackground-image: linear-gradient(%sdeg, %s, %s);" $style_bg $angle $bg.gradient_start $bg.gradient_end }}
{{ $gradient_layer = printf "linear-gradient(%sdeg, %s, %s)" $angle $bg.gradient_start $bg.gradient_end }}
{{ end }}
{{/* Support modern gradient syntax for 2025 */}}
{{ if and $bg.gradient.start $bg.gradient.end }}
{{ $angle := string $bg.gradient.direction | default "135" }}
{{ $start_color := $bg.gradient.start }}
{{ $end_color := $bg.gradient.end }}
{{/* Convert Tailwind color names to CSS custom properties */}}
{{ if not (or (hasPrefix $start_color "var(") (hasPrefix $start_color "rgb(") (hasPrefix $start_color "#")) }}
{{ if hasPrefix $start_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $start_color }}
{{ $start_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $start_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $start_color }}
{{ $start_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $start_color "-" }}
{{ $parts := split $start_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $start_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ if not (or (hasPrefix $end_color "var(") (hasPrefix $end_color "rgb(") (hasPrefix $end_color "#")) }}
{{ if hasPrefix $end_color "primary-" }}
{{ $shade := strings.TrimPrefix "primary-" $end_color }}
{{ $end_color = printf "var(--color-primary-%s)" $shade }}
{{ else if hasPrefix $end_color "secondary-" }}
{{ $shade := strings.TrimPrefix "secondary-" $end_color }}
{{ $end_color = printf "var(--color-secondary-%s)" $shade }}
{{ else if strings.Contains $end_color "-" }}
{{ $parts := split $end_color "-" }}
{{ if eq (len $parts) 2 }}
{{ $color := index $parts 0 }}
{{ $shade := index $parts 1 }}
{{ $end_color = printf "var(--color-%s-%s)" $color $shade }}
{{ end }}
{{ end }}
{{ end }}
{{ $gradient_layer = printf "linear-gradient(%sdeg, %s, %s)" $angle $start_color $end_color }}
{{ end }}
{{ $bg_video := "" }}
@@ -58,17 +222,64 @@
{{ if ne $bg_img.MediaType.SubType "svg" }}
{{ $bg_img = $bg_img.Fit "1920x1920 webp" }}
{{ end }}
{{ $style_bg = printf "%sbackground-image: url('%s');" $style_bg $bg_img.Permalink }}
{{/* Add image layer to background layers for glassmorphism support */}}
{{ $image_layer := printf "url('%s')" $bg_img.Permalink }}
{{ $background_layers = $background_layers | append $image_layer }}
{{/* Background properties will be set in the multi-layer section below */}}
{{ else }}
{{ errorf "Couldn't find `%s` in the `assets/media/` folder - please add it." $bg.image.filename }}
{{ end }}
{{ with $bg.image.size }}
{{/* Allow sizes: actual, cover, and contain. */}}
{{ $style_bg = printf "%sbackground-size: %s;" $style_bg (replace . "actual" "auto") }}
{{ end }}
{{/* Combine all background layers for 2025 glassmorphism effects */}}
{{/* Layer order: image (top) comes first, then gradient (bottom) */}}
{{ if or $background_layers $gradient_layer }}
{{ $all_layers := $background_layers }}
{{ if $gradient_layer }}
{{ $all_layers = $all_layers | append $gradient_layer }}
{{ end }}
{{ with $bg.image.position }}
{{/* Allow valid CSS positions including left, center, and right. */}}
{{ $style_bg = printf "%sbackground-position: %s;" $style_bg . }}
{{ if $all_layers }}
{{ $combined_backgrounds := delimit $all_layers ", " }}
{{ $style_bg = printf "%sbackground-image: %s;" $style_bg $combined_backgrounds }}
{{/* Build multi-layer background properties */}}
{{ $layer_count := len $all_layers }}
{{/* Generate properties for each layer */}}
{{ if gt $layer_count 1 }}
{{/* Multiple layers: image + gradient */}}
{{ $img_size := $bg.image.size | default "100px" }}
{{ $img_position := $bg.image.position | default "center" }}
{{ $img_repeat := $bg.image.repeat | default "repeat" }}
{{/* Handle 'repeat' in position */}}
{{ if eq $img_position "repeat" }}
{{ $img_repeat = "repeat" }}
{{ $img_position = "center" }}
{{ end }}
{{/* Combine properties for both layers */}}
{{ $style_bg = printf "%sbackground-size: %s, cover !important;" $style_bg $img_size }}
{{ $style_bg = printf "%sbackground-position: %s, center !important;" $style_bg $img_position }}
{{ $style_bg = printf "%sbackground-repeat: %s, no-repeat !important;" $style_bg $img_repeat }}
{{ else }}
{{/* Single layer: apply properties normally */}}
{{ if $background_layers }}
{{ with $bg.image.size }}
{{ $style_bg = printf "%sbackground-size: %s !important;" $style_bg . }}
{{ end }}
{{ with $bg.image.position }}
{{ if eq . "repeat" }}
{{ $style_bg = printf "%sbackground-position: center !important;" $style_bg }}
{{ $style_bg = printf "%sbackground-repeat: repeat !important;" $style_bg }}
{{ else }}
{{ $style_bg = printf "%sbackground-position: %s !important;" $style_bg . }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
@@ -129,9 +340,12 @@
{{end}}
*/}}
{{/* Extract background-specific CSS classes */}}
{{ $bg_css_classes := $bg.css_class | default "" }}
{{/* Dedicated child div for bg prevents parallax 100% height issue within new CSS grid page wrapper. */}}
<section id="{{$hash_id}}" class="relative hbb-section {{$widget_class}} {{if $bg.text_color_light}}dark{{else if (eq $bg.text_color_light false)}}light{{end}} {{with $css_classes}}{{.}}{{end}}" {{with $style}}style="{{. | safeCSS}}"{{end}} {{print $extra_attributes | safeHTMLAttr}}>
<div class="home-section-bg {{if $bg.image}} bg-image{{if ($bg.image.parallax | default true) }} parallax{{end}}{{end}}" {{with $style_bg}}style="{{. | safeCSS}}"{{end}}>
<div class="home-section-bg {{if $bg.image}} bg-image{{if ($bg.image.parallax | default true) }} parallax{{end}}{{end}}{{with $bg_css_classes}} {{.}}{{end}}" {{with $style_bg}}style="{{. | safeCSS}}"{{end}}>
{{with $bg_video}}<video class="bg-video {{$bg_video_class}}" playsinline="" preload="auto" loop="" muted autoplay="" tabindex="-1" width="100%" height="100%" src="{{.RelPermalink}}" style="width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1;"></video>{{end}}
</div>
{{if $use_container}}<div class="container mx-auto">{{end}}
@@ -18,6 +18,13 @@
{{ range $r := .Site.Data.blox_aliases.renames }}
{{ $block_type = cond (eq $block_type $r.old) $r.new $block_type }}
{{ end }}
{{/* Store the block type in the page store for asset bundling */}}
{{ $current_blocks := $page.Store.Get "block_types" | default (slice) }}
{{ $page.Store.Set "block_types" ($current_blocks | append $block_type) }}
{{/* Gallery block is now pure CSS - no Alpine.js needed */}}
{{ $widget_config_file := printf "blox/%s--CONFIG.html" $block_type }}
{{ if templates.Exists (printf "_partials/%s" $widget_config_file) }}
{{ $hash_id := $block.id | default (printf "section-%s" (replace $block_type "." "-")) }}
@@ -211,9 +211,6 @@
{{/* !CACHED! Initialized once at site level and then cached for each page */}}
{{- partialCached "init.html" . "hb_init_once" -}}
{{/* Load Third-Party Libraries */}}
{{ partial "libraries.html" . }}
{{ $js_license := printf "/*! Hugo Blox Builder Tailwind UI v%s | https://hugoblox.com/ */\n" site.Data.hugoblox.version }}
{{ $js_license := $js_license | printf "%s/*! Copyright 2016-present George Cushen (https://georgecushen.com/) */\n" }}
{{ $js_license := $js_license | printf "%s/*! License: https://github.com/HugoBlox/hugo-blox-builder/blob/main/LICENSE.md */\n" }}
@@ -229,7 +226,16 @@
{{- $js_notifier := resources.Get "js/hb-notifier.js" | js.Build (dict "targetPath" (printf "js/hb-notifier-%s.js" .Lang ) "params" $js_params) | resources.Minify -}}
{{- $js_clipboard := resources.Get "js/hb-clipboard.js" | js.Build (dict "targetPath" (printf "js/hb-clipboard-%s.js" .Lang ) "params" $js_params) | resources.Minify -}}
{{- $js_citation := resources.Get "js/hb-citation.js" | js.Build (dict "targetPath" (printf "js/hb-citation-%s.js" .Lang ) "params" $js_params) | resources.Minify -}}
{{ $js_bundle := slice $js_bundle_head $js_hugoblox $js_theme $js_lang $js_nav $js_sidebar $js_notifier $js_clipboard $js_citation | resources.Concat (printf "js/hugo-blox-%s.min.js" .Lang) }}
{{/* Start with the core scripts */}}
{{ $js_bundle_contents := slice $js_bundle_head $js_hugoblox $js_theme $js_lang $js_nav $js_sidebar $js_notifier $js_clipboard $js_citation }}
{{/* Discover and include block-specific JavaScript */}}
{{- with partial "functions/get-block-scripts.html" . -}}
{{ $js_bundle_contents = $js_bundle_contents | append . }}
{{- end -}}
{{ $js_bundle := $js_bundle_contents | resources.Concat (printf "js/hugo-blox-%s.min.js" .Lang) }}
{{- if hugo.IsProduction -}}
{{ $js_bundle = $js_bundle | fingerprint "sha256" }}
{{- end -}}
@@ -239,6 +245,9 @@
integrity="{{ $js_bundle.Data.Integrity }}"
></script>
{{/* Load Third-Party Libraries */}}
{{ partial "libraries.html" . }}
{{/* EXTENSIBILITY HOOK: HEAD-END */}}
{{ partial "functions/get_hook" (dict "hook" "head-end" "context" .) }}
</head>