From c3c92f6741cb426335b9bc9a7563d7ea225cc6f0 Mon Sep 17 00:00:00 2001 From: Kinneyzhang Date: Wed, 26 Aug 2026 11:41:43 +0800 Subject: [PATCH] docs: define orthogonal outer and inner layout axes --- docs/proposals/module-boundaries.en.md | 77 +++++++++++++++++-------- docs/proposals/module-boundaries.zh.md | 78 ++++++++++++++------------ 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/docs/proposals/module-boundaries.en.md b/docs/proposals/module-boundaries.en.md index c9fea5a..c11986b 100644 --- a/docs/proposals/module-boundaries.en.md +++ b/docs/proposals/module-boundaries.en.md @@ -38,6 +38,19 @@ The public concepts are: `Host` is an internal Renderer term for a primitive lowered to a backend. It is not a third public visual taxonomy. An `Ebox Node` is a lower-level geometry/render object. +ETAF should keep two orthogonal Box properties instead of turning these concepts into +Components: + +```text +:outer = inline | block +:layout = normal | row | column | flex | grid +``` + +`:outer` describes how the Box participates in its parent's ordinary layout; +`:layout` describes how the Box arranges its own children. `normal` is the clearer +user-facing name for ordinary content layout; the Renderer may lower it to Ebox's +internal ordinary-content path. + The target View model is: ```text @@ -46,18 +59,37 @@ View = Text(content, typography) | ComponentCall(props, slots) ``` -The Box layout modes exposed by the target ETAF API are a closed set: +The Box inner-layout modes exposed by the target ETAF API are a closed set: ```text -column | row | flex | grid +normal | row | column | flex | grid ``` -A Box with multiple children must have an arrangement rule; the target ETAF contract -defaults that arrangement to `column` (vertical stacking). Children are arranged only -by `column`, `row`, `flex`, or `grid`. `Text` owns text measurement, wrapping, and -inline runs, so the target model does not need a separate `flow` layout algorithm. -Current Ebox marks its ordinary content path with `(block flow)`; that is a historical -implementation marker, not a required target algorithm or module. +A plain Box defaults to `:outer 'block :layout 'normal`; it does not silently become a +column. Use `:layout 'column` for vertical stacking, or choose `row`, `flex`, or `grid` +explicitly. Text defaults to inline content in normal layout and owns text measurement, +wrapping, and inline runs. + +The mapping is: + +| CSS meaning | Target ETAF expression | +| --- | --- | +| ordinary block content | `(box :outer 'block :layout 'normal ...)` | +| ordinary inline content | `(box :outer 'inline :layout 'normal ...)` | +| vertical stacking | `(box :layout 'column ...)` | +| horizontal layout | `(box :layout 'row ...)` | +| flex container | `(box :layout 'flex ...)` | +| grid container | `(box :layout 'grid ...)` | + +`inline-flex` is the direct orthogonal combination: + +```elisp +(box :outer 'inline :layout 'flex ...) +``` + +A Component call has no fixed `:outer` or `:layout`; its root Text/Box output decides +how it participates in the parent. A transparent Component may return siblings and +therefore has no single outer Box. `(box "text")` is shorthand for `(box (text "text"))`. ETAF must not expose `(box :content "text")`; `:content` remains an Ebox backend field. @@ -66,25 +98,22 @@ ETAF also does not define a public `spacer` type. A Box with no children is an e Box; Ebox may use an empty-node representation or private helper, but users do not need another visual category. -### 1.3 Should Box expose an outer participation mode? +Box properties remain grouped by owner rather than becoming one unconstrained plist: -The target does not expose `inline`/`block` as a second public ETAF layout axis yet: +| Group | Properties | Purpose | +| --- | --- | --- | +| structure | `:outer`, `:layout`, `:key` | participation, child layout, identity | +| geometry/surface | `:width`, `:height`, min/max, `:margin`, `:padding`, `:border`, `:box-sizing`, `:overflow` | size and surface | +| text | `:face`, `:color`, `:bgcolor`, `:wrap-mode`, `:text-align` | typography and text paint | +| flex item | `:order`, `:flex-grow`, `:flex-shrink`, `:flex-basis`, `:align-self` | valid under a Flex parent | +| grid item | `:grid-row`, `:grid-column`, spans | valid under a Grid parent | +| semantics/interaction | `:class`, `:id`, `:role`, `:ref`, `:aria-*`, `:on-*`, `:use` | Runtime events, Behaviors, and queries | -- the parent Box already decides child placement through `column`, `row`, `flex`, or - `grid`; -- `Text` owns inline runs, wrapping, and text measurement; -- most ETAF Boxes are layout items whose position is owned by the parent layout; -- adding `:outer 'inline` would also require inline boxes, anonymous line boxes, mixed - block/inline flow, and corresponding incremental proofs. +`:outer` and `:layout` are geometry/structure properties, not TP paint slots. ECSS may +resolve them, but Ebox validates the final combination and owns the layout result. -The target public API therefore exposes `Box :layout ...` and inline content through -`Text`. If a real inline badge, inline-flex, or mixed inline/block requirement appears -later, add an orthogonal `:outer` property. It must remain independent from `:layout` -because CSS `inline-flex` means outer inline plus inner flex. Do not turn the two axes -into one growing enum or replace them with a Component. - -Ebox may keep an outer/inner display pair internally; that backend choice does not make -both axes ETAF public API. +Ebox may keep an outer/inner display representation internally; the public split is +the explicit `:outer` and `:layout` contract. ## 2. Module responsibilities diff --git a/docs/proposals/module-boundaries.zh.md b/docs/proposals/module-boundaries.zh.md index 9eb94c3..be4fe76 100644 --- a/docs/proposals/module-boundaries.zh.md +++ b/docs/proposals/module-boundaries.zh.md @@ -46,8 +46,18 @@ CSS 中所有最终可见元素都会产生 box,但 CSS 生成的 box、ETAF Ebox 的后端节点不是同一棵树。`inline` 和 `block` 描述盒子参与父布局的方式; `flex` 和 `grid` 描述盒子内部如何排列子节点。 -因此 ETAF 不应该把 `inline`、`block`、`flex`、`grid` 都做成 Component。目标 -公共模型是: +因此 ETAF 不应该把这些概念做成 Component,而应在 Box 上保留两个正交属性: + +```text +:outer = inline | block +:layout = normal | row | column | flex | grid +``` + +`:outer` 表示这个 Box 如何参与父级的普通布局;`:layout` 表示这个 Box 如何 +排列自己的子节点。`normal` 是 ETAF 用户层对普通内容布局的名称,Renderer +可以把它降低为 Ebox 内部的普通内容路径。 + +目标公共模型是: ```text View = Text(content, typography) @@ -55,58 +65,54 @@ View = Text(content, typography) | ComponentCall(props, slots) ``` -目标中 ETAF 对用户暴露的 `Box` 布局模式是封闭集合: +目标中 ETAF 对用户暴露的 Box 内部布局模式是封闭集合: ```text -column | row | flex | grid +normal | row | column | flex | grid ``` -普通 `Box` 包含多个子节点时必须有明确的排列规则;目标 ETAF 规定默认排列为 -`column`(纵向堆叠)。子节点只通过 `column`、`row`、`flex` 或 `grid` 排列。 -`Text` 自己负责文字测量、换行和 inline runs,因此目标模型不需要额外的 `flow` -布局算法。当前 Ebox 用 CSS-like display pair `(block flow)` 表示普通内容路径, -这是历史实现标记,不是目标架构必须保留的算法或模块。 +普通 `Box` 默认是 `:outer 'block :layout 'normal`,不会隐式变成 `column`。 +需要纵向堆叠时明确写 `:layout 'column`;需要横向、Flex 或 Grid 时明确选择 +对应模式。`Text` 默认作为 inline content 参与 normal 布局,但自身负责文字 +测量、换行和 inline runs。 语义映射大致为: | CSS 语义 | ETAF 目标表达 | | --- | --- | | inline text/run | `text` | -| 普通块级/纵向内容 | `(box ...)` 或 `(box :layout 'column ...)` | +| 普通 block 内容 | `(box :outer 'block :layout 'normal ...)` | +| 普通 inline 内容 | `(box :outer 'inline :layout 'normal ...)` | +| 纵向排列 | `(box :layout 'column ...)` | | horizontal layout | `(box :layout 'row ...)` | | flex container | `(box :layout 'flex ...)` | | grid container | `(box :layout 'grid ...)` | -完整的 `inline-flex` 这类组合属于“外部参与方式 + 内部布局方式”两个轴。当前 -文本应用不需要把完整 CSS `display` 矩阵暴露给用户;未来若 Ebox 确实需要, -应增加正交属性,而不是增加 `inline` Component。 +`inline-flex` 是两个属性的正交组合: -### 1.3 是否需要 Box 的外部参与方式 - -当前目标不把 `inline`/`block` 暴露为 ETAF 的第二个公共布局轴,原因是: - -- ETAF 的父级 `Box` 已经通过 `column`、`row`、`flex` 或 `grid` 明确决定子节点 - 如何参与布局; -- `Text` 自己负责 inline runs、换行和文字测量; -- 大多数 ETAF Box 都是布局项,父级布局拥有它们的位置,不需要再由子节点声明 - 一套 CSS 外部参与规则; -- 引入 `:outer 'inline` 会同时要求 inline box、匿名 line box、混合 block/inline - 流和对应的增量证明,超出当前文本应用的最小模型。 - -因此目标公共 API 只有: - -```text -Box :layout ... -Text inline content +```elisp +(box :outer 'inline :layout 'flex ...) ``` -如果未来出现真实的 inline badge、inline-flex 或混合 inline/block 需求,再增加 -正交的 `:outer` 属性,例如 `:outer 'inline` 或 `:outer 'block`。它必须独立于 -`:layout`,因为 CSS 的 `inline-flex` 本身就是“外部 inline、内部 flex”;不能把 -它们拼成一个越来越大的 `:layout` 枚举,也不能用 Component 代替。 +Component call 自身没有固定的 `:outer` 或 `:layout`;它的根 Text/Box 输出决定 +它如何参与父级布局。透明 Component 可以输出多个兄弟节点,因此不能假定每个 +Component 都有一个外部盒子。 -当前 Ebox 内部保留 outer/inner display pair 是后端实现选择,不代表 ETAF 必须 -暴露这两个轴。 +### 1.3 属性分组 + +Box 的属性按 owner 分组,不能做成一个无约束的大 plist: + +| 分组 | 属性 | 作用 | +| --- | --- | --- | +| 外部/内部结构 | `:outer`、`:layout`、`:key` | 参与方式、子布局、稳定 identity | +| 几何和表面 | `:width`、`:height`、min/max、`:margin`、`:padding`、`:border`、`:box-sizing`、`:overflow` | 尺寸、表面和可见区域 | +| 文本 | `:face`、`:color`、`:bgcolor`、`:wrap-mode`、`:text-align` | Text 或普通内容的排版/绘制 | +| Flex 子项 | `:order`、`:flex-grow`、`:flex-shrink`、`:flex-basis`、`:align-self` | 只在 Flex 父级中有效 | +| Grid 子项 | `:grid-row`、`:grid-column`、span | 只在 Grid 父级中有效 | +| 语义和交互 | `:class`、`:id`、`:role`、`:ref`、`:aria-*`、`:on-*`、`:use` | Runtime 事件、Behavior 和查询 | + +`:outer` 和 `:layout` 是结构/几何属性,变化会触发布局 owner;它们不是 TP +paint slot。ECSS 可以参与解析它们,但最终合法性和布局仍由 Ebox 契约验证。 ### 1.4 子节点与后端 content