نظرة عامة أساسية حول كيفية إنشاء مكون زر تقسيم يسهل الوصول إليه.
في هذه المشاركة، أود أن أشارككم أفكارًا حول طريقة لإنشاء زر تقسيم . جرِّب العرض التوضيحي
إليك نسخة من هذه المشاركة على YouTube إذا كنت تفضّل ذلك:
نظرة عامة
أزرار التقسيم هي أزرار التي تخفي زرًا أساسيًا وقائمة بأزرار إضافية. أنها مفيدة عن إظهار إجراء شائع مع تضمين إجراء ثانوي أقل استخدامًا الإجراءات إلى حين الحاجة. يمكن أن يكون زر التقسيم حاسمًا لمساعدة تصميم مزدحم الحد الأدنى. قد يتذكّر زر التقسيم المتقدّم آخر إجراء للمستخدم. وترقيتها في الموضع الأساسي.
يتوفّر زر تقسيم شائع في تطبيق البريد الإلكتروني. الإجراء الأساسي الإرسال، ولكن ربما يمكنك الإرسال لاحقًا أو حفظ مسودة بدلاً من ذلك:
منطقة الإجراءات المشتركة رائعة، لأن المستخدم لا يحتاج إلى إلقاء نظرة. هم أن تعرف أن إجراءات البريد الإلكتروني الأساسية مضمنة في زر التقسيم.
الأجزاء
دعونا نقسم الأجزاء الأساسية لزر التقسيم قبل مناقشة التنسيق العام وتجربة المستخدم النهائية. إمكانية الوصول إلى VisBug هنا للمساعدة في إظهار عرض مكرو للمكون، بحيث وجوانب HTML والأسلوب وإمكانية الوصول لكل جزء رئيسي.
حاوية زر مقسَّم من المستوى الأعلى
المكون الأعلى مستوى هو flexbox مضمَّن، بفئة من
gui-split-button
، يحتوي على الإجراء الأساسي
و.gui-popup-button
زر الإجراء الأساسي
يتناسب <button>
الذي يكون مرئيًا ويمكن التركيز عليه بشكل مبدئي داخل الحاوية التي تحتوي على
شكلين متطابقين من الزوايا
التركيز،
التمرير
التفاعلات النشطة من أجل
مضمنة في .gui-split-button
.
زر إيقاف النافذة المنبثقة
"زر النافذة المنبثقة" مخصص للتفعيل والإشارة إلى قائمة
الأزرار الثانوية. لاحظ أنّها ليست <button>
ولا يمكن التركيز عليها. ومع ذلك،
هو علامة ارتساء تحديد الموضع لـ .gui-popup
ومضيف :focus-within
المستخدم
لعرض النافذة المنبثقة.
البطاقة المنبثقة
هذه بطاقة عائمة في شريط الارتساء
.gui-popup-button
، موضع مطلق
بشكل دلالي لقائمة الأزرار.
الإجراء أو الإجراءات الثانوية
<button>
يمكن التركيز عليه بحجم خط أصغر قليلاً من حجم الخط الأساسي
زر إجراء على رمز وعلامة
النمط إلى الزر الأساسي.
الخصائص المخصصة
تساعد المتغيرات التالية في خلق تناغم الألوان ومكان مركزي لتعديل القيم المستخدمة في المكون.
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --light (prefers-color-scheme: light);
.gui-split-button {
--theme: hsl(220 75% 50%);
--theme-hover: hsl(220 75% 45%);
--theme-active: hsl(220 75% 40%);
--theme-text: hsl(220 75% 25%);
--theme-border: hsl(220 50% 75%);
--ontheme: hsl(220 90% 98%);
--popupbg: hsl(220 0% 100%);
--border: 1px solid var(--theme-border);
--radius: 6px;
--in-speed: 50ms;
--out-speed: 300ms;
@media (--dark) {
--theme: hsl(220 50% 60%);
--theme-hover: hsl(220 50% 65%);
--theme-active: hsl(220 75% 70%);
--theme-text: hsl(220 10% 85%);
--theme-border: hsl(220 20% 70%);
--ontheme: hsl(220 90% 5%);
--popupbg: hsl(220 10% 30%);
}
}
التخطيطات والألوان
Markup
يبدأ العنصر كـ <div>
باسم فئة مخصّصة.
<div class="gui-split-button"></div>
أضِف الزر الأساسي وعناصر .gui-popup-button
.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions"></span>
</div>
لاحِظ سمتَي aria aria-haspopup
وaria-expanded
. هذه الإشارات هي
لتعرف قارئات الشاشة على إمكانية وحالة التقسيم
المستخدم. تكون السمة title
مفيدة للجميع.
أضِف الرمز <svg>
وعنصر الحاوية .gui-popup
.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup"></ul>
</span>
</div>
بالنسبة للموضع المنبثق المباشر، .gui-popup
هو عنصر ثانوي للزر الذي
وتوسيعه. الهدف الوحيد من هذه الاستراتيجية هو .gui-split-button
.
لا يمكن للحاوية استخدام overflow: hidden
، لأنّ ذلك سيقطع النافذة المنبثقة
التواجد بصريًا.
سيُعلن عن <ul>
مليء بمحتوى <li><button>
على أنّه "زر
الْقَائِمَة إلى برامج قراءة الشاشة، وهي بالضبط الواجهة المقدمة.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li>
<button>Schedule for later</button>
</li>
<li>
<button>Delete</button>
</li>
<li>
<button>Save draft</button>
</li>
</ul>
</span>
</div>
للحصول على لمسة مميزة والاستمتاع بالألوان، أضفنا رموزًا إلى الأزرار الثانوية من https://heroicons.com تكون الرموز اختيارية لكليهما الزرين الأساسي والثانوي.
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
Schedule for later
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Delete
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
</svg>
Save draft
</button></li>
</ul>
</span>
</div>
الأنماط
مع استخدام HTML والمحتوى، تكون الأنماط جاهزة لتوفير الألوان والتنسيق.
تصميم حاوية زر التقسيم
يعمل نوع العرض inline-flex
بشكل جيد لمكوِّن الالتفاف هذا لأنّه
أن تتناسب مع الأزرار أو الإجراءات أو العناصر الأخرى
.gui-split-button {
display: inline-flex;
border-radius: var(--radius);
background: var(--theme);
color: var(--ontheme);
fill: var(--ontheme);
touch-action: manipulation;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
التصميم <button>
وتُستخدم الأزرار بشكل جيد جدًا في إخفاء مقدار التعليمات البرمجية المطلوبة. قد تحتاج إلى التراجع عن أو استبدال الأنماط التلقائية للمتصفّح، ولكن عليك أيضًا فرض بعض التوريث وإضافة حالات التفاعل والتكيّف مع مختلف تفضيلات المستخدم وأنواع الإدخالات. تتضافر أنماط الأزرار بسرعة.
تختلف هذه الأزرار عن الأزرار العادية لأنها تشترك في خلفية بالعنصر الأصلي. عادةً ما يمتلك الزر لون الخلفية والنص. ومع ذلك، يشاركونها ويطبقون خلفيتها الخاصة فقط على التفاعل.
.gui-split-button button {
cursor: pointer;
appearance: none;
background: none;
border: none;
display: inline-flex;
align-items: center;
gap: 1ch;
white-space: nowrap;
font-family: inherit;
font-size: inherit;
font-weight: 500;
padding-block: 1.25ch;
padding-inline: 2.5ch;
color: var(--ontheme);
outline-color: var(--theme);
outline-offset: -5px;
}
إضافة حالات التفاعل باستخدام بعض CSS فئات زائفة واستخدام مطابقة الخصائص المخصصة للولاية:
.gui-split-button button {
…
&:is(:hover, :focus-visible) {
background: var(--theme-hover);
color: var(--ontheme);
& > svg {
stroke: currentColor;
fill: none;
}
}
&:active {
background: var(--theme-active);
}
}
يحتاج الزر الأساسي إلى بعض الأنماط الخاصة لإكمال تأثير التصميم:
.gui-split-button > button {
border-end-start-radius: var(--radius);
border-start-start-radius: var(--radius);
& > svg {
fill: none;
stroke: var(--ontheme);
}
}
وأخيرًا، للحصول على بعض الرموز التعبيرية، يحصل الزر "مظهر فاتح" والرمز على الظل:
.gui-split-button {
@media (--light) {
& > button,
& button:is(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--theme-active);
}
& > .gui-popup-button > svg,
& button:is(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--theme-active));
}
}
}
اهتم زر رائع بالتفاعلات الدقيقة والتفاصيل الدقيقة.
ملاحظة حول :focus-visible
لاحِظ كيف تستخدم أنماط الأزرار :focus-visible
بدلاً من :focus
. :focus
لمسة مهمة لإنشاء واجهة مستخدم يسهل الوصول إليها ولكنها لا غنى عنها
السقوط: لا يتعلق الأمر بذكاء بشأن ما إذا كان المستخدم بحاجة إلى رؤيته أم لا
لا، فسينطبق على أي تركيز.
يحاول الفيديو أدناه تحليل هذا التفاعل الدقيق لإظهار كيف
:focus-visible
هو بديل ذكي.
تصميم زر النافذة المنبثقة
شريط مرن 4ch
لتوسيط رمز ووضع قائمة بأزرار منبثقة أعجبني
الزر الأساسي، فهو يظل شفافًا حتى يتم تمريره أو التفاعل معه بطريقة أخرى
وتمديده لملئه.
.gui-popup-button {
inline-size: 4ch;
cursor: pointer;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
border-inline-start: var(--border);
border-start-end-radius: var(--radius);
border-end-end-radius: var(--radius);
}
وضع طبقة التمرير والتركيز والحالات النشطة باستخدام CSS
يُعد التداخل
أداة الاختيار الوظيفية في :is()
:
.gui-popup-button {
…
&:is(:hover,:focus-within) {
background: var(--theme-hover);
}
/* fixes iOS trying to be helpful */
&:focus {
outline: none;
}
&:active {
background: var(--theme-active);
}
}
تشكّل هذه الأنماط عنصر الجذب الأساسي لعرض النافذة المنبثقة وإخفائها. عندما
.gui-popup-button
لديه focus
على أي من عناصره الثانوية، تم ضبط opacity
، الموضع
وpointer-events
، على الرمز والنافذة المنبثقة.
.gui-popup-button {
…
&:focus-within {
& > svg {
transition-duration: var(--in-speed);
transform: rotateZ(.5turn);
}
& > .gui-popup {
transition-duration: var(--in-speed);
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
}
}
عند اكتمال نمطي الدخول والخروج، تكون القطعة الأخيرة هي مشروطة تحويلات الانتقال اعتمادًا على تفضيل المستخدم للحركة:
.gui-popup-button {
…
@media (--motionOK) {
& > svg {
transition: transform var(--out-speed) ease;
}
& > .gui-popup {
transform: translateY(5px);
transition:
opacity var(--out-speed) ease,
transform var(--out-speed) ease;
}
}
}
ستلاحظ مراقبة شدة باستخدام الرمز أنّ معدل الشفافية لا يزال منقولاً للمستخدمين. الذين يفضلون الحركة المنخفضة.
تصميم النافذة المنبثقة
العنصر .gui-popup
هو قائمة أزرار بطاقة عائمة تستخدم سمات مخصّصة
أصغر قليلاً، ومطابقتها بشكل تفاعلي مع الوحدات الأساسية
وعلى العلامة التجارية باستخدامها للألوان. لاحظ أن الأيقونات أقل تباينًا،
أقل سمكًا، والظلل له تلميح من العلامة التجارية باللون الأزرق. كما هو الحال مع الأزرار،
واجهة المستخدم وتجربة المستخدم القوية نتيجة لتكديس هذه التفاصيل الصغيرة.
.gui-popup {
--shadow: 220 70% 15%;
--shadow-strength: 1%;
opacity: 0;
pointer-events: none;
position: absolute;
bottom: 80%;
left: -1.5ch;
list-style-type: none;
background: var(--popupbg);
color: var(--theme-text);
padding-inline: 0;
padding-block: .5ch;
border-radius: var(--radius);
overflow: hidden;
display: flex;
flex-direction: column;
font-size: .9em;
transition: opacity var(--out-speed) ease;
box-shadow:
0 -2px 5px 0 hsl(var(--shadow) / calc(var(--shadow-strength) + 5%)),
0 1px 1px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 10%)),
0 2px 2px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 12%)),
0 5px 5px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 13%)),
0 9px 9px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 14%)),
0 16px 16px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 20%))
;
}
يتم إعطاء الرموز والأزرار ألوان علامة تجارية لتتلاءم بشكل جيد داخل كل ظلام وبطاقة ذات مظهر فاتح:
.gui-popup {
…
& svg {
fill: var(--popupbg);
stroke: var(--theme);
@media (prefers-color-scheme: dark) {
stroke: var(--theme-border);
}
}
& button {
color: var(--theme-text);
width: 100%;
}
}
تحتوي النافذة المنبثقة للمظهر الداكن على إضافات ظلال نصوص ورموز، بالإضافة إلى إظهار ظل المربع بشدة:
.gui-popup {
…
@media (--dark) {
--shadow-strength: 5%;
--shadow: 220 3% 2%;
& button:not(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--ontheme);
}
& button:not(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--ontheme));
}
}
}
أنماط رموز <svg>
العامة
يبلغ حجم جميع الرموز نسبيًا للزر font-size
الذي يتم استخدامها من خلاله
باستخدام وحدة ch
inline-size
كما يتم تخصيص بعض الأنماط لكل منها للمساعدة في تحديد الرموز الناعمة
بسلاسة.
.gui-split-button svg {
inline-size: 2ch;
box-sizing: content-box;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
التخطيط من اليمين إلى اليسار
السمات المنطقية تؤدي جميع الأعمال المعقّدة.
في ما يلي قائمة الخصائص المنطقية المستخدمة:
- ينشئ display: inline-flex
عنصرًا مرنًا مضمَّنًا.
- padding-block
وpadding-inline
كزوج، بدلاً من padding
للحصول على فوائد المساحة المتروكة حول الجوانب المنطقية.
- border-end-start-radius
و
سيقوم الأصدقاء
الزوايا المستديرة بناءً على اتجاه المستند.
- يضمن استخدام السمة inline-size
بدلاً من width
عدم ربط المقاس بالأبعاد المادية.
- يضيف border-inline-start
حدًا إلى البداية، والذي قد يكون على اليمين أو اليسار بناءً على اتجاه النص البرمجي.
JavaScript
تهدف معظم رموز JavaScript التالية إلى تحسين إمكانية الوصول. اثنان من استخدام المكتبات المساعدة لتسهيل المهام قليلاً. يتم استخدام BlingBlingJS للعبارات الموجزة. طلبات بحث DOM وإعداد أدوات معالجة الأحداث بسهولة، مع تساعد roving-ux في تسهيل تفاعلات لوحة المفاتيح ولوحة الألعاب للنافذة المنبثقة.
import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'
const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')
مع المكتبات المذكورة أعلاه التي تم استيرادها والعناصر المحددة والمحفوظة في المتغيرات، فإن ترقية التجربة على بعد بضع دوال لا تكتمل.
المؤشر المتنقل
عندما تركِّز لوحة مفاتيح أو قارئ شاشة على .gui-popup-button
، نريد
إعادة توجيه التركيز إلى الزر الأول (أو الأحدث) في
.gui-popup
تساعدنا المكتبة في تنفيذ ذلك باستخدام element
وtarget
.
المعلَمات.
popupButtons.forEach(element =>
rovingIndex({
element,
target: 'button',
}))
يوجِّه العنصر الآن التركيز إلى العناصر الثانوية المستهدَفة في <button>
ويفعِّل
التنقل بمفتاح السهم القياسي لتصفح الخيارات.
جارٍ تبديل aria-expanded
بينما من الواضح بصريًا أن النافذة المنبثقة تظهر وتخفي، يحتاج قارئ الشاشة إلى أكثر من الإشارات المرئية. يتم استخدام JavaScript هنا ليتكامل مع تفاعل :focus-within
المستند إلى CSS من خلال تبديل سمة مناسبة لقارئ الشاشة.
popupButtons.on('focusin', e => {
e.currentTarget.setAttribute('aria-expanded', true)
})
popupButtons.on('focusout', e => {
e.currentTarget.setAttribute('aria-expanded', false)
})
جارٍ تفعيل المفتاح Escape
تم إرسال تركيز المستخدم عمدًا إلى فخ، مما يعني أننا بحاجة إلى
توفر طريقة للمغادرة. الطريقة الأكثر شيوعًا هي السماح باستخدام المفتاح Escape
.
للقيام بذلك، راقب ضغطات المفاتيح على الزر المنبثق، نظرًا لأن أي أحداث لوحة المفاتيح على
الأطفال فقّاعة على هذا الوالد.
popupButtons.on('keyup', e => {
if (e.code === 'Escape')
e.target.blur()
})
إذا لاحظ الزر المنبثق أي ضغط على مفتاح Escape
، سيؤدي ذلك إلى إزالة التركيز من نفسه.
مع
blur()
عدد النقرات على زر التقسيم
وأخيرًا، إذا نقر المستخدم على الأزرار أو ينقر عليها أو تفاعل معها لوحة المفاتيح،
تطبيق يحتاج إلى تنفيذ الإجراء المناسب. يتم استخدام فقاعات الأحداث
مرة أخرى هنا، ولكن هذه المرة على حاوية .gui-split-button
، لالتقاط الزر
النقرات من نافذة منبثقة فرعية أو من الإجراء الأساسي
splitButtons.on('click', event => {
if (event.target.nodeName !== 'BUTTON') return
console.info(event.target.innerText)
})
الخاتمة
الآن بعد أن تعرّفت على كيفية إجراء ذلك، كيف يمكنك‽ 🙂
يمكننا تنويع أساليبنا وتعلُّم جميع طرق إنشاء المحتوى على الويب. أنشئ عرضًا توضيحيًا وأضيف روابط تغريدة إليّ. انتقِل إلى قسم الريمكسات في المنتدى أدناه.
ريمكسات من إنشاء المنتدى
- Codepen من تأليف Joost van der Schee