---
title: "List"
description: "Bullet, number and check lists, and CustomList with markers."
requested_language: ar
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/ar/components/list
version: unreleased (main)
docs_index: https://docs.systhema.app/ar/llms.txt
---
> This page isn't translated yet. Showing English.


`List` and `ListItem` render bullet, numbered and check lists whose markers, spacing and colors come from the `list` tokens. `CustomList` lays out items with a marker of your choice, such as an icon or an emoji, in a two-column grid.

```tsx preview title="List"
import { List, ListItem } from '@systhemaui/next'

export default function Demo() {
  return (
    <List type="bullet">
      <ListItem>Tokens exported from Figma as DTCG JSON</ListItem>
      <ListItem>CSS variables and Tailwind utilities generated on sync</ListItem>
      <ListItem>Typed React and Next.js components that read them</ListItem>
    </List>
  )
}
```

## Import

```tsx
import {
  CustomList,
  CustomListContent,
  CustomListItem,
  CustomListItemMarker,
  List,
  ListItem,
} from '@systhemaui/next'
```

In a React app without Next.js, import them from `@systhemaui/react`. The API is the same.

## Variants and tags

The `type` prop picks the marker. `bullet` (the default) and `check` render a `<ul>`, `number` renders an `<ol>`. Use `as` to choose another element.

```tsx preview title="List types"
import { List, ListItem, Paragraph } from '@systhemaui/next'

export default function Demo() {
  return (
    <div className="grid w-full gap-8 sm:grid-cols-3">
      <div>
        <Paragraph type="label">bullet</Paragraph>
        <List type="bullet">
          <ListItem>Header</ListItem>
          <ListItem>Hero</ListItem>
          <ListItem>Footer</ListItem>
        </List>
      </div>
      <div>
        <Paragraph type="label">number</Paragraph>
        <List type="number">
          <ListItem>Install</ListItem>
          <ListItem>Configure</ListItem>
          <ListItem>Sync</ListItem>
        </List>
      </div>
      <div>
        <Paragraph type="label">check</Paragraph>
        <List type="check">
          <ListItem checked>Tokens</ListItem>
          <ListItem checked>Components</ListItem>
          <ListItem checked={false}>Content</ListItem>
        </List>
      </div>
    </div>
  )
}
```

With the default tokens, bullets are en dashes, numbers are followed by a period, and check items show a check mark or a cross.

## Examples

### Check lists

In a `check` list, `checked` decides the marker: `true` shows the check mark, `false` (or no value) the cross. When `checked` is set, the item starts with visually hidden "Checked" or "Unchecked" text, so screen readers announce the state. Change the words with `checkedLabel` and `uncheckedLabel`, for example to translate them.

```tsx preview title="Check list"
import { List, ListItem } from '@systhemaui/next'

export default function Demo() {
  return (
    <List type="check">
      <ListItem checked checkedLabel="Inbegriffen">
        Unbegrenzte Seiten
      </ListItem>
      <ListItem checked checkedLabel="Inbegriffen">
        Eigene Domain
      </ListItem>
      <ListItem checked={false} uncheckedLabel="Nicht inbegriffen">
        Mehrsprachige Inhalte
      </ListItem>
    </List>
  )
}
```

