导航栏下拉
07 - 导航栏下拉工具菜单
日期:2026-04-29(方案更新:2026-05-01;底色微调:2026-05-06)
变更文件:usr/themes/classic-22/header.php、usr/themes/classic-22/theme.css
背景
原导航:首页 / JSON工具 / 我的动态(页面顺序由 CMS 决定,无分组)
需求:改为 首页 / 我的动态 / 更多▾,其中「更多」点击展开下拉,列出各类工具页(目前仅 JSON工具),后续新增工具无需改代码。
方案:Typecho 父子页面层级
利用 Typecho 独立页面的 parent 字段实现分组:
- 父页面(
parent = 0)→ 正常导航链接,若有子页面则渲染为下拉触发器 - 子页面(
parent != 0)→ 收进父页面的下拉列表,不单独出现在顶级导航
新增工具只需在后台把新页面的「父页面」设为「更多工具」,导航自动更新。
CMS 后台操作(一次性设置)
- 新建独立页面:标题
更多,slug 随意(如more),内容留空 - 编辑「JSON工具」页面,将「父页面」设为「更多工具」,保存
- 在页面列表调整排序:「我的动态」排在「更多工具」前面
当前实现(v2,2026-05-01)
使用 HTML 原生 <details>/<summary> 元素,替换原 v1 的 JS class 切换方案。
HTML 结构
<li class="nav-has-sub">
<details>
<summary><?= htmlspecialchars($page['title']) ?> ▾</summary>
<ul>
<?php foreach ($children as $child): ?>
<li><a href="...">...</a></li>
<?php endforeach; ?>
</ul>
</details>
</li>CSS(内联在 header.php <style> 块中)
.nav-menu li.nav-has-sub { position: relative; }
.nav-menu details summary {
list-style: none; cursor: pointer;
display: block; padding: 0.5rem; color: inherit;
}
.nav-menu details summary::-webkit-details-marker { display: none; }
/* 桌面:浮层绝对定位 */
.nav-menu details[open] > ul {
position: absolute; top: 100%; right: 0;
background-color: var(--pico-background-color);
border: 1px solid var(--pico-muted-border-color);
border-radius: var(--pico-border-radius);
min-width: 130px; padding: 0.25rem 0; z-index: 100;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.nav-menu details[open] > ul li a {
display: block; padding: 0.5rem 1rem;
color: var(--pico-color) !important; /* 必须明确指定,不能 inherit */
}
/* 移动端:内联展开 */
@media (max-width: 900px) {
.nav-menu details[open] > ul {
position: static; box-shadow: none; border: none;
background-color: transparent;
}
.nav-menu details[open] > ul li a {
padding-left: 2rem; color: inherit !important;
}
}无需任何 JavaScript,无需 dark mode 覆盖规则。 pico 变量(--pico-background-color、--pico-muted-border-color、--pico-color)在浅色/深色模式下自动切换。
设计细节
| 场景 | 行为 |
|---|---|
桌面点击 <summary> | 浏览器原生切换 [open] 属性,浮层出现/消失 |
| 点击外部区域 | 浏览器原生关闭 <details>(不需要 JS) |
| 移动端点击 | [open] 控制,子项在下方内联展开(缩进 2rem),背景透明 |
| 深色模式 | pico 变量自动适配,无需额外规则 |
| 子页面 active 状态 | 访问子页面时子链接高亮 |
| 父项点击 / 子项 hover 底色 | 无(2026-05-06 去除):保持与其他导航项一致的极简观感 |
关键技术说明
为什么 CSS 放在 header.php 内联 <style> 而非 theme.css
theme.css 只在 colorSchema == 'customize' 时加载(见 header.php 第 9-11 行),其他配色方案下不加载。导航相关 CSS 必须内联在 header.php 以保证任何配色下都生效(详见 dev-errors E08)。
为什么链接颜色用 var(--pico-color) 而非 inherit
浮层子菜单虽然视觉上脱离导航栏,但 DOM 上仍是 .site-navbar 的子节点。inherit 会继承 .site-navbar a 的 var(--pico-primary-inverse)(白色),在白色浮层背景和深色浮层背景上均不可见(详见 dev-errors E09)。
为什么主动去除 hover / click 底色(2026-05-06)
pico.css 默认会给 <a> 在 focus/active 时添加 --pico-text-selection-color 的浅色底色,叠加 hover 底色后下拉区域视觉过重。当前主题其他导航项都没有底色反馈,因此:
- 移除
.nav-sub-menu li a:hover的background-color - 新增
.nav-sub-toggle:focus, .nav-sub-toggle:active { background-color: transparent !important; }覆盖 pico 默认
保留 color 高亮(active 页面子项变蓝)即可提供足够反馈。
v1 方案(已废弃)
展开查看旧方案(hover + JS click)
v1 使用 .nav-has-dropdown + .nav-dropdown + JS class 切换:
- CSS
:hover展开(限@media (hover: hover) and (pointer: fine)) - JS click 切换
.is-openclass,点击外部关闭 - 深色模式通过
[data-theme="dark"]规则手动覆盖背景色
废弃原因:深色模式下链接颜色 inherit 问题无法简洁修复,且 JS 增加了维护成本。native <details> 方案更简洁可靠。