docs: add sticky overlap fix and header docking documentation

docs:添加吸顶重叠修复和页头停靠文档
This commit is contained in:
Dusk 2026-04-25 21:21:33 +08:00
parent 0d0f04aba2
commit c1dbddd9c0
2 changed files with 194 additions and 0 deletions

View file

@ -0,0 +1,97 @@
# 设置页 Sticky 遮挡修复决策
本文锁定本轮“页面标题遮罩 + 卡片标题停靠”修复的最终实现策略。
## 1. 页面标题仍是最高层 sticky
- 保留 `display()` 中的 `pageHeaderEl`
- 最终样式决策:
- `position: sticky`
- `top: 0px`
- `zIndex: 300`
- `backgroundColor: var(--modal-background, var(--background-primary))`
- 继续保留现有 inline style 方式,不新增全局样式表。
原因:
- 当前仓库设置页布局主要仍由 inline style 负责。
- 页面标题是统一的上层遮挡面,应该高于所有卡片标题。
## 2. 页面标题增加独立 mask而不是只依赖 header 本体背景
- 在 `pageHeaderEl` 内增加:
- `data-settings-page-header-mask="true"`
- mask 使用:
- `position: absolute`
- 向上扩展
- 向左右扩展
- 与页面标题相同的不透明主题背景
- mask 层级低于标题文本,但跟随页面标题一起处于最高层 sticky 堆叠上下文。
原因:
- 仅靠 `pageHeaderEl` 自身背景,只能覆盖 header 盒子范围,无法盖住页面标题上方和左右的透明缺口。
## 3. 卡片标题恢复 sticky但固定停在页面标题下方
- `settingsCardToggle` 最终样式决策:
- `position: sticky`
- `top: var(--ahs-settings-page-header-height, 64px)`
- `zIndex: 200`
- `backgroundColor: var(--modal-background, var(--background-primary))`
- 保留现有按钮视觉:
- `width: 100%`
- `border: 2px solid var(--background-modifier-border)`
- `fontSize: 1.5em`
- 点击切换折叠/展开
原因:
- 卡片标题需要 sticky才能满足“滚动到页面标题下方后停住”的交互。
- `top` 必须基于页面标题真实高度,不能再写死为 `0`
- `zIndex` 必须低于页面标题层级,避免钻到标题前面。
## 4. 页面标题高度通过 CSS 变量驱动
- 在 `containerEl` 上写入:
- `--ahs-settings-page-header-height`
- 默认值:
- `64px`
- 运行时优先用 `ResizeObserver` 监听 `pageHeaderEl`
- 如可测量,则通过 `getBoundingClientRect()` 取真实高度并回写像素值。
- 无法测量时保留 `64px` fallback。
原因:
- 测试环境和部分渲染阶段无法可靠给出布局尺寸,需要一个稳定 fallback。
- 真实 Obsidian 设置页中,标题高度可能受字体或缩放影响,运行时测量更稳妥。
## 5. `hide()` 负责 observer 清理
- 在 `hide()` 中断开页面标题高度 observer。
- 同时清掉存储的 observer 引用,避免重复打开设置页时累积监听。
原因:
- `displayInitialized` 会在 `hide()` 后复位,下一次打开会重建 DOM。
- 如果不清理 observer会造成重复观察和不可预测的高度回写。
## 6. 保持不变的边界
- 不改 `pageHeaderEl / settingsCardsContainer / cardEl / headerEl / bodyEl` 这一卡片 shell 结构。
- 不改 `toggleCard()` 的展开/折叠逻辑。
- 不改 `renderCard()` 的局部刷新边界。
- 不改同步逻辑、设置数据结构、命令行为。
- 不引入:
- scroll listener
- `IntersectionObserver`
- `requestAnimationFrame` 滚动逻辑
## 7. 与旧 sticky 文档的偏离说明
- 仓库里已有的旧文档把卡片标题视为 static这是之前一次实现决策。
- 本轮按新的用户合同执行,原因是当前真实 UI 目标已经变化:
- 页面标题需要继续 sticky 并补齐遮罩
- 卡片标题也需要 sticky但停靠在页面标题下方
因此,本轮不是延续旧方案,而是在同一真实代码接点上升级为“两层 sticky”结构。

View file

