إنشاء مكوِّن شريط التحميل

نظرة عامة أساسية حول طريقة تصميم شريط تحميل قابل للتكيّف مع الألوان باستخدام العنصر <progress>

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

تم عرض محتوى فاتح وداكن وغير محدد ومتزايد ومكتمل على Chrome.

في ما يلي إصدار YouTube من هذه المشاركة إذا كنت تفضّل الفيديوهات:

نظرة عامة

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

تعاون تحدي واجهة المستخدم الرسومية (GUI) مع عنصر HTML الحالي <progress> لتوفير بعض الجهد في تسهيل الاستخدام. تدفع الألوان والتخطيطات حدود التخصيص للعنصر المضمن، لتحديث المكون وجعله مناسبًا بشكل أفضل داخل أنظمة التصميم.

علامات التبويب الفاتحة والداكنة في كل متصفّح تقدّم نظرة عامة على رمز التعديل التكيُّفي من أعلى إلى أسفل: 
 Safari وFirefox وChrome.
يتم عرض العرض التوضيحي على متصفِّحات Firefox وSafari وiOS Safari وChrome وAndroid Chrome في المخطّطات الفاتحة والداكنة.

Markup

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

<progress></progress>

وإذا لم يكن هناك value، سيكون مستوى تقدُّم العنصر غير محدَّد. يتم ضبط القيمة التلقائية للسمة max على 1، وبالتالي يتراوح مستوى التقدّم بين 0 و1. على سبيل المثال، ضبط max على 100، سيؤدي إلى ضبط النطاق على 0-100. لقد اخترت البقاء ضمن حدي 0 و1، وترجمة قيم التقدم إلى 0.5 أو 50٪.

مستوى التقدُّم المحوَّل إلى تصنيف

في العلاقة الضمنية، يتم ربط عنصر التقدم بتصنيف مثل التالي:

<label>Loading progress<progress></progress></label>

في العرض التوضيحي، اخترت تضمين التصنيف لقارئات الشاشة فقط. ويتم ذلك من خلال إحاطة نص التصنيف في <span> وتطبيق بعض الأنماط عليه كي يظهر خارج الشاشة بشكل فعّال:

<label>
  <span class="sr-only">Loading progress</span>
  <progress></progress>
</label>

مع خدمة CSS المرفقة التالية من WebAIM:

