Compare commits
3 Commits
landingpag
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
414967441a | ||
| 0b5d248c4e | |||
| b6e57ba551 |
12
parfum-shop/package-lock.json
generated
@ -59,7 +59,6 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@ -850,7 +849,6 @@
|
|||||||
"integrity": "sha512-q9pE8+47bQNHb5eWVcE6oXppA+JTSwvnrhH53m0ZuHuK5MLvwsLoWrWzBTFQqQ06BVxz1gp0HblLsch8o6pvZw==",
|
"integrity": "sha512-q9pE8+47bQNHb5eWVcE6oXppA+JTSwvnrhH53m0ZuHuK5MLvwsLoWrWzBTFQqQ06BVxz1gp0HblLsch8o6pvZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^4.0.3"
|
"picomatch": "^4.0.3"
|
||||||
},
|
},
|
||||||
@ -914,7 +912,6 @@
|
|||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@ -961,7 +958,6 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@ -1025,7 +1021,6 @@
|
|||||||
"integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
|
"integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.26.0"
|
||||||
}
|
}
|
||||||
@ -1081,7 +1076,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@ -1284,7 +1278,6 @@
|
|||||||
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@ -2299,7 +2292,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -2309,7 +2301,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@ -2355,7 +2346,6 @@
|
|||||||
"integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==",
|
"integrity": "sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.120.0",
|
"@oxc-project/types": "=0.120.0",
|
||||||
"@rolldown/pluginutils": "1.0.0-rc.10"
|
"@rolldown/pluginutils": "1.0.0-rc.10"
|
||||||
@ -2557,7 +2547,6 @@
|
|||||||
"integrity": "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==",
|
"integrity": "sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
@ -2682,7 +2671,6 @@
|
|||||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
parfum-shop/public/atmos-hero-image.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
17
parfum-shop/public/atmos-logo-dark.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.43 9.21">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #262626;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M4.05,8.93c-.82,0-1.53-.19-2.14-.58-.61-.39-1.08-.91-1.42-1.57-.34-.66-.5-1.38-.5-2.18s.17-1.53.5-2.19c.33-.65.81-1.17,1.42-1.56.61-.39,1.32-.58,2.14-.58.69,0,1.29.13,1.78.4s.9.63,1.21,1.09V.42h1.26v8.37h-1.26v-1.34c-.31.45-.72.8-1.21,1.07-.5.27-1.09.4-1.78.4ZM4.22,7.79c.62,0,1.15-.14,1.58-.44.43-.29.76-.68.98-1.16.22-.49.33-1.01.33-1.58s-.11-1.11-.33-1.59c-.22-.48-.55-.86-.98-1.16s-.96-.44-1.58-.44-1.14.15-1.59.44-.79.68-1.02,1.16c-.23.48-.35,1.01-.35,1.59s.12,1.1.35,1.58c.23.49.58.87,1.02,1.16.45.29.98.44,1.59.44Z"/>
|
||||||
|
<path class="cls-1" d="M43.01,8.93c-.77,0-1.42-.13-1.95-.39-.53-.26-.94-.59-1.21-1.01-.28-.42-.43-.87-.47-1.37h1.31c.03.28.13.55.29.81s.41.47.75.64c.33.16.77.24,1.31.24.17,0,.37-.02.62-.05s.48-.09.71-.18.42-.22.58-.4.23-.41.23-.69c0-.35-.13-.61-.4-.8-.27-.19-.61-.34-1.04-.44-.43-.11-.88-.21-1.35-.31-.47-.1-.92-.23-1.35-.39-.43-.16-.77-.39-1.04-.69s-.4-.7-.4-1.21c0-.76.28-1.35.83-1.77.55-.42,1.37-.63,2.45-.63.74,0,1.34.12,1.8.34.46.23.82.52,1.05.88.24.36.38.74.43,1.16h-1.27c-.04-.36-.22-.66-.53-.92-.31-.26-.81-.39-1.52-.39-1.33,0-1.99.4-1.99,1.21,0,.33.13.59.4.77.27.18.61.32,1.04.43.42.11.87.21,1.35.3.47.1.92.23,1.35.39s.77.4,1.04.71c.27.31.4.72.4,1.25,0,.82-.31,1.44-.93,1.87-.62.43-1.45.65-2.49.65Z"/>
|
||||||
|
</g>
|
||||||
|
<circle class="cls-1" cx="13.97" cy="4.6" r="4.6"/>
|
||||||
|
<circle class="cls-1" cx="24.01" cy="4.6" r="4.6"/>
|
||||||
|
<circle class="cls-1" cx="33.99" cy="4.6" r="4.6"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
17
parfum-shop/public/atmos-logo-light.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Ebene_1" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 46.43 9.21">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #eaeaea;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<path class="cls-1" d="M4.05,8.93c-.82,0-1.53-.19-2.14-.58-.61-.39-1.08-.91-1.42-1.57-.34-.66-.5-1.38-.5-2.18s.17-1.53.5-2.19c.33-.65.81-1.17,1.42-1.56.61-.39,1.32-.58,2.14-.58.69,0,1.29.13,1.78.4s.9.63,1.21,1.09V.42h1.26v8.37h-1.26v-1.34c-.31.45-.72.8-1.21,1.07-.5.27-1.09.4-1.78.4ZM4.22,7.79c.62,0,1.15-.14,1.58-.44.43-.29.76-.68.98-1.16.22-.49.33-1.01.33-1.58s-.11-1.11-.33-1.59c-.22-.48-.55-.86-.98-1.16s-.96-.44-1.58-.44-1.14.15-1.59.44-.79.68-1.02,1.16c-.23.48-.35,1.01-.35,1.59s.12,1.1.35,1.58c.23.49.58.87,1.02,1.16.45.29.98.44,1.59.44Z"/>
|
||||||
|
<path class="cls-1" d="M43.01,8.93c-.77,0-1.42-.13-1.95-.39-.53-.26-.94-.59-1.21-1.01-.28-.42-.43-.87-.47-1.37h1.31c.03.28.13.55.29.81s.41.47.75.64c.33.16.77.24,1.31.24.17,0,.37-.02.62-.05s.48-.09.71-.18.42-.22.58-.4.23-.41.23-.69c0-.35-.13-.61-.4-.8-.27-.19-.61-.34-1.04-.44-.43-.11-.88-.21-1.35-.31-.47-.1-.92-.23-1.35-.39-.43-.16-.77-.39-1.04-.69s-.4-.7-.4-1.21c0-.76.28-1.35.83-1.77.55-.42,1.37-.63,2.45-.63.74,0,1.34.12,1.8.34.46.23.82.52,1.05.88.24.36.38.74.43,1.16h-1.27c-.04-.36-.22-.66-.53-.92-.31-.26-.81-.39-1.52-.39-1.33,0-1.99.4-1.99,1.21,0,.33.13.59.4.77.27.18.61.32,1.04.43.42.11.87.21,1.35.3.47.1.92.23,1.35.39s.77.4,1.04.71c.27.31.4.72.4,1.25,0,.82-.31,1.44-.93,1.87-.62.43-1.45.65-2.49.65Z"/>
|
||||||
|
</g>
|
||||||
|
<circle class="cls-1" cx="13.97" cy="4.6" r="4.6"/>
|
||||||
|
<circle class="cls-1" cx="24.01" cy="4.6" r="4.6"/>
|
||||||
|
<circle class="cls-1" cx="33.99" cy="4.6" r="4.6"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
parfum-shop/public/blasse-seide-product-image.png
Normal file
|
After Width: | Height: | Size: 952 KiB |
BIN
parfum-shop/public/kalter-beton-product-image.png
Normal file
|
After Width: | Height: | Size: 957 KiB |
BIN
parfum-shop/public/nasser-marmor-product-image.png
Normal file
|
After Width: | Height: | Size: 952 KiB |
BIN
parfum-shop/public/schwarzes-benzin-product-image.png
Normal file
|
After Width: | Height: | Size: 933 KiB |
BIN
parfum-shop/public/verbranntes-chrom-product-image.png
Normal file
|
After Width: | Height: | Size: 951 KiB |
BIN
parfum-shop/public/weisse-asche-product-image.png
Normal file
|
After Width: | Height: | Size: 932 KiB |
87
parfum-shop/src/components/landing/HeroSection.jsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { Link } from "react-router";
|
||||||
|
import IntroOverlay from "./IntroOverlay";
|
||||||
|
|
||||||
|
function HeroSection({
|
||||||
|
heroImageWrapRef,
|
||||||
|
setHeadlinePrimaryRef,
|
||||||
|
setHeadlineSecondaryRef,
|
||||||
|
setDescriptionRef,
|
||||||
|
setActionsRef,
|
||||||
|
overlayRef,
|
||||||
|
overlayTextRef,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<header className="hero" id="home">
|
||||||
|
<div className="hero-media" ref={heroImageWrapRef}>
|
||||||
|
<img
|
||||||
|
src="/atmos-hero-image.png"
|
||||||
|
alt="Atmos Hero"
|
||||||
|
className="hero-media__image"
|
||||||
|
loading="eager"
|
||||||
|
fetchPriority="high"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link to="/" className="hero-brand" aria-label="Atmos Startseite">
|
||||||
|
<img
|
||||||
|
src="/atmos-logo-light.svg"
|
||||||
|
alt="atmos"
|
||||||
|
className="hero-brand__logo"
|
||||||
|
loading="eager"
|
||||||
|
fetchPriority="high"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<nav className="navbar navbar--hero" aria-label="Hauptnavigation">
|
||||||
|
<div className="nav-pill">
|
||||||
|
<a href="#home" className="nav-link active">
|
||||||
|
atmos
|
||||||
|
</a>
|
||||||
|
<a href="#dufte" className="nav-link">
|
||||||
|
{"D\u00FCfte"}
|
||||||
|
</a>
|
||||||
|
<Link to="/discovery-set" className="nav-link">
|
||||||
|
Testen
|
||||||
|
</Link>
|
||||||
|
<a href="#cart" className="nav-link">
|
||||||
|
Cart
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="hero-content">
|
||||||
|
<h1 className="hero-title">
|
||||||
|
<span className="hero-title-line" ref={setHeadlinePrimaryRef}>
|
||||||
|
{"D\u00DCFTE ALS"}
|
||||||
|
</span>
|
||||||
|
<span className="hero-title-line" ref={setHeadlineSecondaryRef}>
|
||||||
|
AUSDRUCK
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="hero-text" ref={setDescriptionRef}>
|
||||||
|
{
|
||||||
|
"Konzeptionelle D\u00FCfte zwischen Materialit\u00E4t, Raum und Charakter."
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="hero-actions" ref={setActionsRef}>
|
||||||
|
<a href="#dufte" className="btn btn-primary">
|
||||||
|
{"Aktuelle D\u00FCfte"}
|
||||||
|
</a>
|
||||||
|
<Link to="/discovery-set" className="btn btn-secondary">
|
||||||
|
Discovery Set
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<IntroOverlay
|
||||||
|
overlayRef={overlayRef}
|
||||||
|
overlayTextRef={overlayTextRef}
|
||||||
|
logoSrc="/atmos-logo-dark.svg"
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HeroSection;
|
||||||
15
parfum-shop/src/components/landing/IntroOverlay.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function IntroOverlay({ overlayRef, overlayTextRef, logoSrc }) {
|
||||||
|
return (
|
||||||
|
<div className="intro-overlay" ref={overlayRef} aria-hidden="true">
|
||||||
|
<div className="intro-overlay__inner">
|
||||||
|
<div className="intro-overlay__text-mask">
|
||||||
|
<div className="intro-overlay__logo" ref={overlayTextRef}>
|
||||||
|
<img src={logoSrc} alt="atmos" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IntroOverlay;
|
||||||
@ -3,7 +3,7 @@ const perfumes = [
|
|||||||
id: "01",
|
id: "01",
|
||||||
slug: "kalter-beton",
|
slug: "kalter-beton",
|
||||||
name: "KALTER BETON",
|
name: "KALTER BETON",
|
||||||
image: "/kalter-beton-product.png",
|
image: "/kalter-beton-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/kalter-beton-hover.webm",
|
fillVideo: "/kalter-beton-hover.webm",
|
||||||
text: "Mineralisch. Roh. Unberührt.",
|
text: "Mineralisch. Roh. Unberührt.",
|
||||||
@ -26,9 +26,9 @@ const perfumes = [
|
|||||||
concentration: "Eau de Parfum (18%)",
|
concentration: "Eau de Parfum (18%)",
|
||||||
edition: "Batch 04/24",
|
edition: "Batch 04/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/kalter-beton-product.png",
|
"/kalter-beton-product-image.png",
|
||||||
"/kalter-beton-product.png",
|
"/kalter-beton-product-image.png",
|
||||||
"/kalter-beton-product.png",
|
"/kalter-beton-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.8,
|
score: 4.8,
|
||||||
@ -83,7 +83,7 @@ const perfumes = [
|
|||||||
id: "02",
|
id: "02",
|
||||||
slug: "nasser-marmor",
|
slug: "nasser-marmor",
|
||||||
name: "NASSER MARMOR",
|
name: "NASSER MARMOR",
|
||||||
image: "/NASSER MARMOR.png",
|
image: "/nasser-marmor-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/nasser-marmor-hover.webm",
|
fillVideo: "/nasser-marmor-hover.webm",
|
||||||
text: "Kühl. Glatt. Sinnlich.",
|
text: "Kühl. Glatt. Sinnlich.",
|
||||||
@ -106,9 +106,9 @@ const perfumes = [
|
|||||||
concentration: "Eau de Parfum (17%)",
|
concentration: "Eau de Parfum (17%)",
|
||||||
edition: "Batch 02/24",
|
edition: "Batch 02/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/NASSER MARMOR.png",
|
"/nasser-marmor-product-image.png",
|
||||||
"/NASSER MARMOR.png",
|
"/nasser-marmor-product-image.png",
|
||||||
"/NASSER MARMOR.png",
|
"/nasser-marmor-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.7,
|
score: 4.7,
|
||||||
@ -163,7 +163,7 @@ const perfumes = [
|
|||||||
id: "03",
|
id: "03",
|
||||||
slug: "blasse-seide",
|
slug: "blasse-seide",
|
||||||
name: "BLASSE SEIDE",
|
name: "BLASSE SEIDE",
|
||||||
image: "/BLASSE SEIDE.png",
|
image: "/blasse-seide-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/blasse-seide-hover.webm",
|
fillVideo: "/blasse-seide-hover.webm",
|
||||||
text: "Blass. Sanft. Kostbar.",
|
text: "Blass. Sanft. Kostbar.",
|
||||||
@ -186,9 +186,9 @@ const perfumes = [
|
|||||||
concentration: "Eau de Parfum (16%)",
|
concentration: "Eau de Parfum (16%)",
|
||||||
edition: "Batch 03/24",
|
edition: "Batch 03/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/BLASSE SEIDE.png",
|
"/blasse-seide-product-image.png",
|
||||||
"/BLASSE SEIDE.png",
|
"/blasse-seide-product-image.png",
|
||||||
"/BLASSE SEIDE.png",
|
"/blasse-seide-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.6,
|
score: 4.6,
|
||||||
@ -243,7 +243,7 @@ const perfumes = [
|
|||||||
id: "04",
|
id: "04",
|
||||||
slug: "weisse-asche",
|
slug: "weisse-asche",
|
||||||
name: "WEISSE ASCHE",
|
name: "WEISSE ASCHE",
|
||||||
image: "/WEISSE ASCHE.png",
|
image: "/weisse-asche-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/weisse-asche-hover.webm",
|
fillVideo: "/weisse-asche-hover.webm",
|
||||||
text: "Still. Staubig. Erhaben.",
|
text: "Still. Staubig. Erhaben.",
|
||||||
@ -266,9 +266,9 @@ const perfumes = [
|
|||||||
concentration: "Eau de Parfum (19%)",
|
concentration: "Eau de Parfum (19%)",
|
||||||
edition: "Batch 01/24",
|
edition: "Batch 01/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/WEISSE ASCHE.png",
|
"/weisse-asche-product-image.png",
|
||||||
"/WEISSE ASCHE.png",
|
"/weisse-asche-product-image.png",
|
||||||
"/WEISSE ASCHE.png",
|
"/weisse-asche-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.7,
|
score: 4.7,
|
||||||
@ -323,7 +323,7 @@ const perfumes = [
|
|||||||
id: "05",
|
id: "05",
|
||||||
slug: "verbranntes-chrom",
|
slug: "verbranntes-chrom",
|
||||||
name: "VERBRANNTES CHROM",
|
name: "VERBRANNTES CHROM",
|
||||||
image: "/VERBRANNTES CHROM.png",
|
image: "/verbranntes-chrom-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/verbranntes-chrom-hover.webm",
|
fillVideo: "/verbranntes-chrom-hover.webm",
|
||||||
text: "Metallisch. Verzehrt. Edel.",
|
text: "Metallisch. Verzehrt. Edel.",
|
||||||
@ -346,9 +346,9 @@ const perfumes = [
|
|||||||
concentration: "Extrait de Parfum (24%)",
|
concentration: "Extrait de Parfum (24%)",
|
||||||
edition: "Batch 05/24",
|
edition: "Batch 05/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/VERBRANNTES CHROM.png",
|
"/verbranntes-chrom-product-image.png",
|
||||||
"/VERBRANNTES CHROM.png",
|
"/verbranntes-chrom-product-image.png",
|
||||||
"/VERBRANNTES CHROM.png",
|
"/verbranntes-chrom-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.9,
|
score: 4.9,
|
||||||
@ -403,7 +403,7 @@ const perfumes = [
|
|||||||
id: "06",
|
id: "06",
|
||||||
slug: "schwarzes-benzin",
|
slug: "schwarzes-benzin",
|
||||||
name: "SCHWARZES BENZIN",
|
name: "SCHWARZES BENZIN",
|
||||||
image: "/SCHWARZES BENZIN.png",
|
image: "/schwarzes-benzin-product-image.png",
|
||||||
fillImage: "/platzhalter.png",
|
fillImage: "/platzhalter.png",
|
||||||
fillVideo: "/schwarzes-benzin-hover.webm",
|
fillVideo: "/schwarzes-benzin-hover.webm",
|
||||||
text: "Dunkel. Glänzend. Verboten.",
|
text: "Dunkel. Glänzend. Verboten.",
|
||||||
@ -426,9 +426,9 @@ const perfumes = [
|
|||||||
concentration: "Eau de Parfum (20%)",
|
concentration: "Eau de Parfum (20%)",
|
||||||
edition: "Batch 06/24",
|
edition: "Batch 06/24",
|
||||||
gallery: [
|
gallery: [
|
||||||
"/SCHWARZES BENZIN.png",
|
"/schwarzes-benzin-product-image.png",
|
||||||
"/SCHWARZES BENZIN.png",
|
"/schwarzes-benzin-product-image.png",
|
||||||
"/SCHWARZES BENZIN.png",
|
"/schwarzes-benzin-product-image.png",
|
||||||
],
|
],
|
||||||
reviews: {
|
reviews: {
|
||||||
score: 4.8,
|
score: 4.8,
|
||||||
|
|||||||
@ -4,78 +4,135 @@
|
|||||||
color: #1f1f1f;
|
color: #1f1f1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.visually-hidden {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
.hero {
|
.hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 720px;
|
width: 100%;
|
||||||
margin-left: 20px;
|
min-height: 100vh;
|
||||||
margin-right: 20px;
|
min-height: 100svh;
|
||||||
margin-top: 0px;
|
min-height: 100dvh;
|
||||||
border-radius: 0;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-image: url("/HERO.jpeg");
|
display: flex;
|
||||||
background-size: cover;
|
align-items: center;
|
||||||
background-position: center;
|
isolation: isolate;
|
||||||
|
background: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-overlay {
|
.hero-media {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background:
|
z-index: 1;
|
||||||
linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.1)),
|
will-change: transform;
|
||||||
linear-gradient(to bottom, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.45));
|
}
|
||||||
|
|
||||||
|
.hero-media__image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .navbar--hero {
|
||||||
|
position: absolute;
|
||||||
|
top: 22px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 12;
|
||||||
|
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 {
|
.hero-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 6;
|
||||||
max-width: 460px;
|
width: min(760px, 100%);
|
||||||
padding: 120px 0 0 38px;
|
padding: clamp(6rem, 11vh, 9rem) clamp(1.2rem, 3.4vw, 3rem)
|
||||||
color: white;
|
clamp(2.6rem, 7vh, 4rem);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow {
|
.hero-title {
|
||||||
margin-bottom: 16px;
|
margin: 0;
|
||||||
font-size: 12px;
|
font-size: clamp(2.8rem, 8.5vw, 6.4rem);
|
||||||
letter-spacing: 0.18em;
|
line-height: 0.88;
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero h1 {
|
|
||||||
margin: 0 0 18px;
|
|
||||||
font-size: 62px;
|
|
||||||
line-height: 0.95;
|
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
letter-spacing: -0.04em;
|
letter-spacing: -0.045em;
|
||||||
color: white;
|
text-transform: uppercase;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title-line {
|
||||||
|
display: block;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title-line + .hero-title-line {
|
||||||
|
margin-top: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-text {
|
.hero-text {
|
||||||
max-width: 320px;
|
margin-top: 1.25rem;
|
||||||
font-size: 15px;
|
max-width: 29rem;
|
||||||
line-height: 1.5;
|
font-size: 0.99rem;
|
||||||
color: rgba(255, 255, 255, 0.85);
|
line-height: 1.58;
|
||||||
|
color: rgba(255, 255, 255, 0.86);
|
||||||
|
will-change: transform, opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-actions {
|
.hero-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
margin-top: 28px;
|
margin-top: 1.9rem;
|
||||||
|
will-change: transform, opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 12px 18px;
|
padding: 12px 20px;
|
||||||
font-size: 14px;
|
font-size: 0.9rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
transition: transform 0.24s ease, opacity 0.24s ease;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero .btn {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
@ -86,14 +143,51 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.16);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.intro-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 26;
|
||||||
|
background: #fff;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-overlay__inner {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: clamp(1rem, 4vw, 2.2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-overlay__text-mask {
|
||||||
|
width: min(96vw, 1200px);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-overlay__logo {
|
||||||
|
width: clamp(100px, 20vw, 340px);
|
||||||
|
max-width: 92vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-overlay__logo img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
/* SECTIONS */
|
/* SECTIONS */
|
||||||
.section {
|
.section {
|
||||||
padding: 28px 20px 10px;
|
padding: 42px 20px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-heading {
|
.section-heading {
|
||||||
@ -123,7 +217,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 0px;
|
border-radius: 0;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
min-height: 360px;
|
min-height: 360px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -224,7 +318,7 @@
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
height: auto;
|
height: auto;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: 0px;
|
border-radius: 0;
|
||||||
transition: transform 0.4s ease;
|
transition: transform 0.4s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +386,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background: #ff6a00;
|
background: #ff6a00;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
border-radius: 0px;
|
border-radius: 0;
|
||||||
padding: 40px 38px;
|
padding: 40px 38px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,18 +449,19 @@
|
|||||||
|
|
||||||
/* RESPONSIVE */
|
/* RESPONSIVE */
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.hero {
|
|
||||||
min-height: 620px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content {
|
.hero-content {
|
||||||
padding: 90px 24px 40px;
|
width: min(640px, 100%);
|
||||||
|
padding-top: 7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero h1,
|
.hero-title,
|
||||||
.section-heading h2,
|
.section-heading h2,
|
||||||
.discovery-copy h2 {
|
.discovery-copy h2 {
|
||||||
font-size: 42px;
|
font-size: clamp(2.45rem, 9vw, 3.2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-text {
|
||||||
|
font-size: 0.94rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
@ -379,20 +474,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.hero {
|
.hero-brand {
|
||||||
margin: 12px;
|
top: 14px;
|
||||||
min-height: 540px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero h1,
|
.hero .navbar--hero {
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
padding: 6.2rem 1rem 2.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title,
|
||||||
.section-heading h2,
|
.section-heading h2,
|
||||||
.discovery-copy h2 {
|
.discovery-copy h2 {
|
||||||
font-size: 34px;
|
font-size: clamp(2.05rem, 13vw, 2.7rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-actions {
|
.hero-actions {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
width: min(300px, 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions .btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 34px 12px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-grid {
|
.product-grid {
|
||||||
@ -402,4 +513,21 @@
|
|||||||
.product-card {
|
.product-card {
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.discovery-section {
|
||||||
|
margin: 12px;
|
||||||
|
padding: 28px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.hero-media,
|
||||||
|
.hero-title-line,
|
||||||
|
.hero-text,
|
||||||
|
.hero-actions,
|
||||||
|
.hero-brand,
|
||||||
|
.intro-overlay {
|
||||||
|
transition: none !important;
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,13 +1,162 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { Link } from "react-router";
|
import { Link } from "react-router";
|
||||||
import { gsap } from "gsap";
|
import { gsap } from "gsap";
|
||||||
|
import HeroSection from "../components/landing/HeroSection";
|
||||||
import perfumes from "../data/perfumes";
|
import perfumes from "../data/perfumes";
|
||||||
import "../pages/LandingPage.css";
|
import "../pages/LandingPage.css";
|
||||||
import "../style/navbar.css";
|
import "../style/navbar.css";
|
||||||
|
|
||||||
|
const INTRO_SESSION_KEY = "atmos-landing-intro-played";
|
||||||
|
|
||||||
function LandingPage() {
|
function LandingPage() {
|
||||||
|
const pageRef = useRef(null);
|
||||||
|
const overlayRef = useRef(null);
|
||||||
|
const overlayTextRef = useRef(null);
|
||||||
|
const heroImageWrapRef = useRef(null);
|
||||||
|
const headlineLineRefs = useRef([]);
|
||||||
|
const heroMetaRefs = useRef([]);
|
||||||
const cardRefs = useRef([]);
|
const cardRefs = useRef([]);
|
||||||
|
|
||||||
|
const [introSettings] = useState(() => {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return { shouldPlayIntro: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefersReducedMotion = window.matchMedia(
|
||||||
|
"(prefers-reduced-motion: reduce)"
|
||||||
|
).matches;
|
||||||
|
const introAlreadyPlayed =
|
||||||
|
window.sessionStorage.getItem(INTRO_SESSION_KEY) === "true";
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldPlayIntro: !prefersReducedMotion && !introAlreadyPlayed,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const { shouldPlayIntro } = introSettings;
|
||||||
|
|
||||||
|
const setHeadlinePrimaryRef = useCallback((element) => {
|
||||||
|
headlineLineRefs.current[0] = element;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setHeadlineSecondaryRef = useCallback((element) => {
|
||||||
|
headlineLineRefs.current[1] = element;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setDescriptionRef = useCallback((element) => {
|
||||||
|
heroMetaRefs.current[0] = element;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setActionsRef = useCallback((element) => {
|
||||||
|
heroMetaRefs.current[1] = element;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const overlay = overlayRef.current;
|
||||||
|
const overlayText = overlayTextRef.current;
|
||||||
|
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) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = gsap.context(() => {
|
||||||
|
gsap.set(heroImageWrap, {
|
||||||
|
scale: 1.22,
|
||||||
|
transformOrigin: "center center",
|
||||||
|
});
|
||||||
|
|
||||||
|
gsap.set(revealTargets, {
|
||||||
|
y: 56,
|
||||||
|
autoAlpha: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!shouldPlayIntro) {
|
||||||
|
gsap.set(heroImageWrap, { scale: 1 });
|
||||||
|
gsap.set(revealTargets, { y: 0, autoAlpha: 1 });
|
||||||
|
gsap.set(overlay, {
|
||||||
|
yPercent: -100,
|
||||||
|
autoAlpha: 0,
|
||||||
|
display: "none",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gsap.set(overlay, { yPercent: 0, autoAlpha: 1, display: "block" });
|
||||||
|
gsap.set(overlayText, { xPercent: 28, yPercent: 140, autoAlpha: 0 });
|
||||||
|
|
||||||
|
const introTimeline = gsap.timeline({
|
||||||
|
defaults: { ease: "power3.out" },
|
||||||
|
onComplete: () => {
|
||||||
|
window.sessionStorage.setItem(INTRO_SESSION_KEY, "true");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
introTimeline
|
||||||
|
.to(overlayText, {
|
||||||
|
xPercent: 0,
|
||||||
|
yPercent: 0,
|
||||||
|
autoAlpha: 1,
|
||||||
|
duration: 1.28,
|
||||||
|
ease: "expo.out",
|
||||||
|
})
|
||||||
|
.to({}, { duration: 0.34 })
|
||||||
|
.to(
|
||||||
|
overlay,
|
||||||
|
{
|
||||||
|
yPercent: -100,
|
||||||
|
duration: 1.46,
|
||||||
|
ease: "power4.out",
|
||||||
|
},
|
||||||
|
">"
|
||||||
|
)
|
||||||
|
.to(
|
||||||
|
heroImageWrap,
|
||||||
|
{
|
||||||
|
scale: 1,
|
||||||
|
duration: 1.6,
|
||||||
|
ease: "power2.out",
|
||||||
|
},
|
||||||
|
"<0.03"
|
||||||
|
)
|
||||||
|
.to(
|
||||||
|
headlineLines,
|
||||||
|
{
|
||||||
|
y: 0,
|
||||||
|
autoAlpha: 1,
|
||||||
|
duration: 1.04,
|
||||||
|
stagger: 0.16,
|
||||||
|
ease: "power3.out",
|
||||||
|
},
|
||||||
|
"<0.27"
|
||||||
|
)
|
||||||
|
.to(
|
||||||
|
heroMeta,
|
||||||
|
{
|
||||||
|
y: 0,
|
||||||
|
autoAlpha: 1,
|
||||||
|
duration: 0.98,
|
||||||
|
stagger: 0.14,
|
||||||
|
ease: "power3.out",
|
||||||
|
},
|
||||||
|
"<0.2"
|
||||||
|
)
|
||||||
|
.set(overlay, { display: "none" });
|
||||||
|
}, pageRef);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ctx.revert();
|
||||||
|
};
|
||||||
|
}, [shouldPlayIntro]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cards = cardRefs.current.filter(Boolean);
|
const cards = cardRefs.current.filter(Boolean);
|
||||||
const cardStates = cards
|
const cardStates = cards
|
||||||
@ -35,7 +184,7 @@ function LandingPage() {
|
|||||||
try {
|
try {
|
||||||
video.currentTime = 0;
|
video.currentTime = 0;
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore errors when setting currentTime
|
// Ignore errors when setting currentTime.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,12 +194,12 @@ function LandingPage() {
|
|||||||
try {
|
try {
|
||||||
video.currentTime = 0;
|
video.currentTime = 0;
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore errors when setting currentTime
|
// Ignore errors when setting currentTime.
|
||||||
}
|
}
|
||||||
|
|
||||||
const playAttempt = video.play();
|
const playAttempt = video.play();
|
||||||
if (playAttempt && typeof playAttempt.catch === "function") {
|
if (playAttempt && typeof playAttempt.catch === "function") {
|
||||||
playAttempt.catch(() => { });
|
playAttempt.catch(() => {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,56 +299,24 @@ function LandingPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page" ref={pageRef}>
|
||||||
<header className="hero">
|
<HeroSection
|
||||||
<nav className="navbar navbar--hero">
|
heroImageWrapRef={heroImageWrapRef}
|
||||||
<div className="nav-pill">
|
setHeadlinePrimaryRef={setHeadlinePrimaryRef}
|
||||||
<a href="#home" className="nav-link active">
|
setHeadlineSecondaryRef={setHeadlineSecondaryRef}
|
||||||
atmos
|
setDescriptionRef={setDescriptionRef}
|
||||||
</a>
|
setActionsRef={setActionsRef}
|
||||||
<a href="#dufte" className="nav-link">
|
overlayRef={overlayRef}
|
||||||
Düfte
|
overlayTextRef={overlayTextRef}
|
||||||
</a>
|
/>
|
||||||
<a href="discovery-set" className="nav-link">
|
|
||||||
Testen
|
|
||||||
</a>
|
|
||||||
<a href="#cart" className="nav-link">
|
|
||||||
Cart
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className="hero-overlay" />
|
|
||||||
|
|
||||||
<div className="hero-content">
|
|
||||||
<p className="eyebrow">NISCHENDÜFTE</p>
|
|
||||||
<h1>
|
|
||||||
DÜFTE ALS
|
|
||||||
<br />
|
|
||||||
AUSDRUCK
|
|
||||||
<br />
|
|
||||||
VON KONZEPT
|
|
||||||
</h1>
|
|
||||||
<p className="hero-text">
|
|
||||||
Konzeptuelle Düfte zwischen Materialität, Raum und Charakter.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="hero-actions">
|
|
||||||
<button className="btn btn-primary">Aktuelle Düfte</button>
|
|
||||||
<Link to="/discovery-set" className="btn btn-secondary">
|
|
||||||
Discovery Set
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section className="section" id="dufte">
|
<section className="section" id="dufte">
|
||||||
<div className="section-heading">
|
<div className="section-heading">
|
||||||
<h2>
|
<h2>
|
||||||
WÄHLE EINE
|
{"W\u00C4HLE EINE"}
|
||||||
<br />
|
<br />
|
||||||
ATMOSPHÄRE
|
{"ATMOSPH\u00C4RE"}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -209,8 +326,8 @@ function LandingPage() {
|
|||||||
to={`/duft/${item.slug}`}
|
to={`/duft/${item.slug}`}
|
||||||
className="product-card"
|
className="product-card"
|
||||||
key={item.id}
|
key={item.id}
|
||||||
ref={(el) => {
|
ref={(element) => {
|
||||||
cardRefs.current[index] = el;
|
cardRefs.current[index] = element;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="product-hover-fill" aria-hidden="true">
|
<div className="product-hover-fill" aria-hidden="true">
|
||||||
@ -244,7 +361,7 @@ function LandingPage() {
|
|||||||
|
|
||||||
<div className="product-bottom">
|
<div className="product-bottom">
|
||||||
<p>{item.text}</p>
|
<p>{item.text}</p>
|
||||||
<span className="arrow">→</span>
|
<span className="arrow">→</span>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@ -259,7 +376,7 @@ function LandingPage() {
|
|||||||
DISCOVERY SET
|
DISCOVERY SET
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Alle 6 Düfte als 2ml Samples 2ml.
|
{"Alle 6 D\u00FCfte als 2ml Samples."}
|
||||||
<br />
|
<br />
|
||||||
Jeden Duft eine Woche tragen.
|
Jeden Duft eine Woche tragen.
|
||||||
<br />
|
<br />
|
||||||
@ -271,7 +388,7 @@ function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="discovery-banner">
|
<div className="discovery-banner">
|
||||||
<img src="/DISCOVERYSET.png" alt="Discovery Set" />
|
<img src="/DISCOVERYSET.png" alt="Discovery Set" loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@ -279,4 +396,4 @@ function LandingPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LandingPage;
|
export default LandingPage;
|
||||||
|
|||||||