ساختن نوار پیشرفت سبک‌دار

عنصر <progress> روشی معنایی برای نمایش پیشرفت تکمیل یک تکلیف است، مانند بارگیری یا بارگذاری اطلاعات یا تکمیل بخشی از یک فرایند چندمرحله‌ای. استفاده از عنصر بومی به دسترس‌پذیری کمک می‌کند، زیرا صفحه‌خوان‌ها دارای role="progressbar" ضمنی هستند و می‌توانند مقادیر را اعلام کنند.

این راهنما یک نوار پیشرفت افقی کاملاً سبک‌دار با سفارشی‌سازی‌های سبک را پیاده‌سازی می‌کند.

نوار پیشرفت یا حلقه پیشرفت؟

  • اگر فضا محدود است یا نسبت ابعادی مربعی ترجیح داده می‌شود، از نوار پیشرفت دایره‌ای (حلقه پیشرفت) استفاده کنید: حلقه پیشرفت
  • برای اکثر سناریوهای پیشرفت نامشخص، گردونه‌ها را ترجیح دهید (گردونه بار کردن). از نوار پیشرفت نامعین زمانی استفاده کنید که وضعیت نامعین موقتی باشد و به‌زودی با نوار پیشرفت معین جایگزین شود (مثلاً انتظار برای فراداده مربوط به بارگذاری قبل‌از اینکه درصد پیشرفت نشان داده شود)

نشانه‌گذاری

  1. از عنصر <progress> بومی استفاده کنید: این عنصر دسترس‌پذیری داخلی و رفتار سازگار با پلاتفرم را ارائه می‌دهد.
  2. اگر مقیاسی متفاوت از مقیاس پیش‌فرض ۰-۱ نیاز است، از max استفاده کنید.
  3. برای مقدار پیشرفت خاص، مشخصه value را تنظیم کنید. برای حالت نامشخص (مثلاً قبل‌از شروع بازخورد ملموس‌تر)، value را حذف کنید.
  4. از <label for="...">، aria-labelledby، یا aria-label برای مرتبط کردن عنصر پیشرفت با برچسب استفاده کنید.

چه نوع سفارشی‌سازی‌ای لازم است؟

برای سبک‌بندی محدود، این ویژگی‌ها کافی است:

  • ‫accent-color برای تغییر رنگ نوار پیشرفت
  • width / inline-size برای تغییر عرض نوار پیشرفت
  • ‫height/block-size به‌طور ناهماهنگ اعمال می‌شود — ابتدا آزمایش کنید
  • writing-mode: vertical-lr برای میله‌های عمودی.
  • جلوه‌هایی که پس‌از رنگ‌آمیزی اعمال می‌شوند: opacity، filter، clip-path، ماسک‌ها، تبدیل‌ها، box-shadow، و غیره.

اگر سبک‌بندی محدود کافی است، از بخش بعدی رد شوید.

هشدار: برخی‌از ویژگی‌ها (مثلاً border-radius، background، border) سبک پیش‌فرض نوار پیشرفت را بدون اطلاع غیرفعال می‌کنند. اگر به سبکی فراتر از ویژگی‌های بالا نیاز دارید، باید سبک کاملاً سفارشی را همان‌طور که در بخش بعدی توضیح داده شده است پیاده‌سازی کنید. سبک‌بندی گسترده‌تری را «فقط برای اینکه شاید کار کند» اعمال نکنید.

سبک‌پردازی کاملاً سفارشی

۱. راه‌اندازی پایه

  • MANDATORY: Use appearance: none to enable custom styling.
  • بازنشانی مرزهای UA با border: 0 solid
  • برای برش دادن نوار فعال به شعاع حاشیه عنصر، از overflow: clip استفاده کنید یا همان border-radius را روی نوار اعمال کنید. از border-radius: inherit در نوار استفاده نکنید، زیرا به ساختار خاص عناصر در DOM سایه بستگی دارد.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

۲. سبک‌پردازی

  • سبک قطعه را در خود عنصر progress تنظیم کنید.
  • از شبه‌عنصر مناسب هدف‌گذاری‌شده در مرورگر برای هدف‌گذاری خود نوار پیشرفت استفاده کنید: ::-webkit-progress-value برای WebKit/Blink و ::-moz-progress-bar برای Firefox.
  • MANDATORY: Do NOT group -webkit- and -moz- pseudo-elements in the same rule. قانون نامعتبر تلقی خواهد شد و نادیده گرفته می‌شود. ‫:is() و :where() در اینجا کمکی نمی‌کنند، زیرا از شبه‌عناصر پشتیبانی نمی‌کنند.
  • هر شبه‌عنصر هدف مرورگر باید دقیقاً یک‌بار نشان داده شود. سفارشی‌سازی‌های مختص وضعیت (مثل اندازه بخش نامشخص و پویانمایی) باید ازطریق ویژگی‌های سفارشی تنظیم‌شده در خود عنصر progress هدایت شوند.
  • اختیاری: inline-size را روی پر کردن انتقال دهید تا تغییرات مقدار تعیین‌شده به‌جای پریدن، پویانمایی شود. Firefox اندازه پر کردن را در چیدمان تعیین می‌کند و آن را نادیده می‌گیرد که بی‌ضرر است.
