feat: turn Ebox playground into layout gallery
Replace the bare fixture with a 720 px three-section Grid and Flex reference, add paired design documentation, and lock the compact-window width contract.\n\nVerified with make check and a single-window GUI capture at a 784 px body width; rendered content stayed at 720 px.
This commit is contained in:
parent
a63dd61f78
commit
35f8eb80ef
82
DESIGN.md
Normal file
82
DESIGN.md
Normal file
@ -0,0 +1,82 @@
|
||||
# Design
|
||||
|
||||
## Source of truth
|
||||
- Status: Active
|
||||
- Last refreshed: 2026-08-05
|
||||
- Primary product surfaces: The buffer opened by `ebox-playground-open` and its public layout example tree.
|
||||
- Evidence reviewed: `ebox-playground.el`, `tests/ebox-playground-tests.el`, `README.md`, `README.zh-CN.md`, the rendered current example, and `../emacs-box/examples/playground/flex-reference.ebox` rendered fullscreen in GUI Emacs.
|
||||
|
||||
## Brand
|
||||
- Personality: Precise, modern, restrained, and educational.
|
||||
- Trust signals: Pixel-aligned sections, exact Grid placement, explicit text contrast, public-only construction, and a layout that remains legible without theme-specific faces.
|
||||
- Avoid: Bare unstyled fixture text, low-contrast pastel typography, arbitrary rainbow colors, oversized blank canvas, and private diagnostic internals.
|
||||
|
||||
## Product goals
|
||||
- Goals: Demonstrate Ebox Grid and composition in a polished standalone example; give the low-level package a visual quality bar consistent with the Flex reference.
|
||||
- Non-goals: ETAF Components, application state, browser imitation, or a second Playground framework.
|
||||
- Success signals: The first frame clearly communicates hierarchy and Grid behavior; every tinted block has readable text; no content crosses the visible viewport.
|
||||
|
||||
## Personas and jobs
|
||||
- Primary personas: Ebox authors and maintainers.
|
||||
- User jobs: Open one buffer, understand the public node contract, and see fixed/fractional tracks, placement, and composition rendered attractively.
|
||||
- Key contexts of use: GUI Emacs, fullscreen demonstrations, and ERT rendering checks.
|
||||
|
||||
## Information architecture
|
||||
- Primary navigation: None; this is one self-contained reference surface.
|
||||
- Core routes/screens: One header and three capability sections.
|
||||
- Content hierarchy: Title and description, labeled capability bands, then small contrasting examples.
|
||||
|
||||
## Design principles
|
||||
- Principle 1: Mirror the Flex reference's title—explanation—example rhythm.
|
||||
- Principle 2: Use one restrained semantic accent per capability section.
|
||||
- Principle 3: Keep the example public-only and small enough to understand from one source file.
|
||||
- Tradeoffs: Prefer a compact representative gallery over exhaustive property coverage.
|
||||
|
||||
## Visual language
|
||||
- Color: Warm paper canvas with terracotta, sage, slate blue, and violet section families; explicit dark ink or white text according to contrast.
|
||||
- Typography: Configured monospace, bold only for titles and short labels.
|
||||
- Spacing/layout rhythm: One-line vertical gaps, 12 px horizontal gaps, 16–24 px section padding.
|
||||
- Shape/radius/elevation: Square one-pixel borders; no shadows or fake radius.
|
||||
- Motion: None.
|
||||
- Imagery/iconography: Text-only.
|
||||
|
||||
## Components
|
||||
- Existing components to reuse: `ebox-create`, `ebox-column`, `ebox-row`, `ebox-flex`, `ebox-grid`, and `ebox-spacer`.
|
||||
- New/changed components: Header band, capability section band, fixed/fractional track cards, placement cards, and public-contract footer.
|
||||
- Variants and states: Static semantic color families only.
|
||||
- Token/component ownership: Palette constants and example composition remain in `ebox-playground.el`; Ebox owns rendering.
|
||||
|
||||
## Accessibility
|
||||
- Target standard: High contrast and readable labels in ordinary GUI Emacs themes.
|
||||
- Keyboard/focus behavior: No interactive controls in this static example.
|
||||
- Contrast/readability: Explicit foreground on every tinted surface.
|
||||
- Screen-reader semantics: Plain descriptive text remains present in the buffer.
|
||||
- Reduced motion and sensory considerations: No motion.
|
||||
|
||||
## Responsive behavior
|
||||
- Supported breakpoints/devices: GUI Emacs windows with at least 760 px of body width, from compact frames to fullscreen desktop widths.
|
||||
- Layout adaptations: The gallery owns one fixed 720 px content canvas; nested Grid and Flex sections divide that width through public Ebox layout behavior instead of reading the viewport independently.
|
||||
- Touch/hover differences: None.
|
||||
|
||||
## Interaction states
|
||||
- Loading: Not applicable.
|
||||
- Empty: Not applicable.
|
||||
- Error: Public Ebox errors surface normally.
|
||||
- Success: The rendered gallery itself is the success state.
|
||||
- Disabled: Not applicable.
|
||||
- Offline/slow network, if applicable: Not applicable.
|
||||
|
||||
## Content voice
|
||||
- Tone: Concise, factual, and self-explaining.
|
||||
- Terminology: Ebox, node, Grid, fixed track, fractional track, placement, public API.
|
||||
- Microcopy rules: Explain what a visible section proves in one sentence.
|
||||
|
||||
## Implementation constraints
|
||||
- Framework/styling system: Emacs 29.1+ and public Ebox constructors/properties only.
|
||||
- Design-token constraints: Reuse the Flex reference's restrained color families without introducing a theme package.
|
||||
- Performance constraints: One synchronous render, no timers or background work.
|
||||
- Compatibility constraints: No ETAF dependency and no `ebox--*` calls.
|
||||
- Test/screenshot expectations: `make check` passes and a fullscreen one-window screenshot shows complete, unclipped labeled sections.
|
||||
|
||||
## Open questions
|
||||
- [ ] Add more standalone examples only when each teaches a distinct public Ebox responsibility / maintainer / prevents fixture sprawl.
|
||||
82
DESIGN.zh-CN.md
Normal file
82
DESIGN.zh-CN.md
Normal file
@ -0,0 +1,82 @@
|
||||
# 设计
|
||||
|
||||
## 真相来源
|
||||
- 状态:有效
|
||||
- 最近更新:2026-08-05
|
||||
- 主要产品界面:`ebox-playground-open` 打开的缓冲区及其公开布局示例树。
|
||||
- 已审阅证据:`ebox-playground.el`、`tests/ebox-playground-tests.el`、`README.md`、`README.zh-CN.md`、当前示例的实际渲染,以及在 GUI Emacs 中全屏渲染的 `../emacs-box/examples/playground/flex-reference.ebox`。
|
||||
|
||||
## 品牌
|
||||
- 个性:精确、现代、克制、适合教学。
|
||||
- 信任信号:像素对齐的分区、准确的 Grid 放置、明确的文字对比度、只使用公开 API,以及不依赖主题 face 也清晰的布局。
|
||||
- 避免:没有样式的裸 fixture 文本、低对比度的浅色文字、随意的彩虹配色、过大的空白画布,以及私有诊断接口。
|
||||
|
||||
## 产品目标
|
||||
- 目标:用一个精致的独立示例演示 Ebox Grid 与组合能力,让底层包达到 Flex 参考示例的视觉质量标准。
|
||||
- 非目标:ETAF Component、应用状态、浏览器仿制品,或第二套 Playground 框架。
|
||||
- 成功信号:第一屏能清楚表达层级和 Grid 行为;每个带色区块的文字都清晰可读;没有内容越过可见视口。
|
||||
|
||||
## 用户与任务
|
||||
- 主要用户:Ebox 作者和维护者。
|
||||
- 用户任务:打开一个缓冲区,理解公开节点契约,并直观看到固定/分数轨道、显式放置和组合渲染。
|
||||
- 主要使用场景:GUI Emacs、全屏演示和 ERT 渲染检查。
|
||||
|
||||
## 信息架构
|
||||
- 主导航:无;这是一个自包含的参考界面。
|
||||
- 核心界面:一个标题区和三个能力分区。
|
||||
- 内容层级:标题与说明、带编号的能力标题带、随后是小型对比示例。
|
||||
|
||||
## 设计原则
|
||||
- 原则 1:延续 Flex 参考示例的“标题—解释—示例”节奏。
|
||||
- 原则 2:每个能力分区只使用一组克制的语义强调色。
|
||||
- 原则 3:示例只使用公开 API,并保持在一个源文件内即可理解的规模。
|
||||
- 取舍:优先紧凑且有代表性的画廊,而不是穷举所有属性。
|
||||
|
||||
## 视觉语言
|
||||
- 颜色:暖纸色画布,搭配陶土色、鼠尾草绿、灰蓝色和紫色分区;按对比度明确设置深色或白色文字。
|
||||
- 字体:使用当前等宽字体,只在标题和短标签中加粗。
|
||||
- 间距与布局节奏:一行垂直间隔、12 px 水平间隔、16–24 px 分区内边距。
|
||||
- 形状/圆角/层级:方形一像素边框;不使用阴影或伪圆角。
|
||||
- 动效:无。
|
||||
- 图像与图标:纯文本。
|
||||
|
||||
## 组件
|
||||
- 复用的现有组件:`ebox-create`、`ebox-column`、`ebox-row`、`ebox-flex`、`ebox-grid` 和 `ebox-spacer`。
|
||||
- 新增/调整的组件:标题带、能力分区标题带、固定/分数轨道卡片、放置卡片和公开契约页脚。
|
||||
- 变体与状态:仅使用静态语义色组。
|
||||
- Token/组件归属:配色常量和示例组合保留在 `ebox-playground.el`;Ebox 负责渲染。
|
||||
|
||||
## 无障碍
|
||||
- 目标标准:在常见 GUI Emacs 主题下保持高对比度和标签可读性。
|
||||
- 键盘/焦点行为:该静态示例没有交互控件。
|
||||
- 对比度/可读性:每个带色表面都显式设置前景色。
|
||||
- 屏幕阅读语义:描述性纯文本保留在缓冲区中。
|
||||
- 减少动态与感官刺激:无动态效果。
|
||||
|
||||
## 响应式行为
|
||||
- 支持的断点/设备:正文宽度至少为 760 px 的 GUI Emacs 窗口,从紧凑窗口到全屏桌面。
|
||||
- 布局适配:画廊统一拥有一个固定的 720 px 内容画布;嵌套 Grid 和 Flex 通过公开 Ebox 布局行为划分该宽度,而不是各自读取视口。
|
||||
- 触控/悬停差异:无。
|
||||
|
||||
## 交互状态
|
||||
- 加载:不适用。
|
||||
- 空状态:不适用。
|
||||
- 错误:公开 Ebox 错误正常向外暴露。
|
||||
- 成功:完整渲染画廊本身就是成功状态。
|
||||
- 禁用:不适用。
|
||||
- 离线/慢网络:不适用。
|
||||
|
||||
## 内容语气
|
||||
- 语气:简洁、事实明确、自解释。
|
||||
- 术语:Ebox、节点、Grid、固定轨道、分数轨道、放置、公开 API。
|
||||
- 微文案规则:用一句话说明每个可见分区证明了什么。
|
||||
|
||||
## 实现约束
|
||||
- 框架/样式系统:Emacs 29.1+,只使用公开 Ebox 构造函数和属性。
|
||||
- 设计 Token 约束:复用 Flex 参考示例的克制色系,不引入主题包。
|
||||
- 性能约束:单次同步渲染,不使用定时器或后台任务。
|
||||
- 兼容性约束:不依赖 ETAF,不调用 `ebox--*`。
|
||||
- 测试/截图要求:`make check` 通过;单窗口 GUI 截图显示完整且未裁剪的标签分区。
|
||||
|
||||
## 开放问题
|
||||
- [ ] 只有当每个新增示例都能讲清一种独立的公开 Ebox 责任时才继续扩展示例,避免 fixture 膨胀。
|
||||
@ -1,6 +1,8 @@
|
||||
# ebox-playground
|
||||
|
||||
`ebox-playground` is the independent developer example package for Ebox. It uses only public Ebox constructors and demonstrates fixed, fractional, two-dimensional, and placed layout. It is separate from `etaf-playground`, which demonstrates the higher-level Component and Runtime framework.
|
||||
`ebox-playground` is the independent developer example package for Ebox. It uses only public Ebox constructors and presents a polished three-part gallery for fixed and fractional tracks, explicit Grid placement, and public Column/Flex/Grid composition. Its restrained terracotta, sage, blue, and violet palette follows the visual standard of the original Flex reference without depending on theme-specific faces.
|
||||
|
||||
The gallery owns one definite 720 px canvas so its nested Grid examples cannot each reinterpret the surrounding viewport width. It fits a compact GUI Emacs window with at least 760 px of body width. `etaf-playground` remains the separate higher-level Component and Runtime showcase.
|
||||
|
||||
```elisp
|
||||
(require 'ebox-playground)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
# ebox-playground
|
||||
|
||||
`ebox-playground` 是 Ebox 的独立开发示例包,只使用 Ebox 公共构造函数,展示固定轨道、分数轨道、二维 Grid 和显式放置。它与展示高层 Component/Runtime 框架的 `etaf-playground` 分开。
|
||||
`ebox-playground` 是 Ebox 的独立开发示例包,只使用 Ebox 公共构造函数,并通过一套精心设计的三段式画廊展示固定与分数轨道、显式 Grid 放置,以及公开的 Column/Flex/Grid 组合能力。克制的陶土色、鼠尾草绿、蓝色与紫色配色延续了原 Flex 参考示例的视觉标准,同时不依赖特定主题 face。
|
||||
|
||||
画廊统一拥有一个明确的 720 px 画布,因此嵌套 Grid 不会各自重新解释外层视口宽度。只要 GUI Emacs 的正文区域不小于 760 px,它就能完整显示。高层 Component/Runtime 展示仍由独立的 `etaf-playground` 提供。
|
||||
|
||||
```elisp
|
||||
(require 'ebox-playground)
|
||||
|
||||
@ -19,22 +19,132 @@
|
||||
(defconst ebox-playground-buffer-name "*Ebox Playground*"
|
||||
"Default buffer name used by `ebox-playground-open'.")
|
||||
|
||||
(defun ebox-playground--header ()
|
||||
"Return the gallery header."
|
||||
(ebox-create
|
||||
:content "Ebox Playground\nPixel-precise layout, composed only from public nodes."
|
||||
:width '(720)
|
||||
:padding '(1 (18))
|
||||
:border "#8F432F"
|
||||
:color "#252A2E"
|
||||
:bgcolor "#FFFDF8"
|
||||
:face 'bold
|
||||
:text-align 'center))
|
||||
|
||||
(defun ebox-playground--section-heading
|
||||
(number title copy border background)
|
||||
"Return a section heading for NUMBER, TITLE, COPY, BORDER, and BACKGROUND."
|
||||
(ebox-create
|
||||
:content (format "%s %s\n%s" number title copy)
|
||||
:width '(720)
|
||||
:padding '(0 (16))
|
||||
:border border
|
||||
:border-bottom-p nil
|
||||
:color "#252A2E"
|
||||
:bgcolor background
|
||||
:wrap-mode 'word))
|
||||
|
||||
(defun ebox-playground--track-grid ()
|
||||
"Return the fixed and fractional track example."
|
||||
(ebox-grid
|
||||
:width '(718)
|
||||
:grid-template-columns '((220) 1fr)
|
||||
:gap '(1 (12))
|
||||
:border "#C97252"
|
||||
:bgcolor "#FFF9F5"
|
||||
(ebox-create :content "FIXED TRACK\n220 px stays exact"
|
||||
:width '(220) :padding '(1 0) :text-align 'center
|
||||
:color "#252A2E" :bgcolor "#F1D4C9")
|
||||
(ebox-create :content "FRACTIONAL TRACK\n1fr receives the remaining width"
|
||||
:width '(486) :padding '(1 0) :text-align 'center
|
||||
:color "#252A2E" :bgcolor "#F9E8E0")
|
||||
(ebox-create :content "The Grid owns one definite width; its tracks divide that space without competing viewport calculations."
|
||||
:grid-column '(1 :span 2) :width '(718) :padding '(1 0)
|
||||
:color "#663322" :bgcolor "#FFFDF8"
|
||||
:text-align 'center :wrap-mode 'word)))
|
||||
|
||||
(defun ebox-playground--placement-grid ()
|
||||
"Return the explicit Grid placement example."
|
||||
(ebox-grid
|
||||
:width '(718)
|
||||
:grid-template-columns '((210) (210) 1fr)
|
||||
:gap '(1 (10))
|
||||
:border "#6D8A73"
|
||||
:bgcolor "#F7FBF7"
|
||||
(ebox-create :content "SPAN 1 → 3" :grid-column '(1 :span 2)
|
||||
:width '(430) :padding '(1 0) :text-align 'center
|
||||
:color "#24422D" :bgcolor "#DCEBDD")
|
||||
(ebox-create :content "COLUMN 3" :grid-column 3 :grid-row 1
|
||||
:width '(278) :padding '(1 0) :text-align 'center
|
||||
:color "#24422D" :bgcolor "#C8DDCB")
|
||||
(ebox-create :content "ROW 2 / COL 1" :grid-column 1 :grid-row 2
|
||||
:width '(210) :padding '(1 0) :text-align 'center
|
||||
:color "#24422D" :bgcolor "#EDF5EE")
|
||||
(ebox-create :content "AUTO PLACED" :width '(210) :padding '(1 0)
|
||||
:text-align 'center
|
||||
:color "#24422D" :bgcolor "#E3F0E5")
|
||||
(ebox-create :content "STABLE CELLS" :width '(278) :padding '(1 0)
|
||||
:text-align 'center
|
||||
:color "#24422D" :bgcolor "#D4E6D7")))
|
||||
|
||||
(defun ebox-playground--composition-flex ()
|
||||
"Return the public node composition example."
|
||||
(ebox-flex
|
||||
:width '(720)
|
||||
:flex-flow '(row wrap)
|
||||
:gap '(1 (12))
|
||||
:padding '(1 (12))
|
||||
:border "#4E7890"
|
||||
:bgcolor "#F7FAFC"
|
||||
(ebox-create :content "COLUMN\nvertical composition"
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
|
||||
:min-width '(180) :padding '(1 (12))
|
||||
:color "#193846" :bgcolor "#D9EAF2")
|
||||
(ebox-create :content "FLEX\nwrap and distribute"
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
|
||||
:min-width '(180) :padding '(1 (12))
|
||||
:color "#3F3655" :bgcolor "#E7E2F1")
|
||||
(ebox-create :content "GRID\nplace in two dimensions"
|
||||
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
|
||||
:min-width '(180) :padding '(1 (12))
|
||||
:color "#6B3020" :bgcolor "#F1D4C9")))
|
||||
|
||||
(defun ebox-playground--footer ()
|
||||
"Return the public-contract footer."
|
||||
(ebox-create
|
||||
:content "PUBLIC CONTRACT ebox-create · ebox-column · ebox-flex · ebox-grid · ebox-spacer"
|
||||
:width '(720)
|
||||
:padding '(0 (16))
|
||||
:border "#8D887F"
|
||||
:color "#4D5651"
|
||||
:bgcolor "#EEEAE2"
|
||||
:wrap-mode 'word))
|
||||
|
||||
;;;###autoload
|
||||
(defun ebox-playground-view ()
|
||||
"Return a public Ebox example layout node without buffer side effects."
|
||||
(ebox-grid
|
||||
:width '(640)
|
||||
:grid-template-columns '((200) 1fr)
|
||||
:grid-template-rows '(1 1 1)
|
||||
:gap '(1 (12))
|
||||
:padding '(1 2)
|
||||
:border '((1) solid "#687386")
|
||||
(ebox-create :content "Ebox Playground" :face 'bold)
|
||||
(ebox-create :content "A pixel-precise two-dimensional layout")
|
||||
(ebox-create :content "Row / column / flex / grid share the public node contract")
|
||||
(ebox-create :content "Fixed and fractional tracks")
|
||||
(ebox-create :content "Placement" :grid-column 1 :grid-row 3)
|
||||
(ebox-create :content "No private calls" :grid-column 2 :grid-row 3)))
|
||||
(ebox-column
|
||||
(ebox-playground--header)
|
||||
(ebox-spacer :height 1)
|
||||
(ebox-playground--section-heading
|
||||
"01" "FIXED + FRACTIONAL"
|
||||
"A definite Grid width divides into one exact track and one flexible track."
|
||||
"#C97252" "#F1D4C9")
|
||||
(ebox-playground--track-grid)
|
||||
(ebox-spacer :height 1)
|
||||
(ebox-playground--section-heading
|
||||
"02" "EXPLICIT PLACEMENT"
|
||||
"Span columns, target cells, and leave the remaining nodes to auto placement."
|
||||
"#6D8A73" "#DCEBDD")
|
||||
(ebox-playground--placement-grid)
|
||||
(ebox-spacer :height 1)
|
||||
(ebox-playground--section-heading
|
||||
"03" "PUBLIC COMPOSITION"
|
||||
"Column, Flex, and Grid share the same public node contract."
|
||||
"#4E7890" "#D9EAF2")
|
||||
(ebox-playground--composition-flex)
|
||||
(ebox-spacer :height 1)
|
||||
(ebox-playground--footer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun ebox-playground-open (&optional buffer-name)
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
|
||||
(ert-deftest ebox-playground-uses-only-public-layout-apis ()
|
||||
"The example should build and render through the public Ebox entry point."
|
||||
(let ((node (ebox-playground-view)))
|
||||
(should (eq (plist-get node :ebox-type) 'grid))
|
||||
(should (string-match-p "Ebox Playground"
|
||||
(substring-no-properties (ebox-render node))))))
|
||||
(let* ((node (ebox-playground-view))
|
||||
(rendered (substring-no-properties (ebox-render node))))
|
||||
(should (eq (plist-get node :ebox-type) 'stack))
|
||||
(dolist (label '("Ebox Playground" "FIXED + FRACTIONAL"
|
||||
"EXPLICIT PLACEMENT" "PUBLIC COMPOSITION"))
|
||||
(should (string-match-p (regexp-quote label) rendered)))))
|
||||
|
||||
(ert-deftest ebox-playground-opens-and-closes-buffer ()
|
||||
"The public open and close commands should own their buffer lifecycle."
|
||||
@ -21,6 +23,17 @@
|
||||
(should (string-match-p "Ebox Playground" (buffer-string)))))
|
||||
(ebox-playground-close name))))
|
||||
|
||||
(ert-deftest ebox-playground-fits-a-compact-gui-window ()
|
||||
"Keep the gallery wide enough to teach Grid without crossing 800 px frames."
|
||||
(let* ((node (ebox-playground-view))
|
||||
(gallery-width (plist-get (car (plist-get node :children)) :width))
|
||||
(rendered (ebox-render node))
|
||||
(widths (mapcar #'ebox-string-pixel-width
|
||||
(ebox-string-lines rendered)))
|
||||
(maximum (apply #'max widths)))
|
||||
(should (= maximum gallery-width))
|
||||
(should (<= gallery-width 760))))
|
||||
|
||||
(provide 'ebox-playground-tests)
|
||||
|
||||
;;; ebox-playground-tests.el ends here
|
||||
|
||||
Loading…
Reference in New Issue
Block a user