feat: add about page for dubai-marina

This commit is contained in:
2025-05-14 16:38:43 +05:00
parent 34e249b8f8
commit d6cba1ef2c
35 changed files with 813 additions and 86 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+221
View File
@@ -0,0 +1,221 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, noarchive">
<meta name="format-detection" content="telephone=no">
<title>Transfonter demo</title>
<link href="stylesheet.css" rel="stylesheet">
<style>
/*
http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* demo styles */
body {
background: #f0f0f0;
color: #000;
}
.page {
background: #fff;
width: 920px;
margin: 0 auto;
padding: 20px 20px 0 20px;
overflow: hidden;
}
.font-container {
overflow-x: auto;
overflow-y: hidden;
margin-bottom: 40px;
line-height: 1.3;
white-space: nowrap;
padding-bottom: 5px;
}
h1 {
position: relative;
background: #444;
font-size: 32px;
color: #fff;
padding: 10px 20px;
margin: 0 -20px 12px -20px;
}
.letters {
font-size: 25px;
margin-bottom: 20px;
}
.s10:before {
content: '10px';
}
.s11:before {
content: '11px';
}
.s12:before {
content: '12px';
}
.s14:before {
content: '14px';
}
.s18:before {
content: '18px';
}
.s24:before {
content: '24px';
}
.s30:before {
content: '30px';
}
.s36:before {
content: '36px';
}
.s48:before {
content: '48px';
}
.s60:before {
content: '60px';
}
.s72:before {
content: '72px';
}
.s10:before, .s11:before, .s12:before, .s14:before,
.s18:before, .s24:before, .s30:before, .s36:before,
.s48:before, .s60:before, .s72:before {
font-family: Arial, sans-serif;
font-size: 10px;
font-weight: normal;
font-style: normal;
color: #999;
padding-right: 6px;
}
pre {
display: block;
padding: 9px;
margin: 0 0 12px;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 13px;
line-height: 1.428571429;
color: #333;
font-weight: normal;
font-style: normal;
background-color: #f5f5f5;
border: 1px solid #ccc;
overflow-x: auto;
border-radius: 4px;
}
/* responsive */
@media (max-width: 959px) {
.page {
width: auto;
margin: 0;
}
}
</style>
</head>
<body>
<div class="page">
<div class="demo">
<h1 style="font-family: 'Mixcase Mixed'; font-weight: normal; font-style: normal;">Mixcase Mixed</h1>
<pre title="Usage">.your-style {
font-family: 'Mixcase Mixed';
font-weight: normal;
font-style: normal;
}</pre>
<pre title="Preload (optional)">
&lt;link rel=&quot;preload&quot; href=&quot;MixcaseMixed.woff2&quot; as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&gt;</pre>
<div class="font-container" style="font-family: 'Mixcase Mixed'; font-weight: normal; font-style: normal;">
<p class="letters">
abcdefghijklmnopqrstuvwxyz<br>
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
0123456789.:,;()*!?'@#&lt;&gt;$%&^+-=~
</p>
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
</div>
</div>
<div class="demo">
<h1 style="font-family: 'Mixcase Unmixed'; font-weight: normal; font-style: normal;">Mixcase Unmixed</h1>
<pre title="Usage">.your-style {
font-family: 'Mixcase Unmixed';
font-weight: normal;
font-style: normal;
}</pre>
<pre title="Preload (optional)">
&lt;link rel=&quot;preload&quot; href=&quot;MixcaseUnmixed.woff2&quot; as=&quot;font&quot; type=&quot;font/woff2&quot; crossorigin&gt;</pre>
<div class="font-container" style="font-family: 'Mixcase Unmixed'; font-weight: normal; font-style: normal;">
<p class="letters">
abcdefghijklmnopqrstuvwxyz<br>
ABCDEFGHIJKLMNOPQRSTUVWXYZ<br>
0123456789.:,;()*!?'@#&lt;&gt;$%&^+-=~
</p>
<p class="s10" style="font-size: 10px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s11" style="font-size: 11px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s12" style="font-size: 12px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s14" style="font-size: 14px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s18" style="font-size: 18px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s24" style="font-size: 24px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s30" style="font-size: 30px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s36" style="font-size: 36px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s48" style="font-size: 48px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s60" style="font-size: 60px;">The quick brown fox jumps over the lazy dog.</p>
<p class="s72" style="font-size: 72px;">The quick brown fox jumps over the lazy dog.</p>
</div>
</div>
</div>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
@font-face {
font-family: 'Mixcase Mixed';
src: url('MixcaseMixed.eot');
src: local('Mixcase Mixed'), local('MixcaseMixed'),
url('MixcaseMixed.eot?#iefix') format('embedded-opentype'),
url('MixcaseMixed.woff2') format('woff2'),
url('MixcaseMixed.woff') format('woff'),
url('MixcaseMixed.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Mixcase Unmixed';
src: url('MixcaseUnmixed.eot');
src: local('Mixcase Unmixed'), local('MixcaseUnmixed'),
url('MixcaseUnmixed.eot?#iefix') format('embedded-opentype'),
url('MixcaseUnmixed.woff2') format('woff2'),
url('MixcaseUnmixed.woff') format('woff'),
url('MixcaseUnmixed.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+1 -1
View File
@@ -7,7 +7,7 @@ import TwitterIcon from './icons/TwitterIcon';
function Footer() {
return (
<footer className='2xl:px-[2.222vw] 2xl:pb-[2.222vw] 2xl:pt-[2.778vw] md:max-2xl:p-6 px-4 py-6 grid 2xl:grid-cols-6 md:max-2xl:grid-cols-4 grid-cols-2 2xl:grid-rows-2 2xl:gap-x-[1.667vw] 2xl:gap-y-[1.111vw] max-2xl:gap-y-6 2xl:rounded-t-[1.667vw] rounded-t-3xl outline outline-[#E2E2DC] bg-white'>
<footer className='z-1 2xl:px-[2.222vw] 2xl:pb-[2.222vw] 2xl:pt-[2.778vw] md:max-2xl:p-6 px-4 py-6 grid 2xl:grid-cols-6 md:max-2xl:grid-cols-4 grid-cols-2 2xl:grid-rows-2 2xl:gap-x-[1.667vw] 2xl:gap-y-[1.111vw] max-2xl:gap-y-6 2xl:rounded-t-[1.667vw] rounded-t-3xl outline outline-[#E2E2DC] bg-white'>
<img
src='/images/logo.svg'
className='2xl:w-[5.972vw] w-[86px] cursor-pointer'
+53 -48
View File
@@ -1,17 +1,17 @@
// import { sequenceVideos } from "../data/sequenceVideos";
import { useState, useRef } from "react";
import gsap from "gsap";
import { useSwipeable } from "react-swipeable";
import { motion, AnimatePresence } from "motion/react";
import Button from "./ui/Button";
import ArrowRightIcon from "./icons/map/ArrowRightIcon";
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
import Compass from "./Compass";
import { useNavigate } from "react-router";
import InfoIcon from "./icons/InfoIcon";
import FullScreenButton from "./FullScreenButton";
import PrivacyPolicyButton from "./PrivacyPolicyButton";
import DisclaimerButton from "./DisclaimerButton";
import { useState, useRef } from 'react';
import gsap from 'gsap';
import { useSwipeable } from 'react-swipeable';
import { motion, AnimatePresence } from 'motion/react';
import Button from './ui/Button';
import ArrowRightIcon from './icons/map/ArrowRightIcon';
import ArrowLeftIcon from './icons/ArrowLeftIcon';
import Compass from './Compass';
import { useNavigate } from 'react-router';
import InfoIcon from './icons/InfoIcon';
import FullScreenButton from './FullScreenButton';
import PrivacyPolicyButton from './PrivacyPolicyButton';
import DisclaimerButton from './DisclaimerButton';
interface SequenceSliderProps {
complexName: string;
@@ -25,8 +25,8 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
const [isAnimating, setIsAnimating] = useState(false);
const handlers = useSwipeable({
onSwipedLeft: () => handleSwipe("next"),
onSwipedRight: () => handleSwipe("prev"),
onSwipedLeft: () => handleSwipe('next'),
onSwipedRight: () => handleSwipe('prev'),
preventScrollOnSwipe: true,
touchEventOptions: {
passive: false,
@@ -37,7 +37,7 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
const [imageLoaded, setImageLoaded] = useState(0);
const [isShowVideo, setIsShowVideo] = useState(true);
const directionRef = useRef<"next" | "prev">("next");
const directionRef = useRef<'next' | 'prev'>('next');
const [isFullScreen, setIsFullScreen] = useState(false);
@@ -45,7 +45,7 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
setImageLoaded((prev) => prev + 1);
}
function handleSwipe(direction: "next" | "prev") {
function handleSwipe(direction: 'next' | 'prev') {
if (imageLoaded < FRAME_COUNT) return;
directionRef.current = direction;
setIsShowVideo(false);
@@ -53,18 +53,18 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
function handleLoadVideo() {}
function animate(direction: "next" | "prev") {
function animate(direction: 'next' | 'prev') {
setIsAnimating(true);
const targetIndex =
currentIndex + (direction === "next" ? FRAME_STEP : -FRAME_STEP); // -1, -2
currentIndex + (direction === 'next' ? FRAME_STEP : -FRAME_STEP); // -1, -2
gsap.to(
{ value: currentIndex },
{
value: targetIndex,
duration: 1,
ease: "power2.inOut",
ease: 'power2.inOut',
onUpdate: function () {
const currentValue = Math.round(this.targets()[0].value);
@@ -103,21 +103,21 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
handlers.ref(el);
rootRef.current = el;
}}
className="relative h-full overflow-hidden"
className='relative h-full overflow-hidden'
>
<AnimatePresence>
{imageLoaded < FRAME_COUNT && (
<motion.div
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-2 bg-white"
className='absolute inset-0 z-10 flex flex-col items-center justify-center gap-2 bg-white'
>
<img
src={`/images/loader.png`}
alt=""
className="w-16 h-16 animate-spin"
alt=''
className='w-16 h-16 animate-spin'
/>
<p className="text-[#00BED7] text-m">
<p className='text-[#00BED7] text-m'>
{Math.round((imageLoaded / FRAME_COUNT) * 100)}%
</p>
</motion.div>
@@ -128,10 +128,10 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
<img
key={index}
src={`/images/sequence/compressed-${
window.innerWidth < 768 ? "mobile" : "desktop"
window.innerWidth < 768 ? 'mobile' : 'desktop'
}/${index}.jpg`}
alt=""
className="absolute object-cover w-full h-full pointer-events-none"
alt=''
className='absolute object-cover w-full h-full pointer-events-none'
style={{
opacity: index === currentIndex ? 1 : 0,
}}
@@ -144,13 +144,13 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
<motion.video
key={currentIndex}
src={`/videos/sequence/${
window.innerWidth < 768 ? "mobile" : "desktop"
window.innerWidth < 768 ? 'mobile' : 'desktop'
}/${Math.floor(currentIndex / FRAME_STEP) + 1}.mp4`}
autoPlay
muted
loop
playsInline
className="absolute object-cover w-full h-full"
className='absolute object-cover w-full h-full'
onLoad={handleLoadVideo}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
@@ -166,54 +166,59 @@ function SequenceSlider({ complexName }: SequenceSliderProps) {
</AnimatePresence>
{imageLoaded === FRAME_COUNT && (
<>
<div className="absolute flex 2xl:gap-[0.556vw] justify-between gap-2 2xl:left-[2.222vw] 2xl:right-[2.222vw] 2xl:top-[2.222vw] max-w-full md:max-2xl:left-6 md:max-2xl:right-6 md:max-2xl:top-6 left-4 right-4 top-4">
<div className='absolute flex 2xl:gap-[0.556vw] justify-between gap-2 2xl:left-[2.222vw] 2xl:right-[2.222vw] 2xl:top-[2.222vw] max-w-full md:max-2xl:left-6 md:max-2xl:right-6 md:max-2xl:top-6 left-4 right-4 top-4'>
<Button
variant="secondary"
className="!bg-white"
onClick={() => navigate("/")}
variant='secondary'
className='!bg-white'
onClick={() => navigate('/')}
>
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
<span className='2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5'>
<ArrowLeftIcon />
</span>
<span className="max-md:hidden">Map</span>
<span className='max-md:hidden'>Map</span>
</Button>
<Button variant="secondary" size="small" className="max-md:hidden">
<span className="2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5">
<Button
variant='secondary'
size='small'
className='max-md:hidden'
onClick={() => navigate('./about')}
>
<span className='2xl:w-[1.389vw] 2xl:h-[1.389vw] w-5 h-5'>
<InfoIcon />
</span>
<span>About</span>
</Button>
</div>
<p className="absolute text-xl font-bold text-white -translate-x-1/2 select-none left-1/2 top-9 max-md:hidden">
<p className='absolute text-xl font-bold text-white -translate-x-1/2 select-none left-1/2 top-9 max-md:hidden'>
ROVE Home Marasi Drive
</p>
<Button
onlyIcon
variant="secondary"
className="absolute top-1/2 -translate-y-1/2 2xl:left-[31.111vw] md:max-2xl:left-[8.854vw] left-4 !bg-[#0D1922]/40 backdrop-blur-md"
variant='secondary'
className='absolute top-1/2 -translate-y-1/2 2xl:left-[31.111vw] md:max-2xl:left-[8.854vw] left-4 !bg-[#0D1922]/40 backdrop-blur-md'
roundedFull
disabled={isAnimating || !isShowVideo}
onClick={() => handleSwipe("prev")}
onClick={() => handleSwipe('prev')}
>
<span className="2xl:w-[1.111vw] 2xl:h-[1.111vw] w-4 h-4 text-white">
<span className='2xl:w-[1.111vw] 2xl:h-[1.111vw] w-4 h-4 text-white'>
<ArrowLeftIcon />
</span>
</Button>
<Button
onlyIcon
variant="secondary"
className="absolute top-1/2 -translate-y-1/2 2xl:right-[31.111vw] md:max-2xl:right-[8.854vw] right-4 !bg-[#0D1922]/40 backdrop-blur-md"
variant='secondary'
className='absolute top-1/2 -translate-y-1/2 2xl:right-[31.111vw] md:max-2xl:right-[8.854vw] right-4 !bg-[#0D1922]/40 backdrop-blur-md'
roundedFull
disabled={isAnimating || !isShowVideo}
onClick={() => handleSwipe("next")}
onClick={() => handleSwipe('next')}
>
<span className="2xl:w-[1.111vw] 2xl:h-[1.111vw] w-4 h-4 text-white">
<span className='2xl:w-[1.111vw] 2xl:h-[1.111vw] w-4 h-4 text-white'>
<ArrowRightIcon />
</span>
</Button>
<Compass imgStyle={{ transform: `rotate(${currentIndex}deg)` }} />
<div className="absolute 2xl:bottom-[2.222vw] 2xl:right-[2.222vw] 2xl:left-[2.222vw] max-w-full flex justify-end items-center 2xl:gap-[0.556vw] gap-2 md:max-2xl:bottom-6 md:max-2xl:left-6 md:max-2xl:right-6 bottom-4 left-4 right-4">
<div className='absolute 2xl:bottom-[2.222vw] 2xl:right-[2.222vw] 2xl:left-[2.222vw] max-w-full flex justify-end items-center 2xl:gap-[0.556vw] gap-2 md:max-2xl:bottom-6 md:max-2xl:left-6 md:max-2xl:right-6 bottom-4 left-4 right-4'>
<DisclaimerButton />
<PrivacyPolicyButton />
<FullScreenButton
+97
View File
@@ -0,0 +1,97 @@
import { useState } from 'react';
import ArrowLeftIcon from './icons/ArrowLeftIcon';
import ArrowRightIcon from './icons/map/ArrowRightIcon';
import { dubaiMarinaWellnessSlides } from '../data/aboutDubaiMarina';
import { AnimatePresence, motion } from 'motion/react';
function Slider() {
const [currentSlide, setCurrentSlide] = useState(0);
const [direction, setDirection] = useState(-1);
const handleNextSlide = () => {
if (currentSlide < dubaiMarinaWellnessSlides.length - 1) {
setDirection(1);
setCurrentSlide(currentSlide + 1);
}
};
const handlePreviousSlide = () => {
if (currentSlide > 0) {
setDirection(-1);
setCurrentSlide(currentSlide - 1);
}
};
return (
<div className='relative w-[63.333vw] h-[27.639vw] rounded-3xl overflow-hidden'>
<AnimatePresence custom={direction}>
<motion.div
key={currentSlide}
className='absolute inset-0 bg-cover bg-no-repeat bg-center'
style={{
backgroundImage: `url(${dubaiMarinaWellnessSlides[currentSlide].image})`,
}}
custom={direction}
initial={{ x: direction > 0 ? '100%' : '-100%' }}
animate={{ x: 0, transition: { duration: 0.5, ease: 'easeInOut' } }}
exit={{
x: direction > 0 ? '100%' : '-100%',
transition: { duration: 0.8, ease: 'easeInOut' },
}}
/>
</AnimatePresence>
<div className='relative z-10 w-full h-full flex flex-col justify-between p-6'>
<div className='flex flex-col gap-4'>
<AnimatePresence mode='wait'>
<motion.h3
key={`title-${currentSlide}`}
className='text-subheadline-m'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
{dubaiMarinaWellnessSlides[currentSlide].title}
</motion.h3>
</AnimatePresence>
<AnimatePresence mode='wait'>
<motion.p
key={`desc-${currentSlide}`}
className='text-s w-[19.861vw] tracking-[-0.07em] leading-[140%]'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
{dubaiMarinaWellnessSlides[currentSlide].description}
</motion.p>
</AnimatePresence>
</div>
<div className='flex items-center gap-[0.556vw] relative'>
<div
className='w-10 h-10 rounded-xl text-[#0D1922] bg-white flex items-center justify-center cursor-pointer hover:bg-[#F3F3F2] transition-all duration-200'
onClick={handlePreviousSlide}
>
<span className='w-5 h-5'>
<ArrowLeftIcon />
</span>
</div>
<div className='text-s'>
{currentSlide + 1}/{dubaiMarinaWellnessSlides.length}
</div>
<div
className='w-10 h-10 rounded-xl text-[#0D1922] bg-white flex items-center justify-center cursor-pointer hover:bg-[#F3F3F2] transition-all duration-200'
onClick={handleNextSlide}
>
<span className='w-5 h-5'>
<ArrowRightIcon />
</span>
</div>
</div>
</div>
</div>
);
}
export default Slider;
+9
View File
@@ -0,0 +1,9 @@
function TextBox({ text }: { text: string }) {
return (
<div className='text-caption-m px-4 py-1.5 border border-[#E2E2DC] rounded-[40px] w-fit text-[#0D1922]'>
{text}
</div>
);
}
export default TextBox;
+20
View File
@@ -0,0 +1,20 @@
function EqualIcon() {
return (
<svg
viewBox='0 0 20 20'
fill='currentColor'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M16.6654 7.64815C16.6654 7.29019 16.3752 7 16.0172 7H10.647H3.98018C3.62222 7 3.33203 7.29019 3.33203 7.64815C3.33203 8.00611 3.62222 8.2963 3.98018 8.2963H10.647H16.0172C16.3752 8.2963 16.6654 8.00611 16.6654 7.64815Z'
fill='currentColor'
/>
<path
d='M16.6654 12.2966C16.6654 11.9386 16.3752 11.6484 16.0172 11.6484H10.647H3.98018C3.62222 11.6484 3.33203 11.9386 3.33203 12.2966C3.33203 12.6545 3.62222 12.9447 3.98018 12.9447H10.647H16.0172C16.3752 12.9447 16.6654 12.6545 16.6654 12.2966Z'
fill='currentColor'
/>
</svg>
);
}
export default EqualIcon;
+75
View File
@@ -0,0 +1,75 @@
export const roveHomeDescription = [
'Fullyfurnished apartments',
'Vibrant art installations',
'Inspiring design',
] as const;
export const wellnessDescription = [
'Vitality Pool',
'Reflexology Pool',
'Cold Plunge Pool',
'Convertible Indoor Infinity Pool',
'Zen Library Area',
'Finnish Sauna Pod',
'Aroma Steam Pod',
'Salt Steam Pod',
'Ultra Shield Oxygen Pod',
'Experience Shower Pods',
] as const;
export const dubaiMarinaFeatures = [
{
name: 'Life-enhancing Amenties',
image: '/images/about-complex/dubai-marina/amenties.jpg',
},
{
name: 'Community',
image: '/images/about-complex/dubai-marina/community.jpg',
},
{
name: 'Wellness Focus',
image: '/images/about-complex/dubai-marina/wellness.jpg',
},
{
name: 'Rove-Style Design',
image: '/images/about-complex/dubai-marina/design.jpg',
},
{
name: 'Fully-Furnished Apartments',
image: '/images/about-complex/dubai-marina/furnished.jpg',
},
] as const;
export const dubaiMarinaDescription = [
{
title: 'Wellness',
description: 'Unlock your inner zen in our wellness playground',
},
{
title: 'Fitness',
description: 'Cancel all your membership. Your new home has it all',
},
{
title: 'Community',
description: 'Connect. Engage. Thrive.',
},
{
title: 'Convenience',
description: 'Your smart living hub',
},
] as const;
export const dubaiMarinaWellnessSlides = [
{
title: 'Indoor Infinity Pool',
description:
'Breezy and open in winter and closed in summer this one-of-a-kind convertible pool takes relaxation to new heights, 165 meters to be precise!',
image: '/images/about-complex/dubai-marina/pool1.jpg',
},
{
title: 'Multi-sensory wellness pods',
description:
'Set against a backdrop of dream-worthy views, immerse yourself in diverse wellness experiences including ultrashield oxygen, cold bucket showers and more.',
image: '/images/about-complex/dubai-marina/pool2.jpg',
},
] as const;
+16 -3
View File
@@ -1,12 +1,13 @@
@import url("/fonts/Usual/stylesheet.css");
@import "tailwindcss";
@import url('/fonts/Usual/stylesheet.css');
@import url('/fonts/Mixcase/stylesheet.css');
@import 'tailwindcss';
@theme {
--breakpoint-2xl: 1440px;
}
body {
font-family: "Usual", sans-serif;
font-family: 'Usual', sans-serif;
color: #0d1922;
}
@@ -20,6 +21,14 @@ button {
@apply 2xl:text-[clamp(56px,3.889vw,68px)] md:max-2xl:text-[clamp(44px,6.366vw,56px)] text-[44px] leading-none;
}
.text-h4 {
@apply 2xl:text-[clamp(20px,1.389vw,28px)] text-[20px] leading-[120%];
}
.text-h5 {
@apply 2xl:text-[clamp(14px,0.972vw,20px)] text-[14px] leading-[125%];
}
.text-subheadline-l {
@apply 2xl:text-[clamp(40px,2.778vw,48px)] md:max-2xl:text-[clamp(32px,4.63vw,40px)] text-[32px] leading-[135%];
}
@@ -48,3 +57,7 @@ button {
@apply 2xl:text-[clamp(10px,0.694vw,12px)] text-[10px] leading-[135%];
}
}
@utility font-mixcase-unmixed {
font-family: 'Mixcase Unmixed', sans-serif;
}
+13 -5
View File
@@ -1,12 +1,20 @@
import Header from "../components/Header";
import { Outlet } from "react-router";
import Footer from "../components/Footer";
import Header from '../components/Header';
import { Outlet, useLocation } from 'react-router';
import Footer from '../components/Footer';
import clsx from 'clsx';
function DefaultLayout() {
const { pathname } = useLocation();
console.log(pathname);
return (
<div className="min-h-dvh flex flex-col select-none bg-[#F3F3F2]">
<div
className={clsx(
'min-h-dvh flex flex-col select-none',
pathname.endsWith('/about') ? 'bg-white' : 'bg-[#F3F3F2]'
)}
>
<Header />
<div className="flex-1 flex flex-col justify-between">
<div className='flex-1 flex flex-col justify-between'>
<Outlet />
</div>
<Footer />
+28 -28
View File
@@ -1,66 +1,66 @@
import "./index.css";
import { QueryClientProvider } from "@tanstack/react-query";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router";
import DefaultLayout from "./layout/DefaultLayout.tsx";
import MainPage from "./pages/MainPage.tsx";
import ModalContainer from "./components/ModalContainer.tsx";
import ComplexPage from "./pages/ComplexPage.tsx";
import FloorsPage from "./pages/FloorsPage.tsx";
import UnitTypesPage from "./pages/UnitTypesPage.tsx";
import AboutPage from "./pages/AboutPages.tsx";
import FavoritesPage from "./pages/FavouritesPage.tsx";
import SearchPage from "./pages/SearchPage.tsx";
import LayoutWithoutFooter from "./layout/LayoutWithoutFooter.tsx";
import { queryClient } from "./lib/queryClient.ts";
import AboutComplexPage from "./pages/AboutComplexPage.tsx";
import './index.css';
import { QueryClientProvider } from '@tanstack/react-query';
import { createRoot } from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router';
import DefaultLayout from './layout/DefaultLayout.tsx';
import MainPage from './pages/MainPage.tsx';
import ModalContainer from './components/ModalContainer.tsx';
import ComplexPage from './pages/ComplexPage.tsx';
import FloorsPage from './pages/FloorsPage.tsx';
import UnitTypesPage from './pages/UnitTypesPage.tsx';
import AboutPage from './pages/AboutPages.tsx';
import FavoritesPage from './pages/FavouritesPage.tsx';
import SearchPage from './pages/SearchPage.tsx';
import LayoutWithoutFooter from './layout/LayoutWithoutFooter.tsx';
import { queryClient } from './lib/queryClient.ts';
import AboutComplexPage from './pages/AboutComplexPage.tsx';
const route = createBrowserRouter([
{
element: <DefaultLayout />,
children: [
{
path: "/unit-types",
path: '/unit-types',
element: <UnitTypesPage />,
},
{
path: "/about",
path: '/about',
element: <AboutPage />,
},
{
path: "/favorites",
path: '/favorites',
element: <FavoritesPage />,
},
{
path: "/search",
path: '/search',
element: <SearchPage />,
},
{
path: '/complex/:complexName/about',
element: <AboutComplexPage />,
},
],
},
{
element: <LayoutWithoutFooter />,
children: [
{
path: "/",
path: '/',
element: <MainPage />,
},
{
path: "/complex/:complexName",
path: '/complex/:complexName',
element: <ComplexPage />,
},
{
path: "/complex/:complexName/floors",
path: '/complex/:complexName/floors',
element: <FloorsPage />,
},
{
path: "/complex/:complexName/about",
element: <AboutComplexPage />,
},
],
},
]);
createRoot(document.getElementById("root")!).render(
createRoot(document.getElementById('root')!).render(
<>
<QueryClientProvider client={queryClient}>
<RouterProvider router={route} />
+254 -1
View File
@@ -1,5 +1,258 @@
import { useScroll, useTransform, motion, useInView } from 'motion/react';
import { useRef } from 'react';
import {
dubaiMarinaFeatures,
dubaiMarinaDescription,
roveHomeDescription,
wellnessDescription,
} from '../data/aboutDubaiMarina';
import EqualIcon from '../components/icons/EqualIcon';
import TextBox from '../components/TextBox';
import clsx from 'clsx';
import Slider from '../components/Slider';
import PlusIcon from '../components/icons/map/PlusIcon';
function AboutComplexPage() {
return <div></div>;
const containerRef = useRef(null);
const sliderRef = useRef(null);
const mapRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ['start start', 'end start'],
});
const firstSectionOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]);
const secondSectionY = useTransform(
scrollYProgress,
[0, 0.4],
['100dvh', '0dvh']
);
const isSliderInView = useInView(sliderRef, {
once: true,
amount: 0.1,
});
const isMapInView = useInView(mapRef, {
once: true,
margin: `0px 0px ${-window.innerHeight / 2}px 0px`,
});
return (
<div className='relative bg-white' ref={containerRef}>
<motion.section
className='h-[calc(100dvh-4.444vw)] w-full bg-white text-black fixed top-[4.444vw] left-0 right-0 z-0'
style={{
opacity: firstSectionOpacity,
}}
>
<div className='absolute inset-0'>
<img
src='/images/about-complex/dubai-marina/tower.jpg'
alt='dubai marina about'
className='h-full w-full object-contain object-top'
/>
<img
src='/images/about-complex/dubai-marina/logo.png'
alt='dubai marina about'
className='absolute top-[5vw] right-[2.778vw] w-[9.931vw] h-[4.444vw]'
/>
</div>
<div className='relative flex flex-col justify-between gap-[4.444vw] flex-1 w-full h-full pt-[5vw] pb-[2.5vw] pl-[2.222vw]'>
<div className='text-2xl font-bold whitespace-pre-line space-y-8'>
<h1 className='text-[5vw] leading-none tracking-[-0.07em] font-mixcase-unmixed font-[500]'>
{`Rove Home
Dubai Marina`}
</h1>
<div className='space-y-2 font-[400]'>
{roveHomeDescription.map((description) => (
<TextBox text={description} />
))}
</div>
</div>
<div className='space-y-8'>
<h4 className='text-h4 text-[#00BED7] w-[20.486vw] font-[500] '>
{`Own the last slice of
Dubai Marina, ROVE Style`}
</h4>
<p className='text-s text-[#0D1922B2] whitespace-pre-line'>
{`With an extended playlist of life-enhancing
amenities, Rove Home is a complete ecosystem
that has everything you'll ever need.`}
</p>
</div>
</div>
</motion.section>
<motion.section
className='w-full text-white bg-white flex justify-center top-[4.444vw] left-0 right-0'
style={{
y: secondSectionY,
zIndex: 1,
}}
>
<div className='w-full'>
<div className='flex flex-col items-center gap-[2.222vw] w-full pb-[3.75vw]'>
<div className='px-8 flex pt-[7.222vw] flex-col items-center gap-[2.222vw]'>
<h1 className='text-[3.889vw] text-[#0D1922] font-mixcase-unmixed'>
Rove Home has it all
</h1>
<p className='text-s text-[#0D1922B2] text-center tracking-[-0.02em] whitespace-pre-line'>
{`Rove Home Dubai Marina features modern-day
conveniences, carefully curated for an active
and social lifestyle.`}
</p>
</div>
<div className='flex gap-[0.556vw] mt-[0.833vw] h-[26.667vw] w-full px-[2.222vw]'>
{dubaiMarinaFeatures.map((feature) => (
<div
key={feature.name}
className={`rounded-3xl flex-1 h-[26.667vw] w-full flex flex-col items-center py-[1.667vw] justify-end bg-cover bg-center bg-no-repeat`}
style={{
backgroundImage: `url(${feature.image})`,
}}
>
<h5 className='text-h5 text-white tracking-[-0.02em]'>
{feature.name}
</h5>
</div>
))}
</div>
</div>
<div className='flex flex-col items-center pt-[4.444vw] h-auto px-8 gap-[4.444vw]'>
<h1 className='font-mixcase-unmixed text-[3.889vw] text-[#0D1922]'>
Dubai, <span className='text-[#0D192266]'>within reach</span>
</h1>
<div className='flex w-full text-[#0D192266] gap-[1.111vw]'>
{dubaiMarinaDescription.map((descriptionItem) => (
<div className='flex-1 text-center flex flex-col gap-[1.111vw]'>
<div className='h-[2px] bg-gray-300 w-full'></div>
<h5
className={clsx(
'text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500] ',
descriptionItem.title === 'Wellness' && 'text-[#00BED7]'
)}
>
{descriptionItem.title}
</h5>
<p
className={clsx(
'text-s leading-[125%] tracking-[-0.02em]',
descriptionItem.title === 'Wellness' && 'text-[#0D1922] '
)}
>
{descriptionItem.description}
</p>
</div>
))}
</div>
<motion.div
ref={sliderRef}
initial={{ opacity: 0, y: 40 }}
animate={
isSliderInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 140 }
}
transition={{ duration: 0.6, ease: 'easeOut' }}
className='flex flex-col gap-8'
>
<Slider />
<div className='flex gap-[0.556vw] w-[63.333vw] flex-wrap justify-center'>
{wellnessDescription.map((wellnessItem) => (
<TextBox text={wellnessItem} />
))}
</div>
</motion.div>
</div>
<div className='text-center w-full flex flex-col items-center gap-[2.222vw] bg-white'>
<h1 className='font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[8.056vw]'>
Dubai's first-ever combinable Apartments
</h1>
<TextBox text='On-demand' />
<p className='text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] whitespace-pre-line'>
{`Enjoy the option to combine 2 apartments and create
a larger space and configuration.`}
</p>
<div className='flex gap-4 relative'>
<div className='w-[19.028vw] text-[#0D1922] h-[27.778vw] p-[1.667vw] rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
Studio²
</h4>
</div>
<div className='w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center'>
<span className='w-5 h-5'>
<PlusIcon />
</span>
</div>
<div className='w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/studio.png)] bg-[length:7.778vw_17.847vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
Studio²
</h4>
</div>
<div className='w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[38.333vw] self-center'>
<span className='w-5 h-5'>
<EqualIcon />
</span>
</div>
<div className='w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom.png)] bg-[length:15.972vw_17.847vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
1 Bedroom²
</h4>
</div>
</div>
<div className='flex gap-4 relative'>
<div className='w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_studio.png)] bg-[length:13.125vw_17.847vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
Studio²
</h4>
</div>
<div className='w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[18.500vw] self-center'>
<span className='w-5 h-5'>
<PlusIcon />
</span>
</div>
<div className='w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/1_bedroom_rotated.png)] bg-[length:17.847vw_13.681vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
1 Bedroom²
</h4>
</div>
<div className='w-10 h-10 rounded-full bg-[#0D1922] flex items-center justify-center absolute left-[50.300vw] self-center'>
<span className='w-5 h-5'>
<EqualIcon />
</span>
</div>
<div className='w-[30.972vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/dubai-marina/2_bedroom.png)] bg-[length:19.028vw_17.847vw] bg-no-repeat bg-center'>
<h4 className='text-h4 tracking-[-0.02em] font-[500]'>
2 Bedroom²
</h4>
</div>
</div>
</div>
<div className='flex flex-col items-center gap-[2.222vw] bg-white px-[2.222vw] pb-[8.333vw]'>
<h1 className='font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[7.222vw]'>
Live central. Live centred
</h1>
<p className='text-s w-[35.278vw] text-[#0D1922B2] leading-[140%] tracking-[-0.02em] text-center whitespace-pre-line'>
{`Located in the heart of Dubai Marina, Rove Home Dubai Marina is where active
living meets modern convenience. Enjoy an energetic lifestyle surrounded by
trendy cafés, shops, and entertainment options all within reach.`}
</p>
<motion.img
ref={mapRef}
src='/images/about-complex/dubai-marina/central_map.jpg'
alt=''
className='rounded-3xl object-cover object-center aspect-[1376/609]'
initial={{ width: '47.083vw' }}
animate={
isMapInView ? { width: '95.556vw' } : { width: '47.083vw' }
}
transition={{ duration: 0.6, ease: 'easeInOut' }}
/>
</div>
</div>
</motion.section>
</div>
);
}
export default AboutComplexPage;