1 GitHub star = 1 new component.

Star the repo
Team & People

Careers Openings

Job listings grouped by department with location, type and remote badges plus a perks strip.

careersjobshiringteam
Live Preview

Join the team

We’re a small, remote-first crew building tools for the open web.

4 open roles
  • Remote-first
  • 4-day work week
  • Learning budget
  • Equity for everyone

Don’t see your role? Send an open application

Usage

Copy and paste this code into your Astro page:

astro
---
import { CareersOpenings } from 'astro-component-kit/sections';
---

<CareersOpenings
  jobs={[
    { title: 'Frontend Engineer', department: 'Engineering', location: 'Europe', type: 'Full-time', remote: true, href: '/jobs/fe' },
  ]}
/>
--- import { CareersOpenings } from 'astro-component-kit/sections'; --- <CareersOpenings jobs={[ { title: 'Frontend Engineer', department: 'Engineering', location: 'Europe', type: 'Full-time', remote: true, href: '/jobs/fe' }, ]} />

Manual Installation

If you are not using the npm package, create a new file and paste the following code: src/components/sections/CareersOpenings.astro:

astro
---
/**
 * CareersOpenings — Job listings section grouped by department, with location,
 * type and remote badges, plus perks strip and a general-application CTA.
 *
 * @param {string} title - Section heading.
 * @param {string} subtitle - Section description.
 * @param {Array<{title: string, department: string, location: string, type: string, remote?: boolean, href: string}>} jobs - Open roles.
 * @param {string[]} perks - Short perk labels.
 * @param {{label: string, href: string}} cta - General application link.
 * @param {string} variant - Row style (list|cards|minimal).
 * @param {string} size - Vertical rhythm (sm|md|lg).
 * @param {string} className - Extra CSS class.
 */
interface Job { title: string; department: string; location: string; type: string; remote?: boolean; href: string }
interface Props {
  title?: string;
  subtitle?: string;
  jobs?: Job[];
  perks?: string[];
  cta?: { label: string; href: string };
  variant?: 'list' | 'cards' | 'minimal';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

const {
  title = 'Join the team',
  subtitle = 'We’re a small, remote-first crew building tools for the open web.',
  jobs = [
    { title: 'Senior Frontend Engineer', department: 'Engineering', location: 'Europe', type: 'Full-time', remote: true, href: '#' },
    { title: 'Design Systems Engineer', department: 'Engineering', location: 'Americas', type: 'Full-time', remote: true, href: '#' },
    { title: 'Product Designer', department: 'Design', location: 'Barcelona', type: 'Full-time', href: '#' },
    { title: 'Developer Advocate', department: 'Marketing', location: 'Anywhere', type: 'Contract', remote: true, href: '#' },
  ],
  perks = ['Remote-first', '4-day work week', 'Learning budget', 'Equity for everyone'],
  cta = { label: 'Send an open application', href: '#' },
  variant = 'list',
  size = 'md',
  className = '',
} = Astro.props;

const id = `co-${Math.random().toString(36).slice(2, 8)}`;
const departments = [...new Set(jobs.map((j) => j.department))];
---

<div class="cq-careers-openings-cq">
<section class={`co co--${variant} co--${size} ${className}`} aria-labelledby={id}>
  <div class="co__inner">
    <header class="co__head">
      <div>
        <h2 id={id} class="co__title">{title}</h2>
        <p class="co__subtitle">{subtitle}</p>
      </div>
      <span class="co__count">{jobs.length} open roles</span>
    </header>

    <ul class="co__perks" aria-label="Perks">
      {perks.map((p) => <li>{p}</li>)}
    </ul>

    {departments.map((d) => (
      <div class="co__dept">
        <h3 class="co__dept-title">{d}</h3>
        <ul class="co__jobs">
          {jobs.filter((j) => j.department === d).map((j) => (
            <li>
              <a class="co__job" href={j.href}>
                <span class="co__job-title">{j.title}</span>
                <span class="co__meta">
                  <span class="co__tag">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z" /><circle cx="12" cy="10" r="3" /></svg>
                    {j.location}
                  </span>
                  <span class="co__tag">{j.type}</span>
                  {j.remote && <span class="co__tag co__tag--remote">Remote</span>}
                </span>
                <svg class="co__arrow" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M8 7h9v9" /></svg>
              </a>
            </li>
          ))}
        </ul>
      </div>
    ))}

