Skip to content

Layout Phase 1 — decomposed components + 8-step pipeline + hybrid Style builder#29

Merged
intendednull merged 18 commits into
mainfrom
claude/v01-layout-foundation
May 9, 2026
Merged

Layout Phase 1 — decomposed components + 8-step pipeline + hybrid Style builder#29
intendednull merged 18 commits into
mainfrom
claude/v01-layout-foundation

Conversation

@intendednull
Copy link
Copy Markdown
Owner

Summary

  • Replaces Phase 0's flat-mega-Style with a hybrid Style builder (struct-literal + fluent setters over the same fields) that decomposes into BoxModel, Display, Position, FlexParams on insert.
  • Installs the spec's 8-step BuiyLayoutStep pipeline as ordered sub-sets of BuiySet::Layout. Phase 1 implements steps 0/1/3/7 (RemovedNodesGc, SyncStyles, TaffyCompute, WriteResolvedLayout); steps 2/4/5/6 are wired empty stubs for later phases (CqActivate, CqFlipCheck, CqFlipReRun, PostTaffyOverrides) so adding container queries / sticky / table / multicol / anchor doesn't reorder anything.
  • sync_styles carries the spec's Or<(Changed<Display>, Changed<BoxModel>, Changed<Position>, Changed<FlexParams>, Changed<FlexItem>, Changed<Children>, Changed<ChildOf>)> filter, so steady-state frames iterate zero entities (matches spec § 9 O(0) contract).
  • Adds components::Visual (background_token, foreground_token, border_radius) carrying the render-side surface that used to leak into the layout Style. Button::new inserts both the layout Style builder and a Visual with the same theme tokens — visual appearance preserved.
  • Spec 2026-05-08-buiy-layout-design flips draft → active; this plan is the first in a series of phase plans (Phase 2-10 sketched in the plan's "Phasing strategy" table — overflow/scroll, grid, writing-modes, container queries, anchors, sticky/table/multicol, stacking + top-layer, transforms, full unit resolution).

Spec: docs/specs/2026-05-08-buiy-layout-design/. Plan: docs/plans/2026-05-08-buiy-layout-foundation.md (12 tasks, all landed across 18 commits).

Test plan

  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps — clean
  • cargo test --workspace — 71 passing, 0 failing (up from Phase 0's ~50)
  • cargo build --example hello_button — clean (visual appearance preserved via the Visual component)
  • One human eyeball on cargo run --example hello_button to confirm the rendered button matches Phase 0 (CI doesn't have a GPU)
  • Smoke-merge confidence: re-run cargo test --workspace post-merge to confirm no rebase artifacts

What's NOT in this PR (Phase 2+)

  • Anchor, GridParams/GridItem, MultiColumn, Container, WritingMode, Overflow, Scroll, Stacking, Transform, Containment components
  • Sub-passes 6a/6b/6c/6d (sticky / table / multicol / anchor)
  • Full unit resolution (em/rem/viewport/container/Fr/Calc) — Phase 1 ships only Px + Percent
  • LogicalBoxModel insert helper / writing-mode inheritance pass

Each is tracked in the plan's "Phasing strategy" table with explicit dependencies.

🤖 Generated with Claude Code

intendednull and others added 18 commits May 8, 2026 20:28
Phase 1 of the layout migration: replace Phase 0's mega-Style with a
hybrid Style builder over decomposed components, install the 8-step
BuiyLayoutStep pipeline skeleton, keep all existing tests + hello_button
green. Add a Visual component (background_token / foreground_token /
border_radius) carrying render-side fields formerly mixed into Style;
eventual home is buiy-render-pipeline-design.

12 TDD-shaped tasks. Three-reviewer swarm (spec coverage, plan quality,
Bevy/Taffy API correctness) ran against the draft; BLOCKERS for the
render-module break and missing change-detection filter on sync_styles
addressed before commit.

Spec: docs/specs/2026-05-08-buiy-layout-design/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… axis enums

Spec: docs/specs/2026-05-08-buiy-layout-design/{box-model,display-and-positioning,flex-and-grid}.md.
Phase 1 of the layout migration. Ships only Length::Px/Percent and
the Sizing/Edges/BoxSizing/FlexAxis/PositionKind shapes; richer
unit resolution (em/rem/viewport/container/calc) lands in Phase 10.

Plan deviation: the plan called for creating a new layout/mod.rs in
this task, but Rust's module system (E0761) disallows both layout.rs
and layout/mod.rs under one 'pub mod layout;'. The submodule lands
via 'mod types;' inside the existing layout.rs; Task 7 transitions
to the directory module by deleting layout.rs.

Two impl blocks (Length, Edges) carry a narrow #[allow(dead_code)]
until Task 3 wires the Style builder; comments mark when the allows
come off.
…ms/AlignContent

Code-review follow-up to commit 1377bbc — three public enums in
layout::types lacked doc comments while their neighbors (FlexAxis,
FlexWrap, PositionKind) had them. Brief 1-2 line doc each citing the
CSS property the variant set models.
After executing Task 1:
- E0761 forbids layout.rs and layout/mod.rs coexisting; Tasks 1-6 add
  'mod xxx;' to the existing layout.rs, and Task 7 transitions to a
  directory module by deleting layout.rs.
- Phase 1 helper methods (Length::px, Edges::all, etc.) lack production
  callers until Task 3+; narrowly-scoped #[allow(dead_code)] on the
  impl blocks keeps clippy -D warnings clean.
- clippy derivable_impls prefers #[derive(Default)] + #[default] over
  manual impl Default for enums with a clearly-defaulted variant.
- Three pub enums (JustifyContent / AlignItems / AlignContent) now
  carry brief CSS-citation doc comments (caught by code review).

Step 1.1, 1.4, 1.7 and the New-files block updated to match.

Tasks 2-6's "Update layout/mod.rs" steps will be corrected in a
follow-up patch as those tasks come up for execution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…, FlexParams, FlexItem

Spec: docs/specs/2026-05-08-buiy-layout-design/architecture.md § 2.1.
Phase 1 of the layout migration. Phase-0-equivalent surface only:
the components Phase 0's mega-Style touched. Other components
(Anchor, Grid*, Container, WritingMode, Overflow, Scroll, Stacking,
Transform, Containment, MultiColumn) land in later phase plans.

Display ships every spec variant for forward stability; Phase 1
translates non-{Block, Flex(_), InlineFlex(_), None} variants to
Block in Task 4 (translate.rs). Position::Fixed and Position::Sticky
similarly defer to Phases 7/8.

mod types and mod components are promoted to 'pub mod' so Task 3+
reach them via crate::layout::{components, types} from outside the
layout module.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code-review follow-up to commit 4ef1be5. Phase 0 puts Default right
after Reflect (see crates/buiy_core/src/components.rs:26,32,55 and
focus.rs:28); Tasks 1-2 deviated by placing Default at the end of
the derive list. Reorder mechanically across types.rs (Task 1) and
components.rs (Task 2) — no semantic change.

This pattern is the convention going forward for all Phase 1 layout
modules.
Code-review follow-up to commit 1bf342e. Phase 0 puts Default right
after Reflect (or after Component, Reflect); Tasks 1-2 deviated by
placing Default later. Plan code blocks for types.rs and components.rs
updated to match Phase 0's ordering, so Tasks 3-7 inherit the
convention without re-running the same fix loop.

Display also flipped from manual 'impl Default { Block }' to
'#[derive(Default)]' + '#[default] Block' (clippy derivable_impls).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…osed components

Spec: docs/specs/2026-05-08-buiy-layout-design/architecture.md § 2.2-2.4.
Style is a Bevy Bundle whose default expands to defaulted decomposed
components; fluent setters write the same fields a struct literal
would. Phase 1 simplification: every component is always inserted
(default-valued or set), to keep the Bundle derive simple. Phase 4
revisits this when LogicalBoxModel needs skip-on-default semantics.

FlexItem stays decomposed-only per the child-side convention; it's
spawned alongside Style, not inside it.
Spec: docs/specs/2026-05-08-buiy-layout-design/architecture.md § 1.2.
Pure function over a borrowed view of the Phase 1 component set.
Phase 1 unit resolution is Px + Percent only; intrinsic / em / rem
/ viewport / container / Fr / Calc are reserved for Phase 10.

Display variants outside {Block, Flex(_), InlineFlex(_), None}
translate to Block; Grid/InlineGrid included until Phase 3 wires
GridParams/GridItem. Position::Fixed / Sticky translate to
Absolute / Relative until Phases 7/8 land the real semantics.
FlexItem.order is stored but not honored (Taffy 0.10 has no
Style.order field).
… config

Spec: docs/specs/2026-05-08-buiy-layout-design/architecture.md § 3.
Eight ordered sub-sets of BuiySet::Layout. Steps 0/1/3/7 do real
work in Phase 1; steps 2/4/5/6 are wired as empty sub-sets that
later phases populate without reordering. Order is enforced by a
test added in Task 10.

configure_pipeline is dead code until Task 7's LayoutPlugin calls
it; narrow #[allow(dead_code)] keeps clippy quiet, removed in 7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…layout root

Code-review follow-up to commit 7820a45. Plan's Step 5.2 calls for
flat re-exports so external callers reach crate::layout::BuiyLayoutStep
(and the internal LayoutPlugin reaches configure_pipeline) without
the pipeline:: namespace. Two-line addition; the wider re-export of
Tasks 1-3's submodule items is held back until Task 7's transition
deletes layout.rs (Phase 0's 'use crate::components::Style' would
collide with 'pub use style::Style' at the crate-public level).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rustfmt drift inherited from Task 3 (commit 1cb0ada) and Task 4
(commit b4c6df6) — those commits were verified against clippy
-D warnings + cargo check, but cargo fmt --check was not part of
the per-task verification gate. CI runs the full check chain
(fmt + clippy + doc + test) per CLAUDE.md, so this needs to be
fmt-clean before Phase 1 lands.

Pure formatting; no semantic change. cargo fmt --all -- --check
clean after this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same struct as Phase 0, moved into its target location with crate-
visible field access (Phase 0's were private). Old layout.rs still
owns the live LayoutTree resource until Task 7 swaps the plugin
over; the new tree.rs is namespaced as crate::layout::tree::LayoutTree
to avoid collision with Phase 0's pub struct LayoutTree.
…ecomposed components

Phase 1 of the layout migration. Tasks 7+8 (atomic) + Task 11
test migration (picking.rs, picking_backend.rs migrated inline since
they reference the deleted Style from components).

- Delete crates/buiy_core/src/layout.rs (flat Phase 0 module).
- Delete components::Style and components::FlexDirection.
- Add layout/mod.rs with LayoutPlugin registering decomposed components
  and chaining the 8-step pipeline (steps 0/1/3/7 active, 2/4/5/6
  empty stubs for later phase plans).
- Add layout/systems.rs with gc_removed_nodes, sync_styles,
  taffy_compute, write_resolved_layout (all pub(super)).
- Add components::Visual (background_token, foreground_token,
  border_radius) carrying the render-side fields formerly on the
  mega-Style; eventual home is buiy-render-pipeline-design.
- Migrate the render extract to query (&Visual, &ResolvedLayout)
  so the workspace continues to compile + render after Style's
  deletion.
- Migrate Button::new to Style::default().width_px(120.0)
  .height_px(32.0).padding(8.0) plus a Visual carrying the three
  theme tokens; visual appearance preserved.
- Style is now a Bundle (decomposes on insert) rather than a
  reflectable Component; reflection now sees the decomposed
  components.
- Re-export decomposed components, the Style builder, and Visual
  from buiy + buiy_core.
- Migrate tests/layout.rs and tests/components.rs (Task 11) to new
  Style builder + Visual API; migrate tests/picking.rs and
  tests/picking_backend.rs (Style removal).

All tests pass: 0 failures across workspace.

Spec: docs/specs/2026-05-08-buiy-layout-design/.
Plan: docs/plans/2026-05-08-buiy-layout-foundation.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…he consumers

Code-review follow-up to commit ac33faa. The #[allow(dead_code)] /
#[allow(unused_imports)] attributes added during Tasks 4/5/6 were
gated on 'until Task 7 wires this in' — Task 7 has now wired
LayoutPlugin / sync_styles / configure_pipeline to consume them.
Drop the stale suppressions; clippy stays clean because the
items are now reachable through the live system schedule.

- pipeline.rs: drop #[allow(dead_code)] on configure_pipeline
  (LayoutPlugin::build calls it).
- translate.rs: drop module-level #![allow(dead_code)]
  (style_to_taffy / StyleView called from sync_styles).
- tree.rs: drop module-level #![allow(dead_code)]; also remove
  the unused taffy_node_for helper (systems access by_entity
  directly per Correction 2 in the Task 7+8 dispatch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…valence

Spec: docs/specs/2026-05-08-buiy-layout-design/architecture.md § 8 tests #1, #3, #4
+ box-model.md § 6.

- layout_topology: parent-resolves-before-children invariant on a
  4-deep tree (architecture.md § 5).
- layout_pipeline_order: tracker systems in each BuiyLayoutStep set
  fire in 0..7 order (architecture.md § 3).
- layout_box_sizing: ContentBox produces 120px total for width 100 +
  padding 10/side; BorderBox produces 100px total (box-model.md § 2.2).
- layout_style_equivalence: struct literal and fluent builder forms
  produce identical ResolvedLayout after one Update (architecture.md § 2.2).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ctive

CHANGELOG records the Phase 0 mega-Style removal, the new layout/
module with 8-step pipeline, the hybrid Style builder, and the
Visual component carrying the render-side surface.

Spec README moves from draft to active. Spec § 4.1 now points at
the Phase 1 plan and clarifies that the migration is a series of
plans (other phases tracked in the plan's phasing table).

docs/README.md index entries for both spec and plan flip from draft
to active.
Final-review follow-ups to commits e19779d (docs) and f3b3eef
(allows cleanup):

- docs/plans/2026-05-08-buiy-layout-foundation.md header bumped from
  draft to active; e19779d's commit message promised this flip but
  the file was missed in the diff.
- Drop three stale #[allow(dead_code)] annotations whose comments
  promised they'd be removed once consumers landed: impl Length
  (Edges::ZERO uses Length::ZERO; px/percent are pub API for end
  users — pub items don't trigger dead_code anyway), impl Edges
  (Edges::all is called from style.rs's padding/margin/border
  setters; ZERO is used by Edges::default), impl Style (button.rs
  uses width_px/height_px/padding; tests use flex_column).

cargo clippy --workspace --all-targets -- -D warnings still clean
after removal — the allows were not suppressing real warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@intendednull intendednull merged commit f361143 into main May 9, 2026
6 checks passed
@intendednull intendednull deleted the claude/v01-layout-foundation branch May 9, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant