refactoring theme system, refactoring scss, creating footer, contact, certifications and skill sections
This commit is contained in:
parent
e13dda79bf
commit
d15418c3b1
|
|
@ -10,7 +10,8 @@
|
|||
"dependencies": {
|
||||
"lucide-react": "^0.539.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1"
|
||||
"react-dom": "^19.1.1",
|
||||
"react-icons": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.32.0",
|
||||
|
|
@ -3255,6 +3256,15 @@
|
|||
"react": "^19.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
|
||||
"integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
"dependencies": {
|
||||
"lucide-react": "^0.539.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1"
|
||||
"react-dom": "^19.1.1",
|
||||
"react-icons": "^5.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.32.0",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import Navbar from '../components/layout/Navigation';
|
|||
import HeroSection from '../components/sections/HeroSection';
|
||||
import AboutSection from '../components/sections/AboutSection';
|
||||
import ServicesSection from '../components/sections/ServicesSection';
|
||||
import SkillsSection from '../components/sections/SkillsSection';
|
||||
import CertificationsSection from '../components/sections/CertificationsSection';
|
||||
import ProjectsSection from '../components/sections/ProjectsSection';
|
||||
import ContactSection from '../components/sections/ContactSection';
|
||||
import Footer from '../components/layout/Footer';
|
||||
|
||||
function PortfolioApp() {
|
||||
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
||||
|
|
@ -18,6 +23,11 @@ function PortfolioApp() {
|
|||
<HeroSection />
|
||||
<AboutSection />
|
||||
<ServicesSection />
|
||||
<SkillsSection />
|
||||
<CertificationsSection />
|
||||
<ProjectsSection />
|
||||
<ContactSection />
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
import { Github, Linkedin, Mail } from 'lucide-react';
|
||||
import { personalConfig, createEmailLink } from '../../config/personal';
|
||||
|
||||
export default function Footer() {
|
||||
// Email obfuscation function using config
|
||||
const handleEmailClick = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
window.location.href = createEmailLink();
|
||||
};
|
||||
|
||||
return (
|
||||
<footer className="footer">
|
||||
<div className="footer__container">
|
||||
<div className="footer__separator"></div>
|
||||
|
||||
<div className="footer__content">
|
||||
<div className="footer__copyright">
|
||||
© {new Date().getFullYear()} {personalConfig.name}. All rights reserved.
|
||||
</div>
|
||||
|
||||
<div className="footer__social">
|
||||
<button
|
||||
className="footer__social-button"
|
||||
onClick={() => window.open(personalConfig.social.github.url, '_blank')}
|
||||
aria-label="GitHub Profile"
|
||||
>
|
||||
<Github className="footer__social-icon" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="footer__social-button"
|
||||
onClick={() => window.open(personalConfig.social.linkedin.url, '_blank')}
|
||||
aria-label="LinkedIn Profile"
|
||||
>
|
||||
<Linkedin className="footer__social-icon" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="footer__social-button"
|
||||
onClick={handleEmailClick}
|
||||
aria-label="Send Email"
|
||||
>
|
||||
<Mail className="footer__social-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState } from 'react';
|
||||
import ThemeToggle from '../ThemeToggle';
|
||||
import MobileMenu from './MobileMenu';
|
||||
import { personalConfig } from '../../config/personal';
|
||||
|
||||
type Props = {
|
||||
theme: 'light' | 'dark';
|
||||
|
|
@ -19,9 +20,8 @@ export default function Navbar({ theme, setTheme }: Props) {
|
|||
];
|
||||
|
||||
return (
|
||||
// TODO Centralize strings, create buttons via loops
|
||||
<div className="navbar">
|
||||
<div className="navbar__name">Sascha Bach</div>
|
||||
<div className="navbar__name">{personalConfig.name}</div>
|
||||
<div className="navbar__container">
|
||||
{menuItems.map((item) => (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Code, Palette, ShieldCheck } from 'lucide-react';
|
||||
import type { SkillBadge } from '../../data/SkillBadge';
|
||||
import type { FeatureCard } from '../../data/FeatureCard';
|
||||
import type { Service } from '../../data/Service';
|
||||
import saschaImage from '../../assets/sascha.png';
|
||||
|
||||
interface AboutSectionProps {
|
||||
|
|
@ -10,14 +10,14 @@ interface AboutSectionProps {
|
|||
bio?: string;
|
||||
profileImage?: string;
|
||||
skillBadges?: SkillBadge[];
|
||||
featureCards?: FeatureCard[];
|
||||
featureCards?: Service[];
|
||||
}
|
||||
|
||||
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.",
|
||||
subtitle = "Web 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 web 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: "primary" },
|
||||
|
|
@ -28,7 +28,7 @@ export default function AboutSection({
|
|||
featureCards = [
|
||||
{
|
||||
icon: Code,
|
||||
title: "Frontend Development",
|
||||
title: "Web Development",
|
||||
description: "Building modern web applications with Angular and React frameworks, leveraging TypeScript and JavaScript for robust, scalable solutions.",
|
||||
colorClass: "primary"
|
||||
},
|
||||
|
|
@ -64,7 +64,7 @@ export default function AboutSection({
|
|||
<div className="about-section__image-wrapper">
|
||||
<img
|
||||
src={profileImage}
|
||||
alt={`${name} - Full Stack Developer`}
|
||||
alt={`${name} - Web Developer`}
|
||||
className="about-section__image"
|
||||
/>
|
||||
<div className="about-section__image-overlay"></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
import { Award } from 'lucide-react';
|
||||
import type { Certification } from '../../data/Certification';
|
||||
|
||||
interface CertificationsSectionProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
certifications?: Certification[];
|
||||
}
|
||||
|
||||
export default function CertificationsSection({
|
||||
title = "Certifications & Achievements",
|
||||
subtitle = "My professional qualifications and continuous learning",
|
||||
certifications = [
|
||||
{
|
||||
name: "Understanding Typescript",
|
||||
issuer: "Udemy",
|
||||
year: "2025",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Angular Step by Step",
|
||||
issuer: "Udemy",
|
||||
year: "2020",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Angular and Typescript",
|
||||
issuer: "LinkedIn TestDome",
|
||||
year: "2020",
|
||||
icon: ""
|
||||
},
|
||||
{
|
||||
name: "Bachelor of Science in Computer Science",
|
||||
issuer: "TU Darmstadt",
|
||||
year: "2015",
|
||||
icon: ""
|
||||
}
|
||||
]
|
||||
}: CertificationsSectionProps) {
|
||||
// Sort certifications by year in descending order (newest first)
|
||||
const sortedCertifications = [...certifications].sort((a, b) => {
|
||||
return parseInt(b.year) - parseInt(a.year);
|
||||
});
|
||||
|
||||
return (
|
||||
<section id="certifications" className="certifications-section">
|
||||
<div className="certifications-section__container">
|
||||
{/* Section Header */}
|
||||
<div className="certifications-section__header">
|
||||
<h2 className="certifications-section__title">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="certifications-section__subtitle">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Certifications Grid */}
|
||||
<div className="certifications-section__grid">
|
||||
{sortedCertifications.map((cert, index) => (
|
||||
<div key={index} className="certifications-section__card">
|
||||
<div className="certifications-section__card-header">
|
||||
{cert.icon ? (
|
||||
<img
|
||||
src={cert.icon}
|
||||
alt={cert.issuer}
|
||||
className="certifications-section__card-icon-image"
|
||||
/>
|
||||
) : (
|
||||
<Award className="certifications-section__card-icon" />
|
||||
)}
|
||||
<h3 className="certifications-section__card-title">{cert.name}</h3>
|
||||
<p className="certifications-section__card-issuer">{cert.issuer}</p>
|
||||
</div>
|
||||
<div className="certifications-section__card-content">
|
||||
<span className="certifications-section__card-badge">
|
||||
{cert.year}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
import { Mail, Github, Linkedin } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { personalConfig, getObfuscatedEmail, createEmailLink } from '../../config/personal';
|
||||
|
||||
interface ContactSectionProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
email?: string;
|
||||
github?: string;
|
||||
linkedin?: string;
|
||||
connectTitle?: string;
|
||||
connectDescription?: string;
|
||||
}
|
||||
|
||||
// TODO Create Backend API for handling form submissions securely
|
||||
export default function ContactSection({
|
||||
title = "Get In Touch",
|
||||
subtitle = "Let's discuss your next project or collaboration opportunity",
|
||||
github = personalConfig.social.github.username,
|
||||
connectTitle = "Let's Connect",
|
||||
connectDescription = "I'm always interested in new opportunities and exciting projects. Whether you have a question or just want to say hi, feel free to reach out!"
|
||||
}: ContactSectionProps) {
|
||||
// Anti-spam honeypot state
|
||||
const [honeypot, setHoneypot] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Email obfuscation function using config
|
||||
const handleEmailClick = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
window.location.href = createEmailLink();
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Check honeypot - if filled, it's likely spam
|
||||
if (honeypot) {
|
||||
console.warn('Spam detected - honeypot field was filled');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent rapid submissions
|
||||
if (isSubmitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Get form data
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const data = {
|
||||
firstName: formData.get('firstName') as string,
|
||||
lastName: formData.get('lastName') as string,
|
||||
email: formData.get('email') as string,
|
||||
subject: formData.get('subject') as string,
|
||||
message: formData.get('message') as string,
|
||||
};
|
||||
|
||||
// Basic validation
|
||||
if (!data.firstName || !data.lastName || !data.email || !data.message) {
|
||||
alert('Please fill in all required fields.');
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(data.email)) {
|
||||
alert('Please enter a valid email address.');
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Submit to Formspree (replace YOUR_FORM_ID with actual ID from Formspree)
|
||||
const response = await fetch('https://formspree.io/f/YOUR_FORM_ID', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: `${data.firstName} ${data.lastName}`,
|
||||
email: data.email,
|
||||
subject: data.subject || 'Contact from Portfolio',
|
||||
message: data.message,
|
||||
_replyto: data.email, // This tells Formspree what email to reply to
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('Thank you for your message! I\'ll get back to you soon.');
|
||||
e.currentTarget.reset();
|
||||
} else {
|
||||
throw new Error('Form submission failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error);
|
||||
alert('Sorry, there was an error sending your message. Please try again.');
|
||||
}
|
||||
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="contact" className="contact-section">
|
||||
<div className="contact-section__container">
|
||||
{/* Section Header */}
|
||||
<div className="contact-section__header">
|
||||
<h2 className="contact-section__title">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="contact-section__subtitle">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Contact Content Grid */}
|
||||
<div className="contact-section__grid">
|
||||
{/* Contact Info */}
|
||||
<div className="contact-section__info">
|
||||
<div className="contact-section__connect">
|
||||
<h3 className="contact-section__connect-title">{connectTitle}</h3>
|
||||
<p className="contact-section__connect-description">
|
||||
{connectDescription}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__details">
|
||||
<div className="contact-section__detail-item">
|
||||
<Mail className="contact-section__detail-icon" />
|
||||
<span className="contact-section__detail-text">{getObfuscatedEmail()}</span>
|
||||
</div>
|
||||
<div className="contact-section__detail-item">
|
||||
<Github className="contact-section__detail-icon" />
|
||||
<span className="contact-section__detail-text">{github}</span>
|
||||
</div>
|
||||
<div className="contact-section__detail-item">
|
||||
<Linkedin className="contact-section__detail-icon" />
|
||||
<span className="contact-section__detail-text">{personalConfig.social.linkedin.username}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__social">
|
||||
<button
|
||||
className="contact-section__social-button"
|
||||
onClick={() => window.open(personalConfig.social.github.url, '_blank')}
|
||||
aria-label="GitHub Profile"
|
||||
>
|
||||
<Github className="contact-section__social-icon" />
|
||||
</button>
|
||||
<button
|
||||
className="contact-section__social-button"
|
||||
onClick={() => window.open(personalConfig.social.linkedin.url, '_blank')}
|
||||
aria-label="LinkedIn Profile"
|
||||
>
|
||||
<Linkedin className="contact-section__social-icon" />
|
||||
</button>
|
||||
<button
|
||||
className="contact-section__social-button"
|
||||
onClick={handleEmailClick}
|
||||
aria-label="Send Email"
|
||||
>
|
||||
<Mail className="contact-section__social-icon" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Contact Form */}
|
||||
<div className="contact-section__form-card">
|
||||
<div className="contact-section__form-header">
|
||||
<h3 className="contact-section__form-title">Send a Message</h3>
|
||||
<p className="contact-section__form-description">
|
||||
I'll get back to you as soon as possible
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form className="contact-section__form" onSubmit={handleSubmit}>
|
||||
{/* Honeypot field - hidden from users but visible to bots */}
|
||||
<div className="contact-section__honeypot">
|
||||
<label htmlFor="website" className="contact-section__honeypot-label">
|
||||
Website (leave blank)
|
||||
</label>
|
||||
<input
|
||||
id="website"
|
||||
name="website"
|
||||
type="text"
|
||||
value={honeypot}
|
||||
onChange={(e) => setHoneypot(e.target.value)}
|
||||
className="contact-section__honeypot-input"
|
||||
tabIndex={-1}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__form-row">
|
||||
<div className="contact-section__form-group">
|
||||
<label htmlFor="firstName" className="contact-section__form-label">
|
||||
First Name *
|
||||
</label>
|
||||
<input
|
||||
id="firstName"
|
||||
name="firstName"
|
||||
type="text"
|
||||
placeholder="John"
|
||||
className="contact-section__form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="contact-section__form-group">
|
||||
<label htmlFor="lastName" className="contact-section__form-label">
|
||||
Last Name *
|
||||
</label>
|
||||
<input
|
||||
id="lastName"
|
||||
name="lastName"
|
||||
type="text"
|
||||
placeholder="Doe"
|
||||
className="contact-section__form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__form-group">
|
||||
<label htmlFor="email" className="contact-section__form-label">
|
||||
Email *
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="john@example.com"
|
||||
className="contact-section__form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__form-group">
|
||||
<label htmlFor="subject" className="contact-section__form-label">
|
||||
Subject
|
||||
</label>
|
||||
<input
|
||||
id="subject"
|
||||
name="subject"
|
||||
type="text"
|
||||
placeholder="Project Inquiry"
|
||||
className="contact-section__form-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="contact-section__form-group">
|
||||
<label htmlFor="message" className="contact-section__form-label">
|
||||
Message *
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Tell me about your project..."
|
||||
rows={5}
|
||||
className="contact-section__form-textarea"
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="contact-section__form-button"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Sending...' : 'Send Message'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ interface HeroSectionProps {
|
|||
|
||||
export default function HeroSection({
|
||||
title = "Web-Developer",
|
||||
description = "Creating responsive, cross-browser compatible web applications with Angular, React, and modern frontend technologies",
|
||||
description = "Creating responsive, cross-browser compatible web applications with Angular, React, and modern web technologies",
|
||||
primaryButtonText = "View my work",
|
||||
secondaryButtonText = "Download Resume",
|
||||
statItems = [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import { Github, ExternalLink } from 'lucide-react';
|
||||
import { personalConfig } from '../../config/personal';
|
||||
import type { Project } from '../../data/Project';
|
||||
|
||||
interface ProjectsSectionProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
projects?: Project[];
|
||||
}
|
||||
|
||||
export default function ProjectsSection({
|
||||
title = "Featured Projects",
|
||||
subtitle = "A showcase of my recent work and personal projects",
|
||||
projects = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Portfolio Website",
|
||||
description: "A personal portfolio website to showcase my skills, projects, and experience.",
|
||||
image: "",
|
||||
technologies: ["React", "TypeScript", "SCSS", "Vercel", "Github Copilot"],
|
||||
github: personalConfig.projects.portfolio,
|
||||
live: ""
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "ErgoVR",
|
||||
description: "A virtual reality application for analysis of motion sickness in VR environments.",
|
||||
image: "",
|
||||
technologies: ["Unity3D", "C#", "Oculus SDK"],
|
||||
github: personalConfig.projects.ergoVR
|
||||
}
|
||||
]
|
||||
}: ProjectsSectionProps) {
|
||||
return (
|
||||
<section id="projects" className="projects-section">
|
||||
<div className="projects-section__container">
|
||||
{/* Section Header */}
|
||||
<div className="projects-section__header">
|
||||
<h2 className="projects-section__title">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="projects-section__subtitle">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Projects Grid */}
|
||||
<div className="projects-section__grid">
|
||||
{projects.map((project) => (
|
||||
<div key={project.id} className="projects-section__card">
|
||||
<div className="projects-section__image-container">
|
||||
<img
|
||||
src={project.image || "/api/placeholder/400/200"}
|
||||
alt={project.title}
|
||||
className="projects-section__image"
|
||||
/>
|
||||
<div className="projects-section__overlay">
|
||||
<div className="projects-section__actions">
|
||||
{project.github && (
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="projects-section__action-button projects-section__action-button--secondary"
|
||||
>
|
||||
<Github className="projects-section__action-icon" />
|
||||
Code
|
||||
</a>
|
||||
)}
|
||||
{project.live && (
|
||||
<a
|
||||
href={project.live}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="projects-section__action-button projects-section__action-button--primary"
|
||||
>
|
||||
<ExternalLink className="projects-section__action-icon" />
|
||||
Live
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="projects-section__content">
|
||||
<div className="projects-section__header-content">
|
||||
<h3 className="projects-section__card-title">{project.title}</h3>
|
||||
<p className="projects-section__card-description">{project.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="projects-section__technologies">
|
||||
{project.technologies.map((tech) => (
|
||||
<span key={tech} className="projects-section__tech-badge">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import React from 'react';
|
||||
import type { Skill } from '../../data/Skill';
|
||||
|
||||
interface SkillCategory {
|
||||
title: string;
|
||||
skills: Skill[];
|
||||
}
|
||||
|
||||
interface SkillsSectionProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
skillCategories?: SkillCategory[];
|
||||
}
|
||||
|
||||
// Helper function to calculate skill level based on value
|
||||
const calculateSkillLevel = (value: number): string => {
|
||||
if (value >= 90) return "Expert";
|
||||
if (value >= 75) return "Advanced";
|
||||
if (value >= 50) return "Intermediate";
|
||||
return "Beginner";
|
||||
};
|
||||
|
||||
// Helper function to get color based on array position
|
||||
const getColorByPosition = (index: number): string => {
|
||||
const colors = ["primary", "secondary", "tertiary", "quaternary", "quinary", "senary"];
|
||||
return colors[index % colors.length];
|
||||
};
|
||||
|
||||
export default function SkillsSection({
|
||||
title = "Skills & Technologies",
|
||||
subtitle = "Technologies I work with to build amazing web experiences",
|
||||
skillCategories = [
|
||||
{
|
||||
title: "Web Frameworks",
|
||||
skills: [
|
||||
{ skill: "Angular", value: 90 },
|
||||
{ skill: "React", value: 70 },
|
||||
{ skill: "Next.js", value: 70 },
|
||||
{ skill: "TypeScript", value: 95 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Styling & Design",
|
||||
skills: [
|
||||
{ skill: "SCSS/Sass", value: 95 },
|
||||
{ skill: "CSS3", value: 90 },
|
||||
{ skill: "Accessibility (a11y) Standards", value: 45 },
|
||||
{ skill: "Bootstrap", value: 65 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Backend Development",
|
||||
skills: [
|
||||
{ skill: "Node.js", value: 65 },
|
||||
{ skill: "Express.js", value: 25 },
|
||||
{ skill: "REST APIs", value: 70 },
|
||||
{ skill: "MongoDB", value: 50 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Development Tools",
|
||||
skills: [
|
||||
{ skill: "Git & GitHub", value: 90 },
|
||||
{ skill: "Visual Studio Code", value: 75 },
|
||||
{ skill: "Vite", value: 80 },
|
||||
{ skill: "NPM", value: 85 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Testing & Quality",
|
||||
skills: [
|
||||
{ skill: "Cypress", value: 85 },
|
||||
{ skill: "Jasmine/Karma", value: 75 },
|
||||
{ skill: "ESLint", value: 90 },
|
||||
{ skill: "Prettier", value: 95 }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "AI-Tools",
|
||||
skills: [
|
||||
{ skill: "GitHub Copilot", value: 85 },
|
||||
{ skill: "Prompt Engineering", value: 45 },
|
||||
{ skill: "Prototyping / MVP-Building with AI", value: 45 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}: SkillsSectionProps) {
|
||||
return (
|
||||
<section id="skills" className="skills-section">
|
||||
<div className="skills-section__container">
|
||||
<div className="skills-section__header">
|
||||
<h2 className="skills-section__title">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="skills-section__subtitle">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="skills-section__grid">
|
||||
{skillCategories.map((category, categoryIndex) => (
|
||||
<div key={categoryIndex} className="skills-section__category">
|
||||
<h3 className="skills-section__category-title">
|
||||
{category.title}
|
||||
</h3>
|
||||
<div className="skills-section__skills">
|
||||
{category.skills.map((skillItem, skillIndex) => {
|
||||
// Use provided level or calculate from value
|
||||
const displayLevel = skillItem.level || calculateSkillLevel(skillItem.value);
|
||||
// Use provided color or get color based on position in array
|
||||
const colorClass = skillItem.color || getColorByPosition(skillIndex);
|
||||
|
||||
return (
|
||||
<div key={skillIndex} className="skills-section__skill">
|
||||
<div className="skills-section__skill-header">
|
||||
<span className="skills-section__skill-name">
|
||||
{skillItem.skill}
|
||||
</span>
|
||||
<div className="skills-section__skill-info">
|
||||
<span className="skills-section__skill-level">
|
||||
{displayLevel}
|
||||
</span>
|
||||
<span className="skills-section__skill-percentage">
|
||||
{skillItem.value}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="skills-section__progress">
|
||||
<div
|
||||
className={`skills-section__progress-bar skills-section__progress-bar--${colorClass}`}
|
||||
style={{ width: `${skillItem.value}%` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
export const personalConfig = {
|
||||
// Basic Information
|
||||
name: "Sascha Bach",
|
||||
title: "Freelance Software Developer",
|
||||
|
||||
// Contact Information
|
||||
email: {
|
||||
user: "freelancer",
|
||||
domain: "sascha-bach.de",
|
||||
full: "freelancer@sascha-bach.de"
|
||||
},
|
||||
|
||||
// Social Links
|
||||
social: {
|
||||
github: {
|
||||
url: "https://github.com/LuciusShadow",
|
||||
username: "LuciusShadow"
|
||||
},
|
||||
linkedin: {
|
||||
url: "https://www.linkedin.com/in/saschabach/",
|
||||
username: "saschabach"
|
||||
}
|
||||
},
|
||||
|
||||
// Project Repository
|
||||
projects: {
|
||||
portfolio: "https://github.com/LuciusShadow/portfolio-page",
|
||||
ergoVR: "https://github.com/LuciusShadow/ErgoVR"
|
||||
},
|
||||
|
||||
// Professional Details
|
||||
location: "Germany",
|
||||
timezone: "CET/CEST",
|
||||
|
||||
// Branding
|
||||
brand: {
|
||||
tagline: "Building digital solutions with passion and precision",
|
||||
description: "Freelance software developer specializing in modern web technologies and user experience."
|
||||
}
|
||||
} as const;
|
||||
|
||||
// Helper functions
|
||||
export const getObfuscatedEmail = () => {
|
||||
return personalConfig.email.full.replace('@', ' [at] ').replace('.', ' [dot] ');
|
||||
};
|
||||
|
||||
export const createEmailLink = () => {
|
||||
return `mailto:${personalConfig.email.user}@${personalConfig.email.domain}`;
|
||||
};
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
export interface ServiceCard {
|
||||
icon: LucideIcon;
|
||||
title: string;
|
||||
description: string;
|
||||
colorClass: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'quinary' | 'senary';
|
||||
}
|
||||
|
|
@ -5,6 +5,6 @@ export interface Project {
|
|||
description: string;
|
||||
image: string;
|
||||
technologies: string[];
|
||||
github: string;
|
||||
live: string;
|
||||
github?: string;
|
||||
live?: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export interface Skill {
|
||||
skill: string;
|
||||
level?: string; // Make level optional
|
||||
value: number;
|
||||
color?: string;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export interface Skill {
|
||||
skill: string;
|
||||
level: string;
|
||||
value: number;
|
||||
color: string;
|
||||
}
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
@use 'themes' as t;
|
||||
@use 'themes/index' as t;
|
||||
@use 'variables';
|
||||
@use 'mixins';
|
||||
@use 'globals';
|
||||
@use 'topbar';
|
||||
@use 'hero-section';
|
||||
@use 'about-section';
|
||||
@use 'services-section';
|
||||
|
||||
// Layout components (topbar, footer, etc.)
|
||||
@use 'layout';
|
||||
|
||||
// All sections imported through index
|
||||
@use 'sections';
|
||||
|
||||
// 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');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
// Layout component imports - for structural components
|
||||
@forward 'topbar';
|
||||
@forward 'footer';
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
@use '../mixins';
|
||||
@use '../variables';
|
||||
|
||||
.footer {
|
||||
background-color: var(--color-background-muted);
|
||||
margin-top: auto; // Push footer to bottom if using flexbox layout
|
||||
|
||||
&__container {
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
&__separator {
|
||||
height: 1px;
|
||||
background-color: var(--color-border);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
}
|
||||
|
||||
&__copyright {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.875rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__social {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
&__social-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-hover);
|
||||
color: var(--color-text);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid var(--color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
&__social-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@use 'globals';
|
||||
@use '../globals';
|
||||
|
||||
.navbar {
|
||||
@include globals.flex-center();
|
||||
|
|
@ -106,21 +106,3 @@
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
@extend %button-reset;
|
||||
display: flex;
|
||||
align-items: center; // Centers vertically
|
||||
justify-content: center; // Centers horizontally
|
||||
font-size: 1rem;
|
||||
transition: transform 0.2s ease;
|
||||
width: 20px;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-theme {
|
||||
width: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// Section imports - organized and easy to manage
|
||||
@forward 'hero-section';
|
||||
@forward 'about-section';
|
||||
@forward 'services-section';
|
||||
@forward 'skills-section';
|
||||
@forward 'certifications-section';
|
||||
@forward 'projects-section';
|
||||
@forward 'contact-section';
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@use 'globals';
|
||||
@use '../globals';
|
||||
|
||||
.about-section {
|
||||
background: var(--about-background);
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
@use '../globals';
|
||||
|
||||
.certifications-section {
|
||||
background: var(--certifications-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-certifications-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;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 2rem;
|
||||
|
||||
@include globals.mobile-only {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@include globals.desktop-only {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
&__card {
|
||||
text-align: center;
|
||||
border: none;
|
||||
border-radius: 1rem;
|
||||
box-shadow: var(--certifications-card-shadow);
|
||||
background: var(--certifications-card-background);
|
||||
padding: 2rem 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--certifications-card-overlay);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-0.25rem);
|
||||
box-shadow: var(--certifications-card-shadow-hover);
|
||||
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__card-header {
|
||||
margin-bottom: 1.5rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__card-icon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
margin: 0 auto 1rem;
|
||||
color: var(--color-certifications-primary);
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__card-icon-image {
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
object-fit: contain;
|
||||
margin: 0 auto 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&__card-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
&__card-issuer {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&__card-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__card-badge {
|
||||
display: inline-block;
|
||||
background: var(--certifications-badge-background);
|
||||
color: var(--color-certifications-badge-text);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
border: 1px solid var(--certifications-badge-border);
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@include globals.mobile-only {
|
||||
&__card {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
&__card-icon,
|
||||
&__card-icon-image {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
&__card-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
@use '../globals';
|
||||
|
||||
.contact-section {
|
||||
background: var(--contact-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-contact-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;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3rem;
|
||||
|
||||
@include globals.desktop-only {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
// Contact Info Section
|
||||
&__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
&__connect-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
&__connect-description {
|
||||
color: var(--color-text-muted);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
&__details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
&__detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
&__detail-icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
color: var(--color-contact-primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__detail-text {
|
||||
color: var(--color-text);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&__social {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
&__social-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border: 1px solid var(--contact-social-border);
|
||||
border-radius: 0.375rem;
|
||||
background: var(--contact-social-bg);
|
||||
color: var(--contact-social-text);
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: var(--contact-social-hover-bg);
|
||||
border-color: var(--contact-social-hover-border);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
&__social-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
// Contact Form Section
|
||||
&__form-card {
|
||||
background: var(--contact-form-bg);
|
||||
border: 1px solid var(--contact-form-border);
|
||||
border-radius: 1rem;
|
||||
box-shadow: var(--contact-form-shadow);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__form-header {
|
||||
padding: 1.5rem 1.5rem 0;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
&__form-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
&__form-description {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
&__form {
|
||||
padding: 0 1.5rem 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
// Honeypot field - hidden from users but visible to bots
|
||||
&__honeypot {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__honeypot-label,
|
||||
&__honeypot-input {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
|
||||
@include globals.mobile-only {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
&__form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
&__form-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
&__form-input,
|
||||
&__form-textarea {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--contact-input-border);
|
||||
border-radius: 0.375rem;
|
||||
background: var(--contact-input-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-contact-primary);
|
||||
box-shadow: 0 0 0 3px var(--contact-input-focus-ring);
|
||||
}
|
||||
}
|
||||
|
||||
&__form-textarea {
|
||||
resize: vertical;
|
||||
min-height: 5rem;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
&__form-button {
|
||||
background: var(--contact-button-bg);
|
||||
color: var(--contact-button-text);
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--contact-button-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active:not(:disabled) {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@include globals.mobile-only {
|
||||
&__grid {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
&__form-card {
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
&__form-header,
|
||||
&__form {
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
&__social {
|
||||
justify-content: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
@use 'globals';
|
||||
@use 'variables' as *;
|
||||
@use 'mixins' as *;
|
||||
@use '../globals';
|
||||
@use '../variables' as *;
|
||||
@use '../mixins' as *;
|
||||
|
||||
// Hero-specific button extensions
|
||||
%hero-button-base {
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
@use '../globals';
|
||||
|
||||
.projects-section {
|
||||
background: var(--projects-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-projects-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;
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 2rem;
|
||||
|
||||
@include globals.mobile-only {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@include globals.desktop-only {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
&__card {
|
||||
background: var(--projects-card-background);
|
||||
border-radius: 1rem;
|
||||
box-shadow: var(--projects-card-shadow);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid var(--projects-card-border);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-0.25rem);
|
||||
box-shadow: var(--projects-card-shadow-hover);
|
||||
|
||||
.projects-section__image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.projects-section__overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__image-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 12rem;
|
||||
}
|
||||
|
||||
&__image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
&__overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--projects-overlay-background);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
&__action-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
|
||||
&--primary {
|
||||
background: var(--projects-button-primary-bg);
|
||||
color: var(--projects-button-primary-text);
|
||||
border-color: var(--projects-button-primary-border);
|
||||
|
||||
&:hover {
|
||||
background: var(--projects-button-primary-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: var(--projects-button-secondary-bg);
|
||||
color: var(--projects-button-secondary-text);
|
||||
border-color: var(--projects-button-secondary-border);
|
||||
|
||||
&:hover {
|
||||
background: var(--projects-button-secondary-hover-bg);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__action-icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
&__header-content {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
&__card-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
&__card-description {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
&__technologies {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
&__tech-badge {
|
||||
display: inline-block;
|
||||
background: var(--projects-tech-badge-bg);
|
||||
color: var(--projects-tech-badge-text);
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
border: 1px solid var(--projects-tech-badge-border);
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@include globals.mobile-only {
|
||||
&__card {
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
&__image-container {
|
||||
height: 10rem;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
&__action-button {
|
||||
justify-content: center;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
@use 'globals';
|
||||
@use 'variables' as *;
|
||||
@use '../globals';
|
||||
@use '../variables' as *;
|
||||
|
||||
.services-section {
|
||||
min-height: 100vh;
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
@use '../globals';
|
||||
@use '../variables' as *;
|
||||
|
||||
.skills-section {
|
||||
padding: 5rem 0;
|
||||
background: var(--skills-background);
|
||||
position: relative;
|
||||
|
||||
// Add decorative background elements
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--skills-background-pattern);
|
||||
opacity: 0.03;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&__container {
|
||||
max-width: 80rem;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
@include globals.desktop-only {
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
text-align: center;
|
||||
margin-bottom: $skills-header-margin;
|
||||
|
||||
@include globals.mobile-only {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 1.875rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
background: var(--gradient-skills-title);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
@include globals.desktop-only {
|
||||
font-size: 2.25rem;
|
||||
}
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--color-text-muted);
|
||||
max-width: 40rem;
|
||||
margin: 0 auto;
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: $skills-category-gap;
|
||||
|
||||
@include globals.mobile-only {
|
||||
grid-template-columns: 1fr;
|
||||
gap: $skills-category-gap-mobile;
|
||||
}
|
||||
}
|
||||
|
||||
&__category {
|
||||
background: var(--skills-category-bg);
|
||||
border: 1px solid var(--skills-category-border);
|
||||
border-radius: 1rem;
|
||||
padding: $skills-category-padding;
|
||||
backdrop-filter: blur(10px);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
|
||||
// Add subtle gradient overlay for depth
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--skills-category-gradient-overlay);
|
||||
opacity: 0.8;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Add decorative corner accent
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: var(--skills-category-accent);
|
||||
clip-path: polygon(100% 0, 0 0, 100% 100%);
|
||||
opacity: 0.1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
|
||||
&::before {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&::after {
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@include globals.mobile-only {
|
||||
padding: $skills-category-padding-mobile;
|
||||
}
|
||||
|
||||
// Card-specific color schemes matching your page theme
|
||||
&:nth-child(1) {
|
||||
// Web Frameworks - Blue theme (matching hero primary)
|
||||
background: var(--skills-category-bg-primary);
|
||||
border-color: var(--skills-category-border-primary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-primary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
// Styling & Design - Green theme (matching about section)
|
||||
background: var(--skills-category-bg-secondary);
|
||||
border-color: var(--skills-category-border-secondary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-secondary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
// Backend Development - Purple theme (matching hero secondary)
|
||||
background: var(--skills-category-bg-tertiary);
|
||||
border-color: var(--skills-category-border-tertiary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-tertiary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
// Development Tools - Teal theme (complementary)
|
||||
background: var(--skills-category-bg-quaternary);
|
||||
border-color: var(--skills-category-border-quaternary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-quaternary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
// Testing & Quality - Orange theme (warm accent)
|
||||
background: var(--skills-category-bg-quinary);
|
||||
border-color: var(--skills-category-border-quinary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-quinary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-quinary);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(6) {
|
||||
// AI-Tools - Indigo theme (tech/AI vibe)
|
||||
background: var(--skills-category-bg-senary);
|
||||
border-color: var(--skills-category-border-senary);
|
||||
|
||||
&::before {
|
||||
background: var(--skills-gradient-senary);
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: var(--skills-accent-senary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__category-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--skills-category-title-color);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__skills {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $skills-skill-gap;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
@include globals.mobile-only {
|
||||
gap: $skills-skill-gap-mobile;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill {
|
||||
// Individual skill container
|
||||
}
|
||||
|
||||
&__skill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
gap: 1rem;
|
||||
|
||||
@include globals.mobile-only {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--skills-skill-name-color);
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
|
||||
@include globals.mobile-only {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill-level {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--skills-skill-level-color);
|
||||
background: var(--skills-progress-bg);
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
backdrop-filter: blur(4px);
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill-percentage {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--skills-skill-percentage-color);
|
||||
|
||||
@include globals.mobile-only {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__progress {
|
||||
height: $skills-progress-height;
|
||||
background: var(--skills-progress-bg);
|
||||
border-radius: $skills-progress-radius;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
&__progress-bar {
|
||||
height: 100%;
|
||||
border-radius: $skills-progress-radius;
|
||||
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||||
transform: translateX(-100%);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
// Semantic hierarchy classes with enhanced gradients
|
||||
&--primary {
|
||||
background: var(--skills-progress-primary);
|
||||
box-shadow: 0 0 8px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: var(--skills-progress-secondary);
|
||||
box-shadow: 0 0 8px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
&--tertiary {
|
||||
background: var(--skills-progress-tertiary);
|
||||
box-shadow: 0 0 8px rgba(139, 92, 246, 0.3);
|
||||
}
|
||||
|
||||
&--quaternary {
|
||||
background: var(--skills-progress-quaternary);
|
||||
box-shadow: 0 0 8px rgba(6, 182, 212, 0.3);
|
||||
}
|
||||
|
||||
&--quinary {
|
||||
background: var(--skills-progress-quinary);
|
||||
box-shadow: 0 0 8px rgba(245, 158, 11, 0.3);
|
||||
}
|
||||
|
||||
&--senary {
|
||||
background: var(--skills-progress-senary);
|
||||
box-shadow: 0 0 8px rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced shimmer animation
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile responsiveness
|
||||
@include globals.mobile-only {
|
||||
.skills-section {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
@use 'sass:map';
|
||||
@use 'variables' as *;
|
||||
|
||||
// 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 using visual hierarchy
|
||||
stat-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
|
||||
stat-primary-value: #2563eb,
|
||||
stat-primary-label: #1d4ed8,
|
||||
|
||||
stat-secondary-bg: linear-gradient(135deg, #dcfce7, #bbf7d0),
|
||||
stat-secondary-value: #16a34a,
|
||||
stat-secondary-label: #15803d,
|
||||
|
||||
stat-tertiary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
|
||||
stat-tertiary-value: #0d9488,
|
||||
stat-tertiary-label: #0f766e,
|
||||
|
||||
stat-quaternary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
|
||||
stat-quaternary-value: #9333ea,
|
||||
stat-quaternary-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 using visual hierarchy
|
||||
skill-badge-primary-bg: #dbeafe,
|
||||
skill-badge-primary-text: #1e40af,
|
||||
skill-badge-secondary-bg: #dcfce7,
|
||||
skill-badge-secondary-text: #166534,
|
||||
skill-badge-tertiary-bg: #e9d5ff,
|
||||
skill-badge-tertiary-text: #7c2d12,
|
||||
skill-badge-quaternary-bg: #fed7aa,
|
||||
skill-badge-quaternary-text: #9a3412,
|
||||
|
||||
// Feature card colors using visual hierarchy
|
||||
feature-card-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
|
||||
feature-card-secondary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
|
||||
feature-card-tertiary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
|
||||
feature-icon-primary: #2563eb,
|
||||
feature-icon-secondary: #9333ea,
|
||||
feature-icon-tertiary: #0d9488,
|
||||
|
||||
feature-title-primary: #1e40af,
|
||||
feature-title-secondary: #7c3aed,
|
||||
feature-title-tertiary: #0f766e,
|
||||
|
||||
feature-description-primary: #1d4ed8,
|
||||
feature-description-secondary: #6d28d9,
|
||||
feature-description-tertiary: #115e59,
|
||||
|
||||
// Services section colors - Tailwind inspired
|
||||
services-background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 25%, #cbd5e1 70%, #94a3b8 100%),
|
||||
gradient-services-title: linear-gradient(90deg, #0f172a, #1e293b, #334155),
|
||||
|
||||
// Service card colors using Tailwind color palette
|
||||
service-card-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe), // blue-100 to blue-200
|
||||
service-card-secondary-bg: linear-gradient(135deg, #dcfce7, #bbf7d0), // green-100 to green-200
|
||||
service-card-tertiary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe), // violet-100 to violet-200
|
||||
service-card-quaternary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4), // teal-100 to teal-200
|
||||
service-card-quinary-bg: linear-gradient(135deg, #fed7aa, #fdba74), // orange-200 to orange-300
|
||||
service-card-senary-bg: linear-gradient(135deg, #fecaca, #fca5a5), // red-200 to red-300
|
||||
|
||||
service-icon-primary: #2563eb, // blue-600
|
||||
service-icon-secondary: #16a34a, // green-600
|
||||
service-icon-tertiary: #7c3aed, // violet-600
|
||||
service-icon-quaternary: #0d9488, // teal-600
|
||||
service-icon-quinary: #ea580c, // orange-600
|
||||
service-icon-senary: #dc2626, // red-600
|
||||
|
||||
service-title-primary: #1e40af, // blue-700
|
||||
service-title-secondary: #166534, // green-700
|
||||
service-title-tertiary: #6d28d9, // violet-700
|
||||
service-title-quaternary: #0f766e, // teal-700
|
||||
service-title-quinary: #c2410c, // orange-700
|
||||
service-title-senary: #b91c1c, // red-700
|
||||
|
||||
service-description-primary: #1d4ed8, // blue-700
|
||||
service-description-secondary: #15803d, // green-700
|
||||
service-description-tertiary: #5b21b6, // violet-800
|
||||
service-description-quaternary: #115e59, // teal-800
|
||||
service-description-quinary: #9a3412, // orange-800
|
||||
service-description-senary: #991b1b, // red-800
|
||||
|
||||
// Shadow variables
|
||||
box-shadow-sm: #{$shadow-sm},
|
||||
box-shadow-md: #{$shadow-md},
|
||||
box-shadow-lg: #{$shadow-lg},
|
||||
);
|
||||
|
||||
// Dark Theme Colors
|
||||
$dark-theme: (
|
||||
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 using visual hierarchy
|
||||
stat-primary-bg:
|
||||
linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
|
||||
stat-primary-value: #2563eb,
|
||||
stat-primary-label: #93c5fd,
|
||||
|
||||
stat-secondary-bg:
|
||||
linear-gradient(135deg, rgba(20, 83, 45, 0.3), rgba(22, 101, 52, 0.3)),
|
||||
stat-secondary-value: #16a34a,
|
||||
stat-secondary-label: #86efac,
|
||||
|
||||
stat-tertiary-bg:
|
||||
linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
|
||||
stat-tertiary-value: #0d9488,
|
||||
stat-tertiary-label: #5eead4,
|
||||
|
||||
stat-quaternary-bg:
|
||||
linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
|
||||
stat-quaternary-value: #9333ea,
|
||||
stat-quaternary-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 using visual hierarchy
|
||||
skill-badge-primary-bg: rgba(30, 58, 138, 0.3),
|
||||
skill-badge-primary-text: #93c5fd,
|
||||
skill-badge-secondary-bg: rgba(20, 83, 45, 0.3),
|
||||
skill-badge-secondary-text: #86efac,
|
||||
skill-badge-tertiary-bg: rgba(88, 28, 135, 0.3),
|
||||
skill-badge-tertiary-text: #c4b5fd,
|
||||
skill-badge-quaternary-bg: rgba(154, 52, 18, 0.3),
|
||||
skill-badge-quaternary-text: #fdba74,
|
||||
|
||||
// Feature card colors using visual hierarchy
|
||||
feature-card-primary-bg:
|
||||
linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
|
||||
feature-card-secondary-bg:
|
||||
linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
|
||||
feature-card-tertiary-bg:
|
||||
linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
|
||||
feature-icon-primary: #3b82f6,
|
||||
feature-icon-secondary: #a855f7,
|
||||
feature-icon-tertiary: #14b8a6,
|
||||
|
||||
feature-title-primary: #93c5fd,
|
||||
feature-title-secondary: #c4b5fd,
|
||||
feature-title-tertiary: #5eead4,
|
||||
|
||||
feature-description-primary: #dbeafe,
|
||||
feature-description-secondary: #e9d5ff,
|
||||
feature-description-tertiary: #ccfbf1,
|
||||
|
||||
// Services section colors - Dark mode with Tailwind palette
|
||||
services-background: linear-gradient(135deg, #0f172a 0%, #1e293b 25%, #334155 70%, #475569 100%),
|
||||
gradient-services-title: linear-gradient(90deg, #f1f5f9, #e2e8f0, #cbd5e1),
|
||||
|
||||
// Service card colors for dark theme - using Tailwind dark colors
|
||||
service-card-primary-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(37, 99, 235, 0.2)), // blue-800/30 to blue-600/20
|
||||
service-card-secondary-bg: linear-gradient(135deg, rgba(22, 101, 52, 0.3), rgba(34, 197, 94, 0.2)), // green-800/30 to green-500/20
|
||||
service-card-tertiary-bg: linear-gradient(135deg, rgba(91, 33, 182, 0.3), rgba(124, 58, 237, 0.2)), // violet-800/30 to violet-600/20
|
||||
service-card-quaternary-bg: linear-gradient(135deg, rgba(17, 94, 89, 0.3), rgba(20, 184, 166, 0.2)), // teal-800/30 to teal-500/20
|
||||
service-card-quinary-bg: linear-gradient(135deg, rgba(154, 52, 18, 0.3), rgba(249, 115, 22, 0.2)), // orange-800/30 to orange-500/20
|
||||
service-card-senary-bg: linear-gradient(135deg, rgba(153, 27, 27, 0.3), rgba(239, 68, 68, 0.2)), // red-800/30 to red-500/20
|
||||
|
||||
service-icon-primary: #60a5fa, // blue-400
|
||||
service-icon-secondary: #4ade80, // green-400
|
||||
service-icon-tertiary: #a78bfa, // violet-400
|
||||
service-icon-quaternary: #2dd4bf, // teal-400
|
||||
service-icon-quinary: #fb923c, // orange-400
|
||||
service-icon-senary: #f87171, // red-400
|
||||
|
||||
service-title-primary: #93c5fd, // blue-300
|
||||
service-title-secondary: #86efac, // green-300
|
||||
service-title-tertiary: #c4b5fd, // violet-300
|
||||
service-title-quaternary: #5eead4, // teal-300
|
||||
service-title-quinary: #fdba74, // orange-300
|
||||
service-title-senary: #fca5a5, // red-300
|
||||
|
||||
service-description-primary: #bfdbfe, // blue-200
|
||||
service-description-secondary: #bbf7d0, // green-200
|
||||
service-description-tertiary: #ddd6fe, // violet-200
|
||||
service-description-quaternary: #99f6e4, // teal-200
|
||||
service-description-quinary: #fed7aa, // orange-200
|
||||
service-description-senary: #fecaca, // red-200
|
||||
|
||||
// Shadow variables
|
||||
box-shadow-sm: #{$shadow-sm},
|
||||
box-shadow-md: #{$shadow-md},
|
||||
box-shadow-lg: #{$shadow-lg},
|
||||
);
|
||||
|
||||
// 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 using semantic naming
|
||||
--stat-primary-bg: #{map.get($theme, stat-primary-bg)};
|
||||
--stat-primary-value: #{map.get($theme, stat-primary-value)};
|
||||
--stat-primary-label: #{map.get($theme, stat-primary-label)};
|
||||
|
||||
--stat-secondary-bg: #{map.get($theme, stat-secondary-bg)};
|
||||
--stat-secondary-value: #{map.get($theme, stat-secondary-value)};
|
||||
--stat-secondary-label: #{map.get($theme, stat-secondary-label)};
|
||||
|
||||
--stat-tertiary-bg: #{map.get($theme, stat-tertiary-bg)};
|
||||
--stat-tertiary-value: #{map.get($theme, stat-tertiary-value)};
|
||||
--stat-tertiary-label: #{map.get($theme, stat-tertiary-label)};
|
||||
|
||||
--stat-quaternary-bg: #{map.get($theme, stat-quaternary-bg)};
|
||||
--stat-quaternary-value: #{map.get($theme, stat-quaternary-value)};
|
||||
--stat-quaternary-label: #{map.get($theme, stat-quaternary-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 using semantic naming
|
||||
--skill-badge-primary-bg: #{map.get($theme, skill-badge-primary-bg)};
|
||||
--skill-badge-primary-text: #{map.get($theme, skill-badge-primary-text)};
|
||||
--skill-badge-secondary-bg: #{map.get($theme, skill-badge-secondary-bg)};
|
||||
--skill-badge-secondary-text: #{map.get($theme, skill-badge-secondary-text)};
|
||||
--skill-badge-tertiary-bg: #{map.get($theme, skill-badge-tertiary-bg)};
|
||||
--skill-badge-tertiary-text: #{map.get($theme, skill-badge-tertiary-text)};
|
||||
--skill-badge-quaternary-bg: #{map.get($theme, skill-badge-quaternary-bg)};
|
||||
--skill-badge-quaternary-text: #{map.get($theme, skill-badge-quaternary-text)};
|
||||
|
||||
// Feature card variables using semantic naming
|
||||
--feature-card-primary-bg: #{map.get($theme, feature-card-primary-bg)};
|
||||
--feature-card-secondary-bg: #{map.get($theme, feature-card-secondary-bg)};
|
||||
--feature-card-tertiary-bg: #{map.get($theme, feature-card-tertiary-bg)};
|
||||
|
||||
--feature-icon-primary: #{map.get($theme, feature-icon-primary)};
|
||||
--feature-icon-secondary: #{map.get($theme, feature-icon-secondary)};
|
||||
--feature-icon-tertiary: #{map.get($theme, feature-icon-tertiary)};
|
||||
|
||||
--feature-title-primary: #{map.get($theme, feature-title-primary)};
|
||||
--feature-title-secondary: #{map.get($theme, feature-title-secondary)};
|
||||
--feature-title-tertiary: #{map.get($theme, feature-title-tertiary)};
|
||||
|
||||
--feature-description-primary: #{map.get($theme, feature-description-primary)};
|
||||
--feature-description-secondary: #{map.get(
|
||||
$theme,
|
||||
feature-description-secondary
|
||||
)};
|
||||
--feature-description-tertiary: #{map.get(
|
||||
$theme,
|
||||
feature-description-tertiary
|
||||
)};
|
||||
|
||||
// Services section variables
|
||||
--services-background: #{map.get($theme, services-background)};
|
||||
--gradient-services-title: #{map.get($theme, gradient-services-title)};
|
||||
|
||||
// Service card variables
|
||||
--service-card-primary-bg: #{map.get($theme, service-card-primary-bg)};
|
||||
--service-card-secondary-bg: #{map.get($theme, service-card-secondary-bg)};
|
||||
--service-card-tertiary-bg: #{map.get($theme, service-card-tertiary-bg)};
|
||||
--service-card-quaternary-bg: #{map.get($theme, service-card-quaternary-bg)};
|
||||
--service-card-quinary-bg: #{map.get($theme, service-card-quinary-bg)};
|
||||
--service-card-senary-bg: #{map.get($theme, service-card-senary-bg)};
|
||||
|
||||
--service-icon-primary: #{map.get($theme, service-icon-primary)};
|
||||
--service-icon-secondary: #{map.get($theme, service-icon-secondary)};
|
||||
--service-icon-tertiary: #{map.get($theme, service-icon-tertiary)};
|
||||
--service-icon-quaternary: #{map.get($theme, service-icon-quaternary)};
|
||||
--service-icon-quinary: #{map.get($theme, service-icon-quinary)};
|
||||
--service-icon-senary: #{map.get($theme, service-icon-senary)};
|
||||
|
||||
--service-title-primary: #{map.get($theme, service-title-primary)};
|
||||
--service-title-secondary: #{map.get($theme, service-title-secondary)};
|
||||
--service-title-tertiary: #{map.get($theme, service-title-tertiary)};
|
||||
--service-title-quaternary: #{map.get($theme, service-title-quaternary)};
|
||||
--service-title-quinary: #{map.get($theme, service-title-quinary)};
|
||||
--service-title-senary: #{map.get($theme, service-title-senary)};
|
||||
|
||||
--service-description-primary: #{map.get($theme, service-description-primary)};
|
||||
--service-description-secondary: #{map.get($theme, service-description-secondary)};
|
||||
--service-description-tertiary: #{map.get($theme, service-description-tertiary)};
|
||||
--service-description-quaternary: #{map.get($theme, service-description-quaternary)};
|
||||
--service-description-quinary: #{map.get($theme, service-description-quinary)};
|
||||
--service-description-senary: #{map.get($theme, service-description-senary)};
|
||||
|
||||
// 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)};
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
$about-light-theme: (
|
||||
// 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 using visual hierarchy
|
||||
skill-badge-primary-bg: #dbeafe,
|
||||
skill-badge-primary-text: #1e40af,
|
||||
skill-badge-secondary-bg: #dcfce7,
|
||||
skill-badge-secondary-text: #166534,
|
||||
skill-badge-tertiary-bg: #e9d5ff,
|
||||
skill-badge-tertiary-text: #7c2d12,
|
||||
skill-badge-quaternary-bg: #fed7aa,
|
||||
skill-badge-quaternary-text: #9a3412,
|
||||
|
||||
// Feature card colors using visual hierarchy
|
||||
feature-card-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
|
||||
feature-card-secondary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
|
||||
feature-card-tertiary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
|
||||
feature-icon-primary: #2563eb,
|
||||
feature-icon-secondary: #9333ea,
|
||||
feature-icon-tertiary: #0d9488,
|
||||
|
||||
feature-title-primary: #1e40af,
|
||||
feature-title-secondary: #7c3aed,
|
||||
feature-title-tertiary: #0f766e,
|
||||
|
||||
feature-description-primary: #1d4ed8,
|
||||
feature-description-secondary: #6d28d9,
|
||||
feature-description-tertiary: #115e59,
|
||||
);
|
||||
|
||||
$about-dark-theme: (
|
||||
// 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 using visual hierarchy
|
||||
skill-badge-primary-bg: rgba(30, 58, 138, 0.3),
|
||||
skill-badge-primary-text: #93c5fd,
|
||||
skill-badge-secondary-bg: rgba(20, 83, 45, 0.3),
|
||||
skill-badge-secondary-text: #86efac,
|
||||
skill-badge-tertiary-bg: rgba(88, 28, 135, 0.3),
|
||||
skill-badge-tertiary-text: #c4b5fd,
|
||||
skill-badge-quaternary-bg: rgba(154, 52, 18, 0.3),
|
||||
skill-badge-quaternary-text: #fdba74,
|
||||
|
||||
// Feature card colors using visual hierarchy
|
||||
feature-card-primary-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
|
||||
feature-card-secondary-bg: linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
|
||||
feature-card-tertiary-bg: linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
|
||||
feature-icon-primary: #3b82f6,
|
||||
feature-icon-secondary: #a855f7,
|
||||
feature-icon-tertiary: #14b8a6,
|
||||
|
||||
feature-title-primary: #93c5fd,
|
||||
feature-title-secondary: #c4b5fd,
|
||||
feature-title-tertiary: #5eead4,
|
||||
|
||||
feature-description-primary: #dbeafe,
|
||||
feature-description-secondary: #e9d5ff,
|
||||
feature-description-tertiary: #ccfbf1,
|
||||
);
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
@use 'sass:map';
|
||||
@use '../variables' as *;
|
||||
|
||||
// Base colors and variables that are used across sections
|
||||
$base-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),
|
||||
gradient-primary: $gradient-primary,
|
||||
gradient-text: $gradient-text,
|
||||
|
||||
// Shadow variables
|
||||
box-shadow-sm: #{$shadow-sm},
|
||||
box-shadow-md: #{$shadow-md},
|
||||
box-shadow-lg: #{$shadow-lg},
|
||||
);
|
||||
|
||||
$base-dark-theme: (
|
||||
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),
|
||||
gradient-primary: $gradient-primary,
|
||||
gradient-text: $gradient-text,
|
||||
|
||||
// Shadow variables
|
||||
box-shadow-sm: #{$shadow-sm},
|
||||
box-shadow-md: #{$shadow-md},
|
||||
box-shadow-lg: #{$shadow-lg},
|
||||
);
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// Certifications section theme definitions
|
||||
|
||||
$certifications-light-theme: (
|
||||
// Certifications section background
|
||||
'certifications-background': linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%),
|
||||
|
||||
// Certifications title gradient (purple to pink)
|
||||
'gradient-certifications-title': linear-gradient(to right, #9333ea, #ec4899),
|
||||
|
||||
// Primary color for icons (purple)
|
||||
'color-certifications-primary': #9333ea,
|
||||
|
||||
// Card styling
|
||||
'certifications-card-background': linear-gradient(135deg, #ffffff 0%, #fafafa 100%),
|
||||
'certifications-card-shadow': 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
'certifications-card-shadow-hover': 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||
'certifications-card-overlay': linear-gradient(135deg, rgba(147, 51, 234, 0.02) 0%, rgba(236, 72, 153, 0.02) 100%),
|
||||
|
||||
// Badge styling (purple/pink theme)
|
||||
'certifications-badge-background': linear-gradient(to right, #f3e8ff, #fce7f3),
|
||||
'certifications-badge-border': rgba(147, 51, 234, 0.2),
|
||||
'color-certifications-badge-text': #7e22ce,
|
||||
);
|
||||
|
||||
$certifications-dark-theme: (
|
||||
// Certifications section background (dark purple/pink gradient)
|
||||
'certifications-background': linear-gradient(135deg, rgba(88, 28, 135, 0.2) 0%, rgba(157, 23, 77, 0.2) 100%),
|
||||
|
||||
// Certifications title gradient (brighter purple to pink for dark mode)
|
||||
'gradient-certifications-title': linear-gradient(to right, #a855f7, #f472b6),
|
||||
|
||||
// Primary color for icons (lighter purple)
|
||||
'color-certifications-primary': #a855f7,
|
||||
|
||||
// Card styling (dark theme)
|
||||
'certifications-card-background': linear-gradient(135deg, #111827 0%, #1f2937 100%),
|
||||
'certifications-card-shadow': 0 10px 15px -3px rgba(0, 0, 0, 0.3),
|
||||
'certifications-card-shadow-hover': 0 20px 25px -5px rgba(0, 0, 0, 0.4),
|
||||
'certifications-card-overlay': linear-gradient(135deg, rgba(168, 85, 247, 0.05) 0%, rgba(244, 114, 182, 0.05) 100%),
|
||||
|
||||
// Badge styling (dark theme with purple/pink)
|
||||
'certifications-badge-background': linear-gradient(to right, rgba(88, 28, 135, 0.3), rgba(157, 23, 77, 0.3)),
|
||||
'certifications-badge-border': rgba(168, 85, 247, 0.3),
|
||||
'color-certifications-badge-text': #d8b4fe,
|
||||
);
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// Contact section theme definitions
|
||||
|
||||
$contact-light-theme: (
|
||||
// Contact section background (light indigo/purple tones)
|
||||
'contact-background': linear-gradient(135deg, #f0f9ff 0%, #faf5ff 100%),
|
||||
|
||||
// Contact title gradient (indigo to purple)
|
||||
'gradient-contact-title': linear-gradient(to right, #4f46e5, #7c3aed),
|
||||
|
||||
// Primary color for icons (indigo)
|
||||
'color-contact-primary': #4f46e5,
|
||||
|
||||
// Form styling
|
||||
'contact-form-bg': linear-gradient(135deg, #ffffff 0%, #fefefe 100%),
|
||||
'contact-form-border': rgba(79, 70, 229, 0.1),
|
||||
'contact-form-shadow': 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
// Input styling
|
||||
'contact-input-bg': #ffffff,
|
||||
'contact-input-border': rgba(79, 70, 229, 0.2),
|
||||
'contact-input-focus-ring': rgba(79, 70, 229, 0.1),
|
||||
|
||||
// Button styling
|
||||
'contact-button-bg': linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%),
|
||||
'contact-button-text': #ffffff,
|
||||
'contact-button-hover-bg': linear-gradient(135deg, #4338ca 0%, #6d28d9 100%),
|
||||
|
||||
// Social button styling
|
||||
'contact-social-bg': rgba(255, 255, 255, 0.8),
|
||||
'contact-social-text': #4b5563,
|
||||
'contact-social-border': rgba(79, 70, 229, 0.2),
|
||||
'contact-social-hover-bg': #ffffff,
|
||||
'contact-social-hover-border': rgba(79, 70, 229, 0.3),
|
||||
);
|
||||
|
||||
$contact-dark-theme: (
|
||||
// Contact section background (dark indigo/purple tones)
|
||||
'contact-background': linear-gradient(135deg, rgba(67, 56, 202, 0.2) 0%, rgba(109, 40, 217, 0.2) 100%),
|
||||
|
||||
// Contact title gradient (brighter indigo to purple for dark mode)
|
||||
'gradient-contact-title': linear-gradient(to right, #6366f1, #a855f7),
|
||||
|
||||
// Primary color for icons (lighter indigo)
|
||||
'color-contact-primary': #6366f1,
|
||||
|
||||
// Form styling (dark theme)
|
||||
'contact-form-bg': linear-gradient(135deg, #111827 0%, #1f2937 100%),
|
||||
'contact-form-border': rgba(99, 102, 241, 0.2),
|
||||
'contact-form-shadow': 0 10px 15px -3px rgba(0, 0, 0, 0.3),
|
||||
|
||||
// Input styling (dark theme)
|
||||
'contact-input-bg': #1f2937,
|
||||
'contact-input-border': rgba(99, 102, 241, 0.3),
|
||||
'contact-input-focus-ring': rgba(99, 102, 241, 0.2),
|
||||
|
||||
// Button styling (dark theme)
|
||||
'contact-button-bg': linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%),
|
||||
'contact-button-text': #ffffff,
|
||||
'contact-button-hover-bg': linear-gradient(135deg, #6366f1 0%, #a855f7 100%),
|
||||
|
||||
// Social button styling (dark theme)
|
||||
'contact-social-bg': rgba(31, 41, 55, 0.8),
|
||||
'contact-social-text': #e5e7eb,
|
||||
'contact-social-border': rgba(99, 102, 241, 0.3),
|
||||
'contact-social-hover-bg': #374151,
|
||||
'contact-social-hover-border': rgba(99, 102, 241, 0.4),
|
||||
);
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
@use '../variables' as *;
|
||||
|
||||
$hero-light-theme: (
|
||||
hero-background: $gradient-hero-light,
|
||||
|
||||
// Stat colors using visual hierarchy
|
||||
stat-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe),
|
||||
stat-primary-value: #2563eb,
|
||||
stat-primary-label: #1d4ed8,
|
||||
|
||||
stat-secondary-bg: linear-gradient(135deg, #dcfce7, #bbf7d0),
|
||||
stat-secondary-value: #16a34a,
|
||||
stat-secondary-label: #15803d,
|
||||
|
||||
stat-tertiary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4),
|
||||
stat-tertiary-value: #0d9488,
|
||||
stat-tertiary-label: #0f766e,
|
||||
|
||||
stat-quaternary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe),
|
||||
stat-quaternary-value: #9333ea,
|
||||
stat-quaternary-label: #7c3aed,
|
||||
);
|
||||
|
||||
$hero-dark-theme: (
|
||||
hero-background: $gradient-hero-dark,
|
||||
|
||||
// Stat colors using visual hierarchy
|
||||
stat-primary-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(30, 64, 175, 0.3)),
|
||||
stat-primary-value: #2563eb,
|
||||
stat-primary-label: #93c5fd,
|
||||
|
||||
stat-secondary-bg: linear-gradient(135deg, rgba(20, 83, 45, 0.3), rgba(22, 101, 52, 0.3)),
|
||||
stat-secondary-value: #16a34a,
|
||||
stat-secondary-label: #86efac,
|
||||
|
||||
stat-tertiary-bg: linear-gradient(135deg, rgba(19, 78, 74, 0.3), rgba(17, 94, 89, 0.3)),
|
||||
stat-tertiary-value: #0d9488,
|
||||
stat-tertiary-label: #5eead4,
|
||||
|
||||
stat-quaternary-bg: linear-gradient(135deg, rgba(88, 28, 135, 0.3), rgba(107, 33, 168, 0.3)),
|
||||
stat-quaternary-value: #9333ea,
|
||||
stat-quaternary-label: #c4b5fd,
|
||||
);
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
@use 'sass:map';
|
||||
@use 'base';
|
||||
@use 'hero';
|
||||
@use 'about';
|
||||
@use 'services';
|
||||
@use 'skills';
|
||||
@use 'certifications';
|
||||
@use 'projects';
|
||||
@use 'contact';
|
||||
|
||||
// Combine all section themes into main theme maps
|
||||
$light-theme: map.merge(
|
||||
base.$base-light-theme,
|
||||
map.merge(
|
||||
hero.$hero-light-theme,
|
||||
map.merge(
|
||||
about.$about-light-theme,
|
||||
map.merge(
|
||||
services.$services-light-theme,
|
||||
map.merge(
|
||||
skills.$skills-light-theme,
|
||||
map.merge(
|
||||
certifications.$certifications-light-theme,
|
||||
map.merge(
|
||||
projects.$projects-light-theme,
|
||||
contact.$contact-light-theme
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$dark-theme: map.merge(
|
||||
base.$base-dark-theme,
|
||||
map.merge(
|
||||
hero.$hero-dark-theme,
|
||||
map.merge(
|
||||
about.$about-dark-theme,
|
||||
map.merge(
|
||||
services.$services-dark-theme,
|
||||
map.merge(
|
||||
skills.$skills-dark-theme,
|
||||
map.merge(
|
||||
certifications.$certifications-dark-theme,
|
||||
map.merge(
|
||||
projects.$projects-dark-theme,
|
||||
contact.$contact-dark-theme
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// 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)};
|
||||
--gradient-primary: #{map.get($theme, gradient-primary)};
|
||||
--gradient-text: #{map.get($theme, gradient-text)};
|
||||
|
||||
// Hero section variables
|
||||
--hero-background: #{map.get($theme, hero-background)};
|
||||
|
||||
// Stat variables using semantic naming
|
||||
--stat-primary-bg: #{map.get($theme, stat-primary-bg)};
|
||||
--stat-primary-value: #{map.get($theme, stat-primary-value)};
|
||||
--stat-primary-label: #{map.get($theme, stat-primary-label)};
|
||||
--stat-secondary-bg: #{map.get($theme, stat-secondary-bg)};
|
||||
--stat-secondary-value: #{map.get($theme, stat-secondary-value)};
|
||||
--stat-secondary-label: #{map.get($theme, stat-secondary-label)};
|
||||
--stat-tertiary-bg: #{map.get($theme, stat-tertiary-bg)};
|
||||
--stat-tertiary-value: #{map.get($theme, stat-tertiary-value)};
|
||||
--stat-tertiary-label: #{map.get($theme, stat-tertiary-label)};
|
||||
--stat-quaternary-bg: #{map.get($theme, stat-quaternary-bg)};
|
||||
--stat-quaternary-value: #{map.get($theme, stat-quaternary-value)};
|
||||
--stat-quaternary-label: #{map.get($theme, stat-quaternary-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 using semantic naming
|
||||
--skill-badge-primary-bg: #{map.get($theme, skill-badge-primary-bg)};
|
||||
--skill-badge-primary-text: #{map.get($theme, skill-badge-primary-text)};
|
||||
--skill-badge-secondary-bg: #{map.get($theme, skill-badge-secondary-bg)};
|
||||
--skill-badge-secondary-text: #{map.get($theme, skill-badge-secondary-text)};
|
||||
--skill-badge-tertiary-bg: #{map.get($theme, skill-badge-tertiary-bg)};
|
||||
--skill-badge-tertiary-text: #{map.get($theme, skill-badge-tertiary-text)};
|
||||
--skill-badge-quaternary-bg: #{map.get($theme, skill-badge-quaternary-bg)};
|
||||
--skill-badge-quaternary-text: #{map.get($theme, skill-badge-quaternary-text)};
|
||||
|
||||
// Feature card variables using semantic naming
|
||||
--feature-card-primary-bg: #{map.get($theme, feature-card-primary-bg)};
|
||||
--feature-card-secondary-bg: #{map.get($theme, feature-card-secondary-bg)};
|
||||
--feature-card-tertiary-bg: #{map.get($theme, feature-card-tertiary-bg)};
|
||||
--feature-icon-primary: #{map.get($theme, feature-icon-primary)};
|
||||
--feature-icon-secondary: #{map.get($theme, feature-icon-secondary)};
|
||||
--feature-icon-tertiary: #{map.get($theme, feature-icon-tertiary)};
|
||||
--feature-title-primary: #{map.get($theme, feature-title-primary)};
|
||||
--feature-title-secondary: #{map.get($theme, feature-title-secondary)};
|
||||
--feature-title-tertiary: #{map.get($theme, feature-title-tertiary)};
|
||||
--feature-description-primary: #{map.get($theme, feature-description-primary)};
|
||||
--feature-description-secondary: #{map.get($theme, feature-description-secondary)};
|
||||
--feature-description-tertiary: #{map.get($theme, feature-description-tertiary)};
|
||||
|
||||
// Services section variables
|
||||
--services-background: #{map.get($theme, services-background)};
|
||||
--gradient-services-title: #{map.get($theme, gradient-services-title)};
|
||||
|
||||
// Service card variables
|
||||
--service-card-primary-bg: #{map.get($theme, service-card-primary-bg)};
|
||||
--service-card-secondary-bg: #{map.get($theme, service-card-secondary-bg)};
|
||||
--service-card-tertiary-bg: #{map.get($theme, service-card-tertiary-bg)};
|
||||
--service-card-quaternary-bg: #{map.get($theme, service-card-quaternary-bg)};
|
||||
--service-card-quinary-bg: #{map.get($theme, service-card-quinary-bg)};
|
||||
--service-card-senary-bg: #{map.get($theme, service-card-senary-bg)};
|
||||
|
||||
--service-icon-primary: #{map.get($theme, service-icon-primary)};
|
||||
--service-icon-secondary: #{map.get($theme, service-icon-secondary)};
|
||||
--service-icon-tertiary: #{map.get($theme, service-icon-tertiary)};
|
||||
--service-icon-quaternary: #{map.get($theme, service-icon-quaternary)};
|
||||
--service-icon-quinary: #{map.get($theme, service-icon-quinary)};
|
||||
--service-icon-senary: #{map.get($theme, service-icon-senary)};
|
||||
|
||||
--service-title-primary: #{map.get($theme, service-title-primary)};
|
||||
--service-title-secondary: #{map.get($theme, service-title-secondary)};
|
||||
--service-title-tertiary: #{map.get($theme, service-title-tertiary)};
|
||||
--service-title-quaternary: #{map.get($theme, service-title-quaternary)};
|
||||
--service-title-quinary: #{map.get($theme, service-title-quinary)};
|
||||
--service-title-senary: #{map.get($theme, service-title-senary)};
|
||||
|
||||
--service-description-primary: #{map.get($theme, service-description-primary)};
|
||||
--service-description-secondary: #{map.get($theme, service-description-secondary)};
|
||||
--service-description-tertiary: #{map.get($theme, service-description-tertiary)};
|
||||
--service-description-quaternary: #{map.get($theme, service-description-quaternary)};
|
||||
--service-description-quinary: #{map.get($theme, service-description-quinary)};
|
||||
--service-description-senary: #{map.get($theme, service-description-senary)};
|
||||
|
||||
// Skills section variables
|
||||
--skills-background: #{map.get($theme, skills-background)};
|
||||
--skills-background-pattern: #{map.get($theme, skills-background-pattern)};
|
||||
--gradient-skills-title: #{map.get($theme, gradient-skills-title)};
|
||||
--skills-category-bg: #{map.get($theme, skills-category-bg)};
|
||||
--skills-category-border: #{map.get($theme, skills-category-border)};
|
||||
--skills-category-title-color: #{map.get($theme, skills-category-title-color)};
|
||||
--skills-skill-name-color: #{map.get($theme, skills-skill-name-color)};
|
||||
--skills-skill-level-color: #{map.get($theme, skills-skill-level-color)};
|
||||
--skills-skill-percentage-color: #{map.get($theme, skills-skill-percentage-color)};
|
||||
--skills-progress-bg: #{map.get($theme, skills-progress-bg)};
|
||||
|
||||
// Card-specific backgrounds and accents
|
||||
--skills-category-bg-primary: #{map.get($theme, skills-category-bg-primary)};
|
||||
--skills-category-border-primary: #{map.get($theme, skills-category-border-primary)};
|
||||
--skills-gradient-primary: #{map.get($theme, skills-gradient-primary)};
|
||||
--skills-accent-primary: #{map.get($theme, skills-accent-primary)};
|
||||
|
||||
--skills-category-bg-secondary: #{map.get($theme, skills-category-bg-secondary)};
|
||||
--skills-category-border-secondary: #{map.get($theme, skills-category-border-secondary)};
|
||||
--skills-gradient-secondary: #{map.get($theme, skills-gradient-secondary)};
|
||||
--skills-accent-secondary: #{map.get($theme, skills-accent-secondary)};
|
||||
|
||||
--skills-category-bg-tertiary: #{map.get($theme, skills-category-bg-tertiary)};
|
||||
--skills-category-border-tertiary: #{map.get($theme, skills-category-border-tertiary)};
|
||||
--skills-gradient-tertiary: #{map.get($theme, skills-gradient-tertiary)};
|
||||
--skills-accent-tertiary: #{map.get($theme, skills-accent-tertiary)};
|
||||
|
||||
--skills-category-bg-quaternary: #{map.get($theme, skills-category-bg-quaternary)};
|
||||
--skills-category-border-quaternary: #{map.get($theme, skills-category-border-quaternary)};
|
||||
--skills-gradient-quaternary: #{map.get($theme, skills-gradient-quaternary)};
|
||||
--skills-accent-quaternary: #{map.get($theme, skills-accent-quaternary)};
|
||||
|
||||
--skills-category-bg-quinary: #{map.get($theme, skills-category-bg-quinary)};
|
||||
--skills-category-border-quinary: #{map.get($theme, skills-category-border-quinary)};
|
||||
--skills-gradient-quinary: #{map.get($theme, skills-gradient-quinary)};
|
||||
--skills-accent-quinary: #{map.get($theme, skills-accent-quinary)};
|
||||
|
||||
--skills-category-bg-senary: #{map.get($theme, skills-category-bg-senary)};
|
||||
--skills-category-border-senary: #{map.get($theme, skills-category-border-senary)};
|
||||
--skills-gradient-senary: #{map.get($theme, skills-gradient-senary)};
|
||||
--skills-accent-senary: #{map.get($theme, skills-accent-senary)};
|
||||
|
||||
// Progress bar colors
|
||||
--skills-progress-primary: #{map.get($theme, skills-progress-primary)};
|
||||
--skills-progress-secondary: #{map.get($theme, skills-progress-secondary)};
|
||||
--skills-progress-tertiary: #{map.get($theme, skills-progress-tertiary)};
|
||||
--skills-progress-quaternary: #{map.get($theme, skills-progress-quaternary)};
|
||||
--skills-progress-quinary: #{map.get($theme, skills-progress-quinary)};
|
||||
--skills-progress-senary: #{map.get($theme, skills-progress-senary)};
|
||||
|
||||
// Certifications section variables
|
||||
--certifications-background: #{map.get($theme, certifications-background)};
|
||||
--gradient-certifications-title: #{map.get($theme, gradient-certifications-title)};
|
||||
--color-certifications-primary: #{map.get($theme, color-certifications-primary)};
|
||||
--certifications-card-background: #{map.get($theme, certifications-card-background)};
|
||||
--certifications-card-shadow: #{map.get($theme, certifications-card-shadow)};
|
||||
--certifications-card-shadow-hover: #{map.get($theme, certifications-card-shadow-hover)};
|
||||
--certifications-card-overlay: #{map.get($theme, certifications-card-overlay)};
|
||||
--certifications-badge-background: #{map.get($theme, certifications-badge-background)};
|
||||
--certifications-badge-border: #{map.get($theme, certifications-badge-border)};
|
||||
--color-certifications-badge-text: #{map.get($theme, color-certifications-badge-text)};
|
||||
|
||||
// Projects section variables
|
||||
--projects-background: #{map.get($theme, projects-background)};
|
||||
--gradient-projects-title: #{map.get($theme, gradient-projects-title)};
|
||||
--projects-card-background: #{map.get($theme, projects-card-background)};
|
||||
--projects-card-border: #{map.get($theme, projects-card-border)};
|
||||
--projects-card-shadow: #{map.get($theme, projects-card-shadow)};
|
||||
--projects-card-shadow-hover: #{map.get($theme, projects-card-shadow-hover)};
|
||||
--projects-overlay-background: #{map.get($theme, projects-overlay-background)};
|
||||
--projects-button-primary-bg: #{map.get($theme, projects-button-primary-bg)};
|
||||
--projects-button-primary-text: #{map.get($theme, projects-button-primary-text)};
|
||||
--projects-button-primary-border: #{map.get($theme, projects-button-primary-border)};
|
||||
--projects-button-primary-hover-bg: #{map.get($theme, projects-button-primary-hover-bg)};
|
||||
--projects-button-secondary-bg: #{map.get($theme, projects-button-secondary-bg)};
|
||||
--projects-button-secondary-text: #{map.get($theme, projects-button-secondary-text)};
|
||||
--projects-button-secondary-border: #{map.get($theme, projects-button-secondary-border)};
|
||||
--projects-button-secondary-hover-bg: #{map.get($theme, projects-button-secondary-hover-bg)};
|
||||
--projects-tech-badge-bg: #{map.get($theme, projects-tech-badge-bg)};
|
||||
--projects-tech-badge-text: #{map.get($theme, projects-tech-badge-text)};
|
||||
--projects-tech-badge-border: #{map.get($theme, projects-tech-badge-border)};
|
||||
|
||||
// Contact section variables
|
||||
--contact-background: #{map.get($theme, contact-background)};
|
||||
--gradient-contact-title: #{map.get($theme, gradient-contact-title)};
|
||||
--color-contact-primary: #{map.get($theme, color-contact-primary)};
|
||||
--contact-form-bg: #{map.get($theme, contact-form-bg)};
|
||||
--contact-form-border: #{map.get($theme, contact-form-border)};
|
||||
--contact-form-shadow: #{map.get($theme, contact-form-shadow)};
|
||||
--contact-input-bg: #{map.get($theme, contact-input-bg)};
|
||||
--contact-input-border: #{map.get($theme, contact-input-border)};
|
||||
--contact-input-focus-ring: #{map.get($theme, contact-input-focus-ring)};
|
||||
--contact-button-bg: #{map.get($theme, contact-button-bg)};
|
||||
--contact-button-text: #{map.get($theme, contact-button-text)};
|
||||
--contact-button-hover-bg: #{map.get($theme, contact-button-hover-bg)};
|
||||
--contact-social-bg: #{map.get($theme, contact-social-bg)};
|
||||
--contact-social-text: #{map.get($theme, contact-social-text)};
|
||||
--contact-social-border: #{map.get($theme, contact-social-border)};
|
||||
--contact-social-hover-bg: #{map.get($theme, contact-social-hover-bg)};
|
||||
--contact-social-hover-border: #{map.get($theme, contact-social-hover-border)};
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// Projects section theme definitions
|
||||
|
||||
$projects-light-theme: (
|
||||
// Projects section background (light orange/red tones)
|
||||
'projects-background': linear-gradient(135deg, #fff7ed 0%, #fef2f2 100%),
|
||||
|
||||
// Projects title gradient (orange to red)
|
||||
'gradient-projects-title': linear-gradient(to right, #ea580c, #dc2626),
|
||||
|
||||
// Card styling
|
||||
'projects-card-background': linear-gradient(135deg, #ffffff 0%, #fefefe 100%),
|
||||
'projects-card-border': rgba(234, 88, 12, 0.1),
|
||||
'projects-card-shadow': 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||
'projects-card-shadow-hover': 0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
||||
|
||||
// Overlay styling
|
||||
'projects-overlay-background': rgba(0, 0, 0, 0.6),
|
||||
|
||||
// Button styling
|
||||
'projects-button-primary-bg': linear-gradient(135deg, #ea580c 0%, #dc2626 100%),
|
||||
'projects-button-primary-text': #ffffff,
|
||||
'projects-button-primary-border': rgba(234, 88, 12, 0.3),
|
||||
'projects-button-primary-hover-bg': linear-gradient(135deg, #dc2626 0%, #b91c1c 100%),
|
||||
|
||||
'projects-button-secondary-bg': rgba(255, 255, 255, 0.9),
|
||||
'projects-button-secondary-text': #374151,
|
||||
'projects-button-secondary-border': rgba(255, 255, 255, 0.2),
|
||||
'projects-button-secondary-hover-bg': #ffffff,
|
||||
|
||||
// Tech badge styling
|
||||
'projects-tech-badge-bg': linear-gradient(135deg, #fed7aa 0%, #fecaca 100%),
|
||||
'projects-tech-badge-text': #c2410c,
|
||||
'projects-tech-badge-border': rgba(234, 88, 12, 0.2),
|
||||
);
|
||||
|
||||
$projects-dark-theme: (
|
||||
// Projects section background (dark orange/red tones)
|
||||
'projects-background': linear-gradient(135deg, rgba(124, 45, 18, 0.2) 0%, rgba(127, 29, 29, 0.2) 100%),
|
||||
|
||||
// Projects title gradient (brighter orange to red for dark mode)
|
||||
'gradient-projects-title': linear-gradient(to right, #fb923c, #f87171),
|
||||
|
||||
// Card styling (dark theme)
|
||||
'projects-card-background': linear-gradient(135deg, #111827 0%, #1f2937 100%),
|
||||
'projects-card-border': rgba(251, 146, 60, 0.2),
|
||||
'projects-card-shadow': 0 4px 6px -1px rgba(0, 0, 0, 0.3),
|
||||
'projects-card-shadow-hover': 0 20px 25px -5px rgba(0, 0, 0, 0.4),
|
||||
|
||||
// Overlay styling
|
||||
'projects-overlay-background': rgba(0, 0, 0, 0.7),
|
||||
|
||||
// Button styling (dark theme)
|
||||
'projects-button-primary-bg': linear-gradient(135deg, #ea580c 0%, #dc2626 100%),
|
||||
'projects-button-primary-text': #ffffff,
|
||||
'projects-button-primary-border': rgba(234, 88, 12, 0.3),
|
||||
'projects-button-primary-hover-bg': linear-gradient(135deg, #fb923c 0%, #f87171 100%),
|
||||
|
||||
'projects-button-secondary-bg': rgba(31, 41, 55, 0.9),
|
||||
'projects-button-secondary-text': #e5e7eb,
|
||||
'projects-button-secondary-border': rgba(75, 85, 99, 0.3),
|
||||
'projects-button-secondary-hover-bg': #374151,
|
||||
|
||||
// Tech badge styling (dark theme)
|
||||
'projects-tech-badge-bg': linear-gradient(135deg, rgba(124, 45, 18, 0.3) 0%, rgba(127, 29, 29, 0.3) 100%),
|
||||
'projects-tech-badge-text': #fed7aa,
|
||||
'projects-tech-badge-border': rgba(251, 146, 60, 0.3),
|
||||
);
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
$services-light-theme: (
|
||||
// Services section colors - Tailwind inspired
|
||||
services-background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 25%, #cbd5e1 70%, #94a3b8 100%),
|
||||
gradient-services-title: linear-gradient(90deg, #0f172a, #1e293b, #334155),
|
||||
|
||||
// Service card colors using Tailwind color palette
|
||||
service-card-primary-bg: linear-gradient(135deg, #dbeafe, #bfdbfe), // blue-100 to blue-200
|
||||
service-card-secondary-bg: linear-gradient(135deg, #dcfce7, #bbf7d0), // green-100 to green-200
|
||||
service-card-tertiary-bg: linear-gradient(135deg, #e9d5ff, #ddd6fe), // violet-100 to violet-200
|
||||
service-card-quaternary-bg: linear-gradient(135deg, #ccfbf1, #99f6e4), // teal-100 to teal-200
|
||||
service-card-quinary-bg: linear-gradient(135deg, #fed7aa, #fdba74), // orange-200 to orange-300
|
||||
service-card-senary-bg: linear-gradient(135deg, #fecaca, #fca5a5), // red-200 to red-300
|
||||
|
||||
service-icon-primary: #2563eb, // blue-600
|
||||
service-icon-secondary: #16a34a, // green-600
|
||||
service-icon-tertiary: #7c3aed, // violet-600
|
||||
service-icon-quaternary: #0d9488, // teal-600
|
||||
service-icon-quinary: #ea580c, // orange-600
|
||||
service-icon-senary: #dc2626, // red-600
|
||||
|
||||
service-title-primary: #1e40af, // blue-700
|
||||
service-title-secondary: #166534, // green-700
|
||||
service-title-tertiary: #6d28d9, // violet-700
|
||||
service-title-quaternary: #0f766e, // teal-700
|
||||
service-title-quinary: #c2410c, // orange-700
|
||||
service-title-senary: #b91c1c, // red-700
|
||||
|
||||
service-description-primary: #1d4ed8, // blue-700
|
||||
service-description-secondary: #15803d, // green-700
|
||||
service-description-tertiary: #5b21b6, // violet-800
|
||||
service-description-quaternary: #115e59, // teal-800
|
||||
service-description-quinary: #9a3412, // orange-800
|
||||
service-description-senary: #991b1b, // red-800
|
||||
);
|
||||
|
||||
$services-dark-theme: (
|
||||
// Services section colors - Dark mode with Tailwind palette
|
||||
services-background: linear-gradient(135deg, #0f172a 0%, #1e293b 25%, #334155 70%, #475569 100%),
|
||||
gradient-services-title: linear-gradient(90deg, #f1f5f9, #e2e8f0, #cbd5e1),
|
||||
|
||||
// Service card colors for dark theme - using Tailwind dark colors
|
||||
service-card-primary-bg: linear-gradient(135deg, rgba(30, 58, 138, 0.3), rgba(37, 99, 235, 0.2)), // blue-800/30 to blue-600/20
|
||||
service-card-secondary-bg: linear-gradient(135deg, rgba(22, 101, 52, 0.3), rgba(34, 197, 94, 0.2)), // green-800/30 to green-500/20
|
||||
service-card-tertiary-bg: linear-gradient(135deg, rgba(91, 33, 182, 0.3), rgba(124, 58, 237, 0.2)), // violet-800/30 to violet-600/20
|
||||
service-card-quaternary-bg: linear-gradient(135deg, rgba(17, 94, 89, 0.3), rgba(20, 184, 166, 0.2)), // teal-800/30 to teal-500/20
|
||||
service-card-quinary-bg: linear-gradient(135deg, rgba(154, 52, 18, 0.3), rgba(249, 115, 22, 0.2)), // orange-800/30 to orange-500/20
|
||||
service-card-senary-bg: linear-gradient(135deg, rgba(153, 27, 27, 0.3), rgba(239, 68, 68, 0.2)), // red-800/30 to red-500/20
|
||||
|
||||
service-icon-primary: #60a5fa, // blue-400
|
||||
service-icon-secondary: #4ade80, // green-400
|
||||
service-icon-tertiary: #a78bfa, // violet-400
|
||||
service-icon-quaternary: #2dd4bf, // teal-400
|
||||
service-icon-quinary: #fb923c, // orange-400
|
||||
service-icon-senary: #f87171, // red-400
|
||||
|
||||
service-title-primary: #93c5fd, // blue-300
|
||||
service-title-secondary: #86efac, // green-300
|
||||
service-title-tertiary: #c4b5fd, // violet-300
|
||||
service-title-quaternary: #5eead4, // teal-300
|
||||
service-title-quinary: #fdba74, // orange-300
|
||||
service-title-senary: #fca5a5, // red-300
|
||||
|
||||
service-description-primary: #bfdbfe, // blue-200
|
||||
service-description-secondary: #bbf7d0, // green-200
|
||||
service-description-tertiary: #ddd6fe, // violet-200
|
||||
service-description-quaternary: #99f6e4, // teal-200
|
||||
service-description-quinary: #fed7aa, // orange-200
|
||||
service-description-senary: #fecaca, // red-200
|
||||
);
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
$skills-light-theme: (
|
||||
// Skills section colors
|
||||
skills-background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 25%, #e2e8f0 75%, #cbd5e1 100%),
|
||||
skills-background-pattern: radial-gradient(circle at 25% 25%, #3b82f6, transparent 50%),
|
||||
gradient-skills-title: linear-gradient(90deg, #1e40af, #3b82f6, #10b981),
|
||||
skills-category-bg: rgba(255, 255, 255, 0.7),
|
||||
skills-category-border: rgba(148, 163, 184, 0.2),
|
||||
skills-category-title-color: #1e293b,
|
||||
skills-skill-name-color: #334155,
|
||||
skills-skill-level-color: #64748b,
|
||||
skills-skill-percentage-color: #475569,
|
||||
skills-progress-bg: rgba(226, 232, 240, 0.8),
|
||||
|
||||
// Card 1 - Web Frameworks (Blue theme - matching hero)
|
||||
skills-category-bg-primary: linear-gradient(135deg, rgba(239, 246, 255, 0.9), rgba(219, 234, 254, 0.95)),
|
||||
skills-category-border-primary: rgba(59, 130, 246, 0.2),
|
||||
skills-gradient-primary: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(37, 99, 235, 0.05)),
|
||||
skills-accent-primary: #3b82f6,
|
||||
|
||||
// Card 2 - Styling & Design (Green theme - matching about)
|
||||
skills-category-bg-secondary: linear-gradient(135deg, rgba(236, 253, 245, 0.9), rgba(209, 250, 229, 0.95)),
|
||||
skills-category-border-secondary: rgba(16, 185, 129, 0.2),
|
||||
skills-gradient-secondary: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(5, 150, 105, 0.05)),
|
||||
skills-accent-secondary: #10b981,
|
||||
|
||||
// Card 3 - Backend Development (Purple theme)
|
||||
skills-category-bg-tertiary: linear-gradient(135deg, rgba(245, 243, 255, 0.9), rgba(237, 233, 254, 0.95)),
|
||||
skills-category-border-tertiary: rgba(139, 92, 246, 0.2),
|
||||
skills-gradient-tertiary: linear-gradient(135deg, rgba(139, 92, 246, 0.1), rgba(124, 58, 237, 0.05)),
|
||||
skills-accent-tertiary: #8b5cf6,
|
||||
|
||||
// Card 4 - Development Tools (Teal theme)
|
||||
skills-category-bg-quaternary: linear-gradient(135deg, rgba(240, 253, 250, 0.9), rgba(204, 251, 241, 0.95)),
|
||||
skills-category-border-quaternary: rgba(6, 182, 212, 0.2),
|
||||
skills-gradient-quaternary: linear-gradient(135deg, rgba(6, 182, 212, 0.1), rgba(8, 145, 178, 0.05)),
|
||||
skills-accent-quaternary: #06b6d4,
|
||||
|
||||
// Card 5 - Testing & Quality (Orange theme)
|
||||
skills-category-bg-quinary: linear-gradient(135deg, rgba(255, 251, 235, 0.9), rgba(254, 243, 199, 0.95)),
|
||||
skills-category-border-quinary: rgba(245, 158, 11, 0.2),
|
||||
skills-gradient-quinary: linear-gradient(135deg, rgba(245, 158, 11, 0.1), rgba(217, 119, 6, 0.05)),
|
||||
skills-accent-quinary: #f59e0b,
|
||||
|
||||
// Card 6 - AI-Tools (Indigo theme)
|
||||
skills-category-bg-senary: linear-gradient(135deg, rgba(238, 242, 255, 0.9), rgba(224, 231, 255, 0.95)),
|
||||
skills-category-border-senary: rgba(99, 102, 241, 0.2),
|
||||
skills-gradient-senary: linear-gradient(135deg, rgba(99, 102, 241, 0.1), rgba(79, 70, 229, 0.05)),
|
||||
skills-accent-senary: #6366f1,
|
||||
|
||||
// Enhanced progress bar colors
|
||||
skills-progress-primary: linear-gradient(90deg, #3b82f6, #1d4ed8, #1e40af),
|
||||
skills-progress-secondary: linear-gradient(90deg, #10b981, #047857, #065f46),
|
||||
skills-progress-tertiary: linear-gradient(90deg, #8b5cf6, #7c3aed, #6d28d9),
|
||||
skills-progress-quaternary: linear-gradient(90deg, #06b6d4, #0891b2, #0e7490),
|
||||
skills-progress-quinary: linear-gradient(90deg, #f59e0b, #d97706, #b45309),
|
||||
skills-progress-senary: linear-gradient(90deg, #6366f1, #4f46e5, #4338ca),
|
||||
);
|
||||
|
||||
$skills-dark-theme: (
|
||||
// Skills section colors
|
||||
skills-background: linear-gradient(135deg, #0f172a 0%, #1e293b 25%, #334155 75%, #475569 100%),
|
||||
skills-background-pattern: radial-gradient(circle at 25% 25%, #1e40af, transparent 50%),
|
||||
gradient-skills-title: linear-gradient(90deg, #60a5fa, #34d399, #a78bfa),
|
||||
skills-category-bg: rgba(30, 41, 55, 0.8),
|
||||
skills-category-border: rgba(71, 85, 105, 0.3),
|
||||
skills-category-title-color: #f1f5f9,
|
||||
skills-skill-name-color: #cbd5e1,
|
||||
skills-skill-level-color: #94a3b8,
|
||||
skills-skill-percentage-color: #e2e8f0,
|
||||
skills-progress-bg: rgba(51, 65, 85, 0.8),
|
||||
|
||||
// Dark theme card backgrounds
|
||||
skills-category-bg-primary: linear-gradient(135deg, rgba(30, 58, 138, 0.4), rgba(29, 78, 216, 0.3)),
|
||||
skills-category-border-primary: rgba(96, 165, 250, 0.3),
|
||||
skills-gradient-primary: linear-gradient(135deg, rgba(96, 165, 250, 0.1), rgba(59, 130, 246, 0.05)),
|
||||
skills-accent-primary: #60a5fa,
|
||||
|
||||
skills-category-bg-secondary: linear-gradient(135deg, rgba(6, 95, 70, 0.4), rgba(4, 120, 87, 0.3)),
|
||||
skills-category-border-secondary: rgba(52, 211, 153, 0.3),
|
||||
skills-gradient-secondary: linear-gradient(135deg, rgba(52, 211, 153, 0.1), rgba(16, 185, 129, 0.05)),
|
||||
skills-accent-secondary: #34d399,
|
||||
|
||||
skills-category-bg-tertiary: linear-gradient(135deg, rgba(88, 28, 135, 0.4), rgba(109, 40, 217, 0.3)),
|
||||
skills-category-border-tertiary: rgba(167, 139, 250, 0.3),
|
||||
skills-gradient-tertiary: linear-gradient(135deg, rgba(167, 139, 250, 0.1), rgba(139, 92, 246, 0.05)),
|
||||
skills-accent-tertiary: #a78bfa,
|
||||
|
||||
skills-category-bg-quaternary: linear-gradient(135deg, rgba(15, 118, 110, 0.4), rgba(17, 94, 89, 0.3)),
|
||||
skills-category-border-quaternary: rgba(34, 211, 238, 0.3),
|
||||
skills-gradient-quaternary: linear-gradient(135deg, rgba(34, 211, 238, 0.1), rgba(6, 182, 212, 0.05)),
|
||||
skills-accent-quaternary: #22d3ee,
|
||||
|
||||
skills-category-bg-quinary: linear-gradient(135deg, rgba(180, 83, 9, 0.4), rgba(217, 119, 6, 0.3)),
|
||||
skills-category-border-quinary: rgba(251, 191, 36, 0.3),
|
||||
skills-gradient-quinary: linear-gradient(135deg, rgba(251, 191, 36, 0.1), rgba(245, 158, 11, 0.05)),
|
||||
skills-accent-quinary: #fbbf24,
|
||||
|
||||
skills-category-bg-senary: linear-gradient(135deg, rgba(67, 56, 202, 0.4), rgba(79, 70, 229, 0.3)),
|
||||
skills-category-border-senary: rgba(129, 140, 248, 0.3),
|
||||
skills-gradient-senary: linear-gradient(135deg, rgba(129, 140, 248, 0.1), rgba(99, 102, 241, 0.05)),
|
||||
skills-accent-senary: #818cf8,
|
||||
|
||||
// Enhanced progress bars for dark theme
|
||||
skills-progress-primary: linear-gradient(90deg, #60a5fa, #3b82f6, #2563eb),
|
||||
skills-progress-secondary: linear-gradient(90deg, #34d399, #10b981, #059669),
|
||||
skills-progress-tertiary: linear-gradient(90deg, #a78bfa, #8b5cf6, #7c3aed),
|
||||
skills-progress-quaternary: linear-gradient(90deg, #22d3ee, #06b6d4, #0891b2),
|
||||
skills-progress-quinary: linear-gradient(90deg, #fbbf24, #f59e0b, #d97706),
|
||||
skills-progress-senary: linear-gradient(90deg, #818cf8, #6366f1, #4f46e5),
|
||||
);
|
||||
|
|
@ -59,4 +59,15 @@ $services-grid-gap: 1.5rem;
|
|||
$services-grid-gap-mobile: 1rem;
|
||||
$services-header-margin: 2rem;
|
||||
|
||||
// Skills Section Variables
|
||||
$skills-header-margin: 3rem;
|
||||
$skills-category-gap: 2rem;
|
||||
$skills-category-gap-mobile: 1.5rem;
|
||||
$skills-skill-gap: 1.5rem;
|
||||
$skills-skill-gap-mobile: 1rem;
|
||||
$skills-progress-height: 0.75rem;
|
||||
$skills-progress-radius: 9999px;
|
||||
$skills-category-padding: 2rem;
|
||||
$skills-category-padding-mobile: 1.5rem;
|
||||
|
||||
// Add any other global variables here
|
||||
|
|
|
|||
Loading…
Reference in New Issue