Zum Inhalt springen
UI Kit
Accent

Breadcrumbs für React

Wo eine Seite steht, als Pfad, gekürzter Pfad mit Menü oder als Leiste. Auf dem Telefon ein Zurück-Link.

Eine von 6 Components in Navigation

2 Variants

Wo eine Seite liegt, auf einen Blick

Breadcrumbs zeigen, wo eine Seite in der Website liegt, und bieten einen Weg eine Ebene nach oben. Diese Component rendert eine nav mit geordneter Liste, markiert die aktuelle Seite mit aria-current und hält die Trenner aus dem heraus, was Screenreader vorlesen.

„Trenner“ zeigt den Pfad mit Pfeil, Schrägstrich oder Punkt, für Shops, Dokus und Blogs. „Gekürzt und eingefasst“ kürzt tiefe Pfade auf ersten und letzten Eintrag mit einem Menü für die Mitte und setzt den Pfad in eine eigene Leiste unter dem Header. Am Handy wird jede Variante zu einem Zurück-Link zur übergeordneten Seite, genau den tippen Menschen dort.

Beginne den Pfad mit der Startseite und ende mit der aktuellen Seite als einfachem Text. Nutze dieselben Namen wie die Navigation. Ergänze BreadcrumbList-Daten, Suchmaschinen zeigen dann den Pfad statt der nackten Adresse.

Variants

Details

Was Brotkrumen mitbringt, aus Registry und Quellcode gelesen.

Kategorie
Navigation
Bereich
Elements
Variants
2
Version
1.0.0 · seit 1.0.0
Datei
components/ui/breadcrumbs.tsx
Bringt mit
utilskiticondropdown-menu
npm-Pakete
Keine
Sprachen
Englisch und Deutsch, 3 eigene Texte
Studio
Im Code anpassen

Props

Die Schnittstelle BreadcrumbsProps aus components/ui/breadcrumbs.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch.

  • itemsPflicht
    Crumb[]
  • separatoroptional
    'chevron' | 'slash' | 'dot'
  • variantoptional
    'default' | 'contained'

    contained: on a quiet pill-shaped surface

  • maxItemsoptional
    number

    Collapse the middle into a menu when there are more items

  • backOnMobileoptional
    boolean

    On phones show only a back link to the parent page. Default true.

Eingebaute Texte

Diese Texte schreibt Brotkrumen selbst. Die Sprache folgt dem KitProvider, über die Prop labels ersetzt du einzelne.

Alle 3 Texte anzeigen
labelEnglisch BreadcrumbDeutsch Brotkrumen
moreEnglisch Show hidden pagesDeutsch Ausgeblendete Seiten zeigen
backEnglisch Back toDeutsch Zurück zu

Barrierefreiheit

Aus dem Quellcode der Component abgeleitet.

  • Brotkrumen reagiert selbst auf die Tasten Pos1.
  • Setzt aria-current und aria-label, damit Screenreader Zweck und Zustand ansagen.
  • Nutzt die Rolle presentation.
  • Ergänzt Text für Screenreader, wo Icons und Grafiken allein stehen.
  • Zeigt bei Bedienung mit der Tastatur einen sichtbaren Fokusring.
  • Kommt ohne Client-State aus. Der Server schreibt sie vollständig, sie ist auch ohne JavaScript lesbar.

Bewegung

  • Bringt eigene Animationen mit, ruhig getaktet und ohne Wackeln.
  • Bei reduzierter Bewegung (prefers-reduced-motion) bleibt sie ruhig. 3 Stellen im Code schalten Bewegung dann ab.

Quellcode

Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.

Datei anzeigen, 133 Zeilen
components/ui/breadcrumbs.tsx
'use client';

import { useRef } from 'react';
import { cn, useInView, useTexts } from '@/lib/kit';
import { Icon, type IconName } from '@/components/ui/icon';
import { DropdownMenu } from '@/components/ui/dropdown-menu';

