Skip to content

Commit 537aaf4

Browse files
leopoldblumclaude
andcommitted
Fix People media grid + Faces of BEARS right-aligning the last incomplete row
CSS-columns masonry balances uniform-aspect images so the last item lands in whichever column keeps heights even — visually the rightmost column. Switch both surfaces to CSS Grid so an incomplete final row left-aligns. Adds a "native-rows" mode to ImageGrid (CSS Grid + native aspect, no 1:1 crop), plumbed through MediaAccordion / CollapsibleImageGrid, and used for the People category. FacesOfBears swaps its inline columns for grid directly since FaceCard isn't an Img. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e83e830 commit 537aaf4

5 files changed

Lines changed: 42 additions & 18 deletions

File tree

src/components/about/FacesOfBears.astro

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,13 @@ const desktopColWidth = `${+(100 / cols).toFixed(3)}vw`;
9696
x-bind:class="isOpen ? 'pb-6' : 'pb-0'"
9797
>
9898
<div x-ref="content" class="p-1">
99-
<div class="columns-2 lg:columns-3 gap-4 lg:gap-6">
99+
<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-6 items-start">
100100
{faces.map((face) => (
101-
<div class="break-inside-avoid mb-4 lg:mb-6">
102-
<FaceCard
103-
coverImage={face.coverImage}
104-
name={face.name}
105-
role={face.role}
106-
/>
107-
</div>
101+
<FaceCard
102+
coverImage={face.coverImage}
103+
name={face.name}
104+
role={face.role}
105+
/>
108106
))}
109107
</div>
110108
</div>

src/components/reusable/CollapsibleImageGrid.astro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ interface Props {
1717
gap?: "sm" | "md" | "lg";
1818
/** Number of preview rows visible when collapsed (default: 1) */
1919
previewRows?: number;
20+
/** Aspect ratio mode for the inner ImageGrid (default: 'native' masonry) */
21+
aspectRatio?: "square" | "native" | "native-rows";
2022
/** Additional CSS classes for the ImageGrid */
2123
class?: string;
2224
}
@@ -26,6 +28,7 @@ const {
2628
cols = 4,
2729
gap = "md",
2830
previewRows = 1,
31+
aspectRatio = "native",
2932
class: className,
3033
} = Astro.props;
3134
@@ -86,7 +89,7 @@ const desktopColWidth = `${+(100 / cols).toFixed(3)}vw`;
8689
cols={cols}
8790
gap={gap}
8891
enableClickToEnlarge={true}
89-
aspectRatio="native"
92+
aspectRatio={aspectRatio}
9093
class={className}
9194
/>
9295
</div>

