Job Card
ProThe canonical open-role block: a category badge, the role title, a one-line summary and a meta tier of location, employment type and salary band. Renders as a chrome-less row or a bordered card.
Installation#
Usage#
Compose the parts you need and omit the rest: there is no showX prop anywhere on this component. A role with no summary simply leaves out JobCardDescription; a board that links whole rows instead of showing a button leaves out JobCardAction. The eyebrow is an ordinary Badge dropped as the first child of JobCardContent, so it takes any tone in the Badge scale.
Anatomy#
import {
JobCard,
JobCardContent,
JobCardTitle,
JobCardDescription,
JobCardMeta,
JobCardMetaItem,
JobCardFlag,
JobCardAction,
} from "@/components/ui/job-card"
<JobCard>
<JobCardContent>
<Badge />
<JobCardTitle />
<JobCardDescription />
<JobCardMeta>
<JobCardMetaItem icon={<JobCardFlag country="…" />} />
</JobCardMeta>
</JobCardContent>
<JobCardAction>…</JobCardAction>
</JobCard>Variants#
plain (the default) is chrome-less: the row sits flush on whatever holds it, and the list that owns it paints the hairline between rows. card gives the role a box of its own: the boundary and nothing else, drawn straight on the page ground with no fill behind it, and with an inset ring rather than a border so the corner stays concentric with the radius inside it.
Layouts#
split (the default) rides the action against the right edge, vertically centred. stacked drops it below the content, which is what a card in a narrow column wants. inline is the table shape: title and meta share one line, and the meta becomes equal-width auto columns so the same template repeats on every row and the columns line up down the whole board. Both row layouts start as a stacked column and only become a row at their breakpoint (smfor split, md for inline), so an action never strands itself in the corner of a narrow frame.
Divided rows#
divided turns a stack of plain rows into a board: a hairline under each one and the vertical band that gives the row its height. It is a rule of its own rather than a border-b, so it stays straight even under a rounded interactive row. Compose it as a real list, one <li> per role and no gap between them: the hairline is the separation, and the trailing rule is dropped from the last list item so the board does not close with a line under nothing.
Interactive rows#
Pass interactive (with asChild and an <a> child) when the whole row opens the role. A plain interactive row pulls its hover fill back into a detached rounded panel so the wash does not bleed to the container edge, while the text stays flush-aligned, and the hairline pulls in by the same amount so it never overhangs the text. There is no press scale: a row that shrinks pulls its fill off the edges it sits against and starts reading as a floating tile, so the press is a deepening fill instead.
Any action, not just a button#
JobCardAction is a plain slot, so a board that wants to show who is hiring drops an AvatarGroup into it instead of a button. Nothing about the recipe changes.
API reference#
JobCard
The root. Renders a <div> and forwards all its props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "plain" | "card" | "plain" | plain is a chrome-less row; card adds an inset ring and padding, no fill. |
| layout | "split" | "stacked" | "inline" | "split" | split puts the action beside the text from sm; stacked keeps it below; inline lines title and meta up in columns from md. |
| divided | boolean | - | A hairline under the row plus its own vertical padding. The rule drops on the last <li>, so wrap each row in one. |
| interactive | boolean | - | Hover, press and focus affordance for a row that is itself a link. |
| asChild | boolean | false | Renders the child (an <li>, an <a>) as the root via Radix Slot. |
JobCardContent
The text column. Drop a Badge in first as the eyebrow. Forwards native <div> props.
JobCardTitle
The role title. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Renders the child (say an <h3>) as the title, for a real heading. |
JobCardDescription
The muted one-line summary. Forwards native <p> props.
JobCardMeta
The meta tier: location, type, salary. Forwards native <div> props.
JobCardMetaItem
One meta fact; the text truncates. Forwards native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| icon | React.ReactNode | - | The leading 20px glyph: a Koala icon or a JobCardFlag. |
JobCardAction
The trailing action slot: a button, an avatar stack, a link. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | The control size imposed on the buttons inside. |
| Data attribute | Description |
|---|---|
| data-control-size | The imposed size. |
JobCardFlag
A country flag cropped into a ringed circle at the meta glyph's size, aria-hidden (the country is spelled out beside it).
| Prop | Type | Default | Description |
|---|---|---|---|
| country* | string | - | An ISO 3166-1 alpha-2 code, e.g. "US". Renders nothing for an unknown code. |
| className | string | - | Merged onto the circle. |