Tworzenie stylizowanego paska postępu

Element <progress> to semantyczny sposób przedstawiania postępu realizacji zadania, takiego jak pobieranie lub przesyłanie informacji albo ukończenie części wieloetapowego procesu. Użycie elementu natywnego ułatwia dostęp, ponieważ czytniki ekranu mają domyślną rolę role="progressbar" i mogą odczytywać wartości.

W tym przewodniku pokazujemy, jak wdrożyć w pełni stylizowany poziomy pasek postępu z dostosowaniami stylu.

Pasek postępu czy okrąg postępu?

  • Jeśli miejsce jest ograniczone lub preferowany jest kwadratowy format obrazu, rozważ użycie okrągłego paska postępu (okręgu postępu): Okrąg postępu
  • W przypadku większości scenariuszy z nieokreślonym postępem preferuj wskaźniki postępu (wskaźnik postępu wczytywania). Używaj pasków postępu bez określenia czasu, gdy stan nieokreślony jest tymczasowy i wkrótce zostanie zastąpiony paskiem postępu z określeniem czasu (np.oczekiwanie na metadane dotyczące przesyłania, zanim będzie można wyświetlić procent postępu).

Znacznik

  1. Użyj natywnego elementu <progress>: zapewnia on wbudowane ułatwienia dostępu i spójne działanie na różnych platformach.
  2. Jeśli potrzebujesz skali innej niż domyślna (0–1), użyj właściwości max.
  3. Aby ustawić konkretną wartość postępu, ustaw atrybut value. Pomiń value w przypadku stanu nieokreślonego (np. przed rozpoczęciem bardziej konkretnych informacji zwrotnych).
  4. Użyj <label for="...">, aria-labelledby lub aria-label, aby powiązać element postępu z etykietą.

Jakiego typu dostosowanie jest potrzebne?

W przypadku ograniczonego stylu wystarczą te właściwości:

  • accent-color, aby zmienić kolor paska postępu;
  • width/inline-size, aby zmienić szerokość paska postępu
  • height/block-size – niespójne stosowanie, najpierw przetestuj
  • writing-mode: vertical-lr w przypadku słupków pionowych.
  • Efekty, które są stosowane po malowaniu: opacity, filter, clip-path, maski, przekształcenia, box-shadow itp.

Jeśli wystarczy Ci ograniczone formatowanie, pomiń następną sekcję.

OSTRZEŻENIE: niektóre właściwości (np. border-radius, background, border) spowodują wyłączenie domyślnego stylu paska postępu bez powiadomienia. Jeśli potrzebujesz stylów innych niż wymienione powyżej, musisz wdrożyć w pełni niestandardowe style, jak opisano w następnej sekcji. NIE stosuj bardziej rozbudowanego stylu „na wszelki wypadek”.

W pełni niestandardowy styl

1. Konfiguracja podstawowa

  • OBOWIĄZKOWE: użyj appearance: none, aby włączyć styl niestandardowy.
  • Zresetuj granice UA za pomocą border: 0 solid
  • Aby przyciąć aktywny pasek do promienia obramowania elementu, użyj overflow: clip LUB zastosuj ten sam border-radius do paska. NIE używaj border-radius: inherit na pasku, który zależy od konkretnej struktury elementów w modelu Shadow DOM.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. Styl

  • Stylizuj ścieżkę w samym elemencie progress.
  • Użyj odpowiedniego pseudoelementu kierowanego na przeglądarkę, aby kierować reklamy na sam pasek postępu: ::-webkit-progress-value w przypadku WebKit/Blink i ::-moz-progress-bar w przypadku Firefoksa.
  • OBOWIĄZKOWO: NIE grupuj pseudoelementów -webkit- i -moz- w tej samej regule. Reguła zostanie uznana za nieprawidłową i będzie ignorowana. Atrybuty :is() i :where() nie pomogą w tym przypadku, ponieważ nie obsługują pseudoelementów.
  • Każdy pseudoelement kierowany na przeglądarkę powinien pojawić się dokładnie raz. Dostosowania dotyczące stanu (takie jak nieokreślony rozmiar segmentu i animacja) powinny być kierowane przez właściwości niestandardowe ustawione w samym elemencie progress.
  • Opcjonalnie: przejście inline-size na wypełnieniu, aby zmiany wartości były animowane, a nie skokowe. Firefox określa rozmiar wypełnienia w układzie i ignoruje go, co nie ma żadnych negatywnych skutków.
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;
}

3. Styl stanu nieokreślonego

