mirror of
https://github.com/harivansh-afk/React-Portfolio.git
synced 2026-04-15 05:02:11 +00:00
initial commit
This commit is contained in:
commit
fb5b992b17
35 changed files with 11573 additions and 0 deletions
2
.env
Normal file
2
.env
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
DISABLE_ESLINT_PLUGIN=true
|
||||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
*.eslintcache
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Ubai Mutl
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
38
README.md
Normal file
38
README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
### Description
|
||||||
|
|
||||||
|
A simple portfolio template for developer/designers built with React.
|
||||||
|
|
||||||
|
### [live preview](https://ubaimutl.github.io/react-portfolio/)
|
||||||
|
|
||||||
|
[](https://ubaimutl.github.io/react-portfolio/)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Fully Responsive
|
||||||
|
- Multi-Page Layout
|
||||||
|
- Contact Form With EmailJs
|
||||||
|
- React-Bootstrap
|
||||||
|
- Edit Content From One Place
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
Get the code
|
||||||
|
|
||||||
|
<pre>git clone https://github.com/ubaimutl/react-portfolio.git</pre>
|
||||||
|
|
||||||
|
Install required dependencies
|
||||||
|
|
||||||
|
<pre>yarn install</pre>
|
||||||
|
|
||||||
|
|
||||||
|
Start the server
|
||||||
|
|
||||||
|
<pre>yarn start</pre>
|
||||||
|
|
||||||
|
### More
|
||||||
|
|
||||||
|
Modify pages content in `src/content_option.js`.
|
||||||
|
|
||||||
|
### Thanks
|
||||||
|
|
||||||
|
If you like this portfolio template don't forget give it a ⭐
|
||||||
45
package.json
Normal file
45
package.json
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "react-portfolio",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.2.3",
|
||||||
|
"emailjs-com": "^3.2.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-bootstrap": "^2.7.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-helmet-async": "^1.0.7",
|
||||||
|
"react-icons": "^4.1.0",
|
||||||
|
"react-router-dom": "^6.6.2",
|
||||||
|
"react-scripts": "^5.0.1",
|
||||||
|
"react-transition-group": "^4.4.2",
|
||||||
|
"typewriter-effect": "^2.18.2",
|
||||||
|
"web-vitals": "^3.1.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject",
|
||||||
|
"predeploy": "yarn build && cp build/index.html build/404.html",
|
||||||
|
"deploy": "gh-pages -d build"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/assets/images/Image with Background Removed.png
Normal file
BIN
public/assets/images/Image with Background Removed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
22
public/index.html
Normal file
22
public/index.html
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="author" content="Harivansh Rathi">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Marcellus&display=swap" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,400;0,500;1,400;1,500&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
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