ComponentsData
Description list
Shows pairs of labels and values, such as a record's settings, in one to three columns.
Import
import { DescriptionList, DescriptionItem } from "@booleanpress/ui/description-list"Usage
Each DescriptionItem takes its label in label and its value as children.
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
export function MailerSummary() {
return (
<DescriptionList>
<DescriptionItem label="Provider">Amazon SES</DescriptionItem>
<DescriptionItem label="From address">no-reply@acme.example</DescriptionItem>
</DescriptionList>
)
}orientation="vertical" (the default) puts each label above its value; horizontal puts it beside, with the labels in a column as wide as the widest one, up to 40% of the list's width (shared between its columns), where a longer label wraps. columns lays out one, two or three items per row from the sm breakpoint, and one per row below it; span on an item stretches a long value across columns, never more than the list has. bordered draws the list as a table, the labels on the subtle fill. size sets 12, 14 or 16 px text with matching spacing, and defaults to the provider's controlSize.
action puts a control at the end of a value, such as a copy button; name it after the item. Values are the consumer's: format numbers and dates with Intl in the provider's locale.
Examples
Basic
A mailer's settings, each label above its value.
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
export default function DescriptionListBasic() {
return (
<DescriptionList className="w-full max-w-sm">
<DescriptionItem label="Provider">Amazon SES</DescriptionItem>
<DescriptionItem label="From address">no-reply@acme.example</DescriptionItem>
<DescriptionItem label="Region">eu-west-1</DescriptionItem>
<DescriptionItem label="Daily limit">50,000 emails</DescriptionItem>
</DescriptionList>
)
}Horizontal
orientation="horizontal" puts each label beside its value, in a column of their own.
import { Badge } from "@booleanpress/ui/badge"
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
export default function DescriptionListHorizontal() {
return (
<DescriptionList orientation="horizontal" className="w-full max-w-md">
<DescriptionItem label="Provider">Amazon SES</DescriptionItem>
<DescriptionItem label="From address">no-reply@acme.example</DescriptionItem>
<DescriptionItem label="Status">
<Badge variant="success">Active</Badge>
</DescriptionItem>
<DescriptionItem label="Last test sent">15 October 2026, 10:30</DescriptionItem>
</DescriptionList>
)
}Columns
columns={3} lays out three items per row; span={3} gives the subject a whole row.
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
export default function DescriptionListColumns() {
return (
<DescriptionList columns={3} className="w-full">
<DescriptionItem label="Requester">Ana Lima</DescriptionItem>
<DescriptionItem label="Assignee">Priya Shah</DescriptionItem>
<DescriptionItem label="Priority">High</DescriptionItem>
<DescriptionItem label="Opened">14 October 2026</DescriptionItem>
<DescriptionItem label="Channel">Email</DescriptionItem>
<DescriptionItem label="Organisation">Acme Ltd</DescriptionItem>
<DescriptionItem label="Subject" span={3}>
SMTP login fails after the password was changed on the provider's side
</DescriptionItem>
</DescriptionList>
)
}Bordered
bordered draws a table, the labels on the subtle fill: beside the values, and above them.
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
export default function DescriptionListBordered() {
return (
<div className="flex w-full flex-col gap-6">
<DescriptionList bordered orientation="horizontal" columns={2}>
<DescriptionItem label="Organisation">Acme Ltd</DescriptionItem>
<DescriptionItem label="Plan">Business</DescriptionItem>
<DescriptionItem label="Seats">12 of 15</DescriptionItem>
<DescriptionItem label="Renews">1 January 2027</DescriptionItem>
<DescriptionItem label="Billing email">billing@acme.example</DescriptionItem>
</DescriptionList>
<DescriptionList bordered columns={3}>
<DescriptionItem label="Organisation">Acme Ltd</DescriptionItem>
<DescriptionItem label="Plan">Business</DescriptionItem>
<DescriptionItem label="Seats">12 of 15</DescriptionItem>
<DescriptionItem label="Renews">1 January 2027</DescriptionItem>
<DescriptionItem label="Billing email">billing@acme.example</DescriptionItem>
</DescriptionList>
</div>
)
}With actions
action puts a copy button at the end of each connection setting.
import { useState } from "react"
import { CheckIcon, CopyIcon } from "lucide-react"
import { Button } from "@booleanpress/ui/button"
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
const SETTINGS = [
{ label: "SMTP host", value: "smtp.acme.example" },
{ label: "Port", value: "587" },
{ label: "Username", value: "mailer@acme.example" },
{ label: "API key", value: "bp_live_4f9a…c21e" },
]
export default function DescriptionListWithActions() {
const [copied, setCopied] = useState<string | null>(null)
const copy = (label: string, value: string) => {
void navigator.clipboard?.writeText(value)
setCopied(label)
}
return (
<DescriptionList bordered orientation="horizontal" className="w-full max-w-md">
{SETTINGS.map(({ label, value }) => (
<DescriptionItem
key={label}
label={label}
action={
<Button variant="ghost" size="icon-sm" aria-label={`Copy ${label}`} onClick={() => copy(label, value)}>
{copied === label ? <CheckIcon /> : <CopyIcon />}
</Button>
}
>
<code className="font-mono text-[0.8125rem]">{value}</code>
</DescriptionItem>
))}
</DescriptionList>
)
}Sizes
sm, default and lg set 12, 14 and 16 px text with matching padding.
import { DescriptionItem, DescriptionList } from "@booleanpress/ui/description-list"
const SIZES = ["sm", "default", "lg"] as const
export default function DescriptionListSizes() {
return (
<div className="flex w-full flex-col gap-6">
{SIZES.map((size) => (
<DescriptionList key={size} size={size} bordered orientation="horizontal" columns={2}>
<DescriptionItem label="Provider">Postmark</DescriptionItem>
<DescriptionItem label="Region">us-east-1</DescriptionItem>
<DescriptionItem label="From address">receipts@acme.example</DescriptionItem>
<DescriptionItem label="Daily limit">10,000 emails</DescriptionItem>
</DescriptionList>
))}
</div>
)
}Accessibility
- Semantics
- A description list:
dl, with each item adivholding adt(the label) and add(the value), so a screen reader pairs every value with its label. - Labels
- A
dltakes no name of its own: put a heading above it. Name every action after its item ("Copy SMTP host"), as the same button repeats on every row. - Focus
- The list takes no focus. Actions are tab stops in reading order.
- Known limits
- The columns are visual only: a screen reader reads the items in source order, row by row.
- Long values wrap. Very long words, such as keys or addresses, break at any character rather than overflow.
Keyboard
| Key | Behaviour |
|---|
API
DescriptionList
Renders a dl and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
bordered | boolean | false | Draws the list as a table: an edge round it and between the items, the labels on the subtle fill. |
columns | 1 | 2 | 3 | 1 | How many items share a row, from the sm breakpoint (640 px); one per row below it. |
orientation | "horizontal" | "vertical" | vertical | vertical puts each label above its value; horizontal puts it beside, the labels in a column of their own. |
size | "default" | "sm" | "lg" | The text size, 12, 14 or 16 px, and the spacing with it. Defaults to the provider's controlSize. |
DescriptionItem
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | The label, shown in the muted colour. | |
action | ReactNode | A control at the end of the value, such as a copy button. Name it after the item ("Copy host"). | |
children | ReactNode | The value. | |
span | number | How many columns the item spans, from the sm breakpoint, for a long value such as an address; at most columns. |
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="description-list" (DescriptionList), data-slot="description-item" (DescriptionItem), and data-orientation, data-size, data-bordered.
Theming
Labels are --muted-foreground, values --foreground. Bordered lists draw 1 px --border lines with a 6 px radius, and the labels sit on --subtle.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--foreground | text |
--muted-foreground | text |
--subtle | background |