├── .gitignore ├── README.md ├── assets └── website.PNG ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── About │ ├── About.js │ └── About.module.css ├── Contact │ ├── Contact.js │ └── Contact.module.css ├── Landing │ ├── Landing.js │ └── Landing.module.css ├── Logo │ ├── Logo.js │ └── Logo.module.css ├── NavBar │ ├── Burger.js │ ├── NavBar.js │ └── RightNav.js ├── Preloader │ └── Preloader.js ├── Project │ ├── Project.js │ └── Project.module.css ├── ProjectList │ ├── ProjectList.js │ └── ProjectList.module.css ├── Social │ ├── Social.js │ └── Social.module.css └── UI │ ├── Cancel.js │ ├── Card.js │ ├── Card.module.css │ ├── Mouse.css │ ├── Mouse.js │ ├── ProjectModal.js │ ├── ProjectModal.module.css │ ├── ThankYou.js │ └── ThankYou.module.css ├── data.js ├── img ├── Fooder.png ├── Jingle.png ├── homepage.png ├── marker.png ├── notepad.png ├── oldportfolio.png └── simplify1.png ├── index.css └── index.js /.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 | config.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 18_React_NN 2 | React: Personal Portfolio 3 | 4 | ## Introduction 5 | Mobile responsive personal portfolio. This portfolio include modern features such as: 6 | * Landing page with engineered stroke-dash animation 7 | * A sticky side footer for socials 8 | * Animation on-hover navigation bar with link to Resume 9 | * Preloader animation using gsap library 10 | * Animated SVG scroll down button 11 | * Cursor animation 12 | * AOS on-scroll animation for smooth scrolling 13 | * Framer-motion to render component animations 14 | * Email JS to validate and send email from contact form 15 | * Google Map API to show current location 16 | * Spinning Skill Cube made with pure css 17 | 18 | ![](./assets/website.PNG) 19 | 20 | ## User Story 21 | 22 | ``` 23 | AS AN employer looking for candidates with experience building single-page applications 24 | I WANT to view a potential employee's deployed React portfolio of work samples 25 | SO THAT I can assess whether they're a good candidate for an open position 26 | ``` 27 | 28 | ## Acceptance Criteria 29 | 30 | ``` 31 | GIVEN a single-page application portfolio for a web developer 32 | WHEN I load the portfolio 33 | THEN I am presented with a page containing a header, a section for content, and a footer 34 | WHEN I view the header 35 | THEN I am presented with the developer's name and navigation with titles corresponding to different sections of the portfolio 36 | WHEN I view the navigation titles 37 | THEN I am presented with the titles About Me, Portfolio, Contact, and Resume, and the title corresponding to the current section is highlighted 38 | WHEN I click on a navigation title 39 | THEN I am presented with the corresponding section below the navigation without the page reloading and that title is highlighted 40 | WHEN I load the portfolio the first time 41 | THEN the About Me title and section are selected by default 42 | WHEN I am presented with the About Me section 43 | THEN I see a recent photo or avatar of the developer and a short bio about them 44 | WHEN I am presented with the Portfolio section 45 | THEN I see titled images of six of the developer’s applications with links to both the deployed applications and the corresponding GitHub repositories 46 | WHEN I am presented with the Contact section 47 | THEN I see a contact form with fields for a name, an email address, and a message 48 | WHEN I move my cursor out of one of the form fields without entering text 49 | THEN I receive a notification that this field is required 50 | WHEN I enter text into the email address field 51 | THEN I receive a notification if I have entered an invalid email address 52 | WHEN I am presented with the Resume section 53 | THEN I see a link to a downloadable resume and a list of the developer’s proficiencies 54 | WHEN I view the footer 55 | THEN I am presented with text or icon links to the developer’s GitHub and LinkedIn profiles, and their profile on a third platform (Stack Overflow, Twitter) 56 | 57 | ``` 58 | 59 | ## Submission 60 | This portfolio was uploaded to GitHub at the following repository link: 61 | [https://github.com/nhanng19/portfolio_react](https://github.com/nhanng19/portfolio_react) 62 | 63 | Deployed Link: 64 | [https://www.nhanngyn.tech/](https://www.nhanngyn.tech/) 65 | -------------------------------------------------------------------------------- /assets/website.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhanng19/portfolio_react/d1041400f2f475664d355e329ba7deccc456e06e/assets/website.PNG -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-svg-core": "^6.2.0", 7 | "@fortawesome/free-brands-svg-icons": "^6.2.0", 8 | "@fortawesome/react-fontawesome": "^0.2.0", 9 | "@react-google-maps/api": "^2.17.0", 10 | "@testing-library/jest-dom": "^5.16.5", 11 | "@testing-library/react": "^13.4.0", 12 | "@testing-library/user-event": "^13.5.0", 13 | "aos": "^2.3.4", 14 | "emailjs": "^4.0.1", 15 | "emailjs-com": "^3.2.0", 16 | "framer-motion": "^7.6.12", 17 | "gsap": "^3.11.3", 18 | "html-react-parser": "^3.0.4", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "react-dotenv": "^0.1.3", 22 | "react-scripts": "5.0.1", 23 | "react-scroll": "^1.8.8", 24 | "react-transition-group": "^4.4.5", 25 | "styled-components": "^5.3.6", 26 | "web-vitals": "^2.1.4" 27 | }, 28 | "scripts": { 29 | "start": "react-scripts start", 30 | "build": "react-scripts build", 31 | "test": "react-scripts test", 32 | "eject": "react-scripts eject" 33 | }, 34 | "eslintConfig": { 35 | "extends": [ 36 | "react-app", 37 | "react-app/jest" 38 | ] 39 | }, 40 | "browserslist": { 41 | "production": [ 42 | ">0.2%", 43 | "not dead", 44 | "not op_mini all" 45 | ], 46 | "development": [ 47 | "last 1 chrome version", 48 | "last 1 firefox version", 49 | "last 1 safari version" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhanng19/portfolio_react/d1041400f2f475664d355e329ba7deccc456e06e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 22 | 23 | 27 | 28 | 37 | Nhan N. || Full Stack Developer 38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 | 46 | 56 |
57 |
58 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhanng19/portfolio_react/d1041400f2f475664d355e329ba7deccc456e06e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhanng19/portfolio_react/d1041400f2f475664d355e329ba7deccc456e06e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 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 Landing from "./components/Landing/Landing"; 2 | import ProjectList from "./components/ProjectList/ProjectList"; 3 | import About from "./components/About/About"; 4 | import Contact from "./components/Contact/Contact"; 5 | import Social from "./components/Social/Social"; 6 | import Navbar from "./components/NavBar/NavBar"; 7 | import { useState } from "react"; 8 | import Preloader from "./components/Preloader/Preloader"; 9 | 10 | function App() { 11 | const [loading, setLoading] = useState(true); 12 | 13 | return ( 14 | <> 15 | {loading ? ( 16 | 17 | ) : ( 18 | <> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | )} 27 | 28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /src/components/About/About.js: -------------------------------------------------------------------------------- 1 | import styles from "./About.module.css"; 2 | import { 3 | faCss3, 4 | faGitAlt, 5 | faHtml5, 6 | faJsSquare, 7 | faReact, 8 | faEnvira, 9 | } from "@fortawesome/free-brands-svg-icons"; 10 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 11 | const About = () => { 12 | return ( 13 |
14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 |
40 |
41 | 42 |
43 |

About Me

44 |

45 | "Design is not just what it looks like and feels like. Design is how 46 | it works" - Steve Jobs 47 |

48 |

49 | I have always had an affinity for technology and the arts, a chemistry 50 | I gratefully utilize today as a developer. I have a serious passion 51 | for creating dynamic digital experiences and intuitive user interfaces 52 | while allowing seamless front-end integration to back-end systems. 53 | Minimalism and "less is more" have always been my philosophy on 54 | removing unnecessary distractions that would keep me from exuding my 55 | creativity and products from executing its idea effectively. I am 56 | interested in anything coding or design related, from software 57 | engineering to UI/UX, and look forward to working on incredible projects 58 | with positive people. 59 |

60 |
61 |
62 | ); 63 | }; 64 | 65 | export default About; 66 | -------------------------------------------------------------------------------- /src/components/About/About.module.css: -------------------------------------------------------------------------------- 1 | .about { 2 | height: 100vh; 3 | display: flex; 4 | align-items: center; 5 | } 6 | 7 | .left { 8 | flex: 1; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | position: relative; 13 | height: 100%; 14 | } 15 | 16 | .right { 17 | flex: 1; 18 | } 19 | 20 | .cube { 21 | flex: 1; 22 | } 23 | 24 | .title { 25 | font-weight: 400; 26 | } 27 | 28 | .sub { 29 | font-style: italic; 30 | margin: 20px 0px; 31 | } 32 | 33 | .description { 34 | font-weight: 300; 35 | } 36 | 37 | .cube { 38 | width: 200px; 39 | height: 200px; 40 | position: absolute; 41 | transform-style: preserve-3d; 42 | animation: rotate 30s linear infinite; 43 | border: none; 44 | } 45 | 46 | .box { 47 | width: 100%; 48 | height: 100%; 49 | position: absolute; 50 | top: 0; 51 | left: 0; 52 | opacity: 0.9; 53 | text-align: center; 54 | font-size: 100px; 55 | display: flex; 56 | justify-content: center; 57 | align-items: center; 58 | } 59 | 60 | 61 | 62 | .box1 { 63 | transform: translateZ(100px); 64 | } 65 | 66 | .box2 { 67 | transform: rotateY(90deg) translateX(100px); 68 | transform-origin: right; 69 | } 70 | 71 | .box3 { 72 | transform: rotateY(180deg) translateZ(100px); 73 | } 74 | 75 | .box4 { 76 | transform: rotateY(-90deg) translateX(-100px); 77 | transform-origin: left; 78 | } 79 | 80 | .box5 { 81 | transform: rotateX(-90deg) translateY(-100px); 82 | transform-origin: top; 83 | } 84 | 85 | .box6 { 86 | transform: rotateX(90deg) translateY(100px); 87 | transform-origin: bottom; 88 | } 89 | 90 | /* Animating the elements */ 91 | @keyframes rotate { 92 | 0%, 93 | 100% { 94 | transform: rotate(0deg); 95 | } 96 | 20% { 97 | transform: rotateY(90deg) rotateZ(90deg); 98 | } 99 | 40% { 100 | transform: rotateY(180deg) rotateZ(-90deg); 101 | } 102 | 60% { 103 | transform: rotateY(270deg) rotateZ(90deg); 104 | } 105 | 80% { 106 | transform: rotateY(360deg) rotateZ(-90deg); 107 | } 108 | } 109 | 110 | @media screen and (max-width: 390px) { 111 | .about { 112 | flex-direction: column; 113 | text-align: center; 114 | margin-top: 50px; 115 | overflow-x: hidden; 116 | } 117 | 118 | .left { 119 | width: 100%; 120 | } 121 | 122 | .right { 123 | padding: 20px; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/components/Contact/Contact.js: -------------------------------------------------------------------------------- 1 | import styles from "./Contact.module.css"; 2 | import { useRef, useState } from "react"; 3 | import emailjs from "emailjs-com"; 4 | import { GoogleMap, useLoadScript, MarkerF } from "@react-google-maps/api"; 5 | import Marker from "../../img/marker.png"; 6 | import Config from "../../config"; 7 | import ThankYou from "../UI/ThankYou"; 8 | import { AnimatePresence } from "framer-motion"; 9 | const Contact = () => { 10 | const formRef = useRef(); 11 | const [done, setDone] = useState(false); 12 | 13 | const handleSubmit = (e) => { 14 | e.preventDefault(); 15 | emailjs 16 | .sendForm( 17 | Config.SERVICE_ID, 18 | Config.TEMPLATE_ID, 19 | formRef.current, 20 | Config.USER_ID 21 | ) 22 | .then( 23 | (result) => { 24 | console.log(result.text); 25 | setDone(true); 26 | }, 27 | (error) => { 28 | console.log(error.text); 29 | } 30 | ); 31 | }; 32 | const { isLoaded } = useLoadScript({ 33 | googleMapsApiKey: Config.API_KEY, 34 | }); 35 | 36 | if (!isLoaded) return
Loading
; 37 | return ( 38 |
39 |
40 |

41 | Interested in my work? Get in touch and send me a message. 42 | Always open to build amazing projects. 43 |

44 |
45 | 51 | 57 | 63 |