@@ -3,6 +3,7 @@ import { Github, Linkedin, Mail, Download, ExternalLink } from 'lucide-react';
33
44export default function Portfolio ( ) {
55 const [ theme , setTheme ] = useState ( 'light' ) ;
6+ const [ copied , setCopied ] = useState ( false ) ;
67
78 const bg = theme === 'light' ? 'bg-white' : 'bg-neutral-900' ;
89 const text = theme === 'light' ? 'text-neutral-900' : 'text-neutral-100' ;
@@ -61,40 +62,92 @@ export default function Portfolio() {
6162
6263 return (
6364 < div className = { `min-h-screen ${ bg } ${ text } transition-colors duration-300` } >
64- < button
65- onClick = { ( ) => setTheme ( theme === 'light' ? 'dark' : 'light' ) }
66- className = { `fixed bottom-6 left-6 z-50 w-12 h-12 rounded-full ${ accent } ${ accentText } flex items-center justify-center text-xs font-mono hover:scale-110 transition-transform shadow-lg` }
67- aria-label = "Toggle theme"
68- >
69- { theme === 'light' ? '●' : '○' }
70- </ button >
7165
72- { /* Header */ }
73- < header className = { `border-b ${ border } py-8 px-6` } >
74- < div className = "max-w-5xl mx-auto" >
75- < div className = "flex items-start justify-between" >
76- < div >
77- < h1 className = "text-4xl font-mono font-bold mb-2" > Emmet Hoversten</ h1 >
78- < p className = { `${ textMuted } font-mono text-sm` } > AI Engineer / Software Developer</ p >
79- </ div >
80- < div className = "flex gap-3" >
81- < a href = "https://github.com/ejhover" target = "_blank" rel = "noopener noreferrer"
82- className = { `${ textMuted } hover:${ text } transition-colors` } >
83- < Github size = { 20 } />
84- </ a >
85- < a href = "https://www.linkedin.com/in/emmet-hoversten-59929a173/" target = "_blank" rel = "noopener noreferrer"
86- className = { `${ textMuted } hover:${ text } transition-colors` } >
87- < Linkedin size = { 20 } />
88- </ a >
89- < a href = "mailto:emmethoversten@gmail.com"
90- className = { `${ textMuted } hover:${ text } transition-colors` } >
66+ { /* navbar/header */ }
67+ < header className = { `border-b ${ border } py-8 px-6 relative` } >
68+ < div className = "max-w-5xl mx-auto flex items-center justify-between relative" >
69+
70+ { /* name/title */ }
71+ < div className = "flex flex-col" >
72+ < h1 className = "text-4xl font-mono font-bold mb-2" > Emmet Hoversten</ h1 >
73+ < p className = { `${ textMuted } font-mono text-sm` } > AI Engineer / Software Developer</ p >
74+ </ div >
75+
76+ { /* lightswitch */ }
77+ < div className = "absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2" >
78+ < button
79+ onClick = { ( ) => setTheme ( theme === 'light' ? 'dark' : 'light' ) }
80+ className = "w-10 h-16 bg-neutral-100 rounded-md shadow-lg border border-neutral-300 flex items-center justify-center active:scale-95 transition-transform"
81+ aria-label = "Toggle theme"
82+ >
83+ { /* screws */ }
84+ < div className = "absolute top-3 w-1 h-1 bg-neutral-400 rounded-full shadow-inner" />
85+ < div className = "absolute bottom-3 w-1 h-1 bg-neutral-400 rounded-full shadow-inner" />
86+
87+ { /* slot background */ }
88+ < div className = "relative w-3 h-10 bg-neutral-300 rounded-sm flex items-center justify-center shadow-inner" >
89+ { /* lever */ }
90+ < div
91+ className = { `
92+ absolute w-3 h-5 bg-white rounded-sm shadow-md
93+ transition-all duration-300 ease-in-out
94+ origin-center
95+ ${ theme === 'light' ? '-translate-y-4' : 'translate-y-4' }
96+ ` }
97+ />
98+ </ div >
99+ </ button >
100+ </ div >
101+
102+ { /* contact icons */ }
103+ < div className = "flex items-center gap-4" >
104+ < div className = "group relative flex items-center px-2 py-2" >
105+ { /* email animation */ }
106+ < div className = "overflow-hidden flex justify-end max-w-0 group-hover:max-w-xs transition-all duration-500 ease-out" >
107+ < button
108+ onClick = { ( ) => {
109+ navigator . clipboard . writeText ( "emmethoversten@gmail.com" ) ;
110+ setCopied ( true ) ;
111+ setTimeout ( ( ) => setCopied ( false ) , 1500 ) ;
112+ } }
113+ className = { `mr-3 text-sm font-mono ${ text } whitespace-nowrap overflow-hidden border-r-2 border-current opacity-0 group-hover:opacity-100 transition-opacity duration-200 delay-200 group-hover:animate-typing` }
114+ >
115+ emmethoversten [at] gmail [dot] com
116+ </ button >
117+ </ div >
118+
119+ { /* mail icon */ }
120+ < button
121+ onClick = { ( ) => {
122+ navigator . clipboard . writeText ( "emmethoversten@gmail.com" ) ;
123+ setCopied ( true ) ;
124+ setTimeout ( ( ) => setCopied ( false ) , 1500 ) ;
125+ } }
126+ className = { `${ textMuted } hover:${ text } transition-all duration-300` }
127+ >
91128 < Mail size = { 20 } />
92- </ a >
129+ </ button >
130+
131+ { /* copy to clipboard animation */ }
132+ < div className = { `absolute mt-10 text-xs font-mono opacity-0 group-hover:opacity-100 transition-opacity duration-200 ${ textMuted } ` } >
133+ { copied ? "Copied!" : "Click to copy" }
134+ </ div >
93135 </ div >
136+
137+ { /* github */ }
138+ < a href = "https://github.com/ejhover" target = "_blank" rel = "noopener noreferrer" className = { `${ textMuted } hover:${ text } transition-all duration-300 px-2` } >
139+ < Github size = { 20 } />
140+ </ a >
141+
142+ { /* linkedin */ }
143+ < a href = "https://www.linkedin.com/in/emmet-hoversten-59929a173/" target = "_blank" rel = "noopener noreferrer" className = { `${ textMuted } hover:${ text } transition-all duration-300 px-2` } >
144+ < Linkedin size = { 20 } />
145+ </ a >
94146 </ div >
95147 </ div >
96148 </ header >
97149
150+
98151 < main className = "max-w-5xl mx-auto px-6 py-16" >
99152 { /* profile */ }
100153 < section className = "mb-20" >
0 commit comments