Canvas navigation components
Build navigation that fits Canvas: reusable names, clear props
(variant, menuName) and slots (logo, utilities, mega-menu regions),
correct data source (Drupal menu vs breadcrumb context vs static), and
accessible markup.
This skill stacks on:
canvas-design-decomposition —
regions, tree, prop/slot sketch, reuse-first naming before code.
canvas-data-fetching — SWR,
JsonApiClient, menu resource pattern, Workbench rules.
Then apply canvas-component-metadata,
canvas-component-definition
(including
references/naming.md),
canvas-styling-conventions, and
canvas-component-composability
for slots and repeatable groups.
Workflow (order matters)
1. Decompose (mandatory)
Follow canvas-design-decomposition
through at least Phase E (props/slots) before writing React or
component.yml.
Navigation-specific prompts:
- Separate chrome (bar, drawer shell) from link lists and optional
columns (footer, mega-menu). Prefer composition over one god-component.
- Repeating link groups or cards → parent + child pattern per
canvas-component-composability/references/repeatable-content.md.
- Use generic
machineNames (main-navigation, footer-navigation), not
page-specific names. Express placement in the page, not the component name.
- Sketch
variant for layout modes (horizontal, drawer, compact footer).
- Add
menuName in the sketch when the nav reads from a Drupal menu
(see step 2). Use slots for logo, utility links, or CTAs when authors
compose those blocks.
2. Choose a data source
See references/data-sources.md for a decision
table. Summary:
| Source |
When |
| Drupal menu |
Editors manage links in Structure → Menus; use menu_items + getResource |
| Breadcrumb |
Trail from current page context via getPageData() |
| Static |
Rare; document why; still provide sensible defaults or slots |
3. Implement the fetch (Drupal menu)
Use the Navigation / Menu Components section in
canvas-data-fetching:
useSWR(menuName ? ['menu_items', menuName] : null, ([type, id]) => client.getResource(type, id))
Array.from(sortMenu(data)) when data is valid; otherwise
FALLBACK_LINKS
- Always define
FALLBACK_LINKS so Workbench and sites without the menu
still render.
- Expose
menuName in component.yml when editors should pick the Drupal
menu (machine name, e.g. main, footer). Hard-coding only the default in
JSX without a prop is fine for examples; production nav that must switch
menus should register menuName per data-fetching rules.
menuName omitted or null in props: use SWR key null and show fallback when
you need static-only preview behavior.
Do not fabricate JSON:API menu payloads in Workbench mocks. Prefer real
loading/error/empty behavior; fallback links cover the empty-menu case.
Menus in Drupal (preferred): Production navigation should live in Drupal
menus (CMS), not only as hardcoded arrays in code. FALLBACK_LINKS are
for Workbench / empty-menu cases — not a long-term substitute for CMS menus on a
real site.
On Acquia Source, prefer creating or updating menus via Source MCP using
acquia-source-navigation-menus
when automating that environment; otherwise use Structure → Menus in Drupal.
Align menu_name with this component’s menuName prop.
For non–Acquia Source targets, use the Drupal admin (or your deployment
process) — do not apply the Source MCP menu skill. If there is no admin/MCP
access, tell the user exactly which menus to create (match menuName /
machine names such as main, footer) and link to Structure → Menus as in
canvas-data-fetching.
4. Breadcrumbs
Breadcrumbs come from page context, not menu_items. Precedent:
examples/components/breadcrumb/index.jsx
(getPageData(), breadcrumbs). Probe or read existing patterns before
inventing fields.
5. Accessibility (minimum)
- Wrap primary link lists in
<nav> with distinct aria-label (or
aria-labelledby pointing at visible heading text).
- Mobile toggles:
aria-expanded, aria-controls when you wire IDs; button
type="button".
- Do not mark every link
aria-current="page"—only the true current item when
the design requires it.
- Nested lists (mega-menu, footer columns): preserve list semantics where
appropriate; keep tab order predictable.
6. Metadata and styling
Reference example
Menu + SWR + sortMenu pattern:
examples/components/main_navigation/index.jsx.
Consider adding menuName to component.yml when editors must choose the
menu without code changes.
Mega-menu and nested menus
- Model composition: parent nav shell + slots for columns or featured
blocks; child components for link groups.
- Nested menu shape: do not assume raw JSON:API nesting. Probe deserialized
output with the same
JsonApiClient call the component will use (see
canvas-data-fetching) before mapping
children.
Further reading
- references/data-sources.md
Anti-duplication
1---2name: canvas-navigation-components3description: Plans and builds Drupal Canvas navigation UI (main nav, footer links, sidebar nav, mobile drawers, breadcrumbs) using design decomposition for structure and props/slots, then JSON:API menu or page-context patterns from canvas-data-fetching. Use when the user asks for navigation, header or footer links, menus, menu_items, mobile nav, or breadcrumb trails. Run after canvas-design-decomposition for layout and API sketches; follow canvas-data-fetching for SWR, JsonApiClient, sortMenu, and menu fallbacks.4---56# Canvas navigation components78Build **navigation** that fits Canvas: reusable names, clear **props**9(`variant`, `menuName`) and **slots** (logo, utilities, mega-menu regions),10correct **data source** (Drupal menu vs breadcrumb context vs static), and11**accessible** markup.1213This skill **stacks on**:14151. [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md) —16 regions, tree, prop/slot sketch, reuse-first naming **before** code.172. [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md) — SWR,18 `JsonApiClient`, menu resource pattern, Workbench rules.1920Then apply [`canvas-component-metadata`](../canvas-component-metadata/SKILL.md),21[`canvas-component-definition`](../canvas-component-definition/SKILL.md)22(including23[`references/naming.md`](../canvas-component-definition/references/naming.md)),24[`canvas-styling-conventions`](../canvas-styling-conventions/SKILL.md), and25[`canvas-component-composability`](../canvas-component-composability/SKILL.md)26for slots and repeatable groups.2728## Workflow (order matters)2930### 1. Decompose (mandatory)3132Follow [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md)33through at least **Phase E** (props/slots) before writing React or34`component.yml`.3536Navigation-specific prompts:3738- Separate **chrome** (bar, drawer shell) from **link lists** and optional39 **columns** (footer, mega-menu). Prefer composition over one god-component.40- Repeating link groups or cards → parent + child pattern per41 [`canvas-component-composability/references/repeatable-content.md`](../canvas-component-composability/references/repeatable-content.md).42- Use generic `machineName`s (`main-navigation`, `footer-navigation`), not43 page-specific names. Express placement in the page, not the component name.44- Sketch **`variant`** for layout modes (horizontal, drawer, compact footer).45- Add **`menuName`** in the sketch when the nav reads from a **Drupal menu**46 (see step 2). Use **slots** for logo, utility links, or CTAs when authors47 compose those blocks.4849### 2. Choose a data source5051See [references/data-sources.md](references/data-sources.md) for a decision52table. Summary:5354| Source | When |55| --------------- | --------------------------------------------------------------------------- |56| **Drupal menu** | Editors manage links in Structure → Menus; use `menu_items` + `getResource` |57| **Breadcrumb** | Trail from current page context via `getPageData()` |58| **Static** | Rare; document why; still provide sensible defaults or slots |5960### 3. Implement the fetch (Drupal menu)6162Use the **Navigation / Menu Components** section in63[`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components):6465- `useSWR(menuName ? ['menu_items', menuName] : null, ([type, id]) => client.getResource(type, id))`66- `Array.from(sortMenu(data))` when data is valid; otherwise67 **`FALLBACK_LINKS`**68- Always define **`FALLBACK_LINKS`** so Workbench and sites without the menu69 still render.70- Expose **`menuName`** in `component.yml` when editors should pick the Drupal71 menu (machine name, e.g. `main`, `footer`). Hard-coding only the default in72 JSX without a prop is fine for examples; **production** nav that must switch73 menus should register `menuName` per data-fetching rules.74- `menuName` omitted or null in props: use SWR key `null` and show fallback when75 you need static-only preview behavior.7677Do **not** fabricate JSON:API menu payloads in Workbench mocks. Prefer real78loading/error/empty behavior; fallback links cover the empty-menu case.7980**Menus in Drupal (preferred):** Production navigation should live in **Drupal81menus** (CMS), not only as hardcoded arrays in code. **`FALLBACK_LINKS`** are82for Workbench / empty-menu cases — not a long-term substitute for CMS menus on a83real site.8485On **Acquia Source**, prefer creating or updating menus via Source MCP using86[`acquia-source-navigation-menus`](../acquia-source-navigation-menus/SKILL.md)87when automating that environment; otherwise use **Structure → Menus** in Drupal.88Align **`menu_name`** with this component’s **`menuName`** prop.8990For **non–Acquia Source** targets, use the **Drupal admin** (or your deployment91process) — do **not** apply the Source MCP menu skill. If there is no admin/MCP92access, **tell the user** exactly which menus to create (match **`menuName`** /93machine names such as `main`, `footer`) and link to **Structure → Menus** as in94[`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components).9596### 4. Breadcrumbs9798Breadcrumbs come from **page context**, not `menu_items`. Precedent:99[`examples/components/breadcrumb/index.jsx`](../../../examples/components/breadcrumb/index.jsx)100(`getPageData()`, `breadcrumbs`). Probe or read existing patterns before101inventing fields.102103### 5. Accessibility (minimum)104105- Wrap primary link lists in `<nav>` with distinct **`aria-label`** (or106 `aria-labelledby` pointing at visible heading text).107- Mobile toggles: `aria-expanded`, `aria-controls` when you wire IDs; button108 **`type="button"`**.109- Do not mark every link `aria-current="page"`—only the true current item when110 the design requires it.111- Nested lists (mega-menu, footer columns): preserve list semantics where112 appropriate; keep tab order predictable.113114### 6. Metadata and styling115116- Props/slots YAML:117 [`canvas-component-metadata`](../canvas-component-metadata/SKILL.md).118- Folder and mocks:119 [`canvas-component-definition`](../canvas-component-definition/SKILL.md).120- Tailwind tokens:121 [`canvas-styling-conventions`](../canvas-styling-conventions/SKILL.md).122123## Reference example124125Menu + SWR + `sortMenu` pattern:126[`examples/components/main_navigation/index.jsx`](../../../examples/components/main_navigation/index.jsx).127Consider adding **`menuName`** to `component.yml` when editors must choose the128menu without code changes.129130## Mega-menu and nested menus131132- Model **composition**: parent nav shell + **slots** for columns or featured133 blocks; child components for link groups.134- **Nested menu shape:** do not assume raw JSON:API nesting. Probe deserialized135 output with the same `JsonApiClient` call the component will use (see136 [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md)) before mapping137 children.138139## Further reading140141- [references/data-sources.md](references/data-sources.md)142143## Anti-duplication144145- Full menu code samples and `menuName` YAML live under **Navigation / Menu146 Components** in147 [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components).148- Decomposition phases A–G are not repeated here—use149 [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md).