إنشاء مكوِّن أشرطة التنقّل

نظرة عامة أساسية حول كيفية إنشاء مكوّن فتات خبز سريع الاستجابة ويسهل الوصول إليه ليتمكّن المستخدمون من التنقّل في موقعك الإلكتروني

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

العرض التوضيحي

إذا كنت تفضّل مشاهدة فيديو، إليك نسخة من هذا المنشور على YouTube:

نظرة عامة

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

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

تجربة المستخدم في الخلفية

في الفيديو التجريبي الخاص بالمكوّن أعلاه، فئات العناصر النائبة هي أنواع من ألعاب الفيديو. يتم إنشاء هذا المسار من خلال الانتقال إلى المسار التالي: home » rpg » indie » on sale، كما هو موضّح أدناه.

يجب أن يتيح عنصر مسار التنقل للمستخدمين التنقّل في التسلسل الهرمي للمعلومات، والانتقال بين الفروع واختيار الصفحات بسرعة ودقة.

هندسة المعلومات

أرى أنّه من المفيد التفكير من منظور المجموعات والعناصر.

المجموعات

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

العناصر

لعبة الفيديو هي سلعة، ويمكن أن تكون مجموعة معيّنة أيضًا سلعة إذا كانت تمثّل مجموعة أخرى. على سبيل المثال، لعبة تقمّص الأدوار هي عنصر ومجموعة صالحة. عندما يكون عنصرًا، يكون المستخدم على صفحة المجموعة هذه. على سبيل المثال، تظهر هذه الفئات الفرعية في صفحة ألعاب تقمّص الأدوار التي تعرض قائمة بألعاب تقمّص الأدوار، بما في ذلك الفئات الفرعية الإضافية AAA وIndie وSelf Published.

في مصطلحات علوم الكمبيوتر، يمثّل مكوّن مسارات التنقل هذا مصفوفة متعددة الأبعاد:

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

سيتضمّن تطبيقك أو موقعك الإلكتروني بنية معلومات مخصّصة (IA) تؤدي إلى إنشاء مصفوفة متعددة الأبعاد مختلفة، ولكن آمل أن يتم تضمين مفهوم الصفحات المقصودة الخاصة بالمجموعات وعملية الانتقال إلى مستويات أعلى في مسارات التنقل أيضًا.

التنسيقات

Markup

تبدأ المكوّنات الجيدة بترميز HTML مناسب. في هذا القسم التالي، سأشرح خيارات الترميز وكيفية تأثيرها في المكوّن بشكل عام.

مخطط الألوان الداكنة والفاتحة

<meta name="color-scheme" content="dark light">

تُعلم العلامة الوصفية color-scheme في المقتطف أعلاه المتصفّح بأنّ هذه الصفحة تريد استخدام أنماط المتصفّح الفاتحة والداكنة. لا تتضمّن أمثلة مسارات التنقّل أي CSS لمخططات الألوان هذه، وبالتالي ستستخدم مسارات التنقّل الألوان التلقائية التي يوفّرها المتصفّح.

<nav class="breadcrumbs" role="navigation"></nav>

من المناسب استخدام العنصر <nav> للتنقّل في الموقع الإلكتروني، والذي يتضمّن دور ARIA ضمنيًا وهو navigation. أثناء الاختبار، لاحظتُ أنّ إضافة السمة role غيّرت طريقة تفاعل قارئ الشاشة مع العنصر، إذ تم الإعلان عنه كعنصر تنقّل، لذا اخترتُ إضافته.

الرموز

عند تكرار رمز على صفحة، يعني العنصر SVG <use> أنّه يمكنك تحديد path مرة واحدة، واستخدامه لجميع مثيلات الرمز. يمنع ذلك تكرار معلومات المسار نفسها، ما يؤدي إلى زيادة حجم المستندات واحتمالية حدوث تناقض في المسار.

لاستخدام هذه الطريقة، أضِف عنصر SVG مخفيًا إلى الصفحة، ثم ضع الرموز في عنصر <symbol> مع معرّف فريد:

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

يقرأ المتصفّح رمز SVG HTML، ويضع معلومات الرمز في الذاكرة، ثم يتابع بقية الصفحة مع الإشارة إلى المعرّف لاستخدامات إضافية للرمز، على النحو التالي:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

أدوات مطوّري البرامج تعرض عنصر استخدام SVG معروضًا.

يمكنك تحديدها مرة واحدة واستخدامها عدة مرات كما تريد، مع الحدّ الأدنى من التأثير في أداء الصفحة وتوفير تصميم مرن. تتم إضافة الإشعار aria-hidden="true" إلى عنصر SVG. لا تفيد الرموز المستخدمين الذين يتصفّحون المحتوى ويسمعونه فقط، لذا يؤدي إخفاؤها إلى منع إضافة ضوضاء غير ضرورية.

هذا هو المكان الذي يختلف فيه مسار التنقل التقليدي عن مسار التنقل في هذا المكوّن. عادةً، يكون هذا <a> رابطًا فقط، ولكن أضفت تجربة مستخدم للتنقّل باستخدام عنصر اختيار مخفي. فئة .crumb مسؤولة عن تحديد موضع الرابط والرمز، بينما فئة .crumbicon مسؤولة عن ترتيب الرمز وعنصر الاختيار معًا. أطلقتُ على هذا العنصر اسم "رابط مقسّم" لأنّ وظائفه تشبه إلى حد كبير وظائف الزر المقسّم، ولكنّه مخصّص للتنقّل بين الصفحات.

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

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

لقطة شاشة يتم فيها تمرير مؤشر الماوس فوق عنصر الاختيار غير المرئي، ويظهر تلميح الأداة السياقي الخاص به.

زخارف الفواصل

<span class="crumb-separator" aria-hidden="true">→</span>

يمكنك استخدام الفواصل أو عدم استخدامها، كما يمكنك استخدام فاصل واحد فقط (راجِع المثال الثالث في الفيديو أعلاه). بعد ذلك، أمنح كل aria-hidden="true" لأنّها عناصر زخرفية ولا يحتاج قارئ الشاشة إلى الإعلان عنها.

تسهّل السمة gap، التي سنتناولها لاحقًا، عملية تحديد المسافة بين هذه العناصر.

الأنماط

بما أنّ اللون يستخدم ألوان النظام، فإنّه يتضمّن في الغالب فجوات ومكدّسات للأنماط.

اتجاه التنسيق ومسار العرض

أداة DevTools تعرض محاذاة شريط التنقّل مع ميزة التراكب المرن.

يضبط عنصر التنقّل الأساسي nav.breadcrumbs سمة مخصّصة ذات نطاق محدّد ليستخدمها العناصر الثانوية، ويحدّد تخطيطًا أفقيًا محاذيًا عموديًا. يضمن ذلك محاذاة الفواصل والأيقونات.

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

يظهر شريط تنقّل واحد محاذيًا عموديًا مع تراكبات flexbox.

يُنشئ كل .crumb أيضًا تخطيطًا أفقيًا محاذيًا عموديًا مع بعض الفجوات، ولكنّه يستهدف بشكل خاص العناصر الثانوية التي تتضمّن روابط ويحدّد النمط white-space: nowrap. هذا أمر بالغ الأهمية بالنسبة إلى مسارات شريط التنقّل المكوّنة من عدة كلمات لأنّنا لا نريد أن تتضمّن عدة أسطر. في وقت لاحق من هذه المشاركة، سنضيف أنماطًا للتعامل مع الفيضان الأفقي الذي تسبّبت فيه السمة white-space.

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

تتم إضافة aria-current="page" للمساعدة في تمييز رابط الصفحة الحالية عن بقية الروابط. لن يقتصر الأمر على توفير مؤشر واضح لمستخدمي برامج قراءة الشاشة بأنّ الرابط مخصّص للصفحة الحالية، بل عملنا أيضًا على تصميم العنصر بشكل مرئي لمساعدة المستخدمين المبصرين في الحصول على تجربة مستخدم مماثلة.

يستخدم المكوّن .crumbicon شبكة لتجميع رمز SVG مع عنصر <select> "غير مرئي تقريبًا".

أدوات مطوّري الشبكة معروضة فوق زر حيث تم تسمية الصف والعمود باسم stack.

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

يظهر العنصر <select> في آخر DOM، لذا يقع في أعلى الحزمة، وهو تفاعلي. أضِف نمط opacity: .01 ليبقى العنصر قابلاً للاستخدام، وستكون النتيجة مربّع اختيار يتناسب تمامًا مع شكل الرمز. هذه طريقة رائعة لتخصيص شكل عنصر <select> مع الحفاظ على الوظائف المضمّنة.

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

القائمة الكاملة

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

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

تؤدي أنماط الفائض إلى إعداد تجربة المستخدم التالية:

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

طلبات الاستعلام عن الوسائط

أحد التعديلات البسيطة على إطارات العرض الأصغر حجمًا هو إخفاء التصنيف "الصفحة الرئيسية"، مع إبقاء الرمز فقط:

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

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

تسهيل الاستخدام

حركة

لا يتضمّن هذا المكوّن الكثير من الحركة، ولكن من خلال تضمين الانتقال في عبارة prefers-reduced-motion، يمكننا منع الحركة غير المرغوب فيها.

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

لا حاجة إلى تغيير أي من الأنماط الأخرى، فالتأثيرات عند التمرير بالماوس والتركيز رائعة ومفهومة بدون transition، ولكن إذا كان من المقبول استخدام الحركة، سنضيف انتقالًا سلسًا إلى التفاعل.

JavaScript

أولاً، بغض النظر عن نوع جهاز التوجيه الذي تستخدمه في موقعك الإلكتروني أو تطبيقك، عندما يغيّر المستخدم مسارات التنقل، يجب تعديل عنوان URL وعرض الصفحة المناسبة للمستخدم. ثانيًا، لتوحيد تجربة المستخدم، تأكَّد من عدم حدوث عمليات تنقّل غير متوقّعة عندما يتصفّح المستخدمون <select>الخيارات فقط.

مقياسان مهمّان لتجربة المستخدم يجب التعامل معهما باستخدام JavaScript: تغيير select ومنع تنشيط حدث التغيير <select> السريع.

يجب منع الحدث المتسرّع بسبب استخدام العنصر <select>. في متصفّح Windows Edge، وربما في متصفّحات أخرى أيضًا، يتم تنشيط الحدث changed select أثناء تصفّح المستخدم للخيارات باستخدام لوحة المفاتيح. لهذا السبب أطلقتُ عليه اسم "متحمّس"، لأنّ المستخدم اختار الخيار بشكل غير مباشر فقط، مثل التمرير بالماوس أو التركيز، ولكنّه لم يؤكّد اختياره باستخدام enter أو click. يؤدي الحدث المتسرّع إلى عدم إمكانية الوصول إلى ميزة تغيير فئة المكوّن، لأنّ فتح مربّع الاختيار وتصفّح عنصر ما سيؤدي إلى تنشيط الحدث وتغيير الصفحة قبل أن يكون المستخدم مستعدًا.

حدث <select> تم تغييره بشكل أفضل

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

تتمثّل الاستراتيجية في مراقبة أحداث الضغط على مفتاح لوحة المفاتيح في كل عنصر <select> وتحديد ما إذا كان المفتاح الذي تم الضغط عليه هو مفتاح تأكيد التنقّل (Tab أو Enter) أو مفتاح التنقّل المكاني (ArrowUp أو ArrowDown). وبناءً على هذا التحديد، يمكن للمكوّن أن يقرّر الانتظار أو الانتقال عند تشغيل الحدث الخاص بالعنصر <select>.

الخاتمة

بعد أن عرفت كيف فعلتُ ذلك، كيف ستفعل أنت ذلك؟ 🙂

لنستكشف الطرق المختلفة لإنشاء المحتوى على الويب. يمكنك إنشاء عرض توضيحي، إرسال تغريدة إليّ تتضمّن رابطًا إليه، وسأضيفه إلى قسم "ريمكسات من المنتدى" أدناه.

ريمكسات من إنشاء المنتدى