Committee
The Committee component displays committee member profiles in a responsive grid layout, showing photos, names, roles, bios, and LinkedIn links.
Overview
Section titled “Overview”- Type: Content Component
- Category: Team Display
- Complexity: Moderate
- File:
src/components/Committee.astro
Features
Section titled “Features”- Type-Safe Data - Uses TypeScript interfaces from committee types
- Configuration-Driven - Members defined in country-specific config files
- Image Handling - Supports both imported and string-path images
- Fallback Content - Placeholder shown when no members exist
- Responsive Grid - Adapts to screen size
- Social Links - LinkedIn integration for networking
interface CommitteeProps extends Partial<CommitteeConfig> { country?: CommitteeCountry; heading?: string; description?: string;}Prop Details
Section titled “Prop Details”| Prop | Type | Default | Description |
|---|---|---|---|
country | CommitteeCountry | undefined | Country code to load committee config |
members | CommitteeMember[] | From config or fallback | Array of committee members |
heading | string | 'Committee' | Section heading text |
description | string | undefined | Optional description below heading |
---import Committee from '@/components/Committee.astro';---
<!-- Automatically loads finlandCommittee from config --><Committee country="finland" />---import Committee from '@/components/Committee.astro';---
<Committee country="sweden" heading="Swedish Representatives" description="Meet our committee members in Sweden"/>---import Committee from '@/components/Committee.astro';import type { CommitteeMember } from '@/data/representation/committee/types';
const customMembers: CommitteeMember[] = [ { name: 'Jane Doe', role: 'President', bio: 'Description here...', imageAlt: 'Jane Doe portrait', linkedinUrl: 'https://linkedin.com/in/janedoe', },];---
<Committee members={customMembers} heading="Leadership Team" />Committee Member Interface
Section titled “Committee Member Interface”interface CommitteeMember { name: string; role: string; bio: string; imageAlt: string; imageSrc?: string | ImageMetadata; // Optional image linkedinUrl?: string; // Optional LinkedIn affiliations?: string; // Optional org affiliations}Country Configuration Files
Section titled “Country Configuration Files”Committee members are defined in country-specific files:
src/data/representation/committee/├── finlandCommittee.ts Best practice example├── swedenCommittee.ts├── norwayCommittee.ts├── icelandCommittee.ts├── kingdomDenmarkCommittee.ts└── types.tsExample Configuration
Section titled “Example Configuration”import type { CommitteeMember } from './types';import johnDoeImage from '@/assets/images/committee/finland/john-doe.webp';
export const finlandCommittee: CommitteeMember[] = [ { name: 'John Doe', role: 'President', bio: 'John has been serving Finnish healthcare since 2015...', imageSrc: johnDoeImage, imageAlt: 'John Doe - President of FiNAN Finland', linkedinUrl: 'https://linkedin.com/in/johndoe', affiliations: 'Helsinki University Hospital', },];Image Handling
Section titled “Image Handling”The component supports two image formats:
1. Imported Images (Recommended)
Section titled “1. Imported Images (Recommended)”import memberPhoto from '@/assets/images/committee/finland/jane-doe.webp';
{ imageSrc: memberPhoto, // ImageMetadata type imageAlt: 'Jane Doe portrait',}Benefits:
- Automatic optimization
- Type safety
- Build-time validation
2. String Paths
Section titled “2. String Paths”{ imageSrc: '/images/john-doe.jpg', // String path imageAlt: 'John Doe portrait',}Use when:
- Images are in
public/directory - External image URLs (not recommended)
No Image Fallback
Section titled “No Image Fallback”If imageSrc is not provided, a placeholder icon appears:
<div class="flex h-full w-full items-center justify-center bg-gray-200"> <svg class="h-8 w-8 text-gray-400" viewBox="0 0 48 48"> <!-- Placeholder image icon --> </svg></div>Styling
Section titled “Styling”Layout
Section titled “Layout”- Grid: Flexbox with wrapping
- Card Style: Member info beside circular photo
- Responsive: Stacks gracefully on mobile
Photo Display
Section titled “Photo Display”- Size: 96x96 pixels (24 Tailwind units)
- Shape: Circular (
rounded-full) - Border: Light gray border
- Object Fit: Cover for proper cropping
Typography
Section titled “Typography”- Name: xl, bold, gray-900
- Affiliations: sm, gray-900 (if present)
- Role: lg, gray-900
- Bio: sm, gray-700, relaxed leading
LinkedIn Button
Section titled “LinkedIn Button”- White background with shadow
- Blue hover state
- LinkedIn icon from assets
Adding Committee Members
Section titled “Adding Committee Members”-
Prepare member photo
- Format: WebP recommended
- Size: At least 200x200px
- Name: kebab-case (e.g.,
john-doe.webp) - Location:
src/assets/images/committee/[country]/
-
Import photo in config file
import johnDoe from '@/assets/images/committee/finland/john-doe.webp'; -
Add member object to array
export const finlandCommittee: CommitteeMember[] = [// ... existing members{name: 'John Doe',role: 'Secretary',bio: 'Brief bio about John...',imageSrc: johnDoe,imageAlt: 'John Doe - Secretary',linkedinUrl: 'https://linkedin.com/in/johndoe',},]; -
Build and test
Terminal window pnpm build
Common Issues
Section titled “Common Issues”Image Not Showing
Section titled “Image Not Showing”Cause: Incorrect image import path
Solution: Verify file exists and path matches:
import photo from '@/assets/images/committee/finland/john-doe.webp';// ^^ Alias ^^ Country ^^ Exact filenameType Errors
Section titled “Type Errors”Cause: Missing required fields
Solution: Ensure all required fields are present:
name,role,bio,imageAltare requiredimageSrc,linkedinUrl,affiliationsare optional
Build Fails
Section titled “Build Fails”Cause: Image file doesn’t exist
Solution: Ensure image file exists at the specified path before importing.
Accessibility
Section titled “Accessibility”- Alt Text: Every image requires descriptive
imageAlt - Semantic HTML: Uses
<article>for each member - Link Labels: LinkedIn links have clear purpose
- Heading Structure: Proper
<h2>for section,<h3>for names
Performance
Section titled “Performance”- Lazy Loading: Images use
loading="lazy"except above fold - Optimized Images: Astro optimizes WebP images automatically
- Responsive Images: Correct size served per device
Best Practices
Section titled “Best Practices”✅ Do:
- Use WebP format for photos
- Write descriptive bios (2-3 sentences)
- Include LinkedIn URLs for networking
- Use consistent photo dimensions
❌ Don’t:
- Use high-resolution photos (wastes bandwidth)
- Leave alt text generic (“image” or “photo”)
- Hardcode member data in the component
- Mix imported and string-path images in same config
Dependencies
Section titled “Dependencies”- Committee Types (
src/data/representation/committee/types.ts) - Committee Configs (
src/data/representation/committee/[country]Committee.ts) - Astro Image (
astro:assets)
Used On
Section titled “Used On”- All 8 representation pages:
/representation/finland/representation/sweden/representation/norway/representation/iceland/representation/kingdom-denmark
Related Components
Section titled “Related Components”- RepresentationContactSection - Contact info for reps
Related Documentation
Section titled “Related Documentation”- Committee Management Guide Essential for editors
- Asset Management - Where to place images
- Data Configuration - Config file patterns
Source Code
Section titled “Source Code”View the complete implementation:
https://github.com/poncardasm/finan-website/blob/main/src/components/Committee.astrohttps://github.com/poncardasm/finan-website/blob/main/src/data/representation/committee/types.tshttps://github.com/poncardasm/finan-website/blob/main/src/data/representation/committee/finlandCommittee.ts