# Breadcrumbs

> Server-rendered breadcrumb navigation that automatically resolves the breadcrumb trail from the active WordPress query context.

# Breadcrumbs

## Overview

Breadcrumbs is a **server-rendered** navigation component that automatically builds a breadcrumb trail from the active WordPress query. Unlike interactive components (Accordion, Tabs, etc.), Breadcrumbs has no client-side runtime — the trail is resolved entirely in PHP during block rendering and injected into the page as final HTML.

The component automatically handles pages with parent/ancestor hierarchies, single posts with category ancestry, custom post type archives, term archives, date archives, author archives, search results, and 404 pages. It also outputs `BreadcrumbList` JSON-LD structured data for SEO when enabled.

Breadcrumbs is a single-component family — there are no sub-components. It is placed into patterns or templates and the server-side trail resolver does the rest.

## Authoring Structure

```
Breadcrumbs (OmeBreadcrumbs)
├── Home slot (optional custom home content)
└── Separator slot (optional custom separator content)
```

### Placement Rules

| Slot | Purpose |
|---|---|
| **home** | Custom content inside the home link. When empty, the `homeLabel` text is used. |
| **separator** | Custom separator between items. When empty, the `separator` text is used. |

## How It Works

Breadcrumbs uses a two-phase rendering pipeline:

1. **Editor / Preview phase** — The component renders a static preview with placeholder items ("Home", "Blog", "Current page") so the editor shows a representative breadcrumb trail.
2. **Frontend phase** — A block interceptor (`BreadcrumbsBlockInterceptor`) replaces the placeholder with a real breadcrumb trail resolved from the current WordPress query context via `BreadcrumbTrailResolver`.

This means the breadcrumb trail is always accurate for the current page — no manual item configuration is needed.

### Supported WordPress Contexts

| Context | Trail |
|---|---|
| **Front page** | Hidden by default (enable with `showOnFrontPage`). |
| **Posts index** | Home → Blog (or custom Posts Page). |
| **Single post** | Home → Blog → Category → Post. |
| **Page (with ancestors)** | Home → Parent → … → Page. |
| **Custom post type** | Home → Archive → Term (if hierarchical) → Post. |
| **Post type archive** | Home → Archive. |
| **Term archive** | Home → … → Term (with ancestor terms for hierarchical taxonomies). |
| **Date archive** | Home → Year → Month → Day (drills down as deep as the URL). |
| **Author archive** | Home → Author name. |
| **Search results** | Home → Search. |
| **404** | Home → Not Found. |

## Quick Start

<CodeExample title="Basic breadcrumbs" language="jsx">
{`<OmeBreadcrumbs
  content='{{"homeLabel":"Home","separator":"/","ariaLabel":"Breadcrumb"}}'
  settings='{{"showHome":true,"showCurrent":true,"linkCurrent":false,"showOnFrontPage":false,"enableSchema":true}}'
/>`}
</CodeExample>

<CodeExample title="Breadcrumbs with custom separator slot" language="jsx">
{`<OmeBreadcrumbs
  content='{{"homeLabel":"Home","separator":"/","ariaLabel":"Breadcrumb"}}'
  settings='{{"showHome":true,"showCurrent":true}}'
>
  {#slot separator}
    →
  {/slot}
</OmeBreadcrumbs>`}
</CodeExample>

<CodeExample title="Breadcrumbs with custom home icon" language="jsx">
{`<OmeBreadcrumbs
  content='{{"homeLabel":"Home","separator":"/","ariaLabel":"Breadcrumb"}}'
  settings='{{"showHome":true,"showCurrent":true}}'
>
  {#slot home}
    <svg class="home-icon" aria-hidden="true" width="16" height="16"><!-- icon SVG --></svg>
  {/slot}
</OmeBreadcrumbs>`}
</CodeExample>

---

## Props

<PropsTable group="Content">
  <Prop name="homeLabel" type="string" defaultValue="Home">
    Text used for the home breadcrumb. Shown when the `home` slot is empty.
  </Prop>
  <Prop name="separator" type="string" defaultValue="/">
    Text shown between breadcrumb items. Shown when the `separator` slot is empty.
  </Prop>
  <Prop name="ariaLabel" type="string" defaultValue="Breadcrumb">
    Accessible label for the `<nav>` landmark. Screen readers announce this to identify the navigation region.
  </Prop>
</PropsTable>

<PropsTable group="Settings">
  <Prop name="showHome" type="boolean" defaultValue="true">
    Includes the home breadcrumb as the first item in the trail.
  </Prop>
  <Prop name="showCurrent" type="boolean" defaultValue="true">
    Includes the current page as the last item in the trail. When `false`, the trail ends at the parent item.
  </Prop>
  <Prop name="linkCurrent" type="boolean" defaultValue="false">
    Renders the current page breadcrumb as a link (``) instead of a plain ``. The current item always has `aria-current="page"` regardless of this setting.
  </Prop>
  <Prop name="showOnFrontPage" type="boolean" defaultValue="false">
    Keeps breadcrumbs visible on the front page. When `false` (default), the component outputs nothing on the front page.
  </Prop>
  <Prop name="enableSchema" type="boolean" defaultValue="true">
    Outputs a `BreadcrumbList` JSON-LD script tag after the navigation markup. Schema is only rendered when there are at least two items with URLs. Disable this if your theme or an SEO plugin already provides breadcrumb schema.
  </Prop>
