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);
|
||||
}, [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 (
|
||||
<header className="floating-header">
|
||||
<div className="header-content">
|
||||
<div className="mobile-page-title">
|
||||
{currentTitle}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className={`mobile-toggle ${isMenuOpen ? "open" : ""}`}
|
||||
onClick={toggleMenu}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { motion, Variants } from "framer-motion";
|
||||
|
||||
interface SkillCategory {
|
||||
category: string;
|
||||
skills: string[];
|
||||
}
|
||||
import { motion, Variants, AnimatePresence } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import SkillCard, { SkillCategory } from "../components/SkillCard";
|
||||
|
||||
interface ProfessionalSkill {
|
||||
skill: string;
|
||||
@@ -13,27 +10,69 @@ interface ProfessionalSkill {
|
||||
const TECHNICAL_SKILLS: SkillCategory[] = [
|
||||
{
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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",
|
||||
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() {
|
||||
const [expandedCategory, setExpandedCategory] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<div className="mainContentBlock" style={{ minWidth: "66vw", display: "flex", justifyContent: "center" }}>
|
||||
<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" }}>
|
||||
Technical Skills
|
||||
</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) => (
|
||||
<motion.div
|
||||
<SkillCard
|
||||
key={category.category}
|
||||
style={{
|
||||
background: "rgba(255, 255, 255, 0.07)",
|
||||
backdropFilter: "blur(10px)",
|
||||
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>
|
||||
category={category}
|
||||
isExpanded={expandedCategory === category.category}
|
||||
onToggle={() => setExpandedCategory(expandedCategory === category.category ? null : category.category)}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -164,7 +187,7 @@ export default function Skills() {
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div >
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.mobile-page-title {
|
||||
display: none;
|
||||
/* Hidden on desktop */
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
@@ -100,9 +105,20 @@
|
||||
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 {
|
||||
justify-content: flex-end;
|
||||
/* Align hamburger to right */
|
||||
justify-content: space-between;
|
||||
/* Push title left, hamburger right */
|
||||
}
|
||||
|
||||
.mobile-toggle {
|
||||
|
||||
Reference in New Issue
Block a user