# Descriptions

> Label and value pairs in a <dl>, laid out as a grid.

Page: https://svelte-lean.vercel.app/primitives/descriptions · Markdown: https://svelte-lean.vercel.app/primitives/descriptions.md · Index: https://svelte-lean.vercel.app/llms.txt

Label and value pairs in a responsive grid, for a summary of settings, an order or a profile.

```svelte
<script lang="ts">
	import '@svelte-lean/styles/descriptions.css';

	const fields = [
		{ label: 'Plan', values: ['Team'] },
		{ label: 'Seats', values: ['12 of 20'] },
		{ label: 'Renews', values: ['28 October 2026'] },
		{ label: 'Billing contacts', values: ['Ada Byron', 'Grace Murray'] },
		{ label: 'Region', values: ['Frankfurt'] }
	];
</script>

<dl data-slean="descriptions" data-slean-columns="3">
	{#each fields as field (field.label)}
		<div>
			<dt>{field.label}</dt>
			{#each field.values as value (value)}
				<dd>{value}</dd>
			{/each}
		</div>
	{/each}
</dl>
```

At most three columns of at least 14rem each; resize the window and the columns follow. No script.

## Examples

### Inline layout

```svelte
<script lang="ts">
	import '@svelte-lean/styles/descriptions.css';
</script>

<dl data-slean="descriptions" data-slean-columns="1" data-slean-layout="inline" data-variant="outlined">
	<div>
		<dt>Repository</dt>
		<dd><a href="/primitives/descriptions">svelte-lean</a></dd>
	</div>
	<div>
		<dt>Default branch</dt>
		<dd>main</dd>
	</div>
	<div>
		<dt>Last deploy</dt>
		<dd><time datetime="2026-09-28T09:12">28 September, 09:12</time></dd>
	</div>
	<div>
		<dt>Environments</dt>
		<dd>Production</dd>
		<dd>Preview</dd>
	</div>
</dl>
```

data-slean-layout="inline" puts each label beside its value; outline cells and one column read like a table.

### In a card

```svelte
<script lang="ts">
	import '@svelte-lean/styles/badge.css';
	import '@svelte-lean/styles/card.css';
	import '@svelte-lean/styles/descriptions.css';
</script>

<article data-slean="card" class="order">
	<header data-slean-part="header">
		<h3 data-slean-part="title">Order 4120</h3>
		<p data-slean-part="description">Placed on 2 October 2026</p>
	</header>
	<div data-slean-part="body">
		<dl data-slean="descriptions" data-slean-columns="2">
			<div>
				<dt>Status</dt>
				<dd><span data-slean="badge" data-color="success">Paid</span></dd>
			</div>
			<div>
				<dt>Total</dt>
				<dd>€184.50</dd>
			</div>
			<div>
				<dt>Customer</dt>
				<dd>Mei Tanaka</dd>
				<dd><a href="mailto:mei@example.com">mei@example.com</a></dd>
			</div>
			<div>
				<dt>Delivery</dt>
				<dd>Express, <time datetime="2026-10-06">6 October</time></dd>
			</div>
		</dl>
	</div>
</article>

<style>
	.order {
		max-inline-size: 36rem;
	}
</style>
```

### Outline grid

```svelte
<script lang="ts">
	import '@svelte-lean/styles/descriptions.css';

	const specs = [
		['Instance', 'c7.large'],
		['vCPU', '2'],
		['Memory', '4 GiB'],
		['Storage', '80 GiB SSD'],
		['Zone', 'eu-central-1a'],
		['Uptime', '41 days']
	];
</script>

<dl data-slean="descriptions" data-slean-columns="3" data-variant="outlined">
	{#each specs as [label, value] (label)}
		<div>
			<dt>{label}</dt>
			<dd>{value}</dd>
		</div>
	{/each}
</dl>
```

## Usage

Plain markup with a stylesheet: install `@svelte-lean/styles`. No JavaScript runs.

```bash
npm install @svelte-lean/styles
```

`stylesheets`

```ts
import '@svelte-lean/styles/descriptions.css';
```

Wrap every `<dt>` and its `<dd>` in a `<div>`; a term with several values has several `<dd>`. Set `data-slean-columns` (1 to 4) to the most columns the content reads well in, or leave it out to fill the width.

Values are text or phrasing content: a `<time datetime>`, a link, a badge. An editable form is not a description list; use fields.

## Anatomy