src/components/reusable/ImageGrid.astro

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ interface Props {
1616
class?: string;
1717
/** Whether to enable click-to-enlarge functionality (default: true) */
1818
enableClickToEnlarge?: boolean;
19-
/** Aspect ratio mode: 'square' crops to 1:1, 'native' uses masonry column layout (default: 'square') */
20-
aspectRatio?: "square" | "native";
19+
/** Aspect ratio mode: 'square' crops to 1:1, 'native' uses masonry column layout, 'native-rows' uses CSS Grid rows with native aspect ratios (default: 'square') */
20+
aspectRatio?: "square" | "native" | "native-rows";
2121
}
2222
2323
const {
@@ -78,7 +78,27 @@ const masonryItemGap: Record<string, string> = {
7878
};
7979
---
8080

81-
{aspectRatio === "native" ? (
81+
{aspectRatio === "native-rows" ? (
82+
<!-- NATIVE-ROWS MODE: CSS Grid with native aspect ratios (clean rows, last row left-aligned) -->
83+
<div
84+
class={`not-prose grid ${gridCols} ${gridGap} items-start px-4 sm:px-6 py-3 ${className || ""}`}
85+
>
86+
{images.map(({ image, alt, description }, index) => (
87+
<div class={enableClickToEnlarge ? '' : 'overflow-hidden rounded-lg'}>
88+
<Img
89+
src={image}
90+
alt={alt}
91+
description={description}
92+
class="w-full h-auto object-cover transition-transform duration-300 group-hover/image:scale-105"
93+
width="100%"
94+
sizes={imageSizes}
95+
loading={index <= cols * 2 ? "eager" : "lazy"}
96+
enableClickToEnlarge={enableClickToEnlarge}
97+
/>
98+
</div>
99+
))}
100+
</div>
101+
) : aspectRatio === "native" ? (
82102
<!-- MASONRY MODE: CSS columns for true masonry layout -->
83103
<div
84104
class={`not-prose ${masonryColCount[cols] ?? masonryColCount[4]} ${gridGap} px-4 sm:px-6 py-3 ${className || ""}`}

src/components/reusable/MediaAccordion.astro

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ interface Props {
2020
cols?: 2 | 3 | 4 | 5 | 6;
2121
/** Gap between images (default: 'md') */
2222
gap?: "sm" | "md" | "lg";
23+
/** Aspect ratio mode for the inner ImageGrid (default: 'native' masonry) */
24+
aspectRatio?: "square" | "native" | "native-rows";
2325
/** Locale for translated UI strings */
2426
locale?: Locale;
2527
}
2628
27-
const { title, images, class: className, cols = 4, gap = "md", locale = "en" } = Astro.props;
29+
const { title, images, class: className, cols = 4, gap = "md", aspectRatio = "native", locale = "en" } = Astro.props;
2830
const thisIsLabel = mediaUiStrings[locale]['this-is'];
2931
const sectionId = `media-${title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')}`;
3032
@@ -55,13 +57,13 @@ const fitsInOneRow = images.length <= effectiveCols;
5557
cols={effectiveCols}
5658
gap={gap}
5759
enableClickToEnlarge={true}
58-
aspectRatio="native"
60+
aspectRatio={aspectRatio}
5961
/>
6062
</div>
6163
</div>
6264
) : (
6365
<div id={sectionId} class={`media-accordion rounded-lg overflow-hidden scroll-mt-20 ${className || ""}`}>
64-
<CollapsibleImageGrid images={images} cols={effectiveCols} gap={gap} previewRows={1}>
66+
<CollapsibleImageGrid images={images} cols={effectiveCols} gap={gap} aspectRatio={aspectRatio} previewRows={1}>
6567
<Fragment slot="header">
6668
{/* Accordion Header */}
6769
<button

src/pages/media.astro

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const visibility = (await getEntry('media-section-visibility', 'settings'))!.dat
2525
let pageContent: Awaited<ReturnType<typeof getPageContent>> = undefined;
2626
let heroImage: ImageMetadata | null = null;
2727
let heroAlt = "";
28-
const displayCategories: { label: string; images: ImageWithAlt[] }[] = [];
28+
const displayCategories: { id: string; label: string; images: ImageWithAlt[] }[] = [];
2929
3030
if (visibility.showMediaPage) {
3131
pageContent = await getPageContent('media/media-title', locale);
@@ -94,12 +94,12 @@ if (visibility.showMediaPage) {
9494
for (const cat of contentCategories) {
9595
if (cat.id === 'all') {
9696
if (allImages.length > 0) {
97-
displayCategories.push({ label: allEntry!.label, images: allImages });
97+
displayCategories.push({ id: 'all', label: allEntry!.label, images: allImages });
9898
}
9999
} else {
100100
const loaded = categoriesWithImages.find((c) => c.id === cat.id);
101101
if (loaded) {
102-
displayCategories.push({ label: loaded.label, images: loaded.images });
102+
displayCategories.push({ id: loaded.id, label: loaded.label, images: loaded.images });
103103
}
104104
}
105105
}
@@ -128,6 +128,7 @@ if (visibility.showMediaPage) {
128128
images={category.images}
129129
cols={4}
130130
gap="md"
131+
aspectRatio={category.id === 'people' ? 'native-rows' : 'native'}
131132
locale={locale}
132133
/>
133134
))

0 commit comments

Comments
 (0)