Почему стоит использовать <progress>, а не <div>?
Многие разработчики по привычке создают прогресс-бары через два вложенных <div>. Однако у нативного тега <progress> есть три критических преимущества:
- Доступность (Accessibility) из коробки: Скринридеры сразу понимают, что это индикатор процесса. Вам не нужно вручную добавлять
role="progressbar",aria-valuenow,aria-valueminиaria-valuemax. Браузер делает это за вас. - Лаконичность кода: Вместо управления шириной внутреннего блока через
style={{ width: '30%' }}, вы просто меняете атрибутvalue. Это чище и логичнее с точки зрения структуры DOM. - Встроенное состояние “Loading”: Если убрать атрибут
value, тег автоматически переходит в состояние indeterminate (неопределенное), что удобно для индикации ожидания без написания лишней логики.
Разметка с <progress>:
<progress class="progress-1" max="100" value="40"></progress>
Проблема «сжатого» градиента
При стилизации стандартного элемента <progress> с помощью градиента разработчики часто сталкиваются с неприятным визуальным эффектом: градиент «сжимается» или «растягивается» вместе с заполнением полоски.
В обычном подходе градиент применяется к самому индикатору (::-webkit-progress-value).
Правильно
Неправильно
Показать CSS код
.progress-bad {
--_height: 1rem;
appearance: none;
-webkit-appearance: none;
width: 100%;
height: var(--_height);
border: none;
border-radius: var(--_height);
overflow: hidden;
}
.progress-bad::-webkit-progress-bar {
background-color: #eee;
border-radius: var(--_height);
}
.progress-bad::-webkit-progress-value {
border-radius: var(--_height);
background-image: linear-gradient(to right, #ef4444 0%, #facc15 50%, #22c55e 100%);
}
.progress-bad::-moz-progress-bar {
border-radius: var(--_height);
background-image: linear-gradient(to right, #ef4444 0%, #facc15 50%, #22c55e 100%);
} Результат: Когда прогресс равен 10%, вы видите только первые 10% градиента (ярко-красный). Когда прогресс 100% — всю растяжку. Цвета выглядят статично привязанными к длине заполненной части, а не к общей шкале.
Теперь добавим всего пару строк кода, и получим правильный вариант:
.progress-1 {
container-type: inline-size;
}
.progress-1::-webkit-progress-value {
background-size: 100cqi;
}
.progress-1::-moz-progress-bar {
background-size: 100cqi;
}
Финальный CSS:
Показать CSS код
.progress-1 {
--_height: 1rem;
appearance: none;
-webkit-appearance: none;
container-type: inline-size;
width: 100%;
height: var(--_height);
border: none;
border-radius: var(--_height);
overflow: hidden;
}
.progress-1::-webkit-progress-bar {
background-color: #eee;
border-radius: var(--_height);
}
.progress-1::-webkit-progress-value {
border-radius: var(--_height);
background-image: linear-gradient(to right, #ef4444 0%, #facc15 50%, #22c55e 100%);
background-size: 100cqi;
}
.progress-1::-moz-progress-bar {
border-radius: var(--_height);
background-image: linear-gradient(to right, #ef4444 0%, #facc15 50%, #22c55e 100%);
background-size: 100cqi;
} В чем главные преимущества?
Фиксация градиента через cqi - используя background-size: 100cqi, мы привязываем размер градиента не к текущей ширине заполненной полоски, а к полной ширине всего элемента <progress>.
Как это работает: Даже если полоска заполнена всего на 20%, она отображает именно ту часть градиента, которая соответствует этому участку на общей шкале. Цвет плавно «проявляется», а не растягивается.
Итог
Цвета больше не зависят от того, насколько заполнена шкала — они зависят от позиции. Это делает индикатор интуитивно понятным: красный всегда означает «начало», желтый — «середину», а зеленый — «финиш», независимо от текущей длины полоски.