# List Styled list elements for ordered, unordered, and definition lists Section: components Docs: https://nebula.kaluza.com/components/list --- ## When to use - When presenting multiple items that benefit from visual separation rather than running together as prose - When items have an inherent order, priority, or sequence that matters: use an ordered list (`Ol`) - When items are discrete and parallel but have no meaningful order: use an unordered list (`Ul`) - When presenting term-and-definition pairs such as a glossary or a set of key facts: use a definition list (`Dl` with `Dt` and `Dd`) ## When not to use - **For two or fewer items** - these read more naturally as prose. Use [Text](/components/text) (`P`) instead. - **For key-value data about an entity** - use [Summary List](/components/summary-list) instead, which is built for structured data rows and provides better semantics for that pattern. - **For navigation links** - use the appropriate navigation component rather than a list of [Text Links](/components/text-link). ## Anatomy **Unordered and ordered lists:** 1. **List container** (`Ul` or `Ol`) 2. **List item** (`Li`) **Definition lists:** 1. **Definition list container** (`Dl`) 2. **Term** (`Dt`) 3. **Definition** (`Dd`) ## Basic usage ```tsx
  1. An example of body text.
  2. An example with emphasis and strong text.
``` ## Variants and features ### Unordered list Use `Ul` when the items are discrete but their order carries no meaning. Items are presented with a disc bullet marker. ```tsx ``` ### Ordered list Use `Ol` when the sequence matters, for example a set of steps or a ranked priority list. Items are numbered automatically. ```tsx
  1. Take a meter reading
  2. Enter it in your account
  3. Wait for your updated bill