| Part | Element | data-slean-part | Required | Notes |
| --- | --- | --- | --- | --- |
| root | `<dl data-slean="descriptions">` | – | yes | data-slean-columns="1\|2\|3\|4", data-slean-layout="stacked\|inline", data-variant="outlined". |
| group | `<div>` | – | yes | One per pair: a \<dt\> followed by one or more \<dd\>. One grid cell. |
| term | `<dt>` | – | yes | The label, in the muted color and the small text size. |
| definition | `<dd>` | – | yes | The value: text, a \<time\>, a link, a badge. Several stack in order. |

## Keyboard

| Key | When | Result |
| --- | --- | --- |
| `Tab` | links inside values | Moves between them (native); the list is not focusable |

## Styling

`descriptions.css` sets the responsive grid, the column cap, the inline layout, the label and value text and the outline cells. The tokens it reads:

| Token | Default (light) | Applies to |
| --- | --- | --- |
| `--slean-space-4` | `calc(var(--slean-spacing) * 4)` | gap, column-gap, padding-inline |
| `--slean-space-6` | `calc(var(--slean-spacing) * 6)` | gap, grid-template-columns |
| `--slean-fg` | `var(--slean-neutral-12)` | color |
| `--slean-text-md` | `1rem` | font-size |
| `--slean-leading` | `1.5` | line-height |
| `--slean-fg-muted` | `var(--slean-neutral-11)` | color |
| `--slean-text-sm` | `0.875rem` | font-size |
| `--slean-space-3` | `calc(var(--slean-spacing) * 3)` | padding-block |
| `--slean-border` | `var(--slean-neutral-6)` | border |

Variant attributes: `data-slean-columns` (`1`, `2`, `3`, `4`); `data-slean-layout` (`inline`); `data-variant` (`outlined`).

## Under the hood

Runtime and size

- **Tier**: 0 · Native
- **Behavior JS**: 0 B brotli · 0 B gzip · `fixtures/results.json (native-only)` · [method](https://svelte-lean.vercel.app/performance/methodology.md)
- **Platform features**: `<dl>`, `<dt>`, `<dd>`, `CSS grid`, `min()`, `max()`
- **Shared listeners**: none
- **Per-instance listeners**: none
- **Lazy state**: none
- **Native base**: `<dl> with <div> groups of <dt> and <dd>`

Tier 0: the descriptions have no behavior module, the Vite plugin maps `descriptions` to no module, and the page ships no Svelte Lean JavaScript for them.

Why this implementation

A page of settings, an order summary or a profile is a set of names and values. HTML has an element for that: `<dl>` with `<dt>` for the name and `<dd>` for the value, which screen readers announce as terms and definitions. HTML also allows a `<div>` around each pair, which gives the stylesheet one grid cell per pair.

`descriptions.css` lays the pairs out with `repeat(auto-fill, minmax(…))`: as many columns as fit, each at least 14rem wide, capped by `data-slean-columns`. The cap is arithmetic on the width, so the layout responds to the width of the list itself, without a media or container query.

### The browser owns

- the description list and its term and definition roles
- grouping a term with its values, several values per term
- the grid tracks that fill the width

### Svelte Lean owns

- descriptions.css: the responsive grid, the column cap, the inline layout, the outlined cells
- the contract: one \<div\> per pair, values as phrasing content
- documentation

Accessibility

- A `<dl>`: screen readers announce the list and each term with its values.
- The `<div>` groups are allowed inside `<dl>` and are ignored by the accessibility tree.
- The grid follows reading order, row by row, so the visual order and the reading order agree.
- Name the list with a heading before it, or `aria-labelledby` on the `<dl>`, when a page has several.

Platform features

| Feature | Baseline | Outside the target |
| --- | --- | --- |
| `<dl> with <div> groups` | Widely available | Not applicable |
| `Grid with min() and max() tracks` | Baseline 2020 | Not applicable |

Without JavaScript

Fully functional: the list and its layout are markup and CSS.

Server rendering and hydration

Static markup; the server renders the values.

Nothing is attached; the list reads the same before and after hydration.

Compatibility

Grid tracks with `min()` and `max()` are Baseline 2020, and `<div>` groups inside `<dl>` are valid HTML in every current browser.

Tests and source

- `apps/playground/tests/primitives/display-extra.spec.ts` Playwright, with page JavaScript enabled and disabled: roles, names, keyboard, geometry, axe
- `packages/styles/tests` static checks of the stylesheet (layers, tokens, specificity, dark parity)

- `packages/primitives/src/descriptions/contract.md` the contract in prose: semantics, keyboard, focus, ARIA, SSR, JS-disabled behavior
- `packages/primitives/src/descriptions/contract.ts` typed constants: name, tier, base, parts, options, events
- `packages/styles/css/descriptions.css` the optional stylesheet
