A centred heading and actions over eight people in grey cards.

Last updated

Open block
Show code
---
/**
 * Simple 04: A centred heading and actions over eight people in grey cards.
 *
 * @block team
 * @order 4
 */
import { AtSign, BriefcaseBusiness, Globe } from '@lucide/astro'
import Section from '@/components/apogee/Section/Section.astro'
import SectionHeader from '@/components/apogee/SectionHeader/SectionHeader.astro'
import Heading from '@/components/apogee/Heading/Heading.astro'
import Image from '@/components/apogee/Image/Image.astro'
import Link from '@/components/apogee/Link/Link.astro'
import Button from '@/components/apogee/Button/Button.astro'
import { plain } from '@/components/apogee/sections/_shared/spine.ts'
import type { TeamContent } from '@/components/apogee/sections/_shared/team.ts'

/** @composable */
interface Props extends TeamContent {}

const {
  eyebrow = "We're hiring!",
  heading = 'Meet our team',
  description = 'We hire diverse, passionate people and build a culture that lets everyone do their best work.',
  primaryAction = { label: 'Open positions', href: '#' },
  secondaryAction = { label: 'About us', href: '#' },
  items = [
    { name: 'Maya Okafor', role: 'Founder and CEO', avatar: 'https://picsum.photos/id/1027/192/192', bio: 'Started the company after ten years building tools for small teams.', links: [{ label: "Maya's social profile", href: '#' }, { label: "Maya's work history", href: '#' }, { label: "Maya's website", href: '#' }] },
    { name: 'Tomas Lindqvist', role: 'Engineering Manager', avatar: 'https://picsum.photos/id/856/192/192', bio: 'Keeps the team shipping and the pager quiet.', links: [{ label: "Tomas's social profile", href: '#' }, { label: "Tomas's work history", href: '#' }, { label: "Tomas's website", href: '#' }] },
    { name: 'Priya Raman', role: 'Product Manager', avatar: 'https://picsum.photos/id/832/192/192', bio: 'Turns customer calls into roadmaps people can read.', links: [{ label: "Priya's social profile", href: '#' }, { label: "Priya's work history", href: '#' }, { label: "Priya's website", href: '#' }] },
    { name: 'Jonah Weiss', role: 'Frontend Developer', avatar: 'https://picsum.photos/id/91/192/192', bio: 'Cares about fast pages and buttons that feel right.', links: [{ label: "Jonah's social profile", href: '#' }, { label: "Jonah's work history", href: '#' }, { label: "Jonah's website", href: '#' }] },
    { name: 'Lucía Ortega', role: 'Backend Developer', avatar: 'https://picsum.photos/id/64/192/192', bio: 'Builds the APIs and keeps the data honest.', links: [{ label: "Lucía's social profile", href: '#' }, { label: "Lucía's work history", href: '#' }, { label: "Lucía's website", href: '#' }] },
    { name: 'Samir Haddad', role: 'Product Designer', avatar: 'https://picsum.photos/id/375/192/192', bio: 'Sketches first, then sweats the last pixel.', links: [{ label: "Samir's social profile", href: '#' }, { label: "Samir's work history", href: '#' }, { label: "Samir's website", href: '#' }] },
    { name: 'Hana Kobayashi', role: 'UX Researcher', avatar: 'https://picsum.photos/id/823/192/192', bio: 'Talks to customers so the rest of us can listen.', links: [{ label: "Hana's social profile", href: '#' }, { label: "Hana's work history", href: '#' }, { label: "Hana's website", href: '#' }] },
    { name: 'Eli Brennan', role: 'Customer Success', avatar: 'https://picsum.photos/id/836/192/192', bio: 'Answers every question as if it were the first.', links: [{ label: "Eli's social profile", href: '#' }, { label: "Eli's work history", href: '#' }, { label: "Eli's website", href: '#' }] },
  ],
} = Astro.props
const icons = [AtSign, BriefcaseBusiness, Globe]
---

<Section spacing="large">
  <div class="flex flex-col gap-12 md:gap-16">
    <div class="flex flex-col gap-8 items-center">
      <SectionHeader eyebrow={eyebrow ?? undefined} heading={plain(heading)} description={description ?? undefined} size="large" align="center" />
      {(primaryAction || secondaryAction) && (
        <div class="flex flex-col gap-3 sm:flex-row-reverse sm:justify-center">
          {primaryAction && <Button href={primaryAction.href} size="large">{primaryAction.label}</Button>}
          {secondaryAction && <Button href={secondaryAction.href} size="large" variant="outline" color="neutral">{secondaryAction.label}</Button>}
        </div>
      )}
    </div>
    <ul class="grid list-none p-0 gap-x-8 gap-y-6 sm:grid-cols-2 md:gap-y-8 lg:grid-cols-3 xl:grid-cols-4">
      {items.map((p) => (
        <li><div class="flex h-full flex-col items-center gap-5 bg-neutral-faded p-6">{p.avatar && <Image src={p.avatar} alt="" width={192} height={192} fit="cover" radius="full" outline class="size-24" />}<div class="flex flex-col items-center text-center"><Heading as="h3" size="h6">{p.name}</Heading><p class="text-primary">{p.role}</p>{p.bio && <p class="mt-2 text-neutral-faded">{p.bio}</p>}{p.links && <ul class="flex list-none gap-4 p-0 mt-4 justify-center">{p.links.map((l, j) => { const Icon = icons[j % icons.length]!; return <li><Link href={l.href} color="neutral" underline="none" aria-label={l.label} class="text-neutral-faded hover:text-neutral"><Icon class="size-5" aria-hidden="true" /></Link></li> })}</ul>}</div></div></li>
      ))}
    </ul>
  </div>
</Section>

Add

npx @ava-ui/apogee add team/simple-04

This copies the block to src/components/apogee/blocks/team/ with everything it uses: Button, Heading, Image, Link, Section, SectionHeader. Every piece of copy is a prop, with the sample copy as its default. Pass yours, or null to hide a part; see copy is props.

Accessibility

  • The heading is an h2 by default, so the block slots into a page under its h1; item titles are one level down.
  • Photos that only decorate have an empty alt; give yours one when it carries meaning.