Files
soroushasadi/components/sections/Expertise.tsx
T
soroush.asadi add78d8460
ci / build (push) Failing after 23s
deploy / deploy (push) Failing after 10m12s
first commit
2026-05-31 12:47:02 +03:30

103 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { useLocale } from '@/lib/i18n/locale-context';
import { SectionHeader } from '@/components/ui/SectionHeader';
import { Counter } from '@/components/ui/Counter';
import { cn } from '@/lib/utils';
const FA_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const;
const toFa = (s: string) =>
s.replace(/\d/g, (d) => FA_DIGITS[Number(d)]);
export function Expertise() {
const { t, locale } = useLocale();
const barsRef = useRef<HTMLDivElement>(null);
const inView = useInView(barsRef, { once: true, margin: '-80px' });
return (
<section id="expertise" className="relative px-5 py-28 sm:px-8">
<div className="mx-auto max-w-7xl">
<SectionHeader
eyebrow={t.expertise.eyebrow}
title={t.expertise.title}
sub={t.expertise.sub}
/>
<div className="mt-14 grid grid-cols-1 gap-10 lg:grid-cols-2">
{/* Metric tiles */}
<div className="grid grid-cols-2 gap-4 self-start">
{t.hero.metrics.map((m, i) => (
<motion.div
key={m.label}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-60px' }}
transition={{
duration: 0.5,
ease: [0.22, 1, 0.36, 1],
delay: 0.05 * i,
}}
className="glass relative overflow-hidden p-6"
>
<span
aria-hidden
className={cn(
'absolute inset-x-0 top-0 h-px',
'bg-gradient-to-r from-transparent via-electric/60 to-transparent',
)}
/>
<div
className={cn(
'font-display text-[clamp(1.8rem,3.5vw,2.6rem)] font-bold leading-none',
['text-electric', 'text-violet', 'text-magenta', 'text-emerald'][i % 4],
)}
>
<Counter value={m.value} locale={locale} />
</div>
<div className="mt-3 text-sm leading-snug text-slate-400">
{m.label}
</div>
</motion.div>
))}
</div>
{/* Skill bars */}
<div ref={barsRef} className="glass relative p-7 sm:p-8">
<span
aria-hidden
className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-magenta/60 to-transparent"
/>
<ul className="flex flex-col gap-6">
{t.expertise.bars.map((b, i) => (
<li key={b.label}>
<div className="mb-2 flex items-baseline justify-between text-sm">
<span className="text-slate-200">{b.label}</span>
<span className="font-mono text-xs text-slate-400">
{locale === 'fa' ? toFa(b.value.toString()) + '٪' : `${b.value}%`}
</span>
</div>
<div className="relative h-1.5 overflow-hidden rounded-full bg-white/[0.05]">
<motion.div
initial={{ width: 0 }}
animate={inView ? { width: `${b.value}%` } : { width: 0 }}
transition={{
duration: 1.2,
ease: [0.22, 1, 0.36, 1],
delay: 0.08 * i,
}}
className="absolute inset-y-0 start-0 rounded-full bg-brand-gradient"
style={{ backgroundSize: '200% 200%' }}
/>
</div>
</li>
))}
</ul>
</div>
</div>
</div>
</section>
);
}