├── .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 | ![image](https://user-images.githubusercontent.com/70171925/170053429-e124179c-3773-4456-abc0-47b8c9235988.png) 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 | 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 |
139 |
140 |
    141 |
  • 142 | 143 |
  • 144 |
  • 145 | 151 |
  • 152 |
  • 153 | 159 |
  • 160 |
  • 161 | 166 |
  • 167 |
  • 168 | 175 |
  • 176 |
177 | 178 | 179 |
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 | JavaScript,  Developer 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 | Sudip Banerjee 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 | Logo 22 | sudip 23 | 24 | 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 | 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 | --------------------------------------------------------------------------------