mirror of
https://github.com/harivansh-afk/React-Portfolio.git
synced 2026-04-15 09:01:17 +00:00
initial commit
This commit is contained in:
commit
fb5b992b17
35 changed files with 11573 additions and 0 deletions
42
src/app/App.css
Normal file
42
src/app/App.css
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
@media only screen and (max-width: 991px) {
|
||||
.s_c {
|
||||
padding-top: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-enter {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.page-enter-active {
|
||||
transform: translateY(0%);
|
||||
transition: all 400ms ease-out;
|
||||
}
|
||||
|
||||
.page-exit {
|
||||
transform: translateY(0%);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.page-exit-active {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transform: translateY(-130%);
|
||||
transition: all 400ms ease-out;
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
.container,
|
||||
.container-lg,
|
||||
.container-md,
|
||||
.container-sm,
|
||||
.container-xl,
|
||||
.container-xxl {
|
||||
max-width: 1140px;
|
||||
}
|
||||
}
|
||||
41
src/app/App.js
Normal file
41
src/app/App.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React, { useEffect } from "react";
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
useLocation,
|
||||
} from "react-router-dom";
|
||||
import withRouter from "../hooks/withRouter";
|
||||
import AppRoutes from "./routes";
|
||||
import Headermain from "../header";
|
||||
import AnimatedCursor from "../hooks/AnimatedCursor";
|
||||
import "./App.css";
|
||||
|
||||
function _ScrollToTop(props) {
|
||||
const { pathname } = useLocation();
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, [pathname]);
|
||||
return props.children;
|
||||
}
|
||||
const ScrollToTop = withRouter(_ScrollToTop);
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<Router basename={process.env.PUBLIC_URL}>
|
||||
<div className="cursor__dot">
|
||||
<AnimatedCursor
|
||||
innerSize={15}
|
||||
outerSize={15}
|
||||
color="255, 255 ,255"
|
||||
outerAlpha={0.4}
|
||||
innerScale={0.7}
|
||||
outerScale={5}
|
||||
/>
|
||||
</div>
|
||||
<ScrollToTop>
|
||||
<Headermain />
|
||||
<AppRoutes />
|
||||
</ScrollToTop>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
42
src/app/routes.js
Normal file
42
src/app/routes.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import React from "react";
|
||||
import { Route, Routes} from "react-router-dom";
|
||||
import withRouter from "../hooks/withRouter"
|
||||
import { Home } from "../pages/home";
|
||||
import { Portfolio } from "../pages/portfolio";
|
||||
import { ContactUs } from "../pages/contact";
|
||||
import { About } from "../pages/about";
|
||||
import { Socialicons } from "../components/socialicons";
|
||||
import { CSSTransition, TransitionGroup } from "react-transition-group";
|
||||
|
||||
const AnimatedRoutes = withRouter(({ location }) => (
|
||||
<TransitionGroup>
|
||||
<CSSTransition
|
||||
key={location.key}
|
||||
timeout={{
|
||||
enter: 400,
|
||||
exit: 400,
|
||||
}}
|
||||
classNames="page"
|
||||
unmountOnExit
|
||||
>
|
||||
<Routes location={location}>
|
||||
<Route exact path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/portfolio" element={<Portfolio />} />
|
||||
<Route path="/contact" element={<ContactUs />} />
|
||||
<Route path="*" element={<Home />} />
|
||||
</Routes>
|
||||
</CSSTransition>
|
||||
</TransitionGroup>
|
||||
));
|
||||
|
||||
function AppRoutes() {
|
||||
return (
|
||||
<div className="s_c">
|
||||
<AnimatedRoutes />
|
||||
<Socialicons />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AppRoutes;
|
||||
BIN
src/assets/images/Image with Background Removed.png
Normal file
BIN
src/assets/images/Image with Background Removed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 225 KiB |
37
src/assets/images/logo.svg
Normal file
37
src/assets/images/logo.svg
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="2em"
|
||||
height="2em"
|
||||
x="0"
|
||||
y="0"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<g>
|
||||
<g xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<circle
|
||||
cx="256"
|
||||
cy="256"
|
||||
fill="#ffffff"
|
||||
r="256"
|
||||
data-original="#ff3333"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="m374.02 119.205v155.57c0 65.08-52.94 118.02-118.02 118.02s-118.02-52.94-118.02-118.02v-155.57h58v155.57c0 33.09 26.93 60.02 60.02 60.02s60.02-26.93 60.02-60.02v-155.57z"
|
||||
fill="#000000"
|
||||
data-original="#f8fffb"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="m374.02 119.2v155.58c0 65.08-52.94 118.02-118.02 118.02v-58c33.09 0 60.02-26.94 60.02-60.02v-155.58z"
|
||||
fill="#000000"
|
||||
data-original="#d8d8d8"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
49
src/components/socialicons/index.js
Normal file
49
src/components/socialicons/index.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import React from "react";
|
||||
import "./style.css";
|
||||
import {
|
||||
FaGithub,
|
||||
FaTwitter,
|
||||
FaFacebookF,
|
||||
FaLinkedin,
|
||||
FaYoutube,
|
||||
FaTwitch,
|
||||
FaInstagram,
|
||||
FaSnapchatGhost,
|
||||
FaTiktok,
|
||||
FaFileAlt
|
||||
} from "react-icons/fa";
|
||||
import { socialprofils } from "../../content_option";
|
||||
|
||||
const ICON_MAPPING = {
|
||||
default: FaFileAlt,
|
||||
facebook: FaFacebookF,
|
||||
github: FaGithub,
|
||||
instagram: FaInstagram,
|
||||
linkedin: FaLinkedin,
|
||||
snapchat: FaSnapchatGhost,
|
||||
tiktok: FaTiktok,
|
||||
twitter: FaTwitter,
|
||||
twitch: FaTwitch,
|
||||
youtube: FaYoutube,
|
||||
resume: FaFileAlt
|
||||
};
|
||||
|
||||
export const Socialicons = (params) => {
|
||||
return (
|
||||
<div className="stick_follow_icon">
|
||||
<ul>
|
||||
{Object.entries(socialprofils).map(([platform, url]) => {
|
||||
const IconComponent = ICON_MAPPING[platform] || ICON_MAPPING.default;
|
||||
return (
|
||||
<li key={platform}>
|
||||
<a href={url}>
|
||||
<IconComponent />
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<p>Follow Me</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
86
src/components/socialicons/style.css
Normal file
86
src/components/socialicons/style.css
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
.stick_follow_icon {
|
||||
top: 50%;
|
||||
left: 30px;
|
||||
width: 20px;
|
||||
height: 200px;
|
||||
position: fixed;
|
||||
margin-top: -100px;
|
||||
}
|
||||
|
||||
.stick_follow_icon ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.stick_follow_icon svg {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
fill: var(--text-color)
|
||||
}
|
||||
|
||||
.stick_follow_icon p {
|
||||
top: 70px;
|
||||
left: -24px;
|
||||
width: 68px;
|
||||
height: 20px;
|
||||
color: var(--text-color);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
transform: rotate( -90deg);
|
||||
}
|
||||
|
||||
.stick_follow_icon ul li {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.stick_follow_icon p:after {
|
||||
top: 9px;
|
||||
right: -48px;
|
||||
width: 40px;
|
||||
height: 1px;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
background-color: var(--text-color);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 991px) {
|
||||
.stick_follow_icon {
|
||||
width: unset;
|
||||
height: unset;
|
||||
position: static;
|
||||
margin-top: unset;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: center;
|
||||
padding: 40px 0;
|
||||
align-items: center;
|
||||
}
|
||||
.stick_follow_icon p {
|
||||
top: unset;
|
||||
left: unset;
|
||||
width: unset;
|
||||
height: unset;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
transform: unset;
|
||||
font-size: 17px;
|
||||
margin-right: 65px;
|
||||
}
|
||||
.stick_follow_icon ul {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.stick_follow_icon ul li {
|
||||
display: inline;
|
||||
margin-bottom: 29px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
21
src/components/themetoggle/index.js
Normal file
21
src/components/themetoggle/index.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { WiMoonAltWaningCrescent4 } from "react-icons/wi";
|
||||
|
||||
|
||||
const Themetoggle = () => {
|
||||
const [theme, settheme] = useState(localStorage.getItem("theme"));
|
||||
const themetoggle = () => {
|
||||
settheme(theme === "dark" ? "light" : "dark");
|
||||
};
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme', theme );
|
||||
}, [theme]);
|
||||
return (
|
||||
<div className="nav_ac" onClick={themetoggle}>
|
||||
<WiMoonAltWaningCrescent4 />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Themetoggle;
|
||||
15
src/components/themetoggle/style.css
Normal file
15
src/components/themetoggle/style.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.theme_toggler {
|
||||
background: var(--primary-color);
|
||||
z-index: 999999999;
|
||||
left: 10px;
|
||||
background: var(--primary-color);
|
||||
display: flex;
|
||||
height: 50px;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.theme_toggler svg {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
}
|
||||
170
src/content_option.js
Normal file
170
src/content_option.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
const logotext = "HARIVANSH";
|
||||
const meta = {
|
||||
title: "Harivansh Rathi",
|
||||
description: "I’m Harivansh Rathi, a Computer Science student at the University of Virginia.",
|
||||
};
|
||||
|
||||
const introdata = {
|
||||
title: "Hi, I'm Harivansh Rathi",
|
||||
animated: {
|
||||
first: "Full Stack Developer",
|
||||
second: "Software Engineer",
|
||||
third: "Tech Enthusiast",
|
||||
},
|
||||
description: "I'm passionate about building exceptional digital experiences that make a difference. With expertise in both frontend and backend development, I create scalable and efficient solutions that solve real-world problems.",
|
||||
your_img_url: "https://i.imgur.com/0y00000.png",
|
||||
};
|
||||
|
||||
const dataabout = {
|
||||
title: "About Me",
|
||||
aboutme: "I am a Computer Science student at the University of Virginia, with a passion for software development and AI. I enjoy building scalable applications and exploring the latest advancements in technology. I am always eager to learn and contribute to innovative projects.",
|
||||
};
|
||||
const worktimeline = [{
|
||||
jobtitle: "Front End Development Intern",
|
||||
where: "UNIKOVE TECHNOLOGIES",
|
||||
date: "June 2024 - August 2024",
|
||||
},
|
||||
{
|
||||
jobtitle: "Software Development Intern",
|
||||
where: "MOGLIX",
|
||||
date: "June 2023 - August 2023",
|
||||
},
|
||||
{
|
||||
jobtitle: "Software Engineering Intern",
|
||||
where: "SAN AUTO",
|
||||
date: "June 2022 - August 2022",
|
||||
},
|
||||
];
|
||||
|
||||
const skills = [{
|
||||
name: "Python",
|
||||
value: 90,
|
||||
},
|
||||
{
|
||||
name: "Java",
|
||||
value: 85,
|
||||
},
|
||||
{
|
||||
name: "Typescript",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "HTML",
|
||||
value: 90,
|
||||
},
|
||||
{
|
||||
name: "CSS/Tailwind",
|
||||
value: 85,
|
||||
},
|
||||
{
|
||||
name: "C/C++",
|
||||
value: 70,
|
||||
},
|
||||
{
|
||||
name: "React.js",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "Node.js",
|
||||
value: 75,
|
||||
},
|
||||
{
|
||||
name: "Express.js",
|
||||
value: 75,
|
||||
},
|
||||
{
|
||||
name: "Vite",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "Next.js 14",
|
||||
value: 70,
|
||||
},
|
||||
{
|
||||
name: "Git/Github",
|
||||
value: 90,
|
||||
},
|
||||
{
|
||||
name: "PostgreSQL",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "MySQL",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "Supabase",
|
||||
value: 75,
|
||||
},
|
||||
{
|
||||
name: "Vercel",
|
||||
value: 80,
|
||||
},
|
||||
{
|
||||
name: "OAuth 2.0",
|
||||
value: 70,
|
||||
},
|
||||
];
|
||||
|
||||
const services = [{
|
||||
title: "Front End Development",
|
||||
description: "Utilizing JavaScript and React.js to refine UX design elements, ensuring seamless functionality and integration.",
|
||||
},
|
||||
{
|
||||
title: "Back End Development",
|
||||
description: "Developing robust and scalable backend solutions using Node.js, Express.js, and SQL databases.",
|
||||
},
|
||||
{
|
||||
title: "AI Application Development",
|
||||
description: "Building AI-powered applications with a focus on document retrieval and data analysis.",
|
||||
},
|
||||
];
|
||||
|
||||
const dataportfolio = [{
|
||||
img: "https://picsum.photos/400/300/?grayscale",
|
||||
description: "Built a React/TypeScript RAG AI chat application with n8n, achieving 95% query response accuracy",
|
||||
link: "https://github.com/harivansh-afk/RAG-ui",
|
||||
},
|
||||
{
|
||||
img: "https://picsum.photos/400/300/?grayscale",
|
||||
description: "Co-authored a research paper on cryptocurrency market predictability with a PHD student at CMU",
|
||||
link: "https://github.com/harivansh-afk/CryptoCurrencyPredictionLSTM",
|
||||
},
|
||||
{
|
||||
img: "https://picsum.photos/400/300/?grayscale",
|
||||
description: "Built a full-stack habit tracker web app with React, TypeScript, and Supabase, boosting scalability by 25%",
|
||||
link: "https://github.com/harivansh-afk/Habit-Tracker",
|
||||
},
|
||||
{
|
||||
img: "https://picsum.photos/400/300/?grayscale",
|
||||
description: "Built a React/TypeScript app with Shadcn/UI and Tailwind, implementing lazy loading for 40% faster loads",
|
||||
link: "https://github.com/harivansh-afk/ENGL-Final-Project",
|
||||
},
|
||||
];
|
||||
|
||||
const contactConfig = {
|
||||
YOUR_EMAIL: "zng2gc@virginia.edu",
|
||||
YOUR_FONE: "+1 434-310-1227",
|
||||
description: "I am currently based in Charlottesville, VA. Feel free to reach out to me for any opportunities or collaborations.",
|
||||
YOUR_SERVICE_ID: "service_id",
|
||||
YOUR_TEMPLATE_ID: "template_id",
|
||||
YOUR_USER_ID: "user_id",
|
||||
};
|
||||
|
||||
const socialprofils = {
|
||||
github: "https://github.com/harivansh-afk",
|
||||
linkedin: "https://linkedin.com/in/harivansh-rathi",
|
||||
resume: "https://docs.google.com/document/d/1TKbtqYinjRNFZiZNbxEwRTz5QDiZ5mOk/edit?usp=sharing&ouid=104313709347999918268&rtpof=true&sd=true"
|
||||
};
|
||||
export {
|
||||
meta,
|
||||
dataabout,
|
||||
dataportfolio,
|
||||
worktimeline,
|
||||
skills,
|
||||
services,
|
||||
introdata,
|
||||
contactConfig,
|
||||
socialprofils,
|
||||
logotext,
|
||||
};
|
||||
72
src/header/index.js
Normal file
72
src/header/index.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import React, { useState } from "react";
|
||||
import "./style.css";
|
||||
import { VscGrabber, VscClose } from "react-icons/vsc";
|
||||
import { Link } from "react-router-dom";
|
||||
import { logotext ,socialprofils } from "../content_option";
|
||||
import Themetoggle from "../components/themetoggle";
|
||||
|
||||
const Headermain = () => {
|
||||
const [isActive, setActive] = useState("false");
|
||||
|
||||
const handleToggle = () => {
|
||||
setActive(!isActive);
|
||||
document.body.classList.toggle("ovhidden");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="fixed-top site__header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<Link className="navbar-brand nav_ac" to="/">
|
||||
{logotext}
|
||||
</Link>
|
||||
<div className="d-flex align-items-center">
|
||||
<Themetoggle />
|
||||
<button className="menu__button nav_ac" onClick={handleToggle}>
|
||||
{!isActive ? <VscClose /> : <VscGrabber />}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`site__navigation ${!isActive ? "menu__opend" : ""}`}>
|
||||
<div className="bg__menu h-100">
|
||||
<div className="menu__wrapper">
|
||||
<div className="menu__container p-3">
|
||||
<ul className="the_menu">
|
||||
<li className="menu_item ">
|
||||
<Link onClick={handleToggle} to="/" className="my-3">Home</Link>
|
||||
</li>
|
||||
<li className="menu_item">
|
||||
<Link onClick={handleToggle} to="/portfolio" className="my-3"> Portfolio</Link>
|
||||
</li>
|
||||
<li className="menu_item">
|
||||
<Link onClick={handleToggle} to="/about" className="my-3">About</Link>
|
||||
</li>
|
||||
<li className="menu_item">
|
||||
<Link onClick={handleToggle} to="/contact" className="my-3"> Contact</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="menu_footer d-flex flex-column flex-md-row justify-content-between align-items-md-center position-absolute w-100 p-3">
|
||||
<div className="d-flex">
|
||||
<a href={socialprofils.github}>Github</a>
|
||||
<a href={socialprofils.linkedin}>LinkedIn</a>
|
||||
<a href={socialprofils.portfolio}>Resume</a>
|
||||
</div>
|
||||
<p className="copyright m-0">copyright __ {logotext}</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="br-top"></div>
|
||||
<div className="br-bottom"></div>
|
||||
<div className="br-left"></div>
|
||||
<div className="br-right"></div>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Headermain;
|
||||
207
src/header/style.css
Normal file
207
src/header/style.css
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
.site__header {
|
||||
top: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.menu__button {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.menu__button:focus,
|
||||
.menu__button:hover {
|
||||
color: var(--text-color);
|
||||
box-shadow: unset;
|
||||
}
|
||||
|
||||
.menu__button svg {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
fill: var(--text-color-2);
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
|
||||
.nav_ac {
|
||||
padding: 5px 15px;
|
||||
margin: 0;
|
||||
border: unset;
|
||||
background: var(--primary-color);
|
||||
font-size: 1.25rem;
|
||||
font-family: Marcellus;
|
||||
color: var(--text-color-2);
|
||||
line-height: 2;
|
||||
height: 50px;
|
||||
font-weight: bold;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.nav_ac:hover {
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
|
||||
.br-top,
|
||||
.br-bottom,
|
||||
.br-right,
|
||||
.br-left {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
.br-top {
|
||||
top: 0;
|
||||
height: 10px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.br-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.br-right {
|
||||
width: 10px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.br-left {
|
||||
width: 10px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cortina__wrapper-menu {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 5em;
|
||||
padding-bottom: 3em;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.site__navigation {
|
||||
height: 100%;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.menu__opend {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.main__menu_ul,
|
||||
.menu_right {
|
||||
opacity: 0;
|
||||
position: relative;
|
||||
transition: 0.5s;
|
||||
transition-delay: 0s;
|
||||
visibility: hidden;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.menu_right {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.site__navigation.menu__opend .main__menu_ul,
|
||||
.site__navigation.menu__opend .menu_right {
|
||||
opacity: 1;
|
||||
transition-delay: 0.6s;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.site__navigation .main__menu_ul li {
|
||||
list-style: none;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.site__navigation .main__menu_ul li a {
|
||||
color: var(--text-color);
|
||||
display: block;
|
||||
font-size: 2.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.bg__menu {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--primary-color);
|
||||
will-change: transform;
|
||||
transform: translateY(-100%);
|
||||
transition: .5s ease all;
|
||||
}
|
||||
|
||||
.menu__opend .bg__menu {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.menu__wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.the_menu {
|
||||
padding-top: 20vh;
|
||||
padding-bottom: 20vh;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.menu__container {
|
||||
margin-left: 33.3333%;
|
||||
}
|
||||
.the_menu {
|
||||
padding-top: 10vh;
|
||||
padding-bottom: 10vh;
|
||||
}
|
||||
}
|
||||
|
||||
.the_menu .menu_item>a {
|
||||
color: var(--text-color-2);
|
||||
line-height: 1;
|
||||
font-size: 2rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: color 250ms cubic-bezier(0, 0, 0.58, 1) 0s;
|
||||
padding: 4px 0px;
|
||||
text-decoration: none;
|
||||
font-family: Marcellus;
|
||||
}
|
||||
|
||||
.the_menu .menu_item>a:hover {
|
||||
color: var(--text-color-3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.the_menu .menu_item>a {
|
||||
font-size: 4.8vw;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_footer {
|
||||
bottom: 0;
|
||||
font-family: Marcellus;
|
||||
font-size: 14px;
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
.menu_footer a {
|
||||
color: var(--text-color-2);
|
||||
margin-right: 10px;
|
||||
text-decoration: none;
|
||||
}
|
||||
329
src/hooks/AnimatedCursor.js
Normal file
329
src/hooks/AnimatedCursor.js
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
import React, {useEffect,useRef,useState,useCallback} from "react"
|
||||
|
||||
const IsDevice = (() => {
|
||||
if (typeof navigator == 'undefined') return
|
||||
|
||||
let ua = navigator.userAgent
|
||||
|
||||
return {
|
||||
info: ua,
|
||||
|
||||
Android() {
|
||||
return ua.match(/Android/i)
|
||||
},
|
||||
BlackBerry() {
|
||||
return ua.match(/BlackBerry/i)
|
||||
},
|
||||
IEMobile() {
|
||||
return ua.match(/IEMobile/i)
|
||||
},
|
||||
iOS() {
|
||||
return ua.match(/iPhone|iPad|iPod/i)
|
||||
},
|
||||
iPad() {
|
||||
return (
|
||||
ua.match(/Mac/) &&
|
||||
navigator.maxTouchPoints &&
|
||||
navigator.maxTouchPoints > 2
|
||||
)
|
||||
},
|
||||
OperaMini() {
|
||||
return ua.match(/Opera Mini/i)
|
||||
},
|
||||
|
||||
/**
|
||||
* Any Device
|
||||
*/
|
||||
any() {
|
||||
return (
|
||||
IsDevice.Android() ||
|
||||
IsDevice.BlackBerry() ||
|
||||
IsDevice.iOS() ||
|
||||
IsDevice.iPad() ||
|
||||
IsDevice.OperaMini() ||
|
||||
IsDevice.IEMobile()
|
||||
)
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
|
||||
function useEventListener(eventName, handler, element = document) {
|
||||
const savedHandler = useRef()
|
||||
|
||||
useEffect(() => {
|
||||
savedHandler.current = handler
|
||||
}, [handler])
|
||||
|
||||
useEffect(() => {
|
||||
const isSupported = element && element.addEventListener
|
||||
if (!isSupported) return
|
||||
|
||||
const eventListener = (event) => savedHandler.current(event)
|
||||
|
||||
element.addEventListener(eventName, eventListener)
|
||||
|
||||
return () => {
|
||||
element.removeEventListener(eventName, eventListener)
|
||||
}
|
||||
}, [eventName, element])
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor Core
|
||||
* Replaces the native cursor with a custom animated cursor, consisting
|
||||
* of an inner and outer dot that scale inversely based on hover or click.
|
||||
*
|
||||
* @author Stephen Scaff (github.com/stephenscaff)
|
||||
*
|
||||
* @param {string} color - rgb color value
|
||||
* @param {number} outerAlpha - level of alpha transparency for color
|
||||
* @param {number} innerSize - inner cursor size in px
|
||||
* @param {number} innerScale - inner cursor scale amount
|
||||
* @param {number} outerSize - outer cursor size in px
|
||||
* @param {number} outerScale - outer cursor scale amount
|
||||
* @param {object} outerStyle - style object for outer cursor
|
||||
* @param {object} innerStyle - style object for inner cursor
|
||||
* @param {array} clickables - array of clickable selectors
|
||||
*
|
||||
*/
|
||||
function CursorCore({
|
||||
outerStyle,
|
||||
innerStyle,
|
||||
color = '220, 90, 90',
|
||||
outerAlpha = 0.3,
|
||||
innerSize = 8,
|
||||
outerSize = 8,
|
||||
outerScale = 6,
|
||||
innerScale = 0.6,
|
||||
trailingSpeed = 8,
|
||||
clickables = [
|
||||
'a',
|
||||
'input[type="text"]',
|
||||
'input[type="email"]',
|
||||
'input[type="number"]',
|
||||
'input[type="submit"]',
|
||||
'input[type="image"]',
|
||||
'label[for]',
|
||||
'select',
|
||||
'textarea',
|
||||
'button',
|
||||
'.link'
|
||||
]
|
||||
}) {
|
||||
const cursorOuterRef = useRef()
|
||||
const cursorInnerRef = useRef()
|
||||
const requestRef = useRef()
|
||||
const previousTimeRef = useRef()
|
||||
const [coords, setCoords] = useState({ x: 0, y: 0 })
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
const [isActiveClickable, setIsActiveClickable] = useState(false)
|
||||
let endX = useRef(0)
|
||||
let endY = useRef(0)
|
||||
|
||||
/**
|
||||
* Primary Mouse move event
|
||||
* @param {number} clientX - MouseEvent.clientx
|
||||
* @param {number} clientY - MouseEvent.clienty
|
||||
*/
|
||||
const onMouseMove = useCallback(({ clientX, clientY }) => {
|
||||
setCoords({ x: clientX, y: clientY })
|
||||
cursorInnerRef.current.style.top = `${clientY}px`
|
||||
cursorInnerRef.current.style.left = `${clientX}px`
|
||||
endX.current = clientX
|
||||
endY.current = clientY
|
||||
}, [])
|
||||
|
||||
// Outer Cursor Animation Delay
|
||||
const animateOuterCursor = useCallback(
|
||||
(time) => {
|
||||
if (previousTimeRef.current !== undefined) {
|
||||
coords.x += (endX.current - coords.x) / trailingSpeed
|
||||
coords.y += (endY.current - coords.y) / trailingSpeed
|
||||
cursorOuterRef.current.style.top = `${coords.y}px`
|
||||
cursorOuterRef.current.style.left = `${coords.x}px`
|
||||
}
|
||||
previousTimeRef.current = time
|
||||
requestRef.current = requestAnimationFrame(animateOuterCursor)
|
||||
},
|
||||
[requestRef] // eslint-disable-line
|
||||
)
|
||||
|
||||
// RAF for animateOuterCursor
|
||||
useEffect(() => {
|
||||
requestRef.current = requestAnimationFrame(animateOuterCursor)
|
||||
return () => cancelAnimationFrame(requestRef.current)
|
||||
}, [animateOuterCursor])
|
||||
|
||||
// Mouse Events State updates
|
||||
const onMouseDown = useCallback(() => setIsActive(true), [])
|
||||
const onMouseUp = useCallback(() => setIsActive(false), [])
|
||||
const onMouseEnterViewport = useCallback(() => setIsVisible(true), [])
|
||||
const onMouseLeaveViewport = useCallback(() => setIsVisible(false), [])
|
||||
|
||||
useEventListener('mousemove', onMouseMove)
|
||||
useEventListener('mousedown', onMouseDown)
|
||||
useEventListener('mouseup', onMouseUp)
|
||||
useEventListener('mouseover', onMouseEnterViewport)
|
||||
useEventListener('mouseout', onMouseLeaveViewport)
|
||||
|
||||
// Cursors Hover/Active State
|
||||
useEffect(() => {
|
||||
if (isActive) {
|
||||
cursorInnerRef.current.style.transform = `translate(-50%, -50%) scale(${innerScale})`
|
||||
cursorOuterRef.current.style.transform = `translate(-50%, -50%) scale(${outerScale})`
|
||||
} else {
|
||||
cursorInnerRef.current.style.transform = 'translate(-50%, -50%) scale(1)'
|
||||
cursorOuterRef.current.style.transform = 'translate(-50%, -50%) scale(1)'
|
||||
}
|
||||
}, [innerScale, outerScale, isActive])
|
||||
|
||||
// Cursors Click States
|
||||
useEffect(() => {
|
||||
if (isActiveClickable) {
|
||||
cursorInnerRef.current.style.transform = `translate(-50%, -50%) scale(${
|
||||
innerScale * 1.2
|
||||
})`
|
||||
cursorOuterRef.current.style.transform = `translate(-50%, -50%) scale(${
|
||||
outerScale * 1.4
|
||||
})`
|
||||
}
|
||||
}, [innerScale, outerScale, isActiveClickable])
|
||||
|
||||
// Cursor Visibility State
|
||||
useEffect(() => {
|
||||
if (isVisible) {
|
||||
cursorInnerRef.current.style.opacity = 1
|
||||
cursorOuterRef.current.style.opacity = 1
|
||||
} else {
|
||||
cursorInnerRef.current.style.opacity = 0
|
||||
cursorOuterRef.current.style.opacity = 0
|
||||
}
|
||||
}, [isVisible])
|
||||
|
||||
useEffect(() => {
|
||||
const clickableEls = document.querySelectorAll(clickables.join(','))
|
||||
|
||||
clickableEls.forEach((el) => {
|
||||
el.style.cursor = 'none'
|
||||
|
||||
el.addEventListener('mouseover', () => {
|
||||
setIsActive(true)
|
||||
})
|
||||
el.addEventListener('click', () => {
|
||||
setIsActive(true)
|
||||
setIsActiveClickable(false)
|
||||
})
|
||||
el.addEventListener('mousedown', () => {
|
||||
setIsActiveClickable(true)
|
||||
})
|
||||
el.addEventListener('mouseup', () => {
|
||||
setIsActive(true)
|
||||
})
|
||||
el.addEventListener('mouseout', () => {
|
||||
setIsActive(false)
|
||||
setIsActiveClickable(false)
|
||||
})
|
||||
})
|
||||
|
||||
return () => {
|
||||
clickableEls.forEach((el) => {
|
||||
el.removeEventListener('mouseover', () => {
|
||||
setIsActive(true)
|
||||
})
|
||||
el.removeEventListener('click', () => {
|
||||
setIsActive(true)
|
||||
setIsActiveClickable(false)
|
||||
})
|
||||
el.removeEventListener('mousedown', () => {
|
||||
setIsActiveClickable(true)
|
||||
})
|
||||
el.removeEventListener('mouseup', () => {
|
||||
setIsActive(true)
|
||||
})
|
||||
el.removeEventListener('mouseout', () => {
|
||||
setIsActive(false)
|
||||
setIsActiveClickable(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
}, [isActive, clickables])
|
||||
|
||||
// Cursor Styles
|
||||
const styles = {
|
||||
cursorInner: {
|
||||
zIndex: 999,
|
||||
display: 'block',
|
||||
position: 'fixed',
|
||||
borderRadius: '50%',
|
||||
width: innerSize,
|
||||
height: innerSize,
|
||||
pointerEvents: 'none',
|
||||
backgroundColor: `rgba(${color}, 1)`,
|
||||
...(innerStyle && innerStyle),
|
||||
transition: 'opacity 0.15s ease-in-out, transform 0.25s ease-in-out'
|
||||
},
|
||||
cursorOuter: {
|
||||
zIndex: 999,
|
||||
display: 'block',
|
||||
position: 'fixed',
|
||||
borderRadius: '50%',
|
||||
pointerEvents: 'none',
|
||||
width: outerSize,
|
||||
height: outerSize,
|
||||
backgroundColor: `rgba(${color}, ${outerAlpha})`,
|
||||
transition: 'opacity 0.15s ease-in-out, transform 0.15s ease-in-out',
|
||||
willChange: 'transform',
|
||||
...(outerStyle && outerStyle)
|
||||
}
|
||||
}
|
||||
|
||||
// Hide / Show global cursor
|
||||
document.body.style.cursor = 'none'
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div ref={cursorOuterRef} style={styles.cursorOuter} />
|
||||
<div ref={cursorInnerRef} style={styles.cursorInner} />
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* AnimatedCursor
|
||||
* Calls and passes props to CursorCore if not a touch/mobile device.
|
||||
*/
|
||||
function AnimatedCursor({
|
||||
outerStyle,
|
||||
innerStyle,
|
||||
color,
|
||||
outerAlpha,
|
||||
innerSize,
|
||||
innerScale,
|
||||
outerSize,
|
||||
outerScale,
|
||||
trailingSpeed,
|
||||
clickables
|
||||
}) {
|
||||
if (typeof navigator !== 'undefined' && IsDevice.any()) {
|
||||
return <React.Fragment></React.Fragment>
|
||||
}
|
||||
return (
|
||||
<CursorCore
|
||||
outerStyle={outerStyle}
|
||||
innerStyle={innerStyle}
|
||||
color={color}
|
||||
outerAlpha={outerAlpha}
|
||||
innerSize={innerSize}
|
||||
innerScale={innerScale}
|
||||
outerSize={outerSize}
|
||||
outerScale={outerScale}
|
||||
trailingSpeed={trailingSpeed}
|
||||
clickables={clickables}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default AnimatedCursor
|
||||
21
src/hooks/withRouter.js
Normal file
21
src/hooks/withRouter.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
function withRouter(Component) {
|
||||
function ComponentWithRouterProp(props) {
|
||||
let location = useLocation();
|
||||
let navigate = useNavigate();
|
||||
let params = useParams();
|
||||
return (
|
||||
<Component
|
||||
{...props}
|
||||
location={location}
|
||||
params={params}
|
||||
navigate={navigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return ComponentWithRouterProp;
|
||||
}
|
||||
|
||||
export default withRouter;
|
||||
96
src/index.css
Normal file
96
src/index.css
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
:root {
|
||||
--bg-color: #0c0c0c;
|
||||
--primary-color: #0d0d0d;
|
||||
--secondary-color: #fff;
|
||||
--text-color: #fff;
|
||||
--text-color-2: #fff;
|
||||
--text-color-3: rgb(204, 0, 0);
|
||||
--overlay-color: rgb(12 12 12 / 63%);
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--bg-color: #ffffff;
|
||||
--primary-color: #ffffff;
|
||||
--secondary-color: #000;
|
||||
--text-color: #000;
|
||||
--text-color-2: #000;
|
||||
--text-color-3: rgb(204, 0, 0);
|
||||
--overlay-color: rgb(255 255 255 / 70%);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: 'Raleway', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
padding-top: 60px;
|
||||
border-left: 10px solid var(--primary-color);
|
||||
border-right: 10px solid var(--primary-color);
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: Marcellus;
|
||||
}
|
||||
|
||||
a,
|
||||
a:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
p {
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.ovhidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.text_2,
|
||||
.text_2:hover {
|
||||
color: var(--text-color-2);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.cursor__dot div {
|
||||
z-index: 999999 !important;
|
||||
}
|
||||
|
||||
.cursor__dot div:last-child {
|
||||
background-color: var(--text-color-3) !important;
|
||||
}
|
||||
|
||||
.cursor__dot div:first-child {
|
||||
filter: invert(1);
|
||||
background-color: var(--overlay-color) !important;
|
||||
}
|
||||
|
||||
.color_pr {
|
||||
color: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.color_sec {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
9
src/index.js
Normal file
9
src/index.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from './app/App';
|
||||
import './index.css';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<App />
|
||||
);
|
||||
107
src/pages/about/index.js
Normal file
107
src/pages/about/index.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import React from "react";
|
||||
import "./style.css";
|
||||
import { Helmet, HelmetProvider } from "react-helmet-async";
|
||||
import { Container, Row, Col } from "react-bootstrap";
|
||||
import {
|
||||
dataabout,
|
||||
meta,
|
||||
worktimeline,
|
||||
skills,
|
||||
} from "../../content_option";
|
||||
|
||||
export const About = () => {
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<Container className="About-header">
|
||||
<Helmet>
|
||||
<meta charSet="utf-8" />
|
||||
<title> About | {meta.title}</title>
|
||||
<meta name="description" content={meta.description} />
|
||||
</Helmet>
|
||||
<Row className="mb-5 mt-3 pt-md-3">
|
||||
<Col lg="8">
|
||||
<h1 className="display-4 mb-4">About me</h1>
|
||||
<hr className="t_border my-4 ml-0 text-left" />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="sec_sp">
|
||||
<Col lg="5">
|
||||
<h3 className="color_sec py-4">{dataabout.title}</h3>
|
||||
</Col>
|
||||
<Col lg="7" className="d-flex align-items-center">
|
||||
<div>
|
||||
<p>{dataabout.aboutme}</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className=" sec_sp">
|
||||
<Col lg="5">
|
||||
<h3 className="color_sec py-4">Work Timline</h3>
|
||||
</Col>
|
||||
<Col lg="7">
|
||||
<table className="table caption-top">
|
||||
<tbody>
|
||||
{worktimeline.map((data, i) => {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<th scope="row">{data.jobtitle}</th>
|
||||
<td>{data.where}</td>
|
||||
<td>{data.date}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="sec_sp">
|
||||
<Col lg="5">
|
||||
<h3 className="color_sec py-4">Skills</h3>
|
||||
</Col>
|
||||
<Col lg="7">
|
||||
{skills.map((data, i) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
<h3 className="progress-title">{data.name}</h3>
|
||||
<div className="progress">
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={{
|
||||
width: `${data.value}%`,
|
||||
}}
|
||||
>
|
||||
<div className="progress-value">{data.value}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="sec_sp">
|
||||
<Col lg="5">
|
||||
<h3 className="color_sec py-4">Education</h3>
|
||||
</Col>
|
||||
<Col lg="7">
|
||||
<div>
|
||||
<p>
|
||||
<strong>University of Virginia, Charlottesville, VA</strong>
|
||||
<br />
|
||||
Bachelor of Arts, Computer Science
|
||||
<br />
|
||||
Expected May 2026
|
||||
<br />
|
||||
Cumulative GPA: 3.55/4.0
|
||||
<br />
|
||||
Relevant Coursework:
|
||||
<br />
|
||||
Data structures and algo, Computer systems and organization,
|
||||
Software dev essentials
|
||||
</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
100
src/pages/about/style.css
Normal file
100
src/pages/about/style.css
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
.sec_sp {
|
||||
margin-bottom: calc(3rem + 5.128vw)
|
||||
}
|
||||
|
||||
.table td,
|
||||
.table th {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.t_border {
|
||||
border-color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.progress-title {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
margin: 15px 0 20px;
|
||||
font-family: 'Raleway';
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 5px;
|
||||
background: var(--secondary);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
margin-bottom: 30px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.progress .progress-bar {
|
||||
position: relative;
|
||||
background: var(--text-color);
|
||||
animation: animate-positive 2s;
|
||||
overflow: visible;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.progress .progress-value {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
right: 8px;
|
||||
font-size: 17px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
@-webkit-keyframes animate-positive {
|
||||
0% {
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animate-positive {
|
||||
0% {
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
.service__title {
|
||||
padding: 8px 0;
|
||||
border-bottom: solid 2px var(--secondary-color);
|
||||
}
|
||||
|
||||
.service-section .service-category-title {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
/*! CSS Used keyframes */
|
||||
|
||||
@-webkit-keyframes fadeInUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
}
|
||||
167
src/pages/contact/index.js
Normal file
167
src/pages/contact/index.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import React, { useState } from "react";
|
||||
import * as emailjs from "emailjs-com";
|
||||
import "./style.css";
|
||||
import { Helmet, HelmetProvider } from "react-helmet-async";
|
||||
import { meta } from "../../content_option";
|
||||
import { Container, Row, Col, Alert } from "react-bootstrap";
|
||||
import { contactConfig } from "../../content_option";
|
||||
|
||||
export const ContactUs = () => {
|
||||
const [formData, setFormdata] = useState({
|
||||
email: "",
|
||||
name: "",
|
||||
message: "",
|
||||
loading: false,
|
||||
show: false,
|
||||
alertmessage: "",
|
||||
variant: "",
|
||||
});
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
setFormdata({ loading: true });
|
||||
|
||||
const templateParams = {
|
||||
from_name: formData.email,
|
||||
user_name: formData.name,
|
||||
to_name: contactConfig.YOUR_EMAIL,
|
||||
message: formData.message,
|
||||
};
|
||||
|
||||
emailjs
|
||||
.send(
|
||||
contactConfig.YOUR_SERVICE_ID,
|
||||
contactConfig.YOUR_TEMPLATE_ID,
|
||||
templateParams,
|
||||
contactConfig.YOUR_USER_ID
|
||||
)
|
||||
.then(
|
||||
(result) => {
|
||||
console.log(result.text);
|
||||
setFormdata({
|
||||
loading: false,
|
||||
alertmessage: "SUCCESS! ,Thankyou for your messege",
|
||||
variant: "success",
|
||||
show: true,
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
console.log(error.text);
|
||||
setFormdata({
|
||||
alertmessage: `Faild to send!,${error.text}`,
|
||||
variant: "danger",
|
||||
show: true,
|
||||
});
|
||||
document.getElementsByClassName("co_alert")[0].scrollIntoView();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
setFormdata({
|
||||
...formData,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<Container>
|
||||
<Helmet>
|
||||
<meta charSet="utf-8" />
|
||||
<title>{meta.title} | Contact</title>
|
||||
<meta name="description" content={meta.description} />
|
||||
</Helmet>
|
||||
<Row className="mb-5 mt-3 pt-md-3">
|
||||
<Col lg="8">
|
||||
<h1 className="display-4 mb-4">Contact Me</h1>
|
||||
<hr className="t_border my-4 ml-0 text-left" />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="sec_sp">
|
||||
<Col lg="12">
|
||||
<Alert
|
||||
//show={formData.show}
|
||||
variant={formData.variant}
|
||||
className={`rounded-0 co_alert ${
|
||||
formData.show ? "d-block" : "d-none"
|
||||
}`}
|
||||
onClose={() => setFormdata({ show: false })}
|
||||
dismissible
|
||||
>
|
||||
<p className="my-0">{formData.alertmessage}</p>
|
||||
</Alert>
|
||||
</Col>
|
||||
<Col lg="5" className="mb-5">
|
||||
<h3 className="color_sec py-4">Get in touch</h3>
|
||||
<address>
|
||||
<strong>Email:</strong>{" "}
|
||||
<a href={`mailto:${contactConfig.YOUR_EMAIL}`}>
|
||||
{contactConfig.YOUR_EMAIL}
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
{contactConfig.hasOwnProperty("YOUR_FONE") ? (
|
||||
<p>
|
||||
<strong>Phone:</strong> {contactConfig.YOUR_FONE}
|
||||
</p>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</address>
|
||||
<p>{contactConfig.description}</p>
|
||||
</Col>
|
||||
<Col lg="7" className="d-flex align-items-center">
|
||||
<form onSubmit={handleSubmit} className="contact__form w-100">
|
||||
<Row>
|
||||
<Col lg="6" className="form-group">
|
||||
<input
|
||||
className="form-control"
|
||||
id="name"
|
||||
name="name"
|
||||
placeholder="Name"
|
||||
value={formData.name || ""}
|
||||
type="text"
|
||||
required
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Col>
|
||||
<Col lg="6" className="form-group">
|
||||
<input
|
||||
className="form-control rounded-0"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
value={formData.email || ""}
|
||||
required
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<textarea
|
||||
className="form-control rounded-0"
|
||||
id="message"
|
||||
name="message"
|
||||
placeholder="Message"
|
||||
rows="5"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
></textarea>
|
||||
<br />
|
||||
<Row>
|
||||
<Col lg="12" className="form-group">
|
||||
<button className="btn ac_btn" type="submit">
|
||||
{formData.loading ? "Sending..." : "Send"}
|
||||
</button>
|
||||
</Col>
|
||||
</Row>
|
||||
</form>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<div className={formData.loading ? "loading-bar" : "d-none"}></div>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
45
src/pages/contact/style.css
Normal file
45
src/pages/contact/style.css
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
.contact__form .form-control {
|
||||
padding: 1.375rem .75rem;
|
||||
line-height: 1.5;
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 0 !important;
|
||||
border: 1px solid var(--secondary-color);
|
||||
}
|
||||
|
||||
.contact__form input.form-control {
|
||||
margin-bottom: 2em;
|
||||
height: calc(2.5em + .75rem + 2px);
|
||||
}
|
||||
|
||||
button.btn.ac_btn:hover {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 10px;
|
||||
z-index: 999999999;
|
||||
background: var(--text-color);
|
||||
transform: translateX(100%);
|
||||
animation: shift-rightwards 1s ease-in-out infinite;
|
||||
animation-delay: .3s;
|
||||
}
|
||||
|
||||
@keyframes shift-rightwards {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
40% {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
60% {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
66
src/pages/home/index.js
Normal file
66
src/pages/home/index.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import React from "react";
|
||||
import "./style.css";
|
||||
import { Helmet, HelmetProvider } from "react-helmet-async";
|
||||
import Typewriter from "typewriter-effect";
|
||||
import { introdata, meta } from "../../content_option";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export const Home = () => {
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<section id="home" className="home">
|
||||
<Helmet>
|
||||
<meta charSet="utf-8" />
|
||||
<title> {meta.title}</title>
|
||||
<meta name="description" content={meta.description} />
|
||||
</Helmet>
|
||||
<div className="intro_sec d-block d-lg-flex align-items-center ">
|
||||
<div
|
||||
className="h_bg-image order-1 order-lg-2 h-100 "
|
||||
style={{ backgroundImage: `url(/assets/images/Image%20with%20Background%20Removed.png)` }}
|
||||
></div>
|
||||
<div className="text order-2 order-lg-1 h-100 d-lg-flex justify-content-center">
|
||||
<div className="align-self-center ">
|
||||
<div className="intro mx-auto">
|
||||
<h2 className="mb-1x">{introdata.title}</h2>
|
||||
<h1 className="fluidz-48 mb-1x">
|
||||
<Typewriter
|
||||
options={{
|
||||
strings: [
|
||||
introdata.animated.first,
|
||||
introdata.animated.second,
|
||||
introdata.animated.third,
|
||||
],
|
||||
autoStart: true,
|
||||
loop: true,
|
||||
deleteSpeed: 10,
|
||||
}}
|
||||
/>
|
||||
</h1>
|
||||
<p className="mb-1x">{introdata.description}</p>
|
||||
<div className="intro_btn-action pb-5">
|
||||
<Link to="/portfolio" className="text_2">
|
||||
<div id="button_p" className="ac_btn btn ">
|
||||
My Portfolio
|
||||
<div className="ring one"></div>
|
||||
<div className="ring two"></div>
|
||||
<div className="ring three"></div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link to="/contact">
|
||||
<div id="button_h" className="ac_btn btn">
|
||||
Contact Me
|
||||
<div className="ring one"></div>
|
||||
<div className="ring two"></div>
|
||||
<div className="ring three"></div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
208
src/pages/home/style.css
Normal file
208
src/pages/home/style.css
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
section {
|
||||
flex: 1 0 auto;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
-webkit-transition: all 0.5s ease-in;
|
||||
-o-transition: all 0.5s ease-in;
|
||||
transition: all 0.5s ease-in;
|
||||
}
|
||||
|
||||
.who_am_I {
|
||||
font-family: Cinzel;
|
||||
}
|
||||
|
||||
.has-first-color {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-portfolio {
|
||||
background: var(--primary-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-portfolio a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-about a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.intro_sec {
|
||||
height: calc(100vh - 60px);
|
||||
min-height: 700px;
|
||||
height: 100vh;
|
||||
margin-top: -60px;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.intro_sec {
|
||||
display: block;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.intro_sec .text,
|
||||
.intro_sec .h_bg-image {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.intro_sec .text,
|
||||
.intro_sec .h_bg-image {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.intro_sec .intro {
|
||||
max-width: 450px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.intro_sec .intro {
|
||||
max-width: 700px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.intro_sec .intro .feature .wrap-icon {
|
||||
background: 0 0 !important;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.intro_sec .intro .feature .wrap-icon svg {
|
||||
color: #5cccc9;
|
||||
}
|
||||
|
||||
.intro_sec .text h1 {
|
||||
font-size: 30px;
|
||||
margin-bottom: 50px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.intro_sec .text h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.intro_sec .h_bg-image {
|
||||
background-size: cover;
|
||||
background-position: center 30%;
|
||||
min-height: 700px;
|
||||
position: relative;
|
||||
max-width: 600px;
|
||||
max-height: 600px;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.ac_btn {
|
||||
padding: 4px 19px;
|
||||
color: var(--secondary-color);
|
||||
position: relative;
|
||||
border: var(--secondary-color) 2px solid;
|
||||
overflow: hidden;
|
||||
transition: all 0.6s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.ac_btn a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ac_btn:hover {
|
||||
box-shadow: 8px 8px 0px var(--text-color), -8px -8px 0px var(--text-color);
|
||||
}
|
||||
|
||||
.ac_btn:hover .one {
|
||||
opacity: 1;
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
|
||||
.ac_btn:hover .two {
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
|
||||
.ac_btn:hover .three {
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
|
||||
.ac_btn:hover .four {
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
|
||||
.ac_btn .ring {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translate3d(0px, 90px, 0px);
|
||||
}
|
||||
|
||||
.ac_btn .one {
|
||||
background-color: #000;
|
||||
transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
z-index: -3;
|
||||
z-index: -4;
|
||||
}
|
||||
|
||||
.ac_btn .two {
|
||||
background-color: var(--primary-color);
|
||||
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
z-index: -3;
|
||||
}
|
||||
|
||||
.ac_btn .three {
|
||||
background-color: var(--secondary-color);
|
||||
z-index: -2;
|
||||
transition: all 0.7s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
z-index: -3;
|
||||
}
|
||||
|
||||
#button_p {
|
||||
background: var(--secondary-color);
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
#button_h:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.intro_sec .h_bg-image .resume-icon {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.intro_sec .h_bg-image .resume-icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.intro_sec .h_bg-image .resume-icon {
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.intro_sec .h_bg-image {
|
||||
filter: saturate(0.5);
|
||||
}
|
||||
38
src/pages/portfolio/index.js
Normal file
38
src/pages/portfolio/index.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import React from "react";
|
||||
import "./style.css";
|
||||
import { Helmet, HelmetProvider } from "react-helmet-async";
|
||||
import { Container, Row, Col } from "react-bootstrap";
|
||||
import { dataportfolio, meta } from "../../content_option";
|
||||
|
||||
export const Portfolio = () => {
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<Container className="About-header">
|
||||
<Helmet>
|
||||
<meta charSet="utf-8" />
|
||||
<title> Projects | {meta.title} </title>
|
||||
<meta name="description" content={meta.description} />
|
||||
</Helmet>
|
||||
<Row className="mb-5 mt-3 pt-md-3">
|
||||
<Col lg="8">
|
||||
<h1 className="display-4 mb-4"> Projects </h1>
|
||||
<hr className="t_border my-4 ml-0 text-left" />
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="mb-5 project_items_ho">
|
||||
{dataportfolio.map((data, i) => {
|
||||
return (
|
||||
<div key={i} className="project_item">
|
||||
<img src={data.img} alt="" />
|
||||
<div className="content">
|
||||
<p>{data.description}</p>
|
||||
<a href={data.link} target="_blank" rel="noopener noreferrer">View Project</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Container>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
82
src/pages/portfolio/style.css
Normal file
82
src/pages/portfolio/style.css
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
.project_items_ho {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.project_item {
|
||||
width: calc(33% - 2rem);
|
||||
text-align: center;
|
||||
margin: 0.5rem;
|
||||
position: relative;
|
||||
background: var(--secondary-color);
|
||||
padding: 6px;
|
||||
border: 1px solid var(--secondary-color);
|
||||
transition: 0.3s ease;
|
||||
font-size: 0;
|
||||
min-height: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.project_item {
|
||||
width: calc(50% - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.project_item {
|
||||
width: calc(100% - 2rem);
|
||||
}
|
||||
}
|
||||
|
||||
.project_item img {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.project_item .content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--overlay-color);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.project_item:hover .content {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.project_item .content p {
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.project_item .content a {
|
||||
background: var(--bg-color);
|
||||
border: solid 1px var(--text-color);
|
||||
padding: 4px 8px;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.project_item .content a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
13
src/reportWebVitals.js
Normal file
13
src/reportWebVitals.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
Loading…
Add table
Add a link
Reference in a new issue