Canvas navigation components
Project type
Before applying this skill, check package.json for a dependency named
@drupal-canvas/headless or starting with @drupal-canvas/headless-. If one is
present, this is a Canvas Headless codebase — read
canvas-headless first. The decomposition,
naming, and accessibility guidance below applies unchanged, but the fetch
patterns (SWR, JsonApiClient, sortMenu, getPageData from drupal-canvas)
are for Canvas-rendered React projects only; in headless projects fetch menus
with the SDK's JSON:API client (getClient()) instead. If no such dependency is
present, this is a Canvas-rendered React codebase: components are React
(index.jsx/.tsx) and everything in this skill applies as written. These are
the only two project types.
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 components78## Project type910Before applying this skill, check `package.json` for a dependency named11`@drupal-canvas/headless` or starting with `@drupal-canvas/headless-`. If one is12present, this is a Canvas Headless codebase — read13[`canvas-headless`](../canvas-headless/SKILL.md) first. The decomposition,14naming, and accessibility guidance below applies unchanged, but the fetch15patterns (SWR, `JsonApiClient`, `sortMenu`, `getPageData` from `drupal-canvas`)16are for Canvas-rendered React projects only; in headless projects fetch menus17with the SDK's JSON:API client (`getClient()`) instead. If no such dependency is18present, this is a Canvas-rendered React codebase: components are React19(`index.jsx`/`.tsx`) and everything in this skill applies as written. These are20the only two project types.2122Build **navigation** that fits Canvas: reusable names, clear **props**23(`variant`, `menuName`) and **slots** (logo, utilities, mega-menu regions),24correct **data source** (Drupal menu vs breadcrumb context vs static), and25**accessible** markup.2627This skill **stacks on**:28291. [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md) —30 regions, tree, prop/slot sketch, reuse-first naming **before** code.312. [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md) — SWR,32 `JsonApiClient`, menu resource pattern, Workbench rules.3334Then apply [`canvas-component-metadata`](../canvas-component-metadata/SKILL.md),35[`canvas-component-definition`](../canvas-component-definition/SKILL.md)36(including37[`references/naming.md`](../canvas-component-definition/references/naming.md)),38[`canvas-styling-conventions`](../canvas-styling-conventions/SKILL.md), and39[`canvas-component-composability`](../canvas-component-composability/SKILL.md)40for slots and repeatable groups.4142## Workflow (order matters)4344### 1. Decompose (mandatory)4546Follow [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md)47through at least **Phase E** (props/slots) before writing React or48`component.yml`.4950Navigation-specific prompts:5152- Separate **chrome** (bar, drawer shell) from **link lists** and optional53 **columns** (footer, mega-menu). Prefer composition over one god-component.54- Repeating link groups or cards → parent + child pattern per55 [`canvas-component-composability/references/repeatable-content.md`](../canvas-component-composability/references/repeatable-content.md).56- Use generic `machineName`s (`main-navigation`, `footer-navigation`), not57 page-specific names. Express placement in the page, not the component name.58- Sketch **`variant`** for layout modes (horizontal, drawer, compact footer).59- Add **`menuName`** in the sketch when the nav reads from a **Drupal menu**60 (see step 2). Use **slots** for logo, utility links, or CTAs when authors61 compose those blocks.6263### 2. Choose a data source6465See [references/data-sources.md](references/data-sources.md) for a decision66table. Summary:6768| Source | When |69| --------------- | --------------------------------------------------------------------------- |70| **Drupal menu** | Editors manage links in Structure → Menus; use `menu_items` + `getResource` |71| **Breadcrumb** | Trail from current page context via `getPageData()` |72| **Static** | Rare; document why; still provide sensible defaults or slots |7374### 3. Implement the fetch (Drupal menu)7576Use the **Navigation / Menu Components** section in77[`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components):7879- `useSWR(menuName ? ['menu_items', menuName] : null, ([type, id]) => client.getResource(type, id))`80- `Array.from(sortMenu(data))` when data is valid; otherwise81 **`FALLBACK_LINKS`**82- Always define **`FALLBACK_LINKS`** so Workbench and sites without the menu83 still render.84- Expose **`menuName`** in `component.yml` when editors should pick the Drupal85 menu (machine name, e.g. `main`, `footer`). Hard-coding only the default in86 JSX without a prop is fine for examples; **production** nav that must switch87 menus should register `menuName` per data-fetching rules.88- `menuName` omitted or null in props: use SWR key `null` and show fallback when89 you need static-only preview behavior.9091Do **not** fabricate JSON:API menu payloads in Workbench mocks. Prefer real92loading/error/empty behavior; fallback links cover the empty-menu case.9394**Menus in Drupal (preferred):** Production navigation should live in **Drupal95menus** (CMS), not only as hardcoded arrays in code. **`FALLBACK_LINKS`** are96for Workbench / empty-menu cases — not a long-term substitute for CMS menus on a97real site.9899On **Acquia Source**, prefer creating or updating menus via Source MCP using100[`acquia-source-navigation-menus`](../acquia-source-navigation-menus/SKILL.md)101when automating that environment; otherwise use **Structure → Menus** in Drupal.102Align **`menu_name`** with this component’s **`menuName`** prop.103104For **non–Acquia Source** targets, use the **Drupal admin** (or your deployment105process) — do **not** apply the Source MCP menu skill. If there is no admin/MCP106access, **tell the user** exactly which menus to create (match **`menuName`** /107machine names such as `main`, `footer`) and link to **Structure → Menus** as in108[`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components).109110### 4. Breadcrumbs111112Breadcrumbs come from **page context**, not `menu_items`. Precedent:113[`examples/components/breadcrumb/index.jsx`](../../../examples/components/breadcrumb/index.jsx)114(`getPageData()`, `breadcrumbs`). Probe or read existing patterns before115inventing fields.116117### 5. Accessibility (minimum)118119- Wrap primary link lists in `<nav>` with distinct **`aria-label`** (or120 `aria-labelledby` pointing at visible heading text).121- Mobile toggles: `aria-expanded`, `aria-controls` when you wire IDs; button122 **`type="button"`**.123- Do not mark every link `aria-current="page"`—only the true current item when124 the design requires it.125- Nested lists (mega-menu, footer columns): preserve list semantics where126 appropriate; keep tab order predictable.127128### 6. Metadata and styling129130- Props/slots YAML:131 [`canvas-component-metadata`](../canvas-component-metadata/SKILL.md).132- Folder and mocks:133 [`canvas-component-definition`](../canvas-component-definition/SKILL.md).134- Tailwind tokens:135 [`canvas-styling-conventions`](../canvas-styling-conventions/SKILL.md).136137## Reference example138139Menu + SWR + `sortMenu` pattern:140[`examples/components/main_navigation/index.jsx`](../../../examples/components/main_navigation/index.jsx).141Consider adding **`menuName`** to `component.yml` when editors must choose the142menu without code changes.143144## Mega-menu and nested menus145146- Model **composition**: parent nav shell + **slots** for columns or featured147 blocks; child components for link groups.148- **Nested menu shape:** do not assume raw JSON:API nesting. Probe deserialized149 output with the same `JsonApiClient` call the component will use (see150 [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md)) before mapping151 children.152153## Further reading154155- [references/data-sources.md](references/data-sources.md)156157## Anti-duplication158159- Full menu code samples and `menuName` YAML live under **Navigation / Menu160 Components** in161 [`canvas-data-fetching`](../canvas-data-fetching/SKILL.md#navigation--menu-components).162- Decomposition phases A–G are not repeated here—use163 [`canvas-design-decomposition`](../canvas-design-decomposition/SKILL.md).