Tips & Tricks (Atualizado: 02/06/2026)

Breadcrumbs acessíveis com Claude Code

Implemente breadcrumbs com Claude Code, JSON-LD, aria-current, CSS mobile e testes em React/Astro.

Breadcrumbs acessíveis com Claude Code

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ãoO que definirFalha comum
RótulosTítulos, categorias ou dicionárioSlugs aparecem sem tratamento
URLsLinks HTML e URLs absolutas no JSON-LDCaminhos relativos nos dados estruturados
Página atualLink ou texto no último itemEstado indicado só por cor
MobileMostrar tudo ou ocultar o meioBreadcrumb ocupa várias linhas
IdiomasPrefixo e labels locaisMistura 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.

#Claude Code #breadcrumb #navigation #structured data #UX
Grátis

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.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.