Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 236 additions & 0 deletions src/components/InteractiveFeatures.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import React, { useState, useEffect } from "react";

interface FeatureSlide {
id: number;
title: string;
description: string;
description2?: string;
image: string;
imageAlt: string;
}

interface InteractiveFeaturesProps {
features: FeatureSlide[];
}

const InteractiveFeatures: React.FC<InteractiveFeaturesProps> = ({
features,
}) => {
const [currentSlide, setCurrentSlide] = useState(0);

const updateSlide = (index: number) => {
setCurrentSlide(index);

// Update content with animation
const titleEl = document.getElementById("slide-title");
const descriptionEl = document.getElementById("slide-description");
const imageEl = document.getElementById("slide-image") as HTMLImageElement;

if (titleEl) {
// Fade out and slide up using Tailwind classes
titleEl.classList.remove("translate-y-0", "opacity-100");
titleEl.classList.add("translate-y-[-10px]", "opacity-0");

setTimeout(() => {
titleEl.textContent = features[index].title || "";
titleEl.classList.remove("translate-y-[-10px]", "opacity-0");
titleEl.classList.add("translate-y-0", "opacity-100");
}, 250);
}

if (descriptionEl) {
// Fade out and slide up using Tailwind classes
descriptionEl.classList.remove("translate-y-0", "opacity-100");
descriptionEl.classList.add("translate-y-[-10px]", "opacity-0");

setTimeout(() => {
descriptionEl.textContent = features[index].description || "";
descriptionEl.classList.remove("translate-y-[-10px]", "opacity-0");
descriptionEl.classList.add("translate-y-0", "opacity-100");
}, 250);
}

// Update second description if it exists
const description2El = document.getElementById("slide-description2");
if (description2El && features[index].description2) {
// Fade out and slide up using Tailwind classes
description2El.classList.remove("translate-y-0", "opacity-100");
description2El.classList.add("translate-y-[-10px]", "opacity-0");

setTimeout(() => {
description2El.textContent = features[index].description2 || "";
description2El.classList.remove("translate-y-[-10px]", "opacity-0");
description2El.classList.add("translate-y-0", "opacity-100");
}, 250);
}
if (imageEl) {
// Fade out and scale down using Tailwind classes
imageEl.classList.remove("scale-100", "opacity-100");
imageEl.classList.add("scale-95", "opacity-0");

setTimeout(() => {
imageEl.src = features[index].image;
imageEl.alt = features[index].imageAlt;
imageEl.classList.remove("scale-95", "opacity-0");
imageEl.classList.add("scale-100", "opacity-100");
}, 250);
}

// Update navigation dots (mobile)
const dots = document.querySelectorAll("#navigation-dots button");
dots.forEach((dot, i) => {
if (i === index) {
dot.classList.remove("bg-gray-600", "hover:bg-gray-500");
dot.classList.add("bg-white");
} else {
dot.classList.remove("bg-white");
dot.classList.add("bg-gray-600", "hover:bg-gray-500");
}
});

// Update navigation dots (desktop)
const dotsDesktop = document.querySelectorAll(
"#navigation-dots-desktop button"
);
dotsDesktop.forEach((dot, i) => {
if (i === index) {
dot.classList.remove("bg-gray-600", "hover:bg-gray-500");
dot.classList.add("bg-white");
} else {
dot.classList.remove("bg-white");
dot.classList.add("bg-gray-600", "hover:bg-gray-500");
}
});

// Update navigation arrows
const prevBtn = document.getElementById("prev-btn") as HTMLButtonElement;
const nextBtn = document.getElementById("next-btn") as HTMLButtonElement;

if (prevBtn) {
if (index === 0) {
prevBtn.classList.remove(
"text-[#02f1dc]",
"hover:text-[#02f1dc]/80",
"hover:bg-[#02f1dc]/10"
);
prevBtn.classList.add("text-gray-400", "cursor-pointer");
prevBtn.disabled = true;
} else {
prevBtn.classList.remove("text-gray-400", "cursor-pointer");
prevBtn.classList.add(
"text-[#02f1dc]",
"hover:text-[#02f1dc]/80",
"hover:bg-[#02f1dc]/10"
);
prevBtn.disabled = false;
}
}

if (nextBtn) {
if (index === features.length - 1) {
nextBtn.classList.remove(
"text-[#02f1dc]",
"hover:text-[#02f1dc]/80",
"hover:bg-[#02f1dc]/10"
);
nextBtn.classList.add("text-gray-400", "cursor-pointer");
nextBtn.disabled = true;
} else {
nextBtn.classList.remove("text-gray-400", "cursor-pointer");
nextBtn.classList.add(
"text-[#02f1dc]",
"hover:text-[#02f1dc]/80",
"hover:bg-[#02f1dc]/10"
);
nextBtn.disabled = false;
}
}
};

const nextSlide = () => {
if (currentSlide < features.length - 1) {
// Add bounce effect
const nextBtn = document.getElementById("next-btn");
if (nextBtn) {
nextBtn.style.transition = "transform 0.075s ease-out";
nextBtn.style.transform = "translateX(-8px)";
setTimeout(() => {
nextBtn.style.transform = "translateX(0)";
setTimeout(() => {
nextBtn.style.transition = "all 0.3s";
}, 75);
}, 75);
}
updateSlide(currentSlide + 1);
}
};

const prevSlide = () => {
if (currentSlide > 0) {
// Add bounce effect
const prevBtn = document.getElementById("prev-btn");
if (prevBtn) {
prevBtn.style.transition = "transform 0.075s ease-out";
prevBtn.style.transform = "translateX(8px)";
setTimeout(() => {
prevBtn.style.transform = "translateX(0)";
setTimeout(() => {
prevBtn.style.transition = "all 0.3s";
}, 75);
}, 75);
}
updateSlide(currentSlide - 1);
}
};

useEffect(() => {
// Navigation dots (mobile)
const dots = document.querySelectorAll("#navigation-dots button");
dots.forEach((dot) => {
dot.addEventListener("click", () => {
const slideIndex = parseInt(dot.getAttribute("data-slide") || "0");
updateSlide(slideIndex);
});
});

// Navigation dots (desktop)
const dotsDesktop = document.querySelectorAll(
"#navigation-dots-desktop button"
);
dotsDesktop.forEach((dot) => {
dot.addEventListener("click", () => {
const slideIndex = parseInt(dot.getAttribute("data-slide") || "0");
updateSlide(slideIndex);
});
});

// Navigation arrows
const prevBtn = document.getElementById("prev-btn");
const nextBtn = document.getElementById("next-btn");

if (prevBtn) {
prevBtn.addEventListener("click", prevSlide);
}

if (nextBtn) {
nextBtn.addEventListener("click", nextSlide);
}

// Cleanup event listeners
return () => {
dots.forEach((dot) => {
dot.removeEventListener("click", () => {});
});
dotsDesktop.forEach((dot) => {
dot.removeEventListener("click", () => {});
});
if (prevBtn) prevBtn.removeEventListener("click", prevSlide);
if (nextBtn) nextBtn.removeEventListener("click", nextSlide);
};
}, [currentSlide, features.length]);

// This component doesn't render anything, it just handles logic
return null;
};

