Merge branch 'homepage-refactoring' into 'dev'

Homepage Refactoring - Pt .3

See merge request rheinsw/website!28
This commit is contained in:
2025-04-16 17:37:41 +00:00
36 changed files with 896 additions and 473 deletions

View File

@@ -6,6 +6,7 @@ import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider"; import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react"; import React from "react";
import {cookies} from "next/headers"; import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Rhein Software", title: "Rhein Software",
@@ -19,11 +20,12 @@ export default async function RootLayout({
}>) { }>) {
const cookieStore = await cookies(); const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light"; const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return ( return (
<html lang="de" data-theme={theme}> <html lang="de" data-theme={theme}>
<head/> <head/>
<body className="antialiased" style={{backgroundColor: "var(--primary-bg)"}}> <body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider> <ThemeProvider>
<Nav/> <Nav/>
{children} {children}

View File

@@ -6,9 +6,10 @@ import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider"; import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react"; import React from "react";
import {cookies} from "next/headers"; import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Rhein Software", title: "Kontakt | Rhein Software",
description: "Rhein Software Development", description: "Rhein Software Development",
}; };
@@ -19,11 +20,12 @@ export default async function RootLayout({
}>) { }>) {
const cookieStore = await cookies(); const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light"; const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return ( return (
<html lang="de" data-theme={theme}> <html lang="de" data-theme={theme}>
<head/> <head/>
<body className="antialiased" style={{backgroundColor: "var(--primary-bg)"}}> <body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider> <ThemeProvider>
<Nav/> <Nav/>
{children} {children}

View File

@@ -13,27 +13,6 @@
transition: background-color 0.7s ease, color 0.7s ease; transition: background-color 0.7s ease, color 0.7s ease;
} }
/* Light mode */
/* Light mode */
[data-theme="light"] {
--primary-bg: #FAFAFA; /* Soft off-white background */
--secondary-bg: #F5F5F7; /* Gentle off-white for subtle separation */
--primary-text: #2E2E2E; /* Dark grey text for clear readability */
--secondary-text: #595959; /* Medium grey for less prominent text */
--nav-bg: #F8F8F8; /* Light grey navigation background */
--footer-bg: #E0E0E0; /* Slightly darker grey for footer contrast */
}
/* Dark mode */
[data-theme="dark"] {
--primary-bg: #2C2C2C; /* Modern dark grey background */
--secondary-bg: #333333; /* A touch lighter grey for section differentiation */
--primary-text: #E0E0E0; /* Light grey text for optimal readability */
--secondary-text: #B0B0B0; /* Medium grey for secondary text elements */
--nav-bg: #272727; /* Subtle variation for the navigation area */
--footer-bg: #242424; /* Deep grey footer to add visual depth */
}
@keyframes float { @keyframes float {
0%, 100% { 0%, 100% {
transform: translateY(0); transform: translateY(0);

View File

@@ -6,9 +6,10 @@ import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider"; import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react"; import React from "react";
import {cookies} from "next/headers"; import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Rhein Software", title: "Rechtliches | Rhein Software",
description: "Rhein Software Development", description: "Rhein Software Development",
}; };
@@ -19,11 +20,12 @@ export default async function RootLayout({
}>) { }>) {
const cookieStore = await cookies(); const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light"; const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return ( return (
<html lang="de" data-theme={theme}> <html lang="de" data-theme={theme}>
<head/> <head/>
<body className="antialiased" style={{backgroundColor: "var(--primary-bg)"}}> <body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider> <ThemeProvider>
<Nav/> <Nav/>
{children} {children}
@@ -33,3 +35,4 @@ export default async function RootLayout({
</html> </html>
); );
} }

View File

@@ -6,9 +6,10 @@ import Footer from "@/components/Footer/Footer";
import {ThemeProvider} from "@/components/provider/ThemeProvider"; import {ThemeProvider} from "@/components/provider/ThemeProvider";
import React from "react"; import React from "react";
import {cookies} from "next/headers"; import {cookies} from "next/headers";
import {themeColors} from "@/components/Helper/ThemeColors";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Rhein Software", title: "Leistungen | Rhein Software",
description: "Rhein Software Development", description: "Rhein Software Development",
}; };
@@ -19,11 +20,12 @@ export default async function RootLayout({
}>) { }>) {
const cookieStore = await cookies(); const cookieStore = await cookies();
const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light"; const theme = cookieStore.get("theme")?.value === "dark" ? "dark" : "light";
const bgColor = themeColors[theme].primaryBg;
return ( return (
<html lang="de" data-theme={theme}> <html lang="de" data-theme={theme}>
<head/> <head/>
<body className="antialiased" style={{backgroundColor: "var(--primary-bg)"}}> <body className="antialiased" style={{backgroundColor: bgColor}}>
<ThemeProvider> <ThemeProvider>
<Nav/> <Nav/>
{children} {children}

View File

@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import Contact from "@/components/Contact/Contact"; import Services from "@/components/Services/Services";
const ContactPage = () => { const ContactPage = () => {
return ( return (
<div> <div>
<Contact/> <Services/>
</div> </div>
); );
}; };

View File

@@ -1,4 +1,3 @@
export const SectionDivider1 = () => { export const SectionDivider1 = () => {
return ( return (
@@ -22,3 +21,14 @@ export const SectionDivider2 = () => {
/> />
); );
}; };
export const SectionDivider3 = () => {
return (
<div
className="w-full h-20 transition-all duration-500 ease-in-out"
style={{
background: `linear-gradient(to bottom, var(--primary-bg), var(--footer-bg))`
}}
/>
);
};

View File

@@ -1,18 +0,0 @@
"use server";
import {cookies} from "next/headers";
// ✅ Get theme from cookies OR detect system preference
export async function getInitialTheme(): Promise<"dark" | "light"> {
const themeCookie = (await cookies()).get("theme")?.value;
if (themeCookie === "dark" || themeCookie === "light") {
return themeCookie;
}
// Detect system preference
const prefersDarkMode =
typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches;
return prefersDarkMode ? "dark" : "light";
}

View File

@@ -3,26 +3,32 @@ export const themeColors: Record<
{ {
primaryBg: string; primaryBg: string;
secondaryBg: string; secondaryBg: string;
navBg: string;
footerBg: string;
primaryText: string; primaryText: string;
secondaryText: string; secondaryText: string;
inputFieldBg: string; inputFieldBg: string;
inputBorder: string; inputBorder: string;
} }
> = { > = {
dark: {
primaryBg: "#121212", // Dark gray/black background (closer to true dark mode)
secondaryBg: "#1e1e1e", // Slightly lighter gray for contrast
primaryText: "#e0e0e0", // Light gray for good readability
secondaryText: "#b0b0b0", // Muted gray for subtle contrast
inputFieldBg: "#252525", // Dark but slightly distinguishable from primaryBg
inputBorder: "#3a3a3a", // Slightly lighter gray for a soft contrast
},
light: { light: {
primaryBg: "#f7f6fb", primaryBg: "#F3F4F6",
secondaryBg: "#ffffff", secondaryBg: "#eff1f3",
primaryText: "#1a1a2e", navBg: "#F9FAFB",
secondaryText: "#4a4a4a", // Muted text color footerBg: "#E5E7EB",
inputFieldBg: "#ffffff", // White input field (same as secondaryBg) primaryText: "#1E293B",
inputBorder: "#dcdcdc", // Light gray border for subtle visibility secondaryText: "#475569",
inputFieldBg: "#ffffff",
inputBorder: "#cbd5e1",
},
dark: {
primaryBg: "#1A1A23",
secondaryBg: "#22222C",
navBg: "#2A2A35",
footerBg: "#1F1F29",
primaryText: "#F0F0F3",
secondaryText: "#C0C2CC",
inputFieldBg: "#2D2D38",
inputBorder: "#4B4B5A",
}, },
}; };

View File

@@ -1,57 +0,0 @@
'use client';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { FiArrowRight } from 'react-icons/fi';
const ContactCTA = () => {
return (
<section
className="relative w-full py-24 transition-theme overflow-hidden"
style={{
background: 'linear-gradient(135deg, var(--secondary-bg), var(--primary-bg))',
}}
>
<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 }}
>
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>
);
};
export default ContactCTA;

View File

@@ -1,26 +1,45 @@
'use client'; 'use client';
import React from "react"; import React from "react";
import Hero from "./Hero/Hero"; import About from "@/components/Home/Sections/About";
import About from "@/components/Home/About/About"; import ContactCTA from "@/components/Home/Sections/ContactCTA";
import ContactCTA from "@/components/Home/Contact/ContactCTA"; import HomeServices from "@/components/Home/Sections/HomeServices";
import {SectionDivider1, SectionDivider2} from "@/components/Helper/SectionDivider"; import TechStack from "@/components/Home/Sections/TechStack";
import HomeServices from "@/components/Home/HomeServices"; import Section from "@/components/Section";
import TechStack from "@/components/Home/TechStack"; import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import Hero from "@/components/Home/Sections/Hero";
const Home = () => { const Home = () => {
const colors = useThemeColors();
return ( return (
<div className="overflow-hidden"> <motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}}>
<Hero/> <Hero/>
<SectionDivider1/> </Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<About/> <About/>
<SectionDivider2/> </Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<HomeServices/> <HomeServices/>
<SectionDivider1/> </Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<TechStack/> <TechStack/>
<SectionDivider2/> </Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ContactCTA/> <ContactCTA/>
</div> </Section>
</motion.div>
); );
}; };

View File

@@ -1,75 +0,0 @@
import React from "react";
import Image from "next/image";
const FullStack = () => {
return (
<div
className="w-[95%] sm:w-[80%] mx-auto items-center grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 transition-theme">
<div className="p-0"
>
<h1 data-aos="fade-up"
className="mt-4 text-xl sm:text-2xl md:text-3xl font-bold"
style={{color: "var(--primary-text)"}} // Dynamic text color
>
💻 Full-Stack Entwicklung
</h1>
<div data-aos="fade-up"
data-aos-delay="200"
>
<p
className="mt-4 text-sm font-medium leading-[2rem]"
style={{color: "var(--secondary-text)"}} // Secondary text color
>
Wir entwickeln skalierbare Backends, performante Frontends und native Apps genau auf deine
Anforderungen zugeschnitten. Dabei setzen wir auf neueste und modernste Technologien, um
flexible
und zukunftssichere Lösungen zu schaffen.
</p>
{/* Technology Logos Section */}
<h2
className="mt-8 text-lg font-semibold"
style={{color: "var(--primary-text)"}}
>
🔧 Tech Stack
</h2>
<span
className="mt-4 text-sm font-medium leading-[2rem]"
style={{color: "var(--secondary-text)"}}
>
Programmiersprachen und Frameworks, die wir für unsere Lösungen einsetzen.
</span>
<div className="mt-6 flex flex-wrap items-center gap-8">
{/* Tech Stack Logos */}
{[
{src: "/images/flutter_logo.png", label: "Flutter", width: 30, height: 30},
{src: "/images/dart_logo.png", label: "Dart", width: 40, height: 40},
{src: "/images/java_logo.png", label: "Java", width: 40, height: 40},
{src: "/images/nextjs_logo.png", label: "NextJS", width: 40, height: 40},
].map(({src, label, width, height}) => (
<div key={label} className="flex flex-col items-center">
<Image src={src} alt={label} width={width} height={height} className="object-contain"/>
<span
className="text-xs mt-2"
style={{color: "var(--secondary-text)"}} // Match theme
>
{label}
</span>
</div>
))}
</div>
</div>
</div>
<div data-aos="fade-up" data-aos-anchor-placement="top-center" className="justify-self-center">
<Image
src="/images/software_dev.jpg"
alt="image"
width={200}
height={200}
className="object-contain"
/>
</div>
</div>
);
};
export default FullStack;

View File

@@ -1,57 +0,0 @@
import React from "react";
import Image from "next/image";
import {TiChevronRightOutline} from "react-icons/ti";
const ManagedServices = () => {
return (
<div
className="w-[95%] sm:w-[80%] mx-auto items-center grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16"
data-aos="fade-up"
data-aos-delay="200"
>
{/* Image Section */}
<div
data-aos="fade-up"
data-aos-anchor-placement="top-center"
className="justify-self-center"
>
<Image
src="/images/software_dev.jpg"
alt="image"
width={200}
height={200}
className="object-contain"
/>
</div>
{/* Text Content */}
<div className="p-0">
<h1
className="mt-4 text-xl sm:text-2xl md:text-3xl font-bold transition-theme"
style={{color: "var(--primary-text)"}}>
Managed Services
</h1>
<p
className="mt-4 text-sm font-medium leading-[2rem] transition-theme"
style={{color: "var(--secondary-text)"}}>
Wir übernehmen das Hosting und Management deiner Server, damit du dich auf dein
Business konzentrieren kannst. Egal ob Cloud-Hosting, dedizierte Server oder hybride Lösungen wir
sorgen für eine sichere, performante und skalierbare Infrastruktur.
</p>
<ul className="mt-7 space-y-2">
{[
"Web- & Cloud-Hosting",
"Mailserver-Management",
"Webserver-Administration",
].map((item, index) => (
<li key={index} className="flex items-center font-semibold transition-theme">
<TiChevronRightOutline className="text-blue-500 mr-2"/>
<span style={{color: "var(--primary-text)"}}>{item}</span>
</li>
))}
</ul>
</div>
</div>
);
};
export default ManagedServices;

View File

@@ -1,28 +0,0 @@
import React from "react";
import FullStack from "@/components/Home/Offer/FullStack";
import ManagedServices from "@/components/Home/Offer/ManagedServices";
const Offer = () => {
return (
<div
className="transition-theme"
style={{
backgroundColor: "var(--primary-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
}}>
<div className="pt-24 pb-16">
<h1 className="mt-6 text-2xl md:text-3xl capitalize font-bold text-center"
data-aos="fade-up"
style={{color: "var(--primary-text)"}}
>
Was bieten wir?
</h1>
<FullStack/>
<ManagedServices/>
</div>
</div>
);
};
export default Offer;

View File

