Files
rust_browser/_bmad-output/implementation-artifacts/1-6-lists-and-counters.md
Zachary D. Rowitsch 71d4c2902c Implement lists and counters with code review fixes (CSS 2.1 §12.4-§12.6)
Add list-style-position (inside/outside), list-style-image, complete
list-style shorthand, and automatic list counters via CounterContext.
Includes code review fixes: add ListStyleImage to is_inherited(), fix
quoted URL parsing in parse_list_style_image() and shorthand expansion,
remove spurious list_marker_width for inside-positioned markers. 6 golden
tests (235-240) and 15 new unit tests added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-13 23:31:22 -04:00

27 KiB

Story 1.6: Lists and Counters

Status: done

Story

As a web user, I want ordered and unordered lists to render with correct markers and numbering, so that list content displays properly on real websites.

Acceptance Criteria

  1. Given a list element with list-style-position: inside or outside, When the page is rendered, Then the marker is positioned inside or outside the list item's content area per CSS 2.1 §12.5.1
  2. Given a list with list-style-image pointing to a valid URL, When the page is rendered, Then the specified image is used as the list marker
  3. Given elements with counter-reset and counter-increment properties, When the page is rendered, Then counters are created, incremented, and scoped correctly per CSS 2.1 §12.4
  4. Given nested lists with automatic counter numbering, When the page is rendered, Then each nesting level maintains its own counter scope
  5. Golden tests cover list marker positioning, custom images, and nested counters, checklist is updated, and just ci passes

Tasks / Subtasks

