{item.text}
→diff --git a/parfum-shop/package-lock.json b/parfum-shop/package-lock.json
index e82d816..dccf42d 100644
--- a/parfum-shop/package-lock.json
+++ b/parfum-shop/package-lock.json
@@ -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",
diff --git a/parfum-shop/package.json b/parfum-shop/package.json
index 70e395e..aa76d48 100644
--- a/parfum-shop/package.json
+++ b/parfum-shop/package.json
@@ -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",
diff --git a/parfum-shop/src/App.css b/parfum-shop/src/App.css
index ac0400d..01e7450 100644
--- a/parfum-shop/src/App.css
+++ b/parfum-shop/src/App.css
@@ -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;
- }
-
-
-}
+}
\ No newline at end of file
diff --git a/parfum-shop/src/App.jsx b/parfum-shop/src/App.jsx
index cacd0fa..f007635 100644
--- a/parfum-shop/src/App.jsx
+++ b/parfum-shop/src/App.jsx
@@ -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
@@ -227,4 +224,10 @@ function ProductDetailPage({ perfumeSlug = "kalter-beton" }) {
);
}
+function ProductDetailPage() {
+ const { perfumeSlug = "kalter-beton" } = useParams();
+
+ return
{item.text}
→