Take 2: background

This commit is contained in:
Mike Karl 2026-02-07 13:42:12 +01:00
parent 6ea3e13cfd
commit 7b115e8202
11 changed files with 35 additions and 634 deletions

View file

@ -1,32 +0,0 @@
import { ReactNode } from 'react';
interface BadgeProps {
children: ReactNode;
variant?: 'gray' | 'green' | 'yellow' | 'red' | 'blue' | 'glass';
border?: boolean;
}
export default function Badge({
children,
variant = 'gray',
border = false,
}: BadgeProps) {
const baseClasses = "rounded-full px-2 py-0.5 text-sm";
const variantClasses = {
gray: "bg-gray-700/50 text-gray-300 border-gray-600/50",
green: "bg-green-500/20 text-green-400 border-green-500/30",
yellow: "bg-yellow-500/20 text-yellow-400 border-yellow-500/30",
red: "bg-red-500/20 text-red-400 border-red-500/30",
blue: "bg-blue-500/20 text-blue-400 border-blue-500/30",
glass: "bg-white/10 text-white border-white/20 backdrop-blur-sm"
};
return (
<span
className={` ${border?"border":""} ${baseClasses} ${variantClasses[variant]}`}
>
{children}
</span>
);
}

View file

@ -1,37 +0,0 @@
import { ReactNode, ButtonHTMLAttributes } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'ghost' | 'outline' | 'glass';
size?: 'sm' | 'md' | 'lg';
}
export default function Button({
children,
variant = 'primary',
size = 'md',
}: ButtonProps) {
const baseClasses = "cursor-pointer font-medium rounded-lg transition-all duration-300 ease-out disabled:opacity-50 disabled:cursor-not-allowed";
const sizeClasses = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg"
};
const variantClasses = {
primary: "bg-blue-600 hover:bg-blue-700 text-white shadow-lg hover:shadow-xl hover:shadow-blue-500/25 hover:scale-105 focus:ring-blue-500",
secondary: "bg-gray-700 hover:bg-gray-600 text-white shadow-lg hover:shadow-xl hover:shadow-gray-500/25 hover:scale-105 focus:ring-gray-500",
ghost: "bg-transparent hover:bg-gray-800/50 text-gray-300 hover:text-white hover:scale-105 focus:ring-gray-500",
outline: "border-2 border-gray-600 hover:border-blue-500 bg-transparent text-gray-300 hover:text-white hover:bg-blue-500/10 hover:scale-105 focus:ring-blue-500",
glass: "bg-white/10 hover:bg-white/20 text-white border border-white/20 hover:border-white/30 backdrop-blur-sm hover:scale-105 focus:ring-white/50"
};
return (
<button
className={`${baseClasses} ${sizeClasses[size]} ${variantClasses[variant]}`}
>
{children}
</button>
);
}

View file

@ -1,64 +0,0 @@
import { ReactNode } from 'react';
interface CardProps {
children: ReactNode;
variant?: 'default' | 'glass' | 'outlined';
hoverable?: boolean;
}
export default function Card({
children,
variant = 'default',
}: CardProps) {
const baseClasses = "relative overflow-hidden rounded-xl transition-all duration-300 ease-out";
const variantClasses = {
default: "bg-gradient-to-br bg-gradient-to-br from-stone-800 via-stone-900 to-stone-800 border border-stone-700",
glass: "bg-gradient-to-br bg-gradient-to-br from-white/5 via-white/10 to-white/5 border border-white/10 backdrop-blur-sm",
outlined: "bg-transparent border-2 border-stone-700/50"
};
return (
<div
className={`${baseClasses} ${variantClasses[variant]}`}
>
{children}
</div>
);
}
interface CardHeaderProps {
children: ReactNode;
}
export function CardHeader({ children }: CardHeaderProps) {
return (
<div className={`p-6 pb-0`}>
{children}
</div>
);
}
interface CardContentProps {
children: ReactNode;
}
export function CardContent({ children }: CardContentProps) {
return (
<div className={`p-6 pb-4`}>
{children}
</div>
);
}
interface CardFooterProps {
children: ReactNode;
}
export function CardFooter({ children }: CardFooterProps) {
return (
<div className={`p-6 pt-8`}>
{children}
</div>
);
}

View file

@ -1,4 +0,0 @@
export { default as Card, CardHeader, CardContent, CardFooter } from './card';
export { default as Button } from './button';
export { default as Badge } from './badge';
export { default as Section } from './section';

View file

@ -1,51 +0,0 @@
import { ReactNode } from 'react';
interface SectionProps {
children?: ReactNode;
title?: string;
subtitle?: string;
className?: string;
variant?: 'default' | 'hero' | 'minimal';
}
export default function Section({
children,
title,
subtitle,
className = '',
variant = 'default'
}: SectionProps) {
const baseClasses = "relative py-16 lg:py-24";
const variantClasses = {
default: "bg-stone-900",
hero: "polka pt-32 lg:pt-40",
minimal: "bg-transparent"
};
return (
<section
className={`${baseClasses} ${variantClasses[variant]} ${className}`}
>
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
{(title || subtitle) && (
<div className="text-center mb-12">
{title && (
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold text-white mb-4 tracking-tight">
{title}
</h2>
)}
{subtitle && (
<p className="text-lg md:text-xl text-gray-400 max-w-3xl mx-auto leading-relaxed">
{subtitle}
</p>
)}
</div>
)}
<div className="relative">
{children}
</div>
</div>
</section>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,91 +0,0 @@
"use client";
import Link from "next/link";
export default function FootBar() {
const currentYear = new Date().getFullYear();
const socialLinks = [
{
name: "GitHub", href: "https://github.com/GoranSalzmann"
},
{
name: "Email", href: "mailto:karl.mike2002@gmail.com"
}
];
const quickLinks = [
{ name: "Lebenslauf", href: "/cv" },
{ name: "Portfolio", href: "/portfolio" },
{ name: "Kontakt", href: "/contact" }
];
return (
<footer className="bg-stone-900 p-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{/* Brand Section */}
<div className="lg:col-span-2">
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center text-white font-bold text-lg">
MK
</div>
<div>
<h3 className="text-xl font-bold text-white">Mike Karl</h3>
<p className="text-gray-400 text-sm">Web und Systementwickler</p>
</div>
</div>
<p className="text-gray-300 mb-6 max-w-md leading-relaxed">
Leidenschaftlicher Entwickler, stets auf der Suche nach neuen Möglichkeiten.
</p>
<p className="mt-4 bg-gradient-to-r from-yellow-400 to-indigo-600 bg-clip-text text-transparent">
<i> Always learning, always building.</i>
</p>
</div>
{/* Quick Links */}
<div>
<h4 className="text-lg font-semibold text-white mb-4">Quick Links</h4>
<ul className="space-y-3">
{quickLinks.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-gray-300 hover:text-white transition-colors duration-300 hover:translate-x-1 inline-block"
>
{link.name}
</Link>
</li>
))}
</ul>
</div>
{/* Social Links */}
<div>
<h4 className="text-lg font-semibold text-white mb-4">Kontakt</h4>
<div className="space-y-3">
{socialLinks.map((social) => (
<a
key={social.name}
href={social.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 text-gray-300 hover:text-white transition-all duration-300 hover:translate-x-1 group"
>
<div className="w-8 h-8 bg-gray-800/50 rounded-lg flex items-center justify-center group-hover:bg-blue-500/20 transition-colors duration-300">
{ /* Icon */ }
</div>
<span>{social.name}</span>
</a>
))}
</div>
</div>
</div>
{/* Bottom Section */}
<div className="mt-6 pt-4 border-t text-sm text-gray-500 flex justify-between">
<p>&copy; {currentYear} Mike Karl. All rights reserved.</p>
<p>Built with Next.js and Tailwind CSS</p>
</div>
</footer>
);
}

View file

@ -5,6 +5,13 @@
box-sizing: border-box; box-sizing: border-box;
} }
:root{
--primary-col: #fff;
--secondary-col: #000;
--tertiary-col-1: #c50022;
--tertiary-col-2: #6d001a;
}
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
} }
@ -12,37 +19,32 @@ html {
body { body {
min-height: 100vh; min-height: 100vh;
font-family: "Space Grotesk", sans-serif; font-family: "Space Grotesk", sans-serif;
background-color: #292524;
} }
.polka{ .bg{
overflow: hidden; background: var(--tertiary-col-2);
will-change: transform;
} }
.polka::before{ .bg1{
content: ""; background: #000;
position: absolute; background: linear-gradient(45deg, rgba(0, 0, 0, 1) 0%, rgba(245, 0, 41, 1) 100%);
width: 200%; width: 1000;
height: 200%; height: 1000;
top: -50%; position: fixed;
left: -50%; top:-500;
z-index: -1; right: 50vw;
background-image: radial-gradient(#0c0a09b5 20%, #292524 22%); border-radius: 50%;
background-size: 80px 80px; z-index: -999;
animation-name: background_keyframes;
animation-duration: 10s;
animation-iteration-count: infinite;
animation-timing-function: linear;
transform: rotate(35deg) scale(2);
contain: paint;
} }
@keyframes background_keyframes { .bg2{
0%{ background: #6D001A;
background-position: 0px 0px; background: linear-gradient(-45deg, rgba(0, 0, 0, 1) 0%, rgba(245, 0, 41, 1) 100%);
} width: 1000;
100%{ height: 1000;
background-position: 160px 80px; position: fixed;
} bottom: -500;
left: 50vw;
border-radius: 50%;
z-index: -999;
} }

View file

@ -1,11 +1,8 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import "./globals.css"; import "./globals.css";
import Navbar from "./navbar";
import FootBar from "./footbar";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Welcome - Mike Karl", title: "Portfolio - Mike Karl",
description: "Personal website of Mike Karl",
}; };
export default function RootLayout({ export default function RootLayout({
@ -14,13 +11,11 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="en"> <html lang="de">
<body <body className="bg">
className={`bg-stone-950`} <div className="bg1" />
> <div className="bg2" />
<Navbar />
{children} {children}
<FootBar />
</body> </body>
</html> </html>
); );

View file

@ -1,85 +0,0 @@
'use client';
import Link from "next/link";
import { useState } from "react";
import { Button } from "./components";
export default function Navbar() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const navItems = [
{ href: "/cv", label: "Lebenslauf" },
{ href: "/portfolio", label: "Portfolio" },
{ href: "/contact", label: "Kontakt" }
];
return (
<nav className="fixed top-0 left-0 right-0 z-50 backdrop-blur-md border-b border-gray-800/30 shadow-xl">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<Link
href="/"
className="text-2xl font-bold text-white hover:text-blue-400 transition-colors duration-300 flex items-center gap-2"
>
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center text-white font-bold text-sm">
MK
</div>
<span className="hidden sm:block">Mike Karl</span>
</Link>
{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-1">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className="px-4 py-2 rounded-lg text-gray-300 hover:text-white hover:bg-gray-800/50 transition-all duration-300 hover:scale-105 relative group"
>
{item.label}
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-blue-500 group-hover:w-full transition-all duration-300"></span>
</Link>
))}
</div>
{/* Mobile Menu Button */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="md:hidden p-2 rounded-lg text-gray-300 hover:text-white hover:bg-gray-800/50 transition-all duration-300"
aria-label="Toggle mobile menu"
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
{isMobileMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
{/* Mobile Menu */}
<div className={`will-change-transform md:hidden transition-all duration-300 overflow-hidden ${isMobileMenuOpen ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
}`}>
<div className="py-6 space-y-2 border-t border-stone-950 border-b">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className="block px-4 py-3 rounded-lg text-gray-300 hover:text-white hover:bg-gray-800/50 transition-all duration-300"
onClick={() => setIsMobileMenuOpen(false)}
>
{item.label}
</Link>
))}
</div>
</div>
</div>
</nav>
);
}

