refactor: move playground fixtures to sibling package

Keep the standalone Ebox package focused on its public layout and DSL primitives while the generic ebox-playground package owns executable .ebox references.
This commit is contained in:
Kinneyzhang 2026-08-05 20:36:24 +08:00
parent de6f56637b
commit ab9efe62a7
14 changed files with 58 additions and 635 deletions

View File

@ -6,7 +6,7 @@ NATIVE_MANIFEST = native/Cargo.toml
NATIVE_TARGET ?= $(shell $(EMACS_BATCH) -l ebox-native-reflow.el --eval '(princ (ebox-native-reflow--rust-target))') NATIVE_TARGET ?= $(shell $(EMACS_BATCH) -l ebox-native-reflow.el --eval '(princ (ebox-native-reflow--rust-target))')
NATIVE_RELEASE_DIR = native/target/$(NATIVE_TARGET)/release NATIVE_RELEASE_DIR = native/target/$(NATIVE_TARGET)/release
.PHONY: all check ci load compile test checkdoc core-tests grid-tests ebox-commit-tests visual-check-tests package-tests selector-tests examples-tests dsl-tests flex-tests docs-contract-tests ci-contract-tests visual-check native-rust-tests native-build diff-check clean package-lint package-lint-install .PHONY: all check ci load compile test checkdoc core-tests grid-tests ebox-commit-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests docs-contract-tests ci-contract-tests visual-check native-rust-tests native-build diff-check clean package-lint package-lint-install
all: check all: check
@ -21,7 +21,7 @@ compile:
rm -f *.elc tests/*.elc scripts/*.elc rm -f *.elc tests/*.elc scripts/*.elc
$(EMACS_BATCH) --eval '(setq byte-compile-error-on-warn t)' -l ebox.el --eval '(ebox-byte-compile)' $(EMACS_BATCH) --eval '(setq byte-compile-error-on-warn t)' -l ebox.el --eval '(ebox-byte-compile)'
test: core-tests grid-tests ebox-commit-tests visual-check-tests package-tests selector-tests examples-tests dsl-tests flex-tests docs-contract-tests ci-contract-tests test: core-tests grid-tests ebox-commit-tests visual-check-tests package-tests selector-tests dsl-tests flex-tests docs-contract-tests ci-contract-tests
core-tests: core-tests:
$(EMACS_BATCH) -l tests/ebox-core-render-tests.el -f ert-run-tests-batch-and-exit $(EMACS_BATCH) -l tests/ebox-core-render-tests.el -f ert-run-tests-batch-and-exit
@ -41,9 +41,6 @@ package-tests:
selector-tests: selector-tests:
$(EMACS_BATCH) -l tests/ebox-selector-tests.el -f ert-run-tests-batch-and-exit $(EMACS_BATCH) -l tests/ebox-selector-tests.el -f ert-run-tests-batch-and-exit
examples-tests:
$(EMACS_BATCH) -L examples -l tests/ebox-examples-tests.el -f ert-run-tests-batch-and-exit
dsl-tests: dsl-tests:
$(EMACS_BATCH) -l tests/ebox-dsl-tests.el -f ert-run-tests-batch-and-exit $(EMACS_BATCH) -l tests/ebox-dsl-tests.el -f ert-run-tests-batch-and-exit

View File

@ -32,7 +32,7 @@ This is the maintainer entry point for the standalone Ebox repository. It descri
The package intentionally does not include application Components, UI controls, reactive data, or a playground implementation. Those are sibling-package responsibilities. The package intentionally does not include application Components, UI controls, reactive data, or a playground implementation. Those are sibling-package responsibilities.
The active contract also covers `Makefile`, `.github/workflows/ci.yml`, `examples/ebox-basic-examples.el`, `examples/playground/basic.ebox`, `examples/playground/comprehensive.ebox`, `examples/playground/flex-reference.ebox`, `examples/playground/responsive-reference.ebox`, `tests/ebox-core-render-tests.el`, `tests/ebox-grid-tests.el`, `tests/ebox-commit-tests.el`, `tests/ebox-examples-tests.el`, `tests/ebox-dsl-tests.el`, `tests/ebox-flex-tests.el`, `tests/ebox-selector-tests.el`, `tests/ebox-package-tests.el`, `tests/ebox-visual-check-tests.el`, `tests/ebox-docs-contract-tests.el`, `tests/ebox-ci-contract-tests.el`, `native/Cargo.toml`, `native/Cargo.lock`, `native/build.rs`, `native/vendor/emacs-30/emacs-module.h`, `native/src/lib.rs`, `native/src/layout.rs`, `native/c/ebox_module.c`, `scripts/ebox-package-lint.el`, and `scripts/ebox-visual-check.el`. The active contract also covers `Makefile`, `.github/workflows/ci.yml`, `tests/ebox-core-render-tests.el`, `tests/ebox-grid-tests.el`, `tests/ebox-commit-tests.el`, `tests/ebox-dsl-tests.el`, `tests/ebox-flex-tests.el`, `tests/ebox-selector-tests.el`, `tests/ebox-package-tests.el`, `tests/ebox-visual-check-tests.el`, `tests/ebox-docs-contract-tests.el`, `tests/ebox-ci-contract-tests.el`, `native/Cargo.toml`, `native/Cargo.lock`, `native/build.rs`, `native/vendor/emacs-30/emacs-module.h`, `native/src/lib.rs`, `native/src/layout.rs`, `native/c/ebox_module.c`, `scripts/ebox-package-lint.el`, and `scripts/ebox-visual-check.el`.
## Runtime model ## Runtime model
@ -77,7 +77,7 @@ Grid currently covers fixed and pixel tracks, `auto`, fractional tracks, `minmax
## Active examples and tests ## Active examples and tests
The maintained examples are `examples/ebox-basic-examples.el` and the `.ebox` fixtures under `examples/playground/`. The regression boundary is the ERT files under `tests/` listed in the repository's documentation contract. No test in this package should load the historical full-stack source tree or an application framework. The standalone package has no bundled playground fixtures. The migrated `.ebox` references and their file runner live in the sibling `ebox-playground` package; this repository tests the DSL and layout primitives directly. The regression boundary is the ERT files under `tests/` listed in the repository's documentation contract. No test in this package should load the historical full-stack source tree or an application framework.
## Verification matrix ## Verification matrix
@ -91,7 +91,6 @@ make ebox-commit-tests
make visual-check-tests make visual-check-tests
make package-tests make package-tests
make selector-tests make selector-tests
make examples-tests
make dsl-tests make dsl-tests
make flex-tests make flex-tests
make docs-contract-tests make docs-contract-tests

View File

@ -32,6 +32,8 @@
本包有意不包含应用 Component、UI control、响应式 data 或 playground 实现;它们属于同级包。历史应用性能记录器和 native reflow 评估器也不属于独立 Ebox 的发布边界Ebox 只保留 native 模块本身、Rust 构建输入和可重复的构建检查。 本包有意不包含应用 Component、UI control、响应式 data 或 playground 实现;它们属于同级包。历史应用性能记录器和 native reflow 评估器也不属于独立 Ebox 的发布边界Ebox 只保留 native 模块本身、Rust 构建输入和可重复的构建检查。
active 合同还覆盖 `Makefile`、`.github/workflows/ci.yml`、`tests/ebox-core-render-tests.el`、`tests/ebox-grid-tests.el`、`tests/ebox-commit-tests.el`、`tests/ebox-dsl-tests.el`、`tests/ebox-flex-tests.el`、`tests/ebox-selector-tests.el`、`tests/ebox-package-tests.el`、`tests/ebox-visual-check-tests.el`、`tests/ebox-docs-contract-tests.el`、`tests/ebox-ci-contract-tests.el`、`native/Cargo.toml`、`native/Cargo.lock`、`native/build.rs`、`native/vendor/emacs-30/emacs-module.h`、`native/src/lib.rs`、`native/src/layout.rs`、`native/c/ebox_module.c`、`scripts/ebox-package-lint.el` 和 `scripts/ebox-visual-check.el`
## 运行时模型 ## 运行时模型
正常数据流是: 正常数据流是:
@ -75,7 +77,7 @@ Source Tree
## Active 示例与测试 ## Active 示例与测试
维护中的示例是 `examples/ebox-basic-examples.el``examples/playground/` 下的 `.ebox` fixture。回归边界是 `tests/` 下由文档合同列出的 ERT 文件。本包测试不得加载历史全栈源码树或应用框架。 独立 Ebox 包不再内置 playground fixture。迁移后的 `.ebox` 参考文件及其运行器位于同级 `ebox-playground` 包;本仓库直接测试 DSL 与布局原语。回归边界是 `tests/` 下由文档合同列出的 ERT 文件。本包测试不得加载历史全栈源码树或应用框架。
## 验证矩阵 ## 验证矩阵
@ -87,7 +89,6 @@ make ebox-commit-tests
make visual-check-tests make visual-check-tests
make package-tests make package-tests
make selector-tests make selector-tests
make examples-tests
make dsl-tests make dsl-tests
make flex-tests make flex-tests
make docs-contract-tests make docs-contract-tests

View File

@ -64,7 +64,7 @@ Use row and column for simple one-dimensional composition. Use flex when free sp
(ebox-create :content "Aside" :grid-column 3 :grid-row 2)) (ebox-create :content "Aside" :grid-column 3 :grid-row 2))
``` ```
Grid tracks can be fixed, fractional, `auto`, `minmax`, or repeated. Explicit placement is one-based. Use positive integer spans and let implicit tracks fill omitted positions. Grid tracks can be fixed, fractional, `auto`, `minmax`, or repeated. Explicit placement is one-based. Use positive integer spans and let implicit tracks fill omitted positions. Multiline items are aligned line by line to their grid rectangle before a wrapper border is painted, keeping a bordered Grid's right edge continuous even when child lines have different natural widths.
## 5. Render text or a buffer ## 5. Render text or a buffer
@ -117,7 +117,9 @@ Selectors query the rendered tree and return public handles. They do not edit th
(box :content "C"))) (box :content "C")))
``` ```
The files under `examples/playground/` are executable fixtures for this syntax. They remain low-level layout examples and do not require the ETAF framework. Inside a `.ebox` fixture, keep the structural form unquoted. Quote list and symbol constants in property positions (for example, `:gap '(1 (12))` and `:justify-content 'center`), while leaving executable Elisp property expressions unquoted. `ebox-playground` evaluates those property expressions before passing the form to `ebox-build`, matching the `etaf-view` value convention.
Executable `.ebox` references are maintained by the sibling [`ebox-playground`](../../ebox-playground/README.md) package. Its `examples/` directory contains the migrated Basic, Comprehensive, Flex, Responsive, and complete Grid reference files; they remain low-level layout examples and do not require the ETAF framework.
## 9. Optional native reflow ## 9. Optional native reflow

View File

@ -64,7 +64,7 @@ Ebox 是底层的 box、布局和 buffer 渲染包。当应用需要精确几何
(ebox-create :content "Aside" :grid-column 3 :grid-row 2)) (ebox-create :content "Aside" :grid-column 3 :grid-row 2))
``` ```
Grid 轨道支持固定值、分数、`auto`、`minmax` 和重复轨道。显式放置从 1 开始span 使用正整数;省略的位置由隐式轨道补齐。 Grid 轨道支持固定值、分数、`auto`、`minmax` 和重复轨道。显式放置从 1 开始span 使用正整数;省略的位置由隐式轨道补齐。多行子项会先逐行对齐到自己的 Grid 矩形,再绘制外层边框;即使子项各行的自然宽度不同,带边框的 Grid 右边缘也会保持连续。
## 5. 渲染文本或 buffer ## 5. 渲染文本或 buffer
@ -117,7 +117,9 @@ Selector 查询已渲染的树并返回公共 handle它不编辑 buffer
(box :content "C"))) (box :content "C")))
``` ```
`examples/playground/` 下的文件是该语法的可执行 fixture。它们只演示低层布局不依赖 ETAF。 `.ebox` fixture 中,结构表单保持不加 quote。属性位置的 list 和 symbol 常量要加 quote例如 `:gap '(1 (12))`、`:justify-content 'center`;可执行的 Elisp 属性表达式则保持不加 quote。`ebox-playground` 会在把 form 交给 `ebox-build` 之前求值这些属性表达式,这与 `etaf-view` 的属性值约定一致。
可执行的 `.ebox` 参考文件由同级 [`ebox-playground`](../../ebox-playground/README.md) 包维护。它的 `examples/` 目录包含迁移后的 Basic、Comprehensive、Flex、Responsive 以及完整 Grid 参考文件;这些文件只演示低层布局,不依赖 ETAF。
## 9. 可选 native reflow ## 9. 可选 native reflow

View File

@ -12,6 +12,30 @@
(require 'ebox-flex) (require 'ebox-flex)
(require 'ebox-grid) (require 'ebox-grid)
(defun ebox--dsl-unquote-value (value)
"Unquote one static DSL property VALUE when it is quoted data."
(if (and (consp value)
(eq (car value) 'quote)
(null (cddr value)))
(cadr value)
value))
(defun ebox--dsl-unquote-properties (items)
"Unquote static property values in top-level DSL ITEMS.
Structural child forms remain untouched; only values immediately following
keyword properties are normalized."
(let (result)
(while items
(let ((item (pop items)))
(if (keywordp item)
(progn
(unless items
(error "ebox-build: missing value for %S" item))
(push item result)
(push (ebox--dsl-unquote-value (pop items)) result))
(push item result))))
(nreverse result)))
(defun ebox--build-split-attrs (items) (defun ebox--build-split-attrs (items)
"Split DSL ITEMS into (PROPS . CHILDREN)." "Split DSL ITEMS into (PROPS . CHILDREN)."
(let (props children) (let (props children)
@ -171,7 +195,7 @@ the child layout in a box using the rendered child layout as content."
(error "ebox-build: invalid DSL node %S" dsl)) (error "ebox-build: invalid DSL node %S" dsl))
(t (t
(let ((tag (car dsl)) (let ((tag (car dsl))
(items (cdr dsl))) (items (ebox--dsl-unquote-properties (cdr dsl))))
(pcase tag (pcase tag
((or 'box 'ebox) (ebox--build-box items)) ((or 'box 'ebox) (ebox--build-box items))
('row (ebox--build-layout tag #'ebox-row items)) ('row (ebox--build-layout tag #'ebox-row items))

View File

@ -1,7 +0,0 @@
(box :content "Playground file"
:box-sizing border-box
:padding (1 (12))
:border t
:border-color "#2563EB"
:bgcolor "#EFF6FF"
:color "#1E3A8A")

View File

@ -1,45 +0,0 @@
(column
(box :content "Ebox comprehensive DSL showcase\nThe outer sections omit :width so Playground viewport sizing drives the layout."
:padding (1 (16)) :border "#334155"
:bgcolor "#F8FAFC" :color "#0F172A" :text-align center)
(spacer :height 1)
(box :content "Width rule in this file\nContainer sections use auto width. Fixed widths remain only where the example demonstrates exact item sizing, basis, min-width, or max-width."
:padding (0 (14)) :border "#64748B"
:bgcolor "#F8FAFC" :color "#111827" :wrap-mode word)
(spacer :height 1)
(flex
:margin (1 0 0 1) :padding (1 (8))
:border "#475569" :bgcolor "#F8FAFC"
:flex-flow (row wrap)
:justify-content space-between :align-items center
:align-content flex-start :row-gap 1 :column-gap (12)
(item
:order -1 :flex-basis (270) :align-self flex-start
(box :content "item order -1\n:flex-basis 270\nalign-self start"
:width (270) :padding (0 (8))
:border "#BE185D" :bgcolor "#FCE7F3" :color "#831843"
:wrap-mode word))
(item
:flex (1 1 (220))
(box :content "item :flex (1 1 220)\ngrow shrink basis"
:width (220) :height 4
:padding (0 (8)) :border "#1D4ED8"
:bgcolor "#DBEAFE" :color "#111827" :wrap-mode word))
(item
:flex-grow 2 :flex-shrink 1 :flex-basis (240)
:align-self center
(box :content "item grow 2\nshrink 1\nbasis 240"
:width (240) :height 4 :padding (0 (8))
:border "#B45309" :bgcolor "#FEF3C7" :color "#78350F"
:wrap-mode word))
(item :flex-shrink 3
:flex-basis (190)
:align-self flex-end
(box :content "item shrink 3\nbasis 190\nalign-self flex-end"
:width (190) :height 4 :padding (0 (8))
:border "#15803D" :bgcolor "#DCFCE7" :color "#14532D"
:wrap-mode word))))

View File

@ -1,463 +0,0 @@
(column ;; :height (viewport-height)
:width (viewport)
(box :content "Ebox CSS Flex Reference\nA modern, self-explaining DSL gallery for CSS flex container and item properties."
:padding (1 (24)) :border "#2F3437"
:bgcolor "#FAF7F0" :color "#1F2328" :text-align center)
(spacer :height 1)
(box :content "Container property: :flex-direction\nMain axis selection. Reverse values swap main-start and main-end while keeping source order intact."
:padding (0 (18)) :border "#D97757"
:bgcolor "#FFF5ED" :color "#3D2B1F")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#D97757" :border-top-p nil
:bgcolor "#FFF8F2"
(column
(box :content "row" :width (220) :padding (0 (10)) :border "#D97757"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :width (220) :height 4 :padding (0 (8)) :border "#D97757"
:border-top-p nil
:bgcolor "#FFF8F2" :flex-direction row :column-gap (10) :align-items center
(item (box :content "A" :width (42) :height 1 :bgcolor "#F7C8B3" :text-align center))
(item (box :content "B" :width (42) :height 1 :bgcolor "#F0A88F" :text-align center))
(item (box :content "C" :width (42) :height 1 :bgcolor "#D97757" :color "#FFFFFF" :text-align center))))
(column
(box :content "row-reverse" :width (220) :padding (0 (10)) :border "#D97757"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :width (220) :height 4 :padding (0 (8)) :border "#D97757"
:border-top-p nil
:bgcolor "#FFF8F2" :flex-direction row-reverse :column-gap (10) :align-items center
(item (box :content "A" :width (42) :height 1 :bgcolor "#F7C8B3" :text-align center))
(item (box :content "B" :width (42) :height 1 :bgcolor "#F0A88F" :text-align center))
(item (box :content "C" :width (42) :height 1 :bgcolor "#D97757" :color "#FFFFFF" :text-align center))))
(column
(box :content "column" :width (220) :padding (0 (10)) :border "#D97757"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :width (220) :height 6 :padding (0 (8)) :border "#D97757"
:border-top-p nil
:bgcolor "#FFF8F2" :flex-direction column :row-gap 1 :align-items stretch
(item (box :content "A" :height 1 :bgcolor "#F7C8B3" :text-align center))
(item (box :content "B" :height 1 :bgcolor "#F0A88F" :text-align center))
(item (box :content "C" :height 1 :bgcolor "#D97757" :color "#FFFFFF" :text-align center))))
(column
(box :content "column-reverse" :width (220) :padding (0 (10)) :border "#D97757"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :width (220) :height 6 :padding (0 (8)) :border "#D97757"
:border-top-p nil
:bgcolor "#FFF8F2" :flex-direction column-reverse :row-gap 1 :align-items stretch
(item (box :content "A" :height 1 :bgcolor "#F7C8B3" :text-align center))
(item (box :content "B" :height 1 :bgcolor "#F0A88F" :text-align center))
(item (box :content "C" :height 1 :bgcolor "#D97757" :color "#FFFFFF" :text-align center)))))
(spacer :height 1)
(box :content "Container property: :flex-wrap\nLine creation. nowrap keeps one flex line; wrap creates new lines; wrap-reverse stacks lines from the opposite cross side."
:padding (0 (18)) :border "#5E7F6A"
:bgcolor "#F3FAF4" :color "#24352A")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#5E7F6A" :border-top-p nil
:bgcolor "#F8FCF8"
(column
(box :content "nowrap" :max-width (293) :padding (0 (10)) :border "#5E7F6A"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293) :padding (0 (8)) :border "#5E7F6A"
:border-top-p nil
:bgcolor "#F8FCF8" :flex-wrap nowrap :column-gap (8) :align-items center
(item :flex-shrink 1 (box :content "A" :width (92) :height 1 :bgcolor "#DCEBDD" :text-align center))
(item :flex-shrink 1 (box :content "B" :width (92) :height 1 :bgcolor "#BFD6C2" :text-align center))
(item :flex-shrink 1 (box :content "C" :width (92) :height 1 :bgcolor "#8FAF95" :text-align center))))
(column
(box :content "wrap" :max-width (293) :padding (0 (10)) :border "#5E7F6A"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293) :padding (0 (8)) :border "#5E7F6A"
:border-top-p nil
:bgcolor "#F8FCF8" :flex-wrap wrap :column-gap (8) :row-gap 1
(item :flex-shrink 0 (box :content "A 118" :width (118) :height 1 :bgcolor "#DCEBDD" :text-align center))
(item :flex-shrink 0 (box :content "B 118" :width (118) :height 1 :bgcolor "#BFD6C2" :text-align center))
(item :flex-shrink 0 (box :content "C 118" :width (118) :height 1 :bgcolor "#8FAF95" :text-align center))))
(column
(box :content "wrap-reverse" :max-width (293) :padding (0 (10)) :border "#5E7F6A"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293) :padding (0 (8)) :border "#5E7F6A"
:border-top-p nil
:bgcolor "#F8FCF8" :flex-wrap wrap-reverse :column-gap (8) :row-gap 1
(item :flex-shrink 0 (box :content "A 118" :width (118) :height 1 :bgcolor "#DCEBDD" :text-align center))
(item :flex-shrink 0 (box :content "B 118" :width (118) :height 1 :bgcolor "#BFD6C2" :text-align center))
(item :flex-shrink 0 (box :content "C 118" :width (118) :height 1 :bgcolor "#8FAF95" :text-align center)))))
(spacer :height 1)
(box :content "Container property: :flex-flow\nShorthand for direction and wrapping. These examples use one property to set both behaviors."
:padding (0 (18)) :border "#6F6A95"
:bgcolor "#F7F4FC" :color "#2F2B45")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#6F6A95" :border-top-p nil
:bgcolor "#FBF9FF"
(column
(box :content ":flex-flow (row wrap)" :max-width (440) :padding (0 (10))
:border "#6F6A95" :bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (440) :padding (0 (8)) :border "#6F6A95"
:border-top-p nil
:bgcolor "#FBF9FF" :flex-flow (row wrap) :gap (1 (12))
(item (box :content "A" :width (170) :height 1 :bgcolor "#E4DEF4" :text-align center))
(item (box :content "B" :width (170) :height 1 :bgcolor "#CBC2E3" :text-align center))
(item (box :content "C wraps" :width (170) :height 1 :bgcolor "#AFA3D1" :text-align center))))
(column
(box :content ":flex-flow (column wrap)" :max-width (440) :padding (0 (10))
:border "#6F6A95" :bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (440) :padding (0 (8)) :border "#6F6A95"
:border-top-p nil
:bgcolor "#FBF9FF" :flex-flow (column wrap) :gap (1 (12))
(item (box :content "A" :width (140) :height 1 :bgcolor "#E4DEF4" :text-align center))
(item (box :content "B" :width (140) :height 1 :bgcolor "#CBC2E3" :text-align center))
(item (box :content "C new column" :width (140) :height 1 :bgcolor "#AFA3D1" :text-align center)))))
(spacer :height 1)
(box :content "Container property: :justify-content\nMain-axis packing after sizing. Distributed values change the space before, between, and after items."
:padding (0 (18)) :border "#3F6D88"
:bgcolor "#F1F8FB" :color "#20313A")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#3F6D88" :border-top-p nil
:bgcolor "#F6FBFD"
(column
(box :content "flex-start" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content flex-start
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "center" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content center
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "flex-end" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content flex-end
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "space-between" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content space-between
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "space-around" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content space-around
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "space-evenly" :width (146) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content space-evenly
(item (box :content "A" :width (28) :height 1 :bgcolor "#CAE6F0" :text-align center))
(item (box :content "B" :width (28) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "start" :width (176) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content start
(item (box :content "start" :width (70) :height 1 :bgcolor "#CAE6F0" :text-align center))))
(column
(box :content "end" :width (176) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content end
(item (box :content "end" :width (70) :height 1 :bgcolor "#A8D2E2" :text-align center))))
(column
(box :content "left" :width (176) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content left
(item (box :content "left" :width (70) :height 1 :bgcolor "#7DB8CE" :text-align center))))
(column
(box :content "right" :width (176) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content right
(item (box :content "right" :width (70) :height 1 :bgcolor "#5F9CB5" :text-align center))))
(column
(box :content "normal" :width (176) :padding (0 (4)) :border "#3F6D88" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :border "#3F6D88" :border-top-p nil :bgcolor "#F6FBFD" :justify-content normal
(item (box :content "normal" :width (70) :height 1 :bgcolor "#3F6D88" :color "#FFFFFF" :text-align center)))))
(spacer :height 1)
(box :content "Container property: :align-items\nCross-axis alignment inside each flex line. Stretch/normal enlarge auto-sized items; start/end/center move shorter ones."
:padding (0 (18)) :border "#9A6B43"
:bgcolor "#FFF7EE" :color "#3A2A1D")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#9A6B43" :border-top-p nil
:bgcolor "#FFFAF5"
(column
(box :content "flex-start" :width (176) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 5 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items flex-start
(item (box :content "short" :width (68) :height 1 :bgcolor "#F1D0AF" :text-align center))
(item (box :content "tall\nitem\nsets line" :width (78) :height 3 :bgcolor "#D9AD83" :text-align center))))
(column
(box :content "center" :width (176) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 5 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items center
(item (box :content "short" :width (68) :height 1 :bgcolor "#F1D0AF" :text-align center))
(item (box :content "tall\nitem\nsets line" :width (78) :height 3 :bgcolor "#D9AD83" :text-align center))))
(column
(box :content "flex-end" :width (176) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 5 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items flex-end
(item (box :content "short" :width (68) :height 1 :bgcolor "#F1D0AF" :text-align center))
(item (box :content "tall\nitem\nsets line" :width (78) :height 3 :bgcolor "#D9AD83" :text-align center))))
(column
(box :content "stretch" :width (176) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 5 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items stretch
(item (box :content "auto height stretches" :width (72) :bgcolor "#F1D0AF" :text-align center))
(item (box :content "line height" :width (78) :height 3 :bgcolor "#D9AD83" :text-align center))))
(column
(box :content "baseline" :width (176) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 5 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items baseline
(item (box :content "baseline" :width (90) :height 1 :bgcolor "#BA8456" :text-align center))))
(column
(box :content "start" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :height 4 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items start
(item (box :content "start" :width (62) :height 1 :bgcolor "#F1D0AF" :text-align center))))
(column
(box :content "end" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :height 4 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items end
(item (box :content "end" :width (62) :height 1 :bgcolor "#D9AD83" :text-align center))))
(column
(box :content "self-start" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :height 4 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items self-start
(item (box :content "self-start" :width (78) :height 1 :bgcolor "#BA8456" :text-align center))))
(column
(box :content "self-end" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :height 4 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items self-end
(item (box :content "self-end" :width (70) :height 1 :bgcolor "#9A6B43" :color "#FFFFFF" :text-align center))))
(column
(box :content "normal" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(flex :width (146) :height 4 :border "#9A6B43" :border-top-p nil :bgcolor "#FFFAF5" :align-items normal
(item (box :content "normal" :width (62) :bgcolor "#7A5132" :color "#FFFFFF" :text-align center))))
(column
(box :content "stretch note" :width (146) :padding (0 (4)) :border "#9A6B43" :bgcolor "#FFFDF8" :text-align center)
(box :content "normal behaves stretch-like for auto cross size in this DSL." :width (146)
:height 4 :padding (0 (8)) :border "#9A6B43" :border-top-p nil
:bgcolor "#FFFAF5" :color "#3A2A1D")))
(spacer :height 1)
(box :content "Container property: :align-content\nCross-axis packing between multiple flex lines. It has no visible effect on a nowrap single-line container."
:padding (0 (18)) :border "#7C6A2F"
:bgcolor "#FBF8E8" :color "#362F16")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#7C6A2F" :border-top-p nil
:bgcolor "#FFFDF0"
(column
(box :content "flex-start" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content flex-start :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "center" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content center :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "flex-end" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content flex-end :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "space-between" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content space-between :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "space-around" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content space-around :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "space-evenly" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content space-evenly :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "stretch" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content stretch :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "start" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content start :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "end" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content end :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "baseline" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content baseline :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "normal" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap wrap :align-content normal :row-gap 1 :column-gap (8)
(item (box :content "A" :width (78) :height 1 :bgcolor "#EEE1A8" :text-align center))
(item (box :content "B" :width (78) :height 1 :bgcolor "#D9C46D" :text-align center))
(item (box :content "C" :width (78) :height 1 :bgcolor "#B89B3D" :text-align center))))
(column
(box :content "nowrap note" :width (176) :padding (0 (4)) :border "#7C6A2F" :bgcolor "#FFFDF8" :text-align center)
(flex :width (176) :height 7 :border "#7C6A2F" :border-top-p nil :bgcolor "#FFFDF0"
:flex-wrap nowrap :align-content normal :row-gap 1 :column-gap (8)
(item (box :content "single line" :width (96) :height 1 :bgcolor "#EEE1A8" :text-align center)))))
(spacer :height 1)
(box :content "Container properties: :gap, :row-gap, :column-gap\n:gaps define spacing between flex items and flex lines. Explicit row/column values override matching sides."
:padding (0 (18)) :border "#3D7068"
:bgcolor "#F1FAF8" :color "#1D3935")
(flex :flex-flow (row wrap) :row-gap 1 :column-gap (12) :padding (0 (12))
:border "#3D7068" :border-top-p nil
:bgcolor "#F7FCFB"
(column
(box :content ":gap (1 (14))" :max-width (293) :padding (0 (10)) :border "#3D7068"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293)
:border "#3D7068" :border-top-p nil :bgcolor "#F7FCFB"
:flex-wrap wrap :gap (1 (14))
(item (box :content "A" :width (104) :height 1 :bgcolor "#CDEBE5" :text-align center))
(item (box :content "B" :width (104) :height 1 :bgcolor "#9FD5CB" :text-align center))
(item (box :content "C" :width (104) :height 1 :bgcolor "#71BDB0" :text-align center))))
(column
(box :content ":row-gap 2" :max-width (293) :padding (0 (10)) :border "#3D7068"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293) :border "#3D7068" :border-top-p nil :bgcolor "#F7FCFB"
:flex-wrap wrap :row-gap 2 :column-gap (8)
(item (box :content "A" :width (104) :height 1 :bgcolor "#CDEBE5" :text-align center))
(item (box :content "B" :width (104) :height 1 :bgcolor "#9FD5CB" :text-align center))
(item (box :content "C" :width (104) :height 1 :bgcolor "#71BDB0" :text-align center))))
(column
(box :content ":column-gap (32)" :max-width (293) :padding (0 (10)) :border "#3D7068"
:bgcolor "#FFFDF8" :color "#3D3A35" :text-align center)
(flex :max-width (293) :border "#3D7068" :border-top-p nil :bgcolor "#F7FCFB"
:flex-wrap wrap :row-gap 1 :column-gap (32)
(item (box :content "A" :width (104) :height 1 :bgcolor "#CDEBE5" :text-align center))
(item (box :content "B" :width (104) :height 1 :bgcolor "#9FD5CB" :text-align center))
(item (box :content "C" :width (104) :height 1 :bgcolor "#71BDB0" :text-align center)))))
(spacer :height 1)
(box :content "Item property: :order\nVisual ordering sorts by :order, then source order. The labels expose both values."
:padding (0 (18)) :border "#8E4A63"
:bgcolor "#FFF4F7" :color "#3A1F2A")
(flex :height 5 :padding (0 (12)) :border "#8E4A63"
:border-top-p nil
:bgcolor "#FFF8FA" :column-gap (12) :align-items center
(item :order 3 (box :content "source 1\n:order 3" :width (150) :height 2 :bgcolor "#F2C4D2" :text-align center))
(item :order 1 (box :content "source 2\n:order 1" :width (150) :height 2 :bgcolor "#DEA4B7" :text-align center))
(item :order 2 (box :content "source 3\n:order 2" :width (150) :height 2 :bgcolor "#C87F9A" :text-align center))
(item :order 1 (box :content "source 4\nsame order" :width (150) :height 2 :bgcolor "#A95F7C" :color "#FFFFFF" :text-align center)))
(spacer :height 1)
(box :content "Item sizing: :flex-grow, :flex-shrink, :flex-basis, and child :width\nFlex starts from basis or width/content, then distributes positive or negative space."
:padding (0 (18)) :border "#2F5F7C"
:bgcolor "#F1F8FC" :color "#1C303D")
(column
(box :content "Grow factors: same basis, different growth weights."
:padding (0 (12)) :border "#2F5F7C" :border-top-p nil
:bgcolor "#FFFDF8" :color "#3D3A35")
(flex :padding (0 (12)) :border "#2F5F7C"
:border-top-p nil
:bgcolor "#F8FCFE" :column-gap (10)
(item :flex-grow 1 :flex-basis (120) (box :content "grow 1\nbasis 120" :height 2 :bgcolor "#C7E3F0" :text-align center))
(item :flex-grow 2 :flex-basis (120) (box :content "grow 2\nbasis 120" :height 2 :bgcolor "#96C7DD" :text-align center))
(item :flex-grow 3 :flex-basis (120) (box :content "grow 3\nbasis 120" :height 2 :bgcolor "#5FA4C2" :text-align center))))
(column
(box :content "Shrink factors: overflowing bases lose space by weighted shrink."
:padding (0 (12)) :border "#2F5F7C" :border-top-p nil
:bgcolor "#FFFDF8" :color "#3D3A35")
(flex :padding (0 (12)) :border "#2F5F7C"
:border-top-p nil
:bgcolor "#F8FCFE" :column-gap (10)
(item :flex-shrink 1 :flex-basis (320) (box :content "shrink 1\nbasis 320" :height 2 :bgcolor "#C7E3F0" :text-align center))
(item :flex-shrink 3 :flex-basis (320) (box :content "shrink 3\nbasis 320" :height 2 :bgcolor "#96C7DD" :text-align center))
(item :flex-shrink 5 :flex-basis (320) (box :content "shrink 5\nbasis 320" :height 2 :bgcolor "#5FA4C2" :text-align center))))
(column
(box :content "Basis versus width: explicit basis overrides width; auto basis uses width/content."
:padding (0 (12)) :border "#2F5F7C" :border-top-p nil
:bgcolor "#FFFDF8" :color "#3D3A35")
(flex :padding (0 (12)) :border "#2F5F7C"
:border-top-p nil
:bgcolor "#F8FCFE" :column-gap (10)
(item :flex-basis (120) (box :content "basis 120\nwidth 240" :width (240) :height 2 :bgcolor "#C7E3F0" :text-align center))
(item :flex-basis auto (box :content "basis auto\nwidth 240" :width (240) :height 2 :bgcolor "#96C7DD" :text-align center))
(item :flex (1 1 (160)) (box :content "flex tuple\nbasis 160" :width (240) :height 2 :bgcolor "#5FA4C2" :text-align center))))
(column
(box :content "Intrinsic basis keywords: content, min-content, and max-content derive size from text."
:padding (0 (12)) :border "#2F5F7C" :border-top-p nil
:bgcolor "#FFFDF8" :color "#3D3A35")
(flex :padding (0 (12)) :border "#2F5F7C"
:border-top-p nil
:bgcolor "#F8FCFE" :flex-wrap wrap :row-gap 1 :column-gap (10)
(item :flex-shrink 0 :flex-basis content
(box :content "content\nnatural text" :width (180) :height 2 :bgcolor "#C7E3F0" :text-align center))
(item :flex-shrink 0 :flex-basis min-content
(box :content "min-content\nwrap choices" :width (180) :height 2 :bgcolor "#96C7DD" :text-align center))
(item :flex-shrink 0 :flex-basis max-content
(box :content "max-content\nkeeps phrase width" :width (180) :height 2 :bgcolor "#5FA4C2" :text-align center))))
(spacer :height 1)
(box :content "Item property: :flex shorthand\nSupported forms: number, none, auto, initial, and (grow shrink basis). Longhands can override shorthand parts."
:padding (0 (18)) :border "#52612F"
:bgcolor "#F8FAEF" :color "#293116")
(flex :height 7 :padding (0 (12)) :border "#52612F"
:border-top-p nil
:bgcolor "#FCFDF6" :flex-wrap wrap :row-gap 1 :column-gap (8) :align-items center
(item :flex 1 (box :content ":flex 1\n= 1 1 0" :bgcolor "#DDE8B2" :text-align center))
(item :flex none (box :content ":flex none\n= 0 0 auto" :width (140) :bgcolor "#C5D681" :text-align center))
(item :flex auto (box :content ":flex auto\n= 1 1 auto" :width (140) :bgcolor "#AABE59" :text-align center))
(item :flex initial (box :content ":flex initial\n= 0 1 auto" :width (140) :bgcolor "#8D9E3C" :text-align center))
(item :flex (2 1 (120)) :flex-basis (160)
(box :content ":flex (2 1 120)\nthen basis 160" :bgcolor "#66762D" :color "#FFFFFF" :text-align center)))
(spacer :height 1)
(box :content "Item property: :align-self\nPer-item cross-axis override. auto inherits :align-items; other values replace the container setting."
:padding (0 (18)) :border "#7B4D38"
:bgcolor "#FFF6F0" :color "#392319")
(flex :height 7 :padding (0 (12)) :border "#7B4D38"
:border-top-p nil
:bgcolor "#FFFAF7" :align-items flex-start
:flex-wrap wrap :row-gap 1 :column-gap (10)
(item :align-self auto (box :content "auto\ninherits start" :width (118) :height 2 :bgcolor "#EFC7B6" :text-align center))
(item :align-self flex-start (box :content "flex-start" :width (118) :height 2 :bgcolor "#DBA58D" :text-align center))
(item :align-self center (box :content "center" :width (118) :height 2 :bgcolor "#BF7F63" :text-align center))
(item :align-self flex-end (box :content "flex-end" :width (118) :height 2 :bgcolor "#9D6048" :color "#FFFFFF" :text-align center))
(item :align-self stretch (box :content "stretch\nauto cross" :width (118) :bgcolor "#7B4D38" :color "#FFFFFF" :text-align center))
(item :align-self normal (box :content "normal\nstretch-like" :width (118) :bgcolor "#5D3929" :color "#FFFFFF" :text-align center)))
(flex :height 6 :padding (0 (12)) :border "#7B4D38"
:border-top-p nil
:bgcolor "#FFFAF7" :align-items flex-start
:flex-wrap wrap :row-gap 1 :column-gap (10)
(item :align-self start (box :content "start" :width (118) :height 2 :bgcolor "#EFC7B6" :text-align center))
(item :align-self end (box :content "end" :width (118) :height 2 :bgcolor "#DBA58D" :text-align center))
(item :align-self self-start (box :content "self-start" :width (118) :height 2 :bgcolor "#BF7F63" :text-align center))
(item :align-self self-end (box :content "self-end" :width (118) :height 2 :bgcolor "#9D6048" :color "#FFFFFF" :text-align center))
(item :align-self baseline (box :content "baseline" :width (118) :height 2 :bgcolor "#7B4D38" :color "#FFFFFF" :text-align center)))
(spacer :height 1)
(box :content "Summary\nUse direction and wrap to create lines, basis/width to choose starting sizes, grow/shrink to distribute space, then justify/align properties to pack items and lines."
:padding (1 (24)) :border "#2F3437"
:bgcolor "#FAF7F0" :color "#1F2328" :text-align center))

View File

@ -1,65 +0,0 @@
(column
:margin-left 1 :margin-top 1 :padding (1 2 1 2) :bgcolor "linen"
(box :content "Ebox Responsive Layout\nDrag the preview window: sections stretch to the current viewport, cards reflow, and text wraps without changing the window layout."
:padding (1 (18)) :border "#334155" :text-align center
:color "#000" :bgcolor "ghost white")
(spacer :height 1)
(box :content "No explicit viewport width here\nThis page mostly omits :width. The Playground binds the current preview width while rendering, so auto width behaves like a responsive container."
:padding (0 (16)) :border "#0F766E"
:bgcolor "#F0FDFA" :color "#134E4A" :wrap-mode word)
(spacer :height 1)
(box :content "Adaptive toolbar\nButtons below keep stable sizes. The row packs as many as fit and then wraps."
:padding (0 (16)) :border "#EA580C" :border-bottom-p nil
:bgcolor "#FFF7ED" :color "#431407" :wrap-mode word)
(flex :flex-flow (row wrap) :column-gap (8) :row-gap 1
:padding (1 (8)) :border "#FDBA74" :bgcolor "#FFFBF5"
(box :content "Render" :width (96) :padding (0 (8))
:border "#EA580C" :bgcolor "#FED7AA" :color "#111827"
:text-align center)
(box :content "Save" :width (88) :padding (0 (8))
:border "#D97706" :bgcolor "#FEF3C7" :color "#111827"
:text-align center)
(box :content "Format" :width (96) :padding (0 (8))
:border "#16A34A" :bgcolor "#DCFCE7" :color "#14532D"
:text-align center)
(box :content "Sample" :width (104) :padding (0 (8))
:border "#0284C7" :bgcolor "#E0F2FE" :color "#0C4A6E"
:text-align center)
(box :content "Write" :width (88) :padding (0 (8))
:border "#9333EA" :bgcolor "#F3E8FF" :color "#3B0764"
:text-align center))
(spacer :height 1)
(box :content "Responsive cards\nEach card starts from a flex-basis, grows when space is available, and wraps to new rows when the preview becomes narrow."
:padding (0 (16)) :border "#2563EB" :border-bottom-p nil
:bgcolor "#EFF6FF" :color "#1E3A8A" :wrap-mode word)
(flex :flex-flow (row wrap) :column-gap (12) :row-gap 1
:padding (1 (8)) :border "#93C5FD" :bgcolor "#F8FBFF"
:justify-content center
(item :flex (1 1 (210))
(box :content "Overview\nThe row uses the current preview width automatically."
:min-width (180) :max-width (280)
:padding (0 (10)) ;; :border "#2563EB"
:bgcolor "#DBEAFE" :color "#111827" :wrap-mode word))
(item :flex (1 1 (210))
(box :content "Editor\nDrag slowly or quickly; resize rendering is debounced."
:min-width (180) :max-width (300)
:padding (0 (10)) ;; :border "#4F46E5"
:bgcolor "#E0E7FF" :color "#111827" :wrap-mode word))
(item :flex (1 1 (210))
(box :content "Preview\nThe preview buffer rerenders after the viewport settles."
:min-width (180) :max-width (320)
:padding (0 (10)) ;; :border "#7C3AED"
:bgcolor "#EDE9FE" :color "#111827" :wrap-mode word))
(item :flex (1 1 (210))
(box :content "Flow\nCards keep readable widths instead of overflowing."
:min-width (180) :max-width (280)
:padding (0 (10)) ;; :border "#0891B2"
:bgcolor "#CFFAFE" :color "#164E63" :wrap-mode word)))
(spacer :height 1)
(box :content "What to watch\nNarrow preview: cards stack and text wraps. Wide preview: cards share a row and expand up to max-width. The source/preview split is preserved."
:padding (0 (16)) :border "#475569"
:bgcolor "#F8FAFC" :color "#0F172A" :wrap-mode word))

View File

@ -17,7 +17,7 @@
(let ((source (ebox-ci-test--read "Makefile"))) (let ((source (ebox-ci-test--read "Makefile")))
(dolist (target '("check:" "ci:" "load:" "compile:" "test:" (dolist (target '("check:" "ci:" "load:" "compile:" "test:"
"core-tests:" "grid-tests:" "ebox-commit-tests:" "core-tests:" "grid-tests:" "ebox-commit-tests:"
"examples-tests:" "docs-contract-tests:" "docs-contract-tests:"
"ci-contract-tests:" "native-rust-tests:" "ci-contract-tests:" "native-rust-tests:"
"native-build:" "diff-check:")) "native-build:" "diff-check:"))
(should (string-match-p (concat "^" (regexp-quote target)) source))) (should (string-match-p (concat "^" (regexp-quote target)) source)))

View File

@ -22,13 +22,8 @@
"ebox-layout.el" "ebox-flex.el" "ebox-grid.el" "ebox-layout.el" "ebox-flex.el" "ebox-grid.el"
"ebox-buffer-backend.el" "ebox-incremental.el" "ebox-dsl.el" "ebox-buffer-backend.el" "ebox-incremental.el" "ebox-dsl.el"
"ebox-selector.el" "ebox-native-reflow.el" "ebox-selector.el" "ebox-native-reflow.el"
"examples/ebox-basic-examples.el"
"examples/playground/basic.ebox"
"examples/playground/comprehensive.ebox"
"examples/playground/flex-reference.ebox"
"examples/playground/responsive-reference.ebox"
"tests/ebox-core-render-tests.el" "tests/ebox-grid-tests.el" "tests/ebox-core-render-tests.el" "tests/ebox-grid-tests.el"
"tests/ebox-commit-tests.el" "tests/ebox-examples-tests.el" "tests/ebox-commit-tests.el"
"tests/ebox-dsl-tests.el" "tests/ebox-flex-tests.el" "tests/ebox-dsl-tests.el" "tests/ebox-flex-tests.el"
"tests/ebox-selector-tests.el" "tests/ebox-package-tests.el" "tests/ebox-selector-tests.el" "tests/ebox-package-tests.el"
"tests/ebox-visual-check-tests.el" "tests/ebox-docs-contract-tests.el" "tests/ebox-visual-check-tests.el" "tests/ebox-docs-contract-tests.el"
@ -42,7 +37,7 @@
(defconst ebox-docs-test--targets (defconst ebox-docs-test--targets
'("check" "ci" "load" "compile" "core-tests" "grid-tests" '("check" "ci" "load" "compile" "core-tests" "grid-tests"
"ebox-commit-tests" "visual-check-tests" "package-tests" "ebox-commit-tests" "visual-check-tests" "package-tests"
"selector-tests" "examples-tests" "dsl-tests" "flex-tests" "selector-tests" "dsl-tests" "flex-tests"
"docs-contract-tests" "ci-contract-tests" "visual-check" "docs-contract-tests" "ci-contract-tests" "visual-check"
"native-rust-tests" "native-build" "package-lint" "diff-check") "native-rust-tests" "native-build" "package-lint" "diff-check")
"Make targets that must be documented and executable.") "Make targets that must be documented and executable.")

View File

@ -90,6 +90,21 @@
:overflow scroll)))) :overflow scroll))))
(should (= (length (ebox-string-lines (ebox-render node))) 4)))) (should (= (length (ebox-string-lines (ebox-render node))) 4))))
(ert-deftest ebox-build-unquotes-static-list-property-values ()
"The DSL should accept quoted list values used by `etaf-view' forms."
(let* ((node (ebox-build
'(grid :width '(120)
:grid-template-columns '((40) 1fr)
:gap '(0 (4))
(box :content "A")
(box :content "B"))))
(props (plist-get node :raw-props))
(line (car (ebox-string-lines (ebox-render node)))))
(should (equal (plist-get props :width) '(120)))
(should (equal (plist-get props :grid-template-columns)
'((40) 1fr)))
(should (= (ebox--string-pixel-width line) 120))))
(ert-deftest ebox-build-composes-fixed-viewport-shell-around-scrollbox () (ert-deftest ebox-build-composes-fixed-viewport-shell-around-scrollbox ()
"A fixed viewport shell can reserve chrome while an inner box scrolls." "A fixed viewport shell can reserve chrome while an inner box scrolls."
(let* ((ebox-viewport-height 6) (let* ((ebox-viewport-height 6)

View File

@ -1,32 +0,0 @@
;;; ebox-examples-tests.el --- Ebox example contracts -*- lexical-binding: t; -*-
(require 'ert)
(require 'ebox)
(defconst ebox-examples-test--root
(expand-file-name ".." (file-name-directory (or load-file-name buffer-file-name)))
"Repository root used by example tests.")
(load-file (expand-file-name "examples/ebox-basic-examples.el"
ebox-examples-test--root))
(ert-deftest ebox-basic-example-builds-a-public-tree ()
"The basic example should build through public constructors."
(let ((node (ebox-basic-examples-build)))
(should (listp node))
(should (string-match-p "Ebox"
(substring-no-properties (ebox-render node))))))
(ert-deftest ebox-dsl-examples-render-without-playground-code ()
"The maintained DSL fixtures should render through the Ebox facade."
(dolist (file '("basic.ebox" "comprehensive.ebox" "flex-reference.ebox"
"responsive-reference.ebox"))
(let ((form
(with-temp-buffer
(insert-file-contents
(expand-file-name (concat "examples/playground/" file)
ebox-examples-test--root))
(read (current-buffer)))))
(should (stringp (ebox-render (ebox-build form)))))))
(provide 'ebox-examples-tests)
;;; ebox-examples-tests.el ends here