Add perfume detail routing, global navbar styles and chage radius on elements

This commit is contained in:
Salih Hasicic 2026-04-03 14:17:31 +02:00
parent a524a5d36a
commit 8140efb951
10 changed files with 523 additions and 492 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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>
);

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 { 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>