    {cta && (
      <p class="co__cta">Don’t see your role? <a href={cta.href}>{cta.label}</a></p>
    )}
  </div>
</section>
</div>
<style>
  .cq-careers-openings-cq { container-type: inline-size; }
  .co {
    --co-py: 5.5rem;
    padding: var(--co-py) var(--sp-6, 1.5rem);
    background: var(--c-bg, #050505);
  }
  .co--sm { --co-py: 3.5rem; }
  .co--lg { --co-py: 7.5rem; }
  .co__inner { max-width: 960px; margin: 0 auto; }
  .co__head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
  .co__title { margin: 0 0 0.5rem; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); }
  .co__subtitle { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); }
  .co__count { padding: 0.35rem 0.85rem; border-radius: 999px; background: rgba(52, 195, 143, 0.12); color: #7ee2b8; font-size: 0.8125rem; font-weight: 650; }
  .co__perks { list-style: none; margin: 0 0 2.5rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .co__perks li { padding: 0.4rem 0.85rem; border-radius: 999px; border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1); font-size: 0.8125rem; color: var(--c-text-2, #a3a3a3); }
  .co__dept + .co__dept { margin-top: 2rem; }
  .co__dept-title { margin: 0 0 0.75rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-primary-light, #d4a017); }
  .co__jobs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
  .co--list .co__jobs { gap: 0; border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
  .co__job {
    display: grid;
    grid-template-columns: 1fr auto 24px;
    align-items: center;
    gap: 1rem;
    min-height: 64px;
    padding: 0.85rem 1rem;
    color: inherit;
    text-decoration: none;
    transition: background 0.2s ease, border-color 0.2s ease;
  }
  .co--list .co__job { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
  .co--cards .co__job { border-radius: var(--r-md, 14px); background: rgb(var(--ink-rgb, 255 255 255) / 0.025); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07)); }
  .co--minimal .co__job { padding-inline: 0; }
  .co__job:hover { background: rgb(var(--ink-rgb, 255 255 255) / 0.035); }
  .co__job:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 10px; }
  .co__job-title { font-size: 1.0625rem; font-weight: 600; color: var(--c-text-1, #fff); }
  .co__meta { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: flex-end; }
  .co__tag { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.2rem 0.6rem; border-radius: 999px; background: rgb(var(--ink-rgb, 255 255 255) / 0.05); font-size: 0.75rem; color: var(--c-text-2, #a3a3a3); }
  .co__tag--remote { background: rgba(57, 135, 229, 0.14); color: #a9cdf7; }
  .co__arrow { color: var(--c-text-3, #8a8a8a); transition: transform 0.2s ease, color 0.2s ease; }
  .co__job:hover .co__arrow { transform: translate(2px, -2px); color: var(--c-text-1, #fff); }
  .co__cta { margin: 2.5rem 0 0; text-align: center; color: var(--c-text-2, #a3a3a3); }
  .co__cta a { color: var(--c-primary-light, #d4a017); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
  @container (max-width: 640px) {
    .co__job { grid-template-columns: 1fr 24px; }
    .co__meta { grid-column: 1; grid-row: 2; justify-content: flex-start; }
    .co__arrow { grid-row: 1 / span 2; grid-column: 2; }
  }
</style>
--- /** * CareersOpenings — Job listings section grouped by department, with location, * type and remote badges, plus perks strip and a general-application CTA. * * @param {string} title - Section heading. * @param {string} subtitle - Section description. * @param {Array<{title: string, department: string, location: string, type: string, remote?: boolean, href: string}>} jobs - Open roles. * @param {string[]} perks - Short perk labels. * @param {{label: string, href: string}} cta - General application link. * @param {string} variant - Row style (list|cards|minimal). * @param {string} size - Vertical rhythm (sm|md|lg). * @param {string} className - Extra CSS class. */ interface Job { title: string; department: string; location: string; type: string; remote?: boolean; href: string } interface Props { title?: string; subtitle?: string; jobs?: Job[]; perks?: string[]; cta?: { label: string; href: string }; variant?: 'list' | 'cards' | 'minimal'; size?: 'sm' | 'md' | 'lg'; className?: string; } const { title = 'Join the team', subtitle = 'We’re a small, remote-first crew building tools for the open web.', jobs = [ { title: 'Senior Frontend Engineer', department: 'Engineering', location: 'Europe', type: 'Full-time', remote: true, href: '#' }, { title: 'Design Systems Engineer', department: 'Engineering', location: 'Americas', type: 'Full-time', remote: true, href: '#' }, { title: 'Product Designer', department: 'Design', location: 'Barcelona', type: 'Full-time', href: '#' }, { title: 'Developer Advocate', department: 'Marketing', location: 'Anywhere', type: 'Contract', remote: true, href: '#' }, ], perks = ['Remote-first', '4-day work week', 'Learning budget', 'Equity for everyone'], cta = { label: 'Send an open application', href: '#' }, variant = 'list', size = 'md', className = '', } = Astro.props; const id = `co-${Math.random().toString(36).slice(2, 8)}`; const departments = [...new Set(jobs.map((j) => j.department))]; --- <div class="cq-careers-openings-cq"> <section class={`co co--${variant} co--${size} ${className}`} aria-labelledby={id}> <div class="co__inner"> <header class="co__head"> <div> <h2 id={id} class="co__title">{title}</h2> <p class="co__subtitle">{subtitle}</p> </div> <span class="co__count">{jobs.length} open roles</span> </header> <ul class="co__perks" aria-label="Perks"> {perks.map((p) => <li>{p}</li>)} </ul> {departments.map((d) => ( <div class="co__dept"> <h3 class="co__dept-title">{d}</h3> <ul class="co__jobs"> {jobs.filter((j) => j.department === d).map((j) => ( <li> <a class="co__job" href={j.href}> <span class="co__job-title">{j.title}</span> <span class="co__meta"> <span class="co__tag"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z" /><circle cx="12" cy="10" r="3" /></svg> {j.location} </span> <span class="co__tag">{j.type}</span> {j.remote && <span class="co__tag co__tag--remote">Remote</span>} </span> <svg class="co__arrow" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M7 17 17 7M8 7h9v9" /></svg> </a> </li> ))} </ul> </div> ))} {cta && ( <p class="co__cta">Don’t see your role? <a href={cta.href}>{cta.label}</a></p> )} </div> </section> </div> <style> .cq-careers-openings-cq { container-type: inline-size; } .co { --co-py: 5.5rem; padding: var(--co-py) var(--sp-6, 1.5rem); background: var(--c-bg, #050505); } .co--sm { --co-py: 3.5rem; } .co--lg { --co-py: 7.5rem; } .co__inner { max-width: 960px; margin: 0 auto; } .co__head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; } .co__title { margin: 0 0 0.5rem; font-size: clamp(1.9rem, 4vw, 2.75rem); font-weight: 800; letter-spacing: -0.03em; color: var(--c-text-1, #fff); } .co__subtitle { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); } .co__count { padding: 0.35rem 0.85rem; border-radius: 999px; background: rgba(52, 195, 143, 0.12); color: #7ee2b8; font-size: 0.8125rem; font-weight: 650; } .co__perks { list-style: none; margin: 0 0 2.5rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; } .co__perks li { padding: 0.4rem 0.85rem; border-radius: 999px; border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.1); font-size: 0.8125rem; color: var(--c-text-2, #a3a3a3); } .co__dept + .co__dept { margin-top: 2rem; } .co__dept-title { margin: 0 0 0.75rem; font-size: 0.8125rem; font-weight: 650; letter-spacing: 0.08em; text-transform: uppercase; color: var(--c-primary-light, #d4a017); } .co__jobs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; } .co--list .co__jobs { gap: 0; border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); } .co__job { display: grid; grid-template-columns: 1fr auto 24px; align-items: center; gap: 1rem; min-height: 64px; padding: 0.85rem 1rem; color: inherit; text-decoration: none; transition: background 0.2s ease, border-color 0.2s ease; } .co--list .co__job { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); } .co--cards .co__job { border-radius: var(--r-md, 14px); background: rgb(var(--ink-rgb, 255 255 255) / 0.025); border: 1px solid var(--c-border, rgb(var(--ink-rgb, 255 255 255) / 0.07)); } .co--minimal .co__job { padding-inline: 0; } .co__job:hover { background: rgb(var(--ink-rgb, 255 255 255) / 0.035); } .co__job:focus-visible { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 10px; } .co__job-title { font-size: 1.0625rem; font-weight: 600; color: var(--c-text-1, #fff); } .co__meta { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: flex-end; } .co__tag { display: inline-flex; align-items: center; gap: 0.3rem; padding: 0.2rem 0.6rem; border-radius: 999px; background: rgb(var(--ink-rgb, 255 255 255) / 0.05); font-size: 0.75rem; color: var(--c-text-2, #a3a3a3); } .co__tag--remote { background: rgba(57, 135, 229, 0.14); color: #a9cdf7; } .co__arrow { color: var(--c-text-3, #8a8a8a); transition: transform 0.2s ease, color 0.2s ease; } .co__job:hover .co__arrow { transform: translate(2px, -2px); color: var(--c-text-1, #fff); } .co__cta { margin: 2.5rem 0 0; text-align: center; color: var(--c-text-2, #a3a3a3); } .co__cta a { color: var(--c-primary-light, #d4a017); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; } @container (max-width: 640px) { .co__job { grid-template-columns: 1fr 24px; } .co__meta { grid-column: 1; grid-row: 2; justify-content: flex-start; } .co__arrow { grid-row: 1 / span 2; grid-column: 2; } } </style>