|
1 | 1 | import { BarChart3, Users, Search, Zap, Shield, Globe } from 'lucide-react'; |
| 2 | +import type { CSSProperties, ReactNode } from 'react'; |
| 3 | + |
| 4 | +type TerminalHeadingProps = { |
| 5 | + title: string; |
| 6 | + as?: 'h2' | 'h3' | 'h4'; |
| 7 | + className?: string; |
| 8 | + promptClassName?: string; |
| 9 | + titleClassName?: string; |
| 10 | + animated?: boolean; |
| 11 | +}; |
| 12 | + |
| 13 | +type TerminalCardProps = { |
| 14 | + label: string; |
| 15 | + children: ReactNode; |
| 16 | + className?: string; |
| 17 | + contentClassName?: string; |
| 18 | + accent?: 'blue' | 'green'; |
| 19 | +}; |
| 20 | + |
| 21 | +const headingSizes: Record<NonNullable<TerminalHeadingProps['as']>, string> = { |
| 22 | + h2: 'text-3xl md:text-4xl', |
| 23 | + h3: 'text-xl md:text-2xl', |
| 24 | + h4: 'text-lg md:text-xl', |
| 25 | +}; |
| 26 | + |
| 27 | +const TerminalHeading = ({ |
| 28 | + title, |
| 29 | + as: HeadingTag = 'h2', |
| 30 | + className = '', |
| 31 | + promptClassName = '', |
| 32 | + titleClassName = '', |
| 33 | + animated = false, |
| 34 | +}: TerminalHeadingProps) => { |
| 35 | + const chars = title.length + 2; |
| 36 | + |
| 37 | + return ( |
| 38 | + <HeadingTag |
| 39 | + className={`inline-flex items-center gap-2 text-balance text-[#c9d1d9] ${headingSizes[HeadingTag]} ${className}`} |
| 40 | + style={animated ? ({ '--terminal-chars': chars } as CSSProperties) : undefined} |
| 41 | + > |
| 42 | + <span |
| 43 | + className={`font-["VT323"] text-[#58a6ff] ${animated ? 'terminal-typewriter inline-block' : ''} ${promptClassName}`} |
| 44 | + > |
| 45 | + > |
| 46 | + </span> |
| 47 | + <span |
| 48 | + className={`font-["VT323"] tracking-[0.03em] ${animated ? 'terminal-typewriter inline-block' : ''} ${titleClassName}`} |
| 49 | + > |
| 50 | + {title} |
| 51 | + </span> |
| 52 | + </HeadingTag> |
| 53 | + ); |
| 54 | +}; |
| 55 | + |
| 56 | +const TerminalCard = ({ |
| 57 | + label, |
| 58 | + children, |
| 59 | + className = '', |
| 60 | + contentClassName = '', |
| 61 | + accent = 'blue', |
| 62 | +}: TerminalCardProps) => { |
| 63 | + const accentClasses = accent === 'green' ? 'text-[#3fb950] border-[#3fb950]/30' : 'text-[#58a6ff] border-[#58a6ff]/30'; |
| 64 | + |
| 65 | + return ( |
| 66 | + <article |
| 67 | + className={`group relative overflow-hidden rounded-2xl border border-[#30363d] bg-[#161b22] shadow-[0_12px_30px_rgba(1,4,9,0.22)] transition-all duration-300 ease-out hover:-translate-y-1 hover:border-[#58a6ff]/40 hover:shadow-[0_18px_38px_rgba(1,4,9,0.35)] ${className}`} |
| 68 | + > |
| 69 | + <div className="flex items-center justify-between gap-4 border-b border-[#30363d] bg-[#0d1117]/90 px-4 py-3"> |
| 70 | + <div className="flex items-center gap-1.5" aria-hidden="true"> |
| 71 | + <span className="h-2.5 w-2.5 rounded-full bg-[#f85149]/80" /> |
| 72 | + <span className="h-2.5 w-2.5 rounded-full bg-[#d29922]/80" /> |
| 73 | + <span className="h-2.5 w-2.5 rounded-full bg-[#3fb950]/80" /> |
| 74 | + </div> |
| 75 | + |
| 76 | + <div className={`flex items-center gap-2 font-["VT323"] text-sm uppercase tracking-[0.22em] ${accentClasses}`}> |
| 77 | + <span>{label}</span> |
| 78 | + <span className="terminal-card-cursor opacity-0 transition-opacity duration-200 group-hover:opacity-100">_</span> |
| 79 | + </div> |
| 80 | + </div> |
| 81 | + |
| 82 | + <div className={`relative px-5 py-5 sm:px-6 sm:py-6 ${contentClassName}`}> |
| 83 | + <div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/5 to-transparent" /> |
| 84 | + {children} |
| 85 | + </div> |
| 86 | + </article> |
| 87 | + ); |
| 88 | +}; |
2 | 89 |
|
3 | 90 | const Features = () => { |
4 | 91 | const features = [ |
@@ -59,28 +146,67 @@ const Features = () => { |
59 | 146 | ]; |
60 | 147 |
|
61 | 148 | return ( |
62 | | - <section id="features" className="px-6 py-6 bg-white dark:bg-gray-900 transition-colors duration-300"> |
63 | | - <div className="mx-auto"> |
64 | | - <div className="text-center mb-16"> |
65 | | - <h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Powerful Features</h2> |
66 | | - <p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto"> |
67 | | - Everything you need to track, analyze, and understand GitHub activity patterns |
| 149 | + <section |
| 150 | + id="features" |
| 151 | + className="relative left-1/2 right-1/2 w-screen -mx-[50vw] overflow-hidden bg-[linear-gradient(to_bottom,#f8fafc_0%,#dbe7f5_20%,#0d1117_100%)] px-6 py-16 text-[#c9d1d9] transition-colors duration-300 sm:py-20" |
| 152 | + style={{ |
| 153 | + boxShadow: '0 -40px 80px rgba(88,166,255,0.08), 0 40px 80px rgba(88,166,255,0.05)', |
| 154 | + }} |
| 155 | + > |
| 156 | + <div className="absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(88,166,255,0.08),_transparent_32%),radial-gradient(circle_at_bottom_right,_rgba(63,185,80,0.05),_transparent_28%)] pointer-events-none" /> |
| 157 | + <div className="absolute inset-x-0 top-0 h-28 bg-gradient-to-b from-[#f8fafc]/75 via-[#dbe7f5]/20 to-transparent pointer-events-none" /> |
| 158 | + <div className="absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-[#0d1117] via-[#0d1117]/70 to-transparent pointer-events-none" /> |
| 159 | + <div className="mx-auto max-w-7xl relative"> |
| 160 | + <div className="mx-auto mb-10 max-w-3xl text-center sm:mb-12"> |
| 161 | + <p className="font-['VT323'] text-sm uppercase tracking-[0.32em] text-[#58a6ff] sm:text-base"> |
| 162 | + > initialize tracking capabilities |
| 163 | + </p> |
| 164 | + <h2 className="mt-3 text-3xl font-semibold tracking-tight text-black md:text-4xl"> |
| 165 | + Powerful Features |
| 166 | + </h2> |
| 167 | + <p className="mx-auto mt-4 max-w-2xl text-sm leading-relaxed text-[#8b949e] sm:text-base"> |
| 168 | + Everything you need to track, analyze, and understand GitHub activity patterns. |
68 | 169 | </p> |
69 | 170 | </div> |
70 | 171 |
|
71 | | - <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> |
| 172 | + <div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3"> |
72 | 173 | {features.map((feature, index) => { |
73 | 174 | const IconComponent = feature.icon; |
| 175 | + const promptLabel = `feature-${String(index + 1).padStart(2, '0')}`; |
| 176 | + const titleTone = index % 2 === 0 ? 'blue' : 'green'; |
74 | 177 | return ( |
75 | | - <div key={index} className={`group h-72 w-full bg-gray-100 dark:bg-gray-800 ${feature.hoverColor} ${feature.borderColor} rounded-2xl shadow-md hover:shadow-2xl hover:shadow-blue-500/20 border dark:border-gray-800 transform hover:-translate-y-3 hover:scale-105 backdrop-blur-sm transition-all duration-300 ease-linear p-6`}> |
76 | | - <div className={`${feature.bgColor} w-12 h-12 rounded-lg flex items-center justify-center mb-6 transition-transform duration-300 group-hover:rotate-6 group-hover:scale-110`}> |
77 | | - <IconComponent className={`h-6 w-6 ${feature.iconColor}`} /> |
| 178 | + <TerminalCard key={index} label={promptLabel} accent={titleTone} className="h-full"> |
| 179 | + <div className="flex h-full flex-col gap-4"> |
| 180 | + <div className="flex items-start gap-4"> |
| 181 | + <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-[#30363d] bg-[#0d1117] text-[#58a6ff] transition-transform duration-300 group-hover:-translate-y-0.5 group-hover:scale-105"> |
| 182 | + <IconComponent className="h-5 w-5" /> |
| 183 | + </div> |
| 184 | + <div className="min-w-0 flex-1 space-y-2"> |
| 185 | + <TerminalHeading |
| 186 | + title={feature.title} |
| 187 | + as="h3" |
| 188 | + className="items-start" |
| 189 | + titleClassName="text-[#f0f6fc]" |
| 190 | + /> |
| 191 | + <div className="flex flex-wrap items-center gap-2 text-[11px] uppercase tracking-[0.24em] text-[#8b949e]"> |
| 192 | + <span className="rounded-full border border-[#30363d] bg-[#0d1117] px-2.5 py-1 font-['VT323'] text-[#58a6ff]"> |
| 193 | + cmd |
| 194 | + </span> |
| 195 | + <span className="font-['VT323']">gh tracker --feature</span> |
| 196 | + </div> |
| 197 | + </div> |
| 198 | + </div> |
| 199 | + |
| 200 | + <p className="text-sm leading-relaxed text-[#8b949e] sm:text-[0.95rem]"> |
| 201 | + {feature.description} |
| 202 | + </p> |
| 203 | + |
| 204 | + <div className="mt-auto flex items-center justify-between border-t border-[#30363d] pt-4 text-xs text-[#8b949e]"> |
| 205 | + <span className="font-['VT323'] uppercase tracking-[0.2em] text-[#3fb950]">ready</span> |
| 206 | + <span className="font-['VT323'] text-[#58a6ff]">{feature.title.toLowerCase().replace(/\s+/g, '-')}</span> |
| 207 | + </div> |
78 | 208 | </div> |
79 | | - <h3 className=" text-2xl font-bold text-gray-900 dark:text-gray-100 group-hover:text-black dark:group-hover:text-white transition-colors duration-300">{feature.title}</h3> |
80 | | - <p className="text-gray-600 dark:text-gray-300 text-base font-semibold leading-relaxed group-hover:text-gray-700 dark:group-hover:text-gray-200 transition-colors duration-300"> |
81 | | - {feature.description} |
82 | | - </p> |
83 | | - </div> |
| 209 | + </TerminalCard> |
84 | 210 | ); |
85 | 211 | })} |
86 | 212 | </div> |
|
0 commit comments