نظرة عامة أساسية حول كيفية إنشاء مكوّن شريط تنقّل سريع الاستجابة ويمكن الوصول إليه ليتمكّن المستخدمون من التنقّل في موقعك الإلكتروني
في هذه المشاركة، أود أن أشارككم أفكارًا حول طريقة لإنشاء مكونات شريط التنقل. جرِّب العرض التجريبي.
إذا كنت تفضّل الفيديو، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:
نظرة عامة
يعرض مكوّن شريط التنقل موضع المستخدِم في التدرّج الهرمي للموقع الإلكتروني. يُستمَد الاسم من قصة هانسل وغريتل، اللذَين ألقيا شريط التنقل خلفهما في بعض الغابات المظلمة وتمكّنا من العثور على طريقهما إلى المنزل من خلال تتبُّع فتات الخبز إلى الوراء.
أشرطة التنقل في هذه المشاركة غير عادية
أشرطة التنقّل
تشبه شريط التنقل. توفر وظائف إضافية عن طريق وضع مجموعات
الصفحات في شريط التنقّل مباشرةً باستخدام <select>
، ما يجعل الوصول متعدد المستويات
ممكن.
تجربة المستخدم في الخلفية
في الفيديو التجريبي للمكوّن أعلاه، تشمل فئات العناصر النائبة أنواع
ألعاب الفيديو. يتم إنشاء هذا المسار من خلال الانتقال إلى المسار التالي: home »
rpg » indie » on sale
، كما هو موضّح أدناه.
من المفترض أن يتيح مكوّن شريط التنقّل هذا للمستخدمين التسلسل الهرمي للمعلومات القفز بين الفروع واختيار الصفحات بسرعة ودقتها.
بنية المعلومات
أجد أنه من المفيد التفكير في المجموعات والعناصر.
المجموعات
المجموعة هي صفيف من الخيارات للاختيار من بينها. من الصفحة الرئيسية نموذج شريط التنقل في هذه المشاركة، والمجموعات هي ألعاب الحركة في الثانية (FPS) ولعبة تقمّص الأدوار (RPG) والمعارك وهي لعبة زاحف مجنون، والرياضة، والألغاز.
السلع
لعبة الفيديو هي سلعة، ويمكن أن تكون مجموعة معيّنة أيضًا سلعة إذا كانت represent another collection. على سبيل المثال، 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>
يقرأ المتصفّح ملف 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>
يمكنك تحديده مرة واحدة واستخدامه عدد المرات التي تريدها، مع الحدّ الأدنى من التأثير في أداء الصفحة
واستخدام أسلوب مرن. تمت إضافة الإشعار aria-hidden="true"
إلى عنصر SVG.
لا تكون الرموز مفيدة للمستخدمين الذين يتصفّحون المحتوى ويسمعونه فقط، وبالتالي فإنّ إخفاء
هذه الرموز عنهم يمنع حدوث أي تشويش غير ضروري.
رابط مقسَّم .crumb
وهذا هو الفرق بين شريط التنقل التقليدي وشريط التنقل في هذا المكوّن.
عادةً ما يكون هذا رابطًا <a>
فقط، ولكن أضفت تجربة مستخدم للتنقّل باستخدام عنصر select مُخفّى. تكون الفئة .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>
لا يُعدّ الرابط وبعض الخيارات أمرًا مميزًا، ولكنّهما يضيفان المزيد من الوظائف إلى مسارbreadcrumb بسيط. إنّ إضافة 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);
}
تنشئ كل .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>
.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>
.
الخاتمة
الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟ 🙂
لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب. أنشئ عرضًا توضيحيًا وأضيف روابط تغريدة إليّ. انتقِل إلى قسم الريمكسات في المنتدى أدناه.
الريمكسات التي أنشأها المستخدمون
- Tux Solbakk كمكوّن ويب: العرض التجريبي والرمز