Merge branch 'homepage-refactoring' into 'dev'

Refactor the whole page.

See merge request rheinsw/website!26
This commit is contained in:
2025-04-11 19:08:43 +00:00
parent d19a90ce9a
commit 14ec81f4c2
33 changed files with 728 additions and 237 deletions

View File

@@ -0,0 +1,97 @@
'use client';
import React from 'react';
import Link from 'next/link';
import {motion} from 'framer-motion';
const Footer = () => {
return (
<motion.footer
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.6, ease: 'easeOut'}}
className="py-10 transition-theme text-white"
style={{
backgroundColor: '#16171f', // modern dark blue-purple tone
}}
>
<div className="w-[90%] mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-8">
{/* Logo and description */}
<div>
<h1 className="text-xl md:text-2xl font-bold text-white">
<span className="text-3xl md:text-4xl text-pink-700">R</span>hein Software
</h1>
</div>
{/* Informationen */}
<div>
<h3 className="text-lg font-semibold text-white">Informationen</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
<li>
<Link href="/contact">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Kontakt
</p>
</Link>
</li>
<li>
<Link href="/contact">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Zahlung und Versand
</p>
</Link>
</li>
</ul>
</div>
{/* Rechtliches */}
<div>
<h3 className="text-lg font-semibold text-white">Rechtliches</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold text-gray-400">
<li>
<Link href="/legal/terms-of-use">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
AGB
</p>
</Link>
</li>
<li>
<Link href="/legal/revocation">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Widerruf
</p>
</Link>
</li>
<li>
<Link href="/legal/privacy">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Datenschutz
</p>
</Link>
</li>
<li>
<Link href="/legal/imprint">
<p className="nav_link transition-all duration-300 ease-in-out hover:text-white">
Impressum
</p>
</Link>
</li>
</ul>
</div>
</div>
{/* Bottom Section */}
<div
className="mt-8 border-t border-gray-600 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-gray-400">
<p className="text-center md:text-left">
© 2025 Rhein Software Development. All rights reserved.
</p>
</div>
</div>
</motion.footer>
);
};
export default Footer;

View File

@@ -1,57 +1,81 @@
import Image from "next/image";
import React from "react";
'use client';
import Link from 'next/link';
import {FiArrowRight} from 'react-icons/fi';
import {motion} from 'framer-motion';
const About = () => {
return (
<div
className="pt-24 pb-16 transition-theme"
style={{
backgroundColor: "var(--secondary-bg)", // Using CSS variable
color: "var(--primary-text)", // Ensuring text color follows theme
transition: "background-color 0.5s ease, color 0.5s ease", // Smooth transition
}}>
<h1
className="mt-6 text-2xl md:text-3xl capitalize font-bold text-center"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}>
Über Uns
</h1>
<div className="w-[95%] sm:w-[80%] mx-auto items-center grid grid-cols-1 lg:grid-cols-2 gap-6 mt-10">
<div className="flex flex-col items-center" data-aos="fade-up" data-aos-anchor-placement="top-center">
<Image
src="/images/About_Picture.png"
alt="image"
width={400}
height={400}
className="object-contain"
<section className="relative w-full py-24 bg-[var(--secondary-bg)] transition-theme">
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<div className="flex flex-col">
{/* Title */}
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left"
style={{color: 'var(--primary-text)'}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
>
Über uns
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
</div>
<div className="p-0">
<p
className="mt-4 text-sm font-medium leading-[2rem]"
data-aos="fade-up"
data-aos-delay="200"
style={{color: "var(--secondary-text)"}}
{/* Text */}
<div className="p-0">
<motion.p
className="text-base md:text-lg leading-relaxed text-[var(--secondary-text)] max-w-4xl"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir sind Rhein-Software ein Team, das sich auf individuelle Softwarelösungen und digitale
Services spezialisiert hat. Unsere Anwendungen sind technisch solide, skalierbar und
durchdacht gebaut für
langfristigen Erfolg.
</motion.p>
<motion.p
className="mt-6 text-base md:text-lg leading-relaxed text-[var(--secondary-text)] max-w-4xl"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}}
>
Von der ersten Idee bis zum Go-Live begleiten wir Unternehmen und Startups mit einem
flexiblen Netzwerk, klarer Kommunikation und einem hohen Anspruch an Qualität. Unsere
Lösungen sind
intuitiv, effizient und genau auf deine Anforderungen zugeschnitten.
</motion.p>
</div>
{/* CTA Button */}
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}}
>
Wir sind Rhein Software Development ein Team, das sich auf maßgeschneiderte Softwarelösungen
und zuverlässige digitale Services spezialisiert hat. Unser Fokus liegt darauf, Software zu
entwickeln, die nicht nur funktioniert, sondern langfristig überzeugt.
</p>
<p
className="mt-4 text-sm font-medium leading-[2rem]"
data-aos="fade-up"
data-aos-delay="200"
style={{color: "var(--secondary-text)"}}
>
Von der ersten Idee bis zur fertigen Anwendung begleiten wir Unternehmen, Startups und
individuelle Projekte mit einer klaren Strategie und einem hohen Anspruch an Qualität. Wir
glauben daran, dass gute Software nicht kompliziert sein muss sondern effizient, intuitiv und
zukunftssicher.
</p>
<Link href="/about">
<button
className="flex items-center gap-2 bg-blue-700 hover:bg-blue-900 text-white font-semibold px-5 py-2 rounded-full shadow-lg transition-all">
Mehr über uns <FiArrowRight size={18}/>
</button>
</Link>
</motion.div>
</div>
</div>
</div>
</section>
);
};

View File

@@ -1,46 +1,56 @@
import Link from "next/link";
import {useContext} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
'use client';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { FiArrowRight } from 'react-icons/fi';
const ContactCTA = () => {
const {theme} = useContext(ThemeContext);
const colors = themeColors[theme];
return (
<div className="text-center py-16 transition-theme"
style={{backgroundColor: "var(--secondary-bg)"}}
<section
className="relative w-full py-24 transition-theme overflow-hidden"
style={{
background: 'linear-gradient(135deg, var(--secondary-bg), var(--primary-bg))',
}}
>
<h2 className="text-2xl md:text-3xl font-bold transition-theme"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}
>
Interesse geweckt?
</h2>
<p className="mt-4 text-sm md:text-base transition-theme"
data-aos="fade-up"
data-aos-delay="200"
style={{color: "var(--secondary-text)"}}
>
Lass uns über dein Projekt sprechen. Wir freuen uns darauf,
deine Ideen in die Realität umzusetzen.
</p>
<Link href="/contact"
data-aos="fade-up"
data-aos-delay="200"
>
<button className="mt-6 px-6 py-3 text-lg font-semibold rounded-lg shadow-md transition-theme"
data-aos="fade-up"
data-aos-delay="400"
style={{
backgroundColor: colors.primaryBg,
color: colors.secondaryText,
}}
<div className="w-full max-w-4xl px-6 md:px-10 mx-auto text-center">
{/* Headline */}
<motion.h2
className="text-3xl md:text-4xl font-bold text-[var(--primary-text)]"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
>
📩 Jetzt Kontakt aufnehmen
</button>
</Link>
</div>
Interesse geweckt?
</motion.h2>
{/* Description */}
<motion.p
className="mt-4 text-sm md:text-base text-[var(--secondary-text)] max-w-xl mx-auto"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.2 }}
>
Lass uns über dein Projekt sprechen. Wir freuen uns darauf, deine Ideen in die Realität umzusetzen.
</motion.p>
{/* CTA Button */}
<motion.div
className="mt-8 flex justify-center"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.3 }}
>
<Link href="/contact">
<button className="inline-flex items-center gap-2 px-6 py-3 text-sm md:text-base font-semibold rounded-full bg-blue-700 hover:bg-blue-900 text-white shadow-md transition-all duration-300">
Jetzt Kontakt aufnehmen <FiArrowRight size={18} />
</button>
</Link>
</motion.div>
</div>
</section>
);
};

View File