const TEXTS = { en: { label: 'Breadcrumb', more: 'Show hidden pages', back: 'Back to' }, de: { label: 'Brotkrumen', more: 'Ausgeblendete Seiten zeigen', back: 'Zurück zu' } };

/* The trail lays itself out from the start, one step after another, when it scrolls into view */
const STYLES =
  '.kitfx-bc-in>li,.kitfx-bc-in>.kitfx-bc-back{animation:kitfx-bc-in .55s var(--ease-kit) both;animation-delay:calc(var(--i,0) * 45ms)}' +
  '@keyframes kitfx-bc-in{from{opacity:0;transform:translateX(-8px)}}' +
  '@media (prefers-reduced-motion:reduce){.kitfx-bc-in>li,.kitfx-bc-in>.kitfx-bc-back{animation:none}}';

/* A line that grows from the start under a link on hover and focus */
const UNDERLINE =
  'relative after:pointer-events-none after:absolute after:inset-x-0 after:-bottom-0.5 after:h-px after:origin-left after:scale-x-0 after:rounded-full after:bg-current after:opacity-60 after:transition-transform after:duration-300 after:ease-[var(--ease-kit)] hover:after:scale-x-100 focus-visible:after:scale-x-100 motion-reduce:after:transition-none';

export interface Crumb {
  label: string;
  href?: string;
  icon?: IconName;
}

export interface BreadcrumbsProps {
  items: Crumb[];
  separator?: 'chevron' | 'slash' | 'dot';
  /** contained: on a quiet pill-shaped surface */
  variant?: 'default' | 'contained';
  /** Collapse the middle into a menu when there are more items */
  maxItems?: number;
  /** On phones show only a back link to the parent page. Default true. */
  backOnMobile?: boolean;
  labels?: Partial<(typeof TEXTS)['en']>;
  className?: string;
}

function Sep({ kind }: { kind: NonNullable<BreadcrumbsProps['separator']> }) {
  if (kind === 'slash') return <span aria-hidden className="select-none text-border-strong">/</span>;
  if (kind === 'dot') return <span aria-hidden className="size-[3px] rounded-full bg-border-strong" />;
  return <Icon name="chevron-right" size={14} className="text-subtle-foreground" />;
}

/**
 * Where the page sits in the site. The last item is the current page (aria-current).
 *   <Breadcrumbs items={[{ label: 'Home', href: '/', icon: 'home' }, { label: 'Docs', href: '/docs' }, { label: 'Tabs' }]} />
 */
