مسار التنقّل (Breadcrumb)

يعرض مكوّن مسار التنقّل مكان الصفحة الحالية في موقعك على شكل سلسلة من الروابط، ويعيد القارئ إلى أي مستوى أعلى بنقرة واحدة. تركّبه من أجزاء، فيضم عدد الخطوات الذي تحتاجه الصفحة، ويتبع اتجاه القراءة فيها.

التثبيت

bash
npx @smicolon/fasla-ui@latest add breadcrumb

قبل إضافة أي مكوّن، شغّل الأمر init مرة واحدة في مشروعك. التفاصيل في صفحة التثبيت.

معاينة

الأنماط (variant)

في Figma أربعة أنماط. يرسم المكوّن BreadcrumbSeparator سهمًا ما لم تمرّر إليه أيقونة، فالأيقونة SlashIcon تعطيك الفاصل المخصص. أما نمطا الشارة فتركيبان: شارة Soft أو Outline من المكوّن Badge داخل كل خطوة.

افتراضي
فاصل مخصص
مع شارة
شارة مفرغة

الأيقونات

أضف أيقونة قبل التسمية بالخاصية icon في المكوّن BreadcrumbLink أو BreadcrumbPage. يضبط المكوّن حجمها على 14px ويترك 4px بينها وبين التسمية، فمرّر الأيقونة دون حجم خاص بها. وفي نمطي الشارة، مرّر الأيقونة إلى المكوّن Badge بخاصيته icon، وهو يضبط حجمها ومسافتها. الأيقونات زخرفية، فيتخطاها قارئ الشاشة.

مسار مختصر

إذا طال المسار، فأبقِ الخطوة الأولى وآخر خطوتين، وضع المكوّن BreadcrumbEllipsis مكان الخطوات التي بينهما. يبقى الترتيب نفسه في الاتجاهين. ولكي يصل القارئ إلى الخطوات المخفية، اجعل النقاط الثلاث العنصرَ المُشغِّل لقائمة منسدلة DropdownMenu تعرض تلك الخطوات روابطَ، كما في هذين المثالين: اضغط على النقاط الثلاث لفتح القائمة. أما النقاط الثلاث وحدها فتدل على موضع الخطوات المحذوفة فحسب.

إتاحة الوصول (accessibility)

  • المكوّن Breadcrumb منطقة تنقّل في الصفحة. سمّها بلغة الصفحة بالخاصية aria-label كما تفعل الأمثلة هنا، وإلا فاسمها Breadcrumb. والخطوات قائمة مرتبة، فيعلن قارئ الشاشة عددها.
  • يحمل المكوّن BreadcrumbPage السمة aria-current="page"، وهو نص عادي، لا رابط إلى الصفحة التي يقرؤها القارئ.
  • الفواصل والأيقونات مخفية عن قارئ الشاشة. أما النقاط الثلاث فيقرؤها «المزيد»، أو «More» حين تكون أقرب قيمة لـ lang هي الإنجليزية.
  • حين تكون النقاط الثلاث العنصرَ المُشغِّل لقائمة، فهي زر. سمّها بالخاصية aria-label كما تفعل الأمثلة هنا، ليعلن قارئ الشاشة ما تحويه القائمة. يفتح مفتاح Enter أو مفتاح المسافة أو السهم السفلي القائمة، وتنتقل مفاتيح الأسهم بين الخطوات، ويفتح مفتاح Enter الرابط، ويغلق مفتاح Escape القائمة ويعيد التركيز إلى النقاط الثلاث.
  • تُظهر الروابط، والنقاط الثلاث حين تفتح قائمة، حلقة التركيز حين تنتقل إليها بلوحة المفاتيح.
  • يتبع المسار السمة dir في الصفحة: في العربية تبدأ الخطوات من بداية السطر، وينقلب السهم معها. ويأتي ارتفاع السطر العربي الأطول من سلّم الخطوط، دون أي إعداد منك.

الخصائص (props)

Breadcrumb

الخاصيةالنوعالافتراضيالوصف
aria-labelstring"Breadcrumb"اسم منطقة التنقّل لقارئ الشاشة، بلغة الصفحة.
childrenReactNodeلا يوجدالمكوّن BreadcrumbList.
classNamestringلا يوجدأصناف إضافية للعنصر nav.

BreadcrumbList

الخاصيةالنوعالافتراضيالوصف
childrenReactNodeلا يوجدالخطوات والفواصل، بترتيب القراءة.
classNamestringلا يوجدأصناف إضافية للعنصر ol.

BreadcrumbItem

الخاصيةالنوعالافتراضيالوصف
childrenReactNodeلا يوجدخطوة واحدة: رابط أو الصفحة الحالية أو النقاط الثلاث.
classNamestringلا يوجدأصناف إضافية للعنصر li.

