Breadcrumbs

Breadcrumbs are used to display the current page's location within a hierarchy and enable easy navigation.

Installation

Add the following Soul components

The breadcrumbs component uses the streamable, skeleton and animated-link components. Make sure you have added them to your project.

Install the following dependencies

npm install clsx lucide-react

Copy and paste the following code into your project

sections/breadcrumbs/index.tsx

import { clsx } from 'clsx';import { ChevronRight } from 'lucide-react';import { Stream, Streamable } from '@/vibes/soul/lib/streamable';import { AnimatedLink } from '@/vibes/soul/primitives/animated-link';import * as Skeleton from '@/vibes/soul/primitives/skeleton';export interface Breadcrumb {  label: string;  href: string;}export interface BreadcrumbsProps {  breadcrumbs: Streamable<Breadcrumb[]>;  className?: string;}/** * This component supports various CSS variables for theming. Here's a comprehensive list, along * with their default values: * * ```css * :root { *   --breadcrumbs-font-family: var(--font-family-body); *   --breadcrumbs-primary-text: hsl(var(--foreground)); *   --breadcrumbs-secondary-text: hsl(var(--contrast-500)); *   --breadcrumbs-icon: hsl(var(--contrast-500)); *   --breadcrumbs-hover: hsl(var(--primary)); * } * ``` */export function Breadcrumbs({ breadcrumbs: streamableBreadcrumbs, className }: BreadcrumbsProps) {  return (    <Stream fallback={<BreadcrumbsSkeleton className={className} />} value={streamableBreadcrumbs}>      {(breadcrumbs) => {        if (breadcrumbs.length === 0) {          return <BreadCrumbEmptyState className={className} />;        }        return (          <nav aria-label="breadcrumb" className={clsx(className)}>            <ol className="flex flex-wrap items-center gap-x-1.5 text-sm @xl:text-base">              {breadcrumbs.map(({ label, href }, index) => {                if (index < breadcrumbs.length - 1) {                  return (                    <li className="inline-flex items-center gap-x-1.5" key={href}>                      <AnimatedLink                        className="font-[family-name:var(--breadcrumbs-font-family,var(--font-family-body))] text-[var(--breadcrumbs-primary-text,hsl(var(--foreground)))] [background:linear-gradient(0deg,var(--breadcrumbs-hover,hsl(var(--primary))),var(--breadcrumbs-hover,hsl(var(--primary))))_no-repeat_left_bottom_/_0_2px]"                        href={href}                      >                        {label}                      </AnimatedLink>                      <ChevronRight                        aria-hidden="true"                        className="text-[var(--breadcrumbs-icon,hsl(var(--contrast-500)))]"                        size={20}                        strokeWidth={1}                      />                    </li>                  );                }                return (                  <li                    className="inline-flex items-center font-[family-name:var(--breadcrumbs-font-family,var(--font-family-body))] text-[var(--breadcrumbs-secondary-text,hsl(var(--contrast-500)))]"                    key={href}                  >                    <span aria-current="page" aria-disabled="true" role="link">                      {label}                    </span>                  </li>                );              })}            </ol>          </nav>        );      }}    </Stream>  );}export function BreadcrumbsSkeleton({ className }: Pick<BreadcrumbsProps, 'className'>) {  return (    <Skeleton.Root      className={clsx('group-has-[[data-pending]]/breadcrumbs:animate-pulse', className)}      pending    >      <div className="flex flex-wrap items-center gap-x-1.5 text-sm @xl:text-base">        <Skeleton.Text characterCount={4} className="rounded text-lg" />        <Skeleton.Icon icon={<ChevronRight aria-hidden className="h-5 w-5" strokeWidth={1} />} />        <Skeleton.Text characterCount={6} className="rounded text-lg" />        <Skeleton.Icon icon={<ChevronRight aria-hidden className="h-5 w-5" strokeWidth={1} />} />        <Skeleton.Text characterCount={6} className="rounded text-lg" />      </div>    </Skeleton.Root>  );}export function BreadCrumbEmptyState({ className }: Pick<BreadcrumbsProps, 'className'>) {  return (    <Skeleton.Root className={className}>      <div className={clsx('min-h-[1lh]', className)} />    </Skeleton.Root>  );}

Usage

import { Breadcrumbs, type Breadcrumb } from '@/vibes/soul/sections/breadcrumbs';function Usage() {  const breadcrumbs: Breadcrumb[] = [    {      label: 'Home',      href: '#1',    },    {      label: 'Bags',      href: '#2',    },    {      label: 'Handle Bags',      href: '#3',    },  ];  return (      <Breadcrumbs breadcrumbs={breadcrumbs} />  );}

API Reference

PropTypeDefault
breadcrumbs*
Streamable<Breadcrumb[]>
className
string
PropTypeDefault
label
string
href
string

CSS Variables

This component supports various CSS variables for theming. Here's a comprehensive list.

:root {  --breadcrumbs-font-family: var(--font-family-body);  --breadcrumbs-primary-text: hsl(var(--foreground));  --breadcrumbs-secondary-text: hsl(var(--contrast-500));  --breadcrumbs-icon: hsl(var(--contrast-500));  --breadcrumbs-hover: hsl(var(--primary));}