Task 10: Build Footer component with ECG decoration
- Created Footer.tsx with decorative ECG waveform SVG - Footer uses Framer Motion for scroll-triggered entrance animation - Centered layout with border-top, muted attribution text - Integrated Footer into App.tsx content phase - Three-phase orchestration (boot → ecg → content) already working
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"active": true,
|
"active": true,
|
||||||
"iteration": 8,
|
"iteration": 9,
|
||||||
"minIterations": 1,
|
"minIterations": 1,
|
||||||
"maxIterations": 0,
|
"maxIterations": 0,
|
||||||
"completionPromise": "COMPLETE",
|
"completionPromise": "COMPLETE",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Experience } from './components/Experience'
|
|||||||
import { Education } from './components/Education'
|
import { Education } from './components/Education'
|
||||||
import { Projects } from './components/Projects'
|
import { Projects } from './components/Projects'
|
||||||
import { Contact } from './components/Contact'
|
import { Contact } from './components/Contact'
|
||||||
|
import { Footer } from './components/Footer'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [phase, setPhase] = useState<Phase>('boot')
|
const [phase, setPhase] = useState<Phase>('boot')
|
||||||
@@ -39,6 +40,7 @@ function App() {
|
|||||||
|
|
||||||
<Contact />
|
<Contact />
|
||||||
</main>
|
</main>
|
||||||
|
<Footer />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
const Footer: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<motion.footer
|
||||||
|
initial={{ opacity: 0, y: 16 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true, margin: '-50px' }}
|
||||||
|
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||||
|
className="text-center pt-12 pb-8 border-t border-slate-200"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="block mx-auto mb-3"
|
||||||
|
width="120"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 120 20"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M 0 10 L 35 10 L 40 10 C 42 10 43 7 45 7 C 47 7 48 10 50 10 L 54 10 L 56 13 L 60 2 L 64 15 L 66 10 L 70 10 C 72 10 73 7 75 7 C 77 7 78 10 80 10 L 120 10"
|
||||||
|
stroke="#00897B"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
opacity="0.3"
|
||||||
|
fill="none"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<p className="font-secondary text-xs text-muted">
|
||||||
|
Andy Charlwood — MPharm, GPhC Registered Pharmacist
|
||||||
|
</p>
|
||||||
|
</motion.footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Footer }
|
||||||
Reference in New Issue
Block a user