</PropsTable>

<PropsTable group="Styling">
  <Prop name="rootClass" type="class" defaultValue="ome-breadcrumbs-root-default">
    CSS class applied to the root `<nav>` element.
  </Prop>
  <Prop name="listClass" type="class" defaultValue="ome-breadcrumbs-list-default">
    CSS class applied to the `` list element.
  </Prop>
  <Prop name="itemClass" type="class" defaultValue="ome-breadcrumbs-item-default">
    CSS class applied to each `` item element.
  </Prop>
  <Prop name="linkClass" type="class" defaultValue="ome-breadcrumbs-link-default">
    CSS class applied to each `` link element.
  </Prop>
  <Prop name="currentClass" type="class" defaultValue="ome-breadcrumbs-current-default">
    CSS class applied to the current page item (whether rendered as a link or span).
  </Prop>
  <Prop name="separatorClass" type="class" defaultValue="ome-breadcrumbs-separator-default">
    CSS class applied to each separator `` element.
  </Prop>
</PropsTable>

---

## Default CSS

The default styles provide a horizontal flex layout with ACSS-aware custom properties:

```css
/* Root: inherits text color and uses small text size */
.ome-breadcrumbs-root-default {
  color: var(--text-dark, currentColor);
  font-size: var(--text-s, 0.875rem);
}

/* List: horizontal flexbox with wrapping */
.ome-breadcrumbs-list-default {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs, 0.75rem);
  margin: 0;
  padding: 0;
}

/* Items: inline flex, no list style */
.ome-breadcrumbs-item-default {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs, 0.75rem);
  list-style: none;
}
```

The fixed base styles enforce `box-sizing: border-box`, hide `[hidden]` elements, and provide `:focus-visible` outline styling on links — matching the same foundation used across all Etch components.

---

## Accessibility

The rendered markup uses semantic HTML:

- The root element is a `<nav>` with `aria-label` (defaults to "Breadcrumb").
- The list uses an ordered list `` for correct semantics.
- The current page item has `aria-current="page"`.
- Separators are marked `aria-hidden="true"` to avoid screen reader noise.
- `:focus-visible` outlines are provided by the fixed base styles.

---

## Common Mistakes

<CommonMistake title="Expecting client-side runtime behavior">
  Breadcrumbs is entirely server-rendered. There is no JavaScript runtime for this component — the trail is resolved in PHP during `render_block`. You cannot dynamically update breadcrumbs with client-side JavaScript.
</CommonMistake>

<CommonMistake title="Setting showOnFrontPage to false and expecting output on the homepage">
  By default `showOnFrontPage` is `false`, which means the component outputs nothing on the front page. If your design calls for breadcrumbs on the homepage, set `showOnFrontPage` to `true` and `showHome` to `true`.
</CommonMistake>

<CommonMistake title="Enabling schema when an SEO plugin already outputs BreadcrumbList">
  If you use Yoast SEO, Rank Math, or another plugin that generates `BreadcrumbList` JSON-LD, set `enableSchema` to `false` to avoid duplicate schema output.
</CommonMistake>

<CommonMistake title="Expecting the editor preview to match the frontend trail">
  The editor renders static placeholder items ("Home", "Blog", "Current page") so you can preview the visual design. The actual breadcrumb trail is only generated on the frontend from the real WordPress query context.
</CommonMistake>

---

## FAQs

<details>
<summary>Can I customize which categories appear for single posts?</summary>

The trail resolver picks the first assigned term (lowest `term_id`) from the post's categories. To control which category appears, assign only one category to the post, or use a plugin that filters `get_the_terms`.

</details>

<details>
<summary>Does this work with custom post types?</summary>

Yes. For custom post types with `has_archive`, the resolver includes the post type archive link. If the post type has hierarchical taxonomies, the first assigned term in the first public hierarchical taxonomy is included in the trail.

</details>

<details>
<summary>Can I use a custom icon or SVG for the separator?</summary>

Yes. Use the `separator` slot to provide custom markup instead of the plain text separator:

```jsx
<OmeBreadcrumbs>
  {#slot separator}
    
      <svg><!-- your icon --></svg>
    
  {/slot}
</OmeBreadcrumbs>
```

</details>

<details>
<summary>What happens on pages with no ancestors?</summary>

For a top-level page, the trail is simply: Home → Page Title. For nested pages, each ancestor is included in order: Home → Parent → Grandparent → Page Title.

</details>

<details>
<summary>Why is the schema not appearing?</summary>

Schema output requires at least two items with URLs in the trail. If `showHome` is `false` and only the current page remains, or if the trail has fewer than two linkable items, the JSON-LD script tag is omitted. Also check that `enableSchema` is `true`.

</details>
