diff --git a/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx b/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx
index 3420828ea1..e800af8409 100644
--- a/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx
+++ b/src/ui/design-system/src/lib/Components/HomeHeader/index.tsx
@@ -7,12 +7,13 @@ export const HomeHeader: React.FC = () => {
-
+
-
+ {/* TODO: Add back in after iteration */}
+ {/*
-
+
*/}
diff --git a/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx b/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx
index 19675cbd46..9340798e13 100644
--- a/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx
+++ b/src/ui/design-system/src/lib/Components/NewsCarousel/index.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import ActionCard from '@site/src/components/ActionCard';
interface CarouselCard {
@@ -13,62 +13,181 @@ interface CarouselCard {
const CAROUSEL_CARDS: CarouselCard[] = [
{
- heading: 'We got a castle!',
- description: 'Apply to be a Flow Scholar to work alongside our team, join us at the Flow Hacker Castle, and represent the Flow community—all on us!',
+ heading: 'Get the newsletter!',
+ description: 'We send out a newsletter every week with the latest news for Flow developers. Sign up to get it in your inbox!',
iconColor: 'green',
cardColor: 'black',
- href: 'https://x.com/flow_blockchain/status/1880405924407587173',
+ href: 'https://e.customeriomail.com/manage_subscription_preferences/dgTdoQoDAI7HAY3HAQGWxJT9wbM-Af61EgxuM6E=',
variant: 'horizontal' as const,
- // icon: 'flow-castle'
+ icon: 'learn'
},
{
- heading: 'Cadence Tutorials',
- description: 'We\'ve updated the first set of Cadence tutorials to be more approachable. Let us know what you think!',
- iconColor: 'blue',
+ heading: 'May the Flow be with You!',
+ description: 'We are wrapping up our month-long vibe coding challenge on Flow! Check out the winners, or submit for the last week!',
+ iconColor: 'green',
cardColor: 'black',
- href: 'https://cadence-lang.org/docs/tutorial/first-steps',
+ href: '/ecosystem/Hackathons%20and%20Events/may-the-flow-be-with-you',
variant: 'horizontal' as const,
- icon: 'tutorials'
+ icon: 'vcs-&-funds'
},
{
- heading: 'Flow is Live on Axelar',
- description: '"With this integration, Flow users, builders and dApps get access to best-in-class interoperability, opening up liquidity from 70+ chains 🦾 🤝"',
+ heading: 'ETH Global Prague',
+ description: 'Earn up to $10,000 in prizes for building on Flow at one of the biggest hackathons in Europe!',
iconColor: 'purple',
cardColor: 'black',
- href: 'https://x.com/axelar/status/1882066175175360998',
+ href: 'https://ethglobal.com/events/prague/prizes/flow',
+ variant: 'horizontal' as const,
+ icon: 'grants'
+ },
+ {
+ heading: 'EthCC Cannes',
+ description: 'Earn up to $20,000 in prizes at the largest annual Ethereum conference in Europe!',
+ iconColor: 'blue',
+ cardColor: 'black',
+ href: 'https://ethglobal.com/events/cannes/prizes/flow',
variant: 'horizontal' as const,
- icon: 'cross-vm-bridge'
+ icon: 'grants'
},
];
export const NewsCarousel: React.FC = () => {
- const [currentIndex, setCurrentIndex] = useState(0);
+ const [activeIndex, setActiveIndex] = useState(0);
+ const [isTransitioning, setIsTransitioning] = useState(false);
+ const [cardsToShow, setCardsToShow] = useState(1);
+ const totalCards = CAROUSEL_CARDS.length;
+ // Calculate maxIndex properly to prevent empty segments
+ const maxIndex = Math.max(0, totalCards - cardsToShow);
+
+ // Initialize cardsToShow based on screen width
+ useEffect(() => {
+ const updateCardsToShow = () => {
+ setCardsToShow(window.innerWidth >= 1024 ? 2 : 1);
+ };
+
+ updateCardsToShow();
+ window.addEventListener('resize', updateCardsToShow);
+ return () => window.removeEventListener('resize', updateCardsToShow);
+ }, []);
+
+ // Handle automatic sliding
+ useEffect(() => {
+ const timer = setInterval(() => {
+ if (!isTransitioning) {
+ handleNext();
+ }
+ }, 5000); // Auto slide every 5 seconds
- const nextSlide = () => {
- setCurrentIndex((prevIndex) => (prevIndex + 1) % CAROUSEL_CARDS.length);
- };
+ return () => clearInterval(timer);
+ }, [activeIndex, isTransitioning]);
- const prevSlide = () => {
- setCurrentIndex((prevIndex) =>
- prevIndex - 1 < 0 ? CAROUSEL_CARDS.length - 1 : prevIndex - 1
- );
- };
+ const handleNext = useCallback(() => {
+ if (isTransitioning) return;
+
+ setIsTransitioning(true);
+
+ // Loop back to beginning if at the end
+ if (activeIndex >= maxIndex) {
+ setActiveIndex(0);
+ } else {
+ setActiveIndex(prev => prev + 1);
+ }
+
+ setTimeout(() => {
+ setIsTransitioning(false);
+ }, 600);
+ }, [activeIndex, isTransitioning, maxIndex]);
- const getVisibleCards = () => {
- if (CAROUSEL_CARDS.length < 2) return [CAROUSEL_CARDS[0]];
+ const handlePrev = useCallback(() => {
+ if (isTransitioning) return;
+
+ setIsTransitioning(true);
+
+ // Loop to the end if at the beginning
+ if (activeIndex <= 0) {
+ setActiveIndex(maxIndex);
+ } else {
+ setActiveIndex(prev => prev - 1);
+ }
+
+ setTimeout(() => {
+ setIsTransitioning(false);
+ }, 600);
+ }, [activeIndex, isTransitioning, maxIndex]);
- const secondIndex = (currentIndex + 1) % CAROUSEL_CARDS.length;
- return [CAROUSEL_CARDS[currentIndex], CAROUSEL_CARDS[secondIndex]];
- };
+ const handleDotClick = useCallback((index: number) => {
+ if (isTransitioning || index === activeIndex) return;
+
+ setIsTransitioning(true);
+ setActiveIndex(index);
+
+ setTimeout(() => {
+ setIsTransitioning(false);
+ }, 600);
+ }, [activeIndex, isTransitioning]);
return (
-
+
+ {/* Card Container */}
+
+
+ {CAROUSEL_CARDS.map((card, index) => (
+
+
{
+ if (card.href.startsWith('https://')) {
+ window.open(card.href, '_blank');
+ } else {
+ window.location.href = card.href;
+ }
+ }}
+ />
+
+ ))}
+
+
+
{/* Navigation Buttons */}
-
+
+
+
+
+ {/* Mobile navigation buttons */}
+
-
-
- {/* Cards Grid */}
-
- {getVisibleCards().map((card, index) => (
-
-
{
- if (card.href.startsWith('https://')) {
- window.open(card.href, '_blank');
- } else {
- window.location.href = card.href;
- }
- }}
+
+ {/* Dots indicator for mobile */}
+
+ {Array.from({ length: maxIndex + 1 }).map((_, index) => (
+
+ ))}
+
+
+
+
+
+ {/* Desktop progress indicator */}
+
+ {Array.from({ length: maxIndex + 1 }).map((_, index) => (
+
diff --git a/src/ui/design-system/src/lib/Pages/HomePage/index.tsx b/src/ui/design-system/src/lib/Pages/HomePage/index.tsx
index c1d1eda4dd..84de77b09c 100644
--- a/src/ui/design-system/src/lib/Pages/HomePage/index.tsx
+++ b/src/ui/design-system/src/lib/Pages/HomePage/index.tsx
@@ -10,7 +10,7 @@ import PageBackground from '../shared/PageBackground';
import ActionCardGrid from '@site/src/components/ActionCardGrid';
import { buildGridData } from './GridData/BuildGridData';
import { growGridData } from './GridData/GrowGridData';
-// import { HomeHeader } from '../../Components/HomeHeader';
+import { HomeHeader } from '../../Components/HomeHeader';
export type HomePageProps = SocialLinksSignupProps & {
concepts?: TutorialCardProps[];
@@ -21,8 +21,7 @@ export type HomePageProps = SocialLinksSignupProps & {
const HomePage = ({ discordUrl, githubUrl }: HomePageProps): JSX.Element => {
return (
- {/* TODO: Add back in after iteration */}
- {/* */}
+