Data

Card

Surface, subtle, ghost and interactive. Subtle cards are quieter: a fainter hairline and no shadow. Interactive cards lift a pixel on hover with a spring settle; render them as links or buttons. Flush cards add no padding: divided rows, or a panel the content pads. ItemRow is an item in such a card (icon tile, name, detail, controls) with ItemRowSkeleton for loading; CardRow is the setting-row inset alone. ClampedText cuts long prose to a number of rendered lines with Show more.

Live example

Import

Each family is its own entry point, so a page loads only what it uses.

tsx
import { Avatar, AvatarGroup } from "@nibapp/ui/components/avatar";import { Badge } from "@nibapp/ui/components/badge";import { Button, IconButton } from "@nibapp/ui/components/button";import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardRow, CardTitle } from "@nibapp/ui/components/card";import { ClampedText } from "@nibapp/ui/components/clamped-text";import { ItemRow, ItemRowSkeleton } from "@nibapp/ui/components/item-row";import { ProgressRing } from "@nibapp/ui/components/progress";import { Switch } from "@nibapp/ui/components/switch";

Props

Each component's own props and variants, read from its source. What it also takes from an element or a Base UI part is named below its table.

Avatar

Image avatar with deterministic tinted initials as fallback, plus optional presence dot.

PropTypeDefaultDescription
shape"circle" | "square""circle"
name (required)string—

The person's (or a bot's) name: the accessible label, the initials and the tint.

srcstring | undefined—
sizeAvatarSize—
statusAvatarStatus | undefined—

Presence dot. Announced in the accessible label, not just shown in color.

personaPersona—

Override the hashed persona tint.

fallbackReactNode—

Replace the initials, e.g. with an icon for a bot or a team.

descriptionstring—

Optional role/descriptor appended to the label, e.g. "CEO".

Also takes the props of a <span> element, except children.

AvatarGroup

Overlapping stack of avatars. Set `--avatar-ring` to the surface color when the group sits on anything other than the page background (e.g. `[--avatar-ring:var(--surface)]` inside a Card).

PropTypeDefaultDescription
sizeAvatarSize"md"
maxnumber—

Show at most this many avatars, then a "+N" chip.

Also takes .

Badge

Small, calm label: subtle tint + matching foreground. Never loud.

PropTypeDefaultDescription
variant"neutral" | "outline" | "success" | "warning" | "danger" | "info""neutral"
neutral
Quiet gray — metadata, counts, model names.
outline
Hairline only — for dense lists where fills get noisy.
info
Neutral wash + fg-muted ink, like every info surface; its `Info` icon is what says "info".
size"sm" | "md""md"
dotbooleanfalse

Leading status dot. Pair with text — the dot alone never carries meaning.

iconReactNode | false—

Leading icon. The info variant defaults to lucide `Info`: info is neutral, not the accent, so the icon is what tells it apart from a plain neutral badge. Pass your own icon to replace it, or `false` to hide it. A `dot` takes the icon's place.

Also takes .

Button

PropTypeDefaultDescription
variant"primary" | "secondary" | "ghost" | "quiet" | "danger" | "danger-ghost" | "link""secondary"
primary
Neutral ink: the main call to action. Actions are ink, never the accent.
secondary
Quiet surface with a hairline — the default for most actions.
ghost
No chrome until hovered — toolbars, inline actions.
quiet
Words that are also an action, in the text's own weight and laid out from the start, so they line up with the text around them: `xs` in a line (a header's crumbs, a title that renames), `sm` a toolbar's picker, `md` a row in a list or a popover ("Open settings", a sidebar's link).
danger
Destructive confirmation.
danger-ghost
Destructive but low emphasis.
link
Inline text action.
size"xs" | "sm" | "md" | "lg""md"
blockboolean—
truncateboolean—

The button may shrink below its label's width; the label then truncates with an ellipsis.

wrapboolean—

The label wraps onto more lines and the button grows, its `leading` icon on the first line: for words that are an action and may run long in another language (a tip). One line keeps the size.

loadingbooleanfalse

Shows a spinner, keeps the button's width, and blocks interaction.

leadingReactNode—

Icon rendered before the label.

trailingReactNode—

Icon or hint (e.g. <Kbd>) rendered after the label.

disabledReasonstring | undefined—

Disables the button and says why: the reason is its tooltip and its accessible description, and the button stays focusable (`aria-disabled`) so a keyboard reaches it.

Also takes .

IconButton

Square, icon-only button. Pair with <Tooltip> to surface the label visually (a `disabledReason` brings its own).

PropTypeDefaultDescription
size"xs" | "sm" | "md" | "lg""md"
aria-label (required)string—

Required: icon-only controls must be named for assistive tech.