.sr-only {
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

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

المنطقة المتأثرة بتقدم التحميل

إذا كانت لديك رؤية سليمة، فقد يكون من السهل ربط مؤشر التقدم بالعناصر ومناطق الصفحة ذات الصلة، ولكن بالنسبة إلى المستخدمين ذوي العجز البصري، لن يكون الأمر واضحًا. يمكنك تحسين هذه الميزة من خلال تخصيص السمة aria-busy للعنصر الأعلى الذي سيتغيّر عند اكتمال التحميل. إضافةً إلى ذلك، عليك الإشارة إلى العلاقة بين مستوى التقدّم ومنطقة التحميل باستخدام aria-describedby.

<main id="loading-zone" aria-busy="true">
  …
  <progress aria-describedby="loading-zone"></progress>
</main>

من JavaScript، يمكنك التبديل بين aria-busy وtrue في بداية المهمة وإلى false بعد الانتهاء.

إضافات سمات ARia

الدور الضمني للعنصر <progress> هو progressbar، ولكنني أوضحتُ ذلك للمتصفّحات التي تفتقر إلى هذا الدور الضمني. لقد أضفتُ أيضًا السمة indeterminate لوضع العنصر في حالة غير معروفة بشكل صريح، وهذا أوضح من أنّ ملاحظة العنصر لا يتضمن مجموعة value.

<label>
  Loading 
  <progress 
    indeterminate 
    role="progressbar" 
    aria-describedby="loading-zone"
    tabindex="-1"
  >unknown</progress>
</label>

استخدِم tabindex="-1" لجعل عنصر التقدّم قابلاً للتركيز من JavaScript. هذا مهم لتقنية قارئ الشاشة، نظرًا لأن التركيز على التقدم مع تغييرات التقدم، سيعلِن للمستخدم عن مدى وصل التقدم المحدث.

الأنماط

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

التنسيق

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

تصميم <progress>

يتم ترك عرض عنصر التقدم بدون تغيير بحيث يمكن أن يتقلص وينمو مع المساحة المطلوبة في التصميم. تتم إزالة الأنماط المضمَّنة من خلال ضبط appearance وborder على none. ويتم ذلك حتى يمكن تسوية العنصر عبر المتصفحات، لأن كل متصفح له أنماطه الخاصة لعنصره.

progress {
  --_track-size: min(10px, 1ex);
  --_radius: 1e3px;

  /*  reset  */
  appearance: none;
  border: none;

  position: relative;
  height: var(--_track-size);
  border-radius: var(--_radius);
  overflow: hidden;
}

تستخدم قيمة 1e3px لـ _radius تدوين الرقم العلمي للتعبير عن عدد كبير، بحيث يتم تقريب border-radius دائمًا. تعادل هذه السمة 1000px. أحب استخدام هذه الطريقة لأن هدفي هو استخدام قيمة كبيرة بما يكفي ليتمكن من ضبطها ونسيانها (وأقصر في الكتابة من 1000px). ومن السهل أيضًا جعلها أكبر إذا لزم الأمر: ما عليك سوى تغيير 3 إلى 4، ثم 1e4px يعادل 10000px.

يتم استخدام السمة overflow: hidden وكان أسلوبها مثيرًا للجدل. لقد سهّل ذلك بعض الأمور، مثل عدم الحاجة إلى تمرير قيم border-radius إلى المسار، وتتبع عناصر التعبئة، ولكن كان ذلك يعني أيضًا عدم الحاجة إلى استخدام عناصر التقدّم خارج العنصر. يمكن إجراء تكرار آخر لعنصر التقدم المخصّص هذا بدون overflow: hidden وقد يوفّر بعض الفرص والرسوم المتحركة أو حالات إكمال أفضل.

اكتمل مستوى التقدم

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

progress:not([max])[value="1"]::before,
progress[max="100"][value="100"]::before {
  content: "✓";
  
  position: absolute;
  inset-block: 0;
  inset-inline: auto 0;
  display: flex;
  align-items: center;
  padding-inline-end: max(calc(var(--_track-size) / 4), 3px);

  color: white;
  font-size: calc(var(--_track-size) / 1.25);
}

لقطة شاشة لشريط التحميل عند نسبة 100% وتظهر علامة اختيار في نهايته

اللون

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

أنماط المتصفّح الفاتح والداكن

لتفعيل عناصر <progress> التكيُّفية الداكنة والفاتحة على موقعك الإلكتروني، ما عليك سوى استخدام color-scheme.

progress {
  color-scheme: light dark;
}

لون معبأ لمستوى التقدّم في موقع واحد

لتلوين عنصر <progress>، استخدِم accent-color.

progress {
  accent-color: rebeccapurple;
}

لاحِظ أنّ لون خلفية المسار يتغيّر من الفاتح إلى الداكن استنادًا إلى accent-color. يضمن المتصفّح ظهور تباين مناسب: أنيق جدًا.

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

اضبط سمتَين مخصّصتَين للعنصر <progress>، واحدة للون المسار والأخرى للون تقدّم المسار. في الاستعلام عن الوسائط prefers-color-scheme، وفِّر قيم ألوان جديدة للمسار وتتبع التقدم.

progress {
  --_track: hsl(228 100% 90%);
  --_progress: hsl(228 100% 50%);
}

@media (prefers-color-scheme: dark) {
  progress {
    --_track: hsl(228 20% 30%);
    --_progress: hsl(228 100% 75%);
  }
}

أنماط التركيز

في وقت سابق أعطينا العنصر فهرس علامة تبويب سالبًا بحيث يمكن التركيز عليه آليًا. يمكنك استخدام :focus-visible لتخصيص التركيز وتفعيل نمط حلقة التركيز الأكثر ذكاءً. باستخدام ذلك، لن يظهر النقر بالماوس والتركيز على حلقة التركيز، لكن نقرات لوحة المفاتيح ستظهر. تشرح هذه المقالة بالتفصيل فيديو YouTube وهو يستحق المراجعة.

progress:focus-visible {
  outline-color: var(--_progress);
  outline-offset: 5px;
}

لقطة شاشة لشريط التحميل يحيط به حلقة تركيز تتطابق كل الألوان.

الأنماط المخصصة على مستوى المتصفحات

يمكنك تخصيص الأنماط من خلال اختيار أجزاء العنصر <progress> التي يعرضها كل متصفّح. يكون استخدام عنصر التقدم علامة واحدة، ولكنه يتكوّن من عدد قليل من العناصر الثانوية التي تظهر من خلال أدوات اختيار CSS الزائفة. ستعرض "أدوات مطوري البرامج في Chrome" هذه العناصر لك إذا فعّلت الإعداد:

  1. انقر بزر الماوس الأيمن على صفحتك واختَر فحص العنصر لإظهار "أدوات مطوري البرامج".
  2. النقر على رمز ترس "الإعدادات" في أعلى يسار نافذة "أدوات مطوري البرامج".
  3. ضمن العنوان العناصر، ابحث عن مربّع الاختيار عرض ظل وكيل المستخدم DOM وفعِّله.

لقطة شاشة للمكان الذي تم فيه تفعيل عرض shadow DOM لوكيل المستخدم في &quot;أدوات مطوري البرامج&quot;

أنماط Safari وChromium

والمتصفّحات المستندة إلى WebKit، مثل Safari وChromium، تعرض ::-webkit-progress-bar و::-webkit-progress-value واللذين يسمحان باستخدام مجموعة فرعية من CSS. في الوقت الحالي، اضبط السمة background-color باستخدام السمات المخصّصة التي تم إنشاؤها سابقًا، والتي تتكيّف مع اللونَين الفاتح والداكن.

/*  Safari/Chromium  */
progress[value]::-webkit-progress-bar {
  background-color: var(--_track);
}

progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
}

