From e19827d6af99ccdea911fd3e69de8214e5718d2f Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Wed, 30 Sep 2026 23:45:54 +0200 Subject: [PATCH] feat(cart): honey glow and drip when the drop lands in the cart After the flying honey drop reaches the header cart icon, the basket now lights up twice in honey colour with a warm halo, and a small honey drop gathers at its lower edge, stretches and runs down. With reduced motion only a single calm glow remains, without the drip. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/components/cart-landing-effect.tsx | 119 +++++++++++++++++++++++++ src/components/site-chrome.tsx | 5 +- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 src/components/cart-landing-effect.tsx diff --git a/src/components/cart-landing-effect.tsx b/src/components/cart-landing-effect.tsx new file mode 100644 index 0000000..9d6eadf --- /dev/null +++ b/src/components/cart-landing-effect.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { useEffect } from "react"; +import { motion, useAnimationControls, useReducedMotion } from "motion/react"; +import { onCartLanded } from "@/components/cart-flight"; + +// Der Korb im Kopfzeilen-Symbol (20 px in einem 40-px-Knopf, viewBox 24): +// seine Unterkante liegt bei y=15, die Mitte bei x=13.5. Dort bildet sich +// der Tropfen — umgerechnet in Pixel des Knopfes. +const DRIP_LEFT_PX = 10 + (13.5 / 24) * 20; +const DRIP_TOP_PX = 10 + (15 / 24) * 20; +const DRIP_WIDTH_PX = 6; +const DRIP_DELAY_S = 0.3; + +/** + * Nachspiel der Landung eines Honigtropfens (components/cart-flight.tsx): + * Der Korb leuchtet zweimal warm in Honigfarbe auf, dann sammelt sich an + * seiner Unterkante ein kleiner Tropfen, zieht sich lang und perlt nach + * unten ab — als waere der Honig durch den Korb gesickert. + * + * Liegt als Overlay im Warenkorb-Knopf (relative) und faengt keine Klicks. + * Das Leuchten ist eine eigene, honigfarbene Kopie des Korbs, deren Deckkraft + * blinkt — so stimmt die Farbe im hellen wie im dunklen Farbschema, ohne die + * Strichfarbe des eigentlichen Symbols anzufassen. Bei reduzierter Bewegung + * bleibt nur ein einzelnes, ruhiges Aufleuchten ohne Tropfen. + */ +export function CartLandingEffect() { + const glowControls = useAnimationControls(); + const dripControls = useAnimationControls(); + const reduceMotion = useReducedMotion(); + + useEffect( + () => + onCartLanded(() => { + if (reduceMotion) { + void glowControls.start({ + opacity: [0, 0.9, 0], + transition: { duration: 0.8, ease: "easeInOut" }, + }); + return; + } + + void glowControls.start({ + opacity: [0, 1, 0.35, 1, 0], + scale: [0.9, 1.08, 1, 1.05, 1], + transition: { duration: 0.75, ease: "easeInOut", times: [0, 0.2, 0.45, 0.65, 1] }, + }); + void dripControls.start({ + // An der Kante sammeln, schwer werden und lang ziehen, + // abloesen und nach unten perlen, verblassen. + opacity: [0, 1, 1, 1, 0], + scaleX: [0.3, 1, 0.85, 0.9, 0.7], + scaleY: [0.2, 1, 1.35, 1, 0.8], + y: [0, 0, 3, 13, 17], + transition: { + duration: 1.1, + delay: DRIP_DELAY_S, + ease: "easeIn", + times: [0, 0.3, 0.55, 0.85, 1], + }, + }); + }), + [glowControls, dripControls, reduceMotion] + ); + + return ( + + ); +} diff --git a/src/components/site-chrome.tsx b/src/components/site-chrome.tsx index 4f0f6e1..a61a36f 100644 --- a/src/components/site-chrome.tsx +++ b/src/components/site-chrome.tsx @@ -15,6 +15,7 @@ import { QuantityStepper } from "@/components/quantity-stepper"; import { LanguageSwitcher } from "@/components/language-switcher"; import { useI18n } from "@/i18n/i18n-provider"; import { CartFlight, onCartLanded } from "@/components/cart-flight"; +import { CartLandingEffect } from "@/components/cart-landing-effect"; import { HexMark } from "@/components/honeycomb"; import type { Dictionary } from "@/i18n/dictionary-type"; import { ThemeToggle } from "@/components/theme-toggle"; @@ -285,7 +286,8 @@ function CartIconButton() { const basketControls = useAnimationControls(); // Landet ein Honigtropfen (components/cart-flight.tsx), faengt der Korb - // ihn sichtbar auf: kurzes Einsinken und Nachwippen. + // ihn sichtbar auf: kurzes Einsinken und Nachwippen. Leuchten und + // abperlender Tropfen kommen aus CartLandingEffect. useEffect( () => onCartLanded(() => { @@ -319,6 +321,7 @@ function CartIconButton() { + {count > 0 ? (