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

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

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

عرض توضيحي

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

نظرة عامة

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

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

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

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

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

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

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

المجموعات

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

السلع

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

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

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 في التنقّل. أثناء إجراء الاختبارات، لاحظتُ أنّ السمة 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>

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

<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، التي سنتناولها بعد ذلك، المسافات بين هذه المسافات.

الأنماط

بما أنّ اللون يستخدم ألوان النظام، فهو في الغالب فجوات وحزم للأنماط!

اتجاه التخطيط وتدفقه

أدوات مطوّري البرامج تعرض محاذاة شريط التنقّل مع ميزة التراكب المرن الخاص بها

يضبط عنصر التنقّل الأساسي 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> "غير مرئي" تقريبًا.

تم عرض &quot;أدوات مطوّري البرامج في الشبكة&quot; متراكبة على زر حيث يحمل كل من الصف والعمود اسم حزمة.

.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>. على نظام التشغيل Windows Edge، وربما في المتصفّحات الأخرى أيضًا، يتم تنشيط حدث changed المحدد أثناء تصفّح المستخدم للخيارات باستخدام لوحة المفاتيح. وهذا هو سبب وصفه بأنّه حريص، لأنّ المستخدم لم يحدّد سوى الخيار الزائف، مثل التمرير أو التركيز، ولكنّه لم يؤكّد الخيار بعد باستخدام 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>.

الخلاصة

الآن بعد أن تعرّفت على كيفية إجراء ذلك، كيف يمكنك‽ 🙂

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

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