diff --git a/src/app/page.tsx b/src/app/page.tsx
index f03cc71..688581f 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -10,7 +10,10 @@ import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import TypingAnimation from '@/components/TypingAnimation';
import ProjectMasonry from '@/components/ProjectMasonry';
+import SocialIcons from '@/components/SocialIcons';
+import Timeline from '@/components/Timeline/Timeline';
import projects from '@/data/projects';
+import timelineData from '@/data/timeline';
export default function HomePage() {
const [showContent, setShowContent] = useState(false);
@@ -32,6 +35,9 @@ export default function HomePage() {
/>
)}
+ {/* Social Icons */}
+ {showContent && }
+
d4m13n
- .dev
+
+ .dev
+
@@ -88,8 +102,25 @@ export default function HomePage() {
}
}}
>
-
-
+
+ Software
+ Software Dev Projects
+
+ } />
+
+ Game Dev
+ Game Dev Projects
+
+ } />
+
@@ -106,7 +137,14 @@ export default function HomePage() {
)}
+
{activeTab === 2 && (
+
+
+
+ )}
+
+ {activeTab === 3 && (
About Me
@@ -122,7 +160,7 @@ export default function HomePage() {
)}
- {activeTab === 3 && (
+ {activeTab === 4 && (
Contact
diff --git a/src/components/ProjectCard.tsx b/src/components/ProjectCard.tsx
index c2f9c9e..c8bb753 100644
--- a/src/components/ProjectCard.tsx
+++ b/src/components/ProjectCard.tsx
@@ -223,13 +223,16 @@ const ProjectCard: React.FC = ({ project }) => {
label={tech}
size="small"
sx={{
- backgroundColor: 'rgba(255,255,255,0.1)',
+ backgroundColor: 'rgba(255, 0, 0, 0.7)', // Red background
color: textColor,
height: 24,
fontSize: '0.75rem',
+ boxShadow: '0 0 10px rgba(255, 0, 0, 0.5), 0 0 20px rgba(255, 0, 0, 0.3)', // Red glow
+ filter: 'drop-shadow(0 2px 4px rgba(255, 0, 0, 0.4))',
'&:hover': {
- backgroundColor: 'rgba(255,255,255,0.2)',
- boxShadow: ambientGlow,
+ backgroundColor: 'rgba(255, 0, 0, 0.8)', // Slightly darker red on hover
+ boxShadow: '0 0 15px rgba(255, 0, 0, 0.6), 0 0 30px rgba(255, 0, 0, 0.4)', // Enhanced red glow
+ filter: 'drop-shadow(0 4px 8px rgba(255, 0, 0, 0.5))',
},
transition: 'all 0.3s ease-in-out',
}}
@@ -265,10 +268,12 @@ const ProjectCard: React.FC = ({ project }) => {
borderRadius: '6px',
padding: '4px 12px',
minWidth: '80px',
+ boxShadow: '0 0 5px rgba(10, 25, 50, 0.3), 0 0 10px rgba(10, 25, 50, 0.2)',
'&:hover': {
- borderColor: textColor,
- backgroundColor: 'rgba(255,255,255,0.05)',
- boxShadow: ambientGlow,
+ borderColor: '#4fd1ff',
+ backgroundColor: 'rgba(79, 209, 255, 0.1)',
+ boxShadow: '0 0 10px rgba(10, 25, 50, 0.5), 0 0 20px rgba(10, 25, 50, 0.4)',
+ filter: 'drop-shadow(0 2px 4px rgba(10, 25, 50, 0.5))',
},
transition: 'all 0.3s ease-in-out',
}}
@@ -294,9 +299,12 @@ const ProjectCard: React.FC = ({ project }) => {
borderRadius: '6px',
padding: '4px 12px',
minWidth: '80px',
+ boxShadow: '0 0 10px rgba(10, 25, 50, 0.5), 0 0 20px rgba(10, 25, 50, 0.4)',
+ filter: 'drop-shadow(0 2px 4px rgba(10, 25, 50, 0.5))',
'&:hover': {
- backgroundColor: 'rgba(79, 209, 255, 0.4)',
- boxShadow: enhancedGlow,
+ backgroundColor: 'rgba(79, 209, 255, 0.5)',
+ boxShadow: '0 0 15px rgba(10, 25, 50, 0.6), 0 0 30px rgba(10, 25, 50, 0.5)',
+ filter: 'drop-shadow(0 4px 8px rgba(10, 25, 50, 0.6))',
},
transition: 'all 0.3s ease-in-out',
}}
diff --git a/src/components/SocialIcons.tsx b/src/components/SocialIcons.tsx
new file mode 100644
index 0000000..3a50c0f
--- /dev/null
+++ b/src/components/SocialIcons.tsx
@@ -0,0 +1,169 @@
+import React from 'react';
+import { Box, IconButton, Tooltip, SvgIcon } from '@mui/material';
+import GitHubIcon from '@mui/icons-material/GitHub';
+import LinkedInIcon from '@mui/icons-material/LinkedIn';
+import { SvgIconProps } from '@mui/material/SvgIcon';
+
+// Custom icons for platforms without MUI icons
+const GiteaIcon = (props: SvgIconProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const DiscordIcon = (props: SvgIconProps) => (
+
+
+
+);
+
+const XIcon = (props: SvgIconProps) => (
+
+
+
+);
+
+const FilesIcon = (props: SvgIconProps) => (
+
+
+
+);
+
+const AIIcon = (props: SvgIconProps) => (
+
+
+
+
+);
+
+// Interface for social media links
+export interface SocialLink {
+ name: string;
+ url: string;
+ icon: React.ElementType;
+}
+
+// Default social links
+const defaultSocialLinks: SocialLink[] = [
+ {
+ name: 'LinkedIn',
+ url: 'https://www.linkedin.com/in/damien-ostler-254663110/',
+ icon: LinkedInIcon,
+ },
+ {
+ name: 'GitHub',
+ url: 'https://github.com/d4m13n-d3v',
+ icon: GitHubIcon,
+ },
+ {
+ name: 'Gitea',
+ url: 'https://git.d4m13n.dev',
+ icon: GiteaIcon,
+ },
+ {
+ name: 'Discord',
+ url: 'https://discord.gg/8dHnaarghJ',
+ icon: DiscordIcon,
+ },
+ {
+ name: 'X',
+ url: 'https://x.com/d4m13n_d3v',
+ icon: XIcon,
+ },
+ {
+ name: 'Files',
+ url: 'https://files.d4m13n.dev',
+ icon: FilesIcon,
+ },
+ {
+ name: 'AI',
+ url: 'https://ai.d4m13n.dev',
+ icon: AIIcon,
+ },
+];
+
+// Glow effects
+const whiteGlowEffects = {
+ textShadow: '0 0 10px rgba(255, 255, 255, 0.25), 0 0 20px rgba(255, 255, 255, 0.15)',
+ filter: 'drop-shadow(0 2px 4px rgba(0, 0, 0, 0.25))'
+};
+
+const blueGlowEffects = {
+ textShadow: '0 0 10px rgba(79, 209, 255, 0.15), 0 0 20px rgba(79, 209, 255, 0.1)',
+ filter: 'drop-shadow(0 2px 4px rgba(0, 0, 0, 0.25))'
+};
+
+interface SocialIconsProps {
+ links?: SocialLink[];
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
+}
+
+const SocialIcons: React.FC = ({
+ links = defaultSocialLinks,
+ position = 'top-right'
+}) => {
+ // Determine position styling
+ const getPositionStyling = () => {
+ switch (position) {
+ case 'top-right':
+ return { top: 16, right: 16 };
+ case 'top-left':
+ return { top: 16, left: 16 };
+ case 'bottom-right':
+ return { bottom: 16, right: 16 };
+ case 'bottom-left':
+ return { bottom: 16, left: 16 };
+ default:
+ return { top: 16, right: 16 };
+ }
+ };
+
+ return (
+
+ {links.map((link) => (
+
+
+
+
+
+ ))}
+
+ );
+};
+
+export default SocialIcons;
\ No newline at end of file
diff --git a/src/components/Timeline/MasonryGrid.tsx b/src/components/Timeline/MasonryGrid.tsx
new file mode 100644
index 0000000..4755fa3
--- /dev/null
+++ b/src/components/Timeline/MasonryGrid.tsx
@@ -0,0 +1,99 @@
+import React, { useState, useEffect } from 'react';
+import { Box, useMediaQuery, useTheme } from '@mui/material';
+import { MasonryGridProps } from './types';
+import MasonryItem from './MasonryItem';
+
+const MasonryGrid: React.FC = ({
+ items,
+ isVisible = true,
+ animationDelay = 0
+}) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
+ const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'md'));
+
+ const [visibleItems, setVisibleItems] = useState(
+ Array(items.length).fill(false)
+ );
+
+ // Staggered animation for items
+ useEffect(() => {
+ if (isVisible) {
+ const timers: NodeJS.Timeout[] = [];
+
+ items.forEach((_, index) => {
+ const timer = setTimeout(() => {
+ setVisibleItems(prev => {
+ const newState = [...prev];
+ newState[index] = true;
+ return newState;
+ });
+ }, animationDelay + (index * 150)); // Stagger each item by 150ms
+
+ timers.push(timer);
+ });
+
+ return () => timers.forEach(timer => clearTimeout(timer));
+ } else {
+ setVisibleItems(Array(items.length).fill(false));
+ }
+ }, [isVisible, items.length, animationDelay]);
+
+ // Determine column count based on screen size
+ const getColumnCount = () => {
+ if (isMobile) return 1;
+ if (isTablet) return 2;
+ return 3;
+ };
+
+ // Distribute items into columns for masonry layout
+ const getColumns = () => {
+ const columnCount = getColumnCount();
+ const columns: React.ReactNode[][] = Array.from({ length: columnCount }, () => []);
+
+ items.forEach((item, index) => {
+ const columnIndex = index % columnCount;
+ columns[columnIndex].push(
+
+ );
+ });
+
+ return columns;
+ };
+
+ const columns = getColumns();
+ const columnCount = getColumnCount();
+
+ return (
+
+ {columns.map((column, index) => (
+
+ {column}
+
+ ))}
+
+ );
+};
+
+export default MasonryGrid;
\ No newline at end of file
diff --git a/src/components/Timeline/MasonryItem.tsx b/src/components/Timeline/MasonryItem.tsx
new file mode 100644
index 0000000..160ff89
--- /dev/null
+++ b/src/components/Timeline/MasonryItem.tsx
@@ -0,0 +1,117 @@
+import React from 'react';
+import { Box, Card, CardContent, CardMedia, Typography, Fade } from '@mui/material';
+import { MasonryItemProps } from './types';
+
+const MasonryItem: React.FC = ({
+ item,
+ isVisible = true,
+ animationDelay = 0
+}) => {
+ // Define colors and effects
+ const redBackground = 'rgba(244, 67, 54, 0.4)'; // Red background similar to exclamation mark
+ const titleColor = '#f44336'; // Material UI red (same as exclamation mark)
+ const titleGlow = '0 0 10px rgba(244, 67, 54, 0.35), 0 0 20px rgba(244, 67, 54, 0.25)'; // Same glow as exclamation mark
+
+ // Enhanced shadow effects with red glow like the exclamation mark
+ const shadowEffects = {
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2), 0 0 10px rgba(244, 67, 54, 0.35), 0 0 20px rgba(244, 67, 54, 0.25)',
+ filter: 'drop-shadow(0 2px 4px rgba(244, 67, 54, 0.3))',
+ transition: 'all 0.3s ease-in-out',
+ '&:hover': {
+ boxShadow: '0 6px 12px rgba(0, 0, 0, 0.3), 0 0 15px rgba(244, 67, 54, 0.5), 0 0 30px rgba(244, 67, 54, 0.35)',
+ filter: 'drop-shadow(0 4px 8px rgba(244, 67, 54, 0.4))',
+ transform: 'translateY(-5px)'
+ }
+ };
+
+ // Render different content based on the item type
+ const renderContent = () => {
+ switch (item.type) {
+ case 'image':
+ return (
+
+
+
+ );
+
+ case 'card':
+ return (
+
+
+ {item.content}
+
+ {item.imageUrl && (
+
+ )}
+
+ );
+
+ case 'text':
+ default:
+ return (
+
+ {item.content}
+
+ );
+ }
+ };
+
+ return (
+
+
+ {renderContent()}
+
+
+ );
+};
+
+export default MasonryItem;
\ No newline at end of file
diff --git a/src/components/Timeline/Timeline.tsx b/src/components/Timeline/Timeline.tsx
new file mode 100644
index 0000000..ffb9838
--- /dev/null
+++ b/src/components/Timeline/Timeline.tsx
@@ -0,0 +1,106 @@
+import React, { useState, useEffect, useRef } from 'react';
+import { Box, useMediaQuery, useTheme } from '@mui/material';
+import { TimelineProps, TimelineOrientation } from './types';
+import TimelineItem from './TimelineItem';
+
+const Timeline: React.FC = ({
+ items,
+ orientation = 'vertical',
+ className
+}) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
+ const timelineRef = useRef(null);
+
+ // Force vertical orientation on mobile
+ const effectiveOrientation: TimelineOrientation = isMobile ? 'vertical' : orientation;
+
+ // Track which items are visible based on scroll position
+ const [visibleItems, setVisibleItems] = useState(
+ Array(items.length).fill(false)
+ );
+
+ // Handle scroll-based animations
+ useEffect(() => {
+ const handleScroll = () => {
+ if (timelineRef.current) {
+ const timelineRect = timelineRef.current.getBoundingClientRect();
+ const timelineItems = timelineRef.current.querySelectorAll('[data-timeline-item]');
+
+ timelineItems.forEach((item, index) => {
+ const rect = item.getBoundingClientRect();
+ const isVisible =
+ rect.top <= window.innerHeight * 0.8 &&
+ rect.bottom >= window.innerHeight * 0.2;
+
+ setVisibleItems(prev => {
+ if (prev[index] !== isVisible) {
+ const newState = [...prev];
+ newState[index] = isVisible;
+ return newState;
+ }
+ return prev;
+ });
+ });
+ }
+ };
+
+ // Initial check
+ handleScroll();
+
+ // Add scroll listener
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, [items.length]);
+
+ return (
+
+ {items.map((item, index) => (
+
+
+
+ ))}
+
+ );
+};
+
+export default Timeline;
\ No newline at end of file
diff --git a/src/components/Timeline/TimelineItem.tsx b/src/components/Timeline/TimelineItem.tsx
new file mode 100644
index 0000000..ec0fa24
--- /dev/null
+++ b/src/components/Timeline/TimelineItem.tsx
@@ -0,0 +1,239 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Typography,
+ Button,
+ Fade,
+ Paper,
+ useTheme,
+ useMediaQuery
+} from '@mui/material';
+import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
+import { TimelineItemProps } from './types';
+import MasonryGrid from './MasonryGrid';
+
+const TimelineItem: React.FC = ({
+ item,
+ orientation,
+ isVisible = true,
+ index
+}) => {
+ const theme = useTheme();
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
+ const [isHovered, setIsHovered] = useState(false);
+
+ // Force vertical orientation on mobile
+ const effectiveOrientation = isMobile ? 'vertical' : orientation;
+
+ // Determine if this item should be on the left or right (for vertical orientation)
+ // or top or bottom (for horizontal orientation)
+ const isAlternating = index % 2 === 1;
+
+ // Enhanced glow effects for the marker - using deep dark blue with stronger glow
+ const markerGlowEffects = {
+ boxShadow: isHovered
+ ? '0 0 15px rgba(10, 25, 50, 0.6), 0 0 30px rgba(10, 25, 50, 0.5)'
+ : '0 0 10px rgba(10, 25, 50, 0.5), 0 0 20px rgba(10, 25, 50, 0.4)',
+ filter: isHovered
+ ? 'drop-shadow(0 4px 8px rgba(10, 25, 50, 0.6))'
+ : 'drop-shadow(0 2px 4px rgba(10, 25, 50, 0.5))',
+ transition: 'all 0.3s ease-in-out',
+ };
+
+ // Subtle shadow for the content without white glow
+ const contentShadowEffects = {
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
+ transition: 'all 0.3s ease-in-out',
+ };
+
+ // Enhanced button hover effects - using darker blue glow
+ const buttonHoverEffects = {
+ boxShadow: '0 0 10px rgba(10, 25, 50, 0.5), 0 0 20px rgba(10, 25, 50, 0.4)',
+ filter: 'drop-shadow(0 2px 4px rgba(10, 25, 50, 0.5))',
+ '&:hover': {
+ transform: 'translateX(5px)',
+ boxShadow: '0 0 15px rgba(10, 25, 50, 0.6), 0 0 30px rgba(10, 25, 50, 0.5)',
+ filter: 'drop-shadow(0 4px 8px rgba(10, 25, 50, 0.6))',
+ },
+ transition: 'all 0.3s ease-in-out',
+ };
+
+ // Render the timeline marker
+ const renderMarker = () => (
+
+
+
+ {item.date}
+
+
+
+ {/* Line connecting to the next item */}
+ {effectiveOrientation === 'vertical' ? (
+
+ ) : (
+
+ )}
+
+ );
+
+ // Render the content section
+ const renderContent = () => (
+
+
+
+ {item.title}
+
+
+
+ {item.description}
+
+
+
+
+ {item.actionUrl && (
+
+ }
+ href={item.actionUrl}
+ sx={{
+ backgroundColor: '#0a1932', // Deep dark blue
+ color: 'white', // White text
+ ...buttonHoverEffects,
+ '&:hover': {
+ ...buttonHoverEffects['&:hover'],
+ backgroundColor: '#152a45', // Slightly lighter on hover
+ }
+ }}
+ >
+ {item.actionText || 'Go to'}
+
+
+ )}
+
+
+ );
+
+ // Layout for vertical orientation
+ if (effectiveOrientation === 'vertical') {
+ return (
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ >
+ {/* For alternating layout on desktop */}
+ {!isMobile && isAlternating ? (
+ <>
+ {renderContent()}
+
+ {renderMarker()}
+
+ {/* Empty space for alignment */}
+ >
+ ) : (
+ <>
+ {/* Empty space for alignment */}
+
+ {renderMarker()}
+
+ {renderContent()}
+ >
+ )}
+
+ );
+ }
+
+ // Layout for horizontal orientation
+ return (
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ >
+ {isAlternating ? (
+ <>
+
+ {renderMarker()}
+
+ {renderContent()}
+ >
+ ) : (
+ <>
+ {renderContent()}
+
+ {renderMarker()}
+
+ >
+ )}
+
+ );
+};
+
+export default TimelineItem;
\ No newline at end of file
diff --git a/src/components/Timeline/index.ts b/src/components/Timeline/index.ts
new file mode 100644
index 0000000..2a5a5d3
--- /dev/null
+++ b/src/components/Timeline/index.ts
@@ -0,0 +1,5 @@
+export { default as Timeline } from './Timeline';
+export { default as TimelineItem } from './TimelineItem';
+export { default as MasonryGrid } from './MasonryGrid';
+export { default as MasonryItem } from './MasonryItem';
+export * from './types';
\ No newline at end of file
diff --git a/src/components/Timeline/types.ts b/src/components/Timeline/types.ts
new file mode 100644
index 0000000..fcad1e2
--- /dev/null
+++ b/src/components/Timeline/types.ts
@@ -0,0 +1,47 @@
+export interface TimelineItemData {
+ id: string;
+ date: string;
+ title: string;
+ description: string;
+ actionUrl?: string;
+ actionText?: string;
+ items: MasonryItemData[];
+}
+
+export interface MasonryItemData {
+ id: string;
+ type: 'image' | 'card' | 'text';
+ content: string;
+ title?: string; // Optional title for the masonry item
+ imageUrl?: string;
+ width?: number; // For controlling the size in the masonry grid
+ height?: number;
+ backgroundColor?: string;
+}
+
+export type TimelineOrientation = 'horizontal' | 'vertical';
+
+export interface TimelineProps {
+ items: TimelineItemData[];
+ orientation?: TimelineOrientation;
+ className?: string;
+}
+
+export interface TimelineItemProps {
+ item: TimelineItemData;
+ orientation: TimelineOrientation;
+ isVisible?: boolean;
+ index: number;
+}
+
+export interface MasonryGridProps {
+ items: MasonryItemData[];
+ isVisible?: boolean;
+ animationDelay?: number;
+}
+
+export interface MasonryItemProps {
+ item: MasonryItemData;
+ isVisible?: boolean;
+ animationDelay?: number;
+}
\ No newline at end of file
diff --git a/src/data/timeline.ts b/src/data/timeline.ts
new file mode 100644
index 0000000..14bed03
--- /dev/null
+++ b/src/data/timeline.ts
@@ -0,0 +1,155 @@
+import { TimelineItemData } from '@/components/Timeline/types';
+
+// Timeline data representing career/project milestones
+const timelineData: TimelineItemData[] = [
+ {
+ id: 'timeline-1',
+ date: '2023',
+ title: 'Full Stack Developer',
+ description: 'Led development of a comprehensive e-commerce platform with advanced product management, shopping cart functionality, and secure payment processing.',
+ actionUrl: 'https://github.com/username/ecommerce-platform',
+ actionText: 'View Project',
+ items: [
+ {
+ id: 'masonry-1-1',
+ type: 'image',
+ content: 'E-commerce dashboard',
+ imageUrl: 'https://source.unsplash.com/random/800x600?ecommerce',
+ width: 100,
+ height: 200
+ },
+ {
+ id: 'masonry-1-2',
+ type: 'card',
+ title: 'Payment Processing',
+ content: 'Implemented secure payment processing with Stripe integration, supporting multiple payment methods and currencies.',
+ backgroundColor: '#f5f5f5',
+ width: 100
+ },
+ {
+ id: 'masonry-1-3',
+ type: 'text',
+ title: 'Tech Stack',
+ content: 'Technologies: React, Node.js, MongoDB, Express, Stripe API',
+ backgroundColor: '#e3f2fd',
+ width: 100
+ }
+ ]
+ },
+ {
+ id: 'timeline-2',
+ date: '2022',
+ title: 'Frontend Developer',
+ description: 'Designed and developed a responsive weather application providing real-time forecasts and historical weather data for locations worldwide.',
+ actionUrl: 'https://github.com/username/weather-app',
+ actionText: 'View Code',
+ items: [
+ {
+ id: 'masonry-2-1',
+ type: 'image',
+ content: 'Weather app interface',
+ imageUrl: 'https://source.unsplash.com/random/800x600?weather',
+ width: 100,
+ height: 180
+ },
+ {
+ id: 'masonry-2-2',
+ type: 'card',
+ content: 'Created an intuitive UI with interactive maps and data visualizations for weather patterns.',
+ backgroundColor: '#e8f5e9',
+ width: 100
+ }
+ ]
+ },
+ {
+ id: 'timeline-3',
+ date: '2021',
+ title: 'Backend Developer',
+ description: 'Developed a scalable task management system with features for task assignment, progress tracking, and automated notifications.',
+ actionUrl: 'https://github.com/username/task-management',
+ actionText: 'Explore Project',
+ items: [
+ {
+ id: 'masonry-3-1',
+ type: 'image',
+ content: 'Task management dashboard',
+ imageUrl: 'https://source.unsplash.com/random/800x600?tasks',
+ width: 100,
+ height: 200
+ },
+ {
+ id: 'masonry-3-2',
+ type: 'text',
+ content: 'Implemented real-time notifications and collaborative features using WebSockets.',
+ backgroundColor: '#fff3e0',
+ width: 100
+ },
+ {
+ id: 'masonry-3-3',
+ type: 'card',
+ content: 'Technologies: TypeScript, Node.js, MongoDB, Socket.io',
+ backgroundColor: '#f5f5f5',
+ width: 100
+ }
+ ]
+ },
+ {
+ id: 'timeline-4',
+ date: '2020',
+ title: 'UI/UX Designer',
+ description: 'Created a personal portfolio website with modern design principles, responsive layouts, and interactive elements.',
+ actionUrl: 'https://github.com/username/portfolio',
+ actionText: 'View Portfolio',
+ items: [
+ {
+ id: 'masonry-4-1',
+ type: 'image',
+ content: 'Portfolio homepage',
+ imageUrl: 'https://source.unsplash.com/random/800x600?portfolio',
+ width: 100,
+ height: 180
+ },
+ {
+ id: 'masonry-4-2',
+ type: 'card',
+ content: 'Designed with a focus on accessibility and performance optimization.',
+ backgroundColor: '#e0f7fa',
+ width: 100
+ }
+ ]
+ },
+ {
+ id: 'timeline-5',
+ date: '2019',
+ title: 'Mobile Developer',
+ description: 'Developed a cross-platform fitness tracking application that helps users monitor workouts, set goals, and track progress.',
+ actionUrl: 'https://github.com/username/fitness-tracker',
+ actionText: 'See Project',
+ items: [
+ {
+ id: 'masonry-5-1',
+ type: 'image',
+ content: 'Fitness tracker dashboard',
+ imageUrl: 'https://source.unsplash.com/random/800x600?fitness',
+ width: 100,
+ height: 200
+ },
+ {
+ id: 'masonry-5-2',
+ type: 'text',
+ content: 'Implemented data visualization for workout statistics and progress tracking.',
+ backgroundColor: '#f3e5f5',
+ width: 100
+ },
+ {
+ id: 'masonry-5-3',
+ type: 'card',
+ content: 'Technologies: React Native, Firebase, Redux, Chart.js',
+ backgroundColor: '#f5f5f5',
+ width: 100
+ }
+ ]
+ }
+];
+
+export default timelineData;
\ No newline at end of file