View file

@ -1,238 +1,6 @@
import { Section, Card, CardHeader, CardContent, CardFooter, Button, Badge } from "./components";
export default function Home() { export default function Home() {
return ( return (
<main> <main className="p-8">
{/* Hero Section */}
<Section variant="hero" title="Mike Karl" subtitle="Fullstack Web und Systementwickler">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card
variant="glass"
hoverable
>
<CardHeader>
<h3 className="text-xl font-semibold text-white mb-2">Über mich</h3>
</CardHeader>
<CardContent>
<p className="text-gray-300">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut ex, labore deleniti fuga repellendus maiores rerum, voluptates dicta accusamus nihil quibusdam dolorum dolor illo et corrupti doloremque velit itaque repudiandae!
</p>
</CardContent>
</Card>
<Card
variant="default"
hoverable
>
<CardHeader>
<h3 className="text-xl font-semibold text-white mb-2">Aktuelle Beschäftigung</h3>
</CardHeader>
<CardContent>
<p className="text-gray-300">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere similique iusto beatae minima ea. Explicabo voluptatem itaque unde sequi, aperiam, dicta nulla nobis maiores, debitis aliquam corrupti quam earum dolor!
</p></CardContent>
</Card>
<Card
variant="outlined"
hoverable
>
<CardHeader>
<h3 className="text-xl font-semibold text-white mb-2">Erfahrung</h3>
</CardHeader>
<CardContent>
<p className="text-gray-300">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisi distinctio neque natus quibusdam? Corrupti reprehenderit nisi repellendus facere, reiciendis, rerum, error iusto doloribus asperiores sapiente alias repellat autem? Earum, possimus?
</p></CardContent>
</Card>
</div>
</Section>
{/* Education Section */}
<Section title="Bildung" subtitle="Meine akademische Laufbahn und professionelle Entwicklung">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card
variant="default"
>
<CardHeader>
<h3 className="text-lg font-semibold text-white">Gymnasium Zwiesel</h3>
<p className="text-gray-400 text-sm">Weiterführende Schule</p>
</CardHeader>
<CardContent>
<p className="text-gray-300">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatum amet porro cumque dolores eveniet sunt labore, vitae ipsam consectetur nisi alias odio temporibus! Mollitia nisi, similique eos quam saepe deleniti!
</p>
</CardContent>
</Card>
<Card
variant="default"
>
<CardHeader>
<h3 className="text-lg font-semibold text-white">Universität Passau</h3>
<p className="text-gray-400 text-sm">Universität</p>
</CardHeader>
<CardContent>
<p className="text-gray-300">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Magnam, cumque excepturi voluptates qui repellendus omnis animi ab placeat ullam soluta error aspernatur repellat. Repellat, omnis facilis sequi perferendis aliquam expedita?
</p>
</CardContent>
</Card>
</div>
</Section>
{/* Skills Section */}
<Section title="Erfahrung" subtitle="Technisches Wissen und Skills">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card variant="outlined">
<CardHeader>
<h3 className="text-lg font-semibold text-white mb-3">Anwendungsentwicklung</h3>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex flex-wrap gap-2">
<Badge variant="blue">JavaScript</Badge>
<Badge variant="blue">Rust</Badge>
<Badge variant="blue">Java</Badge>
<Badge variant="blue">C#</Badge>
</div>
<p className="text-gray-300 text-sm">
Webentwicklung und Systementwicklung
</p>
</div>
</CardContent>
</Card>
<Card variant="outlined">
<CardHeader>
<h3 className="text-lg font-semibold text-white mb-3">Sytemintegration</h3>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex flex-wrap gap-2">
<Badge variant="green">Hardware</Badge>
<Badge variant="green">Virtualisierung</Badge>
<Badge variant="green">Networking</Badge>
</div>
<p className="text-gray-300 text-sm">
Self-Hosting
</p>
</div>
</CardContent>
</Card>
<Card variant="outlined">
<CardHeader>
<h3 className="text-lg font-semibold text-white mb-3">Soft Skills</h3>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex flex-wrap gap-2">
<Badge variant="yellow">Projektmanagement</Badge>
<Badge variant="yellow">Layer 8 ^^</Badge>
</div>
<p className="text-gray-300 text-sm">
Erfahrung im professionellen Umgang mit internen und externen Kunden
</p>
</div>
</CardContent>
</Card>
</div>
</Section>
{/* Portfolio Section */}
<Section title="Portfolio" subtitle="Einige meiner Projekte (allein und im Team)">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card
variant="glass"
>
<CardHeader>
<h3 className="text-lg font-semibold text-white">Raytracing in a Weekend</h3>
<p className="text-gray-400 text-sm">Graphics Programming</p>
</CardHeader>
<CardContent>
<p className="text-gray-300 mb-4">
Ein Nachbau des Raytracers aus der Buchserie "Raytracing in a Weekend". Portiert von C++ auf Rust.
</p>
<div className="flex flex-wrap gap-2">
<Badge variant="blue">Rust</Badge>
<Badge variant="blue">Graphics</Badge>
<Badge variant="blue">Math</Badge>
</div>
</CardContent>
<CardFooter>
<Button variant="outline" size="sm">zum Code</Button>
</CardFooter>
</Card>
<Card
variant="glass"
>
<CardHeader>
<h3 className="text-lg font-semibold text-white">6502 Emulator</h3>
<p className="text-gray-400 text-sm">System Programming</p>
</CardHeader>
<CardContent>
<p className="text-gray-300 mb-4">
Vollständiger Emulator des 6502 Mikroprozessors geschrieben in Rust.
Alle Instruktionen und Addressierungsmodi auf einzelne Taktzyklen genau simuliert.
</p>
<div className="flex flex-wrap gap-2">
<Badge variant="blue">Emulation</Badge>
<Badge variant="blue">Low-level</Badge>
<Badge variant="blue">Hardware</Badge>
</div>
</CardContent>
<CardFooter>
<Button variant="outline" size="sm">zum Code</Button>
</CardFooter>
</Card>
</div>
</Section>
{/* Personal Section */}
<Section title="Persönliches" subtitle="Über die Arbeit hinaus">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card variant="default">
<CardHeader>
<h3 className="text-lg font-semibold text-white">Hobbies</h3>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
<span className="text-gray-300">Motorradfahren</span>
</div>
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-purple-500 rounded-full"></div>
<span className="text-gray-300">Astrofotografie</span>
</div>
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span className="text-gray-300">Elektrotechnik</span>
</div>
</div>
</CardContent>
</Card>
<Card variant="default">
<CardHeader>
<h3 className="text-lg font-semibold text-white">Persönlicher Status</h3>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex items-center gap-3">
<div className="w-2 h-2 bg-red-500 rounded-full"></div>
<span className="text-gray-300">Glücklich Vergeben ❤️</span>
</div>
</div>
</CardContent>
</Card>
</div>
</Section>
{/* Contact Section */}
<Section variant="minimal" title="Kontaktieren Sie mich gerne" subtitle="Sehen Sie sich meine Profile an und kontaktieren sie mich. Ich freue mich auf ein Gespräch mit Ihnen." />
</main> </main>
); );
} }