diff --git a/src/components/features/BenefitsSection/BenefitsContent.tsx b/src/components/features/BenefitsSection/BenefitsContent.tsx new file mode 100644 index 0000000..a34c61f --- /dev/null +++ b/src/components/features/BenefitsSection/BenefitsContent.tsx @@ -0,0 +1,165 @@ +import { useState, useRef, useEffect } from "react"; +import { cn } from "@/util/cn"; +import HandshakeIcon from "./HandshakeIcon"; +import BriefcaseIcon from "./BriefcaseIcon"; +import CheckmarkIcon from "./CheckmarkIcon"; +import { FeatureCard } from "@/components/ui/cards/FeatureCard"; + +// Feature data +const FEATURES = [ + { + icon: HandshakeIcon, + title: "Connect", + content: "Network, collaborate with the Pandesal Dev community members on Discord. Whether you are looking for banter, sharing your portfolio, discussing your hobbies or seeking advice, our community is here to support and engage with you.", + variant: "lightblue", + orientation: "bottom", + }, + { + icon: CheckmarkIcon, + title: "Mentorship", + content: "Explore mentorship avenues in our tech community, where industry professionals volunteer their expertise to help individuals with mock interview practice, CV improvement, and providing career advice. Empower yourself with the guidance you need for career growth and skill enhancement.", + variant: "primary", + orientation: "top", + }, + { + icon: BriefcaseIcon, + title: "Job Opportunities", + content: "Browse through our community's comprehensive selection of job listings, including opportunities across various industries and experience levels. Whether you're seeking a new career path or looking for the perfect candidate, take a look at our available job openings here.", + variant: "pandesal", + orientation: "bottom", + }, +]; + + +export function BenefitsContent() { + const [currentSlide, setCurrentSlide] = useState(0); + const [isMobile, setIsMobile] = useState(false); + const carouselRef = useRef(null); + const touchStartX = useRef(0); + const touchEndX = useRef(0); + + // Check if screen is mobile + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth < 768); + }; + + // Set on initial load + checkIsMobile(); + + // Add resize listener + window.addEventListener('resize', checkIsMobile); + + // Clean up + return () => window.removeEventListener('resize', checkIsMobile); + }, []); + + // Handle next/prev slide + const goToSlide = (index: number) => { + let newIndex = index; + + // Handle bounds + if (index < 0) { + newIndex = FEATURES.length - 1; + } else if (index >= FEATURES.length) { + newIndex = 0; + } + + setCurrentSlide(newIndex); + }; + + // Touch handlers for swipe + const handleTouchStart = (e: React.TouchEvent) => { + touchStartX.current = e.touches[0].clientX; + }; + + const handleTouchMove = (e: React.TouchEvent) => { + touchEndX.current = e.touches[0].clientX; + }; + + const handleTouchEnd = () => { + const diff = touchStartX.current - touchEndX.current; + const threshold = 50; // minimum distance to be considered a swipe + + if (diff > threshold) { + // Swipe left, go to next + if (currentSlide != FEATURES.length - 1) { + goToSlide(currentSlide + 1); + } + } else if (diff < -threshold) { + // Swipe right, go to prev + if (currentSlide != 0) { + goToSlide(currentSlide - 1); + } + } + }; + + return ( +
+ {isMobile ? ( + // Mobile View (Carousel) +
+
+
+ {FEATURES.map((feature, index) => ( +
+ +
+ ))} +
+ + {/* Pagination Dots */} +
+ {FEATURES.map((_, index) => ( +
+
+
+ ) : ( + // Desktop View (Grid) +
+ {FEATURES.map((feature, index) => ( + + ))} +
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/components/features/BenefitsSection/BenefitsSection.tsx b/src/components/features/BenefitsSection/BenefitsSection.tsx new file mode 100644 index 0000000..1b24a01 --- /dev/null +++ b/src/components/features/BenefitsSection/BenefitsSection.tsx @@ -0,0 +1,26 @@ +import yellowStar from "@/assets/yellow-star.svg"; +import { delaGothic } from "@/components/ui/fonts"; +import { cn } from "@/util/cn"; +import { BenefitsContent } from "./BenefitsContent"; + +export default function BenefitsSection() { + return ( +
+
+

+ Be part of pandesal.dev +

+
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/features/BenefitsSection/BriefcaseIcon.tsx b/src/components/features/BenefitsSection/BriefcaseIcon.tsx new file mode 100644 index 0000000..c6cb0f9 --- /dev/null +++ b/src/components/features/BenefitsSection/BriefcaseIcon.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const BriefcaseIcon = (props: SVGProps) => ( + + + +); +export default BriefcaseIcon; diff --git a/src/components/features/BenefitsSection/CheckmarkIcon.tsx b/src/components/features/BenefitsSection/CheckmarkIcon.tsx new file mode 100644 index 0000000..8576599 --- /dev/null +++ b/src/components/features/BenefitsSection/CheckmarkIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const CheckmarkIcon = (props: SVGProps) => ( + + + +); +export default CheckmarkIcon; diff --git a/src/components/features/BenefitsSection/HandshakeIcon.tsx b/src/components/features/BenefitsSection/HandshakeIcon.tsx new file mode 100644 index 0000000..2c86ff1 --- /dev/null +++ b/src/components/features/BenefitsSection/HandshakeIcon.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { SVGProps } from "react"; + +const HandshakeIcon = (props: SVGProps) => ( + + + +); +export default HandshakeIcon; diff --git a/src/components/features/HeroSection/HeroContainer.tsx b/src/components/features/HeroSection/HeroContainer.tsx index f63367c..a50db1a 100644 --- a/src/components/features/HeroSection/HeroContainer.tsx +++ b/src/components/features/HeroSection/HeroContainer.tsx @@ -4,6 +4,6 @@ interface ContainerProps { className?: string; } export function HeroContainer({ children, className }: ContainerProps) { - return
{children}
; + return
{children}
; } diff --git a/src/components/layout.tsx/Layout.tsx b/src/components/layout.tsx/Layout.tsx index 124a859..62143dc 100644 --- a/src/components/layout.tsx/Layout.tsx +++ b/src/components/layout.tsx/Layout.tsx @@ -2,5 +2,11 @@ import React, { PropsWithChildren } from "react"; export const Layout: React.FC = ({ children }) => { - return
{children}
; + return ( +
+
+ {children} +
+
+ ) }; diff --git a/src/components/ui/cards/Card.tsx b/src/components/ui/cards/Card.tsx new file mode 100644 index 0000000..ea15cb8 --- /dev/null +++ b/src/components/ui/cards/Card.tsx @@ -0,0 +1,77 @@ +import * as React from "react" + +import { cn } from "@/util/cn" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } \ No newline at end of file diff --git a/src/components/ui/cards/FeatureCard.tsx b/src/components/ui/cards/FeatureCard.tsx new file mode 100644 index 0000000..cabc13e --- /dev/null +++ b/src/components/ui/cards/FeatureCard.tsx @@ -0,0 +1,135 @@ +import React from "react"; +import { Card, CardHeader, CardTitle, CardDescription } from "./Card"; +import { cn } from "@/util/cn"; +import { delaGothic } from "../fonts"; + +const variants = { + primary: { + card: "text-primary hover:text-background group-hover:text-primary hover:bg-primary hover:shadow-md hover:shadow-primary hover:border-background", + index: "group-hover:text-primary", + }, + lightblue: { + card: "text-lightblue hover:text-background group-hover:text-lightblue hover:bg-lightblue hover:shadow-md hover:shadow-lightblue hover:border-background", + index: "group-hover:text-lightblue", + }, + pandesal: { + card: "text-pandesal hover:text-background group-hover:text-pandesal hover:bg-pandesal hover:shadow-md hover:shadow-pandesal hover:border-background", + index: "group-hover:text-pandesal", + }, +}; + +type VariantType = keyof typeof variants; + +interface FeatureCardProps { + id?: number; + icon: React.ElementType; + title: string; + content: string; + variant?: VariantType; + orientation?: "top" | "bottom"; + className?: string; + children?: React.ReactNode; + showBackgroundText?: boolean; +} + +interface IndexProps { + text?: string; + position?: "top" | "bottom"; + variant?: VariantType; +} + +// Internal BackgroundText component +const Index: React.FC = ({ + text = "text", + position = "bottom", + variant = "primary" +}) => { + const positionClasses = { + top: "-right-10 -top-40", + bottom: "-right-10 -bottom-60", + }; + + return ( +
+ {/* Outline */} +
+ {text} +
+ {/* Body */} +
+ {text} +
+
+ ); +}; + +const FeatureCard = ({ + id, + icon: Icon, + title, + content, + variant = "primary", + orientation = "bottom", + className, + children, + showBackgroundText = false +}: FeatureCardProps) => { + return ( + + {orientation === "top" && +
+ {showBackgroundText && ( + + )} +
+ } + + {Icon && } + + {title} + + + {content} + + + {orientation === "bottom" && +
+ {showBackgroundText && ( + + )} +
+ } + {children} +
+ ); +}; + +export { FeatureCard }; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0beb1e8..433e200 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,3 +1,4 @@ +import BenefitsSection from "@/components/features/BenefitsSection/BenefitsSection"; import HeroSection from "@/components/features/HeroSection/HeroSection"; import { Layout } from "@/components/layout.tsx/Layout"; import NavBar from "@/components/ui/navigation/NavBar"; @@ -7,7 +8,10 @@ export default function Home() { <> - {/* Other sections here */} + + {/* Other sections here */} + + ); } diff --git a/src/styles/globals.css b/src/styles/globals.css index b3001ac..f120e6d 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -9,6 +9,8 @@ --primary: hsl(105.22, 48.25%, 71.96%); /* #A6DA95 */ --secondary: hsl(40.25, 69.91%, 77.84%); /* #EED49F */ --accent: hsl(266.51, 82.69%, 79.61%); /* #C6A0F6 */ + --lightblue: hsl(199, 66%, 69%); /* #7DC4E4 */ + --pandesal: hsl(21, 86%, 73%); /* #F5A97F */ --hero-glow-opacity: 0.07; --hero-glow: hsl(36 83% 65% / var(--hero-glow-opacity)); @@ -28,12 +30,16 @@ --color-primary: var(--primary); --color-secondary: var(--secondary); --color-accent: var(--accent); + --color-lightblue: var(--lightblue); + --color-pandesal: var(--pandesal); --color-glow: var(--hero-glow); --ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1, 0.035); --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); /* --font-dela: var(--font-dela); */ --text-h1: clamp(2.15rem, 7vw, 75px); + --text-h2: clamp(1.375rem, 5vw, 48px); + --text-h3: clamp(1rem, 3.5vw, 36px); --text-p-sm: clamp(0.9rem, 3.5vw, 1.5rem); --border-accent: 'var(--accent)';