Add perfume detail routing, global navbar styles and chage radius on elements
This commit is contained in:
parent
a524a5d36a
commit
8140efb951
57
parfum-shop/package-lock.json
generated
57
parfum-shop/package-lock.json
generated
@ -10,7 +10,8 @@
|
||||
"dependencies": {
|
||||
"gsap": "^3.14.2",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4"
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router": "^7.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
@ -1050,9 +1051,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
|
||||
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1177,6 +1178,19 @@
|
||||
"dev": true,
|
||||
"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": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@ -2219,9 +2233,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -2295,6 +2309,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@ -2302,6 +2317,28 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@ -2370,6 +2407,12 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
"dependencies": {
|
||||
"gsap": "^3.14.2",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4"
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router": "^7.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
|
||||
@ -1,427 +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;
|
||||
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: #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: 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: #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: 24px;
|
||||
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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
import { Routes, Route } from "react-router";
|
||||
import LandingPage from "./pages/LandingPage";
|
||||
import ProductDetailPage from "./components/ProductDetailPage";
|
||||
|
||||
function App() {
|
||||
const showDetailPage = true;
|
||||
|
||||
if (showDetailPage) {
|
||||
return <ProductDetailPage perfumeSlug="kalter-beton" />;
|
||||
}
|
||||
|
||||
return <LandingPage />;
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<LandingPage />} />
|
||||
<Route path="/duft/:perfumeSlug" element={<ProductDetailPage />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@ -1,13 +1,3 @@
|
||||
/*
|
||||
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 --- */
|
||||
|
||||
.detail-page {
|
||||
@ -22,7 +12,7 @@
|
||||
.detail-shell {
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
padding: 38px;
|
||||
}
|
||||
|
||||
@ -64,7 +54,7 @@
|
||||
background: #d9d9d9;
|
||||
aspect-ratio: 1 / 1;
|
||||
overflow: hidden;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.detail-main-image img {
|
||||
@ -84,7 +74,7 @@
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
@ -155,7 +145,7 @@
|
||||
margin-top: 18px;
|
||||
background: #dfdfdf;
|
||||
padding: 16px;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.mood-label {
|
||||
@ -251,7 +241,7 @@
|
||||
min-height: 34px;
|
||||
padding: 8px 14px;
|
||||
border: 1px solid #d2d2d2;
|
||||
border-radius: 999px;
|
||||
border-radius: 0px;
|
||||
background: transparent;
|
||||
font-size: 12px;
|
||||
user-select: none;
|
||||
@ -269,7 +259,7 @@
|
||||
|
||||
.size-card {
|
||||
border: 1px solid #d0d0d0;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
background: #fff;
|
||||
padding: 18px;
|
||||
text-align: center;
|
||||
@ -306,7 +296,7 @@
|
||||
background: #1f1f1f;
|
||||
color: #fff;
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
border-radius: 0px;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
@ -326,7 +316,7 @@
|
||||
|
||||
.discovery-note-btn {
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
border-radius: 18px;
|
||||
background: #ff6a00;
|
||||
color: #fff;
|
||||
padding: 12px 18px;
|
||||
@ -350,7 +340,7 @@
|
||||
.buy-button {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 999px;
|
||||
border-radius: 0px;
|
||||
background: #ff6a00;
|
||||
color: #fff;
|
||||
padding: 18px;
|
||||
@ -393,7 +383,7 @@
|
||||
margin-top: 40px;
|
||||
padding: 40px 38px;
|
||||
background: #ff6a00;
|
||||
border-radius: 24px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.detail-bottom-cta h2 {
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { Link, useNavigate, useParams } from "react-router";
|
||||
import perfumes from "../data/perfumes";
|
||||
import "../navbar.css";
|
||||
import "../style/navbar.css";
|
||||
import "./ProductDetailPage.css";
|
||||
|
||||
function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
|
||||
function ProductDetailContent({ perfumeSlug }) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const perfume = useMemo(
|
||||
() => perfumes.find((item) => item.slug === perfumeSlug) || perfumes[0],
|
||||
[perfumeSlug]
|
||||
@ -31,33 +34,29 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
|
||||
|
||||
return (
|
||||
<div className="detail-page">
|
||||
{/* Navbar */}
|
||||
<nav className="navbar navbar--light">
|
||||
<div className="nav-pill">
|
||||
<a href="#home" className="nav-link active">
|
||||
<Link to="/" className="nav-link">
|
||||
Name
|
||||
</a>
|
||||
<a href="#dufte" className="nav-link">
|
||||
</Link>
|
||||
<Link to="/#dufte" className="nav-link active">
|
||||
Düfte
|
||||
</a>
|
||||
<a href="#testen" className="nav-link">
|
||||
</Link>
|
||||
<Link to="/#testen" className="nav-link">
|
||||
Testen
|
||||
</a>
|
||||
</Link>
|
||||
<a href="#cart" className="nav-link">
|
||||
Cart
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
{/* --- Navbar End --- */}
|
||||
|
||||
{/* Product detail content */}
|
||||
<main className="detail-shell">
|
||||
<button className="back-link" type="button">
|
||||
<button className="back-link" type="button" onClick={() => navigate("/")}>
|
||||
← Zurück zur Startseite
|
||||
</button>
|
||||
|
||||
<section className="detail-layout">
|
||||
{/* Left column */}
|
||||
<div className="detail-gallery">
|
||||
<div className="detail-main-image">
|
||||
<img src={selectedImage} alt={perfume.name} />
|
||||
@ -132,7 +131,6 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right column */}
|
||||
<div className="detail-info">
|
||||
<div className="detail-heading">
|
||||
<h1>{perfume.name}</h1>
|
||||
@ -174,10 +172,10 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
|
||||
automatisch abgezogen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button className="discovery-note-btn" type="button">
|
||||
Zum Set
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<button className="buy-button" type="button">
|
||||
@ -208,7 +206,6 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<section className="detail-bottom-cta">
|
||||
<h2>Lieber erst testen?</h2>
|
||||
<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;
|
||||
@ -1,10 +1,13 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.jsx'
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router";
|
||||
import App from "./App";
|
||||
import "./App.css";
|
||||
|
||||
createRoot(document.getElementById('root')).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
);
|
||||
397
parfum-shop/src/pages/LandingPage.css
Normal file
397
parfum-shop/src/pages/LandingPage.css
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { gsap } from "gsap";
|
||||
import perfumes from "../data/perfumes";
|
||||
import "../App.css";
|
||||
import "../navbar.css";
|
||||
import "../pages/LandingPage.css";
|
||||
import "../style/navbar.css";
|
||||
|
||||
function LandingPage() {
|
||||
const cardRefs = useRef([]);
|
||||
@ -33,7 +34,9 @@ function LandingPage() {
|
||||
video.pause();
|
||||
try {
|
||||
video.currentTime = 0;
|
||||
} catch {}
|
||||
} catch {
|
||||
// Ignore errors when setting currentTime
|
||||
}
|
||||
};
|
||||
|
||||
const playVideo = (video) => {
|
||||
@ -41,7 +44,9 @@ function LandingPage() {
|
||||
|
||||
try {
|
||||
video.currentTime = 0;
|
||||
} catch {}
|
||||
} catch {
|
||||
// Ignore errors when setting currentTime
|
||||
}
|
||||
|
||||
const playAttempt = video.play();
|
||||
if (playAttempt && typeof playAttempt.catch === "function") {
|
||||
@ -198,7 +203,8 @@ function LandingPage() {
|
||||
|
||||
<div className="product-grid">
|
||||
{perfumes.map((item, index) => (
|
||||
<article
|
||||
<Link
|
||||
to={`/duft/${item.slug}`}
|
||||
className="product-card"
|
||||
key={item.id}
|
||||
ref={(el) => {
|
||||
@ -238,7 +244,7 @@ function LandingPage() {
|
||||
<p>{item.text}</p>
|
||||
<span className="arrow">→</span>
|
||||
</div>
|
||||
</article>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user