لقطة شاشة تعرض العناصر الداخلية لعنصر التقدّم

أنماط Firefox

لا يعرض متصفّح Firefox سوى أداة الاختيار الزائفة ::-moz-progress-bar على العنصر <progress>. هذا يعني أيضًا أنه لا يمكننا تلوين المسار بشكل مباشر.

/*  Firefox  */
progress[value]::-moz-progress-bar {
  background-color: var(--_progress);
}

لقطة شاشة لأداة Firefox ومكان العثور على أجزاء عنصر التقدّم

لقطة شاشة لقسم تصحيح الأخطاء حيث يظهر شريط التحميل مفعلاً في كل من Safari وiOS Safari وFirefox وChrome وChrome على Android.

يُرجى العِلم أنّ لون المسار في Firefox تم ضبط لونه من accent-color، فيما يتضمّن iOS Safari مسارًا باللون الأزرق الفاتح. وهو الأمر نفسه في الوضع الداكن، فمتصفّح Firefox يحتوي على مسار داكن اللون ولكنه ليس باللون المخصّص الذي حدّدناه، ويعمل في المتصفّحات المستندة إلى Webkit.

Animation

أثناء العمل باستخدام أدوات الاختيار الزائفة المضمّنة في المتصفح، غالبًا ما تتم إضافة مجموعة محدودة من سمات CSS المسموح بها.

إضافة تأثيرات حركية إلى الأغنية

تعمل إضافة انتقال إلى inline-size لعنصر التقدم مع Chromium وليس مع Safari. ولا يستخدم Firefox أيضًا خاصية انتقال على ::-moz-progress-bar.

/*  Chromium Only 😢  */
progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
  transition: inline-size .25s ease-out;
}

تحريك حالة :indeterminate

وأحصل هنا على مزيد من الإبداع حتى أتمكن من تقديم رسوم متحركة. يتم إنشاء عنصر زائف لـ Chromium وتطبيق تدرج يتم تحريكه ذهابًا وإيابًا لجميع المتصفحات الثلاثة.

الخصائص المخصّصة

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

progress {
  --_indeterminate-track: linear-gradient(to right,
    var(--_track) 45%,
    var(--_progress) 0%,
    var(--_progress) 55%,
    var(--_track) 0%
  );
  --_indeterminate-track-size: 225% 100%;
  --_indeterminate-track-animation: progress-loading 2s infinite ease;
}

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

الإطارات الرئيسية

الهدف هو رسوم متحركة لامتناهية تتنقل ذهابًا وإيابًا. سيتم تعيين الإطارات الرئيسية للبداية والنهاية في CSS. نحتاج إلى إطار رئيسي واحد فقط، وهو الإطار الرئيسي الأوسط في 50%، لإنشاء صورة متحركة تعود إلى نقطة البداية مرارًا وتكرارًا.

@keyframes progress-loading {
  50% {
    background-position: left; 
  }
}

استهداف كل متصفح

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

عنصر زائف في Chromium