@@ -1,96 +0,0 @@
import React from "react";
import Link from "next/link";
const Footer = () => {
return (
<div className="py-10 transition-theme" style={{backgroundColor: "var(--footer-bg)"}}>
<div className="w-[90%] mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-8">
{/* Logo and description */}
<div>
{/* Logo */}
<h1 className="text-xl md:text-2xl font-bold transition-theme"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}
>
<span className="text-3xl md:text-4xl text-pink-700">R</span>hein Software
</h1>
</div>
{/* Our information links */}
<div>
<h3 className="text-lg font-semibold transition-theme"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}
>
Informationen
</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold transition-theme"
data-aos="fade-up"
data-aos-delay="200"
style={{color: "var(--secondary-text)"}}>
<li>
<Link href="/contact" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>Kontakt</p>
</Link>
</li>
<li>
<Link href="/contact" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>Zahlung und
Versand</p>
</Link>
</li>
</ul>
</div>
{/* About us links */}
<div>
<h3 className="text-lg font-semibold transition-theme"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}
>
Rechtliches
</h3>
<ul className="mt-4 space-y-4 text-sm font-semibold transition-theme"
data-aos="fade-up"
data-aos-delay="300"
style={{color: "var(--secondary-text)"}}
>
<li>
<Link href="/legal/terms-of-use" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>AGB</p>
</Link>
</li>
<li>
<Link href="/legal/revocation" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>Widerruf</p>
</Link>
</li>
<li>
<Link href="/legal/privacy" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>Datenschutz</p>
</Link>
</li>
<li>
<Link href="/legal/imprint" key="imprint">
<p className={`nav_link transition-all duration-300 ease-in-out`}>Impressum</p>
</Link>
</li>
</ul>
</div>
</div>
{/* Bottom Section */}
<div
className="mt-8 border-t pt-8 flex flex-col md:flex-row justify-between items-center text-sm transition-theme"
data-aos="fade-up"
style={{color: "var(--secondary-text)", borderColor: "var(--primary-text)"}}
>
<p className="text-center md:text-left">
Copyright © 2025 Rhein Software Development. All rights reserved
</p>
</div>
</div>
</div>
);
};
export default Footer;

View File

@@ -1,15 +1,16 @@
"use client";
'use client';
import Image from "next/image";
import {Typewriter} from "react-simple-typewriter";
import Image from 'next/image';
import {Typewriter} from 'react-simple-typewriter';
import {motion} from 'framer-motion';
const Hero = () => {
return (
<div
className="relative w-full pt-[4vh] md:pt-[12vh] h-screen flex flex-col overflow-hidden"
style={{
backgroundColor: "var(--primary-bg)",
color: "white",
backgroundColor: 'var(--primary-bg)',
color: 'white',
}}
>
{/* Background Image */}
@@ -25,56 +26,71 @@ const Hero = () => {
<div className="absolute inset-0 bg-black/40"/>
</div>
{/* Main Content */}
{/* Main content */}
<div className="relative z-10 flex justify-center flex-col w-[90%] sm:w-[80%] h-full mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12">
{/* Text Content */}
<div>
<h1
data-aos="fade-up"
<motion.h1
className="text-3xl sm:text-4xl md:text-5xl mt-6 mb-6 font-bold text-white"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, ease: 'easeOut'}}
>
Rhein-Software Development
</h1>
</motion.h1>
<p
data-aos="fade-up"
data-aos-delay="200"
<motion.p
className="text-lg md:text-xl text-gray-400"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, delay: 0.2, ease: 'easeOut'}}
>
Digitale Lösungen für dein Unternehmen.
</p>
</motion.p>
<p
data-aos="fade-up"
data-aos-delay="400"
<motion.p
className="mt-4 text-lg md:text-xl font-semibold text-white"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{delay: 0.9, duration: 0.6}}
>
<Typewriter
words={["Beratung", "Entwicklung", "Wartung", "Fehlerbehebung"]}
words={['Beratung', 'Entwicklung', 'Wartung', 'Fehlerbehebung']}
loop={true}
cursor
cursorStyle="_"
typeSpeed={60}
deleteSpeed={40}
delaySpeed={1500} // ⬅️ Delay before it starts typing the FIRST word
delaySpeed={1500}
/>
</p>
</motion.p>
</div>
{/* Floating Image */}
<div
data-aos="fade-up"
data-aos-delay="400"
className="hidden lg:block animate-float"
<motion.div
className="hidden lg:block"
initial={{opacity: 0, y: 30}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.8, ease: 'easeOut', delay: 0.3}}
>
<Image
src="/images/hero.png"
alt="hero graphic"
width={700}
height={700}
/>
</div>
<motion.div
animate={{y: [0, -10, 0]}}
transition={{
duration: 4,
repeat: Infinity,
ease: 'easeInOut',
}}
className="animate-float"
>
<Image
src="/images/hero.png"
alt="hero graphic"
width={700}
height={700}
/>
</motion.div>
</motion.div>
</div>
</div>
</div>

