Breadcrumbs acessíveis com Claude Code
Implemente breadcrumbs com Claude Code, JSON-LD, aria-current, CSS mobile e testes em React/Astro.
Breadcrumbs parecem uma pequena linha acima do título, mas em produção eles conectam arquitetura do site, links internos, acessibilidade, dados estruturados e layout mobile. Uma versão fraca pode parecer correta e ainda assim não informar a página atual a leitores de tela, gerar JSON-LD com URL relativa ou quebrar em telas pequenas.
Nos templates do ClaudeCodeLab, o primeiro rascunho do Claude Code criou Home > Blog > Title, mas esqueceu aria-current, usou URLs relativas no JSON-LD, quebrou no mobile e exibiu slugs crus. A solução é dar ao Claude Code o contrato completo antes de pedir código.
Este guia mostra como implementar e revisar breadcrumbs acessíveis para React, sites estilo Next.js e Astro. Leia também SEO com Claude Code, acessibilidade, Astro e React.
Contrato de implementação
Breadcrumb não é substituto do botão voltar. Ele mostra hierarquia, leva a páginas superiores e ajuda buscadores a entender relações. O Breadcrumb Pattern do WAI-ARIA APG recomenda uma região de navegação com rótulo e aria-current="page" para a página atual.
Os dados estruturados devem seguir schema.org BreadcrumbList, com position para preservar a ordem. Para Google Search, use a documentação oficial de breadcrumb structured data.
| Decisão | O que definir | Falha comum |
|---|---|---|
| Rótulos | Títulos, categorias ou dicionário | Slugs aparecem sem tratamento |
| URLs | Links HTML e URLs absolutas no JSON-LD | Caminhos relativos nos dados estruturados |
| Página atual | Link ou texto no último item | Estado indicado só por cor |
| Mobile | Mostrar tudo ou ocultar o meio | Breadcrumb ocupa várias linhas |
| Idiomas | Prefixo e labels locais | Mistura de idiomas |
Prompt para Claude Code
Implemente breadcrumbs para um site React/Next.js ou Astro.
Requisitos:
- Receber items como { label: string; href: string }[].
- Adicionar aria-current="page" ao último item.
- Usar nav aria-label="Breadcrumb".
- Marcar separadores com aria-hidden="true".
- Gerar JSON-LD BreadcrumbList a partir do mesmo array items.
- Converter URLs do JSON-LD em absolutas usando siteUrl.
- Criar helper para montar items a partir de pathname.
- Transformar slugs em texto legível e permitir override por dicionário.
- No mobile, ocultar itens intermediários mantendo a página atual legível.
- Adicionar testes Vitest para raiz, caminhos profundos, labels localizados e query strings.
- Ao final, listar checks de acessibilidade e dados estruturados.
Para mudanças maiores, peça uma segunda revisão com a checklist de review.
Componente React
import type { ReactNode } from "react";
export type BreadcrumbItem = {
label: string;
href: string;
};
type BreadcrumbProps = {
items: BreadcrumbItem[];
siteUrl: string;
ariaLabel?: string;
};
function toAbsoluteUrl(siteUrl: string, href: string) {
return new URL(href, siteUrl).toString();
}
function Separator(): ReactNode {
return (
<span className="breadcrumb__separator" aria-hidden="true">
/
</span>
);
}
export function Breadcrumb({
items,
siteUrl,
ariaLabel = "Breadcrumb",
}: BreadcrumbProps) {
if (items.length <= 1) return null;
const jsonLd = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: items.map((item, index) => ({
"@type": "ListItem",
position: index + 1,
item: {
"@id": toAbsoluteUrl(siteUrl, item.href),
name: item.label,
},
})),
};
return (
<>
<nav className="breadcrumb" aria-label={ariaLabel}>
<ol className="breadcrumb__list">
{items.map((item, index) => {
const isCurrent = index === items.length - 1;
return (
<li className="breadcrumb__item" key={item.href}>
{index > 0 ? <Separator /> : null}
{isCurrent ? (
<span className="breadcrumb__current" aria-current="page">
{item.label}
</span>
) : (
<a className="breadcrumb__link" href={item.href}>
{item.label}
</a>
)}
</li>
);
})}
</ol>
</nav>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
</>
);
}
Helper de rotas
import type { BreadcrumbItem } from "@/components/Breadcrumb";
export type BreadcrumbLabels = Record<string, string>;
function titleize(segment: string) {
return decodeURIComponent(segment)
.replace(/[-_]+/g, " ")
.replace(/\b\w/g, (char) => char.toUpperCase());
}
export function buildBreadcrumbs(
pathname: string,
labels: BreadcrumbLabels = {},
): BreadcrumbItem[] {
const cleanPath = pathname.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
const segments = cleanPath.split("/").filter(Boolean);
const items: BreadcrumbItem[] = [
{ label: labels["/"] ?? "Home", href: "/" },
];
let href = "";
for (const segment of segments) {
href += `/${segment}`;
items.push({
label: labels[href] ?? labels[segment] ?? titleize(segment),
href,
});
}
return items;
}
import { Breadcrumb } from "@/components/Breadcrumb";
import { buildBreadcrumbs } from "@/lib/breadcrumbs";
const siteUrl = "https://claudecodelab.com";
export default async function ArticlePage() {
const pathname = "/pt/blog/claude-code-breadcrumb-navigation";
const labels = {
"/": "Início",
"/pt": "Português",
"/pt/blog": "Artigos",
"/pt/blog/claude-code-breadcrumb-navigation":
"Breadcrumbs acessíveis com Claude Code",
};
const items = buildBreadcrumbs(pathname, labels);
return (
<main>
<Breadcrumb items={items} siteUrl={siteUrl} ariaLabel="Breadcrumb" />
<h1>Breadcrumbs acessíveis com Claude Code</h1>
</main>
);
}
No Astro, mantenha o mesmo formato de items, use Astro.url.pathname e emita JSON-LD com set:html={JSON.stringify(jsonLd)}.
CSS mobile
.breadcrumb { margin-block: 0 1rem; font-size: 0.875rem; color: #4b5563; }
.breadcrumb__list { display: flex; flex-wrap: wrap; gap: 0.25rem; list-style: none; margin: 0; padding: 0; }
.breadcrumb__item { align-items: center; display: inline-flex; min-width: 0; }
.breadcrumb__link { color: #2563eb; text-decoration: underline; text-underline-offset: 0.15em; }
.breadcrumb__current { color: #111827; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.breadcrumb__separator { color: #9ca3af; margin-inline: 0.35rem; }
@media (max-width: 640px) {
.breadcrumb__list { flex-wrap: nowrap; }
.breadcrumb__item:not(:first-child):not(:nth-last-child(-n + 2)) { display: none; }
.breadcrumb__item:nth-last-child(2)::after { color: #9ca3af; content: "..."; margin-inline: 0.35rem; }
.breadcrumb__current { max-width: 58vw; }
}
Testes
import { describe, expect, it } from "vitest";
import { buildBreadcrumbs } from "./breadcrumbs";
describe("buildBreadcrumbs", () => {
it("returns only Home for the root path", () => {
expect(buildBreadcrumbs("/")).toEqual([{ label: "Home", href: "/" }]);
});
it("builds nested breadcrumbs and ignores query strings", () => {
expect(buildBreadcrumbs("/blog/claude-code?page=2")).toEqual([
{ label: "Home", href: "/" },
{ label: "Blog", href: "/blog" },
{ label: "Claude Code", href: "/blog/claude-code" },
]);
});
it("uses localized labels when provided", () => {
expect(
buildBreadcrumbs("/pt/blog/claude-code-breadcrumb-navigation", {
"/": "Início",
"/pt": "Português",
"/pt/blog": "Artigos",
"/pt/blog/claude-code-breadcrumb-navigation": "Breadcrumbs acessíveis",
}),
).toEqual([
{ label: "Início", href: "/" },
{ label: "Português", href: "/pt" },
{ label: "Artigos", href: "/pt/blog" },
{ label: "Breadcrumbs acessíveis", href: "/pt/blog/claude-code-breadcrumb-navigation" },
]);
});
});
Em E2E, confira nav[aria-label], um único aria-current="page", JSON-LD válido, URLs absolutas e layout mobile sem sobreposição. Veja também testes com Playwright.
Casos de uso e armadilhas
Use breadcrumbs em blogs e documentação, páginas de treinamento ou ecommerce, telas SaaS profundas e sites multilíngues. Eles melhoram contexto, navegação e links internos.
As falhas comuns são indicar a página atual só por cor, usar URL relativa no JSON-LD, mostrar slug cru, quebrar no mobile e manter duas fontes de dados para UI e estrutura. Antes de publicar, revise aria-label, aria-current, separadores ocultos, BreadcrumbList, position, URL absoluta, consistência UI/JSON-LD, mobile, idioma e validação no Search Console.
CTA e verificação
No ClaudeCodeLab, breadcrumbs ajudam a levar leitores para a folha gratuita, produtos e treinamento/consultoria Claude Code. Verifiquei os exemplos separando componente React, helper e testes Vitest; gerar UI e JSON-LD do mesmo items eliminou divergências após renomear categorias.
Resumo
Breadcrumb acessível não é só um separador entre links. Peça a Claude Code labels, rotas, aria-current, JSON-LD, URLs absolutas, comportamento mobile e testes, e revise com as fontes oficiais antes de publicar.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.