Files
rust_browser/_bmad-output/implementation-artifacts/1-11-overflow.md
Zachary D. Rowitsch e3c9d0632a Implement CSS 2.1 overflow: scroll/auto with code review fixes (§11.1, §11.1.1)
Add Overflow::Scroll and Auto enum variants, extend clipping to all
non-visible overflow values, implement visual scroll indicators, and
fix positioned children clipping per CSS 2.1 §11.1.1. Code review
fixes include: DRY overflow parsing helper, rasterizer clip consistency,
scroll indicator corner overlap fix, abspos escape in overflow detection
(recovering 6 WPT tests), and additional golden/unit tests.

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

18 KiB

Story 1.11: Overflow

Status: done

Story

As a web user, I want content that exceeds its container to be clipped or scrollable as specified, So that pages with constrained containers display correctly.

Acceptance Criteria

  1. Given an element with overflow: hidden and content exceeding its bounds When the page is rendered Then overflowing content is clipped at the element's padding edge

  2. Given an element with overflow: scroll When the page is rendered Then a scrolling mechanism is provided regardless of whether content overflows

  3. Given an element with overflow: auto and content exceeding its bounds When the page is rendered Then a scrolling mechanism is provided only when content overflows

  4. Given an element with overflow clipping and positioned children When the page is rendered Then clipping is applied correctly to the element's content and descendants per CSS 2.1 §11.1.1

  5. Golden tests cover each overflow value, checklist is updated, and just ci passes.

