Card
Signature surface with corner-bracket reticles and a mono index header.
"use client";import * as React from "react";import { Badge } from "@/registry/okibi/ui/badge";import { Button } from "@/registry/okibi/ui/button";import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardIndex, CardTitle,} from "@/registry/okibi/ui/card";export default function CardDemo() { const [armed, setArmed] = React.useState(false); return ( <Card className="w-80 max-w-full"> <CardHeader> <CardIndex>SEC-04</CardIndex> <CardTitle>ICE Layer</CardTitle> <CardDescription>Subnet secure</CardDescription> <CardAction> <Badge variant={armed ? "warning" : "success"}> {armed ? "Armed" : "Secure"} </Badge> </CardAction> </CardHeader> <CardContent className="text-sm text-muted-foreground"> Black ICE holding across all four nodes. No trace detected. </CardContent> <CardFooter className="justify-end"> <Button variant={armed ? "signal" : "outline-tech"} size="sm" aria-pressed={armed} onClick={() => setArmed((prev) => !prev)} > {armed ? "Armed" : "Arm"} </Button> </CardFooter> </Card> );}npx shadcn@latest add https://okibi.cndr.dev/r/card.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/card.jsonCopy the source from the Code tab above into components/ui/card.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
<Card>
<CardHeader>
<CardTitle>Access Request</CardTitle>
<CardDescription>Submit credentials for clearance.</CardDescription>
</CardHeader>
<CardContent>Fielded body…</CardContent>
<CardFooter>Docked actions…</CardFooter>
</Card>Anatomy
A Card composes a header band, a body, and a footer band. The header grid holds an optional mono index, the title, the description, and a top-right action cell; CardContent and CardFooter are separated by token hairlines.
<Card>
<CardHeader>
<CardIndex />
<CardTitle />
<CardDescription />
<CardAction />
</CardHeader>
<CardContent />
<CardFooter />
</Card>Examples
Form
Compose the header / content / footer slots into a self-contained form panel — index, title, a fielded body and a docked action row.
import { Button } from "@/registry/okibi/ui/button";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardIndex, CardTitle,} from "@/registry/okibi/ui/card";import { Input } from "@/registry/okibi/ui/input";import { Label } from "@/registry/okibi/ui/label";export default function CardFormDemo() { return ( <Card className="w-full max-w-sm"> <CardHeader> <CardIndex>SEC // 01</CardIndex> <CardTitle>Access Request</CardTitle> <CardDescription>Submit credentials for clearance.</CardDescription> </CardHeader> <CardContent className="space-y-4"> <div className="space-y-2"> <Label htmlFor="operator-id">Operator ID</Label> <Input id="operator-id" placeholder="CH-0x4F" /> </div> <div className="space-y-2"> <Label htmlFor="clearance-code">Clearance code</Label> <Input id="clearance-code" type="password" placeholder="••••••••" /> </div> </CardContent> <CardFooter className="justify-between"> <Button variant="ghost" size="sm"> Cancel </Button> <Button variant="signal" size="sm"> Submit </Button> </CardFooter> </Card> );}Stat panel
Compose the surface as an instrument readout — a mono index header over a large tabular value, a unit, and a signal delta line — rather than a form.
import { ArrowUpRight } from "lucide-react";import { Card, CardContent, CardHeader, CardIndex, CardTitle,} from "@/registry/okibi/ui/card";export default function CardStatDemo() { return ( <Card className="w-full max-w-xs"> <CardHeader> <CardIndex>FLUX // 0x2C</CardIndex> <CardTitle>Core Output</CardTitle> </CardHeader> <CardContent className="space-y-1"> <div className="flex items-baseline gap-x-1.5"> <span className="font-mono text-4xl font-bold tabular-nums leading-none"> 847 </span> <span className="font-mono text-xs uppercase tracking-[0.1em] text-muted-foreground"> kW </span> </div> <div className="flex items-center gap-1 font-mono text-xs font-semibold tabular-nums text-success"> <ArrowUpRight className="size-3.5" aria-hidden="true" /> <span className="sr-only">increase </span> <span>12.4%</span> <span className="text-muted-foreground font-normal">vs prev cycle</span> </div> </CardContent> </Card> );}Clickable
Wrap the surface in a real anchor for a whole-card link: a visible focus ring and a hover lift make it a proper, keyboard-navigable target (no role/tabindex hacks).
import { ArrowUpRight } from "lucide-react";import { Card, CardContent, CardDescription, CardHeader, CardIndex, CardTitle,} from "@/registry/okibi/ui/card";export default function CardClickableDemo() { return ( <a href="#node-7f" className="group block w-full max-w-sm rounded-none outline-none transition-all duration-[var(--duration-fast)] ease-out hover:-translate-x-[2px] hover:-translate-y-[2px] focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background" > <Card className="transition-shadow group-hover:shadow-[6px_6px_0_0_color-mix(in_oklch,var(--color-primary)_38%,transparent)]"> <CardHeader> <CardIndex>NODE // 0x7F</CardIndex> <CardTitle className="transition-colors group-hover:text-signal-spark"> Relay Station </CardTitle> <CardDescription>Open uplink to the orbital mesh.</CardDescription> </CardHeader> <CardContent className="flex items-center justify-between text-sm text-muted-foreground"> <span>14 active channels</span> <ArrowUpRight className="size-4 text-muted-foreground transition-colors group-hover:text-signal-spark" aria-hidden="true" /> </CardContent> </Card> </a> );}Accessibility
- A presentational surface —
Cardis a plaindivwith no implicit role, so semantics come from the content you place inside it. - The four corner-bracket reticles are decorative: each carries
aria-hiddenandpointer-events-none, so assistive tech never announces them. CardTitlerenders adiv, not a heading. Pass real heading content (or render anh2/h3) so a card region is reachable in the heading outline.- Every slot exposes a
data-slothook (card,card-header,card-title,card-action, …) for styling and test queries, not for a11y semantics. - There is no built-in clickable affordance — if the whole card is a target, wrap the heading in a link and add your own focus ring rather than putting a handler on the surface.
Guidelines
Do
- Give
CardTitletrue heading content so the surface lands in the page outline. - Dock controls into
CardAction— the header grid reserves the top-right cell so the title still wraps cleanly on narrow cards. - Leave breathing room around tiled cards; the
4px 4pxsignal shadow and corner reticles need the strong border and a neighbor gap to read.
Don't
- Do not hang an
onClickon the bareCarddiv and call it a button — wire a real link or button inside instead. - Do not nest a
Cardinside aCard; the doubled borders and reticles read as noise, not hierarchy. - Do not override the sharp corners or hard shadow with rounding or blur — that breaks the HUD surface language.
Toggle Group
Segmented control: a strong-bordered bar of Toggles divided by hairline keylines, each filling signal when active. `type="multiple"` for independent toggles, `type="single"` for a one-of-many selector.
Separator
Technical divider with an optional centered mono label, and an optional live signal pulse that travels along the rule.