{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "text-roll-hover",
 "name": "Text-Roll beim Hover (doppelte Zeile rollt durch)",
 "name_en": "Rolling text hover / character stagger roll / Text roll / rolling text / slot hover",
 "kurz": "Jedes Label liegt doppelt in einer einzeiligen Maske; beim Hover rollt eine Zeichenwelle die Kopie herein, ohne dass sich das Layout bewegt.",
 "kategorie": "Schrift-Animationen",
 "kategorie_slug": "schrift-animationen",
 "kategorie_nr": 6,
 "bau_chat": "C1",
 "prioritaet": "A",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 5,
 "stimmung": [
  "ruhig",
  "editorial",
  "präzise",
  "verspielt"
 ],
 "stimmung_roh": [
  "präzise",
  "verspielt",
  "technisch",
  "editorial",
  "modern",
  "ruhig"
 ],
 "technik": [
  "css",
  "splittext",
  "vanilla-js",
  "gsap",
  "motion"
 ],
 "technik_gruppen": [
  "css",
  "js",
  "gsap",
  "andere"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Das Label eines Links oder Buttons liegt zweimal übereinander in einer Maske mit overflow: hidden. Beim Hover rollt die obere Zeile nach oben hinaus (translateY −100 %), die Kopie rollt von unten nach. In der hochwertigen Variante rollt jedes Zeichen einzeln mit kleiner Verzögerung, so läuft eine Welle durch das Wort. Beim Verlassen rollt alles zurück.",
  "warum_es_wirkt": "Das Feedback ist deutlich und braucht keinen zusätzlichen Platz. Die Zeichenwelle wirkt mechanisch und taktil, wie eine Fallblattanzeige. Das Detail wirkt präzise und typografisch und gehört zu den meistkopierten Details auf Award-Seiten.",
  "wann_einsetzen": "Navigation, CTA-Buttons, Footer-Links und Projektlisten mit kurzen Labels.",
  "wann_nicht": "Links im Fließtext, weil der Effekt beim Lesen stört. Lange Labels, weil die Welle zu lange dauert. Schriften mit stark schwankender Zeilenhöhe, weil die Kopie dann springt.",
  "varianten": [
   "Ganze Zeile rollt: zwei Spans, kein Versatz (Mont-Fort: 400 ms, cubic-bezier(.4,0,.1,1)).",
   "Zeichenweise gestaffelt: Index pro Zeichen, transition-delay: calc(var(--i) * 12ms), Kopie 10 ms später (Quentin Hocdé).",
   "Stilwechsel: Die Kopie steht in einem anderen Schnitt oder einer anderen Farbe, zum Beispiel kursiv.",
   "text-shadow-Duplikat: text-shadow: 0 1lh currentColor liefert die zweite Zeile, nur ein DOM-Knoten.",
   "Kopie per ::after mit content: attr(data-text), startet bei top: 100 % und scale(.9) (Cuberto).",
   "Raus und rein getrennt: erst nach oben hinaus, dann von unten herein (Codrops Hyperion: je 0,2 s; MagneticButtons: 0,15 s power2.in, dann 0,2 s expo.out).",
   "Richtungsabhängig: rollt in Richtung der Mausbewegung.",
   "3D-Würfel: Zeichen kippen per rotateX um die eigene Achse."
  ],
  "umsetzung": "HTML: <a href=\"…\" aria-label=\"Kontakt\"><span class=\"roll\" aria-hidden=\"true\"><span class=\"roll-a\">…Zeichen…</span><span class=\"roll-b\">…Zeichen…</span></span></a>. Jedes Zeichen ist ein <span class=\"z\" style=\"--i:0\"> (eigenes Splitting oder SplitText mit propIndex, künftig sibling-index()). CSS: .roll { position: relative; display: inline-block; overflow: hidden; line-height: 1.3 } .roll-b { position: absolute; inset: 0 } .z { display: inline-block; transition: transform .8s cubic-bezier(.16,1,.3,1); transition-delay: calc(var(--i) * 12ms) } .roll-b .z { transform: translateY(100%); transition-delay: calc(var(--i) * 12ms + 10ms) } a:hover .roll-a .z { transform: translateY(-100%) } a:hover .roll-b .z { transform: none }. Hover nur unter @media (hover: hover) and (pointer: fine), :focus-visible immer gleich behandeln. Fallstricke: line-height der Maske groß genug für Unterlängen wählen, sonst blitzen sie oben auf. Bei Zeichen-Spans geht das Kerning verloren, Ligaturen prüfen. Ein wenig padding-inline verhindert abgeschnittene Überhänge.",
  "mobil": "Ohne Hover nicht rollen lassen. Beim Tippen einmal abspielen oder ganz weglassen. Den Hover-Effekt in @media (hover: hover) kapseln.",
  "reduzierte_bewegung": "Kein Rollen. Farbwechsel und eingeblendete Unterstreichung statt Bewegung.",
  "barrierefreiheit": "Der Linktext darf genau einmal vorgelesen werden. Deshalb aria-label am Link und beide Zeichenzeilen aria-hidden, oder der text-shadow-Trick, der gar nicht vorgelesen wird. Zeichen-Spans dürfen den Linknamen nicht zerhacken. :focus-visible löst denselben Effekt aus wie Hover."
 },
 "messwerte": {
  "easing": "cubic-bezier(.16,1,.3,1) (Cuberto); cubic-bezier(.4,0,.1,1) (Mont-Fort/Lusion)",
  "dauer_ms": "800 (Cuberto Nav); 400 (Mont-Fort)",
  "stagger_ms": "12 pro Zeichen, zweite Zeile +10 Versatz (Quentin Hocdé)",
  "distanz": "−100 % bzw. −110 %; Duplikat startet bei top 100 % mit scale .9 (Cuberto)",
  "sonstiges": "Duplikat per content:attr(data-text) spart DOM; Effekt nur für feine Zeiger aktiviert",
  "herkunft": "beobachtet",
  "quelle": "https://cuberto.com/_astro/Footer.v_lo1qnf.css ; https://quentinhocde.com/assets/Layout.Cu5Fn0rw.css ; https://www.mont-fort.com/_astro/_slug_.CRG2uH_C.css",
  "aus_eintrag": [
   "text-roll-hover"
  ]
 },
 "kurve": {
  "bezier": [
   0.16,
   1.0,
   0.3,
   1.0
  ],
  "css": "cubic-bezier(0.16, 1, 0.3, 1)",
  "name": "expo.out",
  "dauer_ms": 800
 },
 "gesehen_bei": [
  {
   "seite": "TRIONN",
   "url": "https://trionn.com/",
   "auszeichnung": "zeigt Awwwards/FWA/CSSDA-Logos (welche Auszeichnung genau, nicht verifiziert)",
   "beobachtung": "Navigationslabels doppelt im Markup (‚WorkWork‘, ‚AboutAbout‘) – typisches Indiz für doppelte Roll-Labels; Animation selbst nicht verifiziert.",
   "art": "website-ungeprueft",
   "browser_gemessen": true
  },
  {
   "seite": "Quai Network",
   "url": "https://www.qu.ai/",
   "auszeichnung": "",
   "beobachtung": "Navigations- und Linklisten liegen doppelt vor, zweite Kopie mit ‚+‘ ergänzt (Hover-Zustand); Codrops hat den Quai-Hover als Tutorial nachgebaut.",
   "art": "website"
  },
  {
   "seite": "Osmo – Button with CSS Character Stagger",
   "url": "https://www.osmo.supply/resource/button-with-css-character-stagger",
   "auszeichnung": "",
   "beobachtung": "Als Ressource gelistet (nur Titel gesehen).",
   "art": "demo"
  },
  {
   "seite": "Codrops ButtonHoverStyles – ‚Hyperion‘",
   "url": "https://github.com/codrops/ButtonHoverStyles",
   "auszeichnung": "",
   "beobachtung": "CSS gelesen: MoveUpInitial 0.2 s → translate3d(0,-105 %,0), dann MoveUpEnd 0.2 s von 100 % auf 0, Text mit mix-blend-mode: difference, Hintergrund skaliert per scaleX mit cubic-bezier(0.7,0,0.2,1).",
   "art": "demo"
  },
  {
   "seite": "Codrops MagneticButtons",
   "url": "https://github.com/codrops/MagneticButtons",
   "auszeichnung": "",
   "beobachtung": "Beim Eintritt: Text 0.15 s power2.in auf y -20 %/opacity 0, dann 0.2 s expo.out von y 100 % auf 0.",
   "art": "demo"
  },
  {
   "seite": "Motion Examples: Rolling text button (+ stagger)",
   "url": "https://motion.dev/examples/react-rolling-text-button-stagger",
   "auszeichnung": "",
   "beobachtung": "Offizielle Beispiele, auch zeichenweise gestaffelt.",
   "art": "demo"
  },
  {
   "seite": "Cuberto",
   "url": "https://cuberto.com/",
   "auszeichnung": "",
   "beobachtung": "Footer.css: .nav__title overflow hidden; span transition transform .8s cubic-bezier(.16,1,.3,1); Duplikat per span:after{content:attr(data-text); top:100%; transform:scale(.9)} rollt beim Hover herein; Buttons: span translateY(-110 %).",
   "art": "website",
   "browser_gemessen": true
  },
  {
   "seite": "Lusion",
   "url": "https://lusion.co/",
   "auszeichnung": "",
   "beobachtung": "about.css: .header-menu-link:hover .header-menu-link-text → translate3d(0,-100%,0), .header-menu-link-text-clone → translateZ(0); Hintergrund-Pill skaliert auf 1, Icon .2 s Verzögerung cubic-bezier(.4,0,.1,1).",
   "art": "website",
   "browser_gemessen": true
  },
  {
   "seite": "Quentin Hocdé",
   "url": "https://quentinhocde.com/",
   "auszeichnung": "",
   "beobachtung": "Nav-Links aus zwei <c-magic-text>-Zeilen (zweite aria-hidden); Layout.css: beim Hover Zeichen der ersten Zeile translate3d(0,-100%,0) mit transition-delay 12 ms, 24 ms, 36 ms … (12 ms pro Zeichen), zweite Zeile ab 22 ms nachgezogen.",
   "art": "website"
  },
  {
   "seite": "Mont-Fort",
   "url": "https://www.mont-fort.com/",
   "auszeichnung": "",
   "beobachtung": "Menü-CTA: zwei spans, zweiter bei bottom −100 %; Hover translate3d(0,-100%,0), transition transform .4s cubic-bezier(.4,0,.1,1), nur bei (hover:hover) and (pointer:fine).",
   "art": "website"
  }
 ],
 "tutorials": [
  {
   "titel": "Osmo – Button with CSS Character Stagger",
   "url": "https://www.osmo.supply/resource/button-with-css-character-stagger"
  },
  {
   "titel": "Osmo – Duplicate Text with Text Shadow",
   "url": "https://www.osmo.supply/resource/duplicate-text-with-text-shadow"
  },
  {
   "titel": "Codrops Tag ‚hover‘ (u. a. Quai-Network-Hover)",
   "url": "https://tympanus.net/codrops/tag/hover/"
  },
  {
   "titel": "Codrops: Button Hover Styles (Artikel)",
   "url": "https://tympanus.net/codrops/?p=53413"
  },
  {
   "titel": "Motion: Rolling text button",
   "url": "https://motion.dev/examples/react-rolling-text-button"
  }
 ],
 "kombinierbar_mit": [
  "magnetische-elemente",
  "kontextueller-cursor",
  "richtungsabhaengiger-marquee-hover",
  "fuellung-vom-eintrittspunkt",
  "unterstreichung-varianten"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/schrift-animationen/text-roll-hover/",
  "meta": "https://cecodesign.de/lab/fundus/schrift-animationen/text-roll-hover/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/schrift-animationen/text-roll-hover/demo"
 },
 "herkunft": {
  "quelldateien": [
   "schriftanimation.json",
   "hover.json",
   "patch-belege-1.json"
  ],
  "pruefnotizen": [
   "patch-belege-1.json: 4 neue Produktionsbelege; trionn.com nicht verifiziert (bleibt ungeprüft)."
  ],
  "zusammengelegt_aus": [
   "hover-roll-text",
   "text-roll-hover"
  ],
  "querverweis_kategorien": [
   "Hover"
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Jedes Label liegt doppelt in einer einzeiligen Maske; beim Hover rollt eine Zeichenwelle die Kopie herein, ohne dass sich das Layout bewegt.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "zeichen": {
    "easing": "cubic-bezier(0.16, 1, 0.3, 1)",
    "easing_name": "expo.out",
    "dauer_ms": 800,
    "stagger_ms": 12,
    "kopie_versatz_ms": 10,
    "distanz": "translateY(-100%) / Kopie translateY(100%) → 0"
   },
   "kursiv": {
    "easing": "cubic-bezier(0.16, 1, 0.3, 1)",
    "easing_name": "expo.out",
    "dauer_ms": 800,
    "stagger_ms": 20,
    "kopie_versatz_ms": 10,
    "kopie_schrift": "Instrument Serif Italic, 1.08em, Farbe #a4482a",
    "distanz": "translateY(-100%) / Kopie translateY(100%) → 0"
   },
   "ganze_zeile": {
    "easing": "cubic-bezier(0.4, 0, 0.1, 1)",
    "dauer_ms": 400,
    "stagger_ms": 0,
    "distanz": "translateY(-100%) / Kopie translateY(100%) → 0"
   },
   "maske": "overflow: hidden; line-height: 1.3; padding-inline: .08em (Überhänge)",
   "verzoegerung": "transition-delay: calc(var(--i) * var(--versatz)); Kopie + var(--nachzug)",
   "hover_nur": "@media (hover: hover) and (pointer: fine); :focus-visible immer",
   "touch_dauer_ms": "dauer + 20 × stagger + 250, dann zurück",
   "reduziert": "Farbwechsel #a4482a + Unterstrich einblenden, 150 ms linear"
  },
  "ausloeser": "hover",
  "touch": "Kein Hover-Rollen. Beim Tippen rollt das Label einmal durch und nach kurzer Zeit zurück (Klasse .an).",
  "reduziert": "Kein Rollen, Kopie ausgeblendet. Hover und Fokus färben das Label ziegelrot und blenden eine Unterstreichung ein (150 ms).",
  "korrekturen": {
   "beschreibung": "Das Label eines Links oder Buttons liegt zweimal übereinander in einer Maske mit overflow: hidden. Beim Hover rollt die obere Zeile nach oben hinaus (translateY −100 %), die Kopie rollt von unten nach. In der hochwertigen Variante rollt jedes Zeichen einzeln mit kleiner Verzögerung, so läuft eine Welle durch das Wort. Beim Verlassen rollt alles zurück.",
   "warum_es_wirkt": "Das Feedback ist deutlich und braucht keinen zusätzlichen Platz. Die Zeichenwelle wirkt mechanisch und taktil, wie eine Fallblattanzeige. Das Detail wirkt präzise und typografisch und gehört zu den meistkopierten Details auf Award-Seiten.",
   "wann_einsetzen": "Navigation, CTA-Buttons, Footer-Links und Projektlisten mit kurzen Labels.",
   "wann_nicht": "Links im Fließtext, weil der Effekt beim Lesen stört. Lange Labels, weil die Welle zu lange dauert. Schriften mit stark schwankender Zeilenhöhe, weil die Kopie dann springt.",
   "varianten": [
    "Ganze Zeile rollt: zwei Spans, kein Versatz (Mont-Fort: 400 ms, cubic-bezier(.4,0,.1,1)).",
    "Zeichenweise gestaffelt: Index pro Zeichen, transition-delay: calc(var(--i) * 12ms), Kopie 10 ms später (Quentin Hocdé).",
    "Stilwechsel: Die Kopie steht in einem anderen Schnitt oder einer anderen Farbe, zum Beispiel kursiv.",
    "text-shadow-Duplikat: text-shadow: 0 1lh currentColor liefert die zweite Zeile, nur ein DOM-Knoten.",
    "Kopie per ::after mit content: attr(data-text), startet bei top: 100 % und scale(.9) (Cuberto).",
    "Raus und rein getrennt: erst nach oben hinaus, dann von unten herein (Codrops Hyperion: je 0,2 s; MagneticButtons: 0,15 s power2.in, dann 0,2 s expo.out).",
    "Richtungsabhängig: rollt in Richtung der Mausbewegung.",
    "3D-Würfel: Zeichen kippen per rotateX um die eigene Achse."
   ],
   "umsetzung": "HTML: <a href=\"…\" aria-label=\"Kontakt\"><span class=\"roll\" aria-hidden=\"true\"><span class=\"roll-a\">…Zeichen…</span><span class=\"roll-b\">…Zeichen…</span></span></a>. Jedes Zeichen ist ein <span class=\"z\" style=\"--i:0\"> (eigenes Splitting oder SplitText mit propIndex, künftig sibling-index()). CSS: .roll { position: relative; display: inline-block; overflow: hidden; line-height: 1.3 } .roll-b { position: absolute; inset: 0 } .z { display: inline-block; transition: transform .8s cubic-bezier(.16,1,.3,1); transition-delay: calc(var(--i) * 12ms) } .roll-b .z { transform: translateY(100%); transition-delay: calc(var(--i) * 12ms + 10ms) } a:hover .roll-a .z { transform: translateY(-100%) } a:hover .roll-b .z { transform: none }. Hover nur unter @media (hover: hover) and (pointer: fine), :focus-visible immer gleich behandeln. Fallstricke: line-height der Maske groß genug für Unterlängen wählen, sonst blitzen sie oben auf. Bei Zeichen-Spans geht das Kerning verloren, Ligaturen prüfen. Ein wenig padding-inline verhindert abgeschnittene Überhänge.",
   "mobil": "Ohne Hover nicht rollen lassen. Beim Tippen einmal abspielen oder ganz weglassen. Den Hover-Effekt in @media (hover: hover) kapseln.",
   "reduzierte_bewegung": "Kein Rollen. Farbwechsel und eingeblendete Unterstreichung statt Bewegung.",
   "barrierefreiheit": "Der Linktext darf genau einmal vorgelesen werden. Deshalb aria-label am Link und beide Zeichenzeilen aria-hidden, oder der text-shadow-Trick, der gar nicht vorgelesen wird. Zeichen-Spans dürfen den Linknamen nicht zerhacken. :focus-visible löst denselben Effekt aus wie Hover."
  },
  "geprueft": "31.10.2026",
  "notiz": "Menü eines erfundenen Gestaltungsstudios (Hallberg & Rau, Köln): vier große Listenzeilen, zwei Nav-Links und ein CTA-Knopf rollen. Umschalter: Zeichen (12 ms, Quentin Hocdé/Cuberto), Kursiv (Kopie in Instrument Serif Italic, 20 ms nach Osmo), Ganze Zeile (Mont-Fort). Werte stehen als CSS-Variablen am body. Vorführung: gezeichneter Zeiger fährt die Zeilen ab, pro Runde eine Variante, beginnend mit Kursiv. Bilder 01.11.2026: eigene Bilder statt Portfolio. Zugang 02.11.2026: --leise .55 → .66 (5,3:1); Knopf-Rahmen im Kontrastmodus."
 }
}
