{
  "generatedFrom": "breadcrumb.contract.json",
  "component": "Breadcrumb",
  "slug": "breadcrumb",
  "group": "Navigation",
  "tier": "leaf-lit",
  "summary": "A trail of ancestor links ending in the current page — and, at size=page-title, the page heading itself (there is no separate page-title component).",
  "install": {
    "package": "@ahaslides-product/design",
    "registry": "https://npm.pkg.github.com",
    "scope": "@ahaslides-product",
    "auth": "Published to GitHub Packages — needs a GitHub token with read:packages. Configure the @ahaslides-product scope in .npmrc before installing.",
    "npmrc": "@ahaslides-product:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}   # a GitHub token with read:packages",
    "command": "npm i @ahaslides-product/design",
    "tokenLayer": "import '@ahaslides-product/design/tokens.css';",
    "import": "import '@ahaslides-product/design/aha-breadcrumb';",
    "element": "aha-breadcrumb",
    "registers": "aha-breadcrumb"
  },
  "feeds": {
    "doc": "https://ahaslides-product.github.io/ahaslides-design/breadcrumb/index.html",
    "md": "https://ahaslides-product.github.io/ahaslides-design/breadcrumb/breadcrumb.md",
    "agentJson": "https://ahaslides-product.github.io/ahaslides-design/breadcrumb.agent.json",
    "llms": "https://ahaslides-product.github.io/ahaslides-design/breadcrumb.llms.txt",
    "index": "https://ahaslides-product.github.io/ahaslides-design/llms.txt"
  },
  "recommendedSnippet": "html",
  "vibeCode": "Emit the HTML snippet by default — <aha-breadcrumb> is a standard custom element that renders on open — React/Vue are thin adapters over the same element. Paste into an .html file and it renders with no build step.",
  "frameworks": {
    "react": {
      "via": "web-component",
      "ref": "aha-breadcrumb"
    },
    "vue": {
      "via": "web-component",
      "ref": "aha-breadcrumb"
    },
    "html": {
      "via": "web-component",
      "ref": "aha-breadcrumb",
      "runnable": "paste-and-run — no build step, renders on open"
    }
  },
  "props": [
    {
      "name": "items",
      "type": "JSON [{label, href?, icon?, disabled?}]",
      "default": "[]",
      "desc": "The trail; an entry with href is a link, the last / href-less entry is the current page, icon adds a leading DS glyph, disabled mutes it"
    },
    {
      "name": "size",
      "type": "'default' | 'mini' | 'page-title'",
      "default": "default",
      "desc": "Scale of the trail. default 13/22 · mini 12/18 for sub-page nav; page-title renders the current crumb as the page heading (24/600 alone, 18/600 in a trail)"
    },
    {
      "name": "heading-level",
      "type": "1 | 2 | 3 | 4 | 5 | 6",
      "default": "1",
      "desc": "Heading tag used for the current crumb when size=page-title — an <h1> page title by default; drop to h2–h6 if the breadcrumb isn't the page's top heading"
    },
    {
      "name": "separator",
      "type": "'caret' | 'slash'",
      "default": "caret",
      "desc": "Divider glyph between crumbs — the DS caret (default) or a slash"
    },
    {
      "name": "maxItems",
      "type": "number",
      "default": "0",
      "desc": "Collapse the middle into an ellipsis toggle when the trail is longer than N (0 = never)"
    },
    {
      "name": "navigate",
      "type": "CustomEvent",
      "default": "—",
      "desc": "Composed event on a link click, detail { index, href }"
    }
  ],
  "tokens": [
    "text-secondary",
    "text-default",
    "text-disabled",
    "color-primary",
    "icon-muted",
    "bg-hover",
    "border-focus",
    "radius-xs"
  ],
  "spec": [
    {
      "label": "Type",
      "value": "Plus Jakarta — default 13/22, mini 12/18, page-title 18/1.3 (solo title 24/1.3)"
    },
    {
      "label": "Link",
      "value": "text-secondary #4A4A4A → hover color-primary #6A1EBB (motion-fast + ease-out)"
    },
    {
      "label": "Current",
      "value": "text-default #1A1A1A, SemiBold 600, aria-current=page (stronger than links)"
    },
    {
      "label": "Page title",
      "value": "size=page-title → current crumb is a real heading (<h1>, heading-level picks h1–h6); whole trail SemiBold 600; standalone (1 item) = Heading4 24/600/ls 0, in a trail = 18/1.3/ls 0.2"
    },
    {
      "label": "Item icon",
      "value": "optional leading <aha-icon>, currentColor — 12 mini / 14 default / 16 page-title"
    },
    {
      "label": "Disabled",
      "value": "text-disabled #B5B5B5, not a link (aria-disabled)"
    },
    {
      "label": "Separator",
      "value": "caret (<aha-icon name=\"system-caret-right\">, icon-muted, 12 · 16 at page-title) or slash \"/\""
    },
    {
      "label": "Collapse",
      "value": "maxItems > trail → first + ellipsis toggle + trailing (maxItems−1)"
    },
    {
      "label": "Gap",
      "value": "8 between crumbs (4 at mini), 6 icon→label"
    }
  ],
  "opinion": {
    "whenToUse": [
      {
        "what": "Breadcrumb (page-title)",
        "when": "the heading of a product page — size=page-title makes the current crumb the page <h1>, alone or at the end of its ancestor path. This is THE page title; do not hand-roll a plain <h1>/heading"
      },
      {
        "what": "Breadcrumb (default / mini)",
        "when": "showing where a page sits in a hierarchy the user can climb back up, without heading-level prominence"
      },
      {
        "what": "Screen heading",
        "when": "the page header ROW — a page title (via this breadcrumb) plus right-aligned action buttons"
      },
      {
        "what": "Tabs",
        "when": "switching between sibling views at the same level — not a hierarchy"
      },
      {
        "what": "Back button",
        "when": "a single linear step back is all that's needed"
      }
    ],
    "note": "The page title is NOT a plain <h1> — it is <aha-breadcrumb size=\"page-title\">, with just the current page as the only item (or the full ancestor path in front of it). A hand-rolled heading is the wrong answer: it drifts from the DS type scale and loses the built-in trail, aria-current, and navigate event. Keep breadcrumbs to the real ancestor path — don't pad them. The last item is the current page and is never a link. Labels are short nouns, sentence case.",
    "dsv3": "COVERED — DS V3 Breadcrumb component set (fileKey MwjvUjVI0HnfwD9EwPXRAy, node 61346:4893) with Components/Link + Components/Separator sub-parts and a Count × Size matrix (Size = Mini / Default / Page title, Count = 1…4+). Bindings from get_variable_defs: default = Body 14 (fontSizeDefault) / lh 1.5 / ls 0.2 (Paragraph); mini = 12 (fontSizeSM) / lh 1.5 / ls 0.3 (Subtext); page-title = SemiBold 600 across the trail at Heading6 18 / lh 1.3 / ls 0.2 (Subheadings), and standalone (Count=1) at Heading4 24 / lh 1.3 / ls 0 (Headlines). Colour token-derived: text-secondary link, text-default current (aria-current, and a real heading in page-title), color-primary hover, icon-muted separator, text-disabled disabled item. Matrix covered: three sizes, page-title heading (h1–h6 via heading-level, solo vs trailed), leading item icons, caret vs slash separator, maxItems middle-collapse (ellipsis toggle), disabled item. Motion + glyphs bound to the shared motion tokens and the DS icon library. (Default kept at 13/22, matching the shipped element and its conformance probe; the Figma Default cell is 14 — within a px of the same role.)"
  },
  "surfaces": [
    "editor",
    "dashboard",
    "admin",
    "settings"
  ],
  "snippets": {
    "html": "<!-- Paste-and-run: save as .html and open in a browser. No build step.\n     <aha-breadcrumb> is the SAME shared custom element React and Vue consume — here in its\n     native form. Theming comes only from the --aha-* tokens in tokens.css. -->\n<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css\">\n<script type=\"module\">\n  import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-breadcrumb.js';   // registers <aha-breadcrumb> (+ <aha-icon>)\n</script>\n\n<!-- Each item: { label, href?, icon?, disabled? }. An href-less / last entry is the current page.\n     separator=\"caret\" (default) or \"slash\"; maxItems collapses the middle into an ellipsis toggle. -->\n<aha-breadcrumb id=\"crumbs\" separator=\"caret\" maxItems=\"4\"\n  items='[{\"label\":\"Home\",\"href\":\"/\",\"icon\":\"system-house\"},{\"label\":\"Presentations\",\"href\":\"/p\",\"icon\":\"system-folder\"},{\"label\":\"Team offsite\",\"href\":\"/p/1\"},{\"label\":\"Q3 deck\",\"href\":\"/p/1/d\"},{\"label\":\"Editor\"}]'>\n</aha-breadcrumb>\n\n<!-- PAGE TITLE — this is how you render a page heading. size=\"page-title\" makes the current crumb a\n     real <h1> (heading-level picks h1–h6). Never hand-roll a plain <h1> for a page header — use this,\n     so the type scale, the ancestor trail, aria-current and the navigate event all come for free. -->\n<aha-breadcrumb size=\"page-title\" items='[{\"label\":\"Presentations\"}]'></aha-breadcrumb>\n<!-- …or with the ancestor path in front of the heading -->\n<aha-breadcrumb size=\"page-title\"\n  items='[{\"label\":\"Home\",\"href\":\"/\"},{\"label\":\"Presentations\",\"href\":\"/p\"},{\"label\":\"Team offsite\"}]'>\n</aha-breadcrumb>\n\n<script>\n  // The last item is the current page (not a link); links emit a composed `navigate` event.\n  document.getElementById('crumbs').addEventListener('navigate', (e) => console.log('go to', e.detail.href));\n</script>\n",
    "react": "import '@ahaslides-product/design/aha-breadcrumb';   // registers <aha-breadcrumb> (+ <aha-icon>)\nimport { useRef, useEffect } from 'react';\n\n// React 18 needs a thin wrapper for the composed `navigate` event; React 19 can bind onNavigate directly.\nfunction AhaBreadcrumb({ items, onNavigate }) {\n  const ref = useRef();\n  useEffect(() => {\n    const el = ref.current;\n    const h = (e) => onNavigate?.(e.detail);\n    el.addEventListener('navigate', h);\n    return () => el.removeEventListener('navigate', h);\n  });\n  return <aha-breadcrumb ref={ref} items={JSON.stringify(items)} separator=\"caret\" maxItems={4} />;\n}\n\n// usage — each item: { label, href?, icon?, disabled? }; the last / href-less entry is the current page.\n<AhaBreadcrumb\n  items={[\n    { label: 'Home', href: '/', icon: 'system-house' },\n    { label: 'Presentations', href: '/p', icon: 'system-folder' },\n    { label: 'Team offsite', href: '/p/1' },\n    { label: 'Editor' },\n  ]}\n  onNavigate={({ href }) => router.push(href)}\n/>\n",
    "vue": "// main.ts — register the element + mark aha-* as custom elements\nimport '@ahaslides-product/design/aha-breadcrumb';\napp.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');\n\n// Component.vue\n<script setup>\nimport { computed } from 'vue';\n// each item: { label, href?, icon?, disabled? }; the last / href-less entry is the current page.\nconst trail = [\n  { label: 'Home', href: '/', icon: 'system-house' },\n  { label: 'Presentations', href: '/p', icon: 'system-folder' },\n  { label: 'Team offsite', href: '/p/1' },\n  { label: 'Editor' },\n];\nconst items = computed(() => JSON.stringify(trail));\nfunction onNavigate(e) { router.push(e.detail.href); }\n</script>\n\n<template>\n  <aha-breadcrumb :items=\"items\" separator=\"caret\" :maxItems=\"4\" @navigate=\"onNavigate\" />\n</template>\n"
  }
}