Change the two icons with `blocks.list.checkedIcon` and `blocks.list.uncheckedIcon` (see [Component CSS blocks](https://docs.systhema.app/ar/styling/blocks.md)).

### Nested lists

Put a nested list in its own `ListItem`, the way rich text from the Lexical editor arrives. That item shows no marker and does not count in a numbered list, so the numbering continues after it. Mark the inner items `nested` so they do not run their own scroll reveal inside the parent's.

```tsx preview title="Nested list"
import { List, ListItem } from '@systhemaui/next'

export default function Demo() {
  return (
    <List type="number">
      <ListItem>Install the packages</ListItem>
      <ListItem>
        <List type="bullet">
          <ListItem nested>@systhemaui/core</ListItem>
          <ListItem nested>@systhemaui/next</ListItem>
        </List>
      </ListItem>
      <ListItem>Run the first sync</ListItem>
      <ListItem>Start the dev server</ListItem>
    </List>
  )
}
```

### Custom markers

`CustomList` is a two-column grid: `CustomListItemMarker` in the first column, `CustomListContent` in the second. The marker is sized from `--list-marker-size` and takes the list marker color, so an emoji, a character or an icon all line up.

```tsx preview title="Custom list"
import {
  CustomList,
  CustomListContent,
  CustomListItem,
  CustomListItemMarker,
} from '@systhemaui/next'

const features = [
  { marker: '⚡', text: 'Fluid spacing that scales with the viewport' },
  { marker: '🎨', text: 'Two color systems, switchable per section' },
  { marker: '🧩', text: 'Payload blocks that render the same components' },
]

export default function Demo() {
  return (
    <CustomList>
      {features.map((feature) => (
        <CustomListItem key={feature.text}>
          <CustomListItemMarker aria-hidden="true">{feature.marker}</CustomListItemMarker>
          <CustomListContent>{feature.text}</CustomListContent>
        </CustomListItem>
      ))}
    </CustomList>
  )
}
```

For an SVG marker, give the `<svg>` a size of `1em`; the marker sets its font size to `--list-icon-size`.

```tsx preview title="Icon markers"
import {
  CustomList,
  CustomListContent,
  CustomListItem,
  CustomListItemMarker,
} from '@systhemaui/next'

export default function Demo() {
  return (
    <CustomList>
      {['Free updates for a year', 'Priority support', 'Unlimited projects'].map((text) => (
        <CustomListItem key={text}>
          <CustomListItemMarker>
            <svg
              width="1em"
              height="1em"
              viewBox="0 -960 960 960"
              fill="currentColor"
              aria-hidden="true"
              focusable="false"
            >
              <path d="m233-120 65-281L80-590l288-25 112-265 112 265 288 25-218 189 65 281-247-149-247 149Z" />
            </svg>
          </CustomListItemMarker>
          <CustomListContent>{text}</CustomListContent>
        </CustomListItem>
      ))}
    </CustomList>
  )
}
```

## Props

### `List`

`List` takes the props of the element it renders (`<ul>` or `<ol>`, or the `as` element).

<!-- generated:props @systhemaui/react ListProps -->

| Prop                       | Type                              | Default | Description |
| -------------------------- | --------------------------------- | ------- | ----------- |
| `as`                       | `ElementType`                     | `'ul'`  |             |
| `type`                     | `'bullet' \| 'number' \| 'check'` | -       |             |
| `className`                | `string`                          | -       |             |
| …and all `<ul>` attributes |                                   |         |             |

<!-- /generated -->

### `ListItem`

`checked` is only meaningful in a `check` list. `checkedLabel` defaults to `'Checked'` and `uncheckedLabel` to `'Unchecked'`; they are rendered only when `checked` is set. `nested` and `disableAnimation` both drop the configured `animationClasses`.

<!-- generated:props @systhemaui/react ListItemProps -->

| Prop                       | Type          | Default | Description |
| -------------------------- | ------------- | ------- | ----------- |
| `as`                       | `ElementType` | `'li'`  |             |
| `nested`                   | `boolean`     | -       |             |
| `className`                | `string`      | -       |             |
| `disableAnimation`         | `boolean`     | -       |             |
| `checked`                  | `boolean`     | -       |             |
| `checkedLabel`             | `string`      | -       |             |
| `uncheckedLabel`           | `string`      | -       |             |
| …and all `<li>` attributes |               |         |             |

<!-- /generated -->

### Custom list parts

`CustomList` renders a `<ul>` and takes `disableAnimation`. `CustomListItem` renders an `<li>`, `CustomListItemMarker` and `CustomListContent` render `<span>`s; each takes `as` for another element.

<!-- generated:props @systhemaui/react CustomListProps -->

| Prop                       | Type          | Default | Description |
| -------------------------- | ------------- | ------- | ----------- |
| `as`                       | `ElementType` | `'ul'`  |             |
| `className`                | `string`      | -       |             |
| `disableAnimation`         | `boolean`     | -       |             |
| …and all `<ul>` attributes |               |         |             |

<!-- /generated -->

<!-- generated:props @systhemaui/react CustomListItemProps -->

| Prop                       | Type          | Default | Description |
| -------------------------- | ------------- | ------- | ----------- |
| `as`                       | `ElementType` | `'li'`  |             |
| `className`                | `string`      | -       |             |
| …and all `<li>` attributes |               |         |             |

<!-- /generated -->

<!-- generated:props @systhemaui/react CustomListItemMarkerProps -->

| Prop                         | Type          | Default  | Description |
| ---------------------------- | ------------- | -------- | ----------- |
| `as`                         | `ElementType` | `'span'` |             |
| `className`                  | `string`      | -        |             |
| …and all `<span>` attributes |               |          |             |

<!-- /generated -->

<!-- generated:props @systhemaui/react CustomListContentProps -->

| Prop                         | Type          | Default  | Description |
| ---------------------------- | ------------- | -------- | ----------- |
| `as`                         | `ElementType` | `'span'` |             |
| `className`                  | `string`      | -        |             |
| …and all `<span>` attributes |               |          |             |

<!-- /generated -->

## HTML and CSS

Without React, put a list class on the `<ul>` or `<ol>`. Markers are `::before` pseudo-elements, so the items need no extra markup; add `is-checked` to a checked item in a check list:

```html
<ul class="list-bullet text-body color-body">
  <li>Header</li>
  <li>Footer</li>
</ul>

<ol class="list-number text-body color-body">
  <li>Install</li>
  <li>Sync</li>
</ol>

<ul class="list-check text-body color-body">
  <li class="is-checked"><span class="sr-only!">Checked </span>Tokens</li>
  <li><span class="sr-only!">Unchecked </span>Content</li>
</ul>

<ul class="list-custom text-body color-body">
  <li class="list-custom-entry">
    <span class="list-custom-entry-marker" aria-hidden="true">⚡</span>
    <span class="list-entry-custom-content">Fluid spacing</span>
  </li>
</ul>
```

The marker size, gaps and color come from the `list` tokens (`--list-marker-size`, `--list-icon-size`, `--list-space-x`, `--list-gap-y`, `--color-list-marker`). The list classes also suppress the native marker, so a project rule that sets `list-style-type` cannot show two markers.

<!-- generated:utilities list -->

| Class                       | Styles                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list-bullet`               | `:is(.list-bullet > li, .list-number > li, .list-check > li) { list-style: none; position: relative; padding-left: calc(var(--list-space-x) + var(--list-marker-size)); margin-block: var(--list-gap-y); }`<br>`:is(.list-bullet > li :first-child, .list-number > li :first-child, .list-check > li :first-child) { margin-top: 0; }`<br>`:is(.list-bullet > li :last-child, .list-number > li :last-child, .list-check > li :last-child) { margin-bottom: 0; }`<br>+ 4 more rules |
| `list-number`               | `:is(.list-bullet > li, .list-number > li, .list-check > li) { list-style: none; position: relative; padding-left: calc(var(--list-space-x) + var(--list-marker-size)); margin-block: var(--list-gap-y); }`<br>`:is(.list-bullet > li :first-child, .list-number > li :first-child, .list-check > li :first-child) { margin-top: 0; }`<br>`:is(.list-bullet > li :last-child, .list-number > li :last-child, .list-check > li :last-child) { margin-bottom: 0; }`<br>+ 7 more rules |
| `list-check`                | `:is(.list-bullet > li, .list-number > li, .list-check > li) { list-style: none; position: relative; padding-left: calc(var(--list-space-x) + var(--list-marker-size)); margin-block: var(--list-gap-y); }`<br>`:is(.list-bullet > li :first-child, .list-number > li :first-child, .list-check > li :first-child) { margin-top: 0; }`<br>`:is(.list-bullet > li :last-child, .list-number > li :last-child, .list-check > li :last-child) { margin-bottom: 0; }`<br>+ 7 more rules |
| `is-checked`                | `:is(.list-check > .is-checked)::before { mask-image: url("data:image/svg+xml,…"); }`                                                                                                                                                                                                                                                                                                                                                                                               |
| `list-custom`               | `& { display: grid; align-items: start; grid-template-columns: auto 1fr; column-gap: var(--list-space-x); row-gap: var(--list-gap-y); }`<br>`:is(.list-custom > li:has( > :is(ul, ol))):before { --tw-content: none; }`                                                                                                                                                                                                                                                             |
| `list-custom-entry`         | `display: contents;`                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `list-custom-entry-marker`  | `grid-column-start: 1;`<br>`width: var(--list-marker-size);`<br>`height: var(--list-marker-size);`<br>`display: inline-grid;`<br>`place-items: center;`<br>`font-size: var(--list-icon-size);`<br>`line-height: 1;`<br>`color: var(--color-list-marker);`                                                                                                                                                                                                                           |
| `list-entry-custom-marker`  | `:is(.list-entry-custom-marker > *) { width: 100%; height: 100%; }`<br>`:is(.list-entry-custom-marker > img, .list-entry-custom-marker > svg) { object-fit: contain; object-position: center; }`                                                                                                                                                                                                                                                                                    |
| `list-entry-custom-content` | `grid-column-start: 2;`                                                                                                                                                                                                                                                                                                                                                                                                                                                             |

<!-- /generated -->

## Next.js

`@systhemaui/next` re-exports the list components from `@systhemaui/react` unchanged. They are server-compatible.

## Accessibility

- Bullet and number markers are CSS, and the elements are real `<ul>`, `<ol>` and `<li>`, so screen readers announce the list and its length.
- A check mark is a picture, not text. Set `checked` on every item of a check list so the state is announced, and translate `checkedLabel` and `uncheckedLabel` with the page.
- Hide a decorative custom marker with `aria-hidden="true"`.

See [Lists and counters](https://docs.systhema.app/ar/concepts/accessibility.md#lists-and-counters).

## Related

- [Paragraph](https://docs.systhema.app/ar/components/paragraph.md)
- [Rich text block](https://docs.systhema.app/ar/payload/blocks/rich-text.md)
- [Component CSS blocks](https://docs.systhema.app/ar/styling/blocks.md)