@ -0,0 +1,97 @@
# 设置页 Sticky 遮挡差距审计
本文基于当前仓库真实实现,定位设置页顶部透视与卡片标题停靠异常的实际接入点。
## 1. 当前 `display()` 已有页面级 sticky header但遮罩不完整
- 入口在 [src/presentation/settings/PluginSettingTab.ts](src/presentation/settings/PluginSettingTab.ts)。
- `display()` 首次渲染时已经创建了 `pageHeaderEl`,并设置了:
- `position: sticky`
- `top: 0`
- `zIndex: 100`
- `background: var(--background-primary)`
- 但当前 `pageHeaderEl` 只有自身盒子背景,没有任何向上、向左右扩展的遮罩层。
结果是:页面标题本身能 sticky但上方和侧边留白区域仍可能透出下层设置内容尤其在 Obsidian 设置容器滚动时更明显。
## 2. 当前卡片标题按钮是 static不会停靠在页面标题下方
- `initializeCards()` 中五张卡片仍是统一 shell
```text
pageHeaderEl
settingsCardsContainer
cardEl
headerEl(button)
bodyEl(div)
```
- `settingsCardToggle` 当前只保留普通按钮样式:
- `display: flex`
- `width: 100%`
- `fontSize: 1.5em`
- `background: var(--background-primary)`
- `border: 2px solid var(--background-modifier-border)`
- 没有:
- `position: sticky`
- `top`
- `zIndex`
结果是:卡片标题不会在滚动到页面顶栏下方时停住,因此无法实现“卡片标题栏顶到 Anki Heading Sync 下方后停止”的设计目标。
## 3. 当前没有页面标题高度变量,也没有测量逻辑
- `containerEl` 当前没有写入 `--ahs-settings-page-header-height`
- `PluginSettingTab` 当前没有:
- `ResizeObserver`
- `getBoundingClientRect()` 回退测量
- 页面标题高度的状态同步
结果是:即使恢复卡片标题 sticky也没有可靠的 `top` 偏移来源,卡片标题无法稳定停靠在页面标题栏下方。
## 4. 当前 `hide()` 不做页面标题测量清理
- `hide()` 当前只做:
- `displayInitialized = false`
- `cardShells.clear()`
- debounce 和缓存状态清理
- 没有任何 observer 断开逻辑。
如果本轮引入页面标题高度观察器,必须在 `hide()` 中断开,避免设置页重复打开后残留多重监听。
## 5. 现有展开/折叠与局部刷新接点应保持不变
- `toggleCard()` 只维护 `expandedCardIds`
- `renderCard()` 只更新单卡:
- 标题文本
- `aria-expanded`
- `bodyEl.style.display`
- `bodyEl.empty()` 后重绘
- 已有测试也覆盖了:
- 折叠展开不整页重建
- 卡片 1 局部刷新不整页重建
这部分已经是正确的局部刷新边界,本轮不应改动。
## 6. 父容器路径上未发现必须先处理的 sticky 阻断样式
- 当前拥有 sticky 行为的直接元素只有页面级 `pageHeaderEl`
- 在 `PluginSettingTab.ts` 的设置页装配路径中,未发现 `settingsCardsContainer`、`cardEl`、`bodyEl` 上存在会直接阻断标题按钮 sticky 的 `overflow: hidden/auto` 接入点。
因此本轮优先按用户合同落两层 sticky并通过最小增量验证真实行为不额外引入滚动监听或观察滚动位置的 JS 逻辑。
## 7. 结论
当前仓库与本轮目标之间的真实差距是:
1. 页面级 sticky 标题栏已有,但缺少完整不透明遮罩。
2. 卡片标题栏当前是 static无法停靠在页面标题栏下方。
3. 缺少页面标题高度 CSS 变量和测量更新逻辑。
4. `hide()` 缺少 observer 清理。
最小且符合仓库现状的修复路径是:
1. 保留 `pageHeaderEl` 作为最高层 sticky header并补一层绝对定位 mask。
2. 在 `containerEl` 上写入 `--ahs-settings-page-header-height`,默认 `64px`,运行时按真实高度更新。
3. 让每个 `settingsCardToggle` 恢复 sticky并使用页面标题高度变量作为 `top`
4. 在 `hide()` 中断开页面标题高度 observer。