Dodaj niestandardową animację dla stanu nieokreślonego: przekształć pseudoelement wypełnienia w krótki segment i przesuwaj go wzdłuż ścieżki.

  • Nadaj styl samemu wypełnieniu. NIE odtwarzaj go jako tła w elemencie progress.
  • Wypełnienie nieokreślone istnieje tylko dzięki animacji: klatki kluczowe określają jego długość za pomocą rzeczywistego elementu inline-size (co jest lepsze niż wbudowany element inline-size w Chromium) i przemieszczają go za pomocą elementu translate. NIE używaj scale jako długości, ponieważ zniekształca to wzór namalowany na wypełnieniu.
  • Wartości procentowe translate są określane względem szerokości wypełnienia, dlatego pozycje należy wyrażać w jednostkach ścieżki: ustaw progress jako kontener o rozmiarze inline i użyj cqi w klatkach kluczowych. Uwzględnij margin-right: auto w from, to, aby Chromium wyświetlał wypełnienie po lewej stronie również w przypadku języków zapisywanych od prawej do lewej.
  • Do przesuwania wypełnienia używaj przekształceń, a nie marginesów.
    • Przekształcenia utrzymują animację w kompozytorze (chyba że animowana jest też właściwość inline-size)
    • Marginesy nie działają w językach zapisywanych od prawej do lewej z 2 powodów:
    • Firefox pozycjonuje nieokreślone wypełnienie, używając tylko fizycznego lewego marginesu, więc margin-inline-start nie ma wpływu na języki pisane od prawej do lewej i wypełnienie nigdy się nie przesuwa.
    • W Chromium i WebKit, które układają wypełnienie jako normalny blok, gdy margines wypycha wypełnienie poza koniec ścieżki, marginesy sumują się do wartości większej niż ścieżka. Standardowy arkusz CSS rozwiązuje ten problem, ignorując margin-left w językach pisanych od prawej do lewej, dzięki czemu segment przeskakuje do krawędzi, zamiast się z niej zsuwać.
    • translate jest stosowany po układzie, więc żaden z tych problemów nie występuje.
  • Aby odzwierciedlić ruch w językach zapisywanych od prawej do lewej (jak w przypadku paska natywnego Chrome), odwróć animację w :dir(rtl):
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • Zdefiniuj właściwości publiczne --progress-bar-indeterminate-* w progress (ich nazwa już określa ich zakres do stanu), aby konsumenci mogli je zastąpić bez znajomości :indeterminate.
  • Aby zmienić długość segmentu (np. krawędź przednia przesuwa się do przodu, a krawędź tylna ją dogania), ustaw --progress-bar-indeterminate-animation na 2 równoczesne animacje: jedną, której klatki kluczowe animują translate w cqi (pozycja), i drugą, której klatki kluczowe animują inline-size (długość), tak aby każda miała własne klatki kluczowe i efekt spowolnienia. NIE próbuj kształtować obu krawędzi na jednej liście klatek kluczowych. Właściwość animation-timing-function dla klatki kluczowej dotyczy każdej właściwości w tej klatce kluczowej.
  • --progress-bar-animation ma zastosowanie w obu stanach i może być używany do efektów takich jak określone migotanie. revert-rule służy do zastępowania tylko wtedy, gdy jest ustawiony, aby zmaksymalizować możliwość tworzenia kompozycji.
  • Nigdy nie wyłączaj animacji nieokreślonej (animation: none pozostawia w Chromium wypełnienie o szerokości 0). Aby zmniejszyć ruch, zwolnij go.
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 */
}

Dodatkowe kwestie dotyczące ułatwień dostępu

Oprócz wymienionych wyżej wskazówek:

  • Powiadom o zakończeniu bez przejmowania zaznaczenia: nie ustawiaj tabindex ani nie wywołuj .focus() w <progress>, ponieważ jest to element nieinteraktywny, a przeniesienie na niego zaznaczenia przeszkadza użytkownikom klawiatury. Aby ogłosić zakończenie zadania w tle, zaktualizuj element stanu polite (role="status" lub aria-live="polite").
  • Nie dodawaj tekstu zastępczego wewnątrz elementu <progress>: tekst wewnątrz elementu <progress> jest ignorowany przez nowoczesne przeglądarki i nie jest używany przez technologie wspomagające osoby z niepełnosprawnością.
  • Unikaj używania skrótu aria-busy + aria-describedby w przypadku nieoznaczonego ogólnego elementu <div>. Atrybut aria-busy nie jest w pełni obsługiwany przez czytniki ekranu, a atrybut aria-describedby w nienazwanym elemencie <div> bez roli jest ignorowany przez technologie wspomagające osoby z niepełnosprawnością. Jeśli zawartość regionu nie jest gotowa podczas wczytywania, ukryj ją za pomocą hidden lub aria-hidden="true".
  • ZADBAJ o odpowiedni kontrast między paskiem postępu a ścieżką, gdy dodajesz style niestandardowe.
  • Rozważ alternatywne sposoby przedstawiania stanu nieokreślonego: animacje paska postępu nieokreślonego wyświetlane równolegle z innymi treściami mogą rozpraszać niektórych użytkowników.

Strategie zastępcze

Kreacje zastępcze i obsługa koloru uzupełniającego w przeglądarkach

Właściwość accent-color jest progresywnym ulepszaniem. Przeglądarki, które nie obsługują tej właściwości, zignorują ją i użyją domyślnych kolorów interfejsu.

Jeśli właściwość accent-color nie jest obsługiwana, a wymagane jest nadanie stylu paskowi postępu, użyj metody niestandardowej, aby zastosować preferowany styl.

Kreacje zastępcze i obsługa przeglądarek w przypadku revert-rule

Jeśli revert-rule nie jest obsługiwany, animation: var(--progress-bar-animation, revert-rule) powraca do animation-name: revert-rule z czasem trwania 0s, gdy --progress-bar-animation nie jest ustawiony, co jest nieszkodliwą operacją, gdy nie ma @keyframes revert-rule.