Dev Log

About: real headshots for Shane/Kelli/Ryan + corrected LinkedIn URLs

· Claude Opus 4.7 (1M context) · Bot
aboutteamimages

What was done

  • Three new portraits added to src/assets/team/: shane.png, kelli.png, ryan.png (originals from .tmp/plans/about/). Astro’s image pipeline auto-generates optimized .webp variants at the requested sizes.
  • /about/ team grid (src/pages/about/index.astro): added a portraitImages slug→ImageMetadata map keyed by team slug. The .team-portrait circle now renders an <Image> (160px requested, lazy loaded) when the slug has an entry; falls back to initials otherwise. Added overflow: hidden and object-fit: cover so the image fills the circle without a halo.
  • /about/{slug}/ bio pages (src/pages/about/[slug].astro): same portraitImages map. Bio hero portrait renders an <Image> (400px requested, eager loaded since it’s above-the-fold) when present; falls back to initials. aria-hidden toggles to false when an image renders (so the alt text is exposed) and true when initials render (purely decorative).
  • Placeholder aside copy updated from “Headshots are placeholders in this prototype. Real photography to follow before public launch.” to “Headshots for the rest of the team are placeholders…” — accurate now that 3 of 14 are real.
  • LinkedIn URL corrections in src/data/team.ts:
    • Kelli: linkedin.com/in/kelli-black-6b0379141linkedin.com/in/kelli-j-black
    • Ryan: linkedin.com/in/vardelinkedin.com/in/ryan-t-benson

Why

Real photography arriving incrementally; first 3 leadership headshots ready. LinkedIn URLs were outdated/wrong (Ryan’s was pointing to an unrelated profile slug varde).

Pattern for future headshots

When the next person’s headshot arrives:

  1. Drop the file into src/assets/team/{slug}.png (or .jpg / .webp)
  2. Add an import + entry in BOTH page files (src/pages/about/index.astro and src/pages/about/[slug].astro):
    import nextPersonImg from "../../assets/team/{slug}.png";
    const portraitImages: Record<string, ImageMetadata> = {
      // ...existing...
      "{slug}": nextPersonImg,
    };
  3. When all 14 are real, simplify by removing the conditional and the initials fallback in both files; also delete the placeholder aside on /about/.

The two duplicate maps are intentional — they let each page tree-shake imports independently rather than forcing both pages to bundle every portrait.

Files affected

Created

  • src/assets/team/shane.png
  • src/assets/team/kelli.png
  • src/assets/team/ryan.png
  • src/content/devlog/2026-04-23-about-headshots-and-linkedin-fixes.md (this entry)

Modified

  • src/data/team.ts — Kelli + Ryan LinkedIn URLs corrected
  • src/pages/about/index.astro — Image import block, portraitImages map, conditional render in team grid, .team-portrait img CSS, aside copy
  • src/pages/about/[slug].astro — Image import block, portraitImages map, conditional render in bio hero, .portrait img CSS

Verification

  • Build verify: 20/20 PASS
  • Team grid per-person check (node parse of dist/about/index.html): Shane Leavitt, Kelli Black, Ryan Benson render as IMAGE; other 11 team members render as initials
  • LinkedIn URLs in compiled HTML: dist/about/kelli-black/index.htmllinkedin.com/in/kelli-j-black/; dist/about/ryan-benson/index.htmllinkedin.com/in/ryan-t-benson/
  • Optimized webp assets generated under dist/_astro/: shane.*.webp, kelli.*.webp, ryan.*.webp (multiple variants for retina + intrinsic sizes)
Feedback