A location menu over open positions grouped by department, each group with a short intro.

Last updated

Open block
Show code
---
/**
 * Card 04: A location menu over open positions grouped by department, each group with a short intro.
 *
 * @block careers
 * @order 8
 */
import Section from '@/components/apogee/Section/Section.astro'
import SectionHeader from '@/components/apogee/SectionHeader/SectionHeader.astro'
import JobCard from '@/components/apogee/JobCard/JobCard.astro'
import { plain } from '@/components/apogee/sections/_shared/spine.ts'
import { type CareersContent, type Job } from '@/components/apogee/sections/_shared/careers.ts'
import Select from '@/components/apogee/Select/Select.astro'
import Heading from '@/components/apogee/Heading/Heading.astro'

const demoJobs: Job[] = [
  {
    title: 'Product Designer',
    href: '#',
    description: "We're looking for a mid-level product designer to join our team.",
    department: 'Design',
    location: 'Remote',
    type: 'Full-time',
    salary: '80k – 100k',
    badge: { label: 'Design', color: 'primary' },
  },
  {
    title: 'Engineering Manager',
    href: '#',
    description: "We're looking for an experienced engineering manager to lead a product team.",
    department: 'Software Development',
    location: 'Remote',
    type: 'Full-time',
    salary: '120k – 150k',
    badge: { label: 'Software', color: 'neutral' },
  },
  {
    title: 'Customer Success Manager',
    href: '#',
    description: "Help our customers get the most out of the product, from first call to renewal.",
    department: 'Customer Success',
    location: 'Lisbon, Portugal',
    type: 'Full-time',
    salary: '70k – 90k',
    badge: { label: 'Customer success', color: 'positive' },
  },
  {
    title: 'UX Researcher',
    href: '#',
    description: "Talk to the people who use what we build and bring what you learn back.",
    department: 'Design',
    location: 'Remote',
    type: 'Contract',
    salary: '75k – 95k',
    badge: { label: 'Design', color: 'primary' },
  },
]

/** @composable */
interface Props extends CareersContent {
  /** A line under each department heading, keyed by department. @shape { [department: string]: string } @category content */
  intros?: Record<string, string> | null
}

const {
  eyebrow = "We're hiring!",
  heading = 'Join our team',
  description = 'Our philosophy is simple: hire a team of diverse, passionate people and foster a culture that empowers you to do your best work.',
  items = demoJobs,
  intros = { Design: 'Open positions in our design team.', 'Software Development': 'Open positions in our software team.', 'Customer Success': 'Open positions in our customer success team.' },
} = Astro.props
// Jobs grouped by department, in order of first appearance.
const groups = [...new Set(items.flatMap((job) => job.department ?? []))].map((department) => ({ department, intro: intros?.[department], jobs: items.filter((job) => job.department === department) }))

---

<Section spacing="large">
  <div class="grid gap-12 lg:grid-cols-[minmax(0,1fr)_minmax(0,2fr)] lg:gap-16">
    <div class="flex flex-col gap-8">
      <SectionHeader eyebrow={eyebrow ?? undefined} heading={plain(heading)} description={description ?? undefined} class="" />
      <Select label="Location" name="location" class="sm:w-56">
        <option selected>Worldwide</option>
        <option>Europe</option>
        <option>North America</option>
        <option>Asia</option>
        <option>Oceania</option>
      </Select>
    </div>
    <ul class="flex list-none flex-col gap-12 p-0">
      {groups.map((g) => (
        <li>
          <Heading as="h3" size="h5">{g.department}</Heading>
          {g.intro && <p class="mt-1 text-neutral-faded">{g.intro}</p>}
          <ul class="mt-5 flex list-none flex-col gap-4 p-0">
            {g.jobs.map((job) => <li><JobCard job={{ ...job, department: undefined }} variant="card" headingLevel={4} /></li>)}
          </ul>
        </li>
      ))}
    </ul>
  </div>
</Section>

Add

npx @ava-ui/apogee add careers/card-04

This copies the block to src/components/apogee/blocks/careers/ with everything it uses: Heading, JobCard, Section, SectionHeader, Select. 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.
  • Each position is one link: the title stretches over the card. Its details are a description list (Location, Type, Salary) for screen readers.
  • Menus are native selects with a label or an aria-label.