feat: modernize blocks with 2025 glassmorphism & UX improvements

CTA Card:
- Add 2025 glassmorphism styling with backdrop blur
- Implement icon support for buttons with hover animations
- Add contrast control (text_color, overlay_opacity)
- Support custom backgrounds (image, gradient, texture layers)

Hero Block:
- Fix hash fragment URL parsing (#portfolio -> #portfolio, not /#portfolio)

Infrastructure:
- Add reusable glassmorphism CSS components
- Create SVG noise texture for glassmorphism effects
This commit is contained in:
George Cushen committed 2025-09-17 00:26:10 +01:00
1 parent ef2b77ea92
commit 9140e2f776
7 files changed
+655 -34

No files matched your search

@@ -1,9 +1,10 @@
@import "page.css";
@import "task-list.css";
@import "copy.css";
@import "sidebar-left.css";
@import "charts.css";
@import "steps.css";
@import "cards.css";
@import "math.css";
@import "author-notes.css";
@import 'page.css';
@import 'task-list.css';
@import 'copy.css';
@import 'sidebar-left.css';
@import 'charts.css';
@import 'steps.css';
@import 'cards.css';
@import 'math.css';
@import 'author-notes.css';
@import 'glassmorphism.css';
@@ -0,0 +1,223 @@
/**
* 2025 Hugo Blox Glassmorphism Utility Classes
*
* Modern glassmorphism effects for Hugo Blox CTA cards and other components
*/
/* ==========================================================================
Glassmorphism Base Classes
========================================================================== */
.glassmorphism-primary {
@apply relative overflow-hidden;
background: linear-gradient(
135deg,
rgb(var(--color-primary-500) / 0.9),
rgb(var(--color-primary-600) / 0.95),
rgb(var(--color-primary-700) / 0.9)
);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-primary::before {
content: '';
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
.glassmorphism-secondary {
@apply relative overflow-hidden;
background: linear-gradient(
135deg,
rgb(var(--color-secondary-500) / 0.9),
rgb(var(--color-secondary-600) / 0.95),
rgb(var(--color-secondary-700) / 0.9)
);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-secondary::before {
content: '';
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.1), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
.glassmorphism-dark {
@apply relative overflow-hidden;
background: linear-gradient(135deg, rgb(0 0 0 / 0.7), rgb(31 41 55 / 0.8), rgb(0 0 0 / 0.9));
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-dark::before {
content: '';
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(255 255 255 / 0.05), transparent, rgb(255 255 255 / 0.1));
mix-blend-mode: overlay;
}
.glassmorphism-light {
@apply relative overflow-hidden;
background: linear-gradient(135deg, rgb(255 255 255 / 0.9), rgb(248 250 252 / 0.95), rgb(241 245 249 / 0.9));
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glassmorphism-light::before {
content: '';
@apply absolute inset-0 pointer-events-none;
background: linear-gradient(45deg, rgb(0 0 0 / 0.02), transparent, rgb(255 255 255 / 0.05));
mix-blend-mode: overlay;
}
/* ==========================================================================
Noise Texture Classes (when used as background image)
========================================================================== */
.noise-texture {
position: relative;
}
.noise-texture::after {
content: '';
@apply absolute inset-0 pointer-events-none;
background-image: url('/media/textures/noise-pattern.svg');
background-size: 100px 100px;
background-repeat: repeat;
opacity: 0.03;
mix-blend-mode: multiply;
}
.dark .noise-texture::after {
mix-blend-mode: screen;
opacity: 0.02;
}
/* ==========================================================================
Enhanced Ring Classes for 2025 Design
========================================================================== */
.glass-ring {
@apply ring-1 ring-white/20 hover:ring-white/40 dark:ring-gray-700/50 dark:hover:ring-gray-600/70;
}
.glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25), 0 0 0 1px rgb(255 255 255 / 0.1),
inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.dark .glass-shadow {
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(255 255 255 / 0.05),
inset 0 1px 0 rgb(255 255 255 / 0.05);
}
/* ==========================================================================
CTA Card Glassmorphism Overlay (works with section backgrounds)
========================================================================== */
.cta-glassmorphism {
@apply relative;
/* Dynamic overlay opacity control */
--glassmorphism-opacity: 0.15;
/* Subtle overlay tint that works with any background */
background: linear-gradient(
135deg,
rgb(255 255 255 / var(--glassmorphism-opacity)) 0%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5)) 50%,
rgb(255 255 255 / var(--glassmorphism-opacity)) 100%
);
/* Enhanced glassmorphism effects */
backdrop-filter: blur(20px) saturate(1.2);
-webkit-backdrop-filter: blur(20px) saturate(1.2);
/* Modern border and shadow for light mode */
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.25),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
}
.cta-glassmorphism:hover {
/* Enhanced hover state */
background: linear-gradient(
135deg,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05)) 0%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.7)) 50%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05)) 100%
);
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.2));
box-shadow: 0 40px 80px -16px rgb(0 0 0 / 0.3),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.1));
}
/* Dark mode adjustments for CTA glassmorphism */
.dark .cta-glassmorphism {
background: linear-gradient(
135deg,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5)) 0%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.25)) 50%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5)) 100%
);
border: 1px solid rgb(255 255 255 / var(--glassmorphism-opacity));
box-shadow: 0 32px 64px -12px rgb(0 0 0 / 0.4),
inset 0 1px 0 rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.5));
}
.dark .cta-glassmorphism:hover {
background: linear-gradient(
135deg,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.75)) 0%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.4)) 50%,
rgb(255 255 255 / calc(var(--glassmorphism-opacity) * 0.75)) 100%
);
border: 1px solid rgb(255 255 255 / calc(var(--glassmorphism-opacity) + 0.05));
}
/* ==========================================================================
CTA Button Glassmorphism (nested within CTA cards)
========================================================================== */
.cta-glassmorphism .group {
/* Ensure proper text contrast in light mode */
color: rgb(var(--color-gray-900));
}
.dark .cta-glassmorphism .group {
/* Maintain white text in dark mode */
color: rgb(var(--color-gray-100));
}
/* Enhanced button glassmorphism for light mode visibility */
.cta-glassmorphism .group > div:first-child {
/* Light mode button background */
background: rgb(255 255 255 / 0.9);
border: 1px solid rgb(0 0 0 / 0.1);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.3);
}
.dark .cta-glassmorphism .group > div:first-child {
/* Dark mode button background */
background: rgb(0 0 0 / 0.4);
border: 1px solid rgb(255 255 255 / 0.2);
box-shadow: 0 10px 25px -5px rgb(0 0 0 / 0.3), 0 4px 6px -2px rgb(0 0 0 / 0.2), inset 0 1px 0 rgb(255 255 255 / 0.1);
}
.cta-glassmorphism .group:hover > div:first-child {
background: rgb(255 255 255);
border: 1px solid rgb(0 0 0 / 0.15);
box-shadow: 0 20px 40px -8px rgb(0 0 0 / 0.15), 0 8px 12px -4px rgb(0 0 0 / 0.1), inset 0 1px 0 rgb(255 255 255 / 0.4);
}
.dark .cta-glassmorphism .group:hover > div:first-child {
background: rgb(0 0 0 / 0.6);
border: 1px solid rgb(255 255 255 / 0.3);
}
@@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<defs>
<!-- Define a noise filter -->
<filter id="glassmorphism-noise-filter">
<feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="4" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/> <!-- make grayscale -->
<feComponentTransfer>
<feFuncA type="linear" slope="0.05"/> <!-- opacity of noise -->
</feComponentTransfer>
</filter>
<!-- Define pattern -->
<pattern id="glassmorphism-noise" width="32" height="32" patternUnits="userSpaceOnUse">
<rect width="32" height="32" filter="url(#glassmorphism-noise-filter)" />
</pattern>
</defs>
<!-- Apply noise pattern -->
<rect width="32" height="32" fill="url(#glassmorphism-noise)" />
</svg>

After

Width:  |  Height:  |  Size: 799 B

+167 -6
View File
@@ -6,12 +6,13 @@ Make a bold statement and drive immediate action with the CTA Card block - a hig
## ✨ Key Features
- **Bold Typography**: Eye-catching large text that commands attention
- **Customizable Styling**: Full control over background colors, CSS classes, and custom styles
- **High-Contrast Design**: Dark backgrounds with light text for maximum readability
- **Prominent Button**: Conversion-optimized button styling with hover effects
- **Markdown Support**: Rich text formatting for titles and descriptions
- **Mobile Responsive**: Scales beautifully from mobile to desktop
- **🪟 2025 Glassmorphism**: Modern translucent backgrounds with backdrop blur effects
- **🎨 Gradient Typography**: Beautiful gradient text effects for maximum visual appeal
- **🔗 Smart Icon Support**: Add icons to buttons with automatic sizing and hover animations
- **📱 Enhanced Responsiveness**: Fluid typography scaling from mobile to desktop
- **✨ Micro-Interactions**: Smooth hover animations with glow effects and transforms
- **🎯 Customizable Styling**: Multiple glassmorphism presets or custom CSS classes
- **♿ Accessibility Enhanced**: Improved focus states and semantic markup
## 🎯 Perfect For
@@ -48,3 +49,163 @@ Make a bold statement and drive immediate action with the CTA Card block - a hig
Transform casual visitors into engaged users with a CTA Card that's impossible to ignore.
## 🎨 2025 Glassmorphism Styling Options
### **Glassmorphism Primary** (Recommended)
```yaml
- block: cta-card
content:
title: 'Ready to Get Started?'
text: 'Join thousands of users creating amazing websites'
button:
text: 'Get Started Free'
url: '/signup/'
icon: 'hero/rocket-launch'
design:
card:
css_class: 'cta-glassmorphism'
text_color: 'auto' # auto|light|dark
overlay_opacity: 0.15 # 0.0-1.0 for contrast control
background:
gradient:
start: 'primary-500'
end: 'primary-700'
direction: 135
image:
filename: 'textures/noise-pattern.svg'
size: '32px'
position: 'repeat'
```
### **Glassmorphism Dark**
```yaml
design:
card:
css_class: 'glassmorphism-dark glass-ring glass-shadow text-white'
```
### **Glassmorphism Light**
```yaml
design:
card:
css_class: 'glassmorphism-light glass-ring glass-shadow text-gray-900'
```
### **Custom Background + Glassmorphism**
```yaml
design:
card:
css_class: 'glassmorphism-secondary noise-texture text-white'
background:
color: 'primary-600'
image:
filename: 'your-background.jpg'
size: 'cover'
position: 'center'
opacity: 0.8
gradient:
start: 'primary-500'
end: 'secondary-600'
direction: 135
```
## 🔗 Icon Support
Add icons to your CTA buttons:
```yaml
button:
text: 'Download Now'
url: '/download/'
icon: 'hero/download' # Download icon
button:
text: 'Contact Us'
url: 'mailto:hello@example.com'
icon: 'hero/paper-airplane' # Email icon
button:
text: 'Learn More'
url: '#features'
icon: 'hero/arrow-right' # Arrow icon
```
## 🎨 Contrast Control Options
### **Text Color Modes**
```yaml
card:
text_color: 'auto' # Auto-detects based on background brightness
text_color: 'light' # Force white text (for dark backgrounds)
text_color: 'dark' # Force dark text (for light backgrounds)
```
### **Overlay Opacity Control**
```yaml
card:
overlay_opacity: 0.1 # Subtle glassmorphism (better for text contrast)
overlay_opacity: 0.15 # Default glassmorphism
overlay_opacity: 0.25 # Strong glassmorphism (may reduce contrast)
overlay_opacity: 0.05 # Minimal glassmorphism (maximum contrast)
```
### **Perfect Contrast Examples**
**Dark Background (Light Text):**
```yaml
design:
card:
css_class: 'cta-glassmorphism'
text_color: 'light'
overlay_opacity: 0.1
background:
gradient:
start: 'gray-800'
end: 'gray-900'
```
**Light Background (Dark Text):**
```yaml
design:
card:
css_class: 'cta-glassmorphism'
text_color: 'dark'
overlay_opacity: 0.2
background:
gradient:
start: 'gray-100'
end: 'gray-300'
```
**Vibrant Background (Auto Detection):**
```yaml
design:
card:
css_class: 'cta-glassmorphism'
text_color: 'auto' # Will detect primary-500 as medium and use adaptive colors
overlay_opacity: 0.15
background:
gradient:
start: 'primary-500'
end: 'primary-700'
```
## ✨ Available Glassmorphism Classes
- **`cta-glassmorphism`**: Smart adaptive glassmorphism with contrast controls
- **`glassmorphism-primary`**: Primary color glassmorphism (legacy)
- **`glassmorphism-secondary`**: Secondary color glassmorphism (legacy)
- **`glassmorphism-dark`**: Dark glassmorphism (legacy)
- **`glassmorphism-light`**: Light glassmorphism (legacy)
- **`glass-ring`**: Enhanced ring borders
- **`glass-shadow`**: Multi-layer glass shadows
- **`noise-texture`**: Subtle noise texture overlay
+89 -14
View File
@@ -11,20 +11,95 @@
{{ $card_class := $block.design.card.css_class }}
{{ $card_style := $block.design.card.css_style }}
{{/* Contrast and accessibility controls */}}
{{ $text_color_mode := $block.design.card.text_color | default "auto" }}
{{ $overlay_opacity := $block.design.card.overlay_opacity | default 0.15 }}
<div class="{{with $card_class}}{{.}}{{else}}bg-black{{end}} p-8 md:px-20 md:py-20 mx-auto max-w-5xl rounded-lg flex flex-col items-center text-center"
{{with $card_style}}style="{{. | safeCSS}}"{{end}}>
<h2 class="text-white text-4xl md:text-6xl tracking-tight">
{{ $title }}
</h2>
<div class="text-gray-100 mt-4 text-lg md:text-xl prose">
{{ $text }}
</div>
{{ with $block.content.button }}
<div class="flex mt-5">
<a href="{{.url}}" class="rounded text-center transition focus-visible:ring-2 ring-offset-2 ring-gray-200 px-5 py-2.5 bg-white hover:bg-gray-300 text-black border-2 border-transparent">
{{.text}}
</a>
</div>
{{/* Auto-detect text color based on background */}}
{{ $text_classes := "" }}
{{ $button_classes := "" }}
{{ if eq $text_color_mode "light" }}
{{ $text_classes = "text-white" }}
{{ $button_classes = "text-gray-900" }}
{{ else if eq $text_color_mode "dark" }}
{{ $text_classes = "text-gray-900" }}
{{ $button_classes = "text-gray-900" }}
{{ else }}
{{/* Auto mode - detect based on gradient colors */}}
{{ $is_dark_bg := false }}
{{ with $block.design.background.gradient.start }}
{{ if or (hasPrefix . "primary-7") (hasPrefix . "primary-8") (hasPrefix . "primary-9") (hasPrefix . "secondary-7") (hasPrefix . "secondary-8") (hasPrefix . "secondary-9") }}
{{ $is_dark_bg = true }}
{{ end }}
{{ end }}
{{ if $is_dark_bg }}
{{ $text_classes = "text-white" }}
{{ $button_classes = "text-gray-900" }}
{{ else }}
{{ $text_classes = "text-gray-900 dark:text-white" }}
{{ $button_classes = "text-gray-900 dark:text-gray-100" }}
{{ end }}
{{ end }}
{{/* Modern 2025 CTA Card - Background controlled via design.background in front matter */}}
{{ $custom_style := printf "--glassmorphism-opacity: %s;" (string $overlay_opacity) }}
{{ if $card_style }}
{{ $custom_style = printf "%s%s" $custom_style $card_style }}
{{ end }}
<div class="relative overflow-hidden {{with $card_class}}{{.}}{{else}}bg-gradient-to-br from-primary-500/90 via-primary-600/95 to-primary-700/90{{end}} p-8 sm:p-12 lg:p-16 xl:p-20 mx-auto max-w-6xl rounded-3xl shadow-2xl flex flex-col items-center text-center"
style="{{ $custom_style | safeCSS }}">
{{/* Modern Typography Hierarchy - Responsive text colors */}}
<h2 class="{{ $text_classes }} text-4xl sm:text-5xl lg:text-6xl xl:text-7xl font-bold tracking-tight leading-tight">
{{ $title }}
</h2>
{{/* Enhanced Text with Better Spacing - Responsive colors */}}
<div class="{{ $text_classes }}/80 mt-6 text-lg sm:text-xl lg:text-2xl max-w-3xl leading-relaxed font-light">
{{ $text }}
</div>
{{ with $block.content.button }}
{{/* Modern 2025 Button Container */}}
<div class="flex mt-10">
{{ $button_link := .url }}
{{ $button_scheme := (urls.Parse $button_link).Scheme }}
{{ $button_target := "" }}
{{ if not $button_scheme }}
{{/* Check if it's a hash fragment (page anchor) */}}
{{ if hasPrefix $button_link "#" }}
{{/* Keep hash fragments as-is */}}
{{ $button_link = $button_link }}
{{ else }}
{{/* Apply relLangURL for relative paths */}}
{{ $button_link = $button_link | relLangURL }}
{{ end }}
{{ else if in (slice "http" "https") $button_scheme }}
{{ $button_target = "target=\"_blank\" rel=\"noopener\"" }}
{{ end }}
{{/* 2025 Glassmorphism Button */}}
<a href="{{ $button_link | safeURL }}"
{{ $button_target | safeHTMLAttr }}
class="group relative inline-flex items-center gap-3 px-8 py-4 text-lg font-semibold {{ $button_classes }} transition-all duration-300 ease-out hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-500/50 dark:focus-visible:ring-white/50 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent">
{{/* Button Background with Glassmorphism */}}
<div class="absolute inset-0 bg-white/95 dark:bg-gray-900/95 backdrop-blur-md rounded-2xl ring-1 ring-gray-200/30 dark:ring-white/30 shadow-lg group-hover:bg-white dark:group-hover:bg-gray-800 group-hover:ring-gray-300/50 dark:group-hover:ring-white/50 group-hover:shadow-2xl transition-all duration-300"></div>
{{/* Button Content */}}
<span class="relative z-10">{{ .text }}</span>
{{ with .icon }}
<span class="relative z-10 transition-transform duration-300 group-hover:translate-x-1">
{{ partial "functions/get_icon" (dict "name" . "attributes" "style=\"height: 1.25em\" class='inline-block'") }}
</span>
{{ end }}
{{/* Subtle glow effect */}}
<div class="absolute inset-0 bg-white/20 dark:bg-white/10 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300 blur-xl"></div>
</a>
</div>
{{ end }}
</div>
+112 -2
View File
@@ -31,19 +31,129 @@
"properties": {
"url": {
"type": "string",
"description": "Button URL"
"description": "Button URL (supports hash fragments, relative paths, and absolute URLs)"
},
"text": {
"type": "string",
"description": "Button text"
},
"icon": {
"type": "string",
"description": "Optional button icon (icon pack format: pack/icon-name)"
}
},
"required": ["url", "text"]
}
}
},
"design": {
"type": "object",
"description": "Design and styling options",
"properties": {
"card": {
"type": "object",
"description": "Card-specific styling",
"properties": {
"css_class": {
"type": "string",
"description": "CSS classes to apply to the card (supports glassmorphism classes: glassmorphism-primary, glassmorphism-dark, glass-ring, glass-shadow, noise-texture)"
},
"text_color": {
"type": "string",
"enum": ["auto", "light", "dark"],
"description": "Text color mode for optimal contrast (auto detects based on background)",
"default": "auto"
},
"overlay_opacity": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Glassmorphism overlay opacity for fine-tuning contrast",
"default": 0.15
},
"css_style": {
"type": "string",
"description": "Custom CSS styles to apply to the card"
}
}
},
"background": {
"type": "object",
"description": "Modern background options supporting layered effects",
"properties": {
"color": {
"type": "string",
"description": "Background color"
},
"gradient": {
"type": "object",
"description": "Modern gradient syntax for 2025 glassmorphism",
"properties": {
"start": {
"type": "string",
"description": "Gradient start color (e.g., 'primary-500', 'blue-600')"
},
"end": {
"type": "string",
"description": "Gradient end color (e.g., 'primary-700', 'blue-800')"
},
"direction": {
"type": "number",
"description": "Gradient angle in degrees (default: 90)",
"default": 90
}
}
},
"image": {
"type": "object",
"description": "Background image (layers with gradient for glassmorphism)",
"properties": {
"filename": {
"type": "string",
"description": "Image filename in assets/media/"
},
"size": {
"type": "string",
"enum": ["cover", "contain", "auto", "100px", "50%"],
"description": "Background size",
"default": "cover"
},
"position": {
"type": "string",
"enum": [
"center",
"top",
"bottom",
"left",
"right",
"repeat",
"top left",
"top right",
"bottom left",
"bottom right"
],
"description": "Background position (use 'repeat' for tiling textures)",
"default": "center"
},
"repeat": {
"type": "string",
"enum": ["repeat", "repeat-x", "repeat-y", "no-repeat"],
"description": "Explicit background-repeat control",
"default": "no-repeat"
},
"opacity": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Image opacity for subtle textures"
}
}
}
}
}
}
}
}
}
]
}
+34 -3
View File
@@ -13,7 +13,24 @@
{{ $announcement := $block.content.announcement }}
<div class="hidden sm:mb-8 sm:flex sm:justify-center">
<div class="relative rounded-full px-3 py-1 text-sm leading-6 text-gray-600 dark:text-gray-300 ring-1 ring-gray-900/10 dark:ring-gray-300 hover:ring-gray-900/20 dark:hover:ring-gray-400">
{{$announcement.text | markdownify}} {{if $announcement.link.text}}<a href="{{$announcement.link.url}}" class="font-semibold text-primary-600 dark:text-primary-300"><span class="absolute inset-0" aria-hidden="true"></span>{{$announcement.link.text}} <span aria-hidden="true">&rarr;</span></a>{{end}}
{{$announcement.text | markdownify}}
{{if $announcement.link.text}}
{{ $announcement_link := $announcement.link.url }}
{{ $announcement_scheme := (urls.Parse $announcement_link).Scheme }}
{{ if not $announcement_scheme }}
{{/* Check if it's a hash fragment (page anchor) */}}
{{ if hasPrefix $announcement_link "#" }}
{{/* Keep hash fragments as-is */}}
{{ $announcement_link = $announcement_link }}
{{ else }}
{{/* Apply relLangURL for relative paths */}}
{{ $announcement_link = $announcement_link | relLangURL }}
{{ end }}
{{ end }}
<a href="{{$announcement_link | safeURL}}" class="font-semibold text-primary-600 dark:text-primary-300">
<span class="absolute inset-0" aria-hidden="true"></span>{{$announcement.link.text}} <span aria-hidden="true">&rarr;</span>
</a>
{{end}}
</div>
</div>
{{end}}
@@ -27,7 +44,14 @@
{{ $scheme := (urls.Parse $link).Scheme }}
{{ $target := "" }}
{{ if not $scheme }}
{{ $link = $link | relLangURL }}
{{/* Check if it's a hash fragment (page anchor) */}}
{{ if hasPrefix $link "#" }}
{{/* Keep hash fragments as-is */}}
{{ $link = $link }}
{{ else }}
{{/* Apply relLangURL for relative paths */}}
{{ $link = $link | relLangURL }}
{{ end }}
{{ else if in (slice "http" "https") $scheme }}
{{ $target = "target=\"_blank\" rel=\"noopener\"" }}
{{ end }}
@@ -45,7 +69,14 @@
{{ $scheme_alt := (urls.Parse $link_alt).Scheme }}
{{ $target_alt := "" }}
{{ if not $scheme_alt }}
{{ $link_alt = $link_alt | relLangURL }}
{{/* Check if it's a hash fragment (page anchor) */}}
{{ if hasPrefix $link_alt "#" }}
{{/* Keep hash fragments as-is */}}
{{ $link_alt = $link_alt }}
{{ else }}
{{/* Apply relLangURL for relative paths */}}
{{ $link_alt = $link_alt | relLangURL }}
{{ end }}
{{ else if in (slice "http" "https") $scheme_alt }}
{{ $target_alt = "target=\"_blank\" rel=\"noopener\"" }}
{{ end }}