Enhance About Me page with modern components and animations
- Replaced existing widgets with modern versions for Work Experience, Certifications, Skills, and Education sections to improve visual appeal and interactivity. - Introduced an animated hero background and enhanced call-to-action buttons for better user engagement. - Implemented staggered animations for content sections to create a more dynamic user experience. - Updated styles for a cohesive glassmorphism effect across all sections, enhancing the overall aesthetic of the page.
This commit is contained in:
224
src/components/widgets/ModernWorkExperience.astro
Normal file
224
src/components/widgets/ModernWorkExperience.astro
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
import WidgetWrapper from '~/components/ui/WidgetWrapper.astro';
|
||||
import Headline from '~/components/ui/Headline.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import type { Widget } from '~/types';
|
||||
|
||||
export interface Props extends Widget {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
tagline?: string;
|
||||
compact?: boolean;
|
||||
items?: Array<{
|
||||
title: string;
|
||||
description?: string;
|
||||
company?: string;
|
||||
date?: string;
|
||||
location?: string;
|
||||
icon?: string;
|
||||
classes?: Record<string, string>;
|
||||
}>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = 'Work Experience',
|
||||
subtitle = 'My professional journey',
|
||||
tagline = '',
|
||||
compact = false,
|
||||
items = [],
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = '',
|
||||
} = Astro.props as Props;
|
||||
|
||||
// Function to get gradient based on company or role
|
||||
const getWorkGradient = (company: string = '', title: string = '') => {
|
||||
const companyLower = company.toLowerCase();
|
||||
const titleLower = title.toLowerCase();
|
||||
|
||||
if (companyLower.includes('cofra')) return 'from-blue-600 to-indigo-700';
|
||||
if (companyLower.includes('hyva')) return 'from-green-600 to-teal-700';
|
||||
if (companyLower.includes('bergsma')) return 'from-purple-600 to-pink-700';
|
||||
if (companyLower.includes('allseas')) return 'from-orange-600 to-red-700';
|
||||
if (companyLower.includes('oz export')) return 'from-cyan-600 to-blue-700';
|
||||
|
||||
// Fallback based on role level
|
||||
if (titleLower.includes('manager')) return 'from-indigo-600 to-purple-700';
|
||||
if (titleLower.includes('professional')) return 'from-blue-600 to-cyan-700';
|
||||
if (titleLower.includes('engineer')) return 'from-green-600 to-blue-700';
|
||||
if (titleLower.includes('consultant')) return 'from-purple-600 to-indigo-700';
|
||||
if (titleLower.includes('administrator')) return 'from-gray-600 to-slate-700';
|
||||
|
||||
return 'from-gray-600 to-gray-700';
|
||||
};
|
||||
|
||||
// Function to get appropriate icon
|
||||
const getWorkIcon = (title: string = '') => {
|
||||
const titleLower = title.toLowerCase();
|
||||
|
||||
if (titleLower.includes('manager')) return 'tabler:user-star';
|
||||
if (titleLower.includes('professional')) return 'tabler:certificate';
|
||||
if (titleLower.includes('engineer')) return 'tabler:code';
|
||||
if (titleLower.includes('consultant')) return 'tabler:user-check';
|
||||
if (titleLower.includes('administrator')) return 'tabler:settings';
|
||||
|
||||
return 'tabler:briefcase';
|
||||
};
|
||||
|
||||
// Function to extract years from date range
|
||||
const getYearRange = (dateStr: string = '') => {
|
||||
if (!dateStr) return '';
|
||||
|
||||
// Handle formats like "02-2025 - Present" or "04-2018 - 09-2018"
|
||||
const match = dateStr.match(/(\d{2})-(\d{4})\s*-\s*(.+)/);
|
||||
if (match) {
|
||||
const startYear = match[2];
|
||||
const endPart = match[3].trim();
|
||||
|
||||
if (endPart.toLowerCase().includes('present') || endPart.toLowerCase().includes('heden')) {
|
||||
return `${startYear} - Present`;
|
||||
} else {
|
||||
const endMatch = endPart.match(/(\d{2})-(\d{4})/);
|
||||
if (endMatch) {
|
||||
return `${startYear} - ${endMatch[2]}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dateStr;
|
||||
};
|
||||
---
|
||||
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={`max-w-7xl mx-auto ${classes?.container ?? ''}`} bg={bg}>
|
||||
<Headline
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
tagline={tagline}
|
||||
classes={{
|
||||
container: 'max-w-3xl',
|
||||
title: 'text-3xl lg:text-4xl',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-8">
|
||||
{items.map((item, index) => {
|
||||
const gradient = getWorkGradient(item.company, item.title);
|
||||
const icon = getWorkIcon(item.title);
|
||||
const yearRange = getYearRange(item.date);
|
||||
|
||||
return (
|
||||
<div
|
||||
class="work-card group bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm rounded-2xl p-6 transition-all duration-300 cursor-default border border-gray-100 dark:border-slate-800 hover:transform hover:scale-[1.02] hover:shadow-xl relative overflow-hidden"
|
||||
>
|
||||
<!-- Top gradient bar -->
|
||||
<div class={`absolute top-0 left-0 right-0 h-1 bg-gradient-to-r ${gradient}`}></div>
|
||||
|
||||
<!-- Header with Icon, Title & Company -->
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class={`flex-shrink-0 w-12 h-12 rounded-xl bg-gradient-to-r ${gradient} flex items-center justify-center group-hover:scale-110 transition-transform duration-300 shadow-lg`}>
|
||||
<Icon name={icon} class="w-6 h-6 text-white" />
|
||||
</div>
|
||||
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-lg text-gray-900 dark:text-white mb-1 group-hover:text-transparent group-hover:bg-clip-text group-hover:bg-gradient-to-r group-hover:from-blue-600 group-hover:to-purple-600 transition-all duration-300 leading-snug">
|
||||
{item.title}
|
||||
</h3>
|
||||
{item.company && (
|
||||
<p class={`font-medium text-transparent bg-clip-text bg-gradient-to-r ${gradient} text-sm mb-1`}>
|
||||
{item.company}
|
||||
</p>
|
||||
)}
|
||||
<div class="flex flex-wrap gap-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
{yearRange && (
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon name="tabler:calendar" class="w-3 h-3" />
|
||||
{yearRange}
|
||||
</span>
|
||||
)}
|
||||
{item.location && (
|
||||
<span class="flex items-center gap-1">
|
||||
<Icon name="tabler:map-pin" class="w-3 h-3" />
|
||||
{item.location}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
{item.description && (
|
||||
<div class="work-description">
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<!-- Shimmer effect -->
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-700 ease-out"></div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</WidgetWrapper>
|
||||
|
||||
<style>
|
||||
.work-card {
|
||||
animation: fadeInUp 0.6s ease-out forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
.work-card:nth-child(1) { animation-delay: 0.1s; }
|
||||
.work-card:nth-child(2) { animation-delay: 0.15s; }
|
||||
.work-card:nth-child(3) { animation-delay: 0.2s; }
|
||||
.work-card:nth-child(4) { animation-delay: 0.25s; }
|
||||
.work-card:nth-child(5) { animation-delay: 0.3s; }
|
||||
.work-card:nth-child(6) { animation-delay: 0.35s; }
|
||||
|
||||
@keyframes fadeInUp {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced description styling */
|
||||
.work-description {
|
||||
max-height: 120px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
|
||||
.work-card:hover .work-description {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 1024px) {
|
||||
.work-card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.work-card {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.work-card .flex {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.work-card .w-12.h-12 {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
|
||||
.work-card .w-6.h-6 {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user