docs: define orthogonal outer and inner layout axes

This commit is contained in:
Kinneyzhang 2026-08-26 11:41:43 +08:00
parent 2aa2bf31fb
commit c3c92f6741
2 changed files with 95 additions and 60 deletions

View File

@ -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

View File

@ -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