Rounded two-column card with copy on one side and a picture or scene on the other, stacking until the Section is 48rem wide. Flip swaps the sides and the hairline divider beside the media is optional.
The source comes with full access.
One payment of $149 ($49 for now) opens the source right here and lets the CLI install every paid component, with lifetime updates.
pnpm dlx shadcn add @01orbital/split-cardnpx shadcn add @01orbital/split-cardyarn shadcn add @01orbital/split-cardbunx --bun shadcn add @01orbital/split-cardPart of the 01orbital library. With a license, put ORBITAL_TOKEN in .env.local and the same command installs it (licensed install, pricing).
It lands at components/card/split-card.tsx, as source you own and edit.
The example also uses button, poster and section. Install them too:
pnpm dlx shadcn add @01orbital/button @01orbital/poster @01orbital/sectionnpx shadcn add @01orbital/button @01orbital/poster @01orbital/sectionyarn shadcn add @01orbital/button @01orbital/poster @01orbital/sectionbunx --bun shadcn add @01orbital/button @01orbital/poster @01orbital/sectionimport type { ComponentProps } from "react"
import { cn } from "cn"
import {
SplitCard,
SplitCardActions,
SplitCardContent,
SplitCardDescription,
SplitCardHeader,
SplitCardMedia,
SplitCardTitle,
} from "@/components/card/split-card"
import {
Section,
SectionContent,
} from "@/components/editorial/section"
import {
Poster,
PosterDescription,
PosterFootnote,
PosterLabel,
PosterTitle,
} from "@/components/poster/poster"
import { buttonVariants } from "@/components/ui/button"
const COPY = {
title: "The economics of delivering on a system",
body: "A study of build time, review cycles and coherence across fifty launches.",
cta: "Download",
label: "01orbital.",
posterTitle: "The economics of delivering on a system",
posterBody: "Cost savings and coherence with the kit",
footnote: "A study of fifty launches, 2026",
} as const
// report-download-01's art, copied here so the demo matches the block.
function OrbitRings({ className, ...props }: ComponentProps<"div">) {
return (
<div
aria-hidden="true"
className={cn(
"pointer-events-none absolute inset-0 before:absolute before:top-[22%] before:left-[8%] before:size-44 before:rounded-full before:border before:border-foreground/25 after:absolute after:right-[6%] after:bottom-[10%] after:size-64 after:rounded-full after:border after:border-dashed after:border-foreground/25",
className
)}
{...props}
/>
)
}
function OrbitMark({ className, ...props }: ComponentProps<"span">) {
return (
<span
aria-hidden="true"
className={cn(
"flex items-center gap-2 text-current before:size-8 before:rounded-full before:border before:border-current/80 after:size-12 after:rounded-full after:border after:border-current/80",
className
)}
{...props}
>
<span className="w-10 border-t border-dashed border-current/80" />
</span>
)
}
export default function SplitCardDemo() {
return (
<Section>
<SectionContent className="items-center">
<SplitCard className="w-full max-w-5xl dark:surface-sheen">
<SplitCardContent>
<SplitCardHeader>
<SplitCardTitle>{COPY.title}</SplitCardTitle>
<SplitCardDescription>{COPY.body}</SplitCardDescription>
</SplitCardHeader>
<SplitCardActions>
<a
href="#"
data-slot="button"
className={cn(
buttonVariants({ variant: "secondary", size: "lg" })
)}
>
{COPY.cta}
</a>
</SplitCardActions>
</SplitCardContent>
<SplitCardMedia divider={false}>
<OrbitRings />
<Poster className="dark rotate-3 text-shadow-legible">
<img
src="https://01orbital.dev/art/kit/layered-currents-415686.png"
alt=""
loading="lazy"
/>
<PosterLabel>{COPY.label}</PosterLabel>
<PosterTitle>{COPY.posterTitle}</PosterTitle>
<PosterDescription>{COPY.posterBody}</PosterDescription>
<OrbitMark className="mt-8" />
<PosterFootnote>{COPY.footnote}</PosterFootnote>
</Poster>
</SplitCardMedia>
</SplitCard>
</SectionContent>
</Section>
)
}Split Card is built from 7 parts. In the example they nest like this:
SplitCard
├─ SplitCardContent
│ ├─ SplitCardHeader
│ │ ├─ SplitCardTitle
│ │ └─ SplitCardDescription
│ └─ SplitCardActions
└─ SplitCardMediaSplitCard: A wide card with copy on one side and a picture or scene on the other. Put a SplitCardContent and a SplitCardMedia inside.SplitCardContent: The copy half of a SplitCard, holding a SplitCardHeader and SplitCardActions. It centers its contents vertically, and its padding grows as the section widens.SplitCardHeader: Groups the SplitCardTitle and SplitCardDescription and caps their width at 28rem.SplitCardTitle: The card's title, an h3 in the display font that steps up a size when the section is 48rem wide. Pass render to use another heading level.SplitCardDescription: Muted supporting text under the SplitCardTitle.SplitCardActions: A row for the card's buttons or links; it wraps when they do not fit.SplitCardMedia: The picture half of a SplitCard. It is at least 24rem tall, centers whatever you put in it, and clips anything that spills over.A wide card with copy on one side and a picture or scene on the other. Put a SplitCardContent and a SplitCardMedia inside.
It responds to the width of the enclosing Section, not the screen. The two halves stack until the section is 48rem wide, then sit side by side, and the padding, title size and divider change at the same width. Outside a Section, the halves always stack.
Prop
Type
It also accepts the HTML attributes of the element it renders.
The copy half of a SplitCard, holding a SplitCardHeader and SplitCardActions. It centers its contents vertically, and its padding grows as the section widens.
It accepts the HTML attributes of the element it renders.
Groups the SplitCardTitle and SplitCardDescription and caps their width at 28rem.
It accepts the HTML attributes of the element it renders.
The card's title, an h3 in the display font that steps up a size when the section is 48rem wide. Pass render to use another heading level.
It accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.
Muted supporting text under the SplitCardTitle.
It accepts the HTML attributes of the element it renders.
A row for the card's buttons or links; it wraps when they do not fit.
It accepts the HTML attributes of the element it renders.
The picture half of a SplitCard. It is at least 24rem tall, centers whatever you put in it, and clips anything that spills over.
A scene fills it with absolute inset-0; an img needs size-full object-cover to fill it.
Prop
Type
It also accepts the HTML attributes of the element it renders.
npm: @base-ui/react, cn. The CLI installs them for you.
Registry pieces it installs with it:
font-hanken-groteskSimple Card
Lightweight card shell with an 8px inset that seats a bevelled preview frame, followed by header, title, description and footer. The default variant has a shadow and ring; subtle uses a faint tint.
Compare
Every Button component side by side: whether it follows the theme or stays dark, how many parts and custom props it has, and whether it is free. Open one for its preview, API and install.