Careers Job Board
blockmarketingCareers page and job openings board with perks overview, live department & location filtering, keyword search, grouped department openings, and general application CTA.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/lit/careers-job-board.json$npx shadcn@latest add https://uipkge.dev/r/lit/careers-job-board.json$yarn dlx shadcn@latest add https://uipkge.dev/r/lit/careers-job-board.json$bunx shadcn@latest add https://uipkge.dev/r/lit/careers-job-board.jsonManifest:
https://uipkge.dev/r/lit/careers-job-board.jsonInstalls to:src/uip/blocks/careers-job-board/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
heading | string | Join our team | optional |
subtitle | string | Help us build the open-source UI infrastructure of tomorrow | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
JobOpeninginterface JobOpening {
id: string
title: string
department: 'Engineering' | 'Design' | 'Product' | 'Marketing'
location: 'Remote' | 'San Francisco, CA' | 'London, UK'
type: string
level: string
salary: string
description: string
}Perkinterface Perk {
icon: IconNode
iconName: string
title: string
description: string
}HTMLElementTagNameMapinterface HTMLElementTagNameMap {
'uip-careers-job-board': UipCareersJobBoard
}
}Files installed (1)
src/uip/blocks/careers-job-board/careers-job-board.ts17.9 kB
import { LitElement, css, html, nothing } from 'lit' import { ArrowRight, Briefcase, Coins, Globe, GraduationCap, HeartHandshake, MapPin, Search, SearchX, TrendingUp, type IconNode, } from 'lucide' import { cn } from '../../lib/utils' import { tailwind } from '../../lib/styles' import { ThemeController } from '../../lib/theme' import { icon } from '../../lib/icon' // Side-effect imports: register the composed elements for standalone use // (consumers who install only this block still get working primitives). import '../../components/badge/badge' import '../../components/button/button' import '../../components/input/input' import type { UipInput } from '../../components/input/input' import '../../components/select/select' import type { UipSelect } from '../../components/select/select' interface JobOpening { id: string title: string department: 'Engineering' | 'Design' | 'Product' | 'Marketing' location: 'Remote' | 'San Francisco, CA' | 'London, UK' type: string level: string salary: string description: string } interface Perk { icon: IconNode iconName: string title: string description: string } const PERKS: Perk[] = [ { icon: Globe, iconName: 'globe', title: 'Remote-first', description: 'Work from anywhere in the world with flexible hours.', }, { icon: GraduationCap, iconName: 'graduation-cap', title: '$5k learning budget', description: 'Annual stipend for courses, books, tools, and conferences.', }, { icon: HeartHandshake, iconName: 'heart-handshake', title: '100% health coverage', description: 'Comprehensive medical, dental, and vision for you and dependents.', }, { icon: TrendingUp, iconName: 'trending-up', title: 'Equity packages', description: 'Meaningful ownership stake with transparent vesting schedules.', }, ] const DEPARTMENTS = [ { id: 'all', label: 'All', count: 12 }, { id: 'Engineering', label: 'Engineering', count: 6 }, { id: 'Design', label: 'Design', count: 2 }, { id: 'Product', label: 'Product', count: 2 }, { id: 'Marketing', label: 'Marketing', count: 2 }, ] as const const LOCATIONS = [ { id: 'all', label: 'All Locations' }, { id: 'Remote', label: 'Remote' }, { id: 'San Francisco, CA', label: 'San Francisco, CA' }, { id: 'London, UK', label: 'London, UK' }, ] as const const JOBS: JobOpening[] = [ { id: 'eng-1', title: 'Staff Frontend Engineer', department: 'Engineering', location: 'Remote', type: 'Full-time', level: 'Staff', salary: '$190k - $240k · 0.3% - 0.6%', description: 'Lead the architecture of our high-performance UI components, compiler toolchains, and multi-framework design systems.', }, { id: 'eng-2', title: 'Senior Full-Stack Engineer', department: 'Engineering', location: 'San Francisco, CA', type: 'Full-time', level: 'Senior', salary: '$160k - $210k · 0.25% - 0.5%', description: 'Build end-to-end features spanning serverless edge runtimes, component registries, and developer tooling.', }, { id: 'eng-3', title: 'Systems & Performance Engineer', department: 'Engineering', location: 'Remote', type: 'Full-time', level: 'Senior', salary: '$170k - $220k · 0.25% - 0.5%', description: 'Optimize bundling pipelines, AST transformers, and static site generation latency across large workspaces.', }, { id: 'eng-4', title: 'Design Systems Engineer', department: 'Engineering', location: 'London, UK', type: 'Full-time', level: 'Mid-Senior', salary: '£110k - £140k · 0.2% - 0.4%', description: 'Bridge design and code by maintaining tokens, accessible headless primitives, and fluid interaction standards.', }, { id: 'eng-5', title: 'Developer Experience Engineer', department: 'Engineering', location: 'Remote', type: 'Full-time', level: 'Mid-Senior', salary: '$150k - $190k · 0.2% - 0.4%', description: 'Craft exceptional CLI workflows, interactive documentation playgrounds, and starter template engines.', }, { id: 'eng-6', title: 'QA & Automation Engineer', department: 'Engineering', location: 'London, UK', type: 'Full-time', level: 'Mid-Senior', salary: '£95k - £125k · 0.15% - 0.3%', description: 'Expand cross-browser visual regression testing suites, accessibility audits, and automated release checks.', }, { id: 'des-1', title: 'Senior Product Designer', department: 'Design', location: 'San Francisco, CA', type: 'Full-time', level: 'Senior', salary: '$155k - $195k · 0.25% - 0.45%', description: 'Define micro-interactions, responsive dashboard workflows, and layout ergonomics across our block catalog.', }, { id: 'des-2', title: 'Brand & Motion Designer', department: 'Design', location: 'Remote', type: 'Full-time', level: 'Mid-Senior', salary: '$140k - $180k · 0.2% - 0.35%', description: 'Create fluid motion principles, brand identity assets, and polished launch visuals for major releases.', }, { id: 'prd-1', title: 'Staff Technical Product Manager', department: 'Product', location: 'Remote', type: 'Full-time', level: 'Staff', salary: '$180k - $230k · 0.3% - 0.5%', description: 'Drive product roadmap for developer workflows, unbundled registry distribution, and monorepo orchestration.', }, { id: 'prd-2', title: 'Product Operations Lead', department: 'Product', location: 'San Francisco, CA', type: 'Full-time', level: 'Senior', salary: '$145k - $185k · 0.2% - 0.35%', description: 'Manage release triage, community feature requests, and cross-team execution cadences.', }, { id: 'mkt-1', title: 'Developer Advocate / Technical Writer', department: 'Marketing', location: 'Remote', type: 'Full-time', level: 'Mid-Senior', salary: '$140k - $180k · 0.2% - 0.4%', description: 'Author technical deep-dives, create interactive video tutorials, and engage with the open-source community.', }, { id: 'mkt-2', title: 'Growth Marketing Lead', department: 'Marketing', location: 'London, UK', type: 'Full-time', level: 'Senior', salary: '£105k - £135k · 0.2% - 0.4%', description: 'Lead developer-focused growth experiments, SEO architecture, launch campaigns, and analytics instrumentation.', }, ] /** * <uip-careers-job-board> — the registry CareersJobBoard block as a web component. * * Class strings are React's CareersJobBoard verbatim. `heading` is React's * `title` (`title` is taken by HTMLElement). React state (useState) becomes * @state fields. The search icon moves from React's absolute overlay into * <uip-input>'s prefix slot (same visual result, no overlay positioning to * maintain), so the input keeps `text-sm` and drops the overlay's `pl-9`. * The location Select is a <uip-select> reading light-DOM <option> children. */ export class UipCareersJobBoard extends LitElement { static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true } // :host display can't be a utility class (the host has no template of its own). static styles = [tailwind, css`:host { display: block; }`] static properties = { heading: {}, subtitle: {}, selectedDepartment: { state: true }, selectedLocation: { state: true }, searchQuery: { state: true }, } heading = 'Join our team' subtitle = 'Help us build the open-source UI infrastructure of tomorrow' private selectedDepartment = 'all' private selectedLocation = 'all' private searchQuery = '' constructor() { super() new ThemeController(this) } connectedCallback() { super.connectedCallback() this.setAttribute('data-uipkge', '') this.setAttribute('data-slot', 'careers-job-board') } private get filteredJobs() { const q = this.searchQuery.trim().toLowerCase() return JOBS.filter((job) => { const matchDept = this.selectedDepartment === 'all' || job.department === this.selectedDepartment const matchLoc = this.selectedLocation === 'all' || job.location === this.selectedLocation if (!matchDept || !matchLoc) return false if (!q) return true return ( job.title.toLowerCase().includes(q) || job.description.toLowerCase().includes(q) || job.department.toLowerCase().includes(q) || job.level.toLowerCase().includes(q) || job.location.toLowerCase().includes(q) || job.type.toLowerCase().includes(q) || job.salary.toLowerCase().includes(q) ) }) } private get departmentGroups() { const allDepts = ['Engineering', 'Design', 'Product', 'Marketing'] as const const targetDepartments = this.selectedDepartment === 'all' ? allDepts : allDepts.filter((dept) => dept === this.selectedDepartment) return targetDepartments .map((dept) => ({ name: dept, jobs: this.filteredJobs.filter((job) => job.department === dept), })) .filter((group) => group.jobs.length > 0) } private get isFiltered() { return this.selectedDepartment !== 'all' || this.selectedLocation !== 'all' || this.searchQuery.trim() !== '' } private resetFilters() { this.selectedDepartment = 'all' this.selectedLocation = 'all' this.searchQuery = '' } render() { const groups = this.departmentGroups return html` <section data-slot="careers-job-board" class=${cn('w-full space-y-12 py-6')}> <div class="mx-auto max-w-3xl space-y-4 text-center"> <uip-badge variant="secondary" class="gap-1.5 px-3 py-1 text-xs font-medium"> ${icon(Briefcase, 'briefcase', 'text-primary size-3.5')} We're hiring </uip-badge> <h1 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">${this.heading}</h1> <p class="text-muted-foreground text-base leading-relaxed sm:text-lg">${this.subtitle}</p> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> ${PERKS.map( (perk) => html`<div class="bg-card border-border flex flex-col gap-3 rounded-xl border p-4 shadow-xs"> <div class="bg-primary/10 text-primary flex size-9 items-center justify-center rounded-lg"> ${icon(perk.icon, perk.iconName, 'size-4.5')} </div> <div class="space-y-1"> <p class="text-foreground text-sm font-semibold">${perk.title}</p> <p class="text-muted-foreground text-xs leading-relaxed">${perk.description}</p> </div> </div>`, )} </div> <div class="bg-card border-border space-y-4 rounded-xl border p-4 shadow-xs"> <div class="flex flex-wrap items-center gap-1.5"> ${DEPARTMENTS.map( (dept) => html`<uip-button variant=${this.selectedDepartment === dept.id ? 'default' : 'outline'} size="sm" class="h-8 rounded-lg text-xs" @click=${() => { this.selectedDepartment = dept.id }} > ${dept.label} <span class=${cn( 'ml-1.5 rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums', this.selectedDepartment === dept.id ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-muted text-muted-foreground', )} > ${dept.count} </span> </uip-button>`, )} </div> <div class="flex flex-col gap-3 sm:flex-row sm:items-center"> <uip-input class="flex-1 text-sm" .value=${this.searchQuery} placeholder="Search roles, technologies, or keywords..." aria-label="Search open positions" @input=${(e: Event) => { this.searchQuery = (e.target as UipInput).value }} > <span slot="prefix">${icon(Search, 'search', 'size-4')}</span> </uip-input> <div class="w-full shrink-0 sm:w-56"> <uip-select class="w-full text-xs sm:text-sm" placeholder="All Locations" aria-label="Filter by location" .value=${this.selectedLocation} @change=${(e: Event) => { this.selectedLocation = (e.target as UipSelect).value }} > ${LOCATIONS.map((loc) => html`<option value=${loc.id}>${loc.label}</option>`)} </uip-select> </div> ${this.isFiltered ? html`<uip-button variant="ghost" size="sm" class="text-muted-foreground hover:text-foreground text-xs" @click=${this.resetFilters} > Reset filters </uip-button>` : nothing} </div> </div> ${groups.length > 0 ? html`<div class="space-y-10"> ${groups.map( (group) => html`<div class="space-y-4"> <div class="border-border flex items-center justify-between border-b pb-3"> <div class="flex items-center gap-2.5"> <h2 class="text-foreground text-lg font-semibold tracking-tight">${group.name}</h2> <uip-badge variant="secondary" class="text-xs font-normal"> ${group.jobs.length} ${group.jobs.length === 1 ? 'opening' : 'openings'} </uip-badge> </div> </div> <div class="space-y-3"> ${group.jobs.map( (job) => html`<article class="group bg-card border-border hover:border-primary/40 relative flex flex-col justify-between gap-4 rounded-xl border p-5 shadow-xs transition-colors duration-150 hover:shadow-sm md:flex-row md:items-center" > <div class="min-w-0 flex-1 space-y-2"> <div class="flex flex-wrap items-center gap-2"> <h3 class="group-hover:text-primary text-foreground text-base font-semibold tracking-tight transition-colors sm:text-lg" > ${job.title} </h3> <uip-badge variant="outline" class="text-xs">${job.level}</uip-badge> </div> <p class="text-muted-foreground line-clamp-2 text-sm leading-relaxed">${job.description}</p> <div class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 pt-1 text-xs"> <span class="inline-flex items-center gap-1.5"> ${icon(MapPin, 'map-pin', 'text-muted-foreground size-3.5 shrink-0')} ${job.location} </span> <span class="inline-flex items-center gap-1.5"> ${icon(Briefcase, 'briefcase', 'text-muted-foreground size-3.5 shrink-0')} ${job.type} </span> <span class="text-foreground inline-flex items-center gap-1.5 font-medium"> ${icon(Coins, 'coins', 'text-muted-foreground size-3.5 shrink-0')} ${job.salary} </span> </div> </div> <div class="shrink-0 pt-2 md:pt-0"> <uip-button class="w-full gap-1.5 md:w-auto" size="sm"> Apply now ${icon(ArrowRight, 'arrow-right', 'size-3.5 transition-transform group-hover:translate-x-0.5')} </uip-button> </div> </article>`, )} </div> </div>`, )} </div>` : html`<div class="border-border flex flex-col items-center justify-center rounded-xl border border-dashed p-12 text-center" > <div class="bg-muted text-muted-foreground mb-4 flex size-12 items-center justify-center rounded-full"> ${icon(SearchX, 'search-x', 'size-6')} </div> <h3 class="text-foreground text-base font-semibold">No matching positions found</h3> <p class="text-muted-foreground mt-1 mb-4 max-w-sm text-sm leading-normal"> We could not find any roles matching your current search or filters. Try adjusting your criteria. </p> <uip-button variant="outline" size="sm" @click=${this.resetFilters}>Clear all filters</uip-button> </div>`} <div class="bg-card border-border flex flex-col items-start justify-between gap-6 rounded-2xl border p-6 shadow-xs sm:flex-row sm:items-center sm:p-8" > <div class="max-w-xl space-y-1.5"> <h3 class="text-foreground text-lg font-semibold tracking-tight">Don't see your role?</h3> <p class="text-muted-foreground text-sm leading-relaxed"> We are always looking for curious, high-craft engineers, designers, and thinkers. Send us an open application and tell us what you'd love to build with us. </p> </div> <uip-button variant="outline" class="shrink-0 gap-2"> Send open application ${icon(ArrowRight, 'arrow-right', 'size-4')} </uip-button> </div> </section> ` } } customElements.get('uip-careers-job-board') || customElements.define('uip-careers-job-board', UipCareersJobBoard) declare global { interface HTMLElementTagNameMap { 'uip-careers-job-board': UipCareersJobBoard } }
Raw manifest:https://uipkge.dev/r/lit/careers-job-board.json