View File

@@ -1,38 +1,25 @@
"use client";
'use client';
import React, {useEffect} from "react";
import React from "react";
import Hero from "./Hero/Hero";
import AOS from "aos";
import "aos/dist/aos.css";
import About from "@/components/Home/About/About";
import Offer from "@/components/Home/Offer/Offer";
import ContactCTA from "@/components/Home/Contact/ContactCTA";
import {SectionDivider1, SectionDivider2} from "@/components/Helper/SectionDivider";
import HomeServices from "@/components/Home/HomeServices";
import TechStack from "@/components/Home/TechStack";
const Home = () => {
useEffect(() => {
const initAOS = async () => {
await import("aos");
AOS.init({
duration: 1000,
easing: "ease",
once: true,
anchorPlacement: "top-bottom",
});
};
initAOS();
}, []);
return (
<div className="overflow-hidden">
<Hero/>
<SectionDivider1/>
<About/>
<SectionDivider2/>
<Offer/>
<HomeServices/>
<SectionDivider1/>
<ContactCTA/>
<TechStack/>
<SectionDivider2/>
<ContactCTA/>
</div>
);
};

View File

@@ -0,0 +1,115 @@
'use client';
import {
FiServer,
FiTool,
FiMonitor,
FiZap,
FiArrowRight,
} from 'react-icons/fi';
import {motion} from 'framer-motion';
const services = [
{
title: 'Beratung',
icon: <FiMonitor size={24}/>,
description:
'Strategische und technische Beratung rund um digitale Produkte und Prozesse. Wir analysieren bestehende Systeme, identifizieren Potenziale und helfen dir, die passende Architektur für dein Projekt zu finden.',
},
{
title: 'Entwicklung',
icon: <FiZap size={24}/>,
description:
'Individuelle Softwareentwicklung skalierbar, wartbar, zukunftssicher. Ob Web-App, API oder internes Tool: Wir setzen moderne Technologien ein, um genau die Lösung zu bauen, die du brauchst.',
},
{
title: 'Managed Services',
icon: <FiServer size={24}/>,
description:
'Wir betreuen Infrastruktur, Server und Systeme verlässlich und performant. Unser Team kümmert sich um Hosting, Monitoring, Backups und sorgt für einen reibungslosen Betrieb deiner Plattform.',
},
{
title: 'Fehlerbehebung',
icon: <FiTool size={24}/>,
description:
'Schnelle Hilfe bei Bugs, Performance-Problemen oder Sicherheitslücken. Wir analysieren Probleme, beheben sie gezielt und sorgen dafür, dass dein System wieder stabil läuft langfristig und zuverlässig.',
},
];
const HomeServices = () => {
return (
<section
className="w-full py-24 bg-[var(--primary-bg)] transition-theme"
id="leistungen"
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
{/* Heading */}
<motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left text-[var(--primary-text)]"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
>
Leistungen
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-10 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
{/* Service Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{services.map((service, index) => (
<motion.div
key={service.title}
className="p-6 rounded-xl border border-[var(--secondary-bg)] bg-[var(--secondary-bg)] shadow-md"
whileHover={{
scale: 1.03,
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
}}
initial={{opacity: 0, y: 30}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{
duration: 0.4,
delay: index * 0.1,
ease: 'easeOut',
}}
>
<div className="mb-3 text-blue-600">{service.icon}</div>
<h3 className="text-xl font-semibold mb-2 text-[var(--primary-text)]">
{service.title}
</h3>
<p className="text-sm text-[var(--secondary-text)] leading-relaxed">
{service.description}
</p>
</motion.div>
))}
</div>
{/* Weitere Leistungen */}
<motion.div
className="mt-10 flex justify-end"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.3}}
>
<a
href="/services"
className="text-sm font-semibold text-blue-600 hover:underline flex items-center gap-1"
>
Weitere Leistungen <FiArrowRight size={16}/>
</a>
</motion.div>
</div>
</section>
);
};
export default HomeServices;

