Fortschrittsanzeige mit benutzerdefiniertem Design erstellen

Das <progress>-Element ist die semantische Möglichkeit, den Fortschritt einer Aufgabe darzustellen, z. B. das Herunter- oder Hochladen von Informationen oder das Abschließen eines Teils eines mehrstufigen Prozesses. Die Verwendung des nativen Elements verbessert die Barrierefreiheit, da Screenreader eine implizite role="progressbar" haben und Werte ansagen können.

In diesem Leitfaden wird eine vollständig gestaltete horizontale Fortschrittsanzeige mit Stilanpassungen implementiert.

Fortschrittsanzeige oder Fortschritts-Ring?

  • Wenn der Platz begrenzt ist oder ein quadratisches Seitenverhältnis bevorzugt wird, können Sie eine kreisförmige Fortschrittsanzeige (Fortschritts-Ring) verwenden: Fortschritts-Ring
  • Für die meisten Szenarien mit unbestimmtem Fortschritt sind rotierende Ladesymbole (Rotierendes Ladesymbol) am besten geeignet. Verwenden Sie unbestimmte Fortschrittsanzeigen, wenn der unbestimmte Status vorübergehend ist und bald durch eine bestimmte Fortschrittsanzeige ersetzt wird (z. B. beim Warten auf Metadaten zu einem Upload, bevor ein Fortschritt in Prozent angezeigt werden kann).

Markieren & Zeichnen

  1. Natives <progress>-Element verwenden: Es bietet integrierte Bedienungshilfen und ein plattformübergreifend einheitliches Verhalten.
  2. Verwenden Sie max, wenn eine andere Skala als die Standardskala von 0 bis 1 erforderlich ist.
  3. Legen Sie für einen bestimmten Fortschrittswert ein value-Attribut fest. Lassen Sie value für einen unbestimmten Status weg (z.B. bevor konkreteres Feedback beginnt).
  4. Verwenden Sie <label for="...">, aria-labelledby oder aria-label, um das Fortschrittselement mit einem Label zu verknüpfen.

Welche Art von Anpassung ist erforderlich?

Für ein eingeschränktes Styling reichen diese Eigenschaften aus:

  • accent-color, um die Farbe der Fortschrittsanzeige zu ändern
  • width / inline-size, um die Breite der Fortschrittsanzeige zu ändern
  • height/block-size wird inkonsistent angewendet – zuerst testen
  • writing-mode: vertical-lr für vertikale Balken.
  • Effekte, die nach dem Zeichnen angewendet werden: opacity, filter, clip-path, Masken, Transformationen, box-shadow usw.

Wenn ein eingeschränktes Styling ausreicht, überspringen Sie den nächsten Abschnitt.

WARNUNG: Bestimmte Attribute (z.B. border-radius, background, border) deaktivieren automatisch den Standardstil der Fortschrittsanzeige. Wenn Sie über die oben genannten Eigenschaften hinausgehende Stile benötigen, müssen Sie vollständig benutzerdefinierte Stile implementieren, wie im nächsten Abschnitt beschrieben. Wenden Sie KEINE umfangreicheren Stile an, „nur für den Fall, dass es funktioniert“.

Vollständig benutzerdefinierte Stile

1. Grundlegende Einrichtung

  • OBLIGATORISCH: Verwenden Sie appearance: none, um benutzerdefinierte Stile zu aktivieren.
  • UA-Grenzen mit border: 0 solid zurücksetzen
  • Wenn Sie die aktive Leiste auf den Rahmenradius des Elements beschneiden möchten, verwenden Sie overflow: clip ODER wenden Sie denselben border-radius auf die Leiste an. Verwenden Sie NICHT border-radius: inherit für die Leiste, da dies von der spezifischen Struktur der Elemente im Shadow-DOM abhängt.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. Stile

  • Gestalten Sie die Spur im progress-Element selbst.
  • Verwenden Sie das entsprechende browserspezifische Pseudoelement, um die Fortschrittsanzeige selbst anzusprechen: ::-webkit-progress-value für WebKit/Blink und ::-moz-progress-bar für Firefox.
  • OBLIGATORISCH: Gruppieren Sie die Pseudoelemente -webkit- und -moz- NICHT in derselben Regel. Die Regel wird als ungültig eingestuft und ignoriert. :is() und :where() helfen hier nicht, da sie keine Pseudoelemente unterstützen.
  • Jedes auf den Browser ausgerichtete Pseudoelement sollte genau einmal vorkommen. Statusspezifische Anpassungen (z. B. die unbestimmte Segmentgröße und Animation) sollten über benutzerdefinierte Eigenschaften weitergeleitet werden, die für das progress-Element selbst festgelegt sind.
  • Optional: Übergang inline-size für die Füllung, damit sich Änderungen des bestimmten Werts animieren lassen, anstatt zu springen. Firefox passt die Größe des Füllbereichs im Layout an und ignoriert ihn, was unbedenklich ist.
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. Stil für unbestimmten Status

Benutzerdefinierte Animation für den unbestimmten Status hinzufügen: Wandeln Sie das Pseudoelement für die Füllung in ein kurzes Segment um und lassen Sie es entlang der Spur gleiten.

  • Gestalten Sie die Füllung selbst. Erstellen Sie sie NICHT als Hintergrund auf progress neu.
  • Die unbestimmte Füllung existiert nur durch ihre Animation: Die Keyframes legen ihre Länge mit einem echten inline-size fest (dies schlägt das Inline-inline-size des Chromium-UA) und bewegen sie mit translate. Verwenden Sie für die Länge NICHT scale, da dadurch alle Muster verzerrt werden, die auf die Füllung gezeichnet werden.
  • translate-Prozentwerte werden anhand der Breite des Füllbereichs aufgelöst. Drücken Sie Positionen stattdessen in Track-Einheiten aus: Machen Sie progress zu einem Container mit Inline-Größe und verwenden Sie cqi in den Keyframes. Fügen Sie margin-right: auto in from, to ein, damit Chromium die Füllung auch bei RTL-Sprachen auf der physischen linken Seite anzeigt.
  • Verwenden Sie Transformationen, nicht Ränder, um die Füllung zu verschieben.
    • Transformationen behalten die Animation im Compositor bei (es sei denn, inline-size wird ebenfalls animiert)
    • Ränder werden in RTL aus zwei verschiedenen Gründen nicht richtig dargestellt:
    • Firefox positioniert die unbestimmte Füllung nur anhand des physischen linken Rands. margin-inline-start hat also keine Auswirkungen auf RTL und die Füllung wird nie verschoben.
    • In Chromium und WebKit, die die Füllung als normalen Block darstellen, addieren sich die Ränder zu mehr als der Spur, sobald ein Rand die Füllung über das Ende der Spur hinaus verschiebt. Im Standard-CSS wird dieses Problem gelöst, indem margin-left in RTL-Sprachen ignoriert wird. Das Segment springt dann an den Rand, anstatt darüber hinaus zu gleiten.
    • translate wird nach dem Layout angewendet, sodass keines der Probleme auftritt.
  • Wenn Sie die Bewegung in Sprachen mit Schreibrichtung von rechts nach links spiegeln möchten (wie es in der nativen Leiste von Chrome der Fall ist), kehren Sie die Animation unter :dir(rtl) um:
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • Definieren Sie die öffentlichen --progress-bar-indeterminate-*-Eigenschaften für progress (ihr Name beschränkt sie bereits auf den Status), damit Nutzer sie überschreiben können, ohne :indeterminate zu kennen.
  • Wenn Sie die Segmentlänge variieren möchten (z. B. die Vorderkante bewegt sich schneller und die Hinterkante holt auf), legen Sie für --progress-bar-indeterminate-animation zwei gleichzeitige Animationen fest: eine, deren Keyframes translate in cqi (Position) animieren, und eine, deren Keyframes inline-size (Länge) animieren. So erhält jede Animation eigene Keyframes und ein eigenes Easing. Versuchen Sie NICHT, beide Kanten in einer einzigen Keyframe-Liste zu formen. Der Keyframe-spezifische animation-timing-function gilt für jede Property in diesem Keyframe.
  • --progress-bar-animation gilt für beide Zustände und kann für Effekte wie ein bestimmtes Schimmern verwendet werden. revert-rule wird nur überschrieben, wenn es festgelegt ist, um die Kombinierbarkeit zu maximieren.
  • Deaktivieren Sie niemals die unbestimmte Animation (animation: none hinterlässt in Chromium eine Füllung mit einer Breite von null). Wenn du die Bewegung reduzieren möchtest, verlangsame sie stattdessen.
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 */
}

Weitere Überlegungen zur Barrierefreiheit im Internet

Zusätzlich zu den oben genannten Richtlinien gilt Folgendes:

  • Abschluss ankündigen, ohne den Fokus zu stehlen: Legen Sie tabindex nicht fest und rufen Sie .focus() nicht für <progress> auf, da es nicht interaktiv ist und das Verschieben des Fokus auf dieses Element Tastaturnutzer stört. Wenn Sie ankündigen möchten, dass eine Hintergrundaufgabe abgeschlossen ist, aktualisieren Sie ein höfliches Statuselement (role="status" oder aria-live="polite").
  • Fügen Sie keinen Fallback-Text in <progress> ein: Text in <progress> wird von modernen Browsern ignoriert und nicht von Hilfstechnologien verwendet.
  • Vermeiden Sie aria-busy + aria-describedby für ein nicht gekennzeichnetes generisches <div>. aria-busy wird von Screenreadern nicht einheitlich unterstützt und aria-describedby für ein unbenanntes <div> ohne Rolle wird von Hilfstechnologien ignoriert. Wenn der Inhalt einer Region beim Laden nicht bereit ist, blenden Sie ihn mit hidden oder aria-hidden="true" aus.
  • EMPFOHLEN: Achten Sie beim Hinzufügen benutzerdefinierter Stile auf einen ausreichenden Kontrast zwischen der Fortschrittsanzeige und der Spur.
  • Alternativen für die Darstellung eines unbestimmten Status in Betracht ziehen: Animationen für unbestimmte Fortschrittsanzeigen, die parallel zu anderen Inhalten angezeigt werden, können von einigen Nutzern als störend empfunden werden.

Fallback-Strategien

Fallbacks und Browserunterstützung für „accent-color“

Das Attribut accent-color ist eine progressive Verbesserung. Browser, die diese Eigenschaft nicht unterstützen, ignorieren sie und verwenden ihre Standard-UI-Farben.

Wenn accent-color nicht unterstützt wird und die Fortschrittsanzeige angepasst werden muss, verwenden Sie die Methode für benutzerdefinierte Stile, um den gewünschten Stil auf die Fortschrittsanzeige anzuwenden.

Fallbacks und Browserunterstützung für revert-rule

Wenn revert-rule nicht unterstützt wird, greift animation: var(--progress-bar-animation, revert-rule) auf animation-name: revert-rule mit einer Dauer von 0s zurück, wenn --progress-bar-animation nicht festgelegt ist. Das ist ein harmloser No-Op, wenn kein @keyframes revert-rule vorhanden ist.