diff --git a/index.html b/index.html index e4b78ea..9e880c6 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Vite + React + TS + Sascha Bach Portfolio
diff --git a/package-lock.json b/package-lock.json index de2a521..6717e11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "portfolio-page", "version": "0.0.0", "dependencies": { + "lucide-react": "^0.539.0", "react": "^19.1.1", "react-dom": "^19.1.1" }, @@ -2966,6 +2967,15 @@ "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": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index 1d98817..4a7e1cb 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "lucide-react": "^0.539.0", "react": "^19.1.1", "react-dom": "^19.1.1" }, diff --git a/src/app/page.tsx b/src/app/page.tsx index 4ddd3fb..880745e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,8 @@ import { useState, useEffect } from 'react'; import '../scss/App.scss'; import Navbar from '../components/layout/Navigation'; +import HeroSection from '../components/sections/HeroSection'; +import AboutSection from '../components/sections/AboutSection'; function PortfolioApp() { const [theme, setTheme] = useState<'light' | 'dark'>('light'); @@ -12,6 +14,8 @@ function PortfolioApp() { return ( <> + +

Aktuelles Theme: {theme}

); diff --git a/src/assets/sascha.png b/src/assets/sascha.png new file mode 100644 index 0000000..509aee4 Binary files /dev/null and b/src/assets/sascha.png differ diff --git a/src/components/layout/MobileMenu.tsx b/src/components/layout/MobileMenu.tsx new file mode 100644 index 0000000..cda8578 --- /dev/null +++ b/src/components/layout/MobileMenu.tsx @@ -0,0 +1,16 @@ +type Props = { + menuItems: string[]; + isOpen: boolean; +}; + +export default function MobileMenu({ menuItems, isOpen }: Props) { + if (!isOpen) return null; + + return ( +
+ {menuItems.map((item) => ( + + ))} +
+ ); +} diff --git a/src/components/layout/navigation.tsx b/src/components/layout/Navigation.tsx similarity index 83% rename from src/components/layout/navigation.tsx rename to src/components/layout/Navigation.tsx index d602491..06c46d7 100644 --- a/src/components/layout/navigation.tsx +++ b/src/components/layout/Navigation.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import ThemeToggle from '../ThemeToggle'; +import MobileMenu from './MobileMenu'; type Props = { theme: 'light' | 'dark'; @@ -41,14 +42,7 @@ export default function Navbar({ theme, setTheme }: Props) { {[0, 1, 2].map(i => )} - {/* Optional: Mobile Menu Dropdown */} - {menuOpen && ( -
- {menuItems.map((item) => ( - - ))} -
- )} + ); diff --git a/src/components/sections/AboutSection.tsx b/src/components/sections/AboutSection.tsx new file mode 100644 index 0000000..8ba4547 --- /dev/null +++ b/src/components/sections/AboutSection.tsx @@ -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 Header */} +
+

+ {title} +

+

+ {subtitle} +

+
+ + {/* Personal Photo and Bio */} +
+
+ {`${name} +
+
+ +
+
+

+ Hi, I'm {name}! +

+

+ {bio} +

+
+ +
+ {skillBadges.map((badge, index) => ( + + {badge.text} + + ))} +
+
+
+ + {/* Feature Cards Grid */} +
+ {featureCards.map((card, index) => { + const IconComponent = card.icon; + return ( +
+
+ +

+ {card.title} +

+
+
+

+ {card.description} +

+
+
+ ); + })} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/sections/HeroSection.tsx b/src/components/sections/HeroSection.tsx new file mode 100644 index 0000000..c5acd1c --- /dev/null +++ b/src/components/sections/HeroSection.tsx @@ -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 ( +
+
+

{title}

+

+ {description} +

+
+ + +
+
+ {statItems.map((item, index) => ( +
+ {item.value} + {item.label} +
+ ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/data/FeatureCard.ts b/src/data/FeatureCard.ts new file mode 100644 index 0000000..285071a --- /dev/null +++ b/src/data/FeatureCard.ts @@ -0,0 +1,8 @@ +import { LucideIcon } from 'lucide-react'; + +export interface FeatureCard { + icon: LucideIcon; + title: string; + description: string; + colorClass: 'blue' | 'purple' | 'teal'; +} \ No newline at end of file diff --git a/src/data/SkillBadge.ts b/src/data/SkillBadge.ts new file mode 100644 index 0000000..b54c5c3 --- /dev/null +++ b/src/data/SkillBadge.ts @@ -0,0 +1,4 @@ +export interface SkillBadge { + text: string; + colorClass: 'blue' | 'green' | 'purple' | 'orange'; +} \ No newline at end of file diff --git a/src/data/StatItem.ts b/src/data/StatItem.ts new file mode 100644 index 0000000..3bfc4d4 --- /dev/null +++ b/src/data/StatItem.ts @@ -0,0 +1,4 @@ +export interface StatItem { + label: string; + value: string; +} \ No newline at end of file diff --git a/src/scss/App.scss b/src/scss/App.scss index 564abad..9c61b65 100644 --- a/src/scss/App.scss +++ b/src/scss/App.scss @@ -1,5 +1,20 @@ @use 'themes' as t; +@use 'variables'; +@use 'mixins'; +@use 'globals'; @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) :root { @@ -12,6 +27,14 @@ } body { + font-family: 'Geist', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; background-color: var(--color-background); 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 +} \ No newline at end of file diff --git a/src/scss/about-section.scss b/src/scss/about-section.scss new file mode 100644 index 0000000..7a8fd08 --- /dev/null +++ b/src/scss/about-section.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/scss/globals.scss b/src/scss/globals.scss index eb7d2fc..0f2b5da 100644 --- a/src/scss/globals.scss +++ b/src/scss/globals.scss @@ -1 +1,75 @@ +@use 'variables' as *; +@use 'mixins' as *; @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; +} + diff --git a/src/scss/hero-section.scss b/src/scss/hero-section.scss new file mode 100644 index 0000000..94d3919 --- /dev/null +++ b/src/scss/hero-section.scss @@ -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; + } + } +} \ No newline at end of file diff --git a/src/scss/mixins.scss b/src/scss/mixins.scss new file mode 100644 index 0000000..7401e86 --- /dev/null +++ b/src/scss/mixins.scss @@ -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; + } +} \ No newline at end of file diff --git a/src/scss/themes.scss b/src/scss/themes.scss index b7f7edb..b350e8d 100644 --- a/src/scss/themes.scss +++ b/src/scss/themes.scss @@ -1,22 +1,203 @@ @use 'sass:map'; +@use 'variables' as *; -// Light Theme Farben +// Light Theme Colors $light-theme: ( primary: #000000, background: #ffffff, 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: ( - primary: #f59e0b, + primary: #9333ea, background: #1f2937, 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) { --color-primary: #{map.get($theme, primary)}; --color-background: #{map.get($theme, background)}; --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)}; } diff --git a/src/scss/topbar.scss b/src/scss/topbar.scss index 1184e78..8b09f5a 100644 --- a/src/scss/topbar.scss +++ b/src/scss/topbar.scss @@ -1,50 +1,35 @@ @use 'globals'; .navbar { - display: flex; + @include globals.flex-center(); justify-content: space-between; - align-items: center; padding: 1em; background: var(--color-background); color: var(--color-text); - height: 30px; + height: 65px; &__name { font-weight: bold; font-size: 1.25rem; } - // Desktop Bar + &__container { &__button { - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - border-radius: 0; - border-color: unset; - background-color: transparent; - opacity: 1; - + @extend %button-reset; + @extend %hover-lift; padding: 0.6rem 1.2rem; - border: none; border-radius: 8px; - cursor: pointer; 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 { - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px var(--box-shadow-active); transform: translateY(0); } } } - // Burger-Button für mobile Geräte + &__burger-button { - flex-direction: column; + @include globals.flex-center(); justify-content: space-between; width: 20px; height: 17px; @@ -85,8 +70,7 @@ /* Mobile Menu Dropdown */ .navbar__mobile-menu { - display: flex; - flex-direction: column; + @include globals.flex-center(); position: absolute; right: 0; background: var(--color-background); @@ -94,46 +78,41 @@ border-radius: 8px; padding: 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; button { - background: none; - border: none; - color: var(--color-text); + @extend %button-reset; padding: 0.5em 1em; text-align: right; - cursor: pointer; border-radius: 4px; transition: background 0.2s; &:hover { - background: rgba(0, 0, 0, 0.05); + background: var(--box-shadow-active); } } } } -// Mobile-Only Styles -@media (max-width: globals.$mobile-breakpoint) { +@include globals.mobile-only { .navbar__container__button { display: none; } } -@media (min-width: globals.$desktop-breakpoint) { +@include globals.desktop-only { .navbar__burger-button { display: none; } } .theme-toggle { - background: none; - border: none; + @extend %button-reset; font-size: 1rem; - cursor: pointer; transition: transform 0.2s ease; width: 20px; + &:hover { transform: scale(1.2); } diff --git a/src/scss/variables.scss b/src/scss/variables.scss index cfe6bd6..fa747b6 100644 --- a/src/scss/variables.scss +++ b/src/scss/variables.scss @@ -1,2 +1,34 @@ +// Breakpoint variables $mobile-breakpoint: 768px; $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