Lists: bullets, markers, and nesting

June 10, 2026 · View on GitHub

addList builds a semantic list node — items plus a marker policy — instead of hand-rolling indented paragraphs. It lives on every flow container (pageFlow, module, section), with a varargs shortcut for the common case and a full ListBuilder lambda for everything else.

Quick lists

section.addList("Crisp vector output", "Deterministic layout", "Snapshot tests");

addList(String...) and addList(List<String>) render bulleted items with the default marker. Leading raw markers in the input ("- item", "• item") are stripped by default so data from mixed sources normalises cleanly; switch that off with normalizeMarkers(false) when the prefix is intentional.

Configured lists

import com.demcha.compose.document.style.DocumentTextStyle;
import com.demcha.compose.font.FontName;

section.addList(list -> list
        .name("Highlights")
        .textStyle(DocumentTextStyle.builder()
                .fontName(FontName.HELVETICA)
                .size(10.5)
                .build())
        .itemSpacing(3)          // gap between items, in points
        .lineSpacing(1.5)        // gap between wrapped lines of one item
        .items("First highlight", "Second highlight", "Third highlight"));

align(TextAlign...), padding(...), and margin(...) round out the block-level controls — the same DocumentInsets conventions as every other builder.

Markers

import com.demcha.compose.document.node.ListMarker;

section.addList(list -> list.dash().items("Dash-marked item"));
section.addList(list -> list.noMarker().items("Markerless row"));
section.addList(list -> list.marker(">").items("Custom prefix"));
section.addList(list -> list
        .marker(ListMarker.custom("✓"))   // explicit ListMarker value
        .items("Tick-marked item"));

bullet(), dash(), and noMarker() are shortcuts over marker(ListMarker). A custom marker can be any string; a trailing space is added automatically. For markerless lists, continuationIndent(" ") sets the prefix used only on wrapped continuation lines, keeping hanging indents readable.

Nested lists

addItem(label, body) opens a child scope: every addItem inside the callback adds a child of that item, recursively. Unset depths fall back to a built-in marker cascade (·), and markerFor(depth, marker) overrides the marker for one depth:

import com.demcha.compose.document.node.ListMarker;

section.addList(list -> list
        .name("Roadmap")
        .markerFor(1, ListMarker.dash())
        .markerFor(2, ListMarker.custom("*"))
        .addItem("Engineering", eng -> eng
                .addItem("Document engine", engine -> engine
                        .addItem("Nested lists")
                        .addItem("Composed table cells"))
                .addItem("Backend SPI", spi -> spi
                        .addItem("DOCX semantic backend")))
        .addItem("Documentation", docs -> docs
                .addItem("Migration guide")
                .addItem("Published ADRs")));

Precedence per depth: an explicit per-item marker wins over markerFor(depth, ...), which wins over the cascade. Mixing flat addItem(String) and nested addItem(label, body) on the same builder is supported — flat items become depth-0 leaves and source order is preserved.

Styled items inside cards

A list is an ordinary block node, so it composes with the usual section chrome — soft panels, accents, spacing:

import com.demcha.compose.document.style.DocumentColor;
import com.demcha.compose.document.style.DocumentInsets;

section.addSection("Checklist", card -> card
        .fillColor(DocumentColor.rgb(248, 244, 234))
        .cornerRadius(8)
        .padding(DocumentInsets.of(10))
        .addList(list -> list
                .marker("✓")
                .itemSpacing(2)
                .items("Tests green", "Docs updated", "Changelog entry")));

For bullets that are drawn shapes rather than text glyphs (diamonds, stars, rating dots), use inline shape runs instead — see the rich-text recipe.

Runnable showcase: NestedListExample (depth cascade, per-depth overrides, mixed flat/nested authoring).