children (required)ReactNode—
loadingbooleanfalse
disabledReasonstring | undefined—

Disables the button and says why, as Button's `disabledReason`.

Also takes the props of Base UI's Button, except aria-label and children.

Card

Container for grouped content. Compose with CardHeader / CardContent / CardFooter, or use `size="flush"` (with `divided` for rows) when the content brings its own padding. A kit part that draws a card on an element it already renders takes `cardStyles` instead.

PropTypeDefaultDescription
variant"surface" | "subtle" | "ghost" | "inset" | "interactive""surface"
surface
Default container: surface + hairline + whisper of shadow.
subtle
Quieter: a fainter hairline and no shadow, for lists and notes set into a page (a settings list, a usage summary, a notice). Usually `size="flush"`, `divided` for rows.
ghost
Structure without chrome — groups content on an existing surface.
inset
Sunken into the surface it sits on: a quoted message, a preview, a plan's step or a field's note inside a card or a drawer. Usually `size="sm"` or `flush` with the content's padding.
interactive
Clickable tile: lifts on hover. Render as <a>/<button> via `render`.
size"sm" | "md" | "flush""md"
flush
No padding or gap of its own: the card frames full-width rows (with `divided`), or one block the caller lays out and pads (a form, a stat, a checkbox).
dividedboolean—

A hairline between the card's children: a list of rows (settings, accounts, devices).

Also takes .

CardAction

Trailing slot in the header (a menu button, badge, …). Spans the title + description rows.

Also takes the props of a <div> element.

CardContent

Also takes the props of a <div> element.

CardDescription

Also takes the props of a <p> element.

CardFooter

PropTypeDefaultDescription
dividedbooleanfalse

Hairline above the footer, for cards with a distinct action bar.

Also takes .

CardHeader

Also takes the props of a <div> element.

CardRow

One padded row of a flush card (`size="flush"`, `divided` between rows): a setting, a field, a "Load more". The setting row's inset, 16 · 14; the row lays out its own content. An item with an icon, a name and a detail is an `ItemRow` (item-row.tsx).

Also takes the props of a <div> element.

CardTitle

Also takes the props of a <h3> element.

ClampedText

Text cut to `lines` rendered rows with a "Show more" toggle — prose of any script and length, Markdown included. Whether it overflows is measured from the layout (and again when the width changes), never guessed from the string; content that fits gets no toggle.

PropTypeDefaultDescription
lines (required)number—

Rows shown before "Show more", at the content's own line height.

children (required)ReactNode—
contentClassNamestring | undefined—

Classes of the clamped box itself (its type sets the line height the clamp counts in).

expandedboolean | undefined—

Controlled expansion, for a host that owns it (a long list that keeps every row's state).

onExpandedChange((expanded: boolean) => void) | undefined—

Also takes the props of a <div> element, except children.

ItemRow

One item in a flush, divided card (`Card size="flush" divided`): an icon tile, its name and a muted detail, its controls at the end. A device, a key, a member, an invoice. `CardRow` is the padding alone, for a row that lays out its own content.

PropTypeDefaultDescription
iconReactNode—

Leading glyph in a tile; an icon in it is sized and muted for you.

title (required)ReactNode—

The item's name, with any badge after it. Wrap a long name in a `truncate` span.

detailReactNode—

One muted line under the name: cut with an ellipsis when it doesn't fit, whole in a tooltip.

trailingReactNode—

Its controls. On a narrow card they drop under the text rather than squeeze it.

childrenReactNode—

More under the detail, wrapping: a status note, a reason.

Also takes the props of a <div> element, except title.

ItemRowSkeleton

`ItemRow`s while a list loads: the tile, a name and a detail line, `rows` times.

PropTypeDefaultDescription
rowsnumber1

ProgressRing

Tiny circular meter for token / context-window usage. Escalates at warning & danger thresholds.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "xl""md"
xl
A card's own meter (a usage card's limit).
value (required)number—
warningAtnumber75

Percentage at which the ring turns amber.

dangerAtnumber90

Percentage at which the ring turns red.

levelProgressRingLevel—

Force a level instead of deriving it from the thresholds.

showValuebooleanfalse

Show the formatted value next to the ring — keeps the state legible without color.

aria-label (required)string—

Required accessible name, e.g. "Context window".

Also takes ComponentProps<typeof Meter.Root>, except children and render.

Switch

Ink toggle with a springy thumb that stretches while pressed.

PropTypeDefaultDescription
size"sm" | "md""md"
sm
16 × 28 — dense rows, menus.
md
20 × 36 — settings rows.
labelReactNode—

Visible label to the right of the switch. For settings rows prefer <Field orientation="horizontal">.

descriptionReactNode—

Also takes the props of Base UI's Switch.Root, except children.