export default InteractiveFeatures;
134 changes: 117 additions & 17 deletions src/components/ScreenFeatures.astro
Original file line number Diff line number Diff line change
@@ -1,37 +1,137 @@
---
import leftHand from "@/assets/left-hand.svg";
import rightHand from "@/assets/right-hand.svg";
import hyperliquid from "@/assets/hyperliquid-logo-green.svg";
import sui from "@/assets/sui-logo-white.png";
import wallet1 from "@/assets/wallet1.jpg";
import purroLogo from "@/assets/purro-logo.png";
import chrome from "@/assets/chrome.png";
import InteractiveFeatures from './InteractiveFeatures';

const features = [
{
id: 1,
title: "Trade smarter, not harder.",
description: "No switching tabs, no missing fees.",
description2: "Just clean UX, and all your assets in one view.",
image: wallet1.src,
imageAlt: "wallet"
},
{
id: 2,
title: "Unrivaled Throughput",
description: "Purro can scale transactions 10,000 times faster than current solutions,",
description2: "revolutionizing network capacity.",
image: chrome.src,
imageAlt: "throughput"
},
{
id: 3,
title: "Zero-Trust Security",
description: "Purro's ensures user assets remain secure even in worst-case scenarios,",
description2: "setting a new standard in decentralized security.",
image: purroLogo.src,
imageAlt: "security"
},
{
id: 4,
title: "Seamless Integration",
description: "Connect with your platforms and tools with just a few clicks.",
description2: "No complex setup required.",
image: wallet1.src,
imageAlt: "integration"
}
];

---