View File

@@ -0,0 +1,150 @@
'use client';
import Image from 'next/image';
import {motion} from 'framer-motion';
const techStack = {
row1: [
{
category: 'Programmiersprachen & Frameworks Backend',
items: [
{id: 'java', label: 'Java'},
{id: 'dart', label: 'Dart'},
{id: 'kotlin', label: 'Kotlin'},
{id: 'spring', label: 'Spring'},
],
},
{
category: 'Programmiersprachen & Frameworks Frontend',
items: [
{id: 'html', label: 'HTML'},
{id: 'css', label: 'CSS'},
{id: 'bootstrap', label: 'Bootstrap'},
{id: 'nextjs', label: 'Next.js'},
{id: 'typescript', label: 'TypeScript'},
{id: 'flutter', label: 'Flutter'},
],
},
],
row2: [
{
category: 'Betriebssysteme',
items: [
{id: 'macos', label: 'macOS'},
{id: 'debian', label: 'Debian'},
{id: 'ubuntu', label: 'Ubuntu'},
],
},
{
category: 'Version Control & Collaboration',
items: [
{id: 'gitlab', label: 'GitLab'},
{id: 'outline', label: 'Outline'},
],
},
{
category: 'DevOps & Infrastruktur',
items: [
{id: 'gitlab-ci', label: 'GitLab CI'},
{id: 'docker', label: 'Docker'},
{id: 'proxmox', label: 'Proxmox'},
],
},
],
};
const TechStack = () => {
return (
<section className="w-full py-20 bg-[var(--secondary-bg)] transition-theme">
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<motion.h2
className="text-2xl md:text-3xl font-bold mb-1 text-left text-[var(--primary-text)]"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
>
Technologien
</motion.h2>
<motion.div
className="w-12 h-[2px] mt-2 mb-6 bg-amber-500"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<motion.p
className="text-sm md:text-base mb-10 text-[var(--secondary-text)]"
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.2}}
>
Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen.
</motion.p>
{/* Row 1 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2}/>
))}
</div>
{/* Row 2 */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group, index) => (
<TechCard
key={group.category}
group={group}
delay={index * 0.2 + 0.4}
/>
))}
</div>
</div>
</section>
);
};
const TechCard = ({
group,
delay,
}: {
group: { category: string; items: { id: string; label: string }[] };
delay: number;
}) => (
<motion.div
className="p-4 rounded-lg border border-[var(--primary-bg)] bg-[var(--primary-bg)] shadow-md transition-theme"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
whileHover={{
scale: 1.03,
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
}}
viewport={{once: true}}
transition={{duration: 0.4, delay, ease: 'easeOut'}}
>
<h3 className="text-base font-semibold mb-4 text-[var(--primary-text)]">
{group.category}
</h3>
<div className="grid grid-cols-3 gap-3">
{group.items.map(({id, label}) => (
<div key={id} className="flex flex-col items-center text-center">
<Image
src={`/images/svg/${id}.svg`}
alt={label}
width={32}
height={32}
className="object-contain"
/>
<span className="text-[10px] mt-1 text-[var(--secondary-text)]">
{label}
</span>
</div>
))}
</div>
</motion.div>
);
export default TechStack;

View File

@@ -63,12 +63,18 @@ const Nav = ({openNav}: Props) => {
{navLinks.map((link) => (
<Link href={link.url} key={link.id}>
<p
className={`nav_link ${contentSize} uppercase transition-all duration-300 ease-in-out ${
className={`relative group ${contentSize} uppercase transition-all duration-300 ease-in-out ${
pathname === link.url
? "text-white font-bold"
: "text-gray-300 font-medium"
}`}>
}`}
>
{link.label}
{pathname !== link.url && (
<span
className="absolute bottom-0 left-0 w-full h-[2px] bg-current transform transition-transform duration-300 origin-right scale-x-0 group-hover:scale-x-100"
/>
)}
</p>
</Link>
))}