feat: Add BentoGrid component to display personal interests on the About page and update Home page text and added framer animations

This commit is contained in:
2026-01-19 22:24:44 -06:00
parent 0054d35234
commit febe971d0c
4 changed files with 255 additions and 23 deletions

View File

@@ -0,0 +1,106 @@
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import '../styles/components/BentoGrid.css';
interface BentoItem {
id: string;
title: string;
description: string;
className?: string; // For sizing: bento-large, bento-wide, bento-tall
bgImage: string | string[];
}
const items: BentoItem[] = [
{
id: 'gaming',
title: "Gaming & Tech",
description: "Wind down with some PC gaming or just appreciating good new gear.",
className: "bento-large",
bgImage: "https://placehold.co/800x800/2a1a4a/FFF?text=Gaming+Setup"
},
{
id: 'snowboarding',
title: "Snowboarding",
description: "The rush of going down the mountains are unmatched",
className: "bento-tall",
bgImage: "https://placehold.co/400x800/a7ffeb/004d40?text=Snowboarding"
},
{
id: 'cooking',
title: "Culinary Arts",
description: "Experimenting with new recipes and flavors in the kitchen.",
className: "", // Standard 1x1
bgImage: "https://placehold.co/400x400/3e2723/d7ccc8?text=Cooking"
},
{
id: 'social',
title: "Family & Friends",
description: "Good times with my favorite people. Including my Girlfriend, Friends and Family",
className: "", // Standard 1x1
bgImage: [
"https://placehold.co/400x400/f8bbd0/880e4f?text=Social+1",
"https://placehold.co/400x400/d81b60/ffffff?text=Social+2",
"https://placehold.co/400x400/880e4f/f8bbd0?text=Social+3"
]
},
{
id: 'homelab',
title: "Home Labbing",
description: "I enjoy the challenge of self-hosting my own digital services at home. I also love tinkering with new hardware and software setups.",
className: "bento-wide",
bgImage: "/homelabber.png"
}
];
const BentoCard = ({ item, index, delay }: { item: BentoItem; index: number; delay: number }) => {
const images = Array.isArray(item.bgImage) ? item.bgImage : [item.bgImage];
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
if (images.length <= 1) return;
const interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % images.length);
}, 5000);
return () => clearInterval(interval);
}, [images.length]);
return (
<motion.div
className={`bento-item ${item.className || ''}`}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: delay + index * 0.1 }}
>
<AnimatePresence mode="popLayout" initial={false}>
<motion.img
key={currentIndex}
src={images[currentIndex]}
alt={item.title}
className="bento-bg"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 0.6, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.8 }}
onError={(e) => {
e.currentTarget.src = "https://placehold.co/600x400?text=" + item.title;
}}
/>
</AnimatePresence>
<div className="bento-overlay" />
<div className="bento-content">
<h3 className="bento-title">{item.title}</h3>
<p className="bento-description">{item.description}</p>
</div>
</motion.div>
);
};
export default function BentoGrid({ delay = 0 }: { delay?: number }) {
return (
<div className="bento-grid-container">
{items.map((item, index) => (
<BentoCard key={item.id} item={item} index={index} delay={delay} />
))}
</div>
);
}

View File

@@ -2,13 +2,9 @@ import { motion } from "framer-motion";
import VisitedMap from "../components/VisitedMap";
import BioSection from "../components/BioSection";
import "../styles/pages/about.css";
import BentoGrid from "../components/BentoGrid";
const ABOUT_TEXT = "Hi! I'm Sasha Bayda, a passionate developer focused on creating beautiful and functional web experiences. With a background in computer science and a keen eye for design, I strive to bridge the gap between technology and user-centric solutions. When I'm not coding, you can find me exploring the outdoors, experimenting with new recipes, or indulging in photography. Feel free to explore my projects and get in touch if you'd like to collaborate or learn more about my work!" + "\n\n" + "Thank you for visiting my digital resume site. I look forward to connecting with you!";
const JANE_TEXT = "Outside of work, I enjoy spending time with my girlfriend, family and friends. Doing things such as cooking, watching shows or movies, gaming, snowboarding, or just relaxing with a glass of something warm.";
const HOMELAB_TEXT = "I love hosting my own applications and learning new technologies. Right now I host my own gitea server to host my own git repositories and host my own 'runners', immich to host my own photo gallery, and a beszel to monitor all of my applications with a dashboards, graphs and webhook alerts."
const ABOUT_TEXT = "Hey, I'm Sasha! You probably know I code, but that's just one part of myself. When I step away from the screen, I've got other things that I'm interested in." + "\n\n" + "Check out that stuff below in the Bento Grid to learn more about me!";
const VISITED_CITIES = [
"Melfort",
@@ -55,6 +51,15 @@ export default function About() {
/>
<motion.div
style={{ marginTop: "60px", marginBottom: "40px" }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.8 }}
>
<h2 style={{ textAlign: "center", fontSize: "1.5rem", marginBottom: "30px", color: "rgba(255,255,255,0.9)" }}>Personal Interests</h2>
<BentoGrid delay={1.0} />
</motion.div>
<motion.div
style={{ marginTop: "40px", width: "100%" }}
initial={{ y: 20, opacity: 0 }}
@@ -67,22 +72,6 @@ export default function About() {
</p>
<VisitedMap places={VISITED_CITIES} />
</motion.div>
<div style={{ marginTop: "40px" }}>
<BioSection
imageSrc="/janepic.jpg, fampic.jpg, gangpic.jpg"
imageAlt="profile"
text={JANE_TEXT}
reversed={true}
/>
</div>
<div style={{ marginTop: "40px" }}>
<BioSection
imageSrc="/beszel.png"
imageAlt="profile"
text={HOMELAB_TEXT}
reversed={false}
/>
</div>
</div>
</motion.div>
</div>

View File

@@ -7,7 +7,7 @@ const welcomeText = `Hello! My name is Sasha Bayda and welcome to my digital res
Here you will find some of my projects, skills, contact information and any information I couldn't fit into my resume.
Feel free to explore and learn more about me and if something isn't answered, don't hesitate to reach out via the contact page!`;
Feel free to explore and learn more about me and if something isn't answered, don't hesitate to reach out via the links found below!`;
const HOVER_TEXTS: { [key: string]: string } = {
"Email": "Drop me a line! I'm always open to new opportunities and interesting conversations.",

View File

@@ -0,0 +1,137 @@
.bento-grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(2, 250px);
gap: 20px;
width: 100%;
max-width: 1200px;
margin: 40px auto;
padding: 0 20px;
box-sizing: border-box;
}
.bento-item {
position: relative;
background: rgba(255, 255, 255, 0.05);
border-radius: 24px;
border: 1px solid rgba(255, 255, 255, 0.1);
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
display: flex;
flex-direction: column;
justify-content: flex-end;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.bento-item:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
border-color: rgba(255, 255, 255, 0.2);
z-index: 10;
}
/* Background Image styling */
.bento-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s ease;
z-index: 1;
opacity: 0.6;
}
.bento-item:hover .bento-bg {
transform: scale(1.1);
opacity: 0.8;
}
/* Overlay gradient for readability */
.bento-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.4) 50%, rgba(0, 0, 0, 0.1) 100%);
z-index: 2;
}
.bento-content {
position: relative;
z-index: 3;
padding: 25px;
color: white;
}
.bento-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 8px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.bento-description {
font-size: 0.95rem;
color: rgba(255, 255, 255, 0.85);
line-height: 1.4;
margin: 0;
}
/* Specific Item Spans */
.bento-large {
grid-column: span 2;
grid-row: span 2;
}
.bento-wide {
grid-column: span 2;
}
.bento-tall {
grid-row: span 2;
}
/* Responsive adjustments */
@media (max-width: 900px) {
.bento-grid-container {
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 250px;
/* Use auto-rows to handle flexible height */
}
.bento-large {
grid-column: span 2;
grid-row: span 1;
/* Reset to standard height on tablet if needed, or keep tall */
}
.bento-tall {
grid-row: span 1;
}
}
@media (max-width: 600px) {
.bento-grid-container {
grid-template-columns: 1fr;
grid-template-rows: auto;
display: flex;
flex-direction: column;
height: auto;
}
.bento-item {
min-height: 250px;
}
.bento-large,
.bento-wide,
.bento-tall {
grid-column: span 1;
grid-row: span 1;
}
}