<div class="min-h-screen bg-gradient-to-b from-[#005b4a] via-[#03503c] to-[#033b32] relative overflow-hidden">
<div class="container max-w-7xl mx-auto py-20 px-4 flex flex-col md:flex-row items-center min-h-screen overflow-hidden">
<div class="w-full md:w-1/2 flex flex-col justify-center">
<div class="scroll-animate-left mb-15 md:mb-90">
<h2 class="text-3xl md:text-5xl text-center md:text-left text-white whitespace-nowrap">
Trade smarter, not harder.
<div class="container max-w-7xl mx-auto py-20 px-4 flex flex-col lg:flex-row items-center min-h-screen overflow-hidden">
<div class="w-full lg:w-1/2 flex flex-col justify-center relative">
<!-- Navigation Dots (Desktop) -->
<div class="hidden lg:flex gap-2 mb-10 lg:mt-15" id="navigation-dots-desktop">
{features.map((_, index) => (
<button
class={`w-4 h-4 rounded-full transition-all duration-300 ${
index === 0 ? 'bg-white' : 'bg-gray-600 hover:bg-gray-500'
}`}
data-slide={index}
aria-label={`Go to slide ${index + 1}`}
/>
))}
</div>

<!-- Content -->
<div class="scroll-animate-left mb-10">
<h2 class="text-3xl lg:text-5xl lg:mb-10 text-center lg:text-left text-white whitespace-nowrap transition-all duration-500 transform translate-y-0 opacity-100" id="slide-title">
{features[0].title}
</h2>
<p class="mt-2 text-sm md:text-lg text-center md:text-left text-white">
No switching tabs, no missing fees. <br />
Just clean UX, and all your assets in one view.
<p class="mt-2 text-sm lg:text-lg text-center lg:text-left text-white transition-all duration-500 transform translate-y-0 opacity-100" id="slide-description">
{features[0].description}
</p>
<p class="mt-1 text-sm lg:text-lg text-center lg:text-left text-white transition-all duration-500 transform translate-y-0 opacity-100" id="slide-description2">
{features[0].description2}
</p>
</div>

<div class="scroll-animate-left delay-200 lg:mt-25 mt-0 md:mb-15">
<p class="text-base md:text-lg text-center md:text-left text-white">Build on</p>
<div class="flex gap-4 md:gap-6 h-6 md:h-8 items-end mt-4 justify-center md:justify-start">
<!-- Navigation Arrows -->
<div class="hidden lg:flex gap-4 mb-50">
<button
id="prev-btn"
class="p-2 transition-all duration-300 text-gray-400 cursor-pointer hover:cursor-pointer hover:translate-x-[-4px] focus:outline-none hover:bg-transparent active:bg-transparent"
disabled
aria-label="Previous slide"
>
<svg width="40" height="24" viewBox="0 0 50 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M45 12H5M12 18L5 12L12 6" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<button
id="next-btn"
class="p-2 transition-all duration-300 text-[#02f1dc] hover:text-[#02f1dc]/80 hover:cursor-pointer hover:translate-x-[4px] focus:outline-none hover:bg-transparent active:bg-transparent"
aria-label="Next slide"
>
<svg width="40" height="24" viewBox="0 0 50 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12H45M38 18L45 12L38 6" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>

<!-- Build on and Logo -->
<div class="scroll-animate-left delay-200">
<p class="text-base lg:text-lg text-center lg:text-left text-white">
Build on
</p>
<div class="flex gap-4 lg:gap-6 h-6 lg:h-8 items-end mt-4 justify-center lg:justify-start">
<img src={hyperliquid.src} alt="Hyperliquid" class="h-full" />
<img src={sui.src} alt="Sui" class="h-full" />
</div>
</div>
</div>

<div class="w-full md:w-1/2 flex justify-center md:justify-end items-center mt-10">
<div class="scroll-animate-right delay-100">
<img src={wallet1.src} alt="wallet" class="w-full md:w-[75%] lg:w-[90%] xl:w-full object-contain" />
<!-- Image Features -->
<div class="w-full lg:w-1/2 flex flex-col justify-center lg:justify-end items-center lg:items-end mt-10">
<div class="scroll-animate-right delay-100 transition-all duration-500">
<img
src={features[0].image}
alt={features[0].imageAlt}
class="w-full lg:w-[80%] lg:mt-40 xl:mt-0 xl:w-full object-contain transition-all duration-500 rounded-3xl transform scale-100 opacity-100"
id="slide-image"
/>
</div>

<!-- Navigation Dots -->
<div class="flex gap-2 mt-8 lg:hidden justify-center" id="navigation-dots">
{features.map((_, index) => (
<button
class={`w-4 h-4 rounded-full transition-all duration-300 ${
index === 0 ? 'bg-white' : 'bg-gray-600 hover:bg-gray-500'
}`}
data-slide={index}
aria-label={`Go to slide ${index + 1}`}
/>
))}
</div>
</div>
</div>
</div>
</div>

<InteractiveFeatures features={features} client:load />