``` ### Definition list Use `Dl` with `Dt` (term) and `Dd` (definition) for glossaries and term-definition pairs. Each term renders in semi-bold, and each definition follows immediately below it. For structured data about an entity (such as an account summary), use [Summary List](/components/summary-list) instead. ```tsx
MPAN
A unique 13-digit number that identifies your electricity supply point.
MPRN
A unique number that identifies your gas supply point.
``` ### Inline formatting Use `Strong` or `Em` from the [Text](/components/text) component inside `Li`, `Dt`, or `Dd` elements to add emphasis where needed. ```tsx ``` ### Nested lists Lists can be nested by placing a `Ul` or `Ol` inside a `Li`. Use nesting sparingly. More than two levels is usually a sign the content needs restructuring. ```tsx ``` ## Rules and guidance ### Usage - **List content** MUST use Nebula List components (Ul, Ol, Li, Dl, Dt, Dd) rather than raw HTML list elements. Rationale: Nebula list components apply responsive typography and spacing from the design token system. [id: list.no-raw-elements; testability: auto] - **Ul, Ol** SHOULD contain at least two items. Rationale: A single-item list provides no benefit over a paragraph. Use P instead. [id: list.minimum-items; testability: auto] - **Ol** SHOULD be used when the order of items is meaningful (steps, priority, ranking). Rationale: Using Ol signals to users and assistive technology that sequence matters. Using Ul when order is meaningful removes this signal. [id: list.ordered-when-sequence-matters; testability: manual] - **Ul** SHOULD be used when items are parallel but their order carries no meaning. [id: list.unordered-when-no-sequence; testability: manual] - **Dl** SHOULD be used for term-and-definition pairs such as glossaries or factual key-value content. [id: list.dl-for-term-definition; testability: manual] - **Dl** SHOULD NOT be used for structured data rows about an entity. Rationale: SummaryList is purpose-built for that pattern and provides better styling and semantics. [id: list.use-summary-list-for-entity-data; testability: manual] - **Nested lists** SHOULD NOT exceed two levels of nesting. Rationale: Deep nesting is visually complex and usually signals that content needs restructuring. [id: list.nesting-depth; testability: auto] ### Content - **Li, Dd** SHOULD be grammatically parallel across items in the same list. Rationale: Parallel structure makes lists easier to scan and understand. [id: list.item-parallelism; testability: manual] - **Li** SHOULD NOT end with a full stop unless list items are complete sentences. [id: list.no-terminal-punctuation; testability: manual] ### Composition - **Li** MUST be a direct child of Ul or Ol. Rationale: Placing Li outside a list container is invalid HTML and breaks screen reader list announcement. [id: list.li-inside-ul-ol; testability: auto] - **Dt, Dd** MUST be direct children of Dl. [id: list.dt-dd-inside-dl; testability: auto] - **Dt** MUST appear before the Dd element it describes. [id: list.dt-before-dd; testability: auto] ## Accessibility ### In brief List is built from native `ul`, `ol`, `li`, `dl`, `dt` and `dd` elements. A screen reader can announce it as a list and say how many items it has, and reads a description list as terms with their details. ### What Nebula provides - `Ul`, `Ol`, `Li`, `Dl`, `Dt` and `Dd` render the native elements of the same name. - `Ul` and `Ol` keep their markers (disc and decimal). - A `Dt` can be followed by more than one `Dd`. ### Accessible design #### Use an ordered list only when the order matters A numbered list tells the user the sequence carries meaning. Use an unordered list when it does not. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) #### Use a description list for terms and their details A glossary or a set of key and value pairs is read as a pair when it is a `Dl`. A run of bold labels and values is read as unrelated lines. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) ### Accessible development #### Always put `Li` directly inside `Ul` or `Ol`, and `Dt` and `Dd` directly inside `Dl` The elements only make a list when they are nested this way. A `Li` outside a list, or a `Dd` outside a `Dl`, is not announced as part of one. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) #### Never type a bullet or a number into the text of a paragraph A hand-made list has no list structure, so a screen reader cannot say how many items there are or let the user skip it. Use the list components. WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html) ### Accessibility testing status Reviewed on 9 October 2026 against v17.0.1. | Test | Status | Tested with | | --- | --- | --- | | Default state | Tested | axe-core via the Storybook test runner (CI) | | Advanced states | Partially tested | axe-core via the Storybook test runner (CI) | | Keyboard navigation | Manually tested | keyboard (reported by the owner) | | Screen reader | Manually tested | VoiceOver with Chrome (reported by the owner) | What each test covers: - Default state: The component as it first appears, before anything is clicked or changed. - Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering. - Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring. - Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state. What these statuses mean: - Tested: Every check in this row passed in an automated run that repeats on every pull request. - Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change. - Partially tested: Some checks in this row were run once and passed; the rest are still to do. - Issue found: A check in this row failed. The ticket is shown beside the status. - Not tested: No check in this row has been run yet. - Not applicable: This row does not apply to the component, for the reason given beside it. The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used. ## Properties ### Ul Also accepts all props from `HTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Ol Also accepts all props from `HTMLAttributes`, `OlHTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Li Also accepts all props from `HTMLAttributes`, `LiHTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Dl Also accepts all props from `HTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Dt Also accepts all props from `HTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ### Dd Also accepts all props from `HTMLAttributes`. - `as` (type `ElementType`, optional). Renders the component as a different element or component, e.g. `as="section"`. - `forwardedAs` (type `ElementType`, optional). Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. ## Styles Reference only; you do not need this to use the component. This is the CSS its styled elements use, with theme tokens shown by name. `${...}` is an interpolation (a theme token, a mixin or another value from the code). A `&[prop]` or `@when ` block holds CSS that applies only under that condition; these are notation, not valid CSS. ### ul ```css list-style-type: disc; margin-top: ${space[1]}; margin-bottom: ${space[1]}; margin-left: ${space[6]}; ${mediaQueries.smallAndUp} { margin-top: ${space[2]}; margin-bottom: ${space[2]}; } &:first-child { margin-top: 0; } &:last-child { margin-bottom: 0; } ``` ### ol ```css list-style-type: decimal; margin-top: ${space[2]}; margin-bottom: ${space[1]}; margin-left: ${space[6]}; ${mediaQueries.smallAndUp} { margin-top: ${space[2]}; margin-bottom: ${space[2]}; } &:first-child { margin-top: 0; } &:last-child { margin-bottom: 0; } ``` ### li ```css font-family: ${fonts.body}; line-height: ${lineHeights.body}; font-weight: ${fontWeights.normal}; color: ${colors.body}; margin: ${space[2]} auto; font-size: ${responsiveFontSizes.body[0]}; ${mediaQueries.smallAndUp} { font-size: ${responsiveFontSizes.body[1]}; } &:first-child { margin-top: 0; } &:last-child { margin-bottom: 0; } ``` ### dl ```css margin-top: ${space[1]}; margin-bottom: ${space[1]}; ${mediaQueries.smallAndUp} { margin-top: ${space[2]}; margin-bottom: ${space[2]}; } &:first-child { margin-top: 0; } &:last-child { margin-bottom: 0; } ``` ### dt ```css font-family: ${fonts.body}; line-height: ${lineHeights.body}; font-weight: ${fontWeights.semiBold}; color: ${colors.heading}; margin-top: ${space[2]}; margin-bottom: ${space[1]}; font-size: ${responsiveFontSizes.body[0]}; ${mediaQueries.smallAndUp} { font-size: ${responsiveFontSizes.body[1]}; margin-top: ${space[3]}; } &:first-child { margin-top: 0; } &:last-child { margin-bottom: 0; } ``` ### dd ```css font-family: ${fonts.body}; line-height: ${lineHeights.body}; font-weight: ${fontWeights.normal}; color: ${colors.body}; font-size: ${responsiveFontSizes.body[0]}; ${mediaQueries.smallAndUp} { font-size: ${responsiveFontSizes.body[1]}; } dd + & { margin-top: ${space[1]}; } ```