# SummaryList

> Labelled values: a record's details, figures in a summary, or answers to check before submitting.

- Import: `import { SummaryList } from '@thekestongroup/ui'`
- Group: Data
- Live example: https://ui.keston.co/storybook/?path=/story/components-data-summarylist--default
- Source: https://github.com/thekestongroup/ui/blob/main/src/components/SummaryList/SummaryList.vue

## When to use

| Use a SummaryList for | Use instead |
|---|---|
| One record's details ("Email: …", "Year group: …") | Many records (Table) |
| Checking answers before submitting, with a Change per row | Editing (form fields) |
| A few figures in a summary card (`compact`) | One headline number (StatTile) |

## Layouts

| Layout | Looks like | Use for |
|---|---|---|
| rows (default) | Label left, value beside it, a line between rows. Label above value when the container is under 384px. | Details, check your answers |
| compact | Label left, value right-aligned and bold, no lines | Figures in a sidebar or summary card |
| stacked | Label above value; 1 column, 2 from 384px, 3 from 672px (container width) | Lots of short facts |

## Anatomy

| Part | Required | Notes |
|---|---|---|
| label | Yes | Black 65%. |
| value | Yes | Text, or the `value` slot (Badge, link). Empty shows "—". |
| description | No | Grey text under the value. |
| action | No | Slot, e.g. a "Change" button, at the end of the row. |

## Properties

| Property | Type | Default | Description |
|---|---|---|---|
| items | { label, value?, description?, emphasis? }[] | — | Required. `emphasis` makes the value bold, e.g. a total. |
| layout | rows \| compact \| stacked | rows | |
| labelWidth | sm \| md \| lg | md | Label column in `rows`: 128 / 192 / 256px. |
| emptyLabel | string | "Not set" | What screen readers hear for an empty value. |

## Sizes

| Part | Value |
|---|---|
| Text | 14px / 20px |
| rows | 12px above and below each row; the first and last flush with the container; 16px between columns; 4px label to value when stacked |
| compact | 8px between rows, 16px between columns |
| stacked | 4px label to value; 16px between rows, 24px between columns |
| Lines (rows) | 1px black 15% |
| Empty value | "—", black 40% |

## Accessibility

| Part | Announced as |
|---|---|
| List | Description list (`<dl>`), each label a term and each value its definition |
| Pairs | Each pair is wrapped together, so they're read together |
| Empty value | "Not set" (the dash is hidden) |
| Change action | Add the row to the button's name, e.g. `Change<span class="sr-only"> email</span>` |

## Platform adaptation

| Platform | Implementation | Differences |
|---|---|---|
| Web / desktop (Vue) | `SummaryList.vue` | — |
| iOS (SwiftUI) | — | `LabeledContent` rows in a `Form` or `List` section. |
| Android (Compose) | — | `ListItem` with `overlineContent` (label) and `headlineContent` (value). |

## Tokens used

| Token | Used for |
|---|---|
| color black | values |
| color black (65%) | labels, descriptions |
| color black (40%) | empty dash |
| color black (15%) | lines |

## Do / Don't

| Do | Don't |
|---|---|
| Keep labels short and values scannable | Put paragraphs in values |
| Use `compact` for numbers, so they line up on the right | Use `compact` for long text values |
| Say which row a Change button is for | Have several "Change" buttons with the same name |

## API (from the source)

The component's exact props, model, events, slots and types, with their documentation comments.

```ts
// SummaryList.vue
/**
 * Labelled values, e.g. a record's details or a summary before you submit:
 * "Email  office@keston.school". A description list (`<dl>`) underneath.
 *
 * Layouts:
 * - rows (default): label on the left, value beside it, a line between rows.
 *   Stacks the label above the value when the container is narrow (under
 *   384px). With an action per row (e.g. Change) for check-your-answers pages.
 * - compact: label left, value right-aligned, no lines. For figures in a
 *   sidebar or summary card ("Moving up a year  1,204").
 * - stacked: label above value, in a grid of up to 3 columns as space
 *   allows. For a lot of short facts.
 *
 * Slots: value (scoped `{ item, index }`, for a Badge, link, etc.), action
 * (scoped, e.g. a Change button).
 */
export interface SummaryListItem {
  label: string
  /** Shown as text. Empty shows "—" (read as "Not set"). Use the value slot for anything richer. */
  value?: string | number | null
  /** Smaller grey text under the value. */
  description?: string
  /** Bold value, e.g. a total. */
  emphasis?: boolean
}

export interface SummaryListProps<Item extends SummaryListItem = SummaryListItem> {
  items: Item[]
  layout?: 'rows' | 'compact' | 'stacked'
  /** Width of the label column in the rows layout. */
  labelWidth?: 'sm' | 'md' | 'lg'
  /** What an empty value is read as. */
  emptyLabel?: string
  class?: HTMLAttributes['class']
}

defineSlots<{
  value?(props: { item: Item; index: number }): unknown
  action?(props: { item: Item; index: number }): unknown
}>()
```
