{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "fortschrittsring",
 "name": "Fortschrittsring",
 "name_en": "Progress ring (conic-gradient + @property)",
 "kurz": "Natives <progress> als Ring: conic-gradient, per @property in 400 ms weich nachgeführt, bei 100 % in Messing.",
 "kategorie": "Mikroanimationen",
 "kategorie_slug": "mikroanimationen",
 "kategorie_nr": 9,
 "bau_chat": "C4",
 "prioritaet": "B",
 "trendstatus": "aufsteigend",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "ruhig",
  "präzise"
 ],
 "stimmung_roh": [
  "präzise",
  "ruhig"
 ],
 "technik": [
  "css"
 ],
 "technik_gruppen": [
  "css"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Ein natives <progress> wird per conic-gradient zum Ring. Der Wert kommt über attr(value type(<number>)) in die Eigenschaft --value. Weil --value per @property als Zahl registriert ist, lässt sie sich animieren: Ändert sich value, läuft der Ring in 400 ms weich nach. Bei 100 % wechselt die Farbe und zeigt den Abschluss.",
  "warum_es_wirkt": "Ein Ring zeigt Fortschritt auf kleinstem Raum und liest sich auf einen Blick. Die kurze, weiche Nachführung macht jede Änderung sichtbar, ohne zu bremsen. Das native Element bringt die Semantik mit, eine SVG-Rechnung entfällt.",
  "wann_einsetzen": "Uploads, mehrstufige Formulare, Auftrags- und Bestellstatus, Kurs- oder Lesefortschritt, Skill-Anzeigen, Hold-to-confirm.",
  "wann_nicht": "Bei unbestimmter Wartezeit, dafür gibt es Spinner. Und nie, wenn der Wert nicht ehrlich ist.",
  "varianten": [
   "Nativer Ring: <progress> mit conic-gradient und @property (Chrome-Leitfaden, Beispiel 150 px, Band 16 px).",
   "SVG-Ring: Kreis mit pathLength=\"1\" und stroke-dashoffset, klassisch und überall lauffähig (Unseen: r 39, Offset per GSAP).",
   "Kompakter Ring mit 20 px neben Text oder in Listen (Stripe: r 8, Strich 2).",
   "Ring im Button beim Absenden (siehe button-zustaende).",
   "Lesefortschritt am Scroll (siehe scroll-fortschritt)."
  ],
  "umsetzung": "@property --value { syntax: '<number>'; inherits: true; initial-value: 0; } progress.ring { --value: attr(value type(<number>), 0); appearance: none; border: 0; inline-size: 150px; aspect-ratio: 1; border-radius: 50%; background: conic-gradient(var(--fill) calc(var(--value) * 1%), var(--track) 0); mask: radial-gradient(farthest-side, #0000 calc(100% - 16.5px), #000 calc(100% - 16px)); } progress.ring::-webkit-progress-bar, progress.ring::-webkit-progress-value { background: transparent; } progress.ring::-moz-progress-bar { background: transparent; } @media (prefers-reduced-motion: no-preference) { progress.ring { transition: --value .4s cubic-bezier(0.4, 0, 0.2, 1); } }. Das Loch in der Mitte schneidet hier eine radiale Maske aus. Der Chrome-Leitfaden nutzt dafür border plus background-clip: border-area. attr() mit Typ nur hinter @supports (x: attr(x type(*))) einsetzen. Ohne diese Unterstützung kopiert JS den Wert nach --value (style.setProperty oder MutationObserver). SVG-Variante: <circle r=\"39\" pathLength=\"1\"> mit stroke-dasharray: 1 2 und stroke-dashoffset: calc(1 - var(--value) / 100), gedreht um -90deg. Die Lücke von 2 verhindert Reste der Linie bei 0 %.",
  "mobil": "Identisch. Ein Ring mit 150 px passt auch auf 390 px Breite.",
  "reduzierte_bewegung": "Keine Transition: Der Ring springt direkt auf den neuen Wert (Dauer 0).",
  "barrierefreiheit": "Natives <progress> mit aria-label, ohne Fallback-Inhalt im Element. Den aktuellen Schritt zusätzlich als Text in einer aria-live-Region ausgeben (Stripe nutzt aria-live=\"polite\"). Eine Prozentzahl in der Ringmitte bekommt aria-hidden, sonst wird sie doppelt vorgelesen. Beim SVG-Ring role=\"progressbar\" mit aria-valuenow setzen."
 },
 "messwerte": {
  "easing": "cubic-bezier(0.4, 0, 0.2, 1); per GSAP set (ohne Easing, Unseen)",
  "dauer_ms": "400",
  "stagger_ms": "",
  "distanz": "150 px Ring, 16 px Stärke (Beispiel); r=8 / Strich 2 (Stripe); r=39, Umfang 244 (Unseen)",
  "sonstiges": "pathLength=1 vereinfacht dasharray (Stripe)",
  "herkunft": "beobachtet",
  "quelle": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/progress-ring; https://stripe.com/ ; https://unseen.co/"
 },
 "kurve": {
  "bezier": [
   0.4,
   0.0,
   0.2,
   1.0
  ],
  "css": "cubic-bezier(0.4, 0, 0.2, 1)",
  "name": "Material Standard",
  "dauer_ms": 400
 },
 "gesehen_bei": [
  {
   "seite": "Chrome Developers: Progress ring",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/progress-ring",
   "auszeichnung": "",
   "beobachtung": "conic-gradient + @property --value, Transition 0.4 s cubic-bezier(0.4,0,0.2,1) nur bei no-preference, background-clip: border-area, Erfolgsfarbe bei value=100 (Hinweis: Code der Seite enthält kleine Inkonsistenzen).",
   "art": "artikel"
  },
  {
   "seite": "Codrops CSSProgress (Repo)",
   "url": "https://github.com/codrops/CSSProgress",
   "auszeichnung": "",
   "beobachtung": "CSS-only Fortschrittsbalken mit Schattierung (Sass) – ältere Referenz.",
   "art": "demo"
  },
  {
   "seite": "Stripe",
   "url": "https://stripe.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (Startseiten-HTML): .progress-ring__graphic in der Console-Query-Box (Stärke-Anzeige, aria-live='polite') – 20×20-SVG, Hintergrundkreis r=8 + Vordergrundkreis r=8 mit pathLength='1', stroke-dasharray '0 1', stroke-width 2, transform rotate(-90deg).",
   "art": "website"
  },
  {
   "seite": "Unseen Studio",
   "url": "https://unseen.co/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (HTML + theme.js): Cursor-Ring .js-cursor-progress-ring (circle r=39, stroke-dasharray 244px, rotate(-90)) zeigt Video-/Drag-Fortschritt; JS setzt strokeDashoffset = round(2.44·(100 − 100·p)) per GSAP, opacity 0 bei p ≤ 0.",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Chrome: Progress ring",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/ui-components/progress-ring"
  }
 ],
 "kombinierbar_mit": [
  "hold-to-confirm",
  "button-zustaende",
  "zahlen-odometer"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/mikroanimationen/fortschrittsring/",
  "meta": "https://cecodesign.de/lab/fundus/mikroanimationen/fortschrittsring/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/mikroanimationen/fortschrittsring/demo"
 },
 "herkunft": {
  "quelldateien": [
   "hover.json",
   "r3-belege-b.json"
  ],
  "pruefnotizen": [
   "r3-belege-b.json (Runde 3, 31.10.2026): Zwei Produktionsbelege (Formular-Feedback und Cursor-Video-Fortschritt)."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Natives <progress> als Ring: conic-gradient, per @property in 400 ms weich nachgeführt, bei 100 % in Messing.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "cubic-bezier(0.4, 0, 0.2, 1) (Material Standard, Messwert)",
   "dauer_ms": 400,
   "uebergang": "transition: --value 400ms cubic-bezier(0.4, 0, 0.2, 1), --fuellung 400ms cubic-bezier(0.4, 0, 0.2, 1)",
   "textwechsel": "opacity 200 ms, gleiche Kurve",
   "ui_hover_ms": 150,
   "nativ": "<progress max=100>, 150 px, Band 16 px (radial-gradient-Maske), Spur rgba(243,242,238,.1), Füllung #f3f2ee, bei value=100 #a88a5a",
   "attr": "--value: attr(value type(<number>), 0) hinter @supports (x: attr(x type(*))), sonst style.setProperty('--value')",
   "svg": "viewBox 0 0 80 80, r 39, pathLength 1, stroke-dasharray 1 2, stroke-dashoffset calc(1 - var(--value) / 100), Spur Strich 1, Bogen Strich 2, rotate -90deg, 150 px",
   "kompakt": "SVG 20 px, r 8, Strich 2, pathLength 1, stroke-dashoffset 1 → 0 in 400 ms",
   "skala": "60 Striche um den Ring, Feld 186 px",
   "schritte": "6, Wert = erledigt / 6 · 100 (0, 17, 33, 50, 67, 83, 100)",
   "vorfuehrung": "Schritt alle 950 ms, bei 100 % 2400 ms halten, Variante wechseln, Rücklauf 1100 ms",
   "farben": "#23282f Anthrazit, #f4f4f2 Produktfläche, #8b9097 Meta, #d9dadb Linien, #a88a5a Messing",
   "schriften": "Bodoni Moda (Titel, Zahl, tabular-nums), Instrument Sans (Text, Versal +0.08em), Geist Mono (Demo-Labels)"
  },
  "ausloeser": "klick",
  "touch": "Kein Hover nötig: Variante, „Schritt abschließen“ und „Zurücksetzen“ per Tippen; Knöpfe unter (hover: none) 38 px hoch. Layout einspaltig, Ring zentriert.",
  "reduziert": "Ring, Prozentzahl und Mini-Ringe springen ohne Übergang auf den neuen Wert, Texte wechseln ohne Ausblenden, der Sekundenzeiger steht.",
  "korrekturen": {
   "beschreibung": "Ein natives <progress> wird per conic-gradient zum Ring. Der Wert kommt über attr(value type(<number>)) in die Eigenschaft --value. Weil --value per @property als Zahl registriert ist, lässt sie sich animieren: Ändert sich value, läuft der Ring in 400 ms weich nach. Bei 100 % wechselt die Farbe und zeigt den Abschluss.",
   "warum_es_wirkt": "Ein Ring zeigt Fortschritt auf kleinstem Raum und liest sich auf einen Blick. Die kurze, weiche Nachführung macht jede Änderung sichtbar, ohne zu bremsen. Das native Element bringt die Semantik mit, eine SVG-Rechnung entfällt.",
   "wann_einsetzen": "Uploads, mehrstufige Formulare, Auftrags- und Bestellstatus, Kurs- oder Lesefortschritt, Skill-Anzeigen, Hold-to-confirm.",
   "wann_nicht": "Bei unbestimmter Wartezeit, dafür gibt es Spinner. Und nie, wenn der Wert nicht ehrlich ist.",
   "varianten": [
    "Nativer Ring: <progress> mit conic-gradient und @property (Chrome-Leitfaden, Beispiel 150 px, Band 16 px).",
    "SVG-Ring: Kreis mit pathLength=\"1\" und stroke-dashoffset, klassisch und überall lauffähig (Unseen: r 39, Offset per GSAP).",
    "Kompakter Ring mit 20 px neben Text oder in Listen (Stripe: r 8, Strich 2).",
    "Ring im Button beim Absenden (siehe button-zustaende).",
    "Lesefortschritt am Scroll (siehe scroll-fortschritt)."
   ],
   "umsetzung": "@property --value { syntax: '<number>'; inherits: true; initial-value: 0; } progress.ring { --value: attr(value type(<number>), 0); appearance: none; border: 0; inline-size: 150px; aspect-ratio: 1; border-radius: 50%; background: conic-gradient(var(--fill) calc(var(--value) * 1%), var(--track) 0); mask: radial-gradient(farthest-side, #0000 calc(100% - 16.5px), #000 calc(100% - 16px)); } progress.ring::-webkit-progress-bar, progress.ring::-webkit-progress-value { background: transparent; } progress.ring::-moz-progress-bar { background: transparent; } @media (prefers-reduced-motion: no-preference) { progress.ring { transition: --value .4s cubic-bezier(0.4, 0, 0.2, 1); } }. Das Loch in der Mitte schneidet hier eine radiale Maske aus. Der Chrome-Leitfaden nutzt dafür border plus background-clip: border-area. attr() mit Typ nur hinter @supports (x: attr(x type(*))) einsetzen. Ohne diese Unterstützung kopiert JS den Wert nach --value (style.setProperty oder MutationObserver). SVG-Variante: <circle r=\"39\" pathLength=\"1\"> mit stroke-dasharray: 1 2 und stroke-dashoffset: calc(1 - var(--value) / 100), gedreht um -90deg. Die Lücke von 2 verhindert Reste der Linie bei 0 %.",
   "mobil": "Identisch. Ein Ring mit 150 px passt auch auf 390 px Breite.",
   "reduzierte_bewegung": "Keine Transition: Der Ring springt direkt auf den neuen Wert (Dauer 0).",
   "barrierefreiheit": "Natives <progress> mit aria-label, ohne Fallback-Inhalt im Element. Den aktuellen Schritt zusätzlich als Text in einer aria-live-Region ausgeben (Stripe nutzt aria-live=\"polite\"). Eine Prozentzahl in der Ringmitte bekommt aria-hidden, sonst wird sie doppelt vorgelesen. Beim SVG-Ring role=\"progressbar\" mit aria-valuenow setzen."
  },
  "geprueft": "01.11.2026",
  "notiz": "Servicestatus einer Uhrenrevision bei der erfundenen Uhrmacherei Guntschna (Bozen), Rezept luxus-manufaktur-stille: Anthrazit und Weiß, Bodoni Moda und Instrument Sans, Messing nur als Linie und als Erfolgsfarbe. Die Uhr ist eine eigene SVG-Zeichnung. Sechs Arbeitsschritte, Wert = erledigt / 6. Umschalter: natives <progress> (conic-gradient, @property, attr() mit Typ und JS-Fallback) und SVG-Haarlinie (pathLength 1). Die Liste nutzt 20-px-Ringe wie Stripe. Die Prozentzahl liest den gerade animierten Wert per getComputedStyle aus --value. Vorführung: ein Schritt alle 950 ms bis 100 %, 2,4 s halten, Variante wechseln. Kurve bewusst Material Standard 400 ms (Messwert des Musters) statt der Rezept-Kurve. AD-Review 01.11.2026: Note 5 Zugang 02.11.2026: --meta #8b9097 → #6a6f76 (4,6:1 auch auf #f4f4f2); Kontrastmodus-Rahmen für Knöpfe."
 }
}