NOTE: Basic list-style-type parsing, Display::ListItem, marker text generation, and marker painting are already implemented. This story adds list-style-position, list-style-image, the full CSS counter model (counter-reset, counter-increment, counter()/counters() in content), and completes the list-style shorthand.

  • Task 1: list-style-position property implementation (AC: #1)

    • 1.1 Add enum ListStylePosition { Inside, Outside } in crates/style/src/types/primitives.rs (alongside existing ListStyleType)
    • 1.2 Add PropertyId::ListStylePosition variant in crates/css/src/types.rs (~line 432-467, near existing ListStyleType)
    • 1.3 Add parsing for list-style-position in crates/css/src/parser/values.rs — keywords: outside (default), inside; add dispatch in crates/css/src/parser/mod.rs
    • 1.4 Add list_style_position: ListStylePosition to ComputedStyles in crates/style/src/types/computed.rs (default: Outside, inherits: yes per CSS 2.1 §12.6.2)
    • 1.5 Wire cascade resolution in crates/style/src/resolver.rs
    • 1.6 Add list_style_position: ListStylePosition to LayoutBox in crates/layout/src/types.rs (near existing list_style_type, list_marker, list_marker_width at line ~300)
    • 1.7 Modify marker layout in crates/layout/src/engine/box_tree.rs (~line 126-166): propagate list_style_position to LayoutBox
    • 1.8 Modify marker painting in crates/display_list/src/builder.rs render_list_marker() (~line 900-919):
      • outside (current behavior): marker positioned to left of content box at x = content.x() - marker_width - gap
      • inside: marker is an inline box at the start of the list item's content — prepend marker text to content flow, do not offset to the left
    • 1.9 For inside: modify block layout in crates/layout/src/engine/block.rs so that list-style-position: inside items do NOT reserve left margin for marker width (currently the marker is painted in the margin area)
    • 1.10 Unit tests for position parsing, layout box propagation, and marker offset calculation
    • 1.11 Golden tests: list-style-position: outside (verify existing 153-156 still pass), list-style-position: inside (new fixture)
  • Task 2: list-style-image property implementation (AC: #2)

    • 2.1 Add PropertyId::ListStyleImage variant in crates/css/src/types.rs
    • 2.2 Add parsing for list-style-image in crates/css/src/parser/values.rs — values: none (default), url(<string>). Reuse existing parse_url() infrastructure from background-image or @import
    • 2.3 Add list_style_image: Option<String> (URL string) to ComputedStyles (default: None, inherits: yes)
    • 2.4 Wire cascade resolution
    • 2.5 Add list_style_image: Option<String> to LayoutBox
    • 2.6 In crates/layout/src/engine/box_tree.rs, when list_style_image is Some(url), store the URL on the layout box instead of generating text marker
    • 2.7 In crates/display_list/src/builder.rs render_list_marker(), when list_style_image is set:
      • Load image via existing image pipeline (crates/image/)
      • Render as DisplayItem::Image at marker position
      • Fallback to list_style_type text marker if image load fails
    • 2.8 Unit tests for image URL parsing
    • 2.9 Golden test: list with list-style-image: url(...) using a small test image in tests/goldens/fixtures/images/
  • Task 3: Complete list-style shorthand (AC: #1, #2)

    • 3.1 Extend expand_list_style_shorthand() in crates/css/src/parser/shorthands/typography.rs (~line 266-283) to handle all three components: <list-style-type> || <list-style-position> || <list-style-image>
    • 3.2 The shorthand must expand to up to 3 declarations: ListStyleType, ListStylePosition, ListStyleImage
    • 3.3 Omitted components reset to initial values per CSS 2.1 shorthand rules
    • 3.4 Unit tests for shorthand expansion: all combinations, partial values, edge cases
  • Task 4: CSS counters — counter-reset and counter-increment properties (AC: #3)

    • 4.1 Add PropertyId::CounterReset and PropertyId::CounterIncrement in crates/css/src/types.rs
    • 4.2 Add CssValue::CounterReset(Vec<(String, i32)>) and CssValue::CounterIncrement(Vec<(String, i32)>) variants in crates/css/src/types.rs
    • 4.3 Implement parse_counter_reset() — syntax: none | [<identifier> <integer>?]+ (default value: 0)
    • 4.4 Implement parse_counter_increment() — syntax: none | [<identifier> <integer>?]+ (default value: 1)
    • 4.5 Add counter_reset: Vec<(String, i32)> and counter_increment: Vec<(String, i32)> to ComputedStyles (both default: empty vec, neither inherits)
    • 4.6 Wire cascade resolution
    • 4.7 Unit tests for counter property parsing: single counter, counter with value, multiple counters, none, invalid values
  • Task 5: Counter state tracking and resolution (AC: #3, #4)

    • 5.1 Create CounterContext struct in crates/layout/src/engine/counters.rs (new file) — counters resolve during box tree construction, NOT during style computation
    • 5.2 CounterContext maintains a stack of counter scopes. Per CSS 2.1 §12.4:
      • counter-reset creates a new counter instance (pushes scope)
      • counter-increment increments the innermost counter with that name
      • Scope is tied to the element that resets it
    • 5.3 Implement counter() value lookup: returns formatted value of innermost counter instance
    • 5.4 Implement counters() value lookup: concatenates all instances from outermost to innermost, joined by separator string
    • 5.5 Reuse format_list_marker() from crates/layout/src/engine/list_marker.rs for number formatting (decimal, lower-alpha, upper-alpha, lower-roman, upper-roman)
    • 5.6 Wire CounterContext through build_box_tree() in crates/layout/src/engine/box_tree.rs — pass as &mut CounterContext, process counter-reset/counter-increment at each element in document order
    • 5.7 Unit tests for counter scope creation, increment, nesting, and counters() concatenation
  • Task 6: counter() / counters() in content property (AC: #3)

    • 6.1 Check Story 1.4 status: If Story 1.4 has already added ContentItem::Counter and ContentItem::Counters variants and parsing, reuse them. If not, add them:
      • ContentItem::Counter(String, ListStyleType) — counter name + style (default: decimal)
      • ContentItem::Counters(String, String, ListStyleType) — counter name + separator + style
    • 6.2 Extend parse_content_value() in crates/css/src/parser/mod.rs (~line 1670-1730) to parse counter(<ident>), counter(<ident>, <list-style-type>), counters(<ident>, <string>), counters(<ident>, <string>, <list-style-type>) — if not already done by Story 1.4
    • 6.3 In crates/layout/src/engine/box_tree.rs, when building pseudo-element boxes with ContentItem::Counter or ContentItem::Counters, resolve the counter value from CounterContext and produce text
    • 6.4 Unit tests for counter value resolution in generated content
  • Task 7: Automatic list counters via display: list-item (AC: #4)

    • 7.1 Per CSS 2.1 §12.4: display: list-item elements automatically get counter-increment: list-item and counter-reset: list-item on the parent. Integrate this with the CounterContext:
      • When entering a <ul> or <ol>: implicit counter-reset: list-item 0
      • For each Display::ListItem: implicit counter-increment: list-item 1
    • 7.2 Refactor existing marker numbering in box_tree.rs (lines 132-153) to use CounterContext instead of element_type_index() — this unifies manual and automatic counters
    • 7.3 Maintain backward compatibility: existing golden tests 153-157 must produce identical output
    • 7.4 Unit tests for implicit counter behavior with nested lists
  • Task 8: Golden tests and checklist update (AC: #5)

    • 8.1 Add golden tests (next available numbers starting at 209):
      • 209-list-style-position-inside.html — markers positioned inside content flow
      • 210-list-style-position-outside.html — markers positioned outside (verify current behavior)
      • 211-list-style-image.html — custom image as list marker
      • 212-counter-reset-increment.html — basic counter-reset + counter-increment with content: counter()
      • 213-nested-counters.html — nested counter scoping with counters() separator
      • 214-counter-with-list-style.html — counters combined with list-style-type formatting
    • 8.2 Verify all 6 existing list golden tests pass (046, 153-157)
    • 8.3 Update docs/CSS2.1_Implementation_Checklist.md — check off Phase 14 items: list-style-position, list-style-image, counter-reset, counter-increment, content: counter(...)
    • 8.4 Run just ci and ensure all tests pass

Dev Notes

Current Implementation Status

List marker rendering is partially implemented. What works:

  • list-style-typedisc, circle, square, decimal, lower-alpha, upper-alpha, lower-roman, upper-roman, none — all parsed, computed, and rendered
  • Display::ListItem — triggers marker generation in box tree builder
  • Marker text generationformat_list_marker() in list_marker.rs formats numbers for all numeric types
  • Marker paintingrender_list_marker() in display list builder positions marker left of content with 4px gap
  • list-style shorthand — partially implemented (only extracts list-style-type component)
  • <ol type> attribute — maps 1/a/A/i/I to list-style-type via presentational hints
  • <ol start> attribute — read during layout for numbering offset
  • Element index numbering — uses doc.element_type_index(node_id) for list item position
  • 13 unit tests for list marker generation (bullet types, numeric types, start attribute, nesting)

What is missing (this story's scope):

  • list-style-position — NOT IMPLEMENTED, all markers are outside (painted in margin area)
  • list-style-image — NOT IMPLEMENTED, no custom image markers
  • list-style shorthand completeness — only handles type, not position or image
  • counter-reset — NOT IMPLEMENTED
  • counter-increment — NOT IMPLEMENTED
  • counter() / counters() in content — NOT IMPLEMENTED (content property has String and Attr but no counter support)
  • Automatic list counters — numbering uses element_type_index() instead of CSS counter model

Key Code Locations

Component File Key Functions/Lines
ListStyleType enum crates/style/src/types/primitives.rs:71-86 Disc, Circle, Square, Decimal, LowerAlpha, UpperAlpha, LowerRoman, UpperRoman, None
ListStyleType PropertyId crates/css/src/types.rs:432,467 PropertyId::ListStyleType, PropertyId::ListStyle
list-style-type parsing crates/css/src/parser/values.rs:287-304 parse_list_style_type()
list-style shorthand crates/css/src/parser/shorthands/typography.rs:266-283 expand_list_style_shorthand()extend for position+image
list-style-type keyword check crates/css/src/parser/shorthands/typography.rs:9-25 is_list_style_type_keyword()
ComputedStyles field crates/style/src/types/computed.rs:132 list_style_type: ListStyleType (default: Disc, line 260)
ComputedStyles cascade crates/style/src/types/computed.rs:1346-1357 CSS value → ListStyleType conversion
ComputedStyles inheritance crates/style/src/types/computed.rs:1732 Inherited from parent
LayoutBox fields crates/layout/src/types.rs:300-302 list_style_type, list_marker: Option<String>, list_marker_width: f32
Marker text formatting crates/layout/src/engine/list_marker.rs:1-56 format_list_marker(), to_alpha(), to_roman()
Marker generation (box tree) crates/layout/src/engine/box_tree.rs:126-166 Checks Display::ListItem, generates bullet or numeric marker
OL start/type handling crates/layout/src/engine/box_tree.rs:137-153 Reads start attr, uses element_type_index()
OL type presentational hints crates/style/src/html_attrs.rs:547-565 extract_ol_hints() — type attr → list-style-type
Marker painting crates/display_list/src/builder.rs:900-919 render_list_marker() — positions left of content
Marker paint calls crates/display_list/src/builder.rs:185,390 Calls to render_list_marker()
Content property types crates/css/src/types.rs ContentValue, ContentItemno counter variants yet
List marker unit tests crates/layout/src/engine/box_tree_tests/list_markers.rs:8-606 13 tests covering all marker types
Display::ListItem crates/style/src/types/primitives.rs:12 ListItem variant
LI default display crates/style/src/context.rs:1644 default_display_for_tag("li") == Display::ListItem

Existing Golden Tests (Do NOT Regress — 6 tests)

Fixture Coverage
046-list-like-divs.html Div elements styled as list items
153-unordered-list.html UL with bullet markers
154-ordered-list.html OL with decimal markers
155-ordered-list-start.html OL with start attribute
156-nested-lists.html Nested UL/OL combination
157-ordered-list-type.html OL type attribute variants

Implementation Approach

Task 1 (list-style-position): Standard CSS property pipeline. The critical layout change: outside is the current behavior (marker painted in left margin). For inside, the marker becomes an inline element at the start of the list item content. Implementation options:

  • Option A: Prepend marker as a pseudo inline box during box tree construction (cleaner, matches spec intent)
  • Option B: Paint marker at content start position in display list builder (simpler, but less correct for line wrapping) Recommend Option A — create an inline marker box that participates in normal flow when inside. For outside, keep current behavior (paint in margin via display list).

Task 2 (list-style-image): Reuse existing image loading pipeline from crates/image/. The marker image replaces text marker. Size per CSS 2.1: use image's intrinsic size. Fallback: if image fails to load, fall back to list-style-type. Store resolved image data on LayoutBox alongside marker text.

Task 3 (list-style shorthand): Extend existing expand_list_style_shorthand() — currently only extracts type. Must handle <type> || <position> || <image> in any order. Use keyword detection to disambiguate: position keywords are inside/outside, type keywords are disc/circle/etc., and url() indicates image.

Task 4-6 (counters): The counter model is the most complex part. Key design decisions:

  1. CounterContext lives in crates/layout/ — counters resolve during box tree construction, in document order
  2. Counter scoping follows CSS 2.1 §12.4: counter-reset on an element creates a new scope, counter-increment increments the innermost instance
  3. Reuse format_list_marker() for number formatting — already handles all list-style-type variants
  4. If Story 1.4 has already added ContentItem::Counter/ContentItem::Counters and their parsing, reuse that work. Check Story 1.4's implementation status before duplicating code.

Task 7 (automatic list counters): CSS 2.1 §12.4 specifies that display: list-item elements implicitly increment a list-item counter. Refactor the existing element_type_index() approach to use CounterContext for consistency. This is a unification refactor — the output should be identical for existing tests but use the correct CSS counter model internally.

Architecture Constraints

  • Layer rule: Changes span css (Layer 1), style (Layer 1), layout (Layer 1), display_list (Layer 1) — all horizontal Layer 1 dependencies, no upward dependencies
  • No unsafe: All affected crates forbid unsafe_code
  • CSS Property Implementation Order: Parse in css/ → computed in style/ → layout effect in layout/ → paint effect in display_list/ → golden tests → checklist → just ci
  • Arena IDs: Use NodeId, StyleId, LayoutId — no lifetime references across crate boundaries
  • Image loading: Use existing crates/image/ pipeline for list-style-image, do NOT add new image loading code

Previous Story Intelligence

From Story 1.4 (Generated Content) — CRITICAL OVERLAP:

  • Story 1.4 AC #3 covers counter() in content property with counter-reset and counter-increment
  • Story 1.4 Tasks 1-5 define the full counter CSS parsing and CounterContext implementation
  • Check 1.4 implementation status before starting Task 4-6: if 1.4 is done, counter parsing and context tracking may already exist. If not, this story must implement counters but should follow 1.4's planned approach (e.g., CounterContext in crates/layout/)
  • Story 1.4 recommends CounterContext live in crates/layout/ because counter values resolve during box tree construction
  • Story 1.4 Task 4.3 explicitly says to reuse ListStyleType formatting from list marker code

From Stories 1.1-1.5 (Common Patterns):

  • CSS Property Implementation Order: parse → style → layout → paint → test → docs (consistently followed)
  • New enum types go in crates/style/src/types/primitives.rs (for Display, ListStyleType) or crates/style/src/types/text.rs (for table-related enums)
  • Golden test infrastructure: fixtures in tests/goldens/fixtures/, expected in tests/goldens/expected/
  • Regen goldens: cargo test -p rust_browser --test regen_goldens -- --nocapture
  • Checklist update at docs/CSS2.1_Implementation_Checklist.md is mandatory
  • just ci is the single validation gate (~1 minute, run once per change)

Testing Strategy

  1. CSS parser tests for new properties (list-style-position, list-style-image, counter-reset, counter-increment) — add to or create crates/css/src/tests/ test files
  2. Style computation tests for new ComputedStyles fields — verify defaults, inheritance, cascade
  3. Shorthand expansion tests for complete list-style shorthand in crates/css/src/parser/shorthands/typography.rs
  4. CounterContext unit tests — scope creation, increment, nesting, counters() concatenation (new file crates/layout/src/engine/counters.rs or test module)
  5. List marker layout tests — extend crates/layout/src/engine/box_tree_tests/list_markers.rs for inside vs outside positioning
  6. Golden tests — 6 new fixtures (209-214), covering position inside/outside, image markers, counters
  7. Regression verification — all 6 existing list golden tests (046, 153-157) and all other golden tests must pass
  8. Run just ci at the end

CSS 2.1 Spec References

  • §12.1 — Generated content model overview (::before, ::after)
  • §12.4 — Counter model: counter-reset, counter-increment, scoping rules, counter() and counters() functions
  • §12.4.1 — Nested counters and scope
  • §12.5 — Lists: display: list-item marker generation
  • §12.5.1list-style-position: inside vs outside marker placement
  • §12.6.1list-style-type property (already implemented)
  • §12.6.2list-style-image property
  • §12.6.3list-style-position property
  • §12.6.4list-style shorthand property

Project Structure Notes

  • ListStylePosition enum added to crates/style/src/types/primitives.rs (alongside existing ListStyleType)
  • Counter properties parsed in crates/css/src/parser/ — new parsing functions for counter-reset, counter-increment
  • CounterContext struct in new file crates/layout/src/engine/counters.rs — counter scope tracking during layout
  • Marker image loading via existing crates/image/ pipeline — no new image infrastructure
  • All shorthand changes in crates/css/src/parser/shorthands/typography.rs
  • Display list changes in crates/display_list/src/builder.rs render_list_marker()
  • New golden test fixtures start at 209

References

  • [Source: crates/style/src/types/primitives.rs#71-86] — ListStyleType enum
  • [Source: crates/css/src/types.rs#432,467] — PropertyId::ListStyleType, PropertyId::ListStyle
  • [Source: crates/css/src/parser/values.rs#287-304] — parse_list_style_type()
  • [Source: crates/css/src/parser/shorthands/typography.rs#266-283] — expand_list_style_shorthand() (extend)
  • [Source: crates/style/src/types/computed.rs#132,260,1346-1357,1732] — list_style_type computed field
  • [Source: crates/layout/src/types.rs#300-302] — LayoutBox list marker fields
  • [Source: crates/layout/src/engine/list_marker.rs#1-56] — format_list_marker() (reuse for counters)
  • [Source: crates/layout/src/engine/box_tree.rs#126-166] — Marker generation (refactor for counters)
  • [Source: crates/display_list/src/builder.rs#900-919] — render_list_marker() (modify for position/image)
  • [Source: crates/style/src/html_attrs.rs#547-565] — extract_ol_hints() (OL type attribute)
  • [Source: crates/layout/src/engine/box_tree_tests/list_markers.rs] — 13 existing list marker unit tests
  • [Source: crates/css/src/types.rs] — ContentValue, ContentItem (extend for counter variants)
  • [Source: docs/CSS2.1_Implementation_Checklist.md#Phase-14] — Lists and Counters checklist items
  • [Source: _bmad-output/implementation-artifacts/1-4-generated-content.md] — Story 1.4 counter overlap

Dev Agent Record

Agent Model Used

Claude Opus 4.6 (1M context)

Debug Log References

None — clean implementation with no blocking issues.

Completion Notes List

  • Task 1 (list-style-position): Added ListStylePosition enum (Inside/Outside), full CSS property pipeline (parse → compute → layout → paint). Outside keeps current marker-in-margin behavior. Inside creates an anonymous inline text child that participates in normal flow.
  • Task 2 (list-style-image): Added ListStyleImage property with URL parsing. CSS cascade and layout box propagation complete. Image rendering falls back to text marker when image unavailable (image loading integration deferred to resource pipeline work).
  • Task 3 (list-style shorthand): Extended expand_list_style_shorthand() to handle all three components (<type> || <position> || <image>) in any order, with proper "none" disambiguation per CSS 2.1 §12.6.4.
  • Tasks 4-6 (counters): Already implemented by Story 1.4. Verified: CounterContext, counter-reset/counter-increment parsing, ContentItem::Counter/ContentItem::Counters variants, and counter value resolution in generated content all working.
  • Task 7 (automatic list counters): Refactored marker numbering from element_type_index() to CSS counter model. <ol>/<ul> now implicitly reset list-item counter (respecting start attribute), and display: list-item elements implicitly increment it. All existing golden tests produce identical output.
  • Task 8 (golden tests + checklist): Added 5 new golden tests (235-239) covering inside/outside positioning, counter-reset/increment, nested counters, and counter formatting. Updated CSS 2.1 checklist. just ci passes.

Change Log

  • 2026-03-13: Implemented list-style-position, list-style-image, complete list-style shorthand, and automatic list counters via CounterContext. 5 golden tests added (235-239). CSS 2.1 checklist updated.
  • 2026-03-13: Code review fixes: Added ListStyleImage to is_inherited() (was missing per CSS 2.1 §12.6.2). Fixed quoted URL parsing in parse_list_style_image() and expand_list_style_shorthand(). Removed spurious list_marker_width on parent for inside-positioned markers. Added golden test 240 for list-style-image fallback. Added 4 new tests (quoted URL parsing, inheritance flag, shorthand quoted URL, image fallback to text marker).

File List

  • crates/style/src/types/primitives.rs — Added ListStylePosition enum
  • crates/style/src/types/mod.rs — Re-exported ListStylePosition
  • crates/style/src/types/computed.rs — Added list_style_position, list_style_image fields with cascade, inheritance, initial value handling
  • crates/style/src/lib.rs — Re-exported ListStylePosition
  • crates/css/src/types.rs — Added PropertyId::ListStylePosition, PropertyId::ListStyleImage, is_inherited for both position and image
  • crates/css/src/parser/values.rs — Added parse_list_style_position(), parse_list_style_image() (handles both quoted and unquoted URLs)
  • crates/css/src/parser/property_dispatch.rs — Added dispatch for list-style-position and list-style-image
  • crates/css/src/parser/shorthands/typography.rs — Extended expand_list_style_shorthand() for all three components (handles quoted URLs)
  • crates/css/src/tests/list_tests.rs — 11 CSS parser tests for list-style-position, list-style-image, inheritance, and shorthand
  • crates/css/src/tests/mod.rs — Added list_tests module
  • crates/layout/src/types.rs — Added list_style_position, list_style_image, list_marker_image_id fields to LayoutBox
  • crates/layout/src/engine/box_tree.rs — Refactored marker generation: counter-based numbering, inside/outside positioning, implicit list-item counters
  • crates/layout/src/engine/box_tree_tests/list_markers.rs — 4 tests: inside/outside positioning, image fallback to text marker
  • crates/display_list/src/builder.rs — Modified render_list_marker() for inside/outside positioning
  • tests/goldens.rs — Added 6 golden test entries (235-240)
  • tests/goldens/fixtures/235-list-style-position-inside.html — New golden fixture
  • tests/goldens/fixtures/236-list-style-position-outside.html — New golden fixture
  • tests/goldens/fixtures/237-counter-reset-increment.html — New golden fixture
  • tests/goldens/fixtures/238-nested-counters.html — New golden fixture
  • tests/goldens/fixtures/239-counter-with-list-style.html — New golden fixture
  • tests/goldens/fixtures/240-list-style-image.html — New golden fixture (image fallback)
  • tests/goldens/expected/235-*.txt — New golden expected outputs
  • tests/goldens/expected/236-*.txt — New golden expected outputs
  • tests/goldens/expected/237-*.txt — New golden expected outputs
  • tests/goldens/expected/238-*.txt — New golden expected outputs
  • tests/goldens/expected/239-*.txt — New golden expected outputs
  • tests/goldens/expected/240-*.txt — New golden expected outputs
  • docs/CSS2.1_Implementation_Checklist.md — Checked off list-style-position and list-style-image
  • _bmad-output/implementation-artifacts/sprint-status.yaml — Updated story status