Primitives Layout and display
Descriptions
Label and value pairs in a description list, laid out as a grid that fills the width with as many columns as fit. The list is native semantics; the grid is CSS. Tier 0.
- Tier
- 0 · Native
- Behavior JS
- 0 B brotli · 0 B gzip ·
fixtures/· methodresults.json (native-only) - 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>
On this page
Example
- Plan
- Team
- Seats
- 12 of 20
- Renews
- Billing contacts
- Ada Byron
- Grace Murray
- Region
- Frankfurt
<dl data-slean="descriptions" data-slean-columns="3">
<div>
<dt>Plan</dt>
<dd>Team</dd>
</div>
<div>
<dt>Seats</dt>
<dd>12 of 20</dd>
</div>
<div>
<dt>Renews</dt>
<dd><time datetime="2026-10-28">28 October 2026</time></dd>
</div>
<div>
<dt>Billing contacts</dt>
<dd>Ada Byron</dd>
<dd>Grace Murray</dd>
</div>
<div>
<dt>Region</dt>
<dd>Frankfurt</dd>
</div>
</dl><script lang="ts">
import '@svelte-lean/styles/descriptions.css';
let { account } = $props();
</script>
<dl data-slean="descriptions" data-slean-columns="3">
{#each account.fields as field (field.label)}
<div>
<dt>{field.label}</dt>
{#each field.values as value (value)}<dd>{value}</dd>{/each}
</div>
{/each}
</dl>Why this implementation exists
A page of settings, an order summary or a profile is a set of names and values. HTML has an element for exactly 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, not of the window, 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 outline cells
- the contract: one <div> per pair, values as phrasing content
- documentation
Usage
The markup needs no package. Install @svelte-lean/styles for the stylesheet; @svelte-lean/primitives adds the typed contract and nothing at runtime.
npm install @svelte-lean/stylespnpm add @svelte-lean/stylesyarn add @svelte-lean/stylesbun add @svelte-lean/stylesimport '@svelte-lean/styles/descriptions.css';Wrap every <dt> and its <dd> in a <div>. A term with several values has several <dd> in the same group.
Set data-slean-columns to the most columns the content reads well in; leave it out to fill the width. data-slean-layout="inline" suits a single column of short values, such as a settings summary.
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="outline". |
| 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. |
Runtime profile
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.
Accessibility contract
- 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-labelledbyon the<dl>, when a page has several.
Keyboard
| Key | When | Result |
|---|---|---|
| Tab | links inside values | Moves between them (native); the list is not focusable |
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
Static markup; the server renders the values.
Before hydration
Nothing is attached; the list reads the same before and after hydration.
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 | 1rem | gap, column-gap, padding-inline |
--slean-space-6 | 1.5rem | 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 | 0.75rem | padding-block |
--slean-border | var(--slean-neutral-6) | border |
Variant attributes: data-slean-columns (1, 2, 3, 4); data-slean-layout (inline); data-variant (outline).
Compatibility notes
Grid tracks with min() and max() are Baseline 2020, widely available, and <div> groups inside <dl> are valid HTML in every current browser.
Examples
Inline layout and outline cells
data-slean-layout="inline" puts each label beside its value, two fifths and three
fifths of the cell, so labels line up down a column. data-variant="outline" draws
every group as a cell whose borders overlap its neighbours'. With data-slean-columns="1" the result reads like a two-column table while staying a description
list.
- Repository
- svelte-lean
- Default branch
- main
- Last deploy
<dl
data-slean="descriptions"
data-slean-columns="1"
data-slean-layout="inline"
data-variant="outline"
>
<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>
</dl>Testing
apps/playground/tests/primitives/display-extra.spec.tsPlaywright, with page JavaScript enabled and disabled: roles, names, keyboard, geometry, axepackages/styles/testsstatic checks of the stylesheet (layers, tokens, specificity, dark parity)
Source
packages/primitives/src/descriptions/contract.mdthe contract in prose: semantics, keyboard, focus, ARIA, SSR, JS-disabled behaviorpackages/primitives/src/descriptions/contract.tstyped constants: name, tier, base, parts, options, eventspackages/styles/css/descriptions.cssthe optional stylesheet