Skip to content

Commit 98666f4

Browse files
committed
add member photos to member cards
1 parent 4478d9b commit 98666f4

File tree

3 files changed

+45
-6
lines changed

3 files changed

+45
-6
lines changed

apps/blog/src/routes/about/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
</div>
4141
{/if}
4242
{#if about.members.length >= 1}
43-
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 w-full">
43+
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 w-full items-stretch">
4444
{#each about.members as member, index (member.name)}
4545
<div use:animate={{ delay: 300 + index * 100 }}>
4646
<MemberCard {member} />

packages/ui/src/components/MemberCard.svelte

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
<script lang="ts">
22
import type { Member } from '@a11y.cool/data';
33
import { Globe, LinkedinLogo } from 'phosphor-svelte';
4+
import { buildImageUrl } from '@a11y.cool/utils';
45
56
let { member }: { member: Member } = $props();
7+
8+
// Get API URL from environment
9+
const apiUrl = import.meta.env.VITE_API_URL;
10+
11+
// Build image URL with format detection
12+
const imageUrl = $derived(member.image ? buildImageUrl(member.image.id, apiUrl) : null);
613
</script>
714

815
<article
9-
class="rounded-3xl shadow-card border border-border-input bg-background-alt p-6 flex flex-col items-start gap-4 min-h-[320px]"
16+
class="rounded-3xl shadow-card border border-border-input bg-background-alt p-6 flex flex-col items-start gap-4 h-full"
1017
>
1118
<!-- Circular member image -->
1219
{#if member.image}
1320
<div
1421
class="w-24 h-24 rounded-full overflow-hidden border-2 border-border-input mb-2 self-center"
1522
>
1623
<img
17-
src={member.image.id}
24+
src={imageUrl}
1825
alt={member.image.description || `${member.name} profile picture`}
1926
class="w-full h-full object-cover"
2027
/>
@@ -39,7 +46,7 @@
3946
</p>
4047

4148
<!-- URL links at the bottom -->
42-
<div class="flex gap-4 mt-auto">
49+
<div class="flex gap-4 mt-auto pt-4">
4350
{#if member.website}
4451
<a
4552
href={member.website}
@@ -48,7 +55,7 @@
4855
class="flex items-center gap-2 px-3 py-2 rounded-lg bg-background border border-border-input hover:bg-muted transition-colors"
4956
aria-label="Visit {member.name}'s website"
5057
>
51-
<Globe size={16} />
58+
<Globe size={16} aria-hidden="true" />
5259
<span class="text-sm">Website</span>
5360
</a>
5461
{/if}
@@ -61,7 +68,7 @@
6168
class="flex items-center gap-2 px-3 py-2 rounded-lg bg-background border border-border-input hover:bg-muted transition-colors"
6269
aria-label="Visit {member.name}'s LinkedIn profile"
6370
>
64-
<LinkedinLogo size={16} />
71+
<LinkedinLogo size={16} aria-hidden="true" />
6572
<span class="text-sm">LinkedIn</span>
6673
</a>
6774
{/if}

packages/utils/src/global-utils.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,35 @@ export const getLangLabel = (lang: string) => {
6767
};
6868
return map[lang] || lang;
6969
};
70+
71+
/**
72+
* Builds an image URL for Directus assets with format detection
73+
* @param imageId - The Directus file ID
74+
* @param apiUrl - The base API URL (from VITE_API_URL)
75+
* @param quality - Image quality (default: 80)
76+
* @returns The complete image URL with format parameters
77+
*/
78+
export const buildImageUrl = (imageId: string, apiUrl: string, quality: number = 80): string => {
79+
// Check if browser supports AVIF format
80+
const supportsAvif =
81+
typeof window !== 'undefined' &&
82+
window.HTMLCanvasElement &&
83+
document.createElement('canvas').toDataURL('image/avif').indexOf('data:image/avif') === 0;
84+
85+
// Check if browser supports WebP format
86+
const supportsWebp =
87+
typeof window !== 'undefined' &&
88+
window.HTMLCanvasElement &&
89+
document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') === 0;
90+
91+
// Determine the best format to use
92+
let format = 'jpg'; // fallback
93+
if (supportsAvif) {
94+
format = 'avif';
95+
} else if (supportsWebp) {
96+
format = 'webp';
97+
}
98+
99+
// Build the URL with the format
100+
return `${apiUrl}/assets/${imageId}?quality=${quality}&format=${format}`;
101+
};

0 commit comments

Comments
 (0)