يسمح Chromium بالعنصر الزائف: ::after الذي يتم استخدامه مع موضع لتغطية العنصر. ويتم استخدام الخصائص المخصصة غير المحددة، وتعمل الرسوم المتحركة ذهابًا وإيابًا بشكل جيد للغاية.

progress:indeterminate::after {
  content: "";
  inset: 0;
  position: absolute;
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
شريط التقدّم في Safari

بالنسبة إلى Safari، يتم تطبيق الخصائص المخصّصة والحركة على شريط تقدّم العنصر الزائف:

progress:indeterminate::-webkit-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
شريط التقدم في Firefox

بالنسبة إلى Firefox، يتم أيضًا تطبيق الخصائص المخصّصة والرسوم المتحركة على شريط تقدم العناصر الزائفة:

progress:indeterminate::-moz-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}

JavaScript

تؤدي لغة JavaScript دورًا مهمًا مع عنصر <progress>. وتتحكّم هذه الآلية في القيمة المرسَلة إلى العنصر وتضمن توفّر معلومات كافية في المستند لبرامج قراءة الشاشة.

const state = {
  val: null
}

يوفر الإصدار التجريبي أزرارًا للتحكم في مستوى التقدّم، حيث يتم تحديث state.val ثم استدعاء دالة لتحديث نموذج كائن المستند (DOM).

document.querySelector('#complete').addEventListener('click', e => {
  state.val = 1
  setProgress()
})

setProgress()

هذه الوظيفة هي المكان الذي يحدث فيه تنسيق واجهة المستخدم/تجربة المستخدم. يمكنك البدء بإنشاء دالة setProgress(). ما مِن معلَمات مطلوبة لأنّ هذه التطبيقات يمكنها الوصول إلى عنصر state وعنصر مستوى التقدّم ومنطقة <main>.

const setProgress = () => {
  
}

ضبط حالة التحميل في منطقة <main>

اعتمادًا على ما إذا كان مستوى التقدّم كاملاً أم لا، يجب تعديل عنصر <main> ذو الصلة للسمة aria-busy:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)
}

محو السمات إذا كان مقدار التحميل غير معروف

إذا كانت القيمة غير معروفة أو لم يتم ضبط السياسة null في هذا الاستخدام، عليك إزالة السمتَين value وaria-valuenow. سيؤدي ذلك إلى جعل <progress> غير محدَّد.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }
}

حلّ المسائل الحسابية للأعداد العشرية في JavaScript

نظرًا لأنني اخترت التمسك بالحد الأقصى الافتراضي للتقدم الذي يبلغ 1، فإن دوال زيادة العرض التوضيحي والتقليل تستخدم الرياضيات العشرية. وJavaScript واللغات الأخرى ليست رائعة دائمًا. إليك دالة roundDecimals() التي ستقصّ الفائض من النتيجة الحسابية:

const roundDecimals = (val, places) =>
  +(Math.round(val + "e+" + places)  + "e-" + places)

تقريب القيمة بحيث يمكن تقديمها وقراءتها:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"
}

ضبط قيمة لبرامج قراءة الشاشة وحالة المتصفّح

تُستخدم القيمة في ثلاثة أماكن في DOM:

  1. السمة value للعنصر <progress>
  2. السمة aria-valuenow.
  3. تمثّل هذه السمة المحتوى النصي الداخلي الخاص بسمة <progress>.
const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent
}

إعطاء التركيز على التقدم

مع تعديل القيم، سيلاحظ المستخدمون المبصرون التغيّر في مستوى التقدّم، لكن لم يتم إرسال إعلان التغيير إلى مستخدمي قارئ الشاشة. عليك التركيز على العنصر <progress> وسيعلن المتصفّح عن التحديث.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent

  progress.focus()
}

لقطة شاشة لتطبيق التعليق الصوتي في نظام التشغيل Mac 
  تقرأ مستوى تقدّم المستخدم في شريط التحميل

الخلاصة

الآن بعد أن عرفت كيف فعلت ذلك، كيف يمكنك‽ 🙂

وهناك بالتأكيد بعض التغييرات التي أود إجراؤها إذا أُتيحت فرصة أخرى. أعتقد أنّ هناك مجالاً لحذف المكوِّن الحالي، ويمكن محاولة إنشاء مكوِّن بدون قيود نمط الفئة الزائفة في العنصر <progress>. الأمر يستحق الاستكشاف!

دعونا ننويع أساليبنا ونتعلم جميع طرق الإنشاء على الويب.

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

ريمكسات من المنتدى