{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "hold-to-confirm",
 "name": "Gedrückt halten zum Bestätigen",
 "name_en": "Hold to confirm / long-press action",
 "kurz": "Ein Angebot wird per Halten angenommen: Ring, Balken oder Schwelle füllen sich in 1,2 s, Loslassen spult dreimal so schnell zurück.",
 "kategorie": "Mikroanimationen",
 "kategorie_slug": "mikroanimationen",
 "kategorie_nr": 9,
 "bau_chat": "C4",
 "prioritaet": "B",
 "trendstatus": "aufsteigend",
 "belegstatus": "teilbelegt",
 "website_belege": 1,
 "stimmung": [
  "präzise",
  "verspielt"
 ],
 "stimmung_roh": [
  "bewusst",
  "haptisch",
  "technisch"
 ],
 "technik": [
  "vanilla-js",
  "svg",
  "motion",
  "css"
 ],
 "technik_gruppen": [
  "css",
  "js",
  "canvas",
  "andere"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Für folgenreiche Aktionen wie Löschen, Absenden oder Kaufen muss der Knopf gehalten werden. Während des Haltens füllt sich ein Ring oder Balken, der Knopf schrumpft leicht und ein Schein wächst. Wer vorher loslässt, spult den Fortschritt zurück. Ist er voll, folgen eine kurze Erfolgsanimation und die Aktion.",
  "warum_es_wirkt": "Die Geste ersetzt den Bestätigungsdialog. Ein versehentlicher Klick löst nichts aus, das Halten fühlt sich bewusst an. Der wachsende Fortschritt zeigt jederzeit, wie weit es noch ist. Das Zurückspulen beim Loslassen macht klar, dass noch nichts passiert ist.",
  "wann_einsetzen": "Bei destruktiven oder verbindlichen Aktionen: Löschen, Angebot annehmen, Bestellung absenden. Außerdem in Spielen und auf Erlebnis-Seiten als „Halten zum Starten“.",
  "wann_nicht": "Nicht für häufige Standardaktionen, dort bremst das Halten nur. Nie als einzigen Weg, wenn Nutzer motorisch eingeschränkt sein könnten.",
  "varianten": [
   "Ring: SVG-Ring um einen runden Knopf, dazu leichtes Schrumpfen und wachsender Schein (Motion-Beispiel)",
   "Balken: Füllung im Knopf von links",
   "Schwelle: Der Druck zählt erst nach 250 ms, dann springen Haltekreise von scale .65 auf 1 (Unseen, dort als Navigation)",
   "„Halten zum Starten“ als Intro auf Erlebnis-Seiten"
  ],
  "umsetzung": "Den Fortschritt p (0 bis 1) als CSS-Variable --p führen, per requestAnimationFrame oder mit el.animate() und @property --p. Halten füllt in 1200 ms linear. Ring: SVG-Kreis mit pathLength=\"1\", stroke-dashoffset: calc(1 - var(--p)), um -90° gedreht. Balken: clip-path: inset(0 calc((1 - var(--p)) * 100%) 0 0). Bei pointerup, pointerleave oder pointercancel vor dem Ende läuft p dreimal so schnell zurück (mit el.animate: reverse() und playbackRate). Bei p = 1 Erfolg zeigen und die Aktion auslösen. Tastatur: Leertaste oder Enter halten, keydown mit e.repeat ignorieren, keyup lässt los.",
  "mobil": "Sehr gut geeignet. Am Knopf Kontextmenü und Textauswahl unterdrücken: user-select: none, -webkit-touch-callout: none, touch-action: none und contextmenu verhindern. Das Ziel groß halten.",
  "reduzierte_bewegung": "Fortschritt als schlichter Balken, ohne Schein, ohne Schrumpfen und ohne Kreise.",
  "barrierefreiheit": "Immer eine Alternative anbieten, etwa einen Link oder einen Bestätigungsdialog (WCAG 2.5.1 und 2.5.7). Die Anleitung gehört ins Label („Zum Annehmen gedrückt halten“). Den Fortschritt optional per role=\"progressbar\" und aria-valuenow melden, den Erfolg per aria-live ansagen."
 },
 "messwerte": {
  "easing": "expo.out",
  "dauer_ms": "Schwelle 250; Hold-Animation 200",
  "stagger_ms": "",
  "distanz": "Hold-Kreis innen scale .65",
  "sonstiges": "Loslassen spult zurück",
  "herkunft": "beobachtet",
  "quelle": "https://unseen.co/"
 },
 "kurve": {
  "bezier": [
   0.16,
   1,
   0.3,
   1
  ],
  "css": "cubic-bezier(0.16, 1, 0.3, 1)",
  "name": "expo.out",
  "dauer_ms": 1200
 },
 "gesehen_bei": [
  {
   "seite": "Motion Examples: Hold to confirm",
   "url": "https://motion.dev/examples/js-hold-to-confirm",
   "auszeichnung": "",
   "beobachtung": "Ein Motion-Value steuert Ring (SVG-Kreis r=120, stroke-width 24, rund, um −90° gedreht), leichtes Herunterskalieren und Glow (blur 20px, scale 2); Werte für Dauer hinter Paywall.",
   "art": "demo"
  },
  {
   "seite": "Unseen Studio",
   "url": "https://unseen.co/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft (theme.js + HTML '<span>Click &amp; Hold</span>'): auf der Projektseite startet mouseDown nach setTimeout 250 ms die pausierte clickHoldTl (Cursor-Inneres scale 0 in .2s, Hold-Kreise scale .65/1, expo.out); mouseUp löscht den Timer und spielt die Timeline rückwärts (reverseOnEnd); Cursor zeigt dazu einen 'Click & Hold'-Hinweis.",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Motion: Hold to confirm",
   "url": "https://motion.dev/examples/js-hold-to-confirm"
  }
 ],
 "kombinierbar_mit": [
  "fortschrittsring",
  "button-zustaende",
  "druck-feedback"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/mikroanimationen/hold-to-confirm/",
  "meta": "https://cecodesign.de/lab/fundus/mikroanimationen/hold-to-confirm/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/mikroanimationen/hold-to-confirm/demo"
 },
 "herkunft": {
  "quelldateien": [
   "hover.json",
   "r3-belege-b.json"
  ],
  "pruefnotizen": [
   "r3-belege-b.json (Runde 3, 31.10.2026): Belegt ist eine Halte-Geste mit Rückspulen, aber als Navigation/Reveal, nicht als Bestätigung einer kritischen Aktion; echte Hold-to-confirm-Buttons auf Websites nicht gefunden."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Ein Angebot wird per Halten angenommen: Ring, Balken oder Schwelle füllen sich in 1,2 s, Loslassen spult dreimal so schnell zurück.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "Füllen linear; Schwelle-Kreise und Erfolg expo.out cubic-bezier(0.16, 1, 0.3, 1); Hover und Textwechsel easeOutQuart cubic-bezier(0.165, 0.84, 0.44, 1)",
   "dauer_ms": 1200,
   "ruecklauf": "dreifache Geschwindigkeit, von voll auf leer 400 ms",
   "schwelle_ms": 250,
   "schwelle_kreise": "Ring (120 px) und äußerer Kreis (136 px, 1 px #e8e8e8) scale .65 → 1, opacity 0 → 1 in 200 ms expo.out; Punkt im Knopf 8 px scale 1 → 0 in 200 ms",
   "fortschritt": "CSS-Variable --p 0 … 1, per requestAnimationFrame (dt höchstens 50 ms)",
   "knopf_rund": "88 px (Touch 96 px), #0b0b09, scale calc(1 - .06 * var(--p))",
   "ring": "SVG 120 px, r 56, Spur 1,5 px #e8e8e8, Bogen 3 px #2f5bea stroke-linecap round, pathLength 1, stroke-dashoffset calc(1 - var(--p)), rotate -90deg",
   "schein": "88 px #2f5bea, filter blur(20px), opacity calc(var(--p) * .42), scale calc(1 + var(--p)); nur Variante Ring",
   "balken": "Pille 340 × 56 px, Rand 1 px, scale calc(1 - .02 * var(--p)), Füllung clip-path inset(0 calc((1 - var(--p)) * 100%) 0 0) mit invertierter Textzeile",
   "erfolg": "scale zurück auf 1 in 250 ms expo.out; Haken stroke-dashoffset 1 → 0 in 320 ms expo.out, 80 ms verzögert; Schein aus in 480 ms; Text 160 ms aus- und wieder eingeblendet; navigator.vibrate(12) wenn vorhanden",
   "ui_hover_ms": 120,
   "vorfuehrung": "Zeiger fährt in 700 ms expo.out an; erster Druck 700 ms (zu kurz, Rücklauf sichtbar), 600 ms Pause, zweiter Druck bis zum Erfolg, 1,7 s stehen; danach nächste Variante (Ring, Balken, Schwelle)",
   "farben": "#fcfcfc Grund, #0b0b09 Text und Knopf, #646464 Sekundärtext, #e8e8e8 Linien, #f0f0f0 Flächen, #2f5bea Fortschritt und Fokus (einzige Farbe); Dark Mode per prefers-color-scheme",
   "schriften": "Host Grotesk 400/500 (alles), Newsreader Italic 1.05em (Kundenname), Geist Mono 11 px versal, Laufweite 0.14em (Metadaten); Ziffern tabular-nums"
  },
  "ausloeser": "klick",
  "touch": "Gleiches Verhalten per Finger: pointerdown startet, pointerup, pointerleave und pointercancel lassen los. Am Knopf sind Kontextmenü, Textauswahl und Scrollgeste unterdrückt (user-select none, -webkit-touch-callout none, touch-action none). Runder Knopf 96 px, Umschalter 10 px Innenabstand; bei schmaler Breite steht der Balken über dem Text und nimmt die volle Breite.",
  "reduziert": "Kein Schrumpfen, kein Schein, kein Ring, keine Kreise. Der Fortschritt läuft als schlichter 3-px-Balken unter dem runden Knopf (Balken-Variante bleibt Balken), der Haken erscheint ohne Zeichnen, Texte wechseln ohne Ausblenden. Halten, Schwelle und Rücklauf funktionieren unverändert.",
  "korrekturen": {
   "beschreibung": "Für folgenreiche Aktionen wie Löschen, Absenden oder Kaufen muss der Knopf gehalten werden. Während des Haltens füllt sich ein Ring oder Balken, der Knopf schrumpft leicht und ein Schein wächst. Wer vorher loslässt, spult den Fortschritt zurück. Ist er voll, folgen eine kurze Erfolgsanimation und die Aktion.",
   "warum_es_wirkt": "Die Geste ersetzt den Bestätigungsdialog. Ein versehentlicher Klick löst nichts aus, das Halten fühlt sich bewusst an. Der wachsende Fortschritt zeigt jederzeit, wie weit es noch ist. Das Zurückspulen beim Loslassen macht klar, dass noch nichts passiert ist.",
   "wann_einsetzen": "Bei destruktiven oder verbindlichen Aktionen: Löschen, Angebot annehmen, Bestellung absenden. Außerdem in Spielen und auf Erlebnis-Seiten als „Halten zum Starten“.",
   "wann_nicht": "Nicht für häufige Standardaktionen, dort bremst das Halten nur. Nie als einzigen Weg, wenn Nutzer motorisch eingeschränkt sein könnten.",
   "varianten": [
    "Ring: SVG-Ring um einen runden Knopf, dazu leichtes Schrumpfen und wachsender Schein (Motion-Beispiel)",
    "Balken: Füllung im Knopf von links",
    "Schwelle: Der Druck zählt erst nach 250 ms, dann springen Haltekreise von scale .65 auf 1 (Unseen, dort als Navigation)",
    "„Halten zum Starten“ als Intro auf Erlebnis-Seiten"
   ],
   "umsetzung": "Den Fortschritt p (0 bis 1) als CSS-Variable --p führen, per requestAnimationFrame oder mit el.animate() und @property --p. Halten füllt in 1200 ms linear. Ring: SVG-Kreis mit pathLength=\"1\", stroke-dashoffset: calc(1 - var(--p)), um -90° gedreht. Balken: clip-path: inset(0 calc((1 - var(--p)) * 100%) 0 0). Bei pointerup, pointerleave oder pointercancel vor dem Ende läuft p dreimal so schnell zurück (mit el.animate: reverse() und playbackRate). Bei p = 1 Erfolg zeigen und die Aktion auslösen. Tastatur: Leertaste oder Enter halten, keydown mit e.repeat ignorieren, keyup lässt los.",
   "mobil": "Sehr gut geeignet. Am Knopf Kontextmenü und Textauswahl unterdrücken: user-select: none, -webkit-touch-callout: none, touch-action: none und contextmenu verhindern. Das Ziel groß halten.",
   "reduzierte_bewegung": "Fortschritt als schlichter Balken, ohne Schein, ohne Schrumpfen und ohne Kreise.",
   "barrierefreiheit": "Immer eine Alternative anbieten, etwa einen Link oder einen Bestätigungsdialog (WCAG 2.5.1 und 2.5.7). Die Anleitung gehört ins Label („Zum Annehmen gedrückt halten“). Den Fortschritt optional per role=\"progressbar\" und aria-valuenow melden, den Erfolg per aria-live ansagen."
  },
  "geprueft": "01.11.2026",
  "notiz": "Welt: Rezept „Stille Visitenkarte“. Die erfundene Salzburger Interaction-Designerin Hanna Pölzl schickt der Hutmacherei Ebner (Hallein) ein Angebot; Halten nimmt es verbindlich an, ein E-Mail-Link ist die Alternative. Drei Varianten per Umschalter: Ring (Motion-Prinzip mit Schrumpfen und Schein), Balken (clip-path-Füllung mit invertiertem Text), Schwelle (Unseen-Messwerte: 250 ms, Kreise .65 → 1 in 200 ms expo.out). 1200 ms und dreifacher Rücklauf stammen aus dem Steckbrief, die Motion-Dauer liegt hinter einer Paywall; Schrumpfen .94, Schein-Deckkraft .42 und Knopfgrößen sind eigene Werte. Prinzipien aus dem Bildfundus: dünner Kreis um einen Enter-Knopf (rogierdeboeve.com), leiser „Click & Hold“-Hinweis (ultranoir.com), Liste mit Zahlenspalte (jetset.nl). AD-Review 01.11.2026: Note 4."
 }
}
