HeroSection and AboutSection created and styled. Refactoring

This commit is contained in:
Sascha Bach 2025-08-14 15:49:46 +02:00
parent 4c141111ee
commit 15c1ec5839
20 changed files with 1084 additions and 50 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title> <title>Sascha Bach Portfolio</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

10
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "portfolio-page", "name": "portfolio-page",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"lucide-react": "^0.539.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1" "react-dom": "^19.1.1"
}, },
@ -2966,6 +2967,15 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/lucide-react": {
"version": "0.539.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.539.0.tgz",
"integrity": "sha512-VVISr+VF2krO91FeuCrm1rSOLACQUYVy7NQkzrOty52Y8TlTPcXcMdQFj9bYzBgXbWCiywlwSZ3Z8u6a+6bMlg==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",

View File

@ -10,6 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"lucide-react": "^0.539.0",
"react": "^19.1.1", "react": "^19.1.1",
"react-dom": "^19.1.1" "react-dom": "^19.1.1"
}, },

View File

@ -1,6 +1,8 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import '../scss/App.scss'; import '../scss/App.scss';
import Navbar from '../components/layout/Navigation'; import Navbar from '../components/layout/Navigation';
import HeroSection from '../components/sections/HeroSection';
import AboutSection from '../components/sections/AboutSection';
function PortfolioApp() { function PortfolioApp() {
const [theme, setTheme] = useState<'light' | 'dark'>('light'); const [theme, setTheme] = useState<'light' | 'dark'>('light');
@ -12,6 +14,8 @@ function PortfolioApp() {
return ( return (
<> <>
<Navbar theme={theme} setTheme={setTheme} /> <Navbar theme={theme} setTheme={setTheme} />
<HeroSection />
<AboutSection />
<h1>Aktuelles Theme: {theme} </h1> <h1>Aktuelles Theme: {theme} </h1>
</> </>
); );

BIN
src/assets/sascha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

@ -0,0 +1,16 @@
type Props = {
menuItems: string[];
isOpen: boolean;
};
export default function MobileMenu({ menuItems, isOpen }: Props) {
if (!isOpen) return null;
return (
<div className="navbar__mobile-menu">
{menuItems.map((item) => (
<button key={item}>{item}</button>
))}
</div>
);
}

View File

@ -1,5 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import ThemeToggle from '../ThemeToggle'; import ThemeToggle from '../ThemeToggle';
import MobileMenu from './MobileMenu';
type Props = { type Props = {
theme: 'light' | 'dark'; theme: 'light' | 'dark';
@ -41,14 +42,7 @@ export default function Navbar({ theme, setTheme }: Props) {
{[0, 1, 2].map(i => <span key={i} className="navbar__burger-button__item"></span>)} {[0, 1, 2].map(i => <span key={i} className="navbar__burger-button__item"></span>)}
</div> </div>
{/* Optional: Mobile Menu Dropdown */} <MobileMenu menuItems={menuItems} isOpen={menuOpen} />
{menuOpen && (
<div className="navbar__mobile-menu">
{menuItems.map((item) => (
<button key={item}>{item}</button>
))}
</div>
)}
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,123 @@
import { Code, Palette, ShieldCheck } from 'lucide-react';
import type { SkillBadge } from '../../data/SkillBadge';
import type { FeatureCard } from '../../data/FeatureCard';
import saschaImage from '../../assets/sascha.png';
interface AboutSectionProps {
name?: string;
title?: string;
subtitle?: string;
bio?: string;
profileImage?: string;
skillBadges?: SkillBadge[];
featureCards?: FeatureCard[];
}
export default function AboutSection({
name = "Sascha Bach",
title = "About Me",
subtitle = "Frontend developer specializing in modern web technologies. Since 2020, I've been building efficient, scalable solutions from complex Angular applications to creative React projects. My focus is on clean, maintainable code that solves real-world problems.",
bio = "As a dedicated frontend developer, I specialize in creating modern, responsive web applications using both Angular and React frameworks with TypeScript. I'm passionate about writing clean, maintainable code and ensuring exceptional user experiences across all devices and browsers. I have extensive experience with Angular and am actively developing my React expertise, while maintaining proficiency in SCSS, testing with Cypress, and working with XSLT templates.",
profileImage = saschaImage,
skillBadges = [
{ text: "Angular & React Developer", colorClass: "blue" },
{ text: "Responsive Design Expert", colorClass: "green" },
{ text: "Cross-Browser Compatible", colorClass: "purple" },
{ text: "Continuous Learner", colorClass: "orange" }
],
featureCards = [
{
icon: Code,
title: "Frontend Development",
description: "Building modern web applications with Angular and React frameworks, leveraging TypeScript and JavaScript for robust, scalable solutions.",
colorClass: "blue"
},
{
icon: Palette,
title: "Responsive Design",
description: "Creating pixel-perfect, responsive interfaces that work seamlessly across all devices and screen sizes.",
colorClass: "purple"
},
{
icon: ShieldCheck,
title: "Cross-Browser Compatibility",
description: "Ensuring consistent user experiences across all major browsers with thorough testing and optimization.",
colorClass: "teal"
}
]
}: AboutSectionProps) {
return (
<section id="about" className="about-section">
<div className="about-section__container">
{/* Section Header */}
<div className="about-section__header">
<h2 className="about-section__title">
{title}
</h2>
<p className="about-section__subtitle">
{subtitle}
</p>
</div>
{/* Personal Photo and Bio */}
<div className="about-section__content">
<div className="about-section__image-wrapper">
<img
src={profileImage}
alt={`${name} - Full Stack Developer`}
className="about-section__image"
/>
<div className="about-section__image-overlay"></div>
</div>
<div className="about-section__bio-section">
<div className="about-section__bio-content">
<h3 className="about-section__greeting">
Hi, I'm {name}!
</h3>
<p className="about-section__bio-text">
{bio}
</p>
</div>
<div className="about-section__skill-badges">
{skillBadges.map((badge, index) => (
<span
key={index}
className={`about-section__skill-badge about-section__skill-badge--${badge.colorClass}`}
>
{badge.text}
</span>
))}
</div>
</div>
</div>
{/* Feature Cards Grid */}
<div className="about-section__features">
{featureCards.map((card, index) => {
const IconComponent = card.icon;
return (
<div
key={index}
className={`about-section__feature-card about-section__feature-card--${card.colorClass}`}
>
<div className="about-section__feature-header">
<IconComponent className={`about-section__feature-icon about-section__feature-icon--${card.colorClass}`} />
<h3 className={`about-section__feature-title about-section__feature-title--${card.colorClass}`}>
{card.title}
</h3>
</div>
<div className="about-section__feature-content">
<p className={`about-section__feature-description about-section__feature-description--${card.colorClass}`}>
{card.description}
</p>
</div>
</div>
);
})}
</div>
</div>
</section>
);
}

View File

@ -0,0 +1,51 @@
import { Download } from 'lucide-react';
import type { StatItem } from '../../data/StatItem';
interface HeroSectionProps {
title?: string;
description?: string;
primaryButtonText?: string;
secondaryButtonText?: string;
statItems?: StatItem[];
}
export default function HeroSection({
title = "Web-Developer",
description = "Creating responsive, cross-browser compatible web applications with Angular, React, and modern frontend technologies",
primaryButtonText = "View my work",
secondaryButtonText = "Download Resume",
statItems = [
{ label: "Years of Experience", value: "10+" },
{ label: "Responsive Design", value: "100%" },
{ label: "Technologies Used", value: "10+" },
{ label: "Frameworks", value: "3+" },
]
}: HeroSectionProps) {
return (
<section className="hero-section">
<div className="hero-section__container">
<h1 className="hero-section__title">{title}</h1>
<p className="hero-section__description">
{description}
</p>
<div className="hero-section__buttons">
<button className="hero-section__button hero-section__button--primary">
{primaryButtonText}
</button>
<button className="hero-section__button hero-section__button--secondary">
<Download className="hero-section__button-icon" />
{secondaryButtonText}
</button>
</div>
<div className="hero-section__stats">
{statItems.map((item, index) => (
<div key={item.label} className={`hero-section__stat-item hero-section__stat-item--${index + 1}`}>
<span className="hero-section__stat-value">{item.value}</span>
<span className="hero-section__stat-label">{item.label}</span>
</div>
))}
</div>
</div>
</section>
);
}

8
src/data/FeatureCard.ts Normal file
View File

@ -0,0 +1,8 @@
import { LucideIcon } from 'lucide-react';
export interface FeatureCard {
icon: LucideIcon;
title: string;
description: string;
colorClass: 'blue' | 'purple' | 'teal';
}

4
src/data/SkillBadge.ts Normal file
View File

@ -0,0 +1,4 @@
export interface SkillBadge {
text: string;
colorClass: 'blue' | 'green' | 'purple' | 'orange';
}

4
src/data/StatItem.ts Normal file
View File

@ -0,0 +1,4 @@
export interface StatItem {
label: string;
value: string;
}

View File

@ -1,5 +1,20 @@
@use 'themes' as t; @use 'themes' as t;
@use 'variables';
@use 'mixins';
@use 'globals';
@use 'topbar'; @use 'topbar';
@use 'hero-section';
@use 'about-section';
// Import Geist font from Google Fonts or local files
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100;200;300;400;500;600;700;800;900&display=swap');
// CSS Reset - Add this
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
// Standard Theme (Light) // Standard Theme (Light)
:root { :root {
@ -12,6 +27,14 @@
} }
body { body {
font-family: 'Geist', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
background-color: var(--color-background); background-color: var(--color-background);
color: var(--color-text); color: var(--color-text);
margin: 0; // Explicitly remove body margin
padding: 0; // Explicitly remove body padding
}
section {
height: 100vh;
margin: 0; // Remove any section margins
} }

280
src/scss/about-section.scss Normal file
View File

@ -0,0 +1,280 @@
@use 'globals';
.about-section {
padding: 5rem 0;
background: var(--about-background);
&__container {
max-width: 80rem;
margin: 0 auto;
padding: 0 1rem;
@include globals.desktop-only {
padding: 0 1.5rem;
}
}
&__header {
text-align: center;
margin-bottom: 4rem;
}
&__title {
font-size: 1.875rem;
font-weight: bold;
margin-bottom: 1rem;
background: var(--gradient-about-title);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
@include globals.desktop-only {
font-size: 2.25rem;
}
}
&__subtitle {
font-size: 1.25rem;
color: var(--color-text-muted);
max-width: 48rem;
margin: 0 auto;
}
&__content {
display: flex;
flex-direction: column;
align-items: center;
gap: 3rem;
margin-bottom: 4rem;
@include globals.desktop-only {
flex-direction: row;
align-items: center;
}
}
&__image-wrapper {
flex-shrink: 0;
position: relative;
}
&__image {
width: 17rem;
height: 21rem;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
border: 4px solid var(--color-background);
}
&__image-overlay {
position: absolute;
inset: 0;
border-radius: 50%;
background: var(--gradient-image-overlay);
}
&__bio-section {
flex: 1;
text-align: center;
display: flex;
flex-direction: column;
gap: 1.5rem;
@include globals.desktop-only {
text-align: left;
}
}
&__bio-content {
display: flex;
flex-direction: column;
gap: 1rem;
}
&__greeting {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 1rem;
color: var(--about-greeting-color);
}
&__bio-text {
font-size: 1.125rem;
color: var(--color-text-muted);
line-height: 1.625;
}
&__skill-badges {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
justify-content: center;
@include globals.desktop-only {
justify-content: flex-start;
}
}
&__skill-badge {
padding: 0.5rem 1rem;
border-radius: 9999px;
font-size: 0.875rem;
font-weight: 500;
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px var(--box-shadow-hover);
}
&--blue {
background: var(--skill-badge-blue-bg);
color: var(--skill-badge-blue-text);
}
&--green {
background: var(--skill-badge-green-bg);
color: var(--skill-badge-green-text);
}
&--purple {
background: var(--skill-badge-purple-bg);
color: var(--skill-badge-purple-text);
}
&--orange {
background: var(--skill-badge-orange-bg);
color: var(--skill-badge-orange-text);
}
}
&__features {
display: grid;
grid-template-columns: 1fr;
gap: 2rem;
@include globals.desktop-only {
grid-template-columns: repeat(3, 1fr);
}
}
&__feature-card {
text-align: center;
border: none;
border-radius: 0.5rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
overflow: hidden;
&:hover {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
transform: translateY(-2px);
}
&--blue {
background: var(--feature-card-blue-bg);
}
&--purple {
background: var(--feature-card-purple-bg);
}
&--teal {
background: var(--feature-card-teal-bg);
}
}
&__feature-header {
padding: 1.5rem;
}
&__feature-icon {
width: 3rem;
height: 3rem;
margin: 0 auto 1rem auto;
&--blue {
color: var(--feature-icon-blue);
}
&--purple {
color: var(--feature-icon-purple);
}
&--teal {
color: var(--feature-icon-teal);
}
}
&__feature-title {
font-size: 1.25rem;
font-weight: 600;
margin: 0;
&--blue {
color: var(--feature-title-blue);
}
&--purple {
color: var(--feature-title-purple);
}
&--teal {
color: var(--feature-title-teal);
}
}
&__feature-content {
padding: 0 1.5rem 1.5rem 1.5rem;
}
&__feature-description {
margin: 0;
&--blue {
color: var(--feature-description-blue);
}
&--purple {
color: var(--feature-description-purple);
}
&--teal {
color: var(--feature-description-teal);
}
}
}
// Mobile responsiveness
@include globals.mobile-only {
.about-section {
padding: 3rem 0;
&__container {
padding: 0 1rem;
}
&__image {
width: 12rem;
height: 12rem;
}
&__title {
font-size: 1.5rem;
}
&__subtitle {
font-size: 1rem;
}
&__bio-text {
font-size: 1rem;
}
&__greeting {
font-size: 1.25rem;
}
}
}

View File

@ -1 +1,75 @@
@use 'variables' as *;
@use 'mixins' as *;
@forward 'variables'; @forward 'variables';
@forward 'mixins';
// Global button placeholder
%button-base {
@include button-base;
}
// Global card placeholder
%card-base {
border-radius: $border-radius-md;
box-shadow: var(--box-shadow-md);
@include card-hover;
}
// Responsive mixins (keep existing)
@mixin mobile-only {
@media (max-width: #{$mobile-breakpoint - 1px}) {
@content;
}
}
@mixin desktop-only {
@media (min-width: $desktop-breakpoint) {
@content;
}
}
// Common button base styles
%button-base {
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: 500;
cursor: pointer;
border: none;
border-radius: 8px;
transition: all 0.3s ease;
text-decoration: none;
&:focus {
outline: none;
}
&:active {
transform: translateY(0);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none !important;
box-shadow: none !important;
}
}
// Interactive element hover effects
%hover-lift {
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px var(--box-shadow-hover);
}
}
// Clean button reset
%button-reset {
background: none;
border: none;
color: inherit;
cursor: pointer;
font: inherit;
}

173
src/scss/hero-section.scss Normal file
View File

@ -0,0 +1,173 @@
@use 'globals';
@use 'variables' as *;
@use 'mixins' as *;
// Hero-specific button extensions
%hero-button-base {
@extend %button-base;
padding: $spacing-sm $spacing-xl;
font-size: $spacing-md;
line-height: 1.5;
border-radius: $border-radius-sm;
gap: $spacing-xs;
border: 2px solid transparent;
box-sizing: border-box;
&:focus {
box-shadow: 0 0 0 3px var(--box-shadow-hover);
}
}
.hero-section {
min-height: 100vh;
@include flex-center;
padding: $spacing-xl $spacing-md;
background: var(--hero-background);
&__container {
max-width: 800px;
width: 100%;
text-align: center;
@include flex-center;
flex-direction: column;
gap: $spacing-xl;
}
&__title {
font-size: 3rem;
font-weight: bold;
margin: 0;
@include gradient-text(var(--gradient-text));
}
&__description {
font-size: 1.2rem;
line-height: 1.6;
color: var(--color-text-muted);
max-width: 600px;
margin: 0;
}
&__buttons {
@include flex-center;
gap: $spacing-lg;
flex-wrap: wrap;
}
&__button {
@extend %hero-button-base;
&--primary {
background: var(--gradient-primary);
color: var(--color-background);
&:hover {
transform: translateY(-1px);
box-shadow: var(--box-shadow-sm);
filter: brightness(1.1);
}
}
&--secondary {
background: transparent;
color: var(--color-text);
border-color: $purple-primary;
padding: calc(#{$spacing-sm} - 1px) $spacing-xl;
&:hover {
background: $purple-primary;
color: var(--color-background);
transform: translateY(-1px);
box-shadow: var(--box-shadow-sm);
}
}
}
&__button-icon {
width: $spacing-md;
height: $spacing-md;
flex-shrink: 0;
}
&__stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: $spacing-xl;
margin-top: $spacing-3xl;
@include globals.desktop-only {
grid-template-columns: repeat(4, 1fr);
}
}
&__stat-item {
text-align: center;
padding: $spacing-md;
border-radius: $border-radius-md;
&--1 {
background: var(--stat-experience-bg);
.hero-section__stat-value { color: var(--stat-experience-value); }
.hero-section__stat-label { color: var(--stat-experience-label); }
}
&--2 {
background: var(--stat-design-bg);
.hero-section__stat-value { color: var(--stat-design-value); }
.hero-section__stat-label { color: var(--stat-design-label); }
}
&--3 {
background: var(--stat-technologies-bg);
.hero-section__stat-value { color: var(--stat-technologies-value); }
.hero-section__stat-label { color: var(--stat-technologies-label); }
}
&--4 {
background: var(--stat-motivation-bg);
.hero-section__stat-value { color: var(--stat-motivation-value); }
.hero-section__stat-label { color: var(--stat-motivation-label); }
}
}
&__stat-value {
font-size: 1.875rem;
font-weight: 700;
line-height: 1.2;
margin-bottom: 0.25rem;
display: block;
}
&__stat-label {
font-size: 0.875rem;
line-height: 1.4;
font-weight: 400;
}
}
// Mobile responsiveness
@include globals.mobile-only {
.hero-section {
&__title {
font-size: 2.5rem;
}
&__description {
font-size: 1.1rem;
}
&__buttons {
flex-direction: column;
gap: $spacing-md;
}
&__button {
width: 100%;
max-width: 300px;
}
}
}

77
src/scss/mixins.scss Normal file
View File

@ -0,0 +1,77 @@
@use 'variables' as *;
// Button base mixin
@mixin button-base {
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: 500;
text-decoration: none;
border: none;
cursor: pointer;
transition: all 0.3s ease;
border-radius: $border-radius-sm;
&:focus {
outline: none;
box-shadow: var(--box-shadow-sm);
}
}
// Card hover effect mixin
@mixin card-hover {
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: var(--box-shadow-lg);
}
}
// Gradient text mixin
@mixin gradient-text($gradient) {
background: $gradient;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
}
// Flex center mixin
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
// Aspect ratio mixin
@mixin aspect-ratio($ratio: 1) {
aspect-ratio: $ratio;
@supports not (aspect-ratio: 1) {
&::before {
content: '';
float: left;
padding-top: calc(100% / #{$ratio});
}
&::after {
content: '';
display: table;
clear: both;
}
}
}
// Responsive breakpoint mixins
@mixin mobile-only {
@media (max-width: variables.$mobile-breakpoint) {
@content;
}
}
@mixin desktop-only {
@media (min-width: variables.$desktop-breakpoint) {
@content;
}
}

View File

@ -1,22 +1,203 @@
@use 'sass:map'; @use 'sass:map';
@use 'variables' as *;
// Light Theme Farben // Light Theme Colors
$light-theme: ( $light-theme: (
primary: #000000, primary: #000000,
background: #ffffff, background: #ffffff,
text: #000000, text: #000000,
text-muted: #6b7280,
active-box-shadow: rgba(0, 0, 0, 0.1),
hover-box-shadow: rgba(0, 0, 0, 0.15),
hero-background: $gradient-hero-light,
gradient-primary: $gradient-primary,
gradient-text: $gradient-text,
// Stat colors for light theme
stat-experience-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
stat-experience-value: #2563eb,
stat-experience-label: #1d4ed8,
stat-design-bg: linear-gradient(135deg, #dcfce7, #bbf7d0),
stat-design-value: #16a34a,
stat-design-label: #15803d,
stat-technologies-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
stat-technologies-value: #0d9488,
stat-technologies-label: #0f766e,
stat-motivation-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
stat-motivation-value: #9333ea,
stat-motivation-label: #7c3aed,
// About section colors
about-background: linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 50%, #f0fdfa 100%),
gradient-about-title: linear-gradient(90deg, #059669, #0d9488),
about-greeting-color: #047857,
gradient-image-overlay: linear-gradient(45deg, rgba(59, 130, 246, 0.2), rgba(147, 51, 234, 0.2)),
// Skill badge colors
skill-badge-blue-bg: #dbeafe,
skill-badge-blue-text: #1e40af,
skill-badge-green-bg: #dcfce7,
skill-badge-green-text: #166534,
skill-badge-purple-bg: #e9d5ff,
skill-badge-purple-text: #7c2d12,
skill-badge-orange-bg: #fed7aa,
skill-badge-orange-text: #9a3412,
// Feature card colors
feature-card-blue-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
feature-card-purple-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
feature-card-teal-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
feature-icon-blue: #2563eb,
feature-icon-purple: #9333ea,
feature-icon-teal: #0d9488,
feature-title-blue: #1e40af,
feature-title-purple: #7c3aed,
feature-title-teal: #0f766e,
feature-description-blue: #1d4ed8,
feature-description-purple: #6d28d9,
feature-description-teal: #115e59,
// Shadow variables using CSS custom properties
box-shadow-sm: #{$shadow-sm},
box-shadow-md: #{$shadow-md},
box-shadow-lg: #{$shadow-lg}
); );
// Dark Theme Farben // Dark Theme Colors
$dark-theme: ( $dark-theme: (
primary: #f59e0b, primary: #9333ea,
background: #1f2937, background: #1f2937,
text: #f9fafb, text: #f9fafb,
text-muted: #9ca3af,
active-box-shadow: rgba(255, 255, 255, 0.1),
hover-box-shadow: rgba(255, 255, 255, 0.15),
hero-background: $gradient-hero-dark,
gradient-primary: $gradient-primary,
gradient-text: $gradient-text,
// Stat colors for dark theme
stat-experience-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
stat-experience-value: #2563eb,
stat-experience-label: #93c5fd,
stat-design-bg: linear-gradient(135deg, rgba(20, 83, 45, 0.3), rgba(22, 101, 52, 0.3)),
stat-design-value: #16a34a,
stat-design-label: #86efac,
stat-technologies-bg: linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
stat-technologies-value: #0d9488,
stat-technologies-label: #5eead4,
stat-motivation-bg: linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
stat-motivation-value: #9333ea,
stat-motivation-label: #c4b5fd,
// About section colors
about-background: linear-gradient(135deg, rgba(20, 83, 45, 0.2) 0%, rgba(6, 78, 59, 0.2) 100%),
gradient-about-title: linear-gradient(90deg, #10b981, #14b8a6),
about-greeting-color: #34d399,
gradient-image-overlay: linear-gradient(45deg, rgba(59, 130, 246, 0.2), rgba(147, 51, 234, 0.2)),
// Skill badge colors
skill-badge-blue-bg: rgba(30, 58, 138, 0.3),
skill-badge-blue-text: #93c5fd,
skill-badge-green-bg: rgba(20, 83, 45, 0.3),
skill-badge-green-text: #86efac,
skill-badge-purple-bg: rgba(88, 28, 135, 0.3),
skill-badge-purple-text: #c4b5fd,
skill-badge-orange-bg: rgba(154, 52, 18, 0.3),
skill-badge-orange-text: #fdba74,
// Feature card colors
feature-card-blue-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
feature-card-purple-bg: linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
feature-card-teal-bg: linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
feature-icon-blue: #3b82f6,
feature-icon-purple: #a855f7,
feature-icon-teal: #14b8a6,
feature-title-blue: #93c5fd,
feature-title-purple: #c4b5fd,
feature-title-teal: #5eead4,
feature-description-blue: #dbeafe,
feature-description-purple: #e9d5ff,
feature-description-teal: #ccfbf1,
// Shadow variables
box-shadow-sm: #{$shadow-sm},
box-shadow-md: #{$shadow-md},
box-shadow-lg: #{$shadow-lg}
); );
// Mixin: SCSS-Map CSS-Variablen schreiben // Mixin: SCSS-Map CSS-Variables
@mixin theme-vars($theme) { @mixin theme-vars($theme) {
--color-primary: #{map.get($theme, primary)}; --color-primary: #{map.get($theme, primary)};
--color-background: #{map.get($theme, background)}; --color-background: #{map.get($theme, background)};
--color-text: #{map.get($theme, text)}; --color-text: #{map.get($theme, text)};
--color-text-muted: #{map.get($theme, text-muted)};
--box-shadow-hover: #{map.get($theme, hover-box-shadow)};
--box-shadow-active: #{map.get($theme, active-box-shadow)};
--hero-background: #{map.get($theme, hero-background)};
--gradient-primary: #{map.get($theme, gradient-primary)};
--gradient-text: #{map.get($theme, gradient-text)};
// Stat variables
--stat-experience-bg: #{map.get($theme, stat-experience-bg)};
--stat-experience-value: #{map.get($theme, stat-experience-value)};
--stat-experience-label: #{map.get($theme, stat-experience-label)};
--stat-design-bg: #{map.get($theme, stat-design-bg)};
--stat-design-value: #{map.get($theme, stat-design-value)};
--stat-design-label: #{map.get($theme, stat-design-label)};
--stat-technologies-bg: #{map.get($theme, stat-technologies-bg)};
--stat-technologies-value: #{map.get($theme, stat-technologies-value)};
--stat-technologies-label: #{map.get($theme, stat-technologies-label)};
--stat-motivation-bg: #{map.get($theme, stat-motivation-bg)};
--stat-motivation-value: #{map.get($theme, stat-motivation-value)};
--stat-motivation-label: #{map.get($theme, stat-motivation-label)};
// About section variables
--about-background: #{map.get($theme, about-background)};
--gradient-about-title: #{map.get($theme, gradient-about-title)};
--about-greeting-color: #{map.get($theme, about-greeting-color)};
--gradient-image-overlay: #{map.get($theme, gradient-image-overlay)};
// Skill badge variables
--skill-badge-blue-bg: #{map.get($theme, skill-badge-blue-bg)};
--skill-badge-blue-text: #{map.get($theme, skill-badge-blue-text)};
--skill-badge-green-bg: #{map.get($theme, skill-badge-green-bg)};
--skill-badge-green-text: #{map.get($theme, skill-badge-green-text)};
--skill-badge-purple-bg: #{map.get($theme, skill-badge-purple-bg)};
--skill-badge-purple-text: #{map.get($theme, skill-badge-purple-text)};
--skill-badge-orange-bg: #{map.get($theme, skill-badge-orange-bg)};
--skill-badge-orange-text: #{map.get($theme, skill-badge-orange-text)};
// Feature card variables
--feature-card-blue-bg: #{map.get($theme, feature-card-blue-bg)};
--feature-card-purple-bg: #{map.get($theme, feature-card-purple-bg)};
--feature-card-teal-bg: #{map.get($theme, feature-card-teal-bg)};
--feature-icon-blue: #{map.get($theme, feature-icon-blue)};
--feature-icon-purple: #{map.get($theme, feature-icon-purple)};
--feature-icon-teal: #{map.get($theme, feature-icon-teal)};
--feature-title-blue: #{map.get($theme, feature-title-blue)};
--feature-title-purple: #{map.get($theme, feature-title-purple)};
--feature-title-teal: #{map.get($theme, feature-title-teal)};
--feature-description-blue: #{map.get($theme, feature-description-blue)};
--feature-description-purple: #{map.get($theme, feature-description-purple)};
--feature-description-teal: #{map.get($theme, feature-description-teal)};
// Shadow variables
--box-shadow-sm: #{map.get($theme, box-shadow-sm)} var(--box-shadow-hover);
--box-shadow-md: #{map.get($theme, box-shadow-md)};
--box-shadow-lg: #{map.get($theme, box-shadow-lg)};
} }

View File

@ -1,50 +1,35 @@
@use 'globals'; @use 'globals';
.navbar { .navbar {
display: flex; @include globals.flex-center();
justify-content: space-between; justify-content: space-between;
align-items: center;
padding: 1em; padding: 1em;
background: var(--color-background); background: var(--color-background);
color: var(--color-text); color: var(--color-text);
height: 30px; height: 65px;
&__name { &__name {
font-weight: bold; font-weight: bold;
font-size: 1.25rem; font-size: 1.25rem;
} }
// Desktop Bar
&__container { &__container {
&__button { &__button {
font-feature-settings: inherit; @extend %button-reset;
font-variation-settings: inherit; @extend %hover-lift;
letter-spacing: inherit;
color: inherit;
border-radius: 0;
border-color: unset;
background-color: transparent;
opacity: 1;
padding: 0.6rem 1.2rem; padding: 0.6rem 1.2rem;
border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer;
transition: box-shadow 0.2s ease, transform 0.2s ease; transition: box-shadow 0.2s ease, transform 0.2s ease;
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
}
&:active { &:active {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px var(--box-shadow-active);
transform: translateY(0); transform: translateY(0);
} }
} }
} }
// Burger-Button für mobile Geräte
&__burger-button { &__burger-button {
flex-direction: column; @include globals.flex-center();
justify-content: space-between; justify-content: space-between;
width: 20px; width: 20px;
height: 17px; height: 17px;
@ -85,8 +70,7 @@
/* Mobile Menu Dropdown */ /* Mobile Menu Dropdown */
.navbar__mobile-menu { .navbar__mobile-menu {
display: flex; @include globals.flex-center();
flex-direction: column;
position: absolute; position: absolute;
right: 0; right: 0;
background: var(--color-background); background: var(--color-background);
@ -94,46 +78,41 @@
border-radius: 8px; border-radius: 8px;
padding: 0.5em; padding: 0.5em;
gap: 0.5em; gap: 0.5em;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 12px var(--box-shadow-hover);
z-index: 10; z-index: 10;
button { button {
background: none; @extend %button-reset;
border: none;
color: var(--color-text);
padding: 0.5em 1em; padding: 0.5em 1em;
text-align: right; text-align: right;
cursor: pointer;
border-radius: 4px; border-radius: 4px;
transition: background 0.2s; transition: background 0.2s;
&:hover { &:hover {
background: rgba(0, 0, 0, 0.05); background: var(--box-shadow-active);
} }
} }
} }
} }
// Mobile-Only Styles @include globals.mobile-only {
@media (max-width: globals.$mobile-breakpoint) {
.navbar__container__button { .navbar__container__button {
display: none; display: none;
} }
} }
@media (min-width: globals.$desktop-breakpoint) { @include globals.desktop-only {
.navbar__burger-button { .navbar__burger-button {
display: none; display: none;
} }
} }
.theme-toggle { .theme-toggle {
background: none; @extend %button-reset;
border: none;
font-size: 1rem; font-size: 1rem;
cursor: pointer;
transition: transform 0.2s ease; transition: transform 0.2s ease;
width: 20px; width: 20px;
&:hover { &:hover {
transform: scale(1.2); transform: scale(1.2);
} }

View File

@ -1,2 +1,34 @@
// Breakpoint variables
$mobile-breakpoint: 768px; $mobile-breakpoint: 768px;
$desktop-breakpoint: 769px; $desktop-breakpoint: 769px;
// Color variables
$purple-primary: #9333ea;
$blue-primary: #2563eb;
// Shadow variables
$shadow-sm: 0 4px 12px;
$shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
$shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
// Border radius variables
$border-radius-sm: 0.375rem;
$border-radius-md: 0.5rem;
$border-radius-full: 9999px;
// Spacing variables
$spacing-xs: 0.5rem;
$spacing-sm: 0.75rem;
$spacing-md: 1rem;
$spacing-lg: 1.5rem;
$spacing-xl: 2rem;
$spacing-2xl: 3rem;
$spacing-3xl: 4rem;
// Gradient variables
$gradient-primary: linear-gradient(90deg, $blue-primary 0%, $purple-primary 100%);
$gradient-text: linear-gradient(to right, $blue-primary, $purple-primary, #0d9488);
$gradient-hero-light: linear-gradient(135deg, #dbeafe 0%, #e0e7ff 50%, #f3e8ff 100%);
$gradient-hero-dark: linear-gradient(135deg, rgba(30, 58, 138, 0.3) 0%, rgba(55, 48, 163, 0.3) 50%, rgba(88, 28, 135, 0.3) 100%);
// Add any other global variables here