{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "validierung-nach-interaktion",
 "name": "Validierung erst nach Interaktion",
 "name_en": "Validate after interaction (:user-invalid / :user-valid)",
 "kurz": "Felder bleiben beim Tippen still, zeigen erst nach Verlassen oder Absenden einen Fehler und werden sofort grün, sobald die Eingabe stimmt.",
 "kategorie": "Mikroanimationen",
 "kategorie_slug": "mikroanimationen",
 "kategorie_nr": 9,
 "bau_chat": "C4",
 "prioritaet": "A",
 "trendstatus": "aufsteigend",
 "belegstatus": "belegt",
 "website_belege": 3,
 "stimmung": [
  "präzise",
  "verspielt"
 ],
 "stimmung_roh": [
  "freundlich",
  "seriös",
  "funktional"
 ],
 "technik": [
  "css",
  "vanilla-js"
 ],
 "technik_gruppen": [
  "css",
  "js"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Felder zeigen keinen Fehler, solange jemand tippt. :user-invalid greift erst, wenn das Feld nach einer Änderung verlassen oder das Formular abgeschickt wurde. Dann färbt sich der Rahmen rot und ein Fehlertext erscheint. Sobald die Eingabe stimmt, verschwindet der Fehler sofort und :user-valid zeigt Grün, optional mit Häkchen. Passwort-Regeln lassen sich über :has() hervorheben.",
  "warum_es_wirkt": "Niemand wird beim Tippen angeschrien. Die Entwarnung kommt trotzdem sofort beim Korrigieren. Das wirkt höflich und souverän.",
  "wann_einsetzen": "In jedem Formular: Kontakt, Newsletter, Checkout, Login, Registrierung.",
  "wann_nicht": "Nicht als einzige Prüfung, wenn der Server entscheidet (dann zusätzlich JS und Serverantwort). Farbe nie als einziges Signal.",
  "varianten": [
   "Reines CSS: :user-invalid plus Fehlertext als Geschwister",
   "Häkchen zeichnet sich bei :user-valid ein, Warnzeichen bei :user-invalid",
   "Passwort-Regelliste per .feld:has(input:user-invalid) einfärben",
   "JS-Fallback mit eigenem Dirty-Status (WeakMap), wie in der Chrome-Anleitung",
   "Kombiniert mit Floating Label"
  ],
  "umsetzung": "input:user-invalid { border-color: #F55252; background: #fbe9e5 } .feld:has(input:user-invalid) .fehler { opacity: 1; translate: 0 } (vorher opacity 0, translate 0 -10px, 300 ms ease-out). input:user-valid { border-color: #2e7d4f }. .feld:has(input:user-invalid) .regeln li:not(.ok) { color: #b42a2a; font-weight: 600 }. Hinweistext über das Feld setzen, dann verdeckt ihn das Autofill-Popover nicht. aria-invalid per kleinem Script synchron halten (input, blur, submit). Shake nur beim Absende-Versuch: @keyframes { 25% { translate: -4px } 75% { translate: 4px } } 300 ms. Formular mit novalidate: Ein Absende-Versuch setzt :user-invalid trotzdem auf allen Feldern. Für Längenregeln pattern statt minlength nehmen, wenn Werte auch per Script gesetzt werden.",
  "mobil": "Identisch. Eingaben mindestens 16 px groß, sonst zoomt iOS.",
  "reduzierte_bewegung": "Kein Shake, kein Wandern. Farbe, Zeichen und Text bleiben.",
  "barrierefreiheit": "Hinweise über aria-describedby, Fehler über aria-errormessage verknüpfen. aria-invalid synchron halten. Statuszeile mit role=\"status\" meldet, wie viele Felder offen sind. Farbe nie allein: immer Text und Zeichen dazu. Fehlertext dunkler als die Rahmenfarbe, damit er 4,5 : 1 erreicht."
 },
 "messwerte": {
  "easing": "ease-out (Tooltip)",
  "dauer_ms": "Fehler ohne Übergang (sofort); Tooltip 300",
  "stagger_ms": "",
  "distanz": "Tooltip translateY −10px → 0",
  "sonstiges": "Fehlerfarbe #F55252 (Ramotion), #fd5959 mit Hintergrundwechsel (Vooban); beide nutzen :user-invalid, Vooban zusätzlich :has() auf dem Feld-Container",
  "herkunft": "beobachtet",
  "quelle": "https://www.ramotion.com/css/main.Dz8nt8Hz.css"
 },
 "kurve": {
  "bezier": [
   0,
   0,
   0.58,
   1
  ],
  "css": "cubic-bezier(0, 0, 0.58, 1)",
  "name": "ease-out",
  "dauer_ms": 300
 },
 "gesehen_bei": [
  {
   "seite": "Chrome Developers: Validate input after interaction",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/forms/validate-input-after-interaction",
   "auszeichnung": "",
   "beobachtung": "Offizielle Anleitung mit Code für E-Mail und Passwort, aria-invalid-Synchronisation, Fallback ohne DOM-Klassen.",
   "art": "artikel"
  },
  {
   "seite": "Chrome Developers: Required field feedback",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/forms/required-field-feedback",
   "auszeichnung": "",
   "beobachtung": "Weitere Anleitung zum Pflichtfeld-Feedback (Titel in Navigation gesehen).",
   "art": "artikel"
  },
  {
   "seite": "Mailchimp",
   "url": "https://mailchimp.com/",
   "auszeichnung": "",
   "beobachtung": "below-fold.css: .inputNumber_input:has(input:user-invalid){border-color:var(--color-error); border-width:.125rem} – Fehlerrahmen erst nach Interaktion.",
   "art": "website"
  },
  {
   "seite": "Ramotion",
   "url": "https://www.ramotion.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): main.Dz8nt8Hz.css: form .input input:user-invalid / textarea:user-invalid {border:1px solid #F55252}; Geschwister .error (absolut, top 70px, #f55252) wird per :user-invalid~.error von opacity 0/visibility hidden auf sichtbar geschaltet; dieselbe Regel auch für das Mailchimp-Newsletter-Formular (#mc_embed_signup). Tooltip daneben erscheint bei :focus mit transition all .3s ease-out und translateY(-10px) → 0.",
   "art": "website"
  },
  {
   "seite": "Vooban",
   "url": "https://vooban.com/",
   "auszeichnung": "",
   "beobachtung": "Live geprüft 31.10.2026 (HTML/Bundle per curl abgerufen): assets/styles/main.css: .c-input-text:has(.c-input-text_input:user-invalid) und .c-textarea:has(…:user-invalid) setzen --outline-color #fd5959 und --input-background-color #4358c8; dieselbe outline 1px wie bei :focus-visible; Checkbox per .c-checkbox_input:user-invalid~.c-checkbox_label.",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Chrome: Validate input after interaction",
   "url": "https://developer.chrome.com/docs/modern-web-guidance/guides/forms/validate-input-after-interaction"
  }
 ],
 "kombinierbar_mit": [
  "passwort-staerke",
  "button-zustaende"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/mikroanimationen/validierung-nach-interaktion/",
  "meta": "https://cecodesign.de/lab/fundus/mikroanimationen/validierung-nach-interaktion/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/mikroanimationen/validierung-nach-interaktion/demo"
 },
 "herkunft": {
  "quelldateien": [
   "hover.json",
   "patch-belege-1.json",
   "r3-belege-a.json"
  ],
  "pruefnotizen": [
   "patch-belege-1.json: Nur 1 Produktionsbeleg; :user-invalid sonst nur als Tailwind-Utility in der Doku-CSS (nicht gezählt).",
   "r3-belege-a.json (Runde 3, 31.10.2026): Mit Mailchimp jetzt drei Produktionsbelege für :user-invalid. Ramotion hält zusätzlich die Klasse .invalid als JS-Fallback."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Felder bleiben beim Tippen still, zeigen erst nach Verlassen oder Absenden einen Fehler und werden sofort grün, sobald die Eingabe stimmt.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "ease-out cubic-bezier(0, 0, 0.58, 1) (gemessen: Ramotion-Tooltip)",
   "dauer_meldung_ms": 300,
   "distanz_meldung": "translateY -10px → 0, dazu opacity 0 → 1",
   "dauer_farbe_ms": 150,
   "haekchen": "stroke-dasharray 22, stroke-dashoffset 22 → 0 in 300 ms",
   "warnzeichen": "opacity 0 → 1, scale 0.6 → 1 in 300 ms",
   "shake": "300 ms, translate -4px / +4px bei 25 % / 75 %, nur beim Absende-Versuch",
   "fehler_rahmen": "#F55252 (Ramotion)",
   "fehler_text": "#b42a2a (dunkler für Kontrast 4,5 : 1)",
   "fehler_grund": "#fbe9e5 (Hintergrundwechsel wie bei Vooban)",
   "gueltig": "#2e7d4f",
   "selektoren": "input:user-invalid, input:user-valid, .feld:has(input:user-invalid) .fehler, .feld:has(input:user-invalid) .regeln li:not(.ok)",
   "ringweiten_regel": "pattern=\"\\s*(4[4-9]|[56]\\d|7[0-2])\\s*/\\s*(4[4-9]|[56]\\d|7[0-2])\\s*\" (zwei Weiten 44 bis 72; pattern statt minlength/min, weil die Vorführung Werte per Script setzt)",
   "reduziert": "kein Shake, kein Wandern, kein Zeichnen; Meldung opacity 150 ms linear"
  },
  "ausloeser": "klick",
  "touch": "Identisch zum Desktop: Fehler nach Verlassen des Felds oder Absenden. Eingaben 17 px gegen Zoom, Umschalter und Material-Auswahl mit größeren Zielen (Material zweispaltig unter 420 px), Knopf volle Breite unter 420 px.",
  "reduziert": "Kein Shake, keine Bewegung der Fehlermeldung, Häkchen erscheint ohne Zeichnen. Rahmenfarbe, Zeichen und Text bleiben, die Meldung blendet in 150 ms ein.",
  "korrekturen": {
   "beschreibung": "Felder zeigen keinen Fehler, solange jemand tippt. :user-invalid greift erst, wenn das Feld nach einer Änderung verlassen oder das Formular abgeschickt wurde. Dann färbt sich der Rahmen rot und ein Fehlertext erscheint. Sobald die Eingabe stimmt, verschwindet der Fehler sofort und :user-valid zeigt Grün, optional mit Häkchen. Passwort-Regeln lassen sich über :has() hervorheben.",
   "warum_es_wirkt": "Niemand wird beim Tippen angeschrien. Die Entwarnung kommt trotzdem sofort beim Korrigieren. Das wirkt höflich und souverän.",
   "wann_einsetzen": "In jedem Formular: Kontakt, Newsletter, Checkout, Login, Registrierung.",
   "wann_nicht": "Nicht als einzige Prüfung, wenn der Server entscheidet (dann zusätzlich JS und Serverantwort). Farbe nie als einziges Signal.",
   "varianten": [
    "Reines CSS: :user-invalid plus Fehlertext als Geschwister",
    "Häkchen zeichnet sich bei :user-valid ein, Warnzeichen bei :user-invalid",
    "Passwort-Regelliste per .feld:has(input:user-invalid) einfärben",
    "JS-Fallback mit eigenem Dirty-Status (WeakMap), wie in der Chrome-Anleitung",
    "Kombiniert mit Floating Label"
   ],
   "umsetzung": "input:user-invalid { border-color: #F55252; background: #fbe9e5 } .feld:has(input:user-invalid) .fehler { opacity: 1; translate: 0 } (vorher opacity 0, translate 0 -10px, 300 ms ease-out). input:user-valid { border-color: #2e7d4f }. .feld:has(input:user-invalid) .regeln li:not(.ok) { color: #b42a2a; font-weight: 600 }. Hinweistext über das Feld setzen, dann verdeckt ihn das Autofill-Popover nicht. aria-invalid per kleinem Script synchron halten (input, blur, submit). Shake nur beim Absende-Versuch: @keyframes { 25% { translate: -4px } 75% { translate: 4px } } 300 ms. Formular mit novalidate: Ein Absende-Versuch setzt :user-invalid trotzdem auf allen Feldern. Für Längenregeln pattern statt minlength nehmen, wenn Werte auch per Script gesetzt werden.",
   "mobil": "Identisch. Eingaben mindestens 16 px groß, sonst zoomt iOS.",
   "reduzierte_bewegung": "Kein Shake, kein Wandern. Farbe, Zeichen und Text bleiben.",
   "barrierefreiheit": "Hinweise über aria-describedby, Fehler über aria-errormessage verknüpfen. aria-invalid synchron halten. Statuszeile mit role=\"status\" meldet, wie viele Felder offen sind. Farbe nie allein: immer Text und Zeichen dazu. Fehlertext dunkler als die Rahmenfarbe, damit er 4,5 : 1 erreicht."
  },
  "geprueft": "01.11.2026",
  "notiz": "Welt: Trauring-Anfrage der erfundenen Goldschmiede Aurel, Pforzheim (Termin in der Werkstatt), gestaltet nach Rezept luxus-manufaktur-stille: Anthrazit #23282f und Weiß, Messing #a88a5a nur als Linie und Fokus, Bodoni Moda (opsz) für Titel, Instrument Sans mit Versal-Labels +0.08em, Tabellenziffern. Links eine eigene SVG-Komposition zweier Platinringe im Lichtkegel (keine fremden Bilder, keine medien/-Logos). Die Vorführung tippt eine unfertige E-Mail ohne Rot, schickt ab (Fehler, Shake), korrigiert (sofort grün), trägt Ringweiten 54 / 58 ein und sendet; die Tafel zeigt die drei Phasen und danach die Anfrage-Nummer. Jede Runde eine Variante (Häkchen, Regelliste, Nur CSS). Echtes :user-invalid ohne Klassen-Trick: requestSubmit() auf dem novalidate-Formular setzt die Nutzer-Gültigkeit, form.reset() setzt sie zurück. Bewegungswerte unverändert; nur die Passwort-Regel wurde zur Ringweiten-Regel. Zugang 02.11.2026: --meta #8b9097 → #6a6f76 (4,6:1 auch auf #f5f5f3); Kontrastmodus-Rahmen für Knöpfe."
 }
}
