updated route pathing, skills.tsx for skill card abstraction and header for mobile
This commit is contained in:
82
src/components/SkillCard.tsx
Normal file
82
src/components/SkillCard.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
export interface SkillDetail {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkillCategory {
|
||||||
|
category: string;
|
||||||
|
skills: SkillDetail[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SkillCardProps {
|
||||||
|
category: SkillCategory;
|
||||||
|
isExpanded: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SkillCard({ category, isExpanded, onToggle }: SkillCardProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
layout
|
||||||
|
onClick={onToggle}
|
||||||
|
style={{
|
||||||
|
background: isExpanded ? "rgba(255, 255, 255, 0.12)" : "rgba(255, 255, 255, 0.07)",
|
||||||
|
backdropFilter: "blur(10px)",
|
||||||
|
border: isExpanded ? "1px solid rgba(255, 255, 255, 0.3)" : "1px solid rgba(255, 255, 255, 0.15)",
|
||||||
|
borderRadius: "12px",
|
||||||
|
padding: "24px",
|
||||||
|
cursor: "pointer",
|
||||||
|
gridColumn: isExpanded ? "1 / -1" : "auto",
|
||||||
|
zIndex: isExpanded ? 10 : 1
|
||||||
|
}}
|
||||||
|
whileHover={!isExpanded ? { scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.12)" } : {}}
|
||||||
|
transition={{ duration: 0.3, type: "spring" }}
|
||||||
|
>
|
||||||
|
<motion.h3 layout="position" style={{ color: "white", marginTop: 0, marginBottom: "15px", fontSize: "1.2rem" }}>
|
||||||
|
{category.category} {isExpanded ? <span style={{ fontSize: "0.8em", opacity: 0.7 }}>(Click to collapse)</span> : null}
|
||||||
|
</motion.h3>
|
||||||
|
|
||||||
|
<motion.div layout="position" style={{ display: "flex", flexWrap: "wrap", gap: "10px", marginBottom: isExpanded ? "20px" : "0" }}>
|
||||||
|
{category.skills.map((skill) => (
|
||||||
|
<span
|
||||||
|
key={skill.name}
|
||||||
|
style={{
|
||||||
|
background: "rgba(255, 255, 255, 0.15)",
|
||||||
|
color: "rgba(255, 255, 255, 0.9)",
|
||||||
|
padding: "6px 12px",
|
||||||
|
borderRadius: "20px",
|
||||||
|
fontSize: "0.85rem",
|
||||||
|
fontWeight: 500,
|
||||||
|
border: "1px solid rgba(255, 255, 255, 0.1)"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{skill.name}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{isExpanded && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: 10 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
style={{ borderTop: "1px solid rgba(255,255,255,0.2)", paddingTop: "20px" }}
|
||||||
|
>
|
||||||
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: "15px" }}>
|
||||||
|
{category.skills.map((skill) => (
|
||||||
|
<div key={skill.name} style={{ display: "flex", flexDirection: "column", gap: "5px" }}>
|
||||||
|
<strong style={{ color: "#fff", fontSize: "1rem" }}>{skill.name}</strong>
|
||||||
|
<p style={{ margin: 0, color: "rgba(255,255,255,0.7)", fontSize: "0.9rem" }}>
|
||||||
|
{skill.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,9 +16,23 @@ export default function FloatingHeader() {
|
|||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
|
const ROUTE_TITLES: { [key: string]: string } = {
|
||||||
|
"/": "Sasha Bayda",
|
||||||
|
"/skills": "Skills",
|
||||||
|
"/work-experience": "Work Experience",
|
||||||
|
"/projects": "Projects",
|
||||||
|
"/about": "About Me"
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentTitle = ROUTE_TITLES[location.pathname] || "Sasha Bayda";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="floating-header">
|
<header className="floating-header">
|
||||||
<div className="header-content">
|
<div className="header-content">
|
||||||
|
<div className="mobile-page-title">
|
||||||
|
{currentTitle}
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`mobile-toggle ${isMenuOpen ? "open" : ""}`}
|
className={`mobile-toggle ${isMenuOpen ? "open" : ""}`}
|
||||||
onClick={toggleMenu}
|
onClick={toggleMenu}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { motion, Variants } from "framer-motion";
|
import { motion, Variants, AnimatePresence } from "framer-motion";
|
||||||
|
import { useState } from "react";
|
||||||
interface SkillCategory {
|
import SkillCard, { SkillCategory } from "../components/SkillCard";
|
||||||
category: string;
|
|
||||||
skills: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProfessionalSkill {
|
interface ProfessionalSkill {
|
||||||
skill: string;
|
skill: string;
|
||||||
@@ -13,27 +10,69 @@ interface ProfessionalSkill {
|
|||||||
const TECHNICAL_SKILLS: SkillCategory[] = [
|
const TECHNICAL_SKILLS: SkillCategory[] = [
|
||||||
{
|
{
|
||||||
category: "Languages",
|
category: "Languages",
|
||||||
skills: ["JavaScript (ES6+)", "TypeScript", "Python", "Java", "C", "C++", "Lua", "HTML", "CSS", "SQL"]
|
skills: [
|
||||||
|
{ name: "JavaScript (ES6+)", description: "Core language for web interactivity, closures, and async programming." },
|
||||||
|
{ name: "TypeScript", description: "Strict syntactical superset of JavaScript adding static typing." },
|
||||||
|
{ name: "Python", description: "Versatile language used for backend scripting and data processing." },
|
||||||
|
{ name: "Java", description: "Object-oriented language for robust enterprise backends." },
|
||||||
|
{ name: "C", description: "Low-level system programming and memory management." },
|
||||||
|
{ name: "C++", description: "High-performance application development with OOP features." },
|
||||||
|
{ name: "Lua", description: "Lightweight scripting often used in embedded systems and game dev." },
|
||||||
|
{ name: "HTML", description: "Standard markup language for document structure." },
|
||||||
|
{ name: "CSS", description: "Style sheet language for presentation and layout." },
|
||||||
|
{ name: "SQL", description: "Standard language for relational database management." }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "Frameworks/Libraries",
|
category: "Frameworks/Libraries",
|
||||||
skills: ["React", "Express.js", "Django REST Framework", "AWS Lambda"]
|
skills: [
|
||||||
|
{ name: "React", description: "Library for building component-based user interfaces." },
|
||||||
|
{ name: "Express.js", description: "Minimalist web framework for Node.js servers." },
|
||||||
|
{ name: "Django REST Framework", description: "Toolkit for building Web APIs with Python/Django." },
|
||||||
|
{ name: "AWS Lambda", description: "Serverless compute service for running code without provisioning." }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "Databases",
|
category: "Databases",
|
||||||
skills: ["PostgreSQL (SQL Based)", "CouchDB (No-SQL Based)"]
|
skills: [
|
||||||
|
{ name: "PostgreSQL", description: "Advanced open-source relational database (SQL)." },
|
||||||
|
{ name: "CouchDB", description: "Seamless multi-master sync capability database (No-SQL)." }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "DevOps & Tools",
|
category: "DevOps & Tools",
|
||||||
skills: ["Docker", "CircleCI", "Git (CLI)", "Postman", "AWS CDK", "Linux CLI"]
|
skills: [
|
||||||
|
{ name: "Docker", description: "Platform for developing, shipping, and running applications in containers." },
|
||||||
|
{ name: "CircleCI", description: "Continuous integration and delivery platform." },
|
||||||
|
{ name: "Git (CLI)", description: "Distributed version control system." },
|
||||||
|
{ name: "Postman", description: "API platform for building and using APIs." },
|
||||||
|
{ name: "AWS CDK", description: "Software development framework for defining cloud infrastructure in code." },
|
||||||
|
{ name: "Linux CLI", description: "Command line interface for system interaction and scripting." }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "AWS Technologies",
|
category: "AWS Technologies",
|
||||||
skills: ["S3", "Cloudfront", "Route 53", "API Gateway", "Lambda", "RDS", "VPC", "ECS", "SQS"]
|
skills: [
|
||||||
|
{ name: "S3", description: "Scalable object storage service." },
|
||||||
|
{ name: "Cloudfront", description: "Content Delivery Network (CDN) service." },
|
||||||
|
{ name: "Route 53", description: "Scalable Domain Name System (DNS) web service." },
|
||||||
|
{ name: "API Gateway", description: "Service to create, publish, maintain, monitor, and secure APIs." },
|
||||||
|
{ name: "Lambda", description: "Serverless compute service." },
|
||||||
|
{ name: "RDS", description: "Managed relational database service." },
|
||||||
|
{ name: "VPC", description: "Logically isolated section of the AWS Cloud." },
|
||||||
|
{ name: "ECS", description: "Fully managed container orchestration service." },
|
||||||
|
{ name: "SQS", description: "Message queuing service for decoupling microservices." }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "Concepts",
|
category: "Concepts",
|
||||||
skills: ["RESTful APIs", "Agile/Scrum", "MVC Architecture", "Cloud Deployment", "Responsive Design"]
|
skills: [
|
||||||
|
{ name: "RESTful APIs", description: "Architectural style for network-based software." },
|
||||||
|
{ name: "Agile/Scrum", description: "Iterative approach to project software delivery." },
|
||||||
|
{ name: "MVC Architecture", description: "Design pattern separating Model, View, and Controller." },
|
||||||
|
{ name: "Cloud Deployment", description: "Process of deploying applications to cloud infrastructure." },
|
||||||
|
{ name: "Responsive Design", description: "Design approach for rendering well on variety of devices." }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -85,6 +124,8 @@ const itemVariants: Variants = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Skills() {
|
export default function Skills() {
|
||||||
|
const [expandedCategory, setExpandedCategory] = useState<string | null>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mainContentBlock" style={{ minWidth: "66vw", display: "flex", justifyContent: "center" }}>
|
<div className="mainContentBlock" style={{ minWidth: "66vw", display: "flex", justifyContent: "center" }}>
|
||||||
<div style={{ height: "30px" }}></div>
|
<div style={{ height: "30px" }}></div>
|
||||||
@@ -99,40 +140,22 @@ export default function Skills() {
|
|||||||
<h2 style={{ color: "white", borderBottom: "1px solid rgba(255,255,255,0.2)", paddingBottom: "10px", marginBottom: "25px" }}>
|
<h2 style={{ color: "white", borderBottom: "1px solid rgba(255,255,255,0.2)", paddingBottom: "10px", marginBottom: "25px" }}>
|
||||||
Technical Skills
|
Technical Skills
|
||||||
</h2>
|
</h2>
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))", gap: "30px" }}>
|
<div className="tech-skills-grid" style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))",
|
||||||
|
gap: "30px",
|
||||||
|
position: "relative" // For layout animations
|
||||||
|
}}>
|
||||||
|
<AnimatePresence>
|
||||||
{TECHNICAL_SKILLS.map((category) => (
|
{TECHNICAL_SKILLS.map((category) => (
|
||||||
<motion.div
|
<SkillCard
|
||||||
key={category.category}
|
key={category.category}
|
||||||
style={{
|
category={category}
|
||||||
background: "rgba(255, 255, 255, 0.07)",
|
isExpanded={expandedCategory === category.category}
|
||||||
backdropFilter: "blur(10px)",
|
onToggle={() => setExpandedCategory(expandedCategory === category.category ? null : category.category)}
|
||||||
border: "1px solid rgba(255, 255, 255, 0.15)",
|
/>
|
||||||
borderRadius: "12px",
|
|
||||||
padding: "24px",
|
|
||||||
}}
|
|
||||||
whileHover={{ scale: 1.02, backgroundColor: "rgba(255, 255, 255, 0.12)" }}
|
|
||||||
>
|
|
||||||
<h3 style={{ color: "white", marginTop: 0, marginBottom: "15px", fontSize: "1.2rem" }}>{category.category}</h3>
|
|
||||||
<div style={{ display: "flex", flexWrap: "wrap", gap: "10px" }}>
|
|
||||||
{category.skills.map((skill) => (
|
|
||||||
<span
|
|
||||||
key={skill}
|
|
||||||
style={{
|
|
||||||
background: "rgba(255, 255, 255, 0.15)",
|
|
||||||
color: "rgba(255, 255, 255, 0.9)",
|
|
||||||
padding: "6px 12px",
|
|
||||||
borderRadius: "20px",
|
|
||||||
fontSize: "0.85rem",
|
|
||||||
fontWeight: 500,
|
|
||||||
border: "1px solid rgba(255, 255, 255, 0.1)"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{skill}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
))}
|
))}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -164,7 +187,7 @@ export default function Skills() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div >
|
||||||
</div>
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,11 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-page-title {
|
||||||
|
display: none;
|
||||||
|
/* Hidden on desktop */
|
||||||
|
}
|
||||||
|
|
||||||
.mobile-toggle {
|
.mobile-toggle {
|
||||||
display: none;
|
display: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -100,9 +105,20 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-page-title {
|
||||||
|
display: block;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
z-index: 1001;
|
||||||
|
/* Ensure visible above nav overlay if needed, though nav usually covers content */
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.header-content {
|
.header-content {
|
||||||
justify-content: flex-end;
|
justify-content: space-between;
|
||||||
/* Align hamburger to right */
|
/* Push title left, hamburger right */
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-toggle {
|
.mobile-toggle {
|
||||||
|
|||||||
Reference in New Issue
Block a user