From 57d519b4744c2825dbc8eb49af8ef5ee630e3cae Mon Sep 17 00:00:00 2001 From: Vidminas <5411598+Vidminas@users.noreply.github.com> Date: Fri, 10 Jul 2026 08:56:57 +0100 Subject: [PATCH] feat(portfolio): add design.aspect_ratio knob for card images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Card image dimensions were hardcoded: a fixed-height box (h-48) plus a fixed 3:2 crop (Fill "600x400"). The box ratio therefore tracked card width / column count and rarely matched the crop, so object-cover re-cropped the already cropped image (a double-crop). Add a `design.aspect_ratio` knob (square | landscape | portrait | wide | photo) that mirrors the gallery block's vocabulary and drives BOTH the CSS aspect box and the Fill dimensions from one map, so the box always matches the crop — a single, predictable crop at any width / column count. Defaults to `photo` (3:2, 600x400), preserving the previous appearance, so it's non-breaking. All aspect class strings are literal in the template so Tailwind's scanner emits them. Documents the option in the block README. Co-Authored-By: Claude Opus 4.8 --- modules/blox/blox/portfolio/README.md | 2 ++ modules/blox/blox/portfolio/block.html | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/modules/blox/blox/portfolio/README.md b/modules/blox/blox/portfolio/README.md index 7c397faae..da1e4279b 100644 --- a/modules/blox/blox/portfolio/README.md +++ b/modules/blox/blox/portfolio/README.md @@ -43,6 +43,7 @@ A flexible, filterable portfolio block for showcasing work with Alpine.js-powere # text: "Browse All" # Custom text design: columns: 3 + aspect_ratio: photo # Card image shape: photo (3:2), landscape, square, portrait, wide fallback_icon: code-bracket # Or: academic-cap, paint-brush, camera, etc. ``` @@ -95,6 +96,7 @@ featured: true | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `columns` | integer | 3 | Grid columns (2, 3, or 4) | +| `aspect_ratio` | string | `photo` | Card image shape — matches the crop to the box so images aren't double-cropped. One of `photo` (3:2), `landscape` (4:3), `square` (1:1), `portrait` (3:4), `wide` (16:9). | | `fallback_icon` | string | `code-bracket` | Icon shown when item has no image. Supports all Hugo Blox icon packs (hero, brands, devicon, emoji, custom). Format: `icon-name` or `pack/icon-name` | | `status_badge.enable` | boolean | true | Show or hide the status badge | diff --git a/modules/blox/blox/portfolio/block.html b/modules/blox/blox/portfolio/block.html index e3db80fb2..3928a0046 100644 --- a/modules/blox/blox/portfolio/block.html +++ b/modules/blox/blox/portfolio/block.html @@ -173,6 +173,28 @@ {{ $fallback_icon = $fallback_raw }} {{ end }} +{{/* Card image aspect ratio (square | landscape | portrait | wide | photo) + The chosen ratio drives BOTH the CSS aspect box and the `Fill` dimensions, + so the box always matches the crop (one predictable crop at any width / + column count). Every class string is written out literally so Tailwind's + scanner generates the utilities. Photo (3:2) is the default with a fixed 600x400 crop. */}} +{{ $ratio_map := dict + "square" (dict "class" "aspect-square" "fill" "600x600 webp") + "landscape" (dict "class" "aspect-[4/3]" "fill" "600x450 webp") + "portrait" (dict "class" "aspect-[3/4]" "fill" "480x640 webp") + "wide" (dict "class" "aspect-[16/9]" "fill" "800x450 webp") + "photo" (dict "class" "aspect-[3/2]" "fill" "600x400 webp") +}} +{{ $ratio_name := "photo" }} +{{ $ratio_raw := index $design "aspect_ratio" }} +{{ if eq (printf "%T" $ratio_raw) "string" }} + {{ $trimmed := lower (strings.TrimSpace $ratio_raw) }} + {{ if $trimmed }}{{ $ratio_name = $trimmed }}{{ end }} +{{ end }} +{{ $ratio := index $ratio_map $ratio_name | default (index $ratio_map "photo") }} +{{ $ratio_class := index $ratio "class" }} +{{ $ratio_fill := index $ratio "fill" }} +
{{/* Header */}} @@ -271,7 +293,7 @@

class="group relative flex flex-col bg-white dark:bg-white/[0.03] backdrop-blur-xl rounded-2xl border border-gray-200 dark:border-white/[0.08] overflow-hidden hover:border-primary-500/50 hover:bg-gray-50 dark:hover:bg-white/[0.06] transition-all duration-300 hover:shadow-xl hover:shadow-primary-500/10 card-hover-lift" > {{/* Card Header / Image */}} -
+
{{ $image := $item.Resources.GetMatch "{featured,cover,thumbnail}*" }} {{ if $image }} {{ if not (reflect.IsImageResourceProcessable $image) }} @@ -281,7 +303,7 @@

class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" > {{ else }} - {{ $img := $image.Fill "600x400 webp" }} + {{ $img := $image.Fill $ratio_fill }}