@@ -1,22 +1,23 @@
'use client'; 'use client';
import Link from 'next/link'; // import Link from 'next/link';
import {FiArrowRight} from 'react-icons/fi'; // import {FiArrowRight} from 'react-icons/fi';
import {motion} from 'framer-motion'; import {motion} from 'framer-motion';
import {useThemeColors} from '@/utils/useThemeColors';
const About = () => { const About = () => {
const colors = useThemeColors();
return ( return (
<section className="relative w-full py-24 bg-[var(--secondary-bg)] transition-theme"> <section
className="relative w-full py-24 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.secondaryBg, color: colors.primaryText}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto"> <div className="w-full max-w-6xl px-6 md:px-10 mx-auto">
<div className="flex flex-col"> <div className="flex flex-col">
{/* Title */} {/* Title */}
<motion.h2 <motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left" className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
style={{color: 'var(--primary-text)'}}
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
> >
Über uns Über uns
</motion.h2> </motion.h2>
@@ -30,9 +31,10 @@ const About = () => {
/> />
{/* Text */} {/* Text */}
<div className="p-0"> <div className="p-0 max-w-4xl">
<motion.p <motion.p
className="text-base md:text-lg leading-relaxed text-[var(--secondary-text)] max-w-4xl" className="text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}} initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}} whileInView={{opacity: 1, y: 0}}
viewport={{once: true}} viewport={{once: true}}
@@ -40,21 +42,20 @@ const About = () => {
> >
Wir sind Rhein-Software ein Team, das sich auf individuelle Softwarelösungen und digitale Wir sind Rhein-Software ein Team, das sich auf individuelle Softwarelösungen und digitale
Services spezialisiert hat. Unsere Anwendungen sind technisch solide, skalierbar und Services spezialisiert hat. Unsere Anwendungen sind technisch solide, skalierbar und
durchdacht gebaut für durchdacht gebaut für langfristigen Erfolg.
langfristigen Erfolg.
</motion.p> </motion.p>
<motion.p <motion.p
className="mt-6 text-base md:text-lg leading-relaxed text-[var(--secondary-text)] max-w-4xl" className="mt-6 text-base md:text-lg leading-relaxed transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 20}} initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}} whileInView={{opacity: 1, y: 0}}
viewport={{once: true}} viewport={{once: true}}
transition={{duration: 0.5, delay: 0.3}} transition={{duration: 0.5, delay: 0.3}}
> >
Von der ersten Idee bis zum Go-Live begleiten wir Unternehmen und Startups mit einem 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 flexiblen Netzwerk, klarer Kommunikation und einem hohen Anspruch an Qualität.
Lösungen sind Unsere Lösungen sind intuitiv, effizient und genau auf deine Anforderungen zugeschnitten.
intuitiv, effizient und genau auf deine Anforderungen zugeschnitten.
</motion.p> </motion.p>
</div> </div>
@@ -66,12 +67,13 @@ const About = () => {
viewport={{once: true}} viewport={{once: true}}
transition={{duration: 0.5, delay: 0.5}} transition={{duration: 0.5, delay: 0.5}}
> >
<Link href="/about"> {/*<Link href="/about">*/}
<button {/* <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"> {/* 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> {/* Mehr über uns <FiArrowRight size={18}/>*/}
</Link> {/* </button>*/}
{/*</Link>*/}
</motion.div> </motion.div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,57 @@
'use client';
import Link from 'next/link';
import {motion} from 'framer-motion';
import {FiArrowRight} from 'react-icons/fi';
import {useThemeColors} from '@/utils/useThemeColors';
const ContactCTA = () => {
const colors = useThemeColors();
return (
<section
className="relative w-full py-24 overflow-hidden transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.primaryBg, color: colors.primaryText}}
>
<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 transition-colors duration-700 ease-in-out"
>
Interesse geweckt?
</motion.h2>
{/* Description */}
<motion.p
className="mt-4 text-sm md:text-base max-w-xl mx-auto transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
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>
);
};
export default ContactCTA;

View File

@@ -1,13 +1,8 @@
'use client'; 'use client';
import { import {FiServer, FiTool, FiMonitor, FiZap, FiArrowRight} from 'react-icons/fi';
FiServer,
FiTool,
FiMonitor,
FiZap,
FiArrowRight,
} from 'react-icons/fi';
import {motion} from 'framer-motion'; import {motion} from 'framer-motion';
import {useThemeColors} from '@/utils/useThemeColors';
const services = [ const services = [
{ {
@@ -37,19 +32,16 @@ const services = [
]; ];
const HomeServices = () => { const HomeServices = () => {
const colors = useThemeColors();
return ( return (
<section <section
className="w-full py-24 bg-[var(--primary-bg)] transition-theme" className="w-full py-24 transition-colors duration-700 ease-in-out"
id="leistungen" style={{backgroundColor: colors.primaryBg}}
> >
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto"> <div className="w-full max-w-6xl px-6 md:px-10 mx-auto" style={{color: colors.primaryText}}>
{/* Heading */}
<motion.h2 <motion.h2
className="text-3xl md:text-4xl font-bold mb-1 text-left text-[var(--primary-text)]" className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4}}
> >
Leistungen Leistungen
</motion.h2> </motion.h2>
@@ -62,12 +54,16 @@ const HomeServices = () => {
transition={{duration: 0.4, delay: 0.1}} transition={{duration: 0.4, delay: 0.1}}
/> />
{/* Service Cards */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{services.map((service, index) => ( {services.map((service, index) => (
<motion.div <motion.div
key={service.title} key={service.title}
className="p-6 rounded-xl border border-[var(--secondary-bg)] bg-[var(--secondary-bg)] shadow-md" className="p-6 rounded-xl border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.secondaryBg,
borderColor: colors.secondaryBg,
color: colors.primaryText,
}}
whileHover={{ whileHover={{
scale: 1.03, scale: 1.03,
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)', boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
@@ -75,24 +71,18 @@ const HomeServices = () => {
initial={{opacity: 0, y: 30}} initial={{opacity: 0, y: 30}}
whileInView={{opacity: 1, y: 0}} whileInView={{opacity: 1, y: 0}}
viewport={{once: true}} viewport={{once: true}}
transition={{ transition={{duration: 0.4, delay: index * 0.1}}
duration: 0.4,
delay: index * 0.1,
ease: 'easeOut',
}}
> >
<div className="mb-3 text-blue-600">{service.icon}</div> <div className="mb-3 text-blue-600">{service.icon}</div>
<h3 className="text-xl font-semibold mb-2 text-[var(--primary-text)]"> <h3 className="text-xl font-semibold mb-2">{service.title}</h3>
{service.title} <p className="text-sm leading-relaxed transition-colors duration-700 ease-in-out"
</h3> style={{color: colors.secondaryText}}>
<p className="text-sm text-[var(--secondary-text)] leading-relaxed">
{service.description} {service.description}
</p> </p>
</motion.div> </motion.div>
))} ))}
</div> </div>
{/* Weitere Leistungen */}
<motion.div <motion.div
className="mt-10 flex justify-end" className="mt-10 flex justify-end"
initial={{opacity: 0}} initial={{opacity: 0}}

View File

@@ -2,6 +2,7 @@
import Image from 'next/image'; import Image from 'next/image';
import {motion} from 'framer-motion'; import {motion} from 'framer-motion';
import {useThemeColors} from "@/utils/useThemeColors";
const techStack = { const techStack = {
row1: [ row1: [
@@ -54,15 +55,16 @@ const techStack = {
}; };
const TechStack = () => { const TechStack = () => {
const colors = useThemeColors();
return ( return (
<section className="w-full py-20 bg-[var(--secondary-bg)] transition-theme"> <section
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto"> className="w-full py-20 transition-colors duration-700 ease-in-out"
style={{backgroundColor: colors.secondaryBg}}
>
<div className="w-full max-w-6xl px-6 md:px-10 mx-auto" style={{color: colors.primaryText}}>
<motion.h2 <motion.h2
className="text-2xl md:text-3xl font-bold mb-1 text-left text-[var(--primary-text)]" className="text-3xl md:text-4xl font-bold mb-1 text-left transition-colors duration-700 ease-in-out"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.5}}
> >
Technologien Technologien
</motion.h2> </motion.h2>
@@ -76,30 +78,20 @@ const TechStack = () => {
/> />
<motion.p <motion.p
className="text-sm md:text-base mb-10 text-[var(--secondary-text)]" className="text-sm md:text-base mb-10 transition-colors duration-700 ease-in-out"
initial={{opacity: 0, y: 10}} style={{color: colors.secondaryText}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.2}}
> >
Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen. Mit diesen Technologien realisieren wir moderne, leistungsstarke Softwarelösungen.
</motion.p> </motion.p>
{/* Row 1 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group, index) => ( {techStack.row1.map((group, index) => (
<TechCard key={group.category} group={group} delay={index * 0.2}/> <TechCard key={group.category} group={group} delay={index * 0.2} colors={colors}/>
))} ))}
</div> </div>
{/* Row 2 */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group, index) => ( {techStack.row2.map((group, index) => (
<TechCard <TechCard key={group.category} group={group} delay={index * 0.2 + 0.4} colors={colors}/>
key={group.category}
group={group}
delay={index * 0.2 + 0.4}
/>
))} ))}
</div> </div>
</div> </div>
@@ -110,35 +102,32 @@ const TechStack = () => {
const TechCard = ({ const TechCard = ({
group, group,
delay, delay,
colors,
}: { }: {
group: { category: string; items: { id: string; label: string }[] }; group: { category: string; items: { id: string; label: string }[] };
delay: number; delay: number;
colors: ReturnType<typeof useThemeColors>;
}) => ( }) => (
<motion.div <motion.div
className="p-4 rounded-lg border border-[var(--primary-bg)] bg-[var(--primary-bg)] shadow-md transition-theme" className="p-4 rounded-lg border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.primaryBg,
borderColor: colors.primaryBg,
color: colors.primaryText,
}}
initial={{opacity: 0, y: 20}} initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}} whileInView={{opacity: 1, y: 0}}
whileHover={{ whileHover={{scale: 1.03, boxShadow: '0 10px 20px rgba(0,0,0,0.1)'}}
scale: 1.03,
boxShadow: '0px 10px 20px rgba(0,0,0,0.1)',
}}
viewport={{once: true}} viewport={{once: true}}
transition={{duration: 0.4, delay, ease: 'easeOut'}} transition={{duration: 0.4, delay}}
> >
<h3 className="text-base font-semibold mb-4 text-[var(--primary-text)]"> <h3 className="text-base font-semibold mb-4">{group.category}</h3>
{group.category}
</h3>
<div className="grid grid-cols-3 gap-3"> <div className="grid grid-cols-3 gap-3">
{group.items.map(({id, label}) => ( {group.items.map(({id, label}) => (
<div key={id} className="flex flex-col items-center text-center"> <div key={id} className="flex flex-col items-center text-center">
<Image <Image src={`/images/svg/${id}.svg`} alt={label} width={32} height={32} className="object-contain"/>
src={`/images/svg/${id}.svg`} <span className="text-[10px] mt-1 transition-colors duration-700 ease-in-out"
alt={label} style={{color: colors.secondaryText}}>
width={32}
height={32}
className="object-contain"
/>
<span className="text-[10px] mt-1 text-[var(--secondary-text)]">
{label} {label}
</span> </span>
</div> </div>

View File

@@ -1,12 +1,12 @@
"use client"; "use client";
import {usePathname} from "next/navigation"; import {usePathname} from "next/navigation";
import {navLinks} from "@/constant/Constant";
import Link from "next/link"; import Link from "next/link";
import React, {useContext, useEffect, useState} from "react"; import React, {useContext, useEffect, useState} from "react";
import {HiBars3BottomRight} from "react-icons/hi2"; import {HiBars3BottomRight} from "react-icons/hi2";
import {ThemeContext} from "@/components/provider/ThemeProvider"; import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors"; import {useThemeColors} from "@/utils/useThemeColors";
import {navLinks} from "@/constant/Constant";
type Props = { type Props = {
openNav: () => void; openNav: () => void;
@@ -17,8 +17,9 @@ const Nav = ({openNav}: Props) => {
const [navHeight, setNavHeight] = useState("h-[10vh]"); const [navHeight, setNavHeight] = useState("h-[10vh]");
const [contentSize, setContentSize] = useState("text-base md:text-lg"); const [contentSize, setContentSize] = useState("text-base md:text-lg");
const [buttonSize, setButtonSize] = useState("md:px-6 md:py-2 px-4 py-1 text-sm"); const [buttonSize, setButtonSize] = useState("md:px-6 md:py-2 px-4 py-1 text-sm");
const {theme, toggleTheme} = useContext(ThemeContext); const {theme, toggleTheme} = useContext(ThemeContext);
const colors = themeColors[theme]; const colors = useThemeColors();
const pathname = usePathname(); const pathname = usePathname();
const navColorClass = theme === "dark" || !navBg ? "text-white" : "text-black"; const navColorClass = theme === "dark" || !navBg ? "text-white" : "text-black";
@@ -47,63 +48,46 @@ const Nav = ({openNav}: Props) => {
className={`fixed w-full transition-all duration-300 ease-in-out ${navHeight} z-[1000] ${ className={`fixed w-full transition-all duration-300 ease-in-out ${navHeight} z-[1000] ${
navBg ? "shadow-md" : "" navBg ? "shadow-md" : ""
}`} }`}
style={{ style={{backgroundColor: navBg ? colors.navBg : "transparent"}}
backgroundColor: navBg ? "var(--nav-bg)" : "transparent",
}}
> >
<div <div className="flex items-center h-full justify-between w-[90%] xl:w-[80%] mx-auto">
className="flex items-center h-full justify-between w-[90%] xl:w-[80%] mx-auto transition-all duration-300 ease-in-out"> <h1 className={`${contentSize} font-bold ${navColorClass}`}>
{/* LOGO */}
<h1 className={`${contentSize} font-bold transition-all duration-300 ease-in-out ${navColorClass}`}>
<span className="text-lg md:text-xl text-pink-700">R</span>hein Software <span className="text-lg md:text-xl text-pink-700">R</span>hein Software
</h1> </h1>
{/* Desktop Nav Links */} <div className="hidden lg:flex items-center space-x-6">
<div className="hidden lg:flex items-center space-x-6 transition-all duration-300 ease-in-out">
{navLinks.map((link) => ( {navLinks.map((link) => (
<Link href={link.url} key={link.id}> <Link href={link.url} key={link.id}>
<p <p className={`relative group ${contentSize} uppercase ${getNavLinkClasses(pathname === link.url, navBg, theme, navColorClass)}`}>
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} {link.label}
{pathname !== link.url && ( {pathname !== link.url && (
<span <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" 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> </p>
</Link> </Link>
))} ))}
</div> </div>
{/* Right Side Buttons */} <div className="flex items-center space-x-3">
<div className="flex items-center space-x-3 transition-all duration-300 ease-in-out"> {pathname !== "/contact" && (
{/* Contact Button */}
<Link href="/contact"> <Link href="/contact">
<button <button
className={`${buttonSize} text-white font-semibold bg-blue-700 hover:bg-blue-900 transition-all duration-300 ease-in-out rounded-full`} className={`${buttonSize} text-white font-semibold bg-blue-700 hover:bg-blue-900 rounded-full`}>
>
Kontakt Kontakt
</button> </button>
</Link> </Link>
)}
{/* Theme Toggle Button */}
<button <button
onClick={toggleTheme} onClick={toggleTheme}
className={`w-7 h-7 flex items-center justify-center rounded-full transition-all duration-300 ease-in-out ${navColorClass}`} className={`w-7 h-7 flex items-center justify-center rounded-full ${navColorClass}`}
style={{backgroundColor: colors.secondaryBg}} style={{backgroundColor: colors.secondaryBg}}
> >
{theme === "dark" ? "🌙" : "☀️"} {theme === "dark" ? "🌙" : "☀️"}
</button> </button>
{/* Burger Menu (for mobile) */}
<HiBars3BottomRight <HiBars3BottomRight
onClick={openNav} onClick={openNav}
className={`w-6 h-6 cursor-pointer lg:hidden transition-all duration-300 ease-in-out ${navColorClass}`} className={`w-6 h-6 cursor-pointer lg:hidden ${navColorClass}`}
/> />
</div> </div>
</div> </div>
@@ -111,4 +95,15 @@ const Nav = ({openNav}: Props) => {
); );
}; };
const getNavLinkClasses = (
isActive: boolean,
navBg: boolean,
theme: string,
navColorClass: string
): string => {
if (isActive) return !navBg ? "text-white font-bold" : `${navColorClass} font-bold`;
if (!navBg) return "text-white font-medium";
return theme === "dark" ? "text-gray-300 font-medium" : "text-gray-700 font-medium";
};
export default Nav; export default Nav;

View File

@@ -1,9 +1,11 @@
'use client';
import {navLinks} from "@/constant/Constant"; import {navLinks} from "@/constant/Constant";
import Link from "next/link"; import Link from "next/link";
import React, {useContext} from "react"; import React, {useContext} from "react";
import {CgClose} from "react-icons/cg"; import {CgClose} from "react-icons/cg";
import {ThemeContext} from "@/components/provider/ThemeProvider"; import {ThemeContext} from "@/components/provider/ThemeProvider";
import { themeColors } from "@/components/Helper/ThemeColors"; import {useThemeColors} from "@/utils/useThemeColors";
type Props = { type Props = {
showNav: boolean; showNav: boolean;
@@ -13,10 +15,12 @@ type Props = {
const MobileNav = ({closeNav, showNav}: Props) => { const MobileNav = ({closeNav, showNav}: Props) => {
const navOpen = showNav ? "translate-y-0 opacity-100" : "-translate-y-20 opacity-0 pointer-events-none"; const navOpen = showNav ? "translate-y-0 opacity-100" : "-translate-y-20 opacity-0 pointer-events-none";
const {theme, toggleTheme} = useContext(ThemeContext); const {theme, toggleTheme} = useContext(ThemeContext);
const colors = themeColors[theme]; const colors = useThemeColors();
const textClass = theme === "dark" ? "text-white" : "text-black";
return ( return (
<div className="lg:hidden"> <div className="lg:hidden">
{/* overlay background */}
<div <div
className={`fixed inset-0 z-[10000] transition-opacity duration-500 ${ className={`fixed inset-0 z-[10000] transition-opacity duration-500 ${
showNav ? "opacity-60 bg-black" : "opacity-0 pointer-events-none" showNav ? "opacity-60 bg-black" : "opacity-0 pointer-events-none"
@@ -24,17 +28,14 @@ const MobileNav = ({ closeNav, showNav }: Props) => {
onClick={closeNav} onClick={closeNav}
/> />
{/* nav menu */}
<div <div
className={`fixed top-0 left-0 w-full z-[10006] transform ${navOpen} transition-all duration-500 ease-in-out text-[var(--primary-text)] shadow-md rounded-b-2xl`} className={`fixed top-0 left-0 w-full z-[10006] transform ${navOpen} transition-all duration-500 ease-in-out shadow-md rounded-b-2xl`}
style={{ backgroundColor: theme === "dark" ? "#2A2A2A" : "#ffffff", color: theme === "dark" ? "#f5f5f5" : "#1a1a1a" }} style={{backgroundColor: colors.navBg}}
> >
<div className="flex flex-col items-center justify-center py-8 space-y-4 px-4 relative"> <div className={`flex flex-col items-center justify-center py-8 space-y-4 px-4 relative ${textClass}`}>
{/* Close icon */}
<CgClose <CgClose
onClick={closeNav} onClick={closeNav}
className="absolute top-4 right-6 sm:right-8 sm:w-7 sm:h-7 w-6 h-6 cursor-pointer p-1" className={`absolute top-4 right-6 sm:right-8 sm:w-7 sm:h-7 w-6 h-6 cursor-pointer p-1 ${textClass}`}
style={{ color: colors.primaryText }}
/> />
{navLinks.map((link) => ( {navLinks.map((link) => (
@@ -45,11 +46,10 @@ const MobileNav = ({ closeNav, showNav }: Props) => {
</Link> </Link>
))} ))}
{/* Theme toggle button */}
<button <button
onClick={toggleTheme} onClick={toggleTheme}
className="mt-4 w-8 h-8 flex items-center justify-center rounded-full border border-gray-400 transition-all duration-300" className={`mt-4 w-8 h-8 flex items-center justify-center rounded-full border border-gray-400 transition-all duration-300 ${textClass}`}
style={{ backgroundColor: colors.secondaryBg, color: colors.primaryText }} style={{backgroundColor: colors.secondaryBg}}
> >
{theme === "dark" ? "🌙" : "☀️"} {theme === "dark" ? "🌙" : "☀️"}
</button> </button>

34
components/Section.tsx Normal file
View File

@@ -0,0 +1,34 @@
'use client';
import React, {CSSProperties} from "react";
import clsx from "clsx";
interface SectionProps {
children: React.ReactNode;
className?: string;
style?: CSSProperties;
shadow?: boolean;
}
const Section: React.FC<SectionProps> = ({
children,
className = "",
style,
shadow = false,
}) => {
return (
<section
className={clsx("relative transition-colors duration-500", className)}
style={style}
>
<div className="relative z-10">{children}</div>
{shadow && (
<div
className="absolute bottom-0 left-0 w-full h-16 pointer-events-none"
/>
)}
</section>
);
};
export default Section;

View File

@@ -0,0 +1,88 @@
// components/Leistungen/Section/OverviewTabs.tsx
'use client';
import React, {useState} from 'react';
import {motion, AnimatePresence} from 'framer-motion';
import {FiMonitor, FiUsers, FiSettings, FiTool} from 'react-icons/fi';
import Development from "@/components/Services/Section/overview/Development";
import Consulting from "@/components/Services/Section/overview/Consulting";
import ManagedServices from "@/components/Services/Section/overview/ManagedServices";
import BugFixing from "@/components/Services/Section/overview/BugFixing";
import {useThemeColors} from "@/utils/useThemeColors";
const tabs = [
{key: 'entwicklung', label: 'Entwicklung', icon: <FiMonitor size={20}/>},
{key: 'beratung', label: 'Beratung', icon: <FiUsers size={20}/>},
{key: 'services', label: 'Managed Services', icon: <FiSettings size={20}/>},
{key: 'support', label: 'Fehlerbehebung', icon: <FiTool size={20}/>},
];
const tabContent: Record<string, React.ReactNode> = {
entwicklung: (
<Development/>
),
beratung: (
<Consulting/>
),
services: (
<ManagedServices/>
),
support: (
<BugFixing/>
),
};
const OverviewTabs = () => {
const [activeTab, setActiveTab] = useState("entwicklung");
const colors = useThemeColors();
return (
<div className="max-w-4xl mx-auto px-6 py-16 text-center transition-theme">
<h2
className="text-3xl font-bold mb-4"
style={{color: colors.primaryText}}
>
Was wir tun
</h2>
<motion.div
className="w-12 h-[2px] mb-8 bg-amber-500 mx-auto"
initial={{opacity: 0, x: -20}}
whileInView={{opacity: 1, x: 0}}
viewport={{once: true}}
transition={{duration: 0.4, delay: 0.1}}
/>
<div className="flex justify-center gap-4 mb-8 flex-wrap">
{tabs.map((tab) => (
<button
key={tab.key}
onClick={() => setActiveTab(tab.key)}
className="flex items-center gap-2 px-4 py-2 rounded-full border text-sm font-medium transition-all"
style={{
color: activeTab === tab.key ? '#ffffff' : colors.primaryText,
backgroundColor: activeTab === tab.key ? '#1D4ED8' : 'transparent',
borderColor: activeTab === tab.key ? '#1D4ED8' : colors.inputBorder,
}}
>
{tab.icon}
{tab.label}
</button>
))}
</div>
<AnimatePresence mode="wait">
<motion.div
key={activeTab}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
exit={{opacity: 0, y: -10}}
transition={{duration: 0.4}}
>
{tabContent[activeTab]}
</motion.div>
</AnimatePresence>
</div>
);
};
export default OverviewTabs;

View File

@@ -0,0 +1,30 @@
// components/Leistungen/Section/Hero.tsx
'use client';
import React from "react";
import {motion} from "framer-motion";
const ServiceHero = () => {
return (
<section className="py-24 text-center max-w-4xl mx-auto">
<motion.h1
className="text-4xl md:text-5xl font-bold mb-6"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6}}
>
Unsere Leistungen
</motion.h1>
<motion.p
className="text-lg text-gray-500"
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.6, delay: 0.2}}
>
Wir bieten maßgeschneiderte Lösungen von der Beratung bis zum Betrieb.
</motion.p>
</section>
);
};
export default ServiceHero;

View File

@@ -0,0 +1,70 @@
'use client';
import React from "react";
import Image from "next/image";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const BugFixing = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] sm:w-[80%] mx-auto grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🐞 Fehlerbehebung & Optimierung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir analysieren und beheben Fehler in bestehenden Systemen, optimieren die Performance und sorgen
dafür, dass deine Software stabil und zuverlässig läuft.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔎 Fokusbereiche
</motion.h3>
<ul className="list-disc list-inside text-sm mt-4 space-y-1" style={{color: colors.secondaryText}}>
<li>Debugging & Troubleshooting</li>
<li>Performance-Analyse</li>
<li>Refactoring von Legacy-Code</li>
<li>Stabilitäts- und Sicherheitsupdates</li>
</ul>
</div>
<div className="justify-self-center">
<motion.div
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
<Image
src="/images/bug_fixing.jpg"
alt="Bug fixing illustration"
width={300}
height={300}
className="object-contain"
/>
</motion.div>
</div>
</div>
);
};
export default BugFixing;

View File

@@ -0,0 +1,60 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const Consulting = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] sm:w-[80%] mx-auto grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🧠 Technische Beratung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
whileInView={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir unterstützen dich dabei, technische Entscheidungen fundiert zu treffen von der Auswahl
geeigneter Technologien bis hin zur Planung skalierbarer Architekturen. Gemeinsam finden wir den
effizientesten Weg von der Idee bis zur Umsetzung praxisnah, zielgerichtet und verständlich.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
whileInView={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔍 Themenbereiche
</motion.h3>
<ul
className="list-disc list-inside text-sm mt-4 space-y-1"
style={{color: colors.secondaryText}}
>
<li>Software-Architektur & Microservices</li>
<li>Technologie- und Framework-Auswahl</li>
<li>Prototyping & Machbarkeitsanalysen</li>
<li>Projektplanung und agile Methodik</li>
</ul>
</div>
</div>
);
};
export default Consulting;

View File

@@ -0,0 +1,210 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import Image from "next/image";
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 TechCard = ({group}: {
group: { category: string; items: { id: string; label: string }[] };
}) => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.4}}
className="p-4 rounded-lg border shadow-md transition-colors duration-700 ease-in-out"
style={{
backgroundColor: colors.primaryBg,
borderColor: colors.primaryBg,
color: colors.primaryText,
}}
>
<h3 className="text-base font-semibold mb-4">{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 transition-colors duration-700 ease-in-out"
style={{color: colors.secondaryText}}
>
{label}
</span>
</div>
))}
</div>
</motion.div>
);
};
const Development = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] sm:w-[80%] mx-auto grid grid-cols-1 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
💻 Full-Stack Entwicklung
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir entwickeln individuelle Softwarelösungen von der nativen Mobile-App über moderne Webseiten bis
hin zu internen Tools.
Unser Fokus liegt auf skalierbaren Architekturen, performanten Frontends und wartbaren Backends.
<br/><br/>
Egal ob API-Entwicklung, Admin-Dashboard oder komplexe Plattform wir setzen moderne Technologien
gezielt ein, um robuste, zukunftssichere Anwendungen zu realisieren.
</motion.p>
<motion.div
className="mt-6 text-sm font-medium space-y-3 pl-2"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
<div className="flex items-center gap-2">
<span className="text-xs">🚀</span>
<p>Native Mobile-Apps mit Flutter</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🌐</span>
<p>Webseiten & Web-Portale mit Next.js</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🧩</span>
<p>Skalierbare Backends mit Spring Boot</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">📊</span>
<p>Individuelle Dashboards & Admin-Panels</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">🔌</span>
<p>API-Entwicklung (REST & GraphQL)</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs"></span>
<p>Automatisierte interne Tools</p>
</div>
<div className="flex items-center gap-2">
<span className="text-xs">📦</span>
<p>CI/CD & Container mit GitLab CI & Docker</p>
</div>
</motion.div>
<motion.h3
className="mt-10 text-lg font-semibold"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🔧 Unser Tech Stack im Überblick
</motion.h3>
<motion.p
className="text-sm font-medium mb-4"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Mit diesem Stack entwickeln wir robuste, moderne Softwarelösungen abgestimmt auf deine
Anforderungen.
</motion.p>
<motion.div
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.5, delay: 0.1}}
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
{techStack.row1.map((group) => (
<TechCard key={group.category} group={group}/>
))}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{techStack.row2.map((group) => (
<TechCard key={group.category} group={group}/>
))}
</div>
</motion.div>
</div>
</div>
);
};
export default Development;

View File

@@ -0,0 +1,62 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
const ManagedServices = () => {
const colors = useThemeColors();
return (
<div
className="w-[95%] sm:w-[80%] mx-auto grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8 mb-16 text-left"
style={{color: colors.primaryText}}
>
<div>
<motion.h2
className="text-xl sm:text-2xl md:text-3xl font-bold mb-4"
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5}}
>
🛠 Managed Services
</motion.h2>
<motion.p
className="text-sm font-medium leading-7"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.2}}
>
Wir übernehmen den Betrieb und die Wartung deiner Anwendungen zuverlässig, sicher und skalierbar.
So kannst du dich voll auf dein Geschäft konzentrieren.
</motion.p>
<motion.h3
className="mt-8 text-lg font-semibold"
initial={{opacity: 0}}
animate={{opacity: 1}}
transition={{duration: 0.4, delay: 0.2}}
>
🧰 Leistungen
</motion.h3>
<motion.ul
className="list-disc list-inside text-sm mt-4 space-y-1"
style={{color: colors.secondaryText}}
initial={{opacity: 0, y: 10}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.5, delay: 0.3}}
>
<li>Monitoring & Logging</li>
<li>Security Updates & Wartung</li>
<li>Cloud Deployment & Hosting</li>
<li>24/7 Systemüberwachung</li>
</motion.ul>
</div>
</div>
);
};
export default ManagedServices;

View File

@@ -0,0 +1,34 @@
'use client';
import React from "react";
import {motion} from "framer-motion";
import {useThemeColors} from "@/utils/useThemeColors";
import ServiceHero from "@/components/Services/Section/ServiceHero";
import OverviewTabs from "@/components/Services/Section/OverviewTabs";
import ContactCTA from "@/components/Home/Sections/ContactCTA";
import Section from "@/components/Section";
const Home = () => {
const colors = useThemeColors();
return (
<motion.div
initial={{opacity: 0, y: 20}}
animate={{opacity: 1, y: 0}}
transition={{duration: 0.7, ease: "easeOut"}}
className="overflow-hidden"
>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ServiceHero/>
</Section>
<Section style={{backgroundColor: colors.secondaryBg}} shadow>
<OverviewTabs/>
</Section>
<Section style={{backgroundColor: colors.primaryBg}} shadow>
<ContactCTA/>
</Section>
</motion.div>
);
};
export default Home;

View File

@@ -1,9 +1,8 @@
"use client"; 'use client';
import {createContext, useEffect, useState} from "react"; import {createContext, useEffect, useState} from "react";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
// Define theme options
type ThemeType = "light" | "dark"; type ThemeType = "light" | "dark";
export const ThemeContext = createContext<{ export const ThemeContext = createContext<{
@@ -11,41 +10,38 @@ export const ThemeContext = createContext<{
toggleTheme: () => void; toggleTheme: () => void;
}>({ }>({
theme: "light", theme: "light",
toggleTheme: () => {}, toggleTheme: () => {
},
}); });
export const ThemeProvider = ({children}: { children: React.ReactNode }) => { export const ThemeProvider = ({children}: { children: React.ReactNode }) => {
const [theme, setTheme] = useState<ThemeType>("light"); const [theme, setTheme] = useState<ThemeType | null>(null);
useEffect(() => { useEffect(() => {
// Get theme from cookies or system preference const saved = Cookies.get("theme") as ThemeType | undefined;
const savedTheme = Cookies.get("theme") as ThemeType | undefined; if (saved === "dark" || saved === "light") {
if (savedTheme === "dark" || savedTheme === "light") { setTheme(saved);
setTheme(savedTheme);
} else { } else {
// Detect system preference
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
setTheme(prefersDark ? "dark" : "light"); const defaultTheme: ThemeType = prefersDark ? "dark" : "light";
Cookies.set("theme", prefersDark ? "dark" : "light", { expires: 365 }); setTheme(defaultTheme);
Cookies.set("theme", defaultTheme, {expires: 365});
} }
}, []); }, []);
// Apply the transition effect when theme changes
useEffect(() => { useEffect(() => {
document.documentElement.classList.add("transition-theme"); if (!theme) return;
document.documentElement.setAttribute("data-theme", theme); document.documentElement.setAttribute("data-theme", theme);
return () => {
document.documentElement.classList.remove("transition-theme");
};
}, [theme]); }, [theme]);
const toggleTheme = () => { const toggleTheme = () => {
const newTheme: ThemeType = theme === "dark" ? "light" : "dark"; const next = theme === "dark" ? "light" : "dark";
setTheme(newTheme); setTheme(next);
Cookies.set("theme", newTheme, { expires: 365 }); Cookies.set("theme", next, {expires: 365});
}; };
if (!theme) return null;
return ( return (
<ThemeContext.Provider value={{theme, toggleTheme}}> <ThemeContext.Provider value={{theme, toggleTheme}}>
{children} {children}

View File

@@ -2,15 +2,10 @@ export const navLinks = [
{ {
id: 1, id: 1,
url: '/', url: '/',
label: 'Home', label: 'Start',
}, },
{ {
id: 2, id: 2,
url: '#',
label: 'Über uns',
},
{
id: 3,
url: '/services', url: '/services',
label: 'Leistungen', label: 'Leistungen',
} }

10
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"aos": "^2.3.4", "aos": "^2.3.4",
"clsx": "^2.1.1",
"framer-motion": "^12.6.5", "framer-motion": "^12.6.5",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"next": "15.1.7", "next": "15.1.7",
@@ -2355,6 +2356,15 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color": { "node_modules/color": {
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",

View File

@@ -10,6 +10,7 @@
}, },
"dependencies": { "dependencies": {
"aos": "^2.3.4", "aos": "^2.3.4",
"clsx": "^2.1.1",
"framer-motion": "^12.6.5", "framer-motion": "^12.6.5",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"next": "15.1.7", "next": "15.1.7",

View File

@@ -4,6 +4,7 @@ export default {
content: [ content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}", "./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}", "./components/**/*.{js,ts,jsx,tsx,mdx}",
"./utils/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}", "./app/**/*.{js,ts,jsx,tsx,mdx}",
], ],
theme: {}, theme: {},

View File

@@ -21,6 +21,7 @@
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/components/*": ["./components/*"], "@/components/*": ["./components/*"],
"@/utils/*": ["./utils/*"],
"@/*": ["./*"] "@/*": ["./*"]
} }
}, },

10
utils/useThemeColors.ts Normal file
View File

@@ -0,0 +1,10 @@
'use client';
import {useContext} from "react";
import {ThemeContext} from "@/components/provider/ThemeProvider";
import {themeColors} from "@/components/Helper/ThemeColors";
export const useThemeColors = () => {
const {theme} = useContext(ThemeContext);
return themeColors[theme];
};