Tasks / Subtasks

  • Task 1: Add Scroll and Auto variants to Overflow enum (AC: #2, #3)

    • 1.1 Add Scroll and Auto variants to Overflow enum in crates/style/src/types/primitives.rs (~line 101)
    • 1.2 Update apply_declared in crates/style/src/types/computed.rs (~line 953) to handle "scroll" and "auto" keyword values for PropertyId::Overflow, OverflowX, OverflowY
    • 1.3 Add unit tests for parsing and applying scroll and auto values
  • Task 2: Ensure BFC triggering for all non-visible overflow values (AC: #2, #3)

    • 2.1 Verify that overflow: scroll and overflow: auto trigger a new Block Formatting Context, just like overflow: hidden already does
    • 2.2 Check crates/layout/src/engine/ for BFC triggering logic — ensure it checks for Overflow != Visible (not just == Hidden)
    • 2.3 Add tests verifying BFC behavior for overflow: auto and overflow: scroll
  • Task 3: Extend clipping to scroll and auto overflow values (AC: #2, #3)

    • 3.1 In crates/display_list/src/builder.rs (~line 188), update needs_clip to include Overflow::Scroll and Overflow::Auto (currently only checks Overflow::Hidden)
    • 3.2 Similarly update inline formatting context overflow clipping (~line 393)
    • 3.3 Update ancestor clip tracking for positioned elements (~line 546)
    • 3.4 Add display list unit tests for clipping with scroll/auto values
  • Task 4: Visual scroll indicator for overflow: scroll and overflow: auto (AC: #2, #3)

    • 4.1 Add a DisplayItem::ScrollIndicator variant (or similar) to paint a simple scrollbar visual cue when overflow is scroll (always) or auto (when content exceeds container)
    • 4.2 In the display list builder, calculate whether content overflows the container bounds
    • 4.3 Emit scroll indicator display items for the right edge (vertical) and/or bottom edge (horizontal) as needed
    • 4.4 In crates/render/src/rasterizer/mod.rs, render the scroll indicator as a simple rectangular track + thumb
    • 4.5 Note: Actual interactive scrolling is NOT in scope for this story — only the visual rendering of scrollbars. Interactive scrolling belongs in Epic 5 (Story 5-5: Page Scrolling).
  • Task 5: Positioned children clipping correctness (AC: #4)

    • 5.1 Review CSS 2.1 §11.1.1 rules for overflow clipping of positioned descendants
    • 5.2 Verify that overflow: hidden/scroll/auto on a non-positioned ancestor does NOT clip position: absolute children whose containing block is a higher ancestor (per spec, clipping applies only to descendants whose containing block is the overflow element or a descendant of it)
    • 5.3 Verify that overflow: hidden/scroll/auto on a positioned element (position: relative/absolute/fixed) DOES clip its positioned descendants
    • 5.4 Check/fix the ancestor clip logic in crates/display_list/src/builder.rs (~line 546) to properly handle this distinction
    • 5.5 Add golden tests for positioned children with overflow clipping edge cases
  • Task 6: Golden tests and documentation (AC: #5)

    • 6.1 Verify existing golden fixtures (059-overflow-hidden.html, 060-overflow-nested.html, 179-z-index-overflow-clip.html) still pass
    • 6.2 Create new golden test fixture for overflow: scroll visual rendering
    • 6.3 Create new golden test fixture for overflow: auto with overflowing content
    • 6.4 Create new golden test fixture for overflow: auto with non-overflowing content (no scrollbar)
    • 6.5 Create golden test fixture for positioned children clipping edge case
    • 6.6 Update docs/CSS2.1_Implementation_Checklist.md — check off overflow items in relevant phases
    • 6.7 Run just ci 2>&1 | tee /tmp/ci-output.txt and fix any issues

Dev Notes

CSS 2.1 Spec References

  • §11.1 overflow property: applies to block containers and elements with overflow other than visible. Values: visible | hidden | scroll | auto. Initial value: visible. Not inherited.
  • §11.1.1 Clipping: "When an element's box has overflow other than visible, the box acts as a clipping region for its content." For positioned descendants, clipping only applies if the overflow element is the containing block (or an ancestor of the containing block).
  • §9.4.1 BFC establishment: "Floats, absolutely positioned elements, block containers that are not block boxes, and block boxes with overflow other than visible establish new block formatting contexts."

Current Overflow Pipeline State

Already working (DO NOT reimplement):

  • PropertyId::Overflow, OverflowX, OverflowY — CSS parsing, shorthand expansion via expand_logical_pair_shorthand()
  • ComputedStyles.overflow_x, overflow_y fields — style computation for visible and hidden
  • LayoutBox.overflow_x, overflow_y — layout propagation
  • DisplayItem::PushClip { rect } / PopClip — clip stack in display list
  • Rasterizer clip stack — apply_clip_stack() in crates/render/src/rasterizer/mod.rs
  • overflow: hidden fully clips content end-to-end including nested overflow contexts
  • BFC triggering for non-visible overflow (already checks != Overflow::Visible in layout)
  • Golden tests: 059-overflow-hidden, 060-overflow-nested, 110-float-overflow-contain, 121-float-nested-bfc, 179-z-index-overflow-clip

Must be added:

  • Overflow::Scroll and Overflow::Auto enum variants
  • "scroll" and "auto" keyword recognition in apply_declared
  • Clipping for scroll/auto (update needs_clip check from == Hidden to != Visible)
  • Visual scroll indicator rendering (non-interactive)
  • Positioned children clipping correctness per §11.1.1

Key Code Locations

Component File What to modify
Overflow enum crates/style/src/types/primitives.rs (~line 101) Add Scroll, Auto variants
apply_declared crates/style/src/types/computed.rs (~line 953) Add "scroll" and "auto" keyword arms
Display list clipping crates/display_list/src/builder.rs (~line 188) Change == Hidden to != Visible for needs_clip
Inline clipping crates/display_list/src/builder.rs (~line 393) Same pattern: != Visible
Positioned clip tracking crates/display_list/src/builder.rs (~line 546) Verify positioned descendant logic
Rasterizer crates/render/src/rasterizer/mod.rs (~line 58) Add scroll indicator rendering
Display item enum crates/display_list/src/lib.rs (~line 68) Add ScrollIndicator variant
Layout BFC check crates/layout/src/engine/ Verify != Visible check (likely already correct)
Shorthand tests crates/css/src/parser/shorthands/mod.rs (~line 919) Existing tests cover scroll/auto parsing
Golden fixtures tests/goldens/fixtures/ New HTML fixtures for scroll/auto
Golden expected tests/goldens/expected/ Expected layout/display list outputs
Checklist docs/CSS2.1_Implementation_Checklist.md Update overflow items

Architecture Constraints

  • No unsafe code — all changes in Layer 1 crates only (style, display_list, render, layout)
  • Phase-based mutation — overflow computed in style phase, clipping applied in display list phase, rendered in paint phase. No back-references.
  • Arena IDs — overflow properties flow through the pipeline via ComputedStyles → LayoutBox → display list builder logic. No shared mutable state.
  • Scroll indicator is visual only — actual interactive scrolling (scroll offset, event handling) is out of scope. That belongs in Epic 5 (Story 5-5: Page Scrolling) which involves the event loop in app_browser (Layer 3).

Implementation Pattern (Follow Exactly)

Follow the established CSS property implementation order:

  1. Enum in style/types/primitives.rs — add Scroll and Auto variants to existing Overflow enum
  2. Compute in style/types/computed.rs — add "scroll" and "auto" keyword handling in apply_declared
  3. Layout verify in layout/ — confirm BFC triggering already handles new variants (should work since existing check is != Visible)
  4. Display list in display_list/builder.rs — update needs_clip to != Visible, add scroll indicator emission
  5. Render in render/rasterizer/ — render scroll indicator display items
  6. Positioned clipping in display_list/builder.rs — verify/fix §11.1.1 compliance
  7. Golden tests in tests/goldens/ — new fixtures + expected outputs
  8. Checklist updatedocs/CSS2.1_Implementation_Checklist.md
  9. CI validationjust ci 2>&1 | tee /tmp/ci-output.txt

Scroll Indicator Implementation Strategy

CSS 2.1 §11.1 says overflow: scroll "should" indicate a scrolling mechanism. Since actual interactive scrolling is deferred to Epic 5:

  • Render a minimal visual scrollbar (e.g., a thin rectangular track on the right/bottom edge of the overflow container)
  • For scroll: always show the scrollbar track (even if content doesn't overflow)
  • For auto: show the scrollbar track only if content exceeds the container's padding box
  • Track dimensions: ~8px wide for vertical, ~8px tall for horizontal
  • Track color: use a neutral gray (e.g., #cccccc track, #888888 thumb)
  • Thumb size proportional to visible content ratio (or a fixed small rectangle if no overflow)
  • This is intentionally minimal — a full scrollbar widget is a later concern

Positioned Children Clipping Strategy

CSS 2.1 §11.1.1 key rule: An element's overflow clipping applies to its descendants, BUT for absolutely positioned elements, the containing block relationship determines whether clipping applies:

  • If the overflow element establishes a containing block (has position: relative/absolute/fixed), then its absolutely positioned descendants ARE clipped
  • If the overflow element is NOT a containing block for the positioned element, the positioned element "escapes" the clip
  • The display list builder's ancestor_clips tracking (~line 546) needs to correctly handle this: when building display items for an absolutely positioned element, only include clips from ancestors that are containing blocks (or descendants of the containing block)

Previous Story Learnings

From story 1-10 (Font Properties) and earlier stories:

  • Follow the enum + apply_declared + golden test pattern exactly
  • New enum types go in crates/style/src/types/primitives.rs (see Visibility, Overflow enums)
  • Golden test fixtures: tests/goldens/fixtures/NNN-descriptive-name.html (sequential numbering)
  • Regen goldens: cargo test -p rust_browser --test regen_goldens -- --nocapture
  • Handle inherit, initial, unset keywords (overflow is NOT inherited, but inherit/initial/unset must still be handled)
  • just ci must pass clean at the end

Git Intelligence

Recent relevant commits:

  • 7c8d2b8 — :link/:visited pseudo-class support (shows style computation pattern)
  • 077426b — Flex row overflow containment fix (shows overflow interaction with flex layout)
  • Existing overflow golden tests added in earlier milestones (059, 060, 110, 121, 179)

Testing Strategy

  1. Unit tests for Overflow enum: parse scroll and auto (in style or CSS tests)
  2. Unit tests for apply_declared with new overflow keywords
  3. Display list tests for clipping with scroll/auto values
  4. Golden tests — minimum 4 new fixtures:
    • overflow: scroll with content (always show scrollbar)
    • overflow: auto with overflowing content (show scrollbar)
    • overflow: auto without overflowing content (no scrollbar)
    • Positioned children with overflow clipping edge case
  5. Regression tests — verify existing overflow golden tests (059, 060, 110, 121, 179) still pass unchanged

Project Structure Notes

  • All changes span Layer 1 crates only — no cross-layer violations
  • Main work in style/, display_list/, render/ crates
  • Layout changes minimal (BFC already handles non-visible overflow)
  • No new external dependencies needed
  • Existing DisplayItem::PushClip/PopClip mechanism reused for scroll/auto

References

  • [Source: CSS 2.1 §11.1 — Overflow property specification]
  • [Source: CSS 2.1 §11.1.1 — Overflow clipping rules for positioned descendants]
  • [Source: CSS 2.1 §9.4.1 — BFC establishment rules]
  • [Source: docs/CSS2.1_Implementation_Checklist.md — Overflow implementation status]
  • [Source: crates/style/src/types/primitives.rs — Overflow enum definition]
  • [Source: crates/style/src/types/computed.rs — apply_declared for overflow]
  • [Source: crates/display_list/src/builder.rs — PushClip/PopClip emission, ancestor clip tracking]
  • [Source: crates/render/src/rasterizer/mod.rs — Clip stack rendering]
  • [Source: crates/css/src/parser/shorthands/mod.rs — Overflow shorthand expansion + tests]
  • [Source: _bmad-output/project-context.md — Full project rules and patterns]

Dev Agent Record

Agent Model Used

Claude Opus 4.6 (1M context)

Debug Log References

  • WPT abspos-overflow tests (001-003, 007-009) restored to pass after code review fixed abspos escape in scroll indicator overflow detection
  • WPT abspos-overflow-010 remains known_fail (overflow:auto on auto-sized div incorrectly detects inline content overflow)
  • WPT flexbox-min-width-auto-003 and overflow-scroll-float-paint-order promoted from known_fail to pass
  • WPT table-cell-007 remains known_fail due to scroll indicator pixel mismatch (overflow-y:scroll on table cell)

Completion Notes List

  • Added Overflow::Scroll and Overflow::Auto enum variants with full CSS parsing support (keyword and CssValue::Auto)
  • BFC triggering already used != Visible check — no changes needed, just verified + added tests
  • Updated all overflow clipping in display list builder from == Hidden to != Visible
  • Added DisplayItem::ScrollIndicator with track/thumb rendering in both rasterizer paths
  • Scroll indicators: 8px wide, #cccccc track, #888888 thumb, proportional thumb sizing
  • overflow: scroll always shows scrollbar; overflow: auto only when content overflows
  • Implemented CSS 2.1 §11.1.1 positioned children clipping: non-positioned overflow elements no longer incorrectly clip absolutely positioned descendants whose containing block is above them
  • 4 new golden tests (263-266) covering scroll, auto-overflow, auto-no-overflow, and positioned children
  • Updated CSS 2.1 implementation checklist
  • just ci passes clean

Code Review Fixes (2026-03-14):

  • Extracted triplicated overflow parsing logic into parse_overflow_value() helper
  • Fixed rasterizer clip inconsistency: first rasterize() path now applies clip stack to ScrollIndicator items
  • Fixed scroll indicator corner overlap: vertical/horizontal tracks now leave a gap when both are shown
  • Fixed scroll indicator overflow detection: abspos children that escape container are now excluded from extent calculation
  • Recovered 6 WPT tests (abspos-overflow 001-003, 007-009) from known_fail back to pass
  • Added golden test 267 for CSS 2.1 §11.1.1 abspos-escapes-non-positioned-overflow case
  • Added 3 new ScrollIndicator unit tests (emission for scroll, auto+overflow, auto+no-overflow)

Change Log

  • 2026-03-14: Implemented overflow: scroll and auto (Story 1.11)
  • 2026-03-14: Code review fixes — DRY overflow parsing, rasterizer clip fix, scroll indicator corner/abspos fixes, +6 WPT recoveries, +1 golden test, +3 unit tests

File List

  • crates/style/src/types/primitives.rs (modified — added Scroll, Auto to Overflow enum)
  • crates/style/src/types/computed.rs (modified — added scroll/auto/CssValue::Auto handling in apply_declared)
  • crates/style/src/tests/positioning.rs (modified — added 4 overflow tests, updated dual-value test)
  • crates/layout/src/engine/block/tests.rs (modified — added BFC tests for scroll/auto)
  • crates/display_list/src/lib.rs (modified — added ScrollIndicator variant + dump + scale)
  • crates/display_list/src/builder.rs (modified — !Visible clipping, scroll indicator emission, §11.1.1 positioned clip fix)
  • crates/display_list/src/tests/clipping_tests.rs (modified — added scroll/auto clipping tests)
  • crates/render/src/rasterizer/mod.rs (modified — added ScrollIndicator rendering in both rasterize paths)
  • tests/goldens.rs (modified — registered 4 new golden tests)
  • tests/goldens/fixtures/263-overflow-scroll.html (new)
  • tests/goldens/fixtures/264-overflow-auto-overflow.html (new)
  • tests/goldens/fixtures/265-overflow-auto-no-overflow.html (new)
  • tests/goldens/fixtures/266-overflow-positioned-children.html (new)
  • tests/goldens/expected/263-overflow-scroll.dl.txt (new)
  • tests/goldens/expected/263-overflow-scroll.layout.txt (new)
  • tests/goldens/expected/264-overflow-auto-overflow.dl.txt (new)
  • tests/goldens/expected/264-overflow-auto-overflow.layout.txt (new)
  • tests/goldens/expected/265-overflow-auto-no-overflow.dl.txt (new)
  • tests/goldens/expected/265-overflow-auto-no-overflow.layout.txt (new)
  • tests/goldens/expected/266-overflow-positioned-children.dl.txt (new)
  • tests/goldens/expected/266-overflow-positioned-children.layout.txt (new)
  • tests/goldens/fixtures/267-overflow-abspos-escape.html (new — §11.1.1 escape case)
  • tests/goldens/expected/267-overflow-abspos-escape.dl.txt (new)
  • tests/goldens/expected/267-overflow-abspos-escape.layout.txt (new)
  • tests/external/wpt/wpt_manifest.toml (modified — promoted 2+6 tests, 2 remain known_fail)
  • docs/CSS2.1_Implementation_Checklist.md (modified — checked off overflow items)
  • _bmad-output/implementation-artifacts/sprint-status.yaml (modified — status updates)
  • _bmad-output/implementation-artifacts/1-11-overflow.md (modified — task checkboxes, dev record)