Merge pull request 'feat: added animations and navbar goodies' (#1) from feat-mobile-menu into main
All checks were successful
Build and Deploy Docker Image / build (push) Successful in 39s
All checks were successful
Build and Deploy Docker Image / build (push) Successful in 39s
Reviewed-on: #1
This commit is contained in:
commit
258d1e77c4
5 changed files with 195 additions and 67 deletions
38
src/App.jsx
38
src/App.jsx
|
@ -1,26 +1,32 @@
|
|||
// src/App.jsx
|
||||
import React from 'react';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import Header from './components/Header';
|
||||
import Footer from './components/Footer';
|
||||
import Home from './components/Home';
|
||||
import About from './components/About';
|
||||
import Roadmap from './components/Roadmap';
|
||||
|
||||
const App = () => (
|
||||
<Router>
|
||||
<Header />
|
||||
<main>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/roadmap" element={<Roadmap />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
</Router>
|
||||
);
|
||||
const App = () => {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const handleMenuToggle = (isOpen) => {
|
||||
setMenuOpen(isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Header onMenuToggle={handleMenuToggle} />
|
||||
<main className={`page-content ${menuOpen ? 'dropdown-active' : ''}`}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/roadmap" element={<Roadmap />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
|
@ -22,3 +22,29 @@
|
|||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
/* Header.css */
|
||||
@keyframes dropdown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-dropdown {
|
||||
animation: dropdown 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
transition: margin-top 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.page-content.dropdown-active {
|
||||
margin-top: 150px;
|
||||
/* Adjust based on the height of the dropdown menu */
|
||||
}
|
|
@ -1,17 +1,41 @@
|
|||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import './Header.css'; // Import the custom CSS file
|
||||
|
||||
const Header = () => (
|
||||
<header className="bg-gray-900 text-white p-4 flex justify-between items-center">
|
||||
<div className="text-2xl font-bold">
|
||||
<Link to="/" className="relative ">DoTechBro.org</Link>
|
||||
</div>
|
||||
<nav className="space-x-4">
|
||||
<Link to="/" className="relative link-underline">Home</Link>
|
||||
<Link to="/about" className="relative link-underline">About</Link>
|
||||
<Link to="/roadmap" className="relative link-underline">Roadmaps</Link>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
const Header = ({ onMenuToggle }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const toggleMenu = () => {
|
||||
setIsOpen(!isOpen);
|
||||
onMenuToggle(!isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-gray-900 text-white p-4 flex justify-between items-center relative">
|
||||
<div className="text-2xl font-bold">
|
||||
<Link to="/" className="relative">DoTechBro.org</Link>
|
||||
</div>
|
||||
<div className="md:hidden">
|
||||
<button onClick={toggleMenu} className="focus:outline-none">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d={isOpen ? "M6 18L18 6M6 6l12 12" : "M4 6h16M4 12h16m-7 6h7"}></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<nav className="hidden md:flex md:space-x-4">
|
||||
<Link to="/" className="link-underline">Home</Link>
|
||||
<Link to="/about" className="link-underline">About</Link>
|
||||
<Link to="/roadmap" className="link-underline">Roadmaps</Link>
|
||||
</nav>
|
||||
{isOpen && (
|
||||
<div className="absolute top-full left-0 w-full bg-gray-900 text-white flex flex-col items-center md:hidden animate-dropdown">
|
||||
<Link to="/" className="block py-2 link-underline" onClick={toggleMenu}>Home</Link>
|
||||
<Link to="/about" className="block py-2 link-underline" onClick={toggleMenu}>About</Link>
|
||||
<Link to="/roadmap" className="block py-2 link-underline" onClick={toggleMenu}>Roadmaps</Link>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
23
src/components/Home.css
Normal file
23
src/components/Home.css
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* Home.css */
|
||||
.page-content {
|
||||
transition: margin-top 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.page-content.dropdown-active {
|
||||
margin-top: 150px;
|
||||
/* Adjust based on the height of the dropdown menu */
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 1s ease-in-out forwards;
|
||||
}
|
|
@ -1,10 +1,40 @@
|
|||
import { useState } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import FeaturedRoadmaps from './FeaturedRoadmaps';
|
||||
import MailingListDialog from './MailingListDialog';
|
||||
import roadmaps from '../roadmaps.json';
|
||||
import './Home.css'; // Import the custom CSS file
|
||||
|
||||
const Home = () => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const sectionsRef = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('animate-fade-in');
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
sectionsRef.current.forEach((section) => {
|
||||
if (section) {
|
||||
observer.observe(section);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
sectionsRef.current.forEach((section) => {
|
||||
if (section) {
|
||||
observer.unobserve(section);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleDialogOpen = () => {
|
||||
setDialogOpen(true);
|
||||
|
@ -14,51 +44,70 @@ const Home = () => {
|
|||
setDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleMenuToggle = (isOpen) => {
|
||||
setMenuOpen(isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center w-full">
|
||||
<div className="w-full p-8 flex flex-col md:flex-row items-center justify-center min-h-screen bg-gray-100">
|
||||
<div className="md:w-1/2 flex flex-col items-center text-center">
|
||||
<h2 className="text-6xl font-bold leading-tight">
|
||||
Pivot into <span className="text-green-600">tech.</span>
|
||||
<br />
|
||||
<span className="font-semibold">with a head start</span>
|
||||
</h2>
|
||||
<p className="mt-4 text-lg max-w-2xl">
|
||||
Empowering minorities to overcome barriers and succeed in the tech industry with confidence.
|
||||
<div className={`page-content ${menuOpen ? 'dropdown-active' : ''}`}>
|
||||
<div className="flex flex-col items-center justify-center w-full">
|
||||
<div
|
||||
className="w-full p-8 flex flex-col md:flex-row items-center justify-center min-h-screen bg-gray-100 opacity-0"
|
||||
ref={(el) => (sectionsRef.current[0] = el)}
|
||||
>
|
||||
<div className="md:w-1/2 flex flex-col items-center text-center">
|
||||
<h2 className="text-6xl font-bold leading-tight">
|
||||
Pivot into <span className="text-green-600">tech.</span>
|
||||
<br />
|
||||
<span className="font-semibold">with a head start</span>
|
||||
</h2>
|
||||
<p className="mt-4 text-lg max-w-2xl">
|
||||
Empowering minorities to overcome barriers and succeed in the tech industry with confidence.
|
||||
</p>
|
||||
<button
|
||||
className="mt-8 px-6 py-3 bg-green-600 text-white text-lg font-semibold rounded hover:bg-green-700 transition duration-300"
|
||||
onClick={handleDialogOpen}
|
||||
>
|
||||
Start Today
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-8 md:mt-0 md:w-1/2 flex justify-center">
|
||||
<img src="/hero-logo.png" alt="Tech illustration" className="w-full max-w-md rounded-lg shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="w-full bg-gray-900 text-white py-16 opacity-0"
|
||||
ref={(el) => (sectionsRef.current[1] = el)}
|
||||
>
|
||||
<FeaturedRoadmaps roadmaps={roadmaps} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="w-full p-8 bg-white text-center my-16 opacity-0"
|
||||
ref={(el) => (sectionsRef.current[2] = el)}
|
||||
>
|
||||
<h2 className="text-4xl font-bold mb-8">Testimonials</h2>
|
||||
<p className="text-lg">Coming soon...</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="w-full p-8 bg-gray-900 text-white text-center opacity-0"
|
||||
ref={(el) => (sectionsRef.current[3] = el)}
|
||||
>
|
||||
<h2 className="text-4xl font-bold mb-8">Join Our Community</h2>
|
||||
<p className="text-lg max-w-2xl mx-auto">
|
||||
Connect with like-minded individuals, share your journey, and get support from our community.
|
||||
</p>
|
||||
<button onClick={handleDialogOpen} className="mt-8 px-6 py-3 bg-green-600 text-white text-lg font-semibold rounded hover:bg-green-700 transition duration-300">
|
||||
Start Today
|
||||
<button className="mt-8 px-6 py-3 bg-green-600 text-white text-lg font-semibold rounded hover:bg-green-700 transition duration-300">
|
||||
Join Now
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-8 md:mt-0 md:w-1/2 flex justify-center">
|
||||
<img src="/hero-logo.png" alt="Tech illustration" className="w-full max-w-md rounded-lg shadow-lg" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full bg-gray-900 text-white py-16">
|
||||
<FeaturedRoadmaps roadmaps={roadmaps} />
|
||||
<MailingListDialog open={dialogOpen} onClose={handleDialogClose} />
|
||||
</div>
|
||||
|
||||
<div className="w-full p-8 bg-white text-center my-16">
|
||||
<h2 className="text-4xl font-bold mb-8">Testimonials</h2>
|
||||
<p className="text-lg">Coming soon...</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full p-8 bg-gray-900 text-white text-center">
|
||||
<h2 className="text-4xl font-bold mb-8">Join Our Community</h2>
|
||||
<p className="text-lg max-w-2xl mx-auto">
|
||||
Connect with like-minded individuals, share your journey, and get support from our community.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleDialogOpen}
|
||||
className="mt-8 px-6 py-3 bg-green-600 text-white text-lg font-semibold rounded hover:bg-green-700 transition duration-300">
|
||||
Join Now
|
||||
</button>
|
||||
</div>
|
||||
<MailingListDialog open={dialogOpen} onClose={handleDialogClose} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
Loading…
Reference in a new issue