Card 01
Department tabs over cards for each open position; without JS every department shows.
Last updated
---
/**
* Card 01: Department tabs over cards for each open position; without JS every department shows.
*
* @block careers
* @order 5
*/
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 { slug, type CareersContent, type Job } from '@/components/apogee/sections/_shared/careers.ts'
import Tabs from '@/components/apogee/Tabs/Tabs.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: 'Account Executive',
href: '#',
description: "Work with teams who are choosing their next design system.",
department: 'Sales',
location: 'London, UK',
type: 'Full-time',
salary: '90k – 120k',
badge: { label: 'Sales', color: 'warning' },
},
{
title: 'Content Marketing Lead',
href: '#',
description: "Write and plan the guides, notes and case studies people share.",
department: 'Marketing',
location: 'Remote',
type: 'Part-time',
salary: '60k – 80k',
badge: { label: 'Marketing', color: 'primary' },
},
{
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 {}
const {
eyebrow = "We're hiring!",
heading = 'Open positions',
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,
categories = [{ label: 'View all', value: 'view-all' }, ...[...new Set(items.flatMap((job) => job.department ?? []))].map((d) => ({ label: d, value: slug(d) }))],
} = Astro.props
---
<Section spacing="large" containerWidth="medium">
<SectionHeader eyebrow={eyebrow ?? undefined} heading={plain(heading)} description={description ?? undefined} align="center" class="mb-12" />
{categories ? (
<Tabs tabs={categories} variant="pills-raised" class="[&_.apogee-tabs-list]:mx-auto [&_.apogee-tabs-list]:w-fit [&_.apogee-tabs-list]:max-w-full">
{categories.map((c, i) => (
<ul slot={`panel-${c.value}`} class="mt-8 flex list-none flex-col gap-6 p-0">
{items.filter((job) => i === 0 || slug(job.department ?? '') === c.value).map((job) => <li><JobCard job={job} variant="card" /></li>)}
</ul>
))}
</Tabs>
) : (
<ul class="mt-8 flex list-none flex-col gap-6 p-0">
{items.map((job) => <li><JobCard job={job} variant="card" /></li>)}
</ul>
)}
</Section>
Show codeHide code
---
/**
* Card 01: Department tabs over cards for each open position; without JS every department shows.
*
* @block careers
* @order 5
*/
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 { slug, type CareersContent, type Job } from '@/components/apogee/sections/_shared/careers.ts'
import Tabs from '@/components/apogee/Tabs/Tabs.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: 'Account Executive',
href: '#',
description: "Work with teams who are choosing their next design system.",
department: 'Sales',
location: 'London, UK',
type: 'Full-time',
salary: '90k – 120k',
badge: { label: 'Sales', color: 'warning' },
},
{
title: 'Content Marketing Lead',
href: '#',
description: "Write and plan the guides, notes and case studies people share.",
department: 'Marketing',
location: 'Remote',
type: 'Part-time',
salary: '60k – 80k',
badge: { label: 'Marketing', color: 'primary' },
},
{
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 {}
const {
eyebrow = "We're hiring!",
heading = 'Open positions',
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,
categories = [{ label: 'View all', value: 'view-all' }, ...[...new Set(items.flatMap((job) => job.department ?? []))].map((d) => ({ label: d, value: slug(d) }))],
} = Astro.props
---
<Section spacing="large" containerWidth="medium">
<SectionHeader eyebrow={eyebrow ?? undefined} heading={plain(heading)} description={description ?? undefined} align="center" class="mb-12" />
{categories ? (
<Tabs tabs={categories} variant="pills-raised" class="[&_.apogee-tabs-list]:mx-auto [&_.apogee-tabs-list]:w-fit [&_.apogee-tabs-list]:max-w-full">
{categories.map((c, i) => (
<ul slot={`panel-${c.value}`} class="mt-8 flex list-none flex-col gap-6 p-0">
{items.filter((job) => i === 0 || slug(job.department ?? '') === c.value).map((job) => <li><JobCard job={job} variant="card" /></li>)}
</ul>
))}
</Tabs>
) : (
<ul class="mt-8 flex list-none flex-col gap-6 p-0">
{items.map((job) => <li><JobCard job={job} variant="card" /></li>)}
</ul>
)}
</Section>Add
npx @ava-ui/apogee add careers/card-01
This copies the block to src/components/apogee/blocks/careers/ with everything it uses: JobCard, Section, SectionHeader, Tabs. 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
h2by default, so the block slots into a page under itsh1; 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.
- The department tabs are a tab list with one panel each; without JS every panel shows, one after another.
Related
JobCardOne open position; title with a status badge, a short description and its location, type and salary, as a list row or a card.
TabsSwitch between panels of related content in the same space.
SelectA labeled, native <select> — full keyboard and mobile picker support for free — or a custom listbox with rich options over the same form control.