├── .eslintrc.js
├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
└── src
├── App.js
├── App.scss
├── assets
├── fonts
│ ├── CoolveticaRg-Regular.woff
│ ├── CoolveticaRg-Regular.woff2
│ ├── LaBelleAurore.woff
│ ├── LaBelleAurore.woff2
│ └── helvetica-neu.ttf
├── images
│ ├── Sudip_logo_white.png
│ ├── codechef.png
│ ├── logo-lines-2.svg
│ ├── logo-lines.svg
│ ├── logo-s.png
│ ├── logo1.png
│ ├── logo2.png
│ ├── logo3.png
│ └── logo4.png
└── sounds
│ └── fake_verthandi.mp3
├── components
├── About
│ ├── index.js
│ └── index.scss
├── AnimatedLetters
│ ├── index.js
│ └── index.scss
├── Contact
│ ├── index.js
│ └── index.scss
├── Home
│ ├── Logo
│ │ ├── index.js
│ │ └── index.scss
│ ├── index.js
│ └── index.scss
├── Layout
│ ├── index.js
│ └── index.scss
├── Sidebar
│ ├── index.js
│ └── index.scss
├── Skills
│ ├── index.js
│ ├── index.scss
│ └── wordcloud.js
└── Soundbar
│ └── index.js
├── index.css
├── index.js
└── reportWebVitals.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | //... existing
3 | parser: '@babel/eslint-parser',
4 | parserOptions: {
5 | sourceType: 'module',
6 | ecmaVersion: 'latest',
7 | requireConfigFile: false,
8 | babelOptions: {
9 | presets: ['@babel/preset-react'],
10 | },
11 | },
12 | plugins: ['react', 'import'],
13 | rules: {
14 | // this is for sorting WITHIN an import
15 | 'sort-imports': [
16 | 'error',
17 | { ignoreCase: true, ignoreDeclarationSort: true },
18 | ],
19 | // this is for sorting imports
20 | 'import/order': [
21 | 'error',
22 | {
23 | groups: [
24 | ['external', 'builtin'],
25 | 'internal',
26 | ['sibling', 'parent'],
27 | 'index',
28 | ],
29 | pathGroups: [
30 | {
31 | pattern: '@(react|react-native)',
32 | group: 'external',
33 | position: 'before',
34 | },
35 | {
36 | pattern: '@src/**',
37 | group: 'internal',
38 | },
39 | ],
40 | pathGroupsExcludedImportTypes: ['internal', 'react'],
41 | 'newlines-between': 'always',
42 | alphabetize: {
43 | order: 'asc',
44 | caseInsensitive: true,
45 | },
46 | },
47 | ],
48 | },
49 | settings: {
50 | react: {
51 | version: 'detect',
52 | },
53 | },
54 | }
55 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | .prettierrc
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # personal-portfolio
2 |
3 | This is my personal portfolio website built using react.
4 |
5 | Visit the website: https://portfolio-metaloopa.vercel.app
6 |
7 | 
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-portfolio",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emailjs/browser": "4.4.1",
7 | "@fortawesome/fontawesome-svg-core": "6.6.0",
8 | "@fortawesome/free-brands-svg-icons": "6.6.0",
9 | "@fortawesome/free-solid-svg-icons": "6.6.0",
10 | "@fortawesome/react-fontawesome": "0.2.2",
11 | "animate.css": "^4.1.1",
12 | "leaflet": "1.9.4",
13 | "loaders.css": "0.1.2",
14 | "react": "18.3.1",
15 | "react-dom": "18.3.1",
16 | "react-leaflet": "4.2.1",
17 | "react-loaders": "3.0.1",
18 | "react-router-dom": "6.28.0",
19 | "react-scripts": "5.0.1",
20 | "react-spinners": "0.14.1",
21 | "react-toastify": "10.0.6",
22 | "react-tsparticles": "2.12.2",
23 | "sass": "1.80.6",
24 | "styled-components": "6.1.13",
25 | "TagCloud": "2.5.0",
26 | "tsparticles": "3.5.0",
27 | "web-vitals": "4.2.4"
28 | },
29 | "scripts": {
30 | "start": "react-scripts start",
31 | "build": "react-scripts build",
32 | "test": "react-scripts test",
33 | "eject": "react-scripts eject",
34 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
35 | "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
36 | "prettify": "prettier --write ."
37 | },
38 | "eslintConfig": {
39 | "extends": [
40 | "react-app",
41 | "react-app/jest",
42 | "prettier"
43 | ]
44 | },
45 | "browserslist": {
46 | "production": [
47 | ">0.2%",
48 | "not dead",
49 | "not op_mini all"
50 | ],
51 | "development": [
52 | "last 1 chrome version",
53 | "last 1 firefox version",
54 | "last 1 safari version"
55 | ]
56 | },
57 | "devDependencies": {
58 | "@babel/eslint-parser": "7.25.9",
59 | "@babel/preset-react": "7.25.9",
60 | "eslint-config-prettier": "9.1.0",
61 | "eslint-plugin-import": "2.31.0",
62 | "prettier": "3.3.3"
63 | }
64 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
15 | Sudip's Portfolio
16 |
17 |
18 |
19 | You need to enable JavaScript to run this app.
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Sudip's Portfolio",
3 | "name": "Sudip's Portfolio",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from 'react-router-dom'
2 |
3 | import About from './components/About'
4 | import Contact from './components/Contact'
5 | import Home from './components/Home'
6 | import Layout from './components/Layout'
7 | import Skills from './components/Skills'
8 | import Soundbar from './components/Soundbar'
9 | import './App.scss'
10 |
11 | function App() {
12 | return (
13 | <>
14 |
15 |
16 | }>
17 | } />
18 | } />
19 | } />
20 | } />
21 |
22 |
23 | >
24 | )
25 | }
26 |
27 | export default App
28 |
--------------------------------------------------------------------------------
/src/App.scss:
--------------------------------------------------------------------------------
1 | $primary-color: #ff4500;
2 |
3 | @import 'animate.css';
4 | @import '~loaders.css/src/animations/pacman.scss';
5 |
6 | @font-face {
7 | font-family: 'Helvetica Neue';
8 | src: url('./assets/fonts/helvetica-neu.ttf') format('ttf');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | @font-face {
14 | font-family: 'La Belle Aurore';
15 | src:
16 | url('./assets/fonts/LaBelleAurore.woff2') format('woff2'),
17 | url('./assets/fonts/LaBelleAurore.woff') format('woff');
18 | font-weight: normal;
19 | font-style: normal;
20 | }
21 |
22 | @font-face {
23 | font-family: 'Coolvetica';
24 | src:
25 | url('./assets/fonts/CoolveticaRg-Regular.woff2') format('woff2'),
26 | url('./assets/fonts/CoolveticaRg-Regular.woff') format('woff');
27 | font-weight: normal;
28 | font-style: normal;
29 | }
30 |
31 | input,
32 | textarea {
33 | font-family: 'Helvetica Neue', sans-serif;
34 | }
35 |
36 | .loader-hidden {
37 | display: none;
38 | }
39 |
40 | .loader-active {
41 | display: block;
42 | position: absolute;
43 | left: 0;
44 | right: 0;
45 | margin: auto;
46 | top: 0;
47 | bottom: 0;
48 | width: 50px;
49 | height: 50px;
50 | animation: fadeOut 1s 1s;
51 | animation-fill-mode: forwards;
52 | }
53 |
--------------------------------------------------------------------------------
/src/assets/fonts/CoolveticaRg-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/fonts/CoolveticaRg-Regular.woff
--------------------------------------------------------------------------------
/src/assets/fonts/CoolveticaRg-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/fonts/CoolveticaRg-Regular.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/LaBelleAurore.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/fonts/LaBelleAurore.woff
--------------------------------------------------------------------------------
/src/assets/fonts/LaBelleAurore.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/fonts/LaBelleAurore.woff2
--------------------------------------------------------------------------------
/src/assets/fonts/helvetica-neu.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/fonts/helvetica-neu.ttf
--------------------------------------------------------------------------------
/src/assets/images/Sudip_logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/Sudip_logo_white.png
--------------------------------------------------------------------------------
/src/assets/images/codechef.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/codechef.png
--------------------------------------------------------------------------------
/src/assets/images/logo-lines-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 | Created by potrace 1.16, written by Peter Selinger 2001-2019
9 |
10 |
12 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/assets/images/logo-lines.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 | Created by potrace 1.16, written by Peter Selinger 2001-2019
9 |
10 |
12 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/assets/images/logo-s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/logo-s.png
--------------------------------------------------------------------------------
/src/assets/images/logo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/logo1.png
--------------------------------------------------------------------------------
/src/assets/images/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/logo2.png
--------------------------------------------------------------------------------
/src/assets/images/logo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/logo3.png
--------------------------------------------------------------------------------
/src/assets/images/logo4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/images/logo4.png
--------------------------------------------------------------------------------
/src/assets/sounds/fake_verthandi.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metal-oopa/personal-portfolio/5579ead1252ad1b8ddbc10d42bb799d492a33b86/src/assets/sounds/fake_verthandi.mp3
--------------------------------------------------------------------------------
/src/components/About/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import { useEffect, useState } from 'react'
3 |
4 | import {
5 | faAws,
6 | faDocker,
7 | faGitAlt,
8 | faGolang,
9 | faJsSquare,
10 | faPython,
11 | } from '@fortawesome/free-brands-svg-icons'
12 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
13 | import Loader from 'react-loaders'
14 |
15 | import AnimatedLetters from '../AnimatedLetters'
16 | import './index.scss'
17 |
18 | const About = () => {
19 | const aboutArray = 'About Me'.split('')
20 |
21 | const [letterClass, setLetterClass] = useState('text-animate')
22 |
23 | useEffect(() => {
24 | const timer = setTimeout(() => {
25 | setLetterClass('text-animate-hover')
26 | }, 2000)
27 | return () => clearTimeout(timer)
28 | }, [])
29 |
30 | return (
31 | <>
32 |
33 |
34 |
35 |
40 |
41 |
42 | I’m a DevOps and Full Stack Engineer with over 2 years of
43 | experience, specializing in automating workflows, optimizing CI/CD
44 | pipelines, and deploying intelligent systems. My expertise spans
45 | across DevOps, full-stack, blockchain development, and aritificial
46 | intelligence, with a strong foundation in security.
47 |
48 |
49 | I’m passionate about solving real-world problems through innovative
50 | technology and continuously learning to stay ahead of industry
51 | trends. Whether it's streamlining infrastructure or building
52 | scalable applications, I bring a holistic approach to every project.
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | >
81 | )
82 | }
83 |
84 | export default About
85 |
--------------------------------------------------------------------------------
/src/components/About/index.scss:
--------------------------------------------------------------------------------
1 | .stage-cube-cont {
2 | width: 50%;
3 | height: 100%;
4 | top: 0;
5 | padding-top: 18%;
6 | margin-left: 0;
7 | position: absolute;
8 | right: 0;
9 | overflow: hidden;
10 | }
11 |
12 | .cubespinner {
13 | animation-name: spincube;
14 | animation-timing-function: ease-in-out;
15 | animation-iteration-count: infinite;
16 | animation-duration: 12s;
17 | transform-style: preserve-3d;
18 | transform-origin: 100px 100px 0;
19 | margin-left: calc(50% - 100px);
20 |
21 | div {
22 | position: absolute;
23 | width: 200px;
24 | height: 200px;
25 | border: 1px solid #ccc;
26 | background: rgba(255, 255, 255, 0.4);
27 | text-align: center;
28 | font-size: 100px;
29 | display: flex;
30 | justify-content: center;
31 | align-items: center;
32 | box-shadow: 0 0 20px 0px lightyellow;
33 | }
34 |
35 | .face1 {
36 | transform: translateZ(100px);
37 | color: #dd0031;
38 | }
39 | .face2 {
40 | transform: rotateY(90deg) translateZ(100px);
41 | color: #f06529;
42 | }
43 | .face3 {
44 | transform: rotateY(90deg) rotateX(90deg) translateZ(100px);
45 | color: #28a4d9;
46 | }
47 | .face4 {
48 | transform: rotateY(180deg) rotateZ(90deg) translateZ(100px);
49 | color: #5ed4f4;
50 | }
51 | .face5 {
52 | transform: rotateY(-90deg) rotateZ(90deg) translateZ(100px);
53 | color: #efd81d;
54 | }
55 | .face6 {
56 | transform: rotateX(-90deg) translateZ(100px);
57 | color: #ec4d28;
58 | }
59 | }
60 |
61 | @keyframes spincube {
62 | from,
63 | to {
64 | transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg);
65 | }
66 | 16% {
67 | transform: rotateY(-90deg);
68 | }
69 | 33% {
70 | transform: rotateY(-90deg) rotateZ(90deg);
71 | }
72 | 50% {
73 | transform: rotateY(-180deg) rotateZ(90deg);
74 | }
75 | 66% {
76 | transform: rotateY(-270deg) rotateX(90deg);
77 | }
78 | 83% {
79 | transform: rotateX(90deg);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/AnimatedLetters/index.js:
--------------------------------------------------------------------------------
1 | import './index.scss'
2 |
3 | const AnimatedLetters = ({ letterClass, strArray, idx }) => {
4 | return (
5 |
6 | {strArray.map((char, i) => (
7 |
8 | {char}
9 |
10 | ))}
11 |
12 | )
13 | }
14 |
15 | export default AnimatedLetters
16 |
--------------------------------------------------------------------------------
/src/components/AnimatedLetters/index.scss:
--------------------------------------------------------------------------------
1 | .text-animate {
2 | display: inline-block;
3 | opacity: 0;
4 | animation: bounceIn;
5 | animation-duration: 1s;
6 | animation-delay: 1s;
7 | animation-fill-mode: forwards;
8 | min-width: 10px;
9 | }
10 |
11 | .text-animate-hover {
12 | min-width: 10px;
13 | display: inline-block;
14 | animation-fill-mode: both;
15 | animation: rubberBand 1s;
16 |
17 | &:hover {
18 | animation: none;
19 | color: #ff4500;
20 | }
21 | }
22 |
23 | @for $i from 1 through 100 {
24 | .text-animate._#{$i} {
25 | animation-delay: #{calc($i / 12)}s;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/Contact/index.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { useRef } from 'react'
3 |
4 | import emailjs from '@emailjs/browser'
5 | import { MapContainer, Marker, Popup, TileLayer } from 'react-leaflet'
6 | import Loader from 'react-loaders'
7 | import { ClipLoader } from 'react-spinners'
8 | import { toast, ToastContainer } from 'react-toastify'
9 | import 'react-toastify/dist/ReactToastify.css'
10 |
11 | import AnimatedLetters from '../AnimatedLetters'
12 | import './index.scss'
13 |
14 | const Contact = () => {
15 | const [letterClass, setLetterClass] = useState('text-animate')
16 | const form = useRef()
17 | const [loading, setLoading] = useState(false)
18 | const contactArray = 'Contact Me'.split('')
19 |
20 | useEffect(() => {
21 | setTimeout(() => {
22 | setLetterClass('text-animate-hover')
23 | }, 3000)
24 | }, [])
25 |
26 | const sendEmail = async (e) => {
27 | e.preventDefault()
28 | setLoading(true)
29 |
30 | const email = form.current.email.value
31 | const res = await verifyEmail(email)
32 | if (!res) {
33 | setLoading(false)
34 | toast.error('Please enter a valid email address', {
35 | position: 'bottom-center',
36 | autoClose: 3500,
37 | hideProgressBar: false,
38 | closeOnClick: true,
39 | pauseOnHover: true,
40 | draggable: true,
41 | progress: undefined,
42 | theme: 'dark',
43 | })
44 | return
45 | }
46 |
47 | let fullName = form.current.name.value
48 | let subject = form.current.subject.value
49 | let message = form.current.message.value
50 |
51 | let firstName = fullName.split(' ')[0]
52 | firstName =
53 | firstName.charAt(0).toUpperCase() + firstName.slice(1).toLowerCase()
54 |
55 | const templateParams = {
56 | firstname: firstName,
57 | name: fullName,
58 | subject: subject,
59 | message: message,
60 | email: email,
61 | }
62 |
63 | emailjs
64 | .send(
65 | process.env.REACT_APP_EMIAL_SERVICE_ID,
66 | process.env.REACT_APP_TEMPLATE_ID,
67 | templateParams,
68 | process.env.REACT_APP_PUBLIC_KEY
69 | )
70 | .then(
71 | () => {
72 | toast.success('Message successfully sent!', {
73 | position: 'bottom-center',
74 | autoClose: 3500,
75 | hideProgressBar: false,
76 | closeOnClick: true,
77 | pauseOnHover: true,
78 | draggable: true,
79 | progress: undefined,
80 | theme: 'dark',
81 | })
82 | const timeout = setTimeout(() => {
83 | form.current.reset()
84 | setLoading(false)
85 | }, 3800)
86 |
87 | return () => clearTimeout(timeout)
88 | },
89 | () => {
90 | setLoading(false)
91 | toast.error('Failed to send the message, please try again', {
92 | position: 'bottom-center',
93 | autoClose: 3500,
94 | hideProgressBar: false,
95 | closeOnClick: true,
96 | pauseOnHover: true,
97 | draggable: true,
98 | progress: undefined,
99 | theme: 'dark',
100 | })
101 | }
102 | )
103 | }
104 |
105 | const verifyEmail = async (email) => {
106 | let res = await fetch(
107 | `https://mailok-email-validation.p.rapidapi.com/verify?email=${email}`,
108 | {
109 | method: 'GET',
110 | headers: {
111 | 'x-rapidapi-host': process.env.REACT_APP_RAPIDAPI_HOST,
112 | 'x-rapidapi-key': process.env.REACT_APP_RAPIDAPI_KEY,
113 | },
114 | }
115 | )
116 |
117 | let data = await res.json()
118 | return res.status === 200 && data.status === 'valid'
119 | }
120 |
121 | return (
122 | <>
123 |
124 |
125 |
126 |
131 |
132 |
133 | I’m open to new opportunities and collaborations! If you’re looking
134 | for someone who can bring fresh ideas and deliver impactful results,
135 | let’s get in touch!
136 |
137 |
138 |
180 |
181 |
182 |
183 | Sudip Banerjee
184 |
185 | Kolkata,
186 | West Bengal,
187 | India
188 |
189 |
190 |
191 |
192 |
193 |
194 | Sudip lives here, come over for a cup of coffee :{')'}
195 |
196 |
197 |
198 |
199 |
200 |
201 | >
202 | )
203 | }
204 |
205 | export default Contact
206 |
--------------------------------------------------------------------------------
/src/components/Contact/index.scss:
--------------------------------------------------------------------------------
1 | .contact-form {
2 | width: 100%;
3 | margin-top: 20px;
4 |
5 | ul {
6 | padding: 0;
7 | margin: 0;
8 |
9 | li {
10 | padding: 0;
11 | margin: 0;
12 | border-radius: 0.5rem;
13 | list-style: none;
14 | margin-bottom: 10px;
15 | opacity: 0;
16 | overflow: hidden;
17 | display: block;
18 | clear: both;
19 | position: relative;
20 | animation: fadeInUp 2s 2s;
21 | animation-fill-mode: forwards;
22 | }
23 |
24 | li.half {
25 | width: 49%;
26 | margin-left: 2%;
27 | float: left;
28 | clear: none;
29 |
30 | &:first-child {
31 | margin-left: 0;
32 | }
33 | }
34 | }
35 |
36 | input[type='text'],
37 | input[type='email'] {
38 | width: 100%;
39 | border: 0;
40 | background: #353535;
41 | height: 50px;
42 | font-size: 16px;
43 | color: #fff;
44 | padding: 0 20px;
45 | box-sizing: border-box;
46 | }
47 |
48 | textarea {
49 | width: 100%;
50 | border: 0;
51 | background: #353535;
52 | height: 50px;
53 | font-size: 16px;
54 | color: #fff;
55 | padding: 20px;
56 | box-sizing: border-box;
57 | min-height: 150px;
58 | }
59 |
60 | .flat-button {
61 | color: #ff4500;
62 | font-size: 11px;
63 | letter-spacing: 3px;
64 | text-decoration: none;
65 | padding: 8px 10px;
66 | border: 1px solid #ff4500;
67 | float: left;
68 | border-radius: 4px;
69 | background: 0 0;
70 | text-transform: uppercase;
71 | float: right;
72 | text-align: center;
73 | margin-right: 10px;
74 |
75 | &:hover {
76 | cursor: pointer;
77 | background-color: #ff4500;
78 | color: #000;
79 | }
80 | }
81 | }
82 |
83 | .map-wrap {
84 | position: relative;
85 | background: rgba(8, 253, 216, 0.1);
86 | float: right;
87 | width: 53%;
88 | height: 115%;
89 | margin-top: -3%;
90 | }
91 |
92 | .leaflet-container {
93 | position: relative;
94 | width: 100%;
95 | height: 100%;
96 | opacity: 0;
97 | animation: backInRight 1s 1.2s;
98 | animation-fill-mode: forwards;
99 | }
100 |
101 | .info-map {
102 | position: absolute;
103 | background: #000;
104 | top: 2%;
105 | left: 6%;
106 | z-index: 1;
107 | width: 30%;
108 | padding: 3%;
109 | color: #fff;
110 | font-family: 'Helvetica';
111 | font-size: 17px;
112 | font-weight: 300;
113 | opacity: 0;
114 | border-radius: 0.5rem;
115 | animation: fadeIn 1s 1.5s;
116 | animation-fill-mode: forwards;
117 |
118 | span {
119 | font-size: 16px;
120 |
121 | span {
122 | color: #ffd700;
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/components/Home/Logo/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import { useRef } from 'react'
3 |
4 | import LogoS from '../../../assets/images/logo-s.png'
5 | import './index.scss'
6 |
7 | const Logo = () => {
8 | const bgRef = useRef()
9 | const outlineLogoRef = useRef()
10 | const solidLogoRef = useRef()
11 |
12 | return (
13 |
14 |
21 |
22 |
29 |
34 |
38 |
39 |
40 |
41 | )
42 | }
43 |
44 | export default Logo
45 |
--------------------------------------------------------------------------------
/src/components/Home/Logo/index.scss:
--------------------------------------------------------------------------------
1 | .logo-container {
2 | z-index: 0;
3 | width: 400px;
4 | height: 609px;
5 | opacity: 1;
6 | position: absolute;
7 | top: 0;
8 | right: 15%;
9 | bottom: 0;
10 | left: auto;
11 | margin: auto;
12 | animation: zoomInDown 2.5s ease-in-out 1s;
13 | user-select: none;
14 |
15 | svg {
16 | width: 100%;
17 | height: auto;
18 | bottom: 0;
19 | transform: rotateZ(30deg) !important;
20 | }
21 |
22 | .solid-logo {
23 | position: absolute;
24 | top: auto;
25 | right: auto;
26 | bottom: auto;
27 | left: 0;
28 | margin: auto;
29 | width: 100%;
30 | opacity: 1;
31 | transform: rotateZ(30deg) !important;
32 | z-index: 1;
33 | }
34 | }
35 |
36 | .svg-container {
37 | stroke: #ffd700;
38 | stroke-width: 10;
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/Home/index.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | import Loader from 'react-loaders'
4 | import { Link } from 'react-router-dom'
5 |
6 | import Logo from './Logo'
7 | import LogoTitle from '../../assets/images/logo-s.png'
8 | import AnimatedLetters from '../AnimatedLetters'
9 | import './index.scss'
10 | import styles from './index.scss'
11 |
12 | const Home = () => {
13 | const [letterClass, setLetterClass] = useState('text-animate')
14 |
15 | const nameArray = 'udip'.split('')
16 | const jobArray = 'Software Engineer'.split('')
17 | const interestArray = 'FullStack | DevOps | Web3'.split('')
18 |
19 | useEffect(() => {
20 | const timer = setTimeout(() => {
21 | setLetterClass('text-animate-hover')
22 | }, 4000)
23 | return () => clearTimeout(timer)
24 | }, [])
25 |
26 | return (
27 | <>
28 |
29 |
30 |
31 | H
32 | i,
33 |
34 | I
35 | 'm
36 |
37 |
42 |
43 |
48 |
49 |
54 |
55 |
56 | Streamlining Workflows | Deploying Intelligent Systems | Building
57 | Scalable Applications
58 |
59 |
60 | CONTACT ME
61 |
62 |
63 |
64 |
65 |
66 |
67 | >
68 | )
69 | }
70 |
71 | export default Home
72 |
--------------------------------------------------------------------------------
/src/components/Home/index.scss:
--------------------------------------------------------------------------------
1 | .home-page {
2 | .text-zone {
3 | position: absolute;
4 | left: 10%;
5 | top: 50%;
6 | transform: translateY(-50%);
7 | width: 40%;
8 | max-height: 90%;
9 |
10 | h1 {
11 | color: #fff;
12 | font-size: 56px;
13 | line-height: 63px;
14 | margin: 0;
15 | font-family: 'Coolvetica';
16 | font-weight: 400;
17 |
18 | &:before {
19 | content: '';
20 | font-family: 'La Belle Aurore', cursive;
21 | color: #00ff7f;
22 | font-size: 18px;
23 | position: absolute;
24 | margin-top: -40px;
25 | left: -15px;
26 | opacity: 0.9;
27 | }
28 |
29 | &:after {
30 | content: ' ';
31 | font-family: 'La Belle Aurore', cursive;
32 | color: #00ff7f;
33 | font-size: 18px;
34 | position: absolute;
35 | margin-top: 18px;
36 | margin-left: 20px;
37 | animation: fadeIn 1s 1.7s backwards;
38 | opacity: 0.9;
39 | }
40 | }
41 |
42 | img {
43 | margin-left: 20px;
44 | opacity: 0;
45 | width: 32px;
46 | height: auto;
47 | animation: rotateIn 1s linear both;
48 | animation-delay: 1.4s;
49 | user-select: none;
50 | }
51 | }
52 |
53 | h2 {
54 | color: #8d8d8d;
55 | margin-top: 20px;
56 | font-weight: 400;
57 | font-size: 11px;
58 | font-family: sans-serif;
59 | letter-spacing: 3px;
60 | animation: fadeIn 1s 1.8s backwards;
61 | }
62 |
63 | .flat-button {
64 | color: #00ff7f;
65 | font-size: 13px;
66 | font-weight: 400;
67 | letter-spacing: 4px;
68 | font-family: sans-serif;
69 | text-decoration: none;
70 | padding: 10px 18px;
71 | border: 1px solid #00ff7f;
72 | margin-top: 25px;
73 | float: left;
74 | animation: fadeInAnimation 1s 1.8s backwards;
75 | white-space: nowrap;
76 | border-radius: 0.5rem;
77 |
78 | &:hover {
79 | background: #00ff7f;
80 | color: rgb(0, 0, 0);
81 | }
82 | }
83 |
84 | .selectDisable {
85 | -webkit-user-select: none;
86 | -khtml-user-select: none;
87 | -moz-user-select: none;
88 | -o-user-select: none;
89 | user-select: none;
90 | }
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/src/components/Layout/index.js:
--------------------------------------------------------------------------------
1 | import { Outlet } from 'react-router-dom'
2 |
3 | import Sidebar from '../Sidebar/'
4 | import './index.scss'
5 |
6 | const Layout = () => {
7 | return (
8 |
9 |
10 |
11 |
<body>
12 |
13 |
14 |
15 |
16 |
17 |
18 | </body>
19 |
20 | </html>
21 |
22 |
23 |
24 | )
25 | }
26 |
27 | export default Layout
28 |
--------------------------------------------------------------------------------
/src/components/Layout/index.scss:
--------------------------------------------------------------------------------
1 | .page {
2 | width: 100%;
3 | height: 100%;
4 | user-select: none;
5 | }
6 |
7 | .top-tags {
8 | bottom: auto;
9 | top: 35px;
10 | }
11 |
12 | .tags {
13 | // color: #ffd700;
14 | // color: #ff4500; // red
15 | color: #00ff7f; // green
16 | opacity: 0.9;
17 | position: absolute;
18 | bottom: 0;
19 | left: 120px;
20 | font-size: 18px;
21 | font-family: 'La Belle Aurore', cursive;
22 | }
23 |
24 | .bottom-tag-html {
25 | margin-left: -20px;
26 | }
27 |
28 | .container {
29 | width: 100%;
30 | will-change: contents;
31 | height: 90%;
32 | min-height: 566px;
33 | position: absolute;
34 | opacity: 0;
35 | top: 5%;
36 | margin: 0 auto;
37 | z-index: 1;
38 | transform-style: preserve-3d;
39 | animation: fadeIn 1s forwards;
40 | animation-delay: 1s;
41 | }
42 |
43 | .container.contact-page,
44 | .container.about-page,
45 | .container.skills-page {
46 | .text-zone {
47 | position: absolute;
48 | left: 10%;
49 | top: 50%;
50 | transform: translateY(-50%);
51 | width: 35%;
52 | vertical-align: middle;
53 | display: table-cell;
54 | max-height: 90%;
55 |
56 | h1 {
57 | font-size: 53px;
58 | font-family: 'Coolvetica';
59 | color: #00ff7f;
60 | font-weight: 400;
61 | margin-top: 0;
62 | position: relative;
63 | margin-bottom: 40px;
64 | left: -10px;
65 |
66 | &:before {
67 | content: '';
68 | font-family: 'La Belle Aurore', cursive;
69 | color: #00ff7f;
70 | font-size: 18px;
71 | position: absolute;
72 | margin-top: -10px;
73 | left: -10px;
74 | opacity: 0.6;
75 | line-height: 18px;
76 | }
77 |
78 | &:after {
79 | content: ' ';
80 | font-family: 'La Belle Aurore', cursive;
81 | color: #00ff7f;
82 | font-size: 18px;
83 | line-height: 18px;
84 | position: absolute;
85 | left: -30px;
86 | bottom: -20px;
87 | margin-left: 20px;
88 | opacity: 0.6;
89 | }
90 | }
91 |
92 | p {
93 | font-size: 17px;
94 | color: #fff;
95 | font-family: sans-serif;
96 | font-weight: 300;
97 | max-width: fit-content;
98 | animation: pulse 1s;
99 | &:nth-of-type(1) {
100 | animation-delay: 1.1s;
101 | }
102 | &:nth-of-type(2) {
103 | animation-delay: 1.2s;
104 | }
105 | &:nth-of-type(3) {
106 | animation-delay: 1.3s;
107 | }
108 | }
109 | }
110 |
111 | .text-animate-hover {
112 | &:hover {
113 | color: #ff4500;
114 | }
115 | }
116 | }
117 |
118 | // n is number of stars required
119 | @function multiple-box-shadow($n) {
120 | $value: '#{random(2000)}px #{random(2000)}px #FFF';
121 | @for $i from 2 through $n {
122 | $value: '#{$value} , #{random(2000)}px #{random(2000)}px #FFF';
123 | }
124 | @return unquote($value);
125 | }
126 |
127 | $shadows-small: multiple-box-shadow(1400);
128 | $shadows-medium: multiple-box-shadow(400);
129 | $shadows-big: multiple-box-shadow(200);
130 |
131 | html {
132 | height: 100%;
133 | // background: radial-gradient(ellipse at bottom, #1B2735 0%, #090A0F 100%);
134 | overflow: hidden;
135 | }
136 | #stars {
137 | width: 1px;
138 | height: 1px;
139 | background: transparent;
140 | box-shadow: $shadows-small;
141 | animation: animStar 50s linear infinite;
142 |
143 | &:after {
144 | content: ' ';
145 | position: absolute;
146 | top: 2000px;
147 | width: 1px;
148 | height: 1px;
149 | background: transparent;
150 | box-shadow: $shadows-small;
151 | }
152 | }
153 | #stars2 {
154 | width: 2px;
155 | height: 2px;
156 | background: transparent;
157 | box-shadow: $shadows-medium;
158 | animation: animStar 100s linear infinite;
159 |
160 | &:after {
161 | content: ' ';
162 | position: absolute;
163 | top: 2000px;
164 | width: 2px;
165 | height: 2px;
166 | background: transparent;
167 | box-shadow: $shadows-medium;
168 | }
169 | }
170 | #stars3 {
171 | width: 3px;
172 | height: 3px;
173 | background: transparent;
174 | box-shadow: $shadows-big;
175 | animation: animStar 150s linear infinite;
176 |
177 | &:after {
178 | content: ' ';
179 | position: absolute;
180 | top: 2000px;
181 | width: 3px;
182 | height: 3px;
183 | background: transparent;
184 | box-shadow: $shadows-big;
185 | }
186 | }
187 |
188 | @keyframes animStar {
189 | from {
190 | transform: translateY(0px);
191 | }
192 | to {
193 | transform: translateY(-2000px);
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/components/Sidebar/index.js:
--------------------------------------------------------------------------------
1 | import './index.scss'
2 | import { faGithub, faLinkedin } from '@fortawesome/free-brands-svg-icons'
3 | import {
4 | faEnvelope,
5 | faHome,
6 | faScrewdriverWrench,
7 | faUser,
8 | } from '@fortawesome/free-solid-svg-icons'
9 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
10 | import { Link, NavLink } from 'react-router-dom'
11 |
12 | import codechefLogo from '../../assets/images/codechef.png'
13 | import LogoS from '../../assets/images/logo-s.png'
14 | import LogoSubtitle from '../../assets/images/Sudip_logo_white.png'
15 |
16 | const Sidebar = () => {
17 | return (
18 | <>
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
77 |
78 | >
79 | )
80 | }
81 |
82 | export default Sidebar
83 |
--------------------------------------------------------------------------------
/src/components/Sidebar/index.scss:
--------------------------------------------------------------------------------
1 | .nav-bar {
2 | background: #181818;
3 | width: 60px;
4 | height: 100%;
5 | position: absolute;
6 | top: 0;
7 | z-index: 3;
8 | min-height: 500px;
9 |
10 | .logo {
11 | display: block;
12 | padding: 8px 0;
13 |
14 | img {
15 | display: block;
16 | margin: 8px auto;
17 | width: 24px;
18 | height: auto;
19 |
20 | &.sub-logo {
21 | width: 50px;
22 | }
23 | }
24 | }
25 |
26 | nav {
27 | display: block;
28 | text-align: center;
29 | position: absolute;
30 | height: 210px;
31 | top: 50%;
32 | margin-top: -120px;
33 | width: 100%;
34 |
35 | a {
36 | font-size: 22px;
37 | color: #4d4d4e;
38 | display: block;
39 | line-height: 51px;
40 | height: 51px;
41 | position: relative;
42 | text-decoration: none;
43 |
44 | i {
45 | transition: all 0.3s ease-out;
46 | }
47 |
48 | &:hover {
49 | color: #ffd700;
50 |
51 | svg {
52 | opacity: 0;
53 | }
54 |
55 | &:after {
56 | opacity: 1;
57 | }
58 | }
59 |
60 | &:after {
61 | content: '';
62 | font-size: 9px;
63 | letter-spacing: 2px;
64 | position: absolute;
65 | bottom: 0;
66 | display: block;
67 | width: 100%;
68 | text-align: center;
69 | opacity: 0;
70 | -webkit-transition: all 0.3s ease-out;
71 | transition: all 0.3s ease-out;
72 | }
73 |
74 | &:first-child {
75 | &:after {
76 | content: 'HOME';
77 | }
78 | }
79 | }
80 |
81 | a.about-link {
82 | &:after {
83 | content: 'ABOUT';
84 | }
85 | }
86 |
87 | a.contact-link {
88 | &:after {
89 | content: 'CONTACT';
90 | }
91 | }
92 |
93 | a.skills-link {
94 | &:after {
95 | content: 'SKILLS';
96 | }
97 | }
98 |
99 | a.active {
100 | svg {
101 | color: #ffd700;
102 | }
103 | }
104 | }
105 |
106 | ul {
107 | position: absolute;
108 | bottom: 20px;
109 | width: 100%;
110 | display: block;
111 | padding: 0;
112 | list-style: none;
113 | text-align: center;
114 | margin: 0;
115 |
116 | li {
117 | a {
118 | padding: 7px 0;
119 | display: block;
120 | font-size: 15px;
121 | line-height: 16px;
122 | color: #4d4d4e;
123 |
124 | &:hover {
125 | color: #ffd700;
126 | }
127 | }
128 | }
129 | }
130 | }
131 |
132 | .random {
133 | color: #b9b9b9;
134 | }
135 |
--------------------------------------------------------------------------------
/src/components/Skills/index.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 |
3 | import Loader from 'react-loaders'
4 |
5 | import WordCloud from './wordcloud'
6 | import AnimatedLetters from '../AnimatedLetters'
7 | import './index.scss'
8 |
9 | const Skills = () => {
10 | const [letterClass, setLetterClass] = useState('text-animate')
11 |
12 | const skillsArray = 'Skills'.split('')
13 |
14 | useEffect(() => {
15 | const timer = setTimeout(() => {
16 | setLetterClass('text-animate-hover')
17 | }, 2000)
18 | return () => clearTimeout(timer)
19 | }, [])
20 |
21 | return (
22 | <>
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 | I have a strong foundation in both development and operations, with
35 | a focus on creating seamless, efficient systems. My experience
36 | includes automating deployment processes, designing scalable
37 | applications, and working with cloud technologies to deliver
38 | reliable solutions.
39 |
40 |
41 | My skill set spans across DevOps, machine learning, full-stack
42 | development, and cloud infrastructure. I’m committed to staying
43 | updated with the latest advancements and continually refining my
44 | expertise to tackle complex challenges effectively.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | >
55 | )
56 | }
57 |
58 | export default Skills
59 |
--------------------------------------------------------------------------------
/src/components/Skills/index.scss:
--------------------------------------------------------------------------------
1 | .tagcloud-wrap {
2 | // background: gray;
3 | float: right;
4 | width: 53%;
5 | height: 115%;
6 | margin-top: -3%;
7 |
8 | .tagcloud {
9 | color: #ff4500;
10 | font-family: 'Poppins', sans-serif;
11 | font-size: 20px;
12 | font-weight: 650;
13 | margin: 10% auto;
14 | }
15 | .tagcloud--item:hover {
16 | color: #00ff7f;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/Skills/wordcloud.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 |
3 | import TagCloud from 'TagCloud'
4 |
5 | const WordCloud = () => {
6 | const [isLoading, setLoad] = useState(true)
7 |
8 | const container = '.content'
9 | const texts = [
10 | 'Python',
11 | 'Typescript',
12 | 'Golang',
13 | 'C++',
14 | 'SQL',
15 | 'Bash',
16 | 'React',
17 | 'NextJS',
18 | 'Firebase',
19 | 'Django',
20 | 'Flask',
21 | 'NodeJS',
22 | 'Express',
23 | 'GraphQL',
24 | 'FastAPI',
25 | 'MySQL',
26 | 'PostgreSQL',
27 | 'MongoDB',
28 | 'Redis',
29 | 'TensorFlow',
30 | 'Keras',
31 | 'PyTorch',
32 | 'Scikit-learn',
33 | 'OpenCV',
34 | 'Docker',
35 | 'Kubernetes',
36 | 'GCP',
37 | 'AWS',
38 | 'Terraform',
39 | 'Git',
40 | 'GitHub',
41 | 'Linux',
42 | 'Java',
43 | 'Flutter',
44 | 'Dart',
45 | 'HTML5',
46 | 'CSS3',
47 | 'JS',
48 | 'C',
49 | 'Solidity',
50 | ]
51 | const options = {
52 | radius: 300,
53 | // animation speed
54 | // slow, normal, fast
55 | maxSpeed: 'fast',
56 | initSpeed: 'fast',
57 | // 0 = top
58 | // 90 = left
59 | // 135 = right-bottom
60 | direction: 135,
61 | // interact with cursor move on mouse out
62 | keep: true,
63 | }
64 | // to render wordcloud each time the page is reloaded
65 | useEffect(() => {
66 | if (isLoading) {
67 | TagCloud(container, texts, options)
68 | setLoad(false)
69 | }
70 | })
71 |
72 | return (
73 |
74 |
75 |
76 | )
77 | }
78 |
79 | export default WordCloud
80 |
--------------------------------------------------------------------------------
/src/components/Soundbar/index.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState } from 'react'
2 |
3 | import styled, { keyframes } from 'styled-components'
4 |
5 | import music from '../../assets/sounds/fake_verthandi.mp3'
6 |
7 | const Box = styled.div`
8 | display: flex;
9 | cursor: pointer;
10 | position: fixed;
11 | right: 8rem;
12 | top: 3rem;
13 | z-index: 3;
14 |
15 | & > *:nth-child(1) {
16 | animation-delay: 0.2s;
17 | }
18 | & > *:nth-child(2) {
19 | animation-delay: 0.3s;
20 | }
21 | & > *:nth-child(3) {
22 | animation-delay: 0.4s;
23 | }
24 | & > *:nth-child(4) {
25 | animation-delay: 0.5s;
26 | }
27 | & > *:nth-child(5) {
28 | animation-delay: 0.8s;
29 | }
30 |
31 | &:before {
32 | content: '';
33 | font-family: 'La Belle Aurore', cursive;
34 | color: #00ff7f;
35 | font-size: 18px;
36 | position: absolute;
37 | margin-top: -20px;
38 | left: -60px;
39 | opacity: 0.9;
40 | line-height: 20 px;
41 | }
42 |
43 | &:after {
44 | content: ' ';
45 | font-family: 'La Belle Aurore', cursive;
46 | color: #00ff7f;
47 | font-size: 18px;
48 | line-height: 20px;
49 | position: absolute;
50 | left: -60px;
51 | bottom: -20px;
52 | opacity: 0.9;
53 | }
54 | `
55 |
56 | const play = keyframes`
57 | 0%{
58 | transform:scaleY(1);
59 | }
60 | 50%{
61 | transform:scaleY(2);
62 | }
63 | 100%{
64 | transform:scaleY(1);
65 | }
66 | `
67 | const Line = styled.span`
68 | background: #00ff7f;
69 | border: 1px solid;
70 |
71 | animation: ${play} 1s ease infinite;
72 | animation-play-state: ${(props) => (props.itemScope ? 'running' : 'paused')};
73 | height: 1rem;
74 | width: 2px;
75 | margin: 0 0.1rem;
76 | `
77 | const SoundBar = () => {
78 | const ref = useRef(null)
79 | const [click, setClick] = useState(false)
80 |
81 | const handleClick = () => {
82 | setClick(!click)
83 |
84 | if (!click) {
85 | ref.current.play()
86 | } else {
87 | ref.current.pause()
88 | }
89 | }
90 | return (
91 | handleClick()}>
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | )
100 | }
101 |
102 | export default SoundBar
103 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-size: 62.5%;
3 | }
4 |
5 | body {
6 | margin: 0;
7 | font:
8 | 300 11px/1.4 'Helvetica Neue',
9 | sans-serif;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | background: #252525;
13 | overflow: hidden;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import ReactDOM from 'react-dom/client'
4 | import { BrowserRouter } from 'react-router-dom'
5 |
6 | import App from './App'
7 | import reportWebVitals from './reportWebVitals'
8 | import './index.css'
9 |
10 | // TODO: Add mouse-trail, work page
11 | const root = ReactDOM.createRoot(document.getElementById('root'))
12 |
13 | root.render(
14 |
15 |
18 |
19 |
20 |
21 | )
22 |
23 | reportWebVitals()
24 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = (onPerfEntry) => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry)
5 | getFID(onPerfEntry)
6 | getFCP(onPerfEntry)
7 | getLCP(onPerfEntry)
8 | getTTFB(onPerfEntry)
9 | })
10 | }
11 | }
12 |
13 | export default reportWebVitals
14 |
--------------------------------------------------------------------------------