EN
Shopify 知识库 · 指南

Horizon 页眉导航:Mega Menu、移动抽屉、吸顶与公告栏

基于 Horizon 4.2.0 静态源码,拆解页眉分区组中的公告栏、桌面 Mega Menu、移动端菜单抽屉与吸顶滚动逻辑,并对照通用导航与抽屉契约列出无障碍差距。
历史资料
请结合文中的适用版本和来源阅读。

本文基于 Horizon 4.2.0,源码固定到 5acd1b6(核验于 2026-10-07)。结论限于此提交,不表示其他版本行为相同;本篇为静态源码分析,未完成店铺运行验证。Dawn 上游已发布 v16.0.0(2026-08-10,GitHub Releases 核对),本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对;本库 Horizon 正文统一以 4.2.0 为基线(固定提交中 config/settings_schema.json 的 theme_version 为 4.2.0)。

对应哪些通用模式

覆盖 Navigation Menu(桌面主导航与 Mega Menu)、Drawer(移动菜单抽屉)、Sticky Bar(吸顶页眉)和 Banner(公告栏)。页面外壳、分区组与首屏高度测量见布局与导航,购物车抽屉走另一套 theme-drawer,见购物车,本篇不重复。面包屑在此版本未内置,见文末搜索口径。

入口与文件

文件职责
sections/header-group.json页眉分区组:公告栏在前、页眉在后;默认 enable_sticky_header: "always"、菜单 main-menu、menu_style: "featured_products"
sections/header.liquid用 content_for 'block' 静态取 logo 与 _header-menu(桌面、mobile、navigation_bar 三种变体),写出 sticky / transparent 属性
blocks/_header-menu.liquid、mega-menu-list桌面菜单:顶层链接、展开按钮、Mega Menu 分栏与精选内容
snippets/header-drawer.liquid移动抽屉,details / summary 结构(mobile 变体)
assets/header-menu.js、header-drawer.js、header.js子菜单开合、抽屉开合、吸顶状态与高度变量
sections/header-announcements.liquid、_announcement、announcement-bar.js公告栏与轮播,仅 header 分组可用

数据从哪来

  • 菜单:_header-menu 的 menu(link_list,默认 main-menu)。link.links 生成子级;link.current 输出 aria-current="page",桌面菜单中 link.active 只加样式类;抽屉按 linklist.levels < 3 与 3 层两条分支渲染。
  • Mega Menu 内容:menu_style 取 text、collection_images、featured_products、featured_collections。前者在子链接没有集合类目标时、后者在顶层链接不是集合或全部商品入口时退回 text。
  • 精选内容延迟加载:section.index == blank(Section Rendering 场景)才算 eager_loading,桌面精选商品、抽屉的精选商品与集合只在该场景输出。非设计模式下 header.liquid 的模块脚本用 requestIdleCallback 调 hydrate(),section-hydration.js 只 morph 带 data-hydration-key 的节点,因此首屏 HTML 不含精选商品。
  • 公告栏:_announcement 的 text、link,分区设置 speed(2–10 秒,默认 5)。空文本块不输出,但自动播放与箭头按 section.blocks.size > 1 判断,可能与实际幻灯片数不一致。

交互怎样运行

  • 桌面菜单:on:pointerenter 激活,on:pointerleave 与 on:blur 停用(on: 由 component.js 在 document 上委托)。激活时把展开按钮的 aria-expanded 置 true 并移除子菜单 inert;指针追踪用 data-safety-box 留出斜向移动的安全区;header-menu 元素自身也被写入 aria-expanded(供 CSS 用,无角色,读屏含义未核验)。overflow-list 放不下时收进 More 按钮,可见项少于 minimum-items(2)时页眉改用抽屉;触屏设备(ontouchstart 且 maxTouchPoints > 0)由 theme.liquid 内联脚本直接设为 data-menu-style="drawer"。
  • 移动抽屉:<details scroll-lock> 加 summary,aria-expanded 由 open() / #close() 写;dialog.js 监听 details 的 toggle 事件锁滚动。动画结束后 trapFocus(details)(focus.js:Tab 首尾循环,focusin 把外部焦点拉回);Escape 由抽屉元素上的 keyup 处理,遮罩点击调用 close。
  • 吸顶:enable_sticky_header(always / scroll-up / never)写成 <header-component sticky>,与透明页眉同开时透明值降为 not-sticky。always 用 .header-section { position: sticky; top: -1px } 加 IntersectionObserver 写 data-sticky-state;scroll-up 由 header.js 的 rAF 节流滚动回调比较 scrollTop,上滚为 active(此时才 sticky),下滚为 idle(opacity: 0)。≥990px 时滚动容器是 .page-wrapper,跨断点会重绑监听。公告栏所在 section 无 sticky 规则,随页滚走。--header-height 由 ResizeObserver 维护,购物车摘要与商品详情的 sticky 偏移读取它。
  • 公告栏:多块时 announcement-bar-component 带 autoplay 与 aria-live="polite",setInterval 切换各幻灯片的 aria-hidden;鼠标进入暂停、离开恢复,箭头复用 slideshow-arrows。

与通用契约的一致与差距

一致

  • 桌面菜单是 nav(aria-label 取 accessibility.header_navigation_label,英文 Primary)加列表;全仓库没有 role="menu" / menuitem 角色(ref="menuitem" 只是脚本引用名),与 Navigation Menu 的「不必用 role=menu」一致。
  • 有子级的顶层项是「链接 + 独立展开按钮」,按钮带 aria-controls、aria-expanded 与 show_submenu 文案,平时 opacity: 0,:focus-visible 才显示;链接聚焦不自动展开(菜单中没有 on:focus)。
  • Escape 关闭并把焦点交还展开按钮(溢出面板交还 More 按钮);折叠子菜单带 inert,不进 Tab 顺序;悬停内容可移入(.menu-list__submenu:hover)、可用 Escape 关闭,与 1.4.13 在代码层面对应,未实测。

差距

  • 无 JS:桌面菜单默认 display: none,仅 data-menu-style='menu' 才显示,该属性只由脚本写;抽屉 .menu-drawer 默认 visibility: hidden,需脚本加 .menu-open。按静态阅读,无 JavaScript 时顶层链接不可达,与契约「顶层链接仍可跳转」有差距(未做禁用脚本实验)。
  • 抽屉不是模态对话框:无 role="dialog" / aria-modal,背景不设 inert,仅靠 trapFocus;未见关闭后把焦点交还 summary 的调用;trapFocus 的 container.focus() 作用于无 tabindex 的 details,初始焦点未核验;summary 的服务端输出没有 aria-expanded。
  • 当前页:只有顶层与抽屉链接输出 aria-current,mega-menu-list 子链接没有;navigation_bar 变体(窄屏横向滚动)是 div 加 ul,没有 nav 与名称。面包屑与「当前位置」不存在。
  • 公告栏:无关闭按钮、有效期字段与暂停控件(仅悬停暂停,键盘聚焦不暂停);无 prefers-reduced-motion 分支;aside 无名称。页面隐藏时 pause() 写入 paused,回到可见时 resume() 遇 paused 直接返回,按静态阅读自动播放此后不再恢复,需浏览器验证。
  • 吸顶遮挡焦点:scroll-padding / scroll-margin 只在与页眉无关的片段出现,未见为 sticky 页眉预留滚动偏移(2.4.11 需实测);header.liquid、header.js 无 prefers-reduced-motion 降级。

定制入口与风险

编辑器可改(schema 设置名):_header-menu 的 menu、menu_style、navigation_bar、drawer_accordion、drawer_accordion_expand_first、drawer_dividers;页眉的 menu_row、show_search、enable_sticky_header、enable_transparent_header_home 等三项、section_height;公告栏的 speed。耦合点:theme.liquid 内联脚本重复了页眉高度、--top-row-height 与 data-menu-style 的计算,注释要求与 utilities.js 同步;header.js 声明 requiredRefs(headerDrawerContainer、headerMenu、headerRowTop),移除菜单块的后果未核验;自定义菜单标记须保留 data-hydration-key。

建议的验证范围

均未执行:禁用 JS 后首页导航是否可达;仅用键盘走完顶层项、展开按钮与子链接,检查 Escape 与焦点回位;触屏与 ≥750px 非触屏各测展开方式;抽屉三层前进与返回;always 与 scroll-up 下锚点跳转、Tab 聚焦是否被页眉遮住;切换标签页后公告是否继续轮播;开启减少动态偏好观察公告与页眉过渡。

搜索口径:对固定提交整个检出(含 sections、snippets、blocks、assets、templates、locales、config,含隐藏文件)用 rg -uui 搜索 breadcrumb,无命中;scroll-padding|scroll-margin 有 5 个文件命中,均与页眉无关。

源码基线