progress {
  --progress-bar-radius: 1e5px;
  border-radius: var(--progress-bar-radius);
  /* Apply color for custom progress track (background) */
  background: var(--progress-bar-track, var(--color-neutral-90));
  /* Subtle depth on the empty part of the track */
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
}

/* Let <progress> styling show through */
progress::-webkit-progress-bar {
  background: transparent;
}

/* Webkit, Chrome */
progress::-webkit-progress-value {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

/* Firefox */
progress::-moz-progress-bar {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

۳. سبک‌پردازی وضعیت نامشخص

افزودن پویانمایی سفارشی برای حالت نامشخص: شبه‌عنصر پر کردن را به یک بخش کوتاه تبدیل کنید و آن را درطول قطعه بلغزانید.

  • خودِ پر کردن را سبک‌بندی کنید؛ آن را به‌عنوان پس‌زمینه در progress بازآفرینی نکنید.
  • پر کردن نامشخص فقط ازطریق پویانمایی آن وجود دارد: فریم‌های کلیدی طول آن را با inline-size واقعی تنظیم می‌کنند (این مقدار از inline-size درون‌خطی UA در Chromium بهتر است) و آن را با translate جابه‌جا می‌کنند. از scale برای طول استفاده نکنید؛ این کار هر الگویی را که روی رنگ‌آمیزی کشیده شده است مخدوش می‌کند.
  • درصدهای translate نسبت به عرض خود پرکننده حل می‌شوند، بنابراین موقعیت‌ها را در واحدهای قطعه بیان کنید: progress را به محفظه اندازه درون‌خطی تبدیل کنید و از cqi در فریم‌های کلیدی استفاده کنید. margin-right: auto را به from, to اضافه کنید تا Chromium در زبان‌های راست‌به‌چپ نیز پر کردن را در سمت چپ فیزیکی قرار دهد.
  • برای جابه‌جا کردن پر کردن، از تبدیل‌ها استفاده کنید، نه حاشیه‌ها.
    • تبدیل‌ها پویانمایی را در ترکیب‌کننده نگه می‌دارند (مگر اینکه inline-size نیز پویانمایی شود)
    • حاشیه‌ها در زبان‌های راست به چپ به دو دلیل جداگانه به‌هم می‌ریزند:
    • Firefox موقعیت پر کردن نامشخص را فقط بااستفاده از حاشیه فیزیکی سمت چپ آن تعیین می‌کند، بنابراین margin-inline-start در RTL تأثیری ندارد و پر کردن هرگز حرکت نمی‌کند.
    • در Chromium و WebKit، که پر کردن را به‌عنوان یک بلوک عادی قرار می‌دهند، وقتی حاشیه پر کردن را از انتهای قطعه فراتر می‌برد، حاشیه‌ها بیشتر از قطعه می‌شوند. CSS استاندارد این مشکل را با نادیده گرفتن margin-left در حالت راست‌به‌چپ حل می‌کند، بنابراین بخش به‌جای لغزیدن از لبه، به لبه می‌پرد.
    • ‫translate پس‌از چیدمان اعمال می‌شود، بنابراین هیچ‌کدام از مشکلات اعمال نمی‌شود.
  • برای قرینه‌سازی حرکت در زبان‌های راست‌به‌چپ (همان‌طور که نوار بومی Chrome انجام می‌دهد)، پویانمایی را در :dir(rtl) معکوس کنید:
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • ویژگی‌های عمومی --progress-bar-indeterminate-* را در progress تعریف کنید (نام آن‌ها ازقبل آن‌ها را به وضعیت محدود می‌کند) تا مصرف‌کنندگان بتوانند بدون اطلاع از :indeterminate آن‌ها را لغو کنند.
  • برای تغییر دادن طول بخش (مثلاً لبه جلویی سریع‌تر حرکت می‌کند و لبه عقبی به آن می‌رسد)، --progress-bar-indeterminate-animation را روی دو پویانمایی هم‌زمان تنظیم کنید: یکی که فریم‌های کلیدی آن translate را در cqi (موقعیت) پویانمایی می‌کند و دیگری که فریم‌های کلیدی آن inline-size (طول) را پویانمایی می‌کند، به‌طوری‌که هرکدام فریم‌های کلیدی و هموارسازی خود را داشته باشند. سعی نکنید هر دو لبه را در یک فهرست قاب کلیدی شکل دهید؛ animation-timing-function هر قاب کلیدی برای هر ویژگی در آن قاب کلیدی اعمال می‌شود.
  • --progress-bar-animation در هر دو حالت اعمال می‌شود و می‌توان از آن برای جلوه‌هایی مثل درخشش معین استفاده کرد. revert-rule فقط برای لغو کردن در زمان تنظیم استفاده می‌شود تا ترکیب‌پذیری به حداکثر برسد.
  • هرگز پویانمایی نامشخص را غیرفعال نکنید (animation: none یک پرکننده با عرض صفر در Chromium باقی می‌گذارد). برای کاهش حرکت، سرعت آن را کم کنید.
progress {
  /* Lets the fill position itself in track units (cqi) */
  container-type: inline-size;
  /* Segment length relative to the track */
  --progress-bar-indeterminate-size: 30%;
  --progress-bar-indeterminate-animation-duration: 1.5s;

  /* Respect prefers-reduced-motion */
  @media (prefers-reduced-motion: reduce) {
    --progress-bar-indeterminate-animation-duration: 10s;
  }
}

progress:indeterminate {
  --progress-bar-animation: var(
      --progress-bar-indeterminate-animation,
      progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear
    );
}

/* Add to the existing pseudo-element rules */
progress::-webkit-progress-value {
  animation: var(--progress-bar-animation, revert-rule);
}

progress::-moz-progress-bar {
  animation: var(--progress-bar-animation, revert-rule);
}

/* Physical: left to right regardless of direction (reverse under :dir(rtl) to mirror). */
@keyframes progress-bar-indeterminate {
  from, to {
    inline-size: var(--progress-bar-indeterminate-size);
    /* Pin to the physical left; Chromium would otherwise lay the fill out at the inline start */
    margin-right: auto;
  }
  from { translate: -100%; } /* Own width: fully outside the left edge */
  to { translate: 100cqi; }  /* Track width: fully outside the right edge */
}

ملاحظات دسترس‌پذیری بیشتر

علاوه‌بر دستورالعمل‌های ذکرشده در بالا:

  • اعلام تکمیل بدون جلب تمرکز: tabindex را تنظیم نکنید یا .focus() را در <progress> فراخوانی نکنید، زیرا غیرتعاملی است و انتقال تمرکز به آن باعث اختلال در کار کاربران صفحه‌کلید می‌شود. برای اعلام زمان پایان یافتن تکلیف پس‌زمینه، عنصر وضعیت مؤدبانه (role="status" یا aria-live="polite") را به‌روز کنید.
  • نوشتار جایگزین را داخل <progress> اضافه نکنید: نوشتار داخل <progress> توسط مرورگرهای مدرن نادیده گرفته می‌شود و فناوری‌های کمکی از آن استفاده نمی‌کنند.
  • از aria-busy + aria-describedby در <div> عمومی بدون برچسب اجتناب کنید. aria-busy پشتیبانی صفحه‌خوان ناسازگار دارد و aria-describedby در <div> بی‌نام بدون نقش توسط فناوری‌های کمکی نادیده گرفته می‌شود. اگر محتوای منطقه‌ای درحین بار شدن آماده نیست، آن را با hidden یا aria-hidden="true" پنهان کنید.
  • باید هنگام افزودن سبک‌های سفارشی، کنتراست مناسبی بین نوار پیشرفت و قطعه وجود داشته باشد.
  • جایگزین‌هایی برای نمایش وضعیت نامشخص درنظر بگیرید: پویانمایی‌های نوار پیشرفت نامشخص که به‌طور موازی با محتوای دیگر نمایش داده می‌شوند ممکن است برای برخی کاربران حواس‌پرت‌کننده باشند.

راهبردهای جایگزین

پشتیبانی مرورگر و جایگزین‌ها برای accent-color

خصوصیت accent-color ارتقای پیش‌رو است. مرورگرهایی که از این ویژگی پشتیبانی نمی‌کنند آن را نادیده می‌گیرند و از رنگ‌های واسط کاربر پیش‌فرض خود استفاده می‌کنند.

اگر accent-color پشتیبانی نمی‌شود و سبک‌بندی نوار پیشرفت لازم است، از روش سبک‌بندی سفارشی‌شده برای اعمال سبک‌بندی ترجیحی به نوار پیشرفت استفاده کنید.

جایگزین‌ها و پشتیبانی مرورگر برای revert-rule

اگر revert-rule پشتیبانی نشود، animation: var(--progress-bar-animation, revert-rule) به animation-name: revert-rule با مدت 0s برمی‌گردد وقتی --progress-bar-animation تنظیم نشده باشد، که یک عملیات بی‌ضرر و بدون تأثیر است وقتی هیچ @keyframes revert-rule وجود نداشته باشد.