Compare commits

...

4 Commits

Author SHA1 Message Date
8140efb951 Add perfume detail routing, global navbar styles and chage radius on elements 2026-04-03 14:17:31 +02:00
anielsen99
a524a5d36a match colors 2026-03-27 22:49:07 +01:00
anielsen99
3628d7b31f Merge branch 'productdetails' 2026-03-27 18:51:31 +01:00
anielsen99
8951b36ae3 revise discovery-section on landingPage 2026-03-26 13:37:41 +01:00
10 changed files with 537 additions and 496 deletions

View File

@ -10,7 +10,8 @@
"dependencies": { "dependencies": {
"gsap": "^3.14.2", "gsap": "^3.14.2",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4" "react-dom": "^19.2.4",
"react-router": "^7.14.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.0",
@ -1050,9 +1051,9 @@
} }
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.12", "version": "1.1.13",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -1177,6 +1178,19 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cookie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -2219,9 +2233,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "4.0.3", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -2295,6 +2309,7 @@
"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"
}, },
@ -2302,6 +2317,28 @@
"react": "^19.2.4" "react": "^19.2.4"
} }
}, },
"node_modules/react-router": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz",
"integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/resolve-from": { "node_modules/resolve-from": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@ -2370,6 +2407,12 @@
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
}, },
"node_modules/set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",

View File

