关于 canvas-navigation-components
面向 Drupal Canvas 与 React 项目的导航组件实现技能,指导组件拆分、数据来源选择和无障碍标记。涵盖 Drupal 菜单、页面面包屑及静态导航的处理方式。
主要功能
- 01按区域拆分导航组件
- 02选择菜单、面包屑或静态数据
- 03使用 SWR 获取 Drupal 菜单
- 04配置菜单属性与组件插槽
- 05遵循导航无障碍要求
适用场景
- 01构建可复用的主导航或页脚导航
- 02从 Drupal 菜单加载链接
- 03根据当前页面显示面包屑
- 04组合含多列内容的巨型菜单
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ drupal-canvas/ skills --skill canvas-navigation-components安装后在 Claude Code 中输入/canvas-navigation-components确认已加载。
附带文件references/data-sources.md
详细介绍
Canvas 导航组件
项目类型
应用此技能前,请检查 package.json 中是否有名为
@drupal-canvas/headless 或以 @drupal-canvas/headless- 开头的依赖项。如果有,
这是一个 Canvas Headless 代码库——请先阅读
canvas-headless。下文关于拆解、
命名和无障碍的指导同样适用。React 组件在 Drupal、Workbench 和 React
headless 项目中使用以下可移植的钩子和 SWR 模式。Headless 服务端代码使用
SDK 的 getClient();原生非 React 组件使用框架原生的数据加载和页面上下文。如果不存在此类依赖项,则这是一个 Canvas 渲染的 React 代码库:组件
使用 React(index.jsx/.tsx),本技能中的所有内容均按原样适用。
项目类型只有这两种。
构建适合 Canvas 的导航:使用可复用的名称、清晰的属性
(variant、menuName)和插槽(徽标、实用功能、超级菜单区域),
选择正确的数据源(Drupal 菜单、面包屑上下文或静态数据),并使用
无障碍标记。
本技能需与以下技能配合使用:
canvas-design-decomposition— 在编写代码之前确定区域、树结构、属性/插槽草图,并优先采用可复用命名。canvas-data-fetching— SWR、useJsonApiClient、菜单资源模式和 Workbench 规则。
然后应用 canvas-component-metadata、
canvas-component-definition
(包括
references/naming.md)、
canvas-styling-conventions 和
canvas-component-composability,
以处理插槽和可重复组。
工作流(顺序很重要)
1. 拆解(必须)
按照 canvas-design-decomposition
至少完成阶段 E(属性/插槽),然后再编写 React 代码或
component.yml。
导航专用提示:
- 将框架(导航栏、抽屉外壳)与链接列表和可选的 列(页脚、超级菜单)分开。优先采用组合,而不是创建一个包罗万象的组件。
- 重复的链接组或卡片 → 按
canvas-component-composability/references/repeatable-content.md采用父组件 + 子组件模式。 - 使用通用的
machineName(main-navigation、footer-navigation),不要使用 特定页面的名称。将组件在页面中的位置表达在页面上,而不是组件名称中。 - 在草图中为布局模式(水平、抽屉、紧凑页脚)设计
variant。 - 如果导航读取Drupal 菜单,请在草图中添加
menuName(见步骤 2)。如果由作者组合这些区块,请使用插槽放置徽标、实用链接或 CTA。
2. 选择数据源
请参阅 references/data-sources.md 中的决策 表。摘要:
| 来源 | 适用情况 |
|---|---|
| Drupal 菜单 | 编辑者在 Structure → Menus 中管理链接;使用 menu_items + getResource |
| 面包屑 | 通过 usePageContext() 获取当前页面上下文中的导航路径 |
| 静态数据 | 少见;请说明原因;仍须提供合理的默认值或插槽 |
3. 实现获取逻辑(Drupal 菜单)
请参阅
canvas-data-fetching
中的 Navigation / Menu Components 部分:
- 使用
drupal-canvas/react中的useJsonApiClient()读取client。 useSWR(client && menuName ? ['menu_items', menuName] : null, ([type, id]) => client.getResource(type, id))- 数据有效时使用
Array.from(sortMenu(data));否则使用FALLBACK_LINKS。不要因isLoading而隐藏可用数据,也不要把草稿会话失败伪装成成功的回退内容。 - 始终定义
FALLBACK_LINKS,以便 Workbench 和没有该菜单的网站仍能正常渲染。 - 当编辑者需要选择 Drupal 菜单时,在
component.yml中公开menuName(机器名称,例如main、footer)。示例中仅在 JSX 里硬编码默认值且不提供 prop 是可以的;生产环境中需要切换菜单的导航必须按照数据获取规则注册menuName。 - 对于仅静态预览,显式将
menuName设为 null,以禁用获取逻辑;省略该属性可能会选用组件的默认菜单。
不要在 Workbench mock 中伪造 JSON:API 菜单载荷。优先采用真实的加载/错误/空数据行为;回退链接用于处理菜单为空的情况。
Drupal 中的菜单(首选): 生产环境中的导航应放在 Drupal 菜单(CMS)中,而不应只是在代码中硬编码数组。对于真实网站,FALLBACK_LINKS 用于 Workbench/菜单为空的情况,不能长期替代 CMS 菜单。
在 Acquia Source 上,自动化该环境时,优先使用 Source MCP,通过 acquia-source-navigation-menus 创建或更新菜单;否则请在 Drupal 中使用 Structure → Menus。让 menu_name 与此组件的 menuName prop 保持一致。
对于非 Acquia Source 目标,请使用 Drupal 管理界面(或你的部署流程)——不要应用 Source MCP 菜单技能。如果没有管理界面/MCP 访问权限,请准确告知用户需要创建哪些菜单(与 menuName/机器名称相符,例如 main、footer),并像 canvas-data-fetching 中那样链接到 Structure → Menus。
4. 面包屑
面包屑来自页面上下文,而非 menu_items。始终读取 drupal-canvas/react 中的 usePageContext(),然后处理页面为 null 或 breadcrumbs 集合为空的情况。请先阅读现有模式,再决定是否需要添加字段。Workbench 默认提供空的面包屑;请在路由应用中验证真实的页面路径。除非应用明确适配路由,否则保留 Drupal 生成的 URL。
5. 无障碍(最低要求)
- 将主要链接列表包在
<nav>中,并使用不同的aria-label(或使用aria-labelledby指向可见标题文本)。 - 移动端切换按钮:在接入 ID 时使用
aria-expanded、aria-controls;按钮使用type="button"。 - 不要将每个链接都标记为
aria-current="page"——只有在设计需要时,才标记真正的当前项。 - 对于嵌套列表(大型菜单、页脚列),请在适当情况下保留列表语义;确保 Tab 顺序符合预期。
6. 元数据和样式
- Props/slots YAML:
canvas-component-metadata。 - 文件夹和 mock:
canvas-component-definition。 - Tailwind 令牌:
canvas-styling-conventions。
参考示例
规范的菜单 + SWR + sortMenu 示例位于
canvas-data-fetching,
其中包含用于编辑器配置的 menuName 元数据。
大型菜单和嵌套菜单
- 模型组合:父级导航外壳 + 用于列或特色 内容块的插槽;子组件用于链接组。
- **嵌套菜单结构:**不要假设原始 JSON:API 中存在嵌套结构。在映射
子级之前,使用组件将调用的同一个
JsonApiClient调用探查反序列化后的 输出(参见canvas-data-fetching)。
延伸阅读
避免重复
- 完整的菜单代码示例和
menuNameYAML 位于canvas-data-fetching中的导航 / 菜单组件部分。 - 此处不重复分解阶段 A–G,请参阅
canvas-design-decomposition。