export function Breadcrumbs({ items, separator = 'chevron', variant = 'default', maxItems, backOnMobile = true, labels, className }: BreadcrumbsProps) {
  const t = useTexts(TEXTS, labels);
  const collapse = maxItems !== undefined && items.length > maxItems && maxItems >= 3;
  const head = collapse ? items.slice(0, 1) : items;
  const hidden = collapse ? items.slice(1, items.length - (maxItems! - 2)) : [];
  const tail = collapse ? items.slice(items.length - (maxItems! - 2)) : [];
  const parent = items.length > 1 ? items[items.length - 2] : undefined;
  const [ref, inView] = useInView<HTMLElement>();
  const waited = useRef(false);
  if (!inView) waited.current = true;
  const entering = inView && waited.current;
  let order = 0;

  const crumb = (c: Crumb, last: boolean) => (
    <li key={c.label} className="flex min-w-0 items-center" style={{ '--i': order++ } as React.CSSProperties}>
      {last || !c.href ? (
        <span aria-current={last ? 'page' : undefined} className={cn('flex min-w-0 items-center gap-1.5 truncate', last ? 'font-medium text-foreground' : 'text-muted-foreground')}>
          {c.icon && <Icon name={c.icon} size={15} />}
          <span className="truncate">{c.label}</span>
        </span>
      ) : (
        <a href={c.href} className={cn('flex min-w-0 items-center gap-1.5 rounded-md text-muted-foreground transition-colors hover:text-foreground', UNDERLINE)}>
          {c.icon && <Icon name={c.icon} size={15} />}
          <span className={cn('truncate', c.icon && c === items[0] && 'max-sm:sr-only')}>{c.label}</span>
        </a>
      )}
    </li>
  );
  const sep = (k: string) => (
    <li key={k} role="presentation" aria-hidden className="flex items-center" style={{ '--i': order++ } as React.CSSProperties}>
      <Sep kind={separator} />
    </li>
  );

  const list: React.ReactNode[] = [];
  head.forEach((c, i) => {
    if (i) list.push(sep(`s${i}`));
    list.push(crumb(c, !collapse && i === items.length - 1));
  });
  if (collapse) {
    list.push(sep('sh'));
    list.push(
      <li key="more" className="flex items-center" style={{ '--i': order++ } as React.CSSProperties}>
        <DropdownMenu
          items={hidden.map((h) => ({ label: h.label, icon: h.icon, href: h.href }))}
          trigger={
            <button type="button" aria-label={t.more} className="group/more grid h-7 w-8 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground aria-expanded:bg-accent aria-expanded:text-foreground">
              <Icon name="more-horizontal" size={16} className="transition-transform duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] group-hover/more:scale-110 group-aria-expanded/more:scale-90 motion-reduce:transition-none" />
            </button>
          }
        />
      </li>,
    );
    tail.forEach((c, i) => {
      list.push(sep(`st${i}`));
      list.push(crumb(c, i === tail.length - 1));
    });
  }

  return (
    <nav ref={ref} aria-label={t.label} className={cn('min-w-0 text-sm', className)}>
      <style href="kitfx-bc" precedence="default">
        {STYLES}
      </style>
      <ol
        className={cn(
          'flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1',
          !inView && 'opacity-0',
          entering && 'kitfx-bc-in',
          variant === 'contained' && 'inline-flex rounded-full border border-border bg-surface px-3.5 py-1.5 shadow-[var(--highlight)]',
          backOnMobile && parent && 'max-sm:hidden',
        )}
      >
        {list}
      </ol>
      {backOnMobile && parent && (
        <a href={parent.href} className={cn('group/back inline-flex min-h-11 items-center gap-1.5 font-medium text-muted-foreground transition-[color,opacity,translate] duration-500 ease-[var(--ease-kit)] hover:text-foreground sm:hidden', !inView && '-translate-x-2 opacity-0')}>
          <Icon name="chevron-left" size={16} className="transition-transform duration-200 ease-[var(--ease-kit)] group-hover/back:-translate-x-0.5 motion-reduce:transition-none" />
          <span className="sr-only">{t.back} </span>
          {parent.label}
        </a>
      )}
    </nav>
  );
}

Häufige Fragen

Wie installiere ich Brotkrumen?

Mit aiw add @ai-website/breadcrumbs. Der Befehl legt die Datei components/ui/breadcrumbs.tsx in dein Projekt, der Code gehört danach dir. Mit dabei sind icon und dropdown-menu. Weitere npm-Pakete braucht sie nicht.

Welche Varianten hat Brotkrumen?

2 Varianten, Trenner und Gekürzt und eingefasst.

Kann ich die Texte von Brotkrumen übersetzen?

Ja. Brotkrumen bringt 3 eigene Texte auf Deutsch und Englisch mit, etwa „Brotkrumen“, „Ausgeblendete Seiten zeigen“ und „Zurück zu“. Die Sprache folgt dem KitProvider, einzelne Texte ersetzt du über die Prop labels.

Kann ein KI-Assistent Brotkrumen einbauen?

Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Brotkrumen mit aiw_components und installieren sie mit aiw_add @ai-website/breadcrumbs. Nenn im Prompt die Variante, etwa variant="contained", dann schreibt der Assistent deine Inhalte direkt in die Props.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert