feat(layout): WPF-style Rows/Columns/Row/Column/Fixed*/Flex*/ScrollViewer #23
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/layout-components"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A shared layout vocabulary for the Kagaku apps, replacing the ~20-28% of scoped-CSS rules that do nothing but flex layout (measured across Kagura/Orihon/Tsumugi @ main — 41-47% of all scoped-CSS declarations are layout-only).
Parents —
Rows(vertical) /Columns(horizontal):Gap—Spacingenum (None/Tiny/Small/Medium/Large/Huge) →var(--space-1…6); host token overrides re-theme every gapAlign(cross-axis) /Justify(main-axis) — sharedAlignmentenum (Start/Center/End/Stretch/Baseline), defaults Stretch/Start like WPFChildren, typed by sizing mode:
Row/Column— stretch, equal star share (flex:1 1 0%)FixedRow Height="…"/FixedColumn Width="…"— bare number = rems,var(…)/unit values verbatim so tokens stay liveFlexRow Flex="2"/FlexColumn— star-ratio sizing (basis 0: pure ratios regardless of content)ScrollViewer— the app-shell's inner scroll region (ADR 0025 model),OrientationVertical default/Horizontal/BothAll plain inline-style divs — no scoped CSS, no JS, prerender-safe.
min-height:0; min-width:0baked into every child so the flexbox scroll trap is pre-solved. The enums are the only public vocabulary;LayoutCss(internal) is the single enum→token bridge.Tests: 39 new bUnit cases — string-exact style contracts, full enum coverage, culture-invariant flex ratios, token pass-through. Mutation-verified: swapping the Small mapping to --space-1 failed exactly
Every_spacing_member_maps_to_its_token(Small).Consumer adoption is deliberately NOT here — Kagura pilots it (TagAuthoring, worst offender at 369 lines) in a follow-up PR with a measured CSS delta, then Orihon/Tsumugi.
Summary
Summary
Coverage
Kagaku.UI - 95.4%
Kagaku.UI.Fluxor - 97.6%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! A WPF-style layout vocabulary — Grid rows/columns reborn as flexbox, with the min-size trap pre-solved and the enums as the only public surface! This is exactly the kind of abstraction that makes my wings flutter~ I read every one of the 714 diff lines, every changed file whole, traced every token to
kagaku-ui.css, and ran my own mutation probes. Knowledge is delicious~ ♡Verdict: ✅ Looks good to me~
Token ground truth (mechanically verified):
--space-1…--space-8all exist (kagaku-ui.css:96-103), so Huge→--space-6resolves — the enum→token scale (None/Tiny=1/Small=2/Medium=3/Large=4/Huge=6) matches the sheet verbatim. Host overrides re-theme every gap, as promised.Mutation probes (both directional, both restored, sha256-verified pristine at
1369751):Smallmapping swapped to--space-1→ exactlyEvery_spacing_member_maps_to_its_token(Small)RED, 13 green — the PR-body claim reproduced independently.LayoutCss.Lengthneutered to identity → exactly the 3 bare-number theory arms RED (28→28rem,3→3rem,3.5→3.5rem), 15 green. The normalizer and the gap bridge are pinned, not decorative~Design decisions checked against siblings:
[Parameter, EditorRequired] … = ""matches the house style (Hotkey.Key, Markdown.Text, Tab.Title, ContextMenu.Label…).Extrasplat withCaptureUnmatchedValuesmatches Button/Card/NavItem… (27 components strong). Style-before-@attributesordering is safe: a consumerstyle="…"inExtrareplaces the whole declaration per spec — matches every splat sibling in the repo.LayoutCssinternal +InternalsVisibleTo Kagaku.UI.Tests(csproj:13) — single bridge, enums stay the only public vocabulary.Rows/Columns/Alignment/Spacingcollide with nothing repo-wide (grep clean).One considered-and-dismissed candidate: ScrollViewer.Horizontal pins
overflow-x:autowhile keepingflex-direction:columnandmin-height:0. Horizontal overflow in a column-direction container works (content min-width forcing,min-width:0present). A pragmatic minimal vocabulary — I couldn't construct a real failure from the components' own contract, so not a blocker. Do keep it in mind when Kagura pilots the horizontal case~One honesty note (non-blocking): the PR body says "34 new bUnit cases"; the branch contains 39 (Rows 14, Columns 3, LayoutChildren 18, ScrollViewer 4). Undercounting is the safe direction — but the number will drift. A quick arithmetic touch-up in the PR body would keep the ledger exact~
✅ What I liked~
flex:1 1 0%for star shares,flex:0 0 <len>for fixed, ratio ×2 for Flex — basis-0 means pure ratios regardless of content, exactly like WPF star sizing. Textbook.min-height:0; min-width:0baked into every child — the trap every hand-written flex-scroll container forgets, pre-solved at the vocabulary level. *InvariantCultureon the double→string flex ratio, withAssert.DoesNotContain(",", …)pinning it — de-DE would otherwise ship0,5 0,5 0%. Someone has been hurt before~ fufu_ => "0"fallback arm inGap()is unreachable today (callers gateGap != Spacing.Nonebefore calling) but it keepsGap()total — every future caller safe by construction. Defensive totality~Assert.Equalstyle contracts (not justContains) — these tests refuse to let a stray declaration slip in unnoticed.Spacingdocs state the exact rem scale in the enum summary — the vocabulary documents itself.Ready for the Kagura pilot, sweet author~ ♡-fufu
Automated review by Jibril · 2026-09-24
CI/CD: passed for head SHA
1369751(coverage bot 9273 — commit 12:12:54Z, coverage 12:13:57Z, coherent; Rows/Columns/Row/Column/Fixed/Flex*/ScrollViewer 100%, LayoutCss 95.2%/93.7%) · Local checks: build/test skipped per CI-green policy; 2 independent mutation probes RED on exactly their own tests, clone left pristine (sha256-verified)*