{
 "$schema": "../../schema.json",
 "schema": "fundus/1",
 "id": "warenkorb-drawer",
 "name": "Warenkorb als Seitenpanel",
 "name_en": "Cart drawer with quantity, undo remove and free-shipping progress",
 "kurz": "Mini-Warenkorb als natives dialog-Panel von rechts: Mengenwahl, Entfernen mit Rückgängig, Versandschwelle als Fortschritt, Leerzustand und Summe inkl. MwSt.",
 "kategorie": "Sektionen & Funktionen",
 "kategorie_slug": "sektionen",
 "kategorie_nr": 16,
 "bau_chat": "D",
 "prioritaet": "D",
 "trendstatus": "etabliert",
 "belegstatus": "belegt",
 "website_belege": 2,
 "stimmung": [
  "präzise",
  "warm"
 ],
 "stimmung_roh": [
  "warm",
  "persönlich",
  "funktional"
 ],
 "technik": [
  "css",
  "vanilla-js",
  "dialog",
  "intl",
  "svg"
 ],
 "technik_gruppen": [
  "css",
  "js",
  "canvas"
 ],
 "leistungskosten": "niedrig",
 "steckbrief": {
  "beschreibung": "Ein Mini-Warenkorb, der als Panel von rechts hereinfährt, statt auf eine eigene Seite zu wechseln. Er ist ein modaler <dialog>: Der Fokus geht hinein, Esc und ein Klick daneben schließen, danach steht der Fokus wieder am Auslöser. Im Panel stehen die Artikel mit Bild, Sorte, Grundpreis, Mengenwahl und „Entfernen“ mit Rückgängig. Oben zeigt ein Fortschritt den Weg zur Versandkostengrenze („Noch 8,40 € bis versandkostenfrei“), unten stehen Zwischensumme, Versand, Summe inklusive Mehrwertsteuer und der Weg zur Kasse. Ist der Korb leer, zeigt er einen Leerzustand mit zwei Vorschlägen.",
  "warum_es_wirkt": "Wer etwas in den Korb legt, bleibt auf der Seite und sieht sofort, was drin ist und was es kostet. Die Versandgrenze gibt einen klaren, ehrlichen Anreiz. Rückgängig nimmt die Angst vor dem falschen Klick.",
  "wann_einsetzen": "Kleine Shops von Rösterei, Hofladen, Weingut, Manufaktur, Mode- und D2C-Marken. Immer wenn Kunden mehrere Artikel kombinieren. Die Variante „Hinweis am Symbol“, wenn Kunden meist weiter stöbern und das Panel stören würde.",
  "wann_nicht": "Bei Einzelkäufen mit Konfiguration (Möbel nach Maß, Reisen), auf die direkt der Checkout folgt. Wenn das Shopsystem einen eigenen Warenkorb erzwingt, der sich nicht anpassen lässt.",
  "varianten": [
   "Seitenpanel (Drawer) mit Mengenwahl, Versandschwelle, Vorschlägen und Summe",
   "Hinweis am Warenkorb-Symbol nach dem Hinzufügen, mit Weg ins Panel oder zur Kasse",
   "Eigene Warenkorb-Seite für große Körbe",
   "Bottom-Sheet auf dem Telefon"
  ],
  "umsetzung": "Panel: <dialog class=\"korb\" aria-labelledby=\"korb-titel\" closedby=\"any\">, öffnen mit showModal(), Fokus auf die Überschrift (tabindex=-1). CSS: .korb { inset: 0 0 0 auto; margin: 0; width: min(440px, 100vw); height: 100dvh; translate: 100% 0; transition: translate .4s var(--kurve), overlay .4s allow-discrete, display .4s allow-discrete } .korb[open] { translate: 0 } @starting-style { .korb[open] { translate: 100% 0 } }; ::backdrop genauso mit Hintergrundfarbe. Ohne closedby den Klick mit e.target === dialog abfangen. html:has(.korb[open]) { overflow: hidden }.\n\nPositionen als Liste mit stabilen Schlüsseln. Menge und Summen an Ort und Stelle aktualisieren, nicht neu aufbauen, damit der Fokus bleibt. Entfernen: Zeile per grid-template-rows 1fr → 0fr zuklappen, an ihre Stelle eine Zeile „… entfernt. Rückgängig“ setzen, Fokus darauf, nach 6 s endgültig. Änderungen über eine aria-live-Region ansagen.\n\nVersandschwelle: Rest = Grenze − Zwischensumme; Text immer, Balken nur zur Unterstützung. „Passt dazu“ nur unter der Grenze zeigen.\n\nPreise mit Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }), Grundpreis je kg, MwSt. je Steuersatz aus den Bruttopreisen; Versandkosten anteilig. Im Shop nach jeder Änderung den Korb vom Server lesen (z. B. Shopify /cart/change.js) und daraus zeichnen. Preisangaben, Grundpreise und Steuersätze vom Kunden prüfen lassen.\n\nVariante Hinweis: <div popover=\"manual\"> am Warenkorb-Symbol, per Anchor Positioning (position-anchor, top: anchor(bottom)) mit festem Fallback; schließt nach 6 s, nicht bei Zeiger oder Fokus darin.",
  "mobil": "Panel in voller Breite, Summe und „Zur Kasse“ unten fest im Panel, Mengenknöpfe 44px. Der Hintergrund scrollt nicht mit.",
  "reduzierte_bewegung": "Panel und Hinweis blenden nur ein (150 ms Deckkraft). Balken, Zeilen und Zähler springen in den Endzustand.",
  "barrierefreiheit": "Natives <dialog> mit showModal(): Fokus bleibt im Panel, Hintergrund inert, Esc schließt, Fokus kehrt zum Auslöser zurück. Überschrift per aria-labelledby. Mengenknöpfe mit Namen („Menge erhöhen, Festungsberg“), Minus bei 1 deaktiviert. Hinzufügen, Mengen, Entfernen und Versandschwelle per aria-live ansagen; nach dem Entfernen Fokus auf „Rückgängig“, bei leerem Korb auf die Überschrift. Warenkorb-Symbol mit Anzahl im Namen. Schwelle als Text, Balken aria-hidden."
 },
 "messwerte": {
  "easing": "cubic-bezier(0.16, 1, 0.3, 1)",
  "dauer_ms": "400 Panel, 200 UI, 500 Versandbalken",
  "stagger_ms": "",
  "distanz": "Panel translateX 100 % → 0",
  "sonstiges": "Kurve und 400 ms aus dem Rezept gastro-warm-appetitlich (Menü/Overlay). Balken 500 ms nach GroPulse (~0,5 s). Bonanza nutzt für den Drawer cubic-bezier(.29,.63,.44,1).",
  "herkunft": "eigene Festlegung",
  "quelle": "Rezept gastro-warm-appetitlich; https://gropulse.com/?p=5238"
 },
 "kurve": {
  "bezier": [
   0.16,
   1.0,
   0.3,
   1.0
  ],
  "css": "cubic-bezier(0.16, 1, 0.3, 1)",
  "name": "expo.out",
  "dauer_ms": 400
 },
 "gesehen_bei": [
  {
   "seite": "Bonanza Coffee Roasters",
   "url": "https://www.bonanzacoffee.de/",
   "auszeichnung": "",
   "beobachtung": "Shopify-Shop einer Berliner Rösterei. Drawer mit transform/visibility-Übergang, Kurve cubic-bezier(.29,.63,.44,1) mit var(--duration-default), in components-sections.css (Rezept-Recherche 31.10.2026, per curl geprüft).",
   "art": "website"
  },
  {
   "seite": "Shopify Dawn, Pull Request „Cart drawer“",
   "url": "https://github.com/Shopify/dawn/pull/1544/files/0ad82025e10953322d98ac556b1c37b776148605",
   "auszeichnung": "",
   "beobachtung": "Warenkorb-Typ wählbar als Drawer, Seite oder Hinweis. Fokus wird nach transitionend im Drawer gefangen, Esc schließt, Status- und Fehlertexte werden angesagt, Leerzustand mit Kollektion (Mai 2022).",
   "art": "website"
  },
  {
   "seite": "freeCodeCamp: Shopify AJAX Cart Drawer",
   "url": "https://www.freecodecamp.org/news/shopify-ajax-cart-drawer/",
   "auszeichnung": "",
   "beobachtung": "Nach jeder Änderung den Warenkorb neu vom Server lesen statt lokal zu zählen; Mengen über /cart/change.js mit dem Schlüssel der Position, Entfernen = Menge 0 (Juli 2026).",
   "art": "tutorial"
  },
  {
   "seite": "GroPulse: Free Shipping Bar",
   "url": "https://gropulse.com/?p=5238",
   "auszeichnung": "",
   "beobachtung": "Fortschritt von links nach rechts füllen, Änderung etwa 0,5 s weich animieren, unter der Grenze den Restbetrag nennen („Add $12 more …“), beim Erreichen Erfolgsfarbe und kurze Bestätigung.",
   "art": "artikel"
  }
 ],
 "tutorials": [
  {
   "titel": "freeCodeCamp: How to Build an AJAX Cart Drawer in Shopify",
   "url": "https://www.freecodecamp.org/news/shopify-ajax-cart-drawer/"
  }
 ],
 "kombinierbar_mit": [
  "in-den-warenkorb-flug",
  "toast-stapel",
  "druck-feedback",
  "einblenden-starting-style"
 ],
 "urls": {
  "detail": "https://cecodesign.de/lab/fundus/sektionen/warenkorb-drawer/",
  "meta": "https://cecodesign.de/lab/fundus/sektionen/warenkorb-drawer/meta.json",
  "demo": "https://cecodesign.de/lab/fundus/sektionen/warenkorb-drawer/demo"
 },
 "herkunft": {
  "quelldateien": [
   "DEF-Recherche 01.11.2026"
  ],
  "pruefnotizen": [
   "Dawn-PR, freeCodeCamp und GroPulse am 01.11.2026 per WebFetch gelesen; Bonanza aus der Rezept-Recherche (direkter Abruf am 01.11.2026 vom Proxy blockiert).",
   "Preisangaben (Grundpreis, MwSt., Versand) sind Beispiel; Pflichtangaben vom Kunden prüfen lassen."
  ]
 },
 "bau": {
  "status": "fertig",
  "kurz": "Mini-Warenkorb als natives dialog-Panel von rechts: Mengenwahl, Entfernen mit Rückgängig, Versandschwelle als Fortschritt, Leerzustand und Summe inkl. MwSt.",
  "demo": true,
  "abhaengigkeiten": [],
  "werte": {
   "easing": "cubic-bezier(0.16, 1, 0.3, 1) (expo.out, Rezept gastro-warm-appetitlich)",
   "dauer_ms": {
    "panel": 400,
    "backdrop": 400,
    "ui": 200,
    "versandbalken": 500,
    "zeile_zuklappen": 400,
    "zeile_herein": 400,
    "zaehler_hupf": 400,
    "hinweis": "200 Deckkraft, 400 Weg",
    "rueckgaengig_frist": 6000
   },
   "distanz": "Panel translateX 100 % → 0; neue Zeile translateX 24px → 0; Hinweis translateY −8px → 0",
   "panel": "dialog mit showModal(), inset: 0 0 0 auto, width min(440px, 100vw), height 100dvh; transition translate, overlay und display allow-discrete, @starting-style; closedby=\"any\" plus Klick auf das dialog-Element als Fallback; Backdrop rgba(41,62,58,.42)",
   "zuklappen": "Zeile als Grid, grid-template-rows 1fr → 0fr und Deckkraft 1 → 0; an ihrer Stelle „… entfernt. Rückgängig“ mit 3px-Ablaufbalken (6 s linear), Fokus auf Rückgängig",
   "versandschwelle": "Grenze 39,00 €, Versand 4,90 €; Balken 8px, Spur #e2d9c2, Füllung Salbei #7f937a, bei Erreichen #293e3a und Fläche #e3ead8 mit Haken",
   "preise": "Intl.NumberFormat de-DE EUR; Grundpreis je kg; MwSt. je Satz (7 % Kaffee, 19 % Zubehör) aus Bruttopreisen, Versand anteilig nach Warenwert",
   "start": "Festungsberg 13,90 € + Rheinlauf 16,70 € = 30,60 € → „Noch 8,40 € bis versandkostenfrei“",
   "menge": "1 bis 10, Knöpfe 40px (Touch 44px), Minus bei 1 gesperrt",
   "farben": "Grund #f0ece0, Panel #fbf8f1, Text #353839, Salbei #7f937a, Dunkelgrün #293e3a (Kasse), Senf #eac16e (Zähler), Linien #ddd5c2/#c9bfa6",
   "schriften": "Fraunces (SOFT 80–100) für Titel und Summe, Commissioner für Text, Labels mit FLAR 80 und 0.15em",
   "vorfuehrung": "Zeiger 620 ms in eigener Top-Layer-Ebene (popover) über dem Panel; Korb öffnen, Filterpapier aus „Passt dazu“, Festungsberg +1 (Schwelle erreicht), Rheinlauf entfernen, Rückgängig, alles entfernen (Leerzustand), schließen",
   "kopf": "sticky, top 0, Hintergrund rgba(240, 236, 224, .94) mit backdrop-filter blur(8px); hält den Anker des Hinweises im Bild"
  },
  "ausloeser": "klick",
  "touch": "Panel in voller Bildschirmbreite, Summe und „Zur Kasse“ unten im Panel, Vorschläge einspaltig, Mengenknöpfe 44px. Kaufen-Knopf heißt „In den Korb“. Hover-Effekte entfallen.",
  "reduziert": "Panel und Hinweis blenden nur ein (Deckkraft 150 ms) statt zu fahren. Versandbalken, Zeilen und Zähler springen in den Endzustand. Der Ablaufbalken von Rückgängig bleibt als Zeitanzeige.",
  "korrekturen": {
   "beschreibung": "Ein Mini-Warenkorb, der als Panel von rechts hereinfährt, statt auf eine eigene Seite zu wechseln. Er ist ein modaler <dialog>: Der Fokus geht hinein, Esc und ein Klick daneben schließen, danach steht der Fokus wieder am Auslöser. Im Panel stehen die Artikel mit Bild, Sorte, Grundpreis, Mengenwahl und „Entfernen“ mit Rückgängig. Oben zeigt ein Fortschritt den Weg zur Versandkostengrenze („Noch 8,40 € bis versandkostenfrei“), unten stehen Zwischensumme, Versand, Summe inklusive Mehrwertsteuer und der Weg zur Kasse. Ist der Korb leer, zeigt er einen Leerzustand mit zwei Vorschlägen.",
   "warum_es_wirkt": "Wer etwas in den Korb legt, bleibt auf der Seite und sieht sofort, was drin ist und was es kostet. Die Versandgrenze gibt einen klaren, ehrlichen Anreiz. Rückgängig nimmt die Angst vor dem falschen Klick.",
   "wann_einsetzen": "Kleine Shops von Rösterei, Hofladen, Weingut, Manufaktur, Mode- und D2C-Marken. Immer wenn Kunden mehrere Artikel kombinieren. Die Variante „Hinweis am Symbol“, wenn Kunden meist weiter stöbern und das Panel stören würde.",
   "wann_nicht": "Bei Einzelkäufen mit Konfiguration (Möbel nach Maß, Reisen), auf die direkt der Checkout folgt. Wenn das Shopsystem einen eigenen Warenkorb erzwingt, der sich nicht anpassen lässt.",
   "varianten": [
    "Seitenpanel (Drawer) mit Mengenwahl, Versandschwelle, Vorschlägen und Summe",
    "Hinweis am Warenkorb-Symbol nach dem Hinzufügen, mit Weg ins Panel oder zur Kasse",
    "Eigene Warenkorb-Seite für große Körbe",
    "Bottom-Sheet auf dem Telefon"
   ],
   "umsetzung": "Panel: <dialog class=\"korb\" aria-labelledby=\"korb-titel\" closedby=\"any\">, öffnen mit showModal(), Fokus auf die Überschrift (tabindex=-1). CSS: .korb { inset: 0 0 0 auto; margin: 0; width: min(440px, 100vw); height: 100dvh; translate: 100% 0; transition: translate .4s var(--kurve), overlay .4s allow-discrete, display .4s allow-discrete } .korb[open] { translate: 0 } @starting-style { .korb[open] { translate: 100% 0 } }; ::backdrop genauso mit Hintergrundfarbe. Ohne closedby den Klick mit e.target === dialog abfangen. html:has(.korb[open]) { overflow: hidden }.\n\nPositionen als Liste mit stabilen Schlüsseln. Menge und Summen an Ort und Stelle aktualisieren, nicht neu aufbauen, damit der Fokus bleibt. Entfernen: Zeile per grid-template-rows 1fr → 0fr zuklappen, an ihre Stelle eine Zeile „… entfernt. Rückgängig“ setzen, Fokus darauf, nach 6 s endgültig. Änderungen über eine aria-live-Region ansagen.\n\nVersandschwelle: Rest = Grenze − Zwischensumme; Text immer, Balken nur zur Unterstützung. „Passt dazu“ nur unter der Grenze zeigen.\n\nPreise mit Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }), Grundpreis je kg, MwSt. je Steuersatz aus den Bruttopreisen; Versandkosten anteilig. Im Shop nach jeder Änderung den Korb vom Server lesen (z. B. Shopify /cart/change.js) und daraus zeichnen. Preisangaben, Grundpreise und Steuersätze vom Kunden prüfen lassen.\n\nVariante Hinweis: <div popover=\"manual\"> am Warenkorb-Symbol, per Anchor Positioning (position-anchor, top: anchor(bottom)) mit festem Fallback; schließt nach 6 s, nicht bei Zeiger oder Fokus darin.",
   "mobil": "Panel in voller Breite, Summe und „Zur Kasse“ unten fest im Panel, Mengenknöpfe 44px. Der Hintergrund scrollt nicht mit.",
   "reduzierte_bewegung": "Panel und Hinweis blenden nur ein (150 ms Deckkraft). Balken, Zeilen und Zähler springen in den Endzustand.",
   "barrierefreiheit": "Natives <dialog> mit showModal(): Fokus bleibt im Panel, Hintergrund inert, Esc schließt, Fokus kehrt zum Auslöser zurück. Überschrift per aria-labelledby. Mengenknöpfe mit Namen („Menge erhöhen, Festungsberg“), Minus bei 1 deaktiviert. Hinzufügen, Mengen, Entfernen und Versandschwelle per aria-live ansagen; nach dem Entfernen Fokus auf „Rückgängig“, bei leerem Korb auf die Überschrift. Warenkorb-Symbol mit Anzahl im Namen. Schwelle als Text, Balken aria-hidden."
  },
  "geprueft": "02.11.2026",
  "notiz": "Welt: Rezept gastro-warm-appetitlich (Rösterei-Shop), erfundene Rösterei Moselbohne mit Hofladen in Koblenz-Güls. Fraunces und Commissioner, Papier #f0ece0, Salbei, Dunkelgrün, Senf. Eigene SVG-Kaffeetüten (Kraftpapier mit Etikett), Handfilter und Filterpapier. Sorten erfunden (Festungsberg, Rheinlauf, Moselschleife, Spätlese). Zustände: unter der Grenze, Grenze erreicht, Zeile entfernt mit Rückgängig, leer mit Vorschlägen; Kasse nicht verbunden (Ansage). Varianten Seitenpanel und Hinweis am Symbol (Popover mit Anchor Positioning). Der Zeiger der Vorführung liegt in einer eigenen Popover-Ebene, damit er über dem modalen Panel sichtbar bleibt. Referenzen: bonanzacoffee.de, github.com/Shopify/dawn, freecodecamp.org, gropulse.com. AD-Review 02.11.2026: Note 4. Kopfzeile sticky, damit der Hinweis am Warenkorb-Symbol auch nach dem Scrollen sichtbar ist (vorher in der Vorschau außerhalb des Bildes).",
  "varianten": [
   {
    "id": "drawer",
    "name": "Seitenpanel",
    "wann": "Shops, in denen Kunden mehrere Artikel kombinieren (Rösterei, Hofladen, Weingut).",
    "werte": {
     "nach_hinzufuegen": "Panel öffnet sofort"
    }
   },
   {
    "id": "hinweis",
    "name": "Hinweis am Symbol",
    "wann": "Sortimente zum Stöbern (Mode, Geschenke), in denen ein Panel nach jedem Klick stören würde.",
    "werte": {
     "nach_hinzufuegen": "Popover am Warenkorb-Symbol mit Artikel, Versandschwelle, „Warenkorb (n)“ und „Zur Kasse“",
     "position": "position-anchor: --korb; top: calc(anchor(bottom) + 10px); right: anchor(right); Fallback fixed top 84px right 16px",
     "breite": "min(360px, 100vw − 24px)",
     "schliessen": "nach 6000 ms, nicht bei Zeiger oder Fokus darin; Esc"
    }
   }
  ]
 }
}
