# List (/docs/list)





## API Reference [#api-reference]

`List` is a moduix typography primitive. Base UI does not provide a matching list
primitive, so the public API stays focused on native `ul`/`ol` semantics, visual
variants, and root `className` composition.

## Basic [#basic]

<Preview cssProperties="listPlaygroundCssProperties">
  <ListExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function ListDemo() {
            return (
              <List className={styles.list}>
                {items.map((item) => (
                  <ListItem key={item}>{item}</ListItem>
                ))}
              </List>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .list {
            width: min(32rem, calc(100vw - var(--spacing-8)));
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            "Use native list semantics.",
            "Keep spacing and typography on the design system scale.",
            "Customize markers with CSS variables.",
          ];
        `}
  </Preview.Data>

  <Preview.CSSProperties>
    {(context) => <ListCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <ListCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`List` is composed as a semantic list root with repeated `li` items. Use `ListItem`
when you want the exported item slot, or native `li` elements when a wrapper component
is unnecessary. The root controls marker style, spacing, and typography tokens.

```text
List (ul | ol)
└─ ListItem
```

| Part       | Role                                                                   |
| ---------- | ---------------------------------------------------------------------- |
| `List`     | Semantic list root that controls marker, spacing, size, and tone.      |
| `ListItem` | Individual item wrapper rendered as native list item semantics (`li`). |

`List` does not use portal-like service layers such as `portal`, `backdrop`, or
`viewport`. The root and direct `li` items are the complete DOM structure.

## Composition [#composition]

Use `as="ul"` or `as="ol"` for list semantics, `marker` for marker visibility/style,
and `size`, `gap`, `tone`, plus `className` for visual customization.

The default marker follows the root element: unordered lists use `disc`, ordered lists
use `decimal`. Pass `marker="none"` for markerless semantic lists or `marker="bullet"`
when marker size, gap, radius, or vertical offset must be styled independently from text.
All direct `li` elements receive root spacing and marker styles. For `marker="bullet"`,
`ListItem` exposes marker/content composition and supports marker styling via
`classNames.marker` and marker content overrides via `marker`.

| Prop     | Values                                                           | Default     |
| -------- | ---------------------------------------------------------------- | ----------- |
| `as`     | `"ul"`, `"ol"`                                                   | `"ul"`      |
| `marker` | `"disc"`, `"decimal"`, `"bullet"`, `"none"`                      | by `as`     |
| `gap`    | `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`, `"2xl"`                  | `"sm"`      |
| `size`   | `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`                           | `"md"`      |
| `tone`   | `"default"`, `"muted"`, `"subtle"`, `"primary"`, `"destructive"` | `"default"` |

## Examples [#examples]

### Ordered [#ordered]

Use `as="ol"` for ordered steps. The marker defaults to decimal for ordered lists.

<Preview>
  <OrderedListExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function OrderedListDemo() {
            return (
              <List as="ol" className={styles.list}>
                {items.map((item) => (
                  <ListItem key={item}>{item}</ListItem>
                ))}
              </List>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .list {
            width: min(32rem, calc(100vw - var(--spacing-8)));
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            "Install the package.",
            "Import the library stylesheet.",
            "Use components in your app.",
          ];
        `}
  </Preview.Data>
</Preview>

### Markerless [#markerless]

Use `marker="none"` when list semantics are useful but visual markers are not.

<Preview>
  <MarkerlessListExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function MarkerlessListDemo() {
            return (
              <List marker="none" className={styles.list}>
                {items.map((item) => (
                  <ListItem key={item}>{item}</ListItem>
                ))}
              </List>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .list {
            width: min(32rem, calc(100vw - var(--spacing-8)));
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            "Markerless item",
            "Another markerless item",
            "Useful for compact metadata groups",
          ];
        `}
  </Preview.Data>
</Preview>

### Native Items [#native-items]

Use native `li` elements when you do not need the exported `ListItem` slot.

<Preview>
  <NativeItemsListExample />

  <Preview.Code>
    {`
          import { List } from "moduix";

          export function NativeItemsListDemo() {
            return (
              <List marker="bullet" className={styles.customBullet}>
                {items.map((item) => (
                  <li key={item}>{item}</li>
                ))}
              </List>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customBullet {
            width: min(32rem, calc(100vw - var(--spacing-8)));
            --list-marker-color: var(--color-chart-2);
            --list-marker-size: 0.5rem;
            --list-marker-gap: var(--spacing-4);
            --list-marker-offset-y: 0.5em;
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            "Use native li elements when a wrapper component is unnecessary.",
            "The root still controls spacing, marker style, size, and tone.",
            "Reach for ListItem when you want the stable item slot.",
          ];
        `}
  </Preview.Data>
</Preview>

### Sizes [#sizes]

Use `size` for visual sizing. Override size-specific CSS variables in your project for responsive typography.

<Preview>
  <ListSizesExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function ListSizesDemo() {
            return (
              <div className={styles.stack}>
                {items.map((item) => (
                  <List key={item.size} size={item.size}>
                    <ListItem>{item.label}</ListItem>
                  </List>
                ))}
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .stack {
            width: min(32rem, calc(100vw - var(--spacing-8)));
            display: flex;
            flex-direction: column;
            gap: var(--spacing-6);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            { size: "xl", label: "Extra-large list item" },
            { size: "md", label: "Medium list item" },
            { size: "xs", label: "Extra-small list item" },
          ] as const;
        `}
  </Preview.Data>
</Preview>

### Gaps [#gaps]

Use `gap` to control vertical spacing between list items.

<Preview>
  <ListGapsExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function ListGapsDemo() {
            return (
              <div className={styles.stack}>
                {groups.map((group) => (
                  <List key={group.gap} gap={group.gap}>
                    {group.items.map((item) => (
                      <ListItem key={item}>{item}</ListItem>
                    ))}
                  </List>
                ))}
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .stack {
            width: min(32rem, calc(100vw - var(--spacing-8)));
            display: flex;
            flex-direction: column;
            gap: var(--spacing-6);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const groups = [
            { gap: "xs", items: ["Extra-small gap", "Second item"] },
            { gap: "lg", items: ["Large gap", "Second item"] },
          ] as const;
        `}
  </Preview.Data>
</Preview>

### Tones [#tones]

Use `tone` when the list should inherit a semantic text color from the design system.

<Preview>
  <ListTonesExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function ListTonesDemo() {
            return (
              <div className={styles.toneGrid}>
                {items.map((item) => (
                  <List key={item.tone} tone={item.tone}>
                    <ListItem>{item.label}</ListItem>
                  </List>
                ))}
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .toneGrid {
            width: min(32rem, calc(100vw - var(--spacing-8)));
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: var(--spacing-4) var(--spacing-6);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            { tone: "default", label: "Default list tone" },
            { tone: "muted", label: "Muted list tone" },
            { tone: "subtle", label: "Subtle list tone" },
            { tone: "primary", label: "Primary list tone" },
            { tone: "destructive", label: "Destructive list tone" },
          ] as const;
        `}
  </Preview.Data>
</Preview>

### Custom Styles [#custom-styles]

Use `marker="bullet"` when marker size, color, gap, or vertical offset must be controlled independently from the text.

<Preview>
  <CustomStylesListExample />

  <Preview.Code>
    {`
          import { List, ListItem } from "moduix";

          export function CustomStylesListDemo() {
            return (
              <List marker="bullet" className={styles.customBullet}>
                {items.map((item) => (
                  <ListItem key={item} classNames={{ marker: styles.glowMarker }}>
                    {item}
                  </ListItem>
                ))}
              </List>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customBullet {
            width: min(32rem, calc(100vw - var(--spacing-8)));
            --list-marker-color: var(--color-chart-2);
            --list-marker-size: 0.5rem;
            --list-marker-gap: var(--spacing-4);
            --list-marker-offset-y: 0.5em;
          }

          .glowMarker {
            box-shadow:
              0 0 0 0.25rem color-mix(in oklab, var(--list-marker-color), transparent 78%),
              0 0.125rem 0.375rem color-mix(in oklab, var(--list-marker-color), transparent 68%);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const items = [
            "Bullet size is independent from text size.",
            "Bullet color and gap are controlled by CSS variables.",
            "Text color remains unchanged.",
          ];
        `}
  </Preview.Data>
</Preview>
