Fazekit

Code

"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
import { careersContent, type CareersContent, type Role } from "./data";
import { Navbar } from "./sections/navbar";
import { Hero, Mission } from "./sections/hero";
import { Benefits, Team, Values } from "./sections/culture";
import { Roles } from "./sections/roles";
import { RoleDetail, type Application } from "./sections/role-detail";
import { Cta, Faq, Footer, Process } from "./sections/closing";
import { scrollToId } from "./sections/ui";

export type { CareersContent, Role, Application };

export interface CareersTemplateProps {
  /** Override brand, hero, values, team, benefits, roles, process, FAQ or footer. Missing keys fall back to the demo copy. */
  content?: Partial<CareersContent>;
  /** Called with the form values and CV file after a successful application. */
  onApply?: (application: Application) => void;
  className?: string;
}

/**
 * Nimbus Careers — a company careers page.
 * Floating pill navbar, mission hero with CSS-art team collage, scroll-revealed mission quote, values,
 * team photo bento, benefits grid, filterable open roles, hiring process timeline, FAQ, CTA and footer —
 * plus a role detail view with a validated application form (CV upload with drag & drop).
 */
export function CareersTemplate({ content, onApply, className }: CareersTemplateProps) {
  const c: CareersContent = { ...careersContent, ...content };
  const [roleId, setRoleId] = React.useState<string | null>(null);
  const pending = React.useRef<string | null | undefined>(undefined);
  const homeScroll = React.useRef(0);
  const role = roleId ? c.roles.items.find((r) => r.id === roleId) : undefined;

  React.useLayoutEffect(() => {
    const t = pending.current;
    if (t === undefined) return;
    pending.current = undefined;
    if (t === "__restore") window.scrollTo({ top: homeScroll.current, behavior: "auto" });
    else if (t) requestAnimationFrame(() => scrollToId(t));
    else {
      window.scrollTo({ top: 0, behavior: "auto" });
      document.getElementById("nc-role-title")?.focus({ preventScroll: true });
    }
  }, [roleId]);

  const openRole = (id: string) => {
    if (!roleId) homeScroll.current = window.scrollY;
    pending.current = null;
    setRoleId(id);
  };
  const goHome = (anchor?: string) => {
    if (!roleId) {
      if (anchor) scrollToId(anchor);
      else window.scrollTo({ top: 0, behavior: "smooth" });
      return;
    }
    pending.current = anchor ?? "__restore";
    setRoleId(null);
  };

  const footerNav = (label: string) => {
    const map: Record<string, string> = { "Open roles": "roles", "Life at Nimbus": "team", Benefits: "benefits", "Hiring process": "process" };
    if (map[label]) goHome(map[label]);
  };

  return (
    <div className={cn("relative w-full overflow-x-clip bg-background text-foreground antialiased", className)}>
      <a
        href="#nc-main"
        className="sr-only z-[90] rounded-md bg-foreground px-3 py-2 text-sm text-background focus:not-sr-only focus:fixed focus:left-3 focus:top-3"
      >
        Skip to content
      </a>
      <Navbar brand={c.brand} links={c.nav} onNav={(h) => goHome(h.slice(1))} onHome={() => goHome()} cta="Open roles" spy={!role} />
      <main id="nc-main">
        {role ? (
          <RoleDetail key={role.id} role={role} onBack={() => goHome("roles")} onApplySubmit={onApply} />
        ) : (
          <>
            <Hero hero={c.hero} onRoles={() => scrollToId("roles")} onTeam={() => scrollToId("team")} />
            <Mission mission={c.mission} />
            <Values values={c.values} />
            <Team team={c.team} />
            <Benefits benefits={c.benefits} />
            <Roles roles={c.roles} onOpen={openRole} />
            <Process process={c.process} />
            <Faq faq={c.faq} />
            <Cta cta={c.cta} onRoles={() => scrollToId("roles")} />
          </>
        )}
      </main>
      <Footer brand={c.brand} footer={c.footer} onNav={footerNav} />
    </div>
  );
}

export default CareersTemplate;

More in Landing Pages

View all →