导航栏下拉

文档编号 07

07 - 导航栏下拉工具菜单

日期:2026-04-29(方案更新:2026-05-01;底色微调:2026-05-06)
变更文件:usr/themes/classic-22/header.phpusr/themes/classic-22/theme.css

背景

原导航:首页 / JSON工具 / 我的动态(页面顺序由 CMS 决定,无分组)

需求:改为 首页 / 我的动态 / 更多▾,其中「更多」点击展开下拉,列出各类工具页(目前仅 JSON工具),后续新增工具无需改代码。


方案:Typecho 父子页面层级

利用 Typecho 独立页面的 parent 字段实现分组:

  • 父页面parent = 0)→ 正常导航链接,若有子页面则渲染为下拉触发器
  • 子页面parent != 0)→ 收进父页面的下拉列表,不单独出现在顶级导航

新增工具只需在后台把新页面的「父页面」设为「更多工具」,导航自动更新。

CMS 后台操作(一次性设置)

  1. 新建独立页面:标题 更多,slug 随意(如 more),内容留空
  2. 编辑「JSON工具」页面,将「父页面」设为「更多工具」,保存
  3. 在页面列表调整排序:「我的动态」排在「更多工具」前面

当前实现(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 avar(--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:hoverbackground-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-open class,点击外部关闭
  • 深色模式通过 [data-theme="dark"] 规则手动覆盖背景色

废弃原因:深色模式下链接颜色 inherit 问题无法简洁修复,且 JS 增加了维护成本。native <details> 方案更简洁可靠。