mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-11 03:12:56 +00:00
feat(images): add responsive srcset/sizes across Tailwind layouts
Introduce reusable partial process_responsive_image.html (fit/fill/resize/responsive), using Hugo .Process, WebP by default, skip GIFs, respect Hugo imaging defaults Preserve original look: pre-fit/pre-fill to intended dimensions, then generate responsive variants; use fitted dims for width/height to avoid CLS single.html: featured image pre-fit by placement (720/1200/2560), responsive srcset, fetchpriority=high; author avatar uses 2x retina (32→16) article-grid.html: main image pre-fit 960×540 (fill/fit), responsive srcset; author avatar uses 2x retina (40→20) card.html: pre-fill 655×655, responsive srcset render-image.html: default display 760px with responsive srcset (matches prior layout) page_author_card.html: high‑res avatar pipeline with srcset (sizes=96px) testimonials.html: avatar uses 2x retina (96→48) CTA/resume banners: responsive srcset + sizes Fix issues: avoid using fallback dims for display Closes #1988
This commit is contained in:
1 parent
899982ea25
commit
bab17c129d
10 files changed
+414
-84
No files matched your search
@@ -25,7 +25,7 @@
|
||||
|
||||
<figure {{ with $fig_class }}class="{{.}}"{{end}} {{ with $id }}id="figure-{{ . }}"{{ end }}>
|
||||
<div class="flex justify-center ">
|
||||
<div class="w-100" {{ with $max_width }}style="max-width: {{.}}"{{end}}>
|
||||
<div class="w-full" {{ with $max_width }}style="max-width: {{.}}"{{end}}>
|
||||
{{- if $img -}}
|
||||
{{- $isSVG := eq $img.MediaType.SubType "svg" -}}
|
||||
{{- $isGIF := eq $img.MediaType.SubType "gif" -}}
|
||||
@@ -38,16 +38,15 @@
|
||||
{{- with $height }} height="{{.}}"{{end}}
|
||||
{{- with $img_class }} class="{{.}}"{{end}} />
|
||||
{{- else }}
|
||||
{{- $img_lg := $img.Fit "1200x1200 webp" -}}
|
||||
{{- $img_md := $img_lg.Fit "760x760 webp" -}}{{/* Match `.docs-article-container` max-width */}}
|
||||
{{- $img_sm := $img_md.Fit "400x400 webp" -}}
|
||||
{{/* Process responsive images - use 760px as default display size */}}
|
||||
{{- $img_md := $img.Fit "760x760 webp" -}}
|
||||
{{- $responsive := partial "functions/process_responsive_image.html" (dict "image" $img_md "mode" "responsive") -}}
|
||||
{{- $width := $width | default $img_md.Width -}}
|
||||
{{- $height := $height | default $img_md.Height -}}
|
||||
<img alt="{{ $alt }}" srcset="
|
||||
{{ $img_sm.RelPermalink }} 400w,
|
||||
{{ $img_md.RelPermalink }} 760w,
|
||||
{{ $img_lg.RelPermalink }} 1200w"
|
||||
src="{{ $img_sm.RelPermalink }}"
|
||||
<img alt="{{ $alt }}"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $width }}"
|
||||
height="{{ $height }}"
|
||||
loading="lazy"
|
||||
|
||||
@@ -13,12 +13,30 @@
|
||||
{{ $image_path := path.Join "media" .image }}
|
||||
{{ $image := resources.GetMatch $image_path }}
|
||||
{{ with $image }}
|
||||
{{ $image = $image.Process "Resize 800x webp" }}
|
||||
<div class="gap-8 items-center py-8 px-4 mx-auto max-w-screen-xl xl:gap-16 md:grid md:grid-cols-2 sm:py-16 lg:px-6">
|
||||
{{ if ne $image.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $image
|
||||
"mode" "fit"
|
||||
"sizes" (slice 400 600 800 1200)
|
||||
) }}
|
||||
<img class="w-full"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $responsive.fallback.Width }}"
|
||||
height="{{ $responsive.fallback.Height }}"
|
||||
alt="{{$title | plainify}}" {{ if (modBool $idx 2)}}style="order: 1"{{end}}>
|
||||
{{ else }}
|
||||
<img class="w-full"
|
||||
src="{{$image.RelPermalink}}"
|
||||
width="{{$image.Width}}"
|
||||
height="{{$image.Height}}"
|
||||
alt="{{$title | plainify}}" {{ if (modBool $idx 2)}}style="order: 1"{{end}}>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ errorf "%s uses the `cta-image-paragraph` blox which specifies a non-existent `image` at `assets/%s`. Please add the image to this folder and try again." $page.File.Path $image_path }}
|
||||
{{ end }}
|
||||
<div class="gap-8 items-center py-8 px-4 mx-auto max-w-screen-xl xl:gap-16 md:grid md:grid-cols-2 sm:py-16 lg:px-6">
|
||||
<img class="w-full" src="{{$image.RelPermalink}}" alt="{{$title | plainify}}" {{ if (modBool $idx 2)}}style="order: 1"{{end}}>
|
||||
<div class="mt-4 md:mt-0">
|
||||
<h2 class="mb-4 text-4xl tracking-tight font-extrabold text-gray-900 dark:text-white">{{$title}}</h2>
|
||||
<p class="mb-6 font-light text-gray-500 md:text-lg dark:text-gray-400">{{$text}}</p>
|
||||
|
||||
@@ -35,9 +35,31 @@
|
||||
<div class="flex-none m-w-[130px] mx-auto md:mx-0">
|
||||
|
||||
{{ $img := "" }}
|
||||
{{with $block.design.banner.filename}}
|
||||
{{- $img = resources.Get (path.Join "media" .) -}}
|
||||
{{ if $img }}
|
||||
{{with $block.design.banner.filename}}
|
||||
{{- $img = resources.Get (path.Join "media" .) -}}
|
||||
{{ if $img }}
|
||||
{{ if ne $img.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $img
|
||||
"mode" "fill"
|
||||
"aspect_ratio" "16:9"
|
||||
"sizes" (slice 768 1024 1366 1920 2560)
|
||||
) }}
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
|
||||
<div class="w-full bg-white rounded dark:bg-gray-800">
|
||||
<div class="h-2/4 sm:h-64 overflow-hidden w-full">
|
||||
<img class="w-full object-cover"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="100vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $responsive.fallback.Width }}"
|
||||
height="{{ $responsive.fallback.Height }}"
|
||||
alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{/* Handle GIF without processing */}}
|
||||
{{- $img = $img.Process "webp" -}}
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
|
||||
<div class="w-full bg-white rounded dark:bg-gray-800">
|
||||
@@ -50,7 +72,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
<div id="profile" class="flex justify-center items-center flex-col">
|
||||
{{ if $avatar }}
|
||||
|
||||
@@ -34,17 +34,40 @@
|
||||
{{with $block.design.banner.filename}}
|
||||
{{- $img = resources.Get (path.Join "media" .) -}}
|
||||
{{ if $img }}
|
||||
{{- $img = $img.Process "webp" -}}
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
|
||||
<div class="w-full bg-white rounded dark:bg-gray-800">
|
||||
<div class="h-2/4 sm:h-64 overflow-hidden w-full">
|
||||
<img class="w-full object-cover"
|
||||
src="{{$img.RelPermalink}}"
|
||||
width="{{$img.Width}}" height="{{$img.Height}}"
|
||||
alt="" />
|
||||
{{ if ne $img.MediaType.SubType "gif" }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $img
|
||||
"mode" "fill"
|
||||
"aspect_ratio" "16:9"
|
||||
"sizes" (slice 768 1024 1366 1920 2560)
|
||||
) }}
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
|
||||
<div class="w-full bg-white rounded dark:bg-gray-800">
|
||||
<div class="h-2/4 sm:h-64 overflow-hidden w-full">
|
||||
<img class="w-full object-cover"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="100vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $responsive.fallback.Width }}"
|
||||
height="{{ $responsive.fallback.Height }}"
|
||||
alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{/* Handle GIF without processing */}}
|
||||
{{- $img = $img.Process "webp" -}}
|
||||
<div class="w-full bg-gray-200 dark:bg-gray-900 flex flex-wrap items-center justify-center">
|
||||
<div class="w-full bg-white rounded dark:bg-gray-800">
|
||||
<div class="h-2/4 sm:h-64 overflow-hidden w-full">
|
||||
<img class="w-full object-cover"
|
||||
src="{{$img.RelPermalink}}"
|
||||
width="{{$img.Width}}" height="{{$img.Height}}"
|
||||
alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
@@ -33,8 +33,14 @@
|
||||
<div class="flex items-center justify-center mt-6 space-x-3">
|
||||
{{ $image := resources.GetMatch (path.Join "media" .image) }}
|
||||
{{ with $image }}
|
||||
{{ $image = $image.Process "Fill 75x75 center webp" }}
|
||||
<img class="w-12 h-12 rounded-full" src="{{$image.RelPermalink}}" alt="{{$item.name | plainify}}">
|
||||
{{/* Serve a single 2x retina source for a 48px display */}}
|
||||
{{ $avatar_96 := $image.Process "Fill 96x96 Center webp" }}
|
||||
<img class="w-12 h-12 rounded-full"
|
||||
src="{{$avatar_96.RelPermalink}}"
|
||||
width="48"
|
||||
height="48"
|
||||
alt="{{$item.name | plainify}}"
|
||||
loading="lazy">
|
||||
{{ end }}
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="pr-3 font-medium text-gray-900 dark:text-white">{{.name}}</div>
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
{{/*
|
||||
Responsive Image Processor for Hugo Blox Builder (HBB)
|
||||
Uses Hugo's .Process method with configurable quality and responsive breakpoints
|
||||
|
||||
Parameters:
|
||||
- image: Hugo image resource (required)
|
||||
- sizes: Array of widths for responsive breakpoints (optional, defaults to comprehensive set)
|
||||
- mode: Processing mode - "fit", "fill", "resize", or "responsive" (optional, defaults to "fit")
|
||||
- "fit": Maintains original aspect ratio, scales to fit within width (ignores aspect_ratio param)
|
||||
- "fill": Crops to exact dimensions, requires aspect_ratio for height calculation
|
||||
- "resize": Same as fit, maintains aspect ratio
|
||||
- "responsive": Creates responsive variants from already-processed image (no further processing)
|
||||
- anchor: Anchor point for cropping when using fill (optional, defaults to "smart")
|
||||
- aspect_ratio: Target aspect ratio for fill mode only (optional, format "width:height")
|
||||
- quality: Image quality override (optional, uses Hugo config by default)
|
||||
|
||||
Returns: Map with processed images and srcset string
|
||||
- .srcset: Complete srcset attribute value
|
||||
- .fallback: Default/smallest image for src attribute
|
||||
- .sizes: Array of processed image resources
|
||||
*/}}
|
||||
|
||||
{{ $image := .image }}
|
||||
{{ $sizes := .sizes | default (slice 320 480 768 1024 1366 1920 2560) }}
|
||||
{{ $mode := .mode | default "fit" }}
|
||||
{{ $anchor := .anchor | default "smart" }}
|
||||
{{ $aspect_ratio := .aspect_ratio | default "" }}
|
||||
{{ $quality := .quality }}
|
||||
|
||||
{{/* Initialize return variables */}}
|
||||
{{ $processed_images := slice }}
|
||||
{{ $srcset_parts := slice }}
|
||||
{{ $fallback_image := "" }}
|
||||
|
||||
{{/* Check if image supports processing - only allow known processable formats */}}
|
||||
{{ $can_process := false }}
|
||||
{{ if $image.MediaType }}
|
||||
{{ $format := $image.MediaType.SubType }}
|
||||
{{ $isJPEG := eq $format "jpeg" }}
|
||||
{{ $isPNG := eq $format "png" }}
|
||||
{{ $isWebP := eq $format "webp" }}
|
||||
{{ $can_process = or $isJPEG $isPNG $isWebP }}
|
||||
{{ else }}
|
||||
{{/* Try detecting by file extension if MediaType not available */}}
|
||||
{{ $ext := path.Ext $image.RelPermalink | lower }}
|
||||
{{ $isJPEG := or (eq $ext ".jpg") (eq $ext ".jpeg") }}
|
||||
{{ $isPNG := eq $ext ".png" }}
|
||||
{{ $isWebP := eq $ext ".webp" }}
|
||||
{{ $can_process = or $isJPEG $isPNG $isWebP }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $can_process }}
|
||||
{{/* Handle responsive mode - for already processed images */}}
|
||||
{{ if eq $mode "responsive" }}
|
||||
{{/* Convert to WebP if needed */}}
|
||||
{{ $base_image := $image }}
|
||||
{{ if ne $image.MediaType.SubType "gif" }}{{ $base_image = $image.Process "webp" }}{{ end }}
|
||||
|
||||
{{/* Create responsive variants by resizing down from the processed image */}}
|
||||
{{ range $width := $sizes }}
|
||||
{{/* Only create smaller variants */}}
|
||||
{{ if and (le $width $base_image.Width) (gt $width 0) }}
|
||||
{{ $resized := $base_image.Resize (printf "%dx webp" $width) }}
|
||||
{{ $processed_images = $processed_images | append $resized }}
|
||||
|
||||
{{/* Add to srcset */}}
|
||||
{{ $srcset_part := printf "%s %dw" $resized.RelPermalink $resized.Width }}
|
||||
{{ $srcset_parts = $srcset_parts | append $srcset_part }}
|
||||
|
||||
{{/* Set fallback to smallest image */}}
|
||||
{{ if not $fallback_image }}
|
||||
{{ $fallback_image = $resized }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Always include the original size as the largest option */}}
|
||||
{{ $srcset_part := printf "%s %dw" $base_image.RelPermalink $base_image.Width }}
|
||||
{{ $srcset_parts = $srcset_parts | append $srcset_part }}
|
||||
{{ $processed_images = $processed_images | append $base_image }}
|
||||
{{ if not $fallback_image }}{{ $fallback_image = $base_image }}{{ end }}
|
||||
|
||||
{{ else }}
|
||||
{{/* Process images for each breakpoint */}}
|
||||
{{ range $width := $sizes }}
|
||||
{{/* Only process if the source image is larger than target width */}}
|
||||
{{ if ge $image.Width $width }}
|
||||
{{ $process_params := "" }}
|
||||
{{/* Build processing parameters based on mode */}}
|
||||
{{- if eq $mode "resize" -}}
|
||||
{{- if $quality -}}
|
||||
{{- $process_params = printf "resize %dx webp q%d" $width $quality -}}
|
||||
{{- else -}}
|
||||
{{- $process_params = printf "resize %dx webp" $width -}}
|
||||
{{- end -}}
|
||||
{{- else if eq $mode "fill" -}}
|
||||
{{- if $aspect_ratio -}}
|
||||
{{- $ratio_parts := split $aspect_ratio ":" -}}
|
||||
{{- $ratio_width := int (index $ratio_parts 0) -}}
|
||||
{{- $ratio_height := int (index $ratio_parts 1) -}}
|
||||
{{- $height := div (mul $width $ratio_height) $ratio_width -}}
|
||||
{{- if $quality -}}
|
||||
{{- $process_params = printf "fill %dx%d webp q%d %s" $width $height $quality $anchor -}}
|
||||
{{- else -}}
|
||||
{{- $process_params = printf "fill %dx%d webp %s" $width $height $anchor -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{/* Fill mode without aspect ratio - use resize instead to maintain original proportions */}}
|
||||
{{- if $quality -}}
|
||||
{{- $process_params = printf "resize %dx webp q%d" $width $quality -}}
|
||||
{{- else -}}
|
||||
{{- $process_params = printf "resize %dx webp" $width -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{/* Default to fit - always maintain original aspect ratio */}}
|
||||
{{/* Fit mode should not force an aspect ratio, it should fit within bounds */}}
|
||||
{{- if $quality -}}
|
||||
{{- $process_params = printf "resize %dx webp q%d" $width $quality -}}
|
||||
{{- else -}}
|
||||
{{- $process_params = printf "resize %dx webp" $width -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Process the image */}}
|
||||
{{- $processed := $image.Process $process_params -}}
|
||||
{{- $processed_images = $processed_images | append $processed -}}
|
||||
|
||||
{{/* Add to srcset */}}
|
||||
{{- $srcset_part := printf "%s %dw" $processed.RelPermalink $processed.Width -}}
|
||||
{{- $srcset_parts = $srcset_parts | append $srcset_part -}}
|
||||
|
||||
{{/* Set fallback to smallest image */}}
|
||||
{{- if not $fallback_image -}}
|
||||
{{- $fallback_image = $processed -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{ end }}
|
||||
{{- else -}}
|
||||
{{/* Handle unsupported formats (SVG, GIF) */}}
|
||||
{{- $srcset_parts = $srcset_parts | append (printf "%s %dw" $image.RelPermalink $image.Width) -}}
|
||||
{{- $fallback_image = $image -}}
|
||||
{{- $processed_images = $processed_images | append $image -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Return processed data */}}
|
||||
{{ return (dict
|
||||
"srcset" (delimit $srcset_parts ", ")
|
||||
"fallback" $fallback_image
|
||||
"sizes" $processed_images
|
||||
) }}
|
||||
@@ -15,15 +15,25 @@
|
||||
|
||||
<div class="flex pt-12 pb-4">
|
||||
{{ if $avatar }}
|
||||
{{ $authorImage := $avatar.Fill "384x384 Center" }}
|
||||
<img
|
||||
class="mr-4 h-24 w-24 {{if ne $avatar_shape "square"}}rounded-full{{end}}"
|
||||
width="96"
|
||||
height="96"
|
||||
alt="{{$author_page.Title}}"
|
||||
src="{{ $authorImage.RelPermalink }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
{{/* Use utility for high-res avatar processing (smart for Retina displays) */}}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $avatar
|
||||
"mode" "fill"
|
||||
"aspect_ratio" "1:1"
|
||||
"anchor" "Center"
|
||||
"sizes" (slice 96 192 288 384)
|
||||
) }}
|
||||
|
||||
<img
|
||||
class="mr-4 h-24 w-24 {{if ne $avatar_shape "square"}}rounded-full{{end}}"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="96px"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="96"
|
||||
height="96"
|
||||
alt="{{$author_page.Title}}"
|
||||
loading="lazy"
|
||||
/>
|
||||
{{ end }}
|
||||
<div class="place-self-center">
|
||||
<div class="text-[0.6rem] uppercase leading-3 text-neutral-500 dark:text-neutral-400">
|
||||
|
||||
@@ -14,27 +14,56 @@
|
||||
<div class="group cursor-pointer">
|
||||
|
||||
{{ with $resource }}
|
||||
{{ $image := "" }}
|
||||
{{if $fill_image}}
|
||||
{{ $image = .Fill (printf "960x540 %s" $anchor) }}
|
||||
{{else}}
|
||||
{{ $image = .Fit (printf "960x540 %s" $anchor) }}
|
||||
{{end}}
|
||||
{{ if ne $image.MediaType.SubType "gif" }}{{ $image = $image.Process "webp" }}{{ end }}
|
||||
<div class="overflow-hidden rounded-md bg-gray-100 transition-all hover:scale-105 dark:bg-gray-800">
|
||||
{{/* Skip GIF processing */}}
|
||||
{{ if ne .MediaType.SubType "gif" }}
|
||||
{{/* Process original image first, then create responsive variants */}}
|
||||
{{ $original_image := "" }}
|
||||
{{if $fill_image}}
|
||||
{{ $original_image = .Fill (printf "960x540 %s" $anchor) }}
|
||||
{{else}}
|
||||
{{ $original_image = .Fit (printf "960x540 %s" $anchor) }}
|
||||
{{end}}
|
||||
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $original_image
|
||||
"mode" "responsive"
|
||||
"sizes" (slice 480 768 960 1200 1920)
|
||||
) }}
|
||||
<div class="overflow-hidden rounded-md bg-gray-100 transition-all hover:scale-105 dark:bg-gray-800">
|
||||
<a
|
||||
class="relative block aspect-video"
|
||||
href="{{ $link }}" {{ $target | safeHTMLAttr }}>
|
||||
|
||||
<a
|
||||
class="relative block aspect-video"
|
||||
href="{{ $link }}" {{ $target | safeHTMLAttr }}>
|
||||
<img alt="{{ $item.Title | plainify }}"
|
||||
class="{{if $fill_image}}object-fill{{else}}object-contain{{end}} transition-all"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $original_image.Width }}"
|
||||
height="{{ $original_image.Height }}"
|
||||
decoding="async"
|
||||
fetchpriority="high"
|
||||
loading="lazy"
|
||||
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;" />
|
||||
</a>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{/* Handle GIF without processing */}}
|
||||
<div class="overflow-hidden rounded-md bg-gray-100 transition-all hover:scale-105 dark:bg-gray-800">
|
||||
<a
|
||||
class="relative block aspect-video"
|
||||
href="{{ $link }}" {{ $target | safeHTMLAttr }}>
|
||||
|
||||
<img alt="{{ $item.Title | plainify }}"
|
||||
class="{{if $fill_image}}object-fill{{else}}object-contain{{end}} transition-all"
|
||||
data-nimg="fill"
|
||||
decoding="async"
|
||||
fetchpriority="high" height="{{ $image.Height }}" loading="lazy" src="{{ $image.RelPermalink }}"
|
||||
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;"
|
||||
width="{{ $image.Width }}"></a>
|
||||
</div>
|
||||
<img alt="{{ $item.Title | plainify }}"
|
||||
class="{{if $fill_image}}object-fill{{else}}object-contain{{end}} transition-all"
|
||||
src="{{ .RelPermalink }}"
|
||||
width="{{ .Width }}"
|
||||
height="{{ .Height }}"
|
||||
loading="lazy"
|
||||
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;" />
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{end}}
|
||||
<div class="">
|
||||
<div class="">
|
||||
@@ -70,16 +99,16 @@
|
||||
<div class="relative h-5 w-5 flex-shrink-0">
|
||||
{{ $avatar := (.Resources.ByType "image").GetMatch "*avatar*" }}
|
||||
{{ if $avatar }}
|
||||
{{ $authorImage := $avatar.Process "Fill 100x100 Center webp" }}
|
||||
<img alt="avatar"
|
||||
class="rounded-full object-cover"
|
||||
data-nimg="fill"
|
||||
decoding="async" height="{{$authorImage.Height}}"
|
||||
loading="lazy"
|
||||
sizes="20px"
|
||||
src="{{$authorImage.RelPermalink}}"
|
||||
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;"
|
||||
width="{{$authorImage.Width}}">
|
||||
{{/* Use a single 2x retina source for a 20px display */}}
|
||||
{{ $avatar_40 := $avatar.Process "Fill 40x40 Center webp" }}
|
||||
<img alt="avatar"
|
||||
class="rounded-full object-cover"
|
||||
src="{{$avatar_40.RelPermalink}}"
|
||||
width="20"
|
||||
height="20"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style="position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent;" />
|
||||
{{ else }}
|
||||
{{ warnf "An image named `avatar` was not found in the `%s` folder" $value.Path }}
|
||||
{{ end }}
|
||||
|
||||
@@ -14,9 +14,21 @@
|
||||
<div class="md:flex">
|
||||
<div class="md:flex-shrink-0 overflow-hidden">
|
||||
{{ with $resource }}
|
||||
{{ $image := .Fill (printf "655x655 %s" $anchor) }}
|
||||
{{ if ne $image.MediaType.SubType "gif" }}{{ $image = $image.Process "webp" }}{{ end }}
|
||||
<img class="h-48 w-full object-cover md:w-48 hover:scale-125 transition duration-500 cursor-pointer object-cover" loading="lazy" src="{{ $image.RelPermalink }}" height="{{ $image.Height }}" width="{{ $image.Width }}" alt="{{ $item.Title | plainify }}">
|
||||
{{ $original_image := .Fill (printf "655x655 %s" $anchor) }}
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $original_image
|
||||
"mode" "responsive"
|
||||
"sizes" (slice 192 384 480 655)
|
||||
) }}
|
||||
|
||||
<img class="h-48 w-full object-cover md:w-48 hover:scale-125 transition duration-500 cursor-pointer object-cover"
|
||||
srcset="{{ $responsive.srcset }}"
|
||||
sizes="(max-width: 768px) 100vw, 192px"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $original_image.Width }}"
|
||||
height="{{ $original_image.Height }}"
|
||||
loading="lazy"
|
||||
alt="{{ $item.Title | plainify }}">
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="p-8">
|
||||
|
||||
@@ -48,8 +48,14 @@
|
||||
{{ end }}
|
||||
<div class="group inline-flex items-center text-current gap-x-1.5 mx-1">
|
||||
{{ with $avatar }}
|
||||
{{ $avatar = $avatar.Process "Fill 100x100 Center webp" }}
|
||||
<img src="{{ $avatar.RelPermalink }}" alt="{{ $author_page.Title }}" class="inline-block h-4 w-4 rounded-full border border-current" loading="lazy" />
|
||||
{{/* Simple 2x retina image for small avatar */}}
|
||||
{{ $avatar_32 := $avatar.Process "Fill 32x32 Center webp" }}
|
||||
<img src="{{ $avatar_32.RelPermalink }}"
|
||||
width="16"
|
||||
height="16"
|
||||
alt="{{ $author_page.Title }}"
|
||||
class="inline-block h-4 w-4 rounded-full border border-current"
|
||||
loading="lazy" />
|
||||
{{ end }}
|
||||
<div {{if $author_page.Params.website}}class="group-hover:underline"{{end}}>{{ $author_page.Title }}</div>
|
||||
</div>
|
||||
@@ -116,26 +122,78 @@
|
||||
|
||||
{{/* Determine image placement. */}}
|
||||
{{ $placement := .Params.image.placement | default 1 }}{{/* Default to full column width. */}}
|
||||
{{/* Fit image to container's max width */}}
|
||||
{{/* Configure responsive processing based on placement */}}
|
||||
{{ $image_container := "" }}
|
||||
{{ $responsive_sizes := slice }}
|
||||
{{ $sizes_attr := "" }}
|
||||
{{ if eq $placement 2}}
|
||||
{{ $image_container = "container" }}
|
||||
{{ $image = $featured.Fit "1200x2500" }}
|
||||
{{ $image_container = "container" }}
|
||||
{{ $responsive_sizes = slice 480 768 1024 1200 1600 }}
|
||||
{{ $sizes_attr = "(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1200px) 80vw, 1200px" }}
|
||||
{{else if eq $placement 3}}
|
||||
{{ $image_container = "container-fluid" }}
|
||||
{{ $image := $featured.Fit "2560x2560" }}
|
||||
{{ $image_container = "container-fluid" }}
|
||||
{{ $responsive_sizes = slice 768 1024 1366 1920 2560 }}
|
||||
{{ $sizes_attr = "(max-width: 768px) 100vw, (max-width: 1366px) 90vw, (max-width: 1920px) 80vw, 2560px" }}
|
||||
{{else}}
|
||||
{{ $image_container = "article-container" }}
|
||||
{{ $image = $featured.Fit "720x2500" }}
|
||||
{{ $image_container = "article-container" }}
|
||||
{{ $responsive_sizes = slice 320 480 640 720 960 }}
|
||||
{{ $sizes_attr = "(max-width: 480px) 100vw, (max-width: 640px) 90vw, (max-width: 720px) 80vw, 720px" }}
|
||||
{{end}}
|
||||
{{ if ne $image.MediaType.SubType "gif" }}{{ $image = $image.Process "webp" }}{{ end }}
|
||||
{{/* Featured image */}}
|
||||
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16" style="max-width: {{$image.Width}}px; max-height: {{$image.Height}}px;">
|
||||
<div style="position: relative">
|
||||
<img src="{{ $image.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" alt="{{ with $.Params.image.alt_text }}{{.}}{{ end }}" class="featured-image">
|
||||
{{ with $.Params.image.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
|
||||
|
||||
{{/* Process featured image using utility with placement-specific constraints */}}
|
||||
{{ if ne $featured.MediaType.SubType "gif" }}
|
||||
{{/* Pre-fit image to placement constraints, then apply responsive processing */}}
|
||||
{{ $fitted_image := $featured }}
|
||||
{{ if eq $placement 2}}
|
||||
{{ $fitted_image = $featured.Fit "1200x2500" }}
|
||||
{{else if eq $placement 3}}
|
||||
{{ $fitted_image = $featured.Fit "2560x2560" }}
|
||||
{{else}}
|
||||
{{ $fitted_image = $featured.Fit "720x2500" }}
|
||||
{{end}}
|
||||
|
||||
{{ $responsive := partial "functions/process_responsive_image.html" (dict
|
||||
"image" $fitted_image
|
||||
"mode" "responsive"
|
||||
"sizes" $responsive_sizes
|
||||
) }}
|
||||
|
||||
{{/* Featured image - use fitted image dimensions for container, responsive for srcset */}}
|
||||
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16" style="max-width: {{$fitted_image.Width}}px; max-height: {{$fitted_image.Height}}px;">
|
||||
<div style="position: relative">
|
||||
<img srcset="{{ $responsive.srcset }}"
|
||||
sizes="{{ $sizes_attr }}"
|
||||
src="{{ $responsive.fallback.RelPermalink }}"
|
||||
width="{{ $fitted_image.Width }}"
|
||||
height="{{ $fitted_image.Height }}"
|
||||
alt="{{ with $.Params.image.alt_text }}{{.}}{{ end }}"
|
||||
class="featured-image"
|
||||
fetchpriority="high">
|
||||
{{ with $.Params.image.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{/* Handle GIF - apply same placement constraints without responsive processing */}}
|
||||
{{ $image := $featured }}
|
||||
{{ if eq $placement 2}}
|
||||
{{ $image = $featured.Fit "1200x2500" }}
|
||||
{{else if eq $placement 3}}
|
||||
{{ $image = $featured.Fit "2560x2560" }}
|
||||
{{else}}
|
||||
{{ $image = $featured.Fit "720x2500" }}
|
||||
{{end}}
|
||||
<div class="article-header {{$image_container}} featured-image-wrapper mt-4 mb-16" style="max-width: {{$image.Width}}px; max-height: {{$image.Height}}px;">
|
||||
<div style="position: relative">
|
||||
<img src="{{ $image.RelPermalink }}"
|
||||
width="{{ $image.Width }}"
|
||||
height="{{ $image.Height }}"
|
||||
alt="{{ with $.Params.image.alt_text }}{{.}}{{ end }}"
|
||||
class="featured-image"
|
||||
fetchpriority="high">
|
||||
{{ with $.Params.image.caption }}<span class="article-header-caption">{{ . | markdownify | emojify }}</span>{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{end}}
|
||||
|
||||
{{/* EVENT / PUBLICATION METADATA */}}
|
||||
|
||||
Reference in new issue
Block a user