BreadcrumbLink

الخاصيةالنوعالافتراضيالوصف
hrefstringلا يوجدوجهة الخطوة. ويقبل الرابط كل سمات العنصر a الأخرى أيضًا.
asChildbooleanfalseيجعل العنصر الذي بداخله، مثل المكوّن Link في Next.js، هو الرابط.
iconReactNodeلا يوجدأيقونة قبل التسمية، بحجم 14px.
childrenReactNodeلا يوجدتسمية الخطوة.
classNamestringلا يوجدأصناف إضافية للرابط.

BreadcrumbPage

الخاصيةالنوعالافتراضيالوصف
iconReactNodeلا يوجدأيقونة قبل التسمية، بحجم 14px.
childrenReactNodeلا يوجداسم الصفحة الحالية.
classNamestringلا يوجدأصناف إضافية للصفحة الحالية.

BreadcrumbSeparator

الخاصيةالنوعالافتراضيالوصف
childrenReactNodeلا يوجدأيقونة تحلّ محل السهم، مثل SlashIcon.
classNamestringلا يوجدأصناف إضافية للفاصل.

BreadcrumbEllipsis

الخاصيةالنوعالافتراضيالوصف
classNamestringلا يوجدأصناف إضافية للنقاط الثلاث. نص قارئ الشاشة مضمَّن فيها.

الاستخدام

مسار أساسي

tsx
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export function DocumentBreadcrumb() {
  return (
    <Breadcrumb aria-label="مسار التنقّل">
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink href="/">الرئيسية</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink href="/projects">
            المشاريع
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink href="/docs">
            المستندات
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>إضافة مستند</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

مسار مختصر

tsx
import { HouseIcon } from "lucide-react"
import {
  Breadcrumb,
  BreadcrumbEllipsis,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"

const TRIGGER = [
  "-m-[5px] inline-flex size-6 cursor-pointer items-center",
  "justify-center rounded-[var(--radius-sm)] transition-colors",
  "hover:bg-accent data-[popup-open]:bg-accent",
  "focus-visible:outline focus-visible:outline-2",
  "focus-visible:outline-ring motion-reduce:transition-none",
].join(" ")

export function CollapsedBreadcrumb() {
  return (
    <Breadcrumb aria-label="مسار التنقّل">
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink href="/" icon={<HouseIcon />}>
            الرئيسية
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <DropdownMenu>
            <DropdownMenuTrigger
              aria-label="الخطوات المخفية"
              className={TRIGGER}
            >
              <BreadcrumbEllipsis />
            </DropdownMenuTrigger>
            <DropdownMenuContent>
              <DropdownMenuItem render={<a href="/projects" />}>
                المشاريع
              </DropdownMenuItem>
              <DropdownMenuItem render={<a href="/projects/website" />}>
                الموقع الإلكتروني
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbLink href="/docs">المستندات</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>إضافة مستند</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

فاصل مخصص

tsx
import { SlashIcon } from "lucide-react"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export function SlashBreadcrumb() {
  return (
    <Breadcrumb aria-label="مسار التنقّل">
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink href="/">الرئيسية</BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator>
          <SlashIcon />
        </BreadcrumbSeparator>
        <BreadcrumbItem>
          <BreadcrumbPage>إضافة مستند</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

مع رابط Next.js

tsx
import Link from "next/link"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

export function ProjectBreadcrumb() {
  return (
    <Breadcrumb aria-label="مسار التنقّل">
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink asChild>
            <Link href="/projects">المشاريع</Link>
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator />
        <BreadcrumbItem>
          <BreadcrumbPage>المستندات</BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}

مع الشارات

tsx
import { SlashIcon } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"

const DIMMED = [
  "rounded-full opacity-60",
  "hover:opacity-100 focus-visible:opacity-100",
].join(" ")

export function DepartmentBreadcrumb() {
  return (
    <Breadcrumb aria-label="مسار التنقّل">
      <BreadcrumbList>
        <BreadcrumbItem>
          <BreadcrumbLink href="/marketing" className={DIMMED}>
            <Badge variant="soft" size="md">
              التسويق
            </Badge>
          </BreadcrumbLink>
        </BreadcrumbItem>
        <BreadcrumbSeparator>
          <SlashIcon />
        </BreadcrumbSeparator>
        <BreadcrumbItem>
          <BreadcrumbPage>
            <Badge variant="soft" size="md">
              التسويق الإلكتروني
            </Badge>
          </BreadcrumbPage>
        </BreadcrumbItem>
      </BreadcrumbList>
    </Breadcrumb>
  )
}