1 звезда на GitHub = 1 новый компонент.

Поставить звезду
Команда и авторы

Careers Openings

Вакансии по отделам с локацией, типом занятости, удалёнкой и списком преимуществ.

careersjobshiringteam
Живое превью

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

Использование

Скопируйте и вставьте этот код в ваш React-компонент:

tsx
import { CareersOpenings } from 'react-component-kit-ui/sections';

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

Ручная установка

Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/sections/CareersOpenings.tsx + CareersOpenings.css:

CareersOpenings.tsx
'use client';

import './CareersOpenings.css';

interface Job { title: string; department: string; location: string; type: string; remote?: boolean; href: string }

/**
 * 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.
 */
export interface CareersOpeningsProps {
  title?: string;
  subtitle?: string;
  jobs?: Job[];
  perks?: string[];
  cta?: { label: string; href: string };
  variant?: 'list' | 'cards' | 'minimal';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function CareersOpenings({
  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 = '',
}: CareersOpeningsProps) {
  const id = `co-${Math.random().toString(36).slice(2, 8)}`;
  const departments = [...new Set(jobs.map((j) => j.department))];
  return (
    <>
      <div className="cq-careers-openings-cq" data-rk-6d35fa="">
      <section className={`co co--${variant} co--${size} ${className}`} aria-labelledby={id} data-rk-6d35fa="">
        <div className="co__inner" data-rk-6d35fa="">
          <header className="co__head" data-rk-6d35fa="">
            <div data-rk-6d35fa="">
              <h2 id={id} className="co__title" data-rk-6d35fa="">{title}</h2>
              <p className="co__subtitle" data-rk-6d35fa="">{subtitle}</p>
            </div>
            <span className="co__count" data-rk-6d35fa="">{jobs.length} open roles</span>
          </header>
      
          <ul className="co__perks" aria-label="Perks" data-rk-6d35fa="">
            {perks.map((p, __i: number) => <li key={__i} data-rk-6d35fa="">{p}</li>)}
          </ul>
      
          {departments.map((d, __i: number) => (
            <div key={__i} className="co__dept" data-rk-6d35fa="">
              <h3 className="co__dept-title" data-rk-6d35fa="">{d}</h3>
              <ul className="co__jobs" data-rk-6d35fa="">
                {jobs.filter((j) => j.department === d).map((j, __i: number) => (
                  <li key={__i} data-rk-6d35fa="">
                    <a className="co__job" href={j.href} data-rk-6d35fa="">
                      <span className="co__job-title" data-rk-6d35fa="">{j.title}</span>
                      <span className="co__meta" data-rk-6d35fa="">
                        <span className="co__tag" data-rk-6d35fa="">
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true" data-rk-6d35fa=""><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z" data-rk-6d35fa="" /><circle cx="12" cy="10" r="3" data-rk-6d35fa="" /></svg>
                          {j.location}
                        </span>
                        <span className="co__tag" data-rk-6d35fa="">{j.type}</span>
                        {j.remote && <span className="co__tag co__tag--remote" data-rk-6d35fa="">Remote</span>}
                      </span>
                      <svg className="co__arrow" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true" data-rk-6d35fa=""><path d="M7 17 17 7M8 7h9v9" data-rk-6d35fa="" /></svg>
                    </a>
                  </li>
                ))}
              </ul>
            </div>
          ))}
      
          {cta && (
            <p className="co__cta" data-rk-6d35fa="">Don’t see your role? <a href={cta.href} data-rk-6d35fa="">{cta.label}</a></p>
          )}
        </div>
      </section>
      </div>
    </>
  );
}

export default CareersOpenings;
CareersOpenings.css
/* CareersOpenings — ported from astro-component-kit (sections/CareersOpenings.astro) */

.cq-careers-openings-cq:where([data-rk-6d35fa]) { container-type: inline-size; }
.co:where([data-rk-6d35fa]) {
  --co-py: 5.5rem;
  padding: var(--co-py) var(--sp-6, 1.5rem);
  background: var(--c-bg, #050505);
}
.co--sm:where([data-rk-6d35fa]) { --co-py: 3.5rem; }
.co--lg:where([data-rk-6d35fa]) { --co-py: 7.5rem; }
.co__inner:where([data-rk-6d35fa]) { max-width: 960px; margin: 0 auto; }
.co__head:where([data-rk-6d35fa]) { display: flex; justify-content: space-between; align-items: flex-end; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
.co__title:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) { margin: 0; font-size: 1.0625rem; color: var(--c-text-2, #a3a3a3); }
.co__count:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) { list-style: none; margin: 0 0 2.5rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.co__perks:where([data-rk-6d35fa]) li:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) + .co__dept:where([data-rk-6d35fa]) { margin-top: 2rem; }
.co__dept-title:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.co--list:where([data-rk-6d35fa]) .co__jobs:where([data-rk-6d35fa]) { gap: 0; border-top: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
.co__job:where([data-rk-6d35fa]) {
  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:where([data-rk-6d35fa]) .co__job:where([data-rk-6d35fa]) { border-bottom: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.07); }
.co--cards:where([data-rk-6d35fa]) .co__job:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) .co__job:where([data-rk-6d35fa]) { padding-inline: 0; }
.co__job:hover:where([data-rk-6d35fa]) { background: rgb(var(--ink-rgb, 255 255 255) / 0.035); }
.co__job:focus-visible:where([data-rk-6d35fa]) { outline: 2px solid var(--c-primary-light, #d4a017); outline-offset: 2px; border-radius: 10px; }
.co__job-title:where([data-rk-6d35fa]) { font-size: 1.0625rem; font-weight: 600; color: var(--c-text-1, #fff); }
.co__meta:where([data-rk-6d35fa]) { display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: flex-end; }
.co__tag:where([data-rk-6d35fa]) { 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:where([data-rk-6d35fa]) { background: rgba(57, 135, 229, 0.14); color: #a9cdf7; }
.co__arrow:where([data-rk-6d35fa]) { color: var(--c-text-3, #8a8a8a); transition: transform 0.2s ease, color 0.2s ease; }
.co__job:hover:where([data-rk-6d35fa]) .co__arrow:where([data-rk-6d35fa]) { transform: translate(2px, -2px); color: var(--c-text-1, #fff); }
.co__cta:where([data-rk-6d35fa]) { margin: 2.5rem 0 0; text-align: center; color: var(--c-text-2, #a3a3a3); }
.co__cta:where([data-rk-6d35fa]) a:where([data-rk-6d35fa]) { color: var(--c-primary-light, #d4a017); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
@container (max-width: 640px) {
  .co__job:where([data-rk-6d35fa]) { grid-template-columns: 1fr 24px; }
  .co__meta:where([data-rk-6d35fa]) { grid-column: 1; grid-row: 2; justify-content: flex-start; }
  .co__arrow:where([data-rk-6d35fa]) { grid-row: 1 / span 2; grid-column: 2; }
}