Expo 布局契约
本契约描述 OINK v1.3.1。主题开发使用同级 checkout;发布验收在关闭替换与 工作区后验证公开模块。消费站升级与部署分别检查。
本页规定实现必须遵循的行为。创建内容请阅读 Expo 相册, 设计理由见设计决策。
内容与导航
type: expo 选择相册布局,分区可通过 cascade 将其应用到下级页面。
一个相册对应一个 Hugo 叶子页面包,包含 index.md 和 images/ 目录。
图片元数据放在相册 front matter 的 resources 中,无需为每张图片单独创建内容文件。
分区页面列出下级主题和相册。共享导航树支持嵌套分区、当前语言和移动端抽屉。
Expo 使用全部可用页面宽度,没有右侧大纲。页面显式设置的 page_width 可以覆盖宽度。
顶部导航栏遵循共享站点与页面设置,Expo 默认不会将其关闭。
与 Book 相同,没有设置 sidebar_root_for 祖先的相册以其顶层分区而非首页树作为侧栏与根切换器的根。
配置
expo/config.html 按以下顺序解析配置:
- 使用页面的
expo块,包括 Hugo cascade 提供的配置块。 - 页面没有该块时,使用站点的
params.ui.expo。 - 逐字段验证;字段缺失或无效时,使用对应的内置默认值。
页面块整体替换站点块,解析器不会合并这两个块中的字段。
| 字段 | 可选值 | 内置默认值 |
|---|---|---|
images |
字符串,匹配页面包内的图片资源 | images/* |
mode |
wall、cards 或 seamless |
wall |
size |
160 至 360 的整数,单位为 CSS 像素 | 240 |
无效配置产生警告并使用回退值。带有 --panicOnWarning 的构建会因该警告失败。
尺寸是布局计算的目标值,不会同时指定每个图片块的宽和高。
图片资源
expo/items.html 为所有输出选择图片、解析元数据。它使用 Hugo 资源元数据与共享
媒体解析器,并排除非图片资源。可选的 params.original 必须指向同一页面包中的图片。
构建过程不会抓取远程相册图片。
| 值 | 规则 |
|---|---|
| 标题 | 使用资源标题;未指定时由文件名生成 |
| 替代文字 | 使用 params.alt;为空或缺失时回退到标题 |
| 说明与署名 | 将 params.caption 和 params.byline 作为纯文本处理 |
| 图片顺序 | 按数字 params.weight 升序排列,默认为零 |
| 同权重图片 | 保持资源集合的名称顺序 |
| 显式 ID | 以 ASCII 字母或数字开头,后续字符还可使用 _ 或 - |
| 图片锚点 | 在 ID 前添加 img-;未指定 ID 时使用资源名称的哈希 |
ID 必须在相册内唯一。无效或重复 ID 产生警告,并回退到文件名哈希。
重命名文件会改变默认锚点。需要长期引用时,应保持显式 ID 与相册 URL 不变。
ID 可使用字符串或 YAML 整数。回退哈希也被占用时,追加第一个未使用的数字后缀,
确保开发构建仍输出唯一锚点。使用 --panicOnWarning 发布时,无效或重复的元数据会使构建失败。
可处理图片的宽或高超过 960 像素时,生成不超过 960 × 960 像素的预览。 GIF 也会生成静态预览,用于暂停和减少动态效果。动态源图仍然可用,原始文件保持不变。
相册卡片使用排序后的第一张图片作为封面。分区没有自身图片时,从下级相册中选取 首张可用图片。封面使用 4:3 画框,可能裁切预览。大型收藏由作者拆成相册; Expo 不会自动对图片资源分页。
显式图片 ID 接受字符串或 YAML 整数。无效或重复 ID 会告警;文件名哈希也被
占用时,回退使用第一个未占用的数字后缀。expo.images 必须是非空的资源
通配式;params.original 即使为 false 或空值,也必须通过页面包图片校验。
严格发布会拒绝这些诊断。
布局与控件
| 模式 | 排列与说明 |
|---|---|
| 展墙 | 按行排列图片块,间距为 6 CSS 像素;支持悬浮的设备在悬浮或聚焦时显示说明 |
| 卡片 | 每张图依次填入当前最短的列;说明始终可见 |
| 无缝 | 沿用展墙计算,图片块间距为零;说明行为相同 |
对于极宽或极高图片,图片块的宽高比有上限和下限。块内完整显示图片,因此可能留白。 不支持悬浮的设备在图片下方持续显示说明。
工具栏包含一个共用背景的三格单选组,后面依次是播放/暂停按钮和横向尺寸滑杆。 滑杆两端分别用小图与大图图标标示。所有控件垂直居中。没有 GIF 的相册保留禁用的 播放按钮。图标控件具有本地化名称、适用的提示文字,以及可见的键盘焦点。 卡片与相册封面使用预设的圆角设置;展墙与无缝图片块保持直角。获得焦点的图片块在说明之上 显示双色内嵌焦点环。
外观控件位于共享导航中。相册主体不显示外观选择器或文章操作菜单。 没有 JavaScript 时,图片链接与说明仍然可见。
读者偏好与外观
浏览器将所选模式与尺寸保存在 td-expo-mode 和 td-expo-size 中,只有合法值会生效。
有效的存储值优先于作者默认值,并在同一来源下跨相册、刷新保留。
存储值无效或存储不可用时,使用作者默认值。
显示模式和尺寸独立于外观。Paper、Slate、Ink、Terminal 在亮暗模式下使用共享的 颜色、字体与圆角设置。切换预设后,网格会根据字体变化重新计算,同时保留读者选择 的显示模式和尺寸。
GIF 在网格与查看器中默认播放。播放/暂停控件同时控制当前相册及其查看器的动态效果,
该设置不会跨页面加载保存。prefers-reduced-motion: reduce 默认使用静态预览,
读者仍可主动播放。系统偏好变化时会更新播放状态。前四个图片块立即加载,其余网格图片采用懒加载。
布局稳定性
当滚动条占用布局宽度时,Expo 保留文档滚动条空间。相册高度接近视口高度时, 布局必须保持稳定。网格重算高度时,滚动条不能反复出现、消失。 打开或关闭查看器时,底层网格的可用宽度必须保持不变。
查看器行为
查看器使用原生模态对话框。图片显示区域不包含工具栏和说明区域。
W、H 表示原图宽、高;A、B 表示图片区域可用宽、高,单位为 CSS 像素。
| 操作 | 缩放比例 |
|---|---|
| 打开或重置 | min(1, A/W, B/H) |
| 放大至适屏 | min(A/W, B/H) |
| 放大 | 当前比例乘以 1.25,不超过缩放上限 |
| 缩小 | 当前比例除以 1.25,不低于缩放下限 |
较小的比例决定受限边。两种自动缩放操作都完整容纳图片,图片区域不出现滚动条。 重置让小图保持 100%,放大至适屏则可以放大小图。放大至适屏不调用浏览器 Fullscreen API。 只有手动缩放可以超出图片区域,并启用滚动。
百分比将原图像素按 CSS 像素计算。缩放保留观看中心,重置使图片居中。 切换图片会恢复初始尺寸与位置。上一张/下一张在相册首尾停止;到达边界后的按键 不会改变当前缩放比例与位置。
指针与键盘输入
按住鼠标主键拖动,可移动放大后的图片。图片随指针移动,并在边界停止。 只有超出显示区域的方向可以移动。放大后的图片在触摸输入下保留原生滚动。 松开或取消指针、切换图片、重置、窗口失焦都会结束拖动。图片完整显示在图片区域内时, 至少 48 CSS 像素的横向滑动会切换图片;双指缩放和页面已缩放时不切换。
双击或双触在初始尺寸与近看之间切换。缩小显示的原图以 100% 显示,并保持指针下的位置不动; 已经是 100% 的原图放大至适屏。处于其他尺寸时恢复初始尺寸。
查看器快捷键在打开的对话框内生效,不接管文字输入、输入法组合输入或
Ctrl/Command/Alt 组合键。对话框打开时,页面级单键导航暂停。
keyboard_nav 控制页面导航脚本,不会禁用 Expo 查看器脚本。
| 按键 | 操作 |
|---|---|
| W / A / S / D | 将放大后的图片向上/左/下/右移动,每次 80 CSS 像素 |
| Q 或 H | 上一张 |
| E 或 L | 下一张 |
| J 或 − | 缩小 |
| K 或 + | 放大 |
| R 或 0 | 重置尺寸和位置 |
| F | 放大至适屏并居中 |
| Escape | 关闭查看器 |
无需横向滚动时,左右方向键用于切换图片;否则保留原生滚动。 按钮提示显示适用的快捷键。从右到左书写的语言中相册顺序镜像:指向起始方向的方向键打开上一张, 滑动方向与之一致,上一张/下一张图标同样镜像。
链接、焦点与加载
打开原图在新标签页中显示原图文件。在相册中定位会关闭查看器、更新 URL 锚点、
滚动到当前图片,并聚焦其触发链接。普通关闭操作将焦点返回最初打开查看器的链接。
原生对话框约束焦点,并阻止与底层页面交互。查看器打开时,底层页面不能滚动。
查看器注册到 OinkSurfaceCoordinator:打开时关闭其他浮层,closeOthers() 或
closeAll() 也能关闭查看器。移交到新界面后,焦点保持在新界面中。
原图加载期间,查看器按最终尺寸显示网格预览;原图加载失败时保留该预览。 每张图片加载完成后,查看器预取前后各一张相邻图片。
Expo 只在有图片的 HTML 相册中加载查看器和布局脚本。普通 Image Zoom 保持单图行为, 不会再次包裹 Expo 图片链接。没有 JavaScript 或不支持对话框时,这些链接直接打开原图。
切换图片时,先清除上一张已解码的图片;下一张原图加载期间,按最终尺寸显示 新的网格预览,原图加载失败时保留预览。相册正文与图片标题在 Markdown 中 保持为独立区块。
其他输出
所有输出使用同一套图片解析器。各格式通过常规 OINK 输出配置启用。
| 输出 | 图片内容 |
|---|---|
| 静态预览、标题、说明、署名与原图链接;不加载 Expo 脚本 | |
| Markdown 与 LLMSFULL | 原图链接与元数据 |
| LLMS | 在现有索引中列出相册页面 |
| RSS | 静态图片说明 |
| 浏览器打印 | 展开相册内容,隐藏操作控件 |
作者负责翻译相册内容和资源元数据,图片控件使用共享界面语言目录。
已有的 gallery 围栏仍是正文组件,遵循自身契约。
验收与发布边界
在主题 checkout 中运行专项检查:
在同级文档站 checkout 中运行站点验收:
双语示例是集成测试与视觉检查页面。
tests/browser/expo.spec.mjs 检查交互与无障碍。
expo-stability.spec.mjs 在视口高度临界点采样连续帧,并强制启用占宽度的滚动条,
不依赖宿主设置。
要补充 Chromium、Firefox 和 WebKit 检查,先安装匹配的 Playwright 浏览器, 再从文档站 checkout 运行:
独立的 Graph 副本提供大型收藏的补充证据,不替代文档站的验收测试。 本地验收、主题发布、消费站升级与部署是不同的步骤。