diff --git a/parfum-shop/src/App.jsx b/parfum-shop/src/App.jsx index 82ea10d..a1b763c 100644 --- a/parfum-shop/src/App.jsx +++ b/parfum-shop/src/App.jsx @@ -1,3 +1,4 @@ +import { useRef } from "react"; import { Routes, Route, useLocation } from "react-router"; import LandingPage from "./pages/LandingPage"; import ProductDetailPage from "./components/ProductDetailPage"; @@ -12,26 +13,33 @@ import SupportChatbot from "./components/SupportChatbot"; import ScrollToTop from "./components/ScrollToTop"; import ShopDrawer from "./components/ShopDrawer"; import CartToast from "./components/CartToast"; +import useScrollTextReveal from "./hooks/useScrollTextReveal"; +import "./style/textReveal.css"; function App() { const location = useLocation(); + const routeContentRef = useRef(null); const shouldFlushFooter = location.pathname === "/" || location.pathname.startsWith("/duft/"); + useScrollTextReveal(routeContentRef, [location.pathname]); + return ( <> - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
diff --git a/parfum-shop/src/components/ProductDetailPage.jsx b/parfum-shop/src/components/ProductDetailPage.jsx index 2af906c..dd50a6d 100644 --- a/parfum-shop/src/components/ProductDetailPage.jsx +++ b/parfum-shop/src/components/ProductDetailPage.jsx @@ -247,11 +247,13 @@ function ProductDetailContent({ perfumeSlug }) {
-
+
- Edition 04 -

{perfume.name}

-

{perfume.shortText}

+ + Edition 04 + +

{perfume.name}

+

{perfume.shortText}

@@ -282,8 +284,8 @@ function ProductDetailContent({ perfumeSlug }) {
-
-
+
+
Discovery Set wird einmalig angerechnet

Nur das erste Discovery Set erzeugt CHF 48 Guthaben. Es wird @@ -297,7 +299,7 @@ function ProductDetailContent({ perfumeSlug }) { )}

- + Zum Set
@@ -468,15 +470,15 @@ function ProductDetailContent({ perfumeSlug }) {
-
-

Lieber erst testen?

-

+

+

Lieber erst testen?

+

Bestelle ein 2ml Sample für CHF 12 oder das komplette Discovery Set mit allen 6 Düften für CHF 48. Beide werden beim späteren Full-Size-Kauf vollständig angerechnet.

-
+
-
+
- DISCOVERY SET -

+ + DISCOVERY SET + +

DER SICHERSTE EINSTIEG
IN DIE WELT DER NISCHEN- @@ -42,12 +44,12 @@ function DiscoverySetPage() { DÜFTE

-

+

6 Düfte × 2ml. Jeden Duft eine Woche tragen. Verstehen, was wirklich funktioniert. Ohne Risiko.

-
+
@@ -93,7 +95,7 @@ function DiscoverySetPage() {
-
+
@@ -110,10 +112,12 @@ function DiscoverySetPage() {
-
+
- IM SET ENTHALTEN -

ALLE 6 SIGNATURE-DÜFTE ZUM TESTEN.

+ + IM SET ENTHALTEN + +

ALLE 6 SIGNATURE-DÜFTE ZUM TESTEN.

@@ -139,11 +143,11 @@ function DiscoverySetPage() {
-
-

So funktioniert's

+
+

So funktioniert's

-
+
1

Bestellen

@@ -152,7 +156,7 @@ function DiscoverySetPage() {

-
+
2

Testen

@@ -161,7 +165,7 @@ function DiscoverySetPage() {

-
+
3

Entscheiden

@@ -173,11 +177,13 @@ function DiscoverySetPage() {

-
+
- WARUM DISCOVERY SET -

DER KLÜGERE EINSTIEG IN NISCHENDÜFTE.

-

+ + WARUM DISCOVERY SET + +

DER KLÜGERE EINSTIEG IN NISCHENDÜFTE.

+

Nischen-Parfums sind keine Impulskäufe. Sie brauchen Zeit, um zu verstehen, wie sie auf deiner Haut funktionieren, wie sie sich im Alltag entwickeln und ob sie wirklich zu dir passen. @@ -185,7 +191,7 @@ function DiscoverySetPage() {

-
+
×

Traditioneller Weg

@@ -197,7 +203,10 @@ function DiscoverySetPage() {

-
+

Discovery Set Weg

@@ -210,7 +219,7 @@ function DiscoverySetPage() {
-
+
diff --git a/parfum-shop/src/pages/ImpressumPage.jsx b/parfum-shop/src/pages/ImpressumPage.jsx index ca5a719..de927b3 100644 --- a/parfum-shop/src/pages/ImpressumPage.jsx +++ b/parfum-shop/src/pages/ImpressumPage.jsx @@ -7,18 +7,20 @@ function ImpressumPage() {
-
- RECHTLICHE ANGABEN -

IMPRESSUM

-

+

+ + RECHTLICHE ANGABEN + +

IMPRESSUM

+

Dieses Impressum enthält die rechtlichen Angaben zu atmos sowie Informationen zur Verantwortlichkeit, Erreichbarkeit und zu den veröffentlichten Inhalten dieser Website.

-
-
+
+
ANBIETER

Unternehmen

@@ -32,7 +34,7 @@ function ImpressumPage() {

-
+
KONTAKT

Erreichbarkeit

@@ -42,7 +44,7 @@ function ImpressumPage() {

-
+
VERTRETUNGSBERECHTIGT

Geschäftsführung

@@ -52,7 +54,7 @@ function ImpressumPage() {

-
+
HANDELSREGISTER

Registereintrag

@@ -63,13 +65,15 @@ function ImpressumPage() {

-
+
- INHALTLICHE VERANTWORTUNG -

VERANTWORTLICH FÜR DEN INHALT

+ + INHALTLICHE VERANTWORTUNG + +

VERANTWORTLICH FÜR DEN INHALT

-
+

Verantwortlich für diese Website und die publizierten Inhalte:

@@ -85,13 +89,15 @@ function ImpressumPage() {
-
+
- HAFTUNG -

HAFTUNGSAUSSCHLUSS

+ + HAFTUNG + +

HAFTUNGSAUSSCHLUSS

-
+

Trotz sorgfältiger inhaltlicher Kontrolle übernimmt atmos keine Gewähr für die Aktualität, Richtigkeit, Vollständigkeit oder @@ -105,13 +111,15 @@ function ImpressumPage() {

-
+
- URHEBERRECHT -

URHEBER- UND MARKENRECHTE

+ + URHEBERRECHT + +

URHEBER- UND MARKENRECHTE

-
+

Sämtliche Inhalte dieser Website, einschliesslich Texte, Bilder, Gestaltungselemente, Logos und Marken, sind urheberrechtlich @@ -123,13 +131,15 @@ function ImpressumPage() {

-
+
- TRANSPARENZ -

KLARE ANGABEN UND ERREICHBARKEIT

+ + TRANSPARENZ + +

KLARE ANGABEN UND ERREICHBARKEIT

-
+

atmos legt Wert auf eine klare, transparente und nachvollziehbare Kommunikation. Dieses Impressum dient der eindeutigen diff --git a/parfum-shop/src/pages/LandingPage.css b/parfum-shop/src/pages/LandingPage.css index 1e170b3..d874dae 100644 --- a/parfum-shop/src/pages/LandingPage.css +++ b/parfum-shop/src/pages/LandingPage.css @@ -43,6 +43,7 @@ display: block; object-fit: cover; object-position: center; + will-change: transform; } .hero .navbar--hero { @@ -54,21 +55,6 @@ padding-top: 0; } -.hero-brand { - position: absolute; - top: 22px; - left: clamp(1rem, 1.45vw, 20px); - z-index: 14; - display: inline-flex; - align-items: center; -} - -.hero-brand__logo { - display: block; - width: clamp(74px, 8vw, 112px); - height: auto; -} - .hero-content { position: relative; z-index: 6; @@ -92,7 +78,13 @@ .hero-title-line { display: block; - will-change: transform, opacity; + overflow: hidden; + padding-bottom: 0.08em; + margin-bottom: -0.08em; +} + +.hero-title-line .reveal-line { + will-change: transform; } .hero-title-line + .hero-title-line { @@ -446,6 +438,7 @@ height: 100%; object-fit: cover; display: block; + will-change: transform; } /* RESPONSIVE */ @@ -475,10 +468,6 @@ } @media (max-width: 640px) { - .hero-brand { - top: 14px; - } - .hero .navbar--hero { top: 14px; } diff --git a/parfum-shop/src/pages/LandingPage.jsx b/parfum-shop/src/pages/LandingPage.jsx index 0d83411..9e47436 100644 --- a/parfum-shop/src/pages/LandingPage.jsx +++ b/parfum-shop/src/pages/LandingPage.jsx @@ -7,6 +7,7 @@ import { } from "react"; import { Link } from "react-router"; import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; import HeroSection from "../components/landing/HeroSection"; import perfumes from "../data/perfumes"; import "../pages/LandingPage.css"; @@ -14,11 +15,15 @@ import "../style/navbar.css"; const INTRO_SESSION_KEY = "atmos-landing-intro-played"; +gsap.registerPlugin(ScrollTrigger); + function LandingPage() { const pageRef = useRef(null); const overlayRef = useRef(null); const overlayTextRef = useRef(null); const heroImageWrapRef = useRef(null); + const heroImageRef = useRef(null); + const discoveryImageRef = useRef(null); const headlineLineRefs = useRef([]); const heroMetaRefs = useRef([]); const cardRefs = useRef([]); @@ -62,9 +67,8 @@ function LandingPage() { const heroImageWrap = heroImageWrapRef.current; const headlineLines = headlineLineRefs.current.filter(Boolean); const heroMeta = heroMetaRefs.current.filter(Boolean); - const revealTargets = [...headlineLines, ...heroMeta]; - if (!overlay || !overlayText || !heroImageWrap || revealTargets.length === 0) { + if (!overlay || !overlayText || !heroImageWrap || headlineLines.length === 0) { return undefined; } @@ -74,14 +78,22 @@ function LandingPage() { transformOrigin: "center center", }); - gsap.set(revealTargets, { - y: 56, + gsap.set(headlineLines, { + yPercent: 115, + rotate: 2.2, + transformOrigin: "0% 100%", + force3D: true, + }); + + gsap.set(heroMeta, { + y: 36, autoAlpha: 0, }); if (!shouldPlayIntro) { gsap.set(heroImageWrap, { scale: 1 }); - gsap.set(revealTargets, { y: 0, autoAlpha: 1 }); + gsap.set(headlineLines, { yPercent: 0, rotate: 0 }); + gsap.set(heroMeta, { y: 0, autoAlpha: 1 }); gsap.set(overlay, { yPercent: -100, autoAlpha: 0, @@ -130,11 +142,11 @@ function LandingPage() { .to( headlineLines, { - y: 0, - autoAlpha: 1, - duration: 1.04, - stagger: 0.16, - ease: "power3.out", + yPercent: 0, + rotate: 0, + duration: 1.18, + stagger: 0.1, + ease: "power4.out", }, "<0.27" ) @@ -143,11 +155,11 @@ function LandingPage() { { y: 0, autoAlpha: 1, - duration: 0.98, - stagger: 0.14, - ease: "power3.out", + duration: 1.02, + stagger: 0.12, + ease: "power4.out", }, - "<0.2" + "<0.16" ) .set(overlay, { display: "none" }); }, pageRef); @@ -157,6 +169,69 @@ function LandingPage() { }; }, [shouldPlayIntro]); + useLayoutEffect(() => { + const heroImageWrap = heroImageWrapRef.current; + const heroImage = heroImageRef.current; + const discoveryImage = discoveryImageRef.current; + + if (!heroImageWrap || !heroImage || !discoveryImage || typeof window === "undefined") { + return undefined; + } + + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { + return undefined; + } + + const heroSection = heroImageWrap.closest(".hero"); + const discoveryBanner = discoveryImage.closest(".discovery-banner"); + + if (!heroSection || !discoveryBanner) { + return undefined; + } + + const ctx = gsap.context(() => { + gsap.set(heroImage, { + scale: 1.14, + yPercent: -4, + transformOrigin: "center center", + force3D: true, + }); + + gsap.to(heroImage, { + yPercent: 8, + ease: "none", + scrollTrigger: { + trigger: heroSection, + start: "top top", + end: "bottom top", + scrub: 1.15, + }, + }); + + gsap.set(discoveryImage, { + scale: 1.16, + yPercent: -8, + transformOrigin: "center center", + force3D: true, + }); + + gsap.to(discoveryImage, { + yPercent: 10, + ease: "none", + scrollTrigger: { + trigger: discoveryBanner, + start: "top bottom", + end: "bottom top", + scrub: 1.2, + }, + }); + }, pageRef); + + return () => { + ctx.revert(); + }; + }, []); + useEffect(() => { const cards = cardRefs.current.filter(Boolean); const cardStates = cards @@ -302,6 +377,7 @@ function LandingPage() {

-
+
-

+

{"W\u00C4HLE EINE"}
{"ATMOSPH\u00C4RE"} @@ -368,27 +444,37 @@ function LandingPage() {

-
+
-

+

DER SICHERE EINSTIEG
DISCOVERY SET

-

+

{"Alle 6 D\u00FCfte als 2ml Samples."}
Jeden Duft eine Woche tragen.
Verstehen, was funktioniert.

- + Discovery Set bestellen
- Discovery Set + Discovery Set
diff --git a/parfum-shop/src/pages/SmallBatchPage.jsx b/parfum-shop/src/pages/SmallBatchPage.jsx index 061167e..13c0a58 100644 --- a/parfum-shop/src/pages/SmallBatchPage.jsx +++ b/parfum-shop/src/pages/SmallBatchPage.jsx @@ -63,30 +63,36 @@ function SmallBatchPage() {
-
- SMALL BATCH / ARCHIVE / PROTOTYPE -

EARLY ACCESS

-

+

+ + SMALL BATCH / ARCHIVE / PROTOTYPE + +

EARLY ACCESS

+

Limited releases are reserved for customers with enough purchase history to understand the atmos material language.

{!user ? ( -
- LOGIN REQUIRED -

Sign in to check access.

-

Small Batch access is calculated from your completed orders.

+
+ + LOGIN REQUIRED + +

Sign in to check access.

+

Small Batch access is calculated from your completed orders.

) : ( <> -
- ACCESS STATUS -

{loyalty.unlocked ? "Unlocked" : "Locked"}

-
+
+ + ACCESS STATUS + +

{loyalty.unlocked ? "Unlocked" : "Locked"}

+
= 3}> @@ -102,9 +108,9 @@ function SmallBatchPage() { {state.loading &&

Loading access...

} {loyalty.unlocked && ( -
+
{state.releases.map((release) => ( -
+
{release.type}

{release.name}

{release.note}

diff --git a/parfum-shop/src/pages/SupportPage.jsx b/parfum-shop/src/pages/SupportPage.jsx index 565c848..fae656c 100644 --- a/parfum-shop/src/pages/SupportPage.jsx +++ b/parfum-shop/src/pages/SupportPage.jsx @@ -1,3 +1,4 @@ +import { Link } from "react-router"; import SharedNavbar from "../components/SharedNavbar"; import "./SupportPage.css"; @@ -7,10 +8,12 @@ function SupportPage() {
-
+
- SUPPORT -

+ + SUPPORT + +

WIR HELFEN
KLAR, DIREKT @@ -19,7 +22,7 @@ function SupportPage() {
UMWEGE

-

+

Wenn du Fragen zu Bestellung, Versand, Discovery Set, Produkten oder deiner Auswahl hast, ist das Support-Team von atmos für dich da. Präzise, persönlich und mit dem Anspruch, jedes Anliegen sorgfältig @@ -27,7 +30,7 @@ function SupportPage() {

-
+
KONTAKT

Für Anliegen rund um Bestellung, Produkte, Versand und allgemeine @@ -51,8 +54,8 @@ function SupportPage() {

-
-
+
+
BESTELLUNG

Fragen zu einer laufenden Bestellung

@@ -61,7 +64,7 @@ function SupportPage() {

-
+
VERSAND

Lieferung und Zustellung

@@ -70,7 +73,7 @@ function SupportPage() {

-
+
PRODUKTE

Beratung zu Duft, Sample oder Discovery Set

@@ -80,13 +83,15 @@ function SupportPage() {

-
+
- SO ERREICHST DU UNS -

EIN GUTER SUPPORT BEGINNT MIT EINER KLAREN ANFRAGE.

+ + SO ERREICHST DU UNS + +

EIN GUTER SUPPORT BEGINNT MIT EINER KLAREN ANFRAGE.

-
+

Damit wir dein Anliegen möglichst schnell bearbeiten können, hilft es, wenn du uns in deiner Nachricht die wichtigsten Informationen direkt @@ -102,8 +107,8 @@ function SupportPage() {

-
-
+
+
AM BESTEN MITGEBEN
  • Bestellnummer, falls bereits bestellt wurde
  • @@ -113,7 +118,7 @@ function SupportPage() {
-
+
KONTAKT

support@atmos.ch

@@ -126,14 +131,16 @@ function SupportPage() {

-
+
- HÄUFIGE FRAGEN -

DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.

+ + HÄUFIGE FRAGEN + +

DIE WICHTIGSTEN THEMEN AUF EINEN BLICK.

-
+

Wie lange dauert der Versand?

Bestellungen werden in der Regel innerhalb von 1–2 Werktagen @@ -142,7 +149,7 @@ function SupportPage() {

-
+

Kann ich zuerst testen?

Ja. Dafür ist das Discovery Set oder ein einzelnes Sample gedacht. @@ -151,7 +158,7 @@ function SupportPage() {

-
+

Ich bin unsicher, welcher Duft zu mir passt.

Schreib uns kurz, welche Duftcharaktere, Materialien oder Stimmungen @@ -159,7 +166,7 @@ function SupportPage() {

-
+

Was tun bei einem Problem mit der Bestellung?

Kontaktiere uns direkt mit deiner Bestellnummer und einer kurzen @@ -170,17 +177,21 @@ function SupportPage() {

-
+
- atmos SUPPORT -

NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE.

-

+ + atmos SUPPORT + +

+ NICHT AUTOMATISIERT AUF DISTANZ. SONDERN PERSÖNLICH UND PRÄZISE. +

+

Wir möchten, dass sich auch der Service so anfühlt wie die Marke selbst: klar, hochwertig und sorgfältig.

-
+
E-Mail senden diff --git a/parfum-shop/src/style/navbar.css b/parfum-shop/src/style/navbar.css index ed4a86d..9dc546e 100644 --- a/parfum-shop/src/style/navbar.css +++ b/parfum-shop/src/style/navbar.css @@ -27,6 +27,16 @@ transition: 0.2s ease; } +.nav-link--brand { + padding: 8px 12px; +} + +.nav-brand-logo { + display: block; + width: clamp(56px, 5.4vw, 78px); + height: auto; +} + .nav-button { border: none; font-family: inherit; @@ -90,4 +100,12 @@ padding: 8px 10px; font-size: 12px; } + + .nav-link--brand { + padding: 8px 10px; + } + + .nav-brand-logo { + width: 54px; + } }