@ -12,7 +12,8 @@
"dependencies": { "dependencies": {
"gsap": "^3.14.2", "gsap": "^3.14.2",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4" "react-dom": "^19.2.4",
"react-router": "^7.14.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.0",

View File

@ -1,417 +1,15 @@
.page { * {
min-height: 100vh; box-sizing: border-box;
}
html,
body,
#root {
margin: 0;
min-height: 100%;
font-family: Arial, Helvetica, sans-serif;
}
body {
background: #efefef; background: #efefef;
color: #1f1f1f; }
}
/*
Hallo im CSS,
damit ihr euch hier nicht durch einen wilden Haufen aus Klassen kämpfen müsst,
versuche ich die Bereiche sauber zu kommentieren. So ist schneller sichtbar,
welche Styles wohin gehören und wo welche Section anfängt.
Euer Freund und Helfer Salih
Bei Bugs, Verzweiflung oder akuten CSS-Zusammenbrüchen lesen Sie https://stackoverflow.com/questions
oder fragen Sie Salih oder eine KI Ihres Vertrauens.
Erreichbar unter salih.hasicic@stud.fhgr.ch oder telefonisch, fall Sie die Nummer haben*/
/* HERO */
.hero {
position: relative;
min-height: 720px;
margin-left: 20px;
margin-right: 20px;
margin-top: 0px;
border-radius: 0 0 18px 18px;
overflow: hidden;
background-image: url("/HERO.jpeg");
background-size: cover;
background-position: center;
}
.hero-overlay {
position: absolute;
inset: 0;
background:
linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.1)),
linear-gradient(to bottom, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.45));
}
.hero-content {
position: relative;
z-index: 2;
max-width: 460px;
padding: 120px 0 0 38px;
color: white;
}
.eyebrow {
margin-bottom: 16px;
font-size: 12px;
letter-spacing: 0.18em;
opacity: 0.85;
}
.hero h1 {
margin: 0 0 18px;
font-size: 62px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: white;
}
.hero-text {
max-width: 320px;
font-size: 15px;
line-height: 1.5;
color: rgba(255, 255, 255, 0.85);
}
.hero-actions {
display: flex;
gap: 12px;
margin-top: 28px;
}
.btn {
border: none;
border-radius: 999px;
padding: 12px 18px;
font-size: 14px;
cursor: pointer;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
}
.btn-primary {
background: #ff6a00;
color: white;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.15);
color: white;
backdrop-filter: blur(8px);
}
/* --------------------------------------------------- */
/* SECTIONS */
.section {
padding: 28px 20px 10px;
}
.section-heading {
margin-bottom: 28px;
}
.section-heading h2,
.discovery-copy h2 {
margin: 0;
font-size: 52px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: #1d1d1d;
}
/* --------------------------------------------------- */
/* GRID */
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
}
.product-card {
position: relative;
isolation: isolate;
overflow: hidden;
background: #f5f5f5;
border: 1px solid #d9d9d9;
border-radius: 18px;
padding: 18px;
min-height: 360px;
display: flex;
flex-direction: column;
justify-content: space-between;
cursor: pointer;
transition: transform 0.15s ease, border-color 0.15s ease;
}
.product-card:focus-visible {
outline: 2px solid #ff6a00;
outline-offset: 3px;
}
.product-hover-fill {
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
}
.product-hover-image,
.product-hover-video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.product-hover-image {
background-size: cover;
background-position: center;
}
.product-hover-video {
display: block;
object-fit: cover;
}
.product-hover-fill::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.08),
rgba(0, 0, 0, 0.18)
);
}
.product-card:active {
transform: scale(0.97);
border-color: #ff6a00;
}
.product-top {
position: relative;
z-index: 4;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
}
.product-id {
font-size: 18px;
color: #5f5f5f;
}
.product-top h3 {
margin: 0;
font-size: 18px;
font-weight: 400;
text-align: right;
letter-spacing: 0.02em;
}
.product-image-wrap {
position: relative;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
min-height: 180px;
padding: 20px 0;
width: 100%;
overflow: hidden;
}
.product-image {
position: relative;
z-index: 1;
width: 100%;
max-width: 600px;
height: auto;
object-fit: contain;
border-radius: 10px;
transition: transform 0.4s ease;
}
.product-card:hover .product-image {
transform: scale(1.05);
}
.product-bottom {
position: relative;
z-index: 4;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 12px;
}
.product-bottom p {
margin: 0;
max-width: 170px;
font-size: 15px;
line-height: 1.35;
color: #444;
}
.arrow {
font-size: 26px;
color: #ff6a00;
line-height: 1;
}
.product-id,
.product-top h3,
.product-bottom p,
.arrow {
transition: color 0.25s ease;
}
.product-card:hover .product-id,
.product-card:hover .product-top h3,
.product-card:hover .product-bottom p,
.product-card:hover .arrow,
.product-card:focus-within .product-id,
.product-card:focus-within .product-top h3,
.product-card:focus-within .product-bottom p,
.product-card:focus-within .arrow {
color: #fff;
mix-blend-mode: difference;
}
.product-card:active .product-id,
.product-card:active .product-top h3,
.product-card:active .product-bottom p,
.product-card:active .arrow {
color: #ff6a00;
mix-blend-mode: normal;
transform: scale(1.02);
transition: all 0.1s ease;
}
/* DISCOVERY */
.discovery-section {
display: grid;
grid-template-columns: 600px 1fr;
gap: 28px;
align-items: center;
padding: 40px 20px 50px;
}
.discovery-copy h2 {
margin: 0;
font-size: 42px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: #1d1d1d;
}
.discovery-copy p {
margin-top: 18px;
font-size: 15px;
line-height: 1.5;
color: #555;
}
.discovery-banner {
position: relative;
width: 100%;
max-width: 1300px;
height: 340px;
border-radius: 20px;
overflow: hidden;
}
.discovery-banner img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.banner-btn {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: none;
border-radius: 999px;
padding: 12px 18px;
background: rgba(255, 255, 255, 0.22);
color: white;
backdrop-filter: blur(10px);
cursor: pointer;
}
/* --------------------------------------------------- */
/* RESPONSIVE */
@media (max-width: 900px) {
.hero {
min-height: 620px;
}
.hero-content {
padding: 90px 24px 40px;
}
.hero h1,
.section-heading h2,
.discovery-copy h2 {
font-size: 42px;
}
.product-grid {
grid-template-columns: repeat(2, 1fr);
}
.discovery-section {
grid-template-columns: 1fr;
}
}
@media (max-width: 640px) {
.hero {
margin: 12px;
min-height: 540px;
}
.nav-pill {
gap: 4px;
padding: 6px;
}
.nav-link {
padding: 8px 10px;
font-size: 12px;
}
.hero h1,
.section-heading h2,
.discovery-copy h2 {
font-size: 34px;
}
.hero-actions {
flex-direction: column;
align-items: flex-start;
}
.product-grid {
grid-template-columns: 1fr;
}
.product-card {
min-height: 320px;
}
}

View File

@ -1,14 +1,14 @@
import { Routes, Route } from "react-router";
import LandingPage from "./pages/LandingPage"; import LandingPage from "./pages/LandingPage";
import ProductDetailPage from "./components/ProductDetailPage"; import ProductDetailPage from "./components/ProductDetailPage";
function App() { function App() {
const showDetailPage = true; return (
<Routes>
if (showDetailPage) { <Route path="/" element={<LandingPage />} />
return <ProductDetailPage perfumeSlug="kalter-beton" />; <Route path="/duft/:perfumeSlug" element={<ProductDetailPage />} />
} </Routes>
);
return <LandingPage />;
} }
export default App; export default App;

View File

@ -1,18 +1,8 @@
/*
Hallo im CSS,
ich versuche auch hier die Struktur sauber zu kommentieren, damit ihr nicht
im Styling-Dschungel verloren geht und schneller versteht, was wohin gehört.
Bei Bugs, kleinen Krisen oder emotionalem Kontrollverlust bitte
https://stackoverflow.com/questions konsultieren
oder fragt Salih oder eine KI eures Vertrauens.
*/
/* --- Product Detail Page Wrapper Start --- */ /* --- Product Detail Page Wrapper Start --- */
.detail-page { .detail-page {
min-height: 100vh; min-height: 100vh;
color: #191919; color: #1f1f1f;
padding: 38px; padding: 38px;
background: background:
linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.1)), linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.1)),
@ -20,8 +10,9 @@
} }
.detail-shell { .detail-shell {
background: #f7f7f7; background: #f5f5f5;
border-radius: 18px; border: 1px solid #d9d9d9;
border-radius: 0px;
padding: 38px; padding: 38px;
} }
@ -36,7 +27,7 @@
margin-bottom: 18px; margin-bottom: 18px;
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
color: #222; color: #1f1f1f;
} }
/* --- Back Link End --- */ /* --- Back Link End --- */
@ -60,10 +51,10 @@
} }
.detail-main-image { .detail-main-image {
background: #ddd; background: #d9d9d9;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
overflow: hidden; overflow: hidden;
border-radius: 18px; border-radius: 0px;
} }
.detail-main-image img { .detail-main-image img {
@ -83,7 +74,7 @@
width: 88px; width: 88px;
height: 88px; height: 88px;
border: none; border: none;
border-radius: 18px; border-radius: 0px;
background: #fff; background: #fff;
padding: 0; padding: 0;
cursor: pointer; cursor: pointer;
@ -154,7 +145,7 @@
margin-top: 18px; margin-top: 18px;
background: #dfdfdf; background: #dfdfdf;
padding: 16px; padding: 16px;
border-radius: 18px; border-radius: 0px;
} }
.mood-label { .mood-label {
@ -217,7 +208,7 @@
.detail-heading p { .detail-heading p {
font-size: 22px; font-size: 22px;
color: #3f3f3f; color: #5f5f5f;
margin: 0; margin: 0;
} }
@ -231,7 +222,7 @@
margin-bottom: 10px; margin-bottom: 10px;
font-size: 11px; font-size: 11px;
letter-spacing: 0.24em; letter-spacing: 0.24em;
color: #666; color: #5f5f5f;
} }
/* --- Right Column End --- */ /* --- Right Column End --- */
@ -250,7 +241,7 @@
min-height: 34px; min-height: 34px;
padding: 8px 14px; padding: 8px 14px;
border: 1px solid #d2d2d2; border: 1px solid #d2d2d2;
border-radius: 999px; border-radius: 0px;
background: transparent; background: transparent;
font-size: 12px; font-size: 12px;
user-select: none; user-select: none;
@ -268,7 +259,7 @@
.size-card { .size-card {
border: 1px solid #d0d0d0; border: 1px solid #d0d0d0;
border-radius: 18px; border-radius: 0px;
background: #fff; background: #fff;
padding: 18px; padding: 18px;
text-align: center; text-align: center;
@ -294,7 +285,7 @@
.size-card small { .size-card small {
font-size: 11px; font-size: 11px;
color: #666; color: #5f5f5f;
} }
/* --- Size Selection End --- */ /* --- Size Selection End --- */
@ -302,10 +293,10 @@
/* --- Discovery Hinweis + Kaufen Button Start --- */ /* --- Discovery Hinweis + Kaufen Button Start --- */
.discovery-note { .discovery-note {
background-color: #1f1f1f; background: #1f1f1f;
color: #fff; color: #fff;
padding: 14px 16px; padding: 14px 16px;
border-radius: 18px; border-radius: 0px;
justify-content: space-between; justify-content: space-between;
display: flex; display: flex;
gap: 20px; gap: 20px;
@ -325,8 +316,8 @@
.discovery-note-btn { .discovery-note-btn {
border: none; border: none;
border-radius: 999px; border-radius: 18px;
background-color: #ff6a00; background: #ff6a00;
color: #fff; color: #fff;
padding: 12px 18px; padding: 12px 18px;
font-size: 14px; font-size: 14px;
@ -349,7 +340,7 @@
.buy-button { .buy-button {
width: 100%; width: 100%;
border: none; border: none;
border-radius: 999px; border-radius: 0px;
background: #ff6a00; background: #ff6a00;
color: #fff; color: #fff;
padding: 18px; padding: 18px;
@ -380,7 +371,6 @@
.detail-copy-block p { .detail-copy-block p {
white-space: pre-line; white-space: pre-line;
color: #2b2b2b;
line-height: 1.55; line-height: 1.55;
margin: 0; margin: 0;
} }
@ -392,8 +382,8 @@
.detail-bottom-cta { .detail-bottom-cta {
margin-top: 40px; margin-top: 40px;
padding: 40px 38px; padding: 40px 38px;
background-color: #ff6a00; background: #ff6a00;
border-radius: 24px; border-radius: 0px;
} }
.detail-bottom-cta h2 { .detail-bottom-cta h2 {
@ -426,7 +416,7 @@
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
transition: transform 0.2s ease, opacity 0.2s ease; transition: transform 0.2s ease, opacity 0.2s ease;
background-color: #fff; background: #fff;
color: #ff6a00; color: #ff6a00;
} }

View File

@ -1,9 +1,12 @@
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { Link, useNavigate, useParams } from "react-router";
import perfumes from "../data/perfumes"; import perfumes from "../data/perfumes";
import "../navbar.css"; import "../style/navbar.css";
import "./ProductDetailPage.css"; import "./ProductDetailPage.css";
function ProductDetailPage({ perfumeSlug = "kalter-beton" }) { function ProductDetailContent({ perfumeSlug }) {
const navigate = useNavigate();
const perfume = useMemo( const perfume = useMemo(
() => perfumes.find((item) => item.slug === perfumeSlug) || perfumes[0], () => perfumes.find((item) => item.slug === perfumeSlug) || perfumes[0],
[perfumeSlug] [perfumeSlug]
@ -31,33 +34,29 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
return ( return (
<div className="detail-page"> <div className="detail-page">
{/* Navbar */}
<nav className="navbar navbar--light"> <nav className="navbar navbar--light">
<div className="nav-pill"> <div className="nav-pill">
<a href="#home" className="nav-link active"> <Link to="/" className="nav-link">
Name Name
</a> </Link>
<a href="#dufte" className="nav-link"> <Link to="/#dufte" className="nav-link active">
Düfte Düfte
</a> </Link>
<a href="#testen" className="nav-link"> <Link to="/#testen" className="nav-link">
Testen Testen
</a> </Link>
<a href="#cart" className="nav-link"> <a href="#cart" className="nav-link">
Cart Cart
</a> </a>
</div> </div>
</nav> </nav>
{/* --- Navbar End --- */}
{/* Product detail content */}
<main className="detail-shell"> <main className="detail-shell">
<button className="back-link" type="button"> <button className="back-link" type="button" onClick={() => navigate("/")}>
Zurück zur Startseite Zurück zur Startseite
</button> </button>
<section className="detail-layout"> <section className="detail-layout">
{/* Left column */}
<div className="detail-gallery"> <div className="detail-gallery">
<div className="detail-main-image"> <div className="detail-main-image">
<img src={selectedImage} alt={perfume.name} /> <img src={selectedImage} alt={perfume.name} />
@ -132,7 +131,6 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
</div> </div>
</div> </div>
{/* Right column */}
<div className="detail-info"> <div className="detail-info">
<div className="detail-heading"> <div className="detail-heading">
<h1>{perfume.name}</h1> <h1>{perfume.name}</h1>
@ -174,10 +172,10 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
automatisch abgezogen. automatisch abgezogen.
</p> </p>
</div> </div>
<button className="discovery-note-btn" type="button"> <button className="discovery-note-btn" type="button">
Zum Set Zum Set
</button> </button>
</div> </div>
<button className="buy-button" type="button"> <button className="buy-button" type="button">
@ -208,7 +206,6 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
</div> </div>
</section> </section>
{/* Bottom CTA */}
<section className="detail-bottom-cta"> <section className="detail-bottom-cta">
<h2>Lieber erst testen?</h2> <h2>Lieber erst testen?</h2>
<p> <p>
@ -227,4 +224,10 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
); );
} }
function ProductDetailPage() {
const { perfumeSlug = "kalter-beton" } = useParams();
return <ProductDetailContent key={perfumeSlug} perfumeSlug={perfumeSlug} />;
}
export default ProductDetailPage; export default ProductDetailPage;

View File

@ -1,10 +1,13 @@
import { StrictMode } from 'react' import React from "react";
import { createRoot } from 'react-dom/client' import ReactDOM from "react-dom/client";
import './index.css' import { BrowserRouter } from "react-router";
import App from './App.jsx' import App from "./App";
import "./App.css";
createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById("root")).render(
<StrictMode> <React.StrictMode>
<App /> <BrowserRouter>
</StrictMode>, <App />
) </BrowserRouter>
</React.StrictMode>
);

View File

@ -0,0 +1,397 @@
.page {
min-height: 100vh;
background: #efefef;
color: #1f1f1f;
}
/* HERO */
.hero {
position: relative;
min-height: 720px;
margin-left: 20px;
margin-right: 20px;
margin-top: 0px;
border-radius: 0;
overflow: hidden;
background-image: url("/HERO.jpeg");
background-size: cover;
background-position: center;
}
.hero-overlay {
position: absolute;
inset: 0;
background:
linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.1)),
linear-gradient(to bottom, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.45));
}
.hero-content {
position: relative;
z-index: 2;
max-width: 460px;
padding: 120px 0 0 38px;
color: white;
}
.eyebrow {
margin-bottom: 16px;
font-size: 12px;
letter-spacing: 0.18em;
opacity: 0.85;
}
.hero h1 {
margin: 0 0 18px;
font-size: 62px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: white;
}
.hero-text {
max-width: 320px;
font-size: 15px;
line-height: 1.5;
color: rgba(255, 255, 255, 0.85);
}
.hero-actions {
display: flex;
gap: 12px;
margin-top: 28px;
}
.btn {
border: none;
border-radius: 999px;
padding: 12px 18px;
font-size: 14px;
cursor: pointer;
transition: transform 0.2s ease, opacity 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
}
.btn-primary {
background: #ff6a00;
color: #fff;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.15);
color: #fff;
backdrop-filter: blur(8px);
}
/* SECTIONS */
.section {
padding: 28px 20px 10px;
}
.section-heading {
margin-bottom: 28px;
}
.section-heading h2,
.discovery-copy h2 {
margin: 0;
font-size: 52px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: #1f1f1f;
}
/* GRID */
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 18px;
}
.product-card {
position: relative;
isolation: isolate;
overflow: hidden;
background: #f5f5f5;
border: 1px solid #d9d9d9;
border-radius: 0px;
padding: 18px;
min-height: 360px;
display: flex;
flex-direction: column;
justify-content: space-between;
cursor: pointer;
transition: transform 0.15s ease, border-color 0.15s ease;
text-decoration: none;
color: inherit;
}
.product-card:focus-visible {
outline: 2px solid #ff6a00;
outline-offset: 3px;
}
.product-hover-fill {
position: absolute;
inset: 0;
z-index: 2;
pointer-events: none;
}
.product-hover-image,
.product-hover-video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.product-hover-image {
background-size: cover;
background-position: center;
}
.product-hover-video {
display: block;
object-fit: cover;
}
.product-hover-fill::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.08),
rgba(0, 0, 0, 0.18)
);
}
.product-card:active {
transform: scale(0.97);
border-color: #ff6a00;
}
.product-top {
position: relative;
z-index: 4;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 12px;
}
.product-id {
font-size: 18px;
color: #5f5f5f;
}
.product-top h3 {
margin: 0;
font-size: 18px;
font-weight: 400;
text-align: right;
letter-spacing: 0.02em;
}
.product-image-wrap {
position: relative;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
min-height: 180px;
padding: 20px 0;
width: 100%;
overflow: hidden;
}
.product-image {
position: relative;
z-index: 1;
width: 100%;
max-width: 600px;
height: auto;
object-fit: contain;
border-radius: 0px;
transition: transform 0.4s ease;
}
.product-card:hover .product-image {
transform: scale(1.05);
}
.product-bottom {
position: relative;
z-index: 4;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 12px;
}
.product-bottom p {
margin: 0;
max-width: 170px;
font-size: 15px;
line-height: 1.35;
color: #5f5f5f;
}
.arrow {
font-size: 26px;
color: #ff6a00;
line-height: 1;
}
.product-id,
.product-top h3,
.product-bottom p,
.arrow {
transition: color 0.25s ease;
}
.product-card:hover .product-id,
.product-card:hover .product-top h3,
.product-card:hover .product-bottom p,
.product-card:hover .arrow,
.product-card:focus-within .product-id,
.product-card:focus-within .product-top h3,
.product-card:focus-within .product-bottom p,
.product-card:focus-within .arrow {
color: #fff;
mix-blend-mode: difference;
}
.product-card:active .product-id,
.product-card:active .product-top h3,
.product-card:active .product-bottom p,
.product-card:active .arrow {
color: #ff6a00;
mix-blend-mode: normal;
transform: scale(1.02);
transition: all 0.1s ease;
}
/* DISCOVERY */
.discovery-section {
display: grid;
grid-template-columns: 600px 1fr;
gap: 28px;
align-items: center;
background: #ff6a00;
margin: 20px;
border-radius: 0px;
padding: 40px 38px;
}
.discovery-copy h2 {
margin: 0;
font-size: 42px;
line-height: 0.95;
font-weight: 300;
letter-spacing: -0.04em;
color: #fff;
}
.discovery-copy p {
margin-top: 18px;
font-size: 15px;
line-height: 1.5;
color: #fff;
}
.discovery-btn {
border: none;
border-radius: 999px;
padding: 12px 18px;
font-size: 14px;
cursor: pointer;
transition: transform 0.2s ease, opacity 0.2s ease;
background: #fff;
color: #ff6a00;
}
.discovery-btn:hover {
transform: translateY(-1px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
}
.discovery-btn:active {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
background: rgba(255, 255, 255, 0.8);
}
.discovery-banner {
position: relative;
width: 100%;
max-width: 1300px;
height: 340px;
border-radius: 20px;
overflow: hidden;
}
.discovery-banner img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* RESPONSIVE */
@media (max-width: 900px) {
.hero {
min-height: 620px;
}
.hero-content {
padding: 90px 24px 40px;
}
.hero h1,
.section-heading h2,
.discovery-copy h2 {
font-size: 42px;
}
.product-grid {
grid-template-columns: repeat(2, 1fr);
}
.discovery-section {
grid-template-columns: 1fr;
}
}
@media (max-width: 640px) {
.hero {
margin: 12px;
min-height: 540px;
}
.hero h1,
.section-heading h2,
.discovery-copy h2 {
font-size: 34px;
}
.hero-actions {
flex-direction: column;
align-items: flex-start;
}
.product-grid {
grid-template-columns: 1fr;
}
.product-card {
min-height: 320px;
}
}

View File

@ -1,8 +1,9 @@
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { Link } from "react-router";
import { gsap } from "gsap"; import { gsap } from "gsap";
import perfumes from "../data/perfumes"; import perfumes from "../data/perfumes";
import "../App.css"; import "../pages/LandingPage.css";
import "../navbar.css"; import "../style/navbar.css";
function LandingPage() { function LandingPage() {
const cardRefs = useRef([]); const cardRefs = useRef([]);
@ -33,7 +34,9 @@ function LandingPage() {
video.pause(); video.pause();
try { try {
video.currentTime = 0; video.currentTime = 0;
} catch {} } catch {
// Ignore errors when setting currentTime
}
}; };
const playVideo = (video) => { const playVideo = (video) => {
@ -41,7 +44,9 @@ function LandingPage() {
try { try {
video.currentTime = 0; video.currentTime = 0;
} catch {} } catch {
// 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") {
@ -198,7 +203,8 @@ function LandingPage() {
<div className="product-grid"> <div className="product-grid">
{perfumes.map((item, index) => ( {perfumes.map((item, index) => (
<article <Link
to={`/duft/${item.slug}`}
className="product-card" className="product-card"
key={item.id} key={item.id}
ref={(el) => { ref={(el) => {
@ -238,7 +244,7 @@ function LandingPage() {
<p>{item.text}</p> <p>{item.text}</p>
<span className="arrow"></span> <span className="arrow"></span>
</div> </div>
</article> </Link>
))} ))}
</div> </div>
</section> </section>
@ -251,17 +257,17 @@ function LandingPage() {
DISCOVERY SET DISCOVERY SET
</h2> </h2>
<p> <p>
6 Samples × 2ml. Alle 6 Düfte als 2ml Samples 2ml.
<br /> <br />
Jeden Duft eine Woche tragen. Jeden Duft eine Woche tragen.
<br /> <br />
Verstehen, was funktioniert. Verstehen, was funktioniert.
</p> </p>
<button className="discovery-btn">Discovery Set bestellen</button>
</div> </div>
<div className="discovery-banner"> <div className="discovery-banner">
<img src="/DISCOVERYSET.png" alt="Discovery Set" /> <img src="/DISCOVERYSET.png" alt="Discovery Set" />
<button className="banner-btn">Discovery Set bestellen</button>
</div> </div>
</section> </section>
</main> </main>