├── .eslintignore
├── .node-version
├── .prettierignore
├── src
├── components
│ ├── Contact
│ │ ├── Contact.css
│ │ └── Contact.js
│ ├── Projects
│ │ ├── Projects.css
│ │ └── Projects.js
│ ├── Skills
│ │ ├── Skills.css
│ │ └── Skills.js
│ ├── ScrollToTop
│ │ ├── ScrollToTop.css
│ │ └── ScrollToTop.js
│ ├── Header
│ │ ├── Header.css
│ │ └── Header.js
│ ├── Footer
│ │ ├── Footer.js
│ │ └── Footer.css
│ ├── ProjectContainer
│ │ ├── ProjectContainer.css
│ │ └── ProjectContainer.js
│ ├── Navbar
│ │ ├── Navbar.css
│ │ └── Navbar.js
│ └── About
│ │ ├── About.css
│ │ └── About.js
├── index.js
├── index.css
├── App.js
├── contexts
│ └── theme.js
├── portfolio.js
└── App.css
├── public
├── images
│ └── cleanfolio.png
└── index.html
├── .prettierrc.json
├── .gitignore
├── .eslintrc.json
├── LICENSE
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | v16.7.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/src/components/Contact/Contact.css:
--------------------------------------------------------------------------------
1 | .contact {
2 | flex-direction: column;
3 | }
4 |
--------------------------------------------------------------------------------
/public/images/cleanfolio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjshkhr/cleanfolio/HEAD/public/images/cleanfolio.png
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "semi": false,
4 | "singleQuote": true,
5 | "jsxSingleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/src/components/Projects/Projects.css:
--------------------------------------------------------------------------------
1 | .projects__grid {
2 | max-width: 1100px;
3 | margin: 0 auto;
4 | display: grid;
5 | grid-template-columns: repeat(auto-fit, minmax(18em, 1fr));
6 | grid-gap: 2em;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/Skills/Skills.css:
--------------------------------------------------------------------------------
1 | .skills__list {
2 | max-width: 450px;
3 | width: 95%;
4 | margin: 0 auto;
5 | display: flex;
6 | flex-wrap: wrap;
7 | justify-content: center;
8 | }
9 |
10 | .skills__list-item {
11 | margin: 0.5em;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/ScrollToTop.css:
--------------------------------------------------------------------------------
1 | .scroll-top {
2 | position: fixed;
3 | bottom: 2em;
4 | right: 4em;
5 | background-color: transparent;
6 | }
7 |
8 | @media (max-width: 900px) {
9 | .scroll-top {
10 | display: none;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/Header/Header.css:
--------------------------------------------------------------------------------
1 | .header {
2 | height: 8em;
3 | max-width: 1100px;
4 | width: 95%;
5 | margin: 0 auto;
6 | justify-content: space-between;
7 | }
8 |
9 | @media (max-width: 600px) {
10 | .header {
11 | height: 6em;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { render } from 'react-dom'
2 | import App from './App'
3 | import { ThemeProvider } from './contexts/theme'
4 | import './index.css'
5 |
6 | render(
7 |
8 |
9 | ,
10 | document.getElementById('root')
11 | )
12 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import './Footer.css'
2 |
3 | const Footer = () => (
4 |
12 | )
13 |
14 | export default Footer
15 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | padding: 3em 0;
3 | margin-top: 4em;
4 | text-align: center;
5 | }
6 |
7 | .footer__link {
8 | font-size: 0.9rem;
9 | font-weight: 600;
10 | color: var(--clr-fg);
11 | }
12 |
13 | @media (max-width: 600px) {
14 | .footer {
15 | padding: 2em;
16 | margin-top: 3em;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.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.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/components/Contact/Contact.js:
--------------------------------------------------------------------------------
1 | import { contact } from '../../portfolio'
2 | import './Contact.css'
3 |
4 | const Contact = () => {
5 | if (!contact.email) return null
6 |
7 | return (
8 |
16 | )
17 | }
18 |
19 | export default Contact
20 |
--------------------------------------------------------------------------------
/src/components/Header/Header.js:
--------------------------------------------------------------------------------
1 | import { header } from '../../portfolio'
2 | import Navbar from '../Navbar/Navbar'
3 | import './Header.css'
4 |
5 | const Header = () => {
6 | const { homepage, title } = header
7 |
8 | return (
9 |
10 |
11 | {homepage ? (
12 |
13 | {title}
14 |
15 | ) : (
16 | title
17 | )}
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default Header
25 |
--------------------------------------------------------------------------------
/src/components/Skills/Skills.js:
--------------------------------------------------------------------------------
1 | import uniqid from 'uniqid'
2 | import { skills } from '../../portfolio'
3 | import './Skills.css'
4 |
5 | const Skills = () => {
6 | if (!skills.length) return null
7 |
8 | return (
9 |
10 | Skills
11 |
12 | {skills.map((skill) => (
13 | -
14 | {skill}
15 |
16 | ))}
17 |
18 |
19 | )
20 | }
21 |
22 | export default Skills
23 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:react/recommended",
9 | "airbnb",
10 | "prettier"
11 | ],
12 | "parserOptions": {
13 | "ecmaFeatures": {
14 | "jsx": true
15 | },
16 | "ecmaVersion": 12,
17 | "sourceType": "module"
18 | },
19 | "plugins": ["react", "prettier"],
20 | "rules": {
21 | "no-unused-vars": "warn",
22 | "no-console": "off",
23 | "react/react-in-jsx-scope": "off",
24 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
25 | "react/prop-types": "off"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/Projects/Projects.js:
--------------------------------------------------------------------------------
1 | import uniqid from 'uniqid'
2 | import { projects } from '../../portfolio'
3 | import ProjectContainer from '../ProjectContainer/ProjectContainer'
4 | import './Projects.css'
5 |
6 | const Projects = () => {
7 | if (!projects.length) return null
8 |
9 | return (
10 |
11 | Projects
12 |
13 |
14 | {projects.map((project) => (
15 |
16 | ))}
17 |
18 |
19 | )
20 | }
21 |
22 | export default Projects
23 |
--------------------------------------------------------------------------------
/src/components/ProjectContainer/ProjectContainer.css:
--------------------------------------------------------------------------------
1 | .project {
2 | padding: 2em;
3 | margin: 0 auto;
4 | text-align: center;
5 | box-shadow: var(--shadow);
6 | transition: transform 0.2s linear;
7 | }
8 |
9 | .project:hover {
10 | transform: translateY(-7px);
11 | }
12 |
13 | .project__description {
14 | margin-top: 1em;
15 | }
16 |
17 | .project__stack {
18 | display: flex;
19 | flex-wrap: wrap;
20 | justify-content: center;
21 | margin: 1.2em 0;
22 | }
23 |
24 | .project__stack-item {
25 | margin: 0.5em;
26 | font-weight: 500;
27 | font-size: 0.8rem;
28 | color: var(--clr-fg-alt);
29 | }
30 |
31 | .project .link--icon {
32 | margin-left: 0.5em;
33 | }
34 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 | John Smith
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/Navbar/Navbar.css:
--------------------------------------------------------------------------------
1 | .nav__list {
2 | margin-right: 1.5em;
3 | display: flex;
4 | }
5 | .nav__list-item {
6 | margin-left: 1.5em;
7 | }
8 |
9 | .app .nav__hamburger {
10 | display: none;
11 | }
12 |
13 | .nav__theme {
14 | margin-top: 0.4em;
15 | }
16 |
17 | @media (max-width: 600px) {
18 | .nav__list {
19 | display: none;
20 | flex-direction: column;
21 | justify-content: center;
22 | align-items: center;
23 | position: fixed;
24 | inset: 0;
25 | width: 100%;
26 | height: 100%;
27 | z-index: 2;
28 | }
29 |
30 | .nav__list-item {
31 | margin: 0.5em 0;
32 | }
33 |
34 | .app .nav__hamburger {
35 | display: flex;
36 | z-index: 2;
37 | margin-left: 0.8em;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/ScrollToTop.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import ArrowUpwardIcon from '@material-ui/icons/ArrowUpward'
3 | import './ScrollToTop.css'
4 |
5 | const ScrollToTop = () => {
6 | const [isVisible, setIsVisible] = useState(false)
7 |
8 | useEffect(() => {
9 | const toggleVisibility = () =>
10 | window.pageYOffset > 500 ? setIsVisible(true) : setIsVisible(false)
11 |
12 | window.addEventListener('scroll', toggleVisibility)
13 | return () => window.removeEventListener('scroll', toggleVisibility)
14 | }, [])
15 |
16 | return isVisible ? (
17 |
22 | ) : null
23 | }
24 |
25 | export default ScrollToTop
26 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | border: 0;
5 | outline: 0;
6 | background-color: inherit;
7 | color: inherit;
8 | font-family: inherit;
9 | font-size: inherit;
10 | box-shadow: none;
11 | box-sizing: border-box;
12 | }
13 |
14 | html {
15 | scroll-behavior: smooth;
16 | }
17 |
18 | h1,
19 | h2,
20 | h3,
21 | h4 {
22 | line-height: 1.2;
23 | color: var(--clr-fg-alt);
24 | }
25 |
26 | h1 {
27 | font-size: 4rem;
28 | }
29 |
30 | h2 {
31 | font-size: 2rem;
32 | }
33 |
34 | h3 {
35 | font-size: 1.5rem;
36 | }
37 |
38 | h4 {
39 | font-size: 1.3rem;
40 | }
41 |
42 | ul {
43 | list-style-type: none;
44 | }
45 |
46 | a {
47 | text-decoration: none;
48 | }
49 |
50 | button {
51 | cursor: pointer;
52 | }
53 |
54 | @media (max-width: 900px) {
55 | h1 {
56 | font-size: 2.6rem;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import { ThemeContext } from './contexts/theme'
3 | import Header from './components/Header/Header'
4 | import About from './components/About/About'
5 | import Projects from './components/Projects/Projects'
6 | import Skills from './components/Skills/Skills'
7 | import ScrollToTop from './components/ScrollToTop/ScrollToTop'
8 | import Contact from './components/Contact/Contact'
9 | import Footer from './components/Footer/Footer'
10 | import './App.css'
11 |
12 | const App = () => {
13 | const [{ themeName }] = useContext(ThemeContext)
14 |
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default App
33 |
--------------------------------------------------------------------------------
/src/contexts/theme.js:
--------------------------------------------------------------------------------
1 | import { createContext, useEffect, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | const ThemeContext = createContext()
5 |
6 | const ThemeProvider = ({ children }) => {
7 | const [themeName, setThemeName] = useState('light')
8 |
9 | useEffect(() => {
10 | const darkMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
11 | setThemeName(darkMediaQuery.matches ? 'dark' : 'light')
12 | darkMediaQuery.addEventListener('change', (e) => {
13 | setThemeName(e.matches ? 'dark' : 'light')
14 | });
15 | }, [])
16 |
17 | const toggleTheme = () => {
18 | const name = themeName === 'dark' ? 'light' : 'dark'
19 | localStorage.setItem('themeName', name)
20 | setThemeName(name)
21 | }
22 |
23 | return (
24 |
25 | {children}
26 |
27 | )
28 | }
29 |
30 | ThemeProvider.propTypes = {
31 | children: PropTypes.node.isRequired,
32 | }
33 |
34 | export { ThemeProvider, ThemeContext }
35 |
--------------------------------------------------------------------------------
/src/components/About/About.css:
--------------------------------------------------------------------------------
1 | .about {
2 | flex-direction: column;
3 | margin-top: 3em;
4 | }
5 |
6 | .about__name {
7 | color: var(--clr-primary);
8 | }
9 |
10 | .about__role {
11 | margin-top: 1.2em;
12 | }
13 |
14 | .about__desc {
15 | font-size: 1rem;
16 | max-width: 600px;
17 | }
18 |
19 | .about__desc,
20 | .about__contact {
21 | margin-top: 2.4em;
22 | }
23 |
24 | .about .link--icon {
25 | margin-right: 0.8em;
26 | }
27 |
28 | .about .btn--outline {
29 | margin-right: 1em;
30 | }
31 |
32 | @media (max-width: 600px) {
33 | .app .about {
34 | align-items: flex-start;
35 | margin-top: 2em;
36 | }
37 | }
38 |
39 | .about__header {
40 | display: flex;
41 | align-items: center;
42 | gap: 1.5rem; /* space between picture and text */
43 | margin-bottom: 1.5rem;
44 | }
45 |
46 | .about__picture {
47 | width: 180px;
48 | height: 180px;
49 | border-radius: 70%; /* round profile */
50 | object-fit: cover;
51 | box-shadow: 0 4px 10px rgba(0,0,0,0.15);
52 | }
53 |
54 | .about__intro {
55 | display: flex;
56 | flex-direction: column;
57 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Raj Shekhar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/ProjectContainer/ProjectContainer.js:
--------------------------------------------------------------------------------
1 | import uniqid from 'uniqid'
2 | import GitHubIcon from '@material-ui/icons/GitHub'
3 | import LaunchIcon from '@material-ui/icons/Launch'
4 | import './ProjectContainer.css'
5 |
6 | const ProjectContainer = ({ project }) => (
7 |
8 |
9 | {project.image && (

18 | )}
19 |
20 |
{project.name}
21 |
22 |
{project.description}
23 | {project.stack && (
24 |
25 | {project.stack.map((item) => (
26 | -
27 | {item}
28 |
29 | ))}
30 |
31 | )}
32 |
33 | {project.sourceCode && (
34 |
39 |
40 |
41 | )}
42 |
43 | {project.livePreview && (
44 |
49 |
50 |
51 | )}
52 |
53 | )
54 |
55 | export default ProjectContainer
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cleanfolio",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://rjshkhr.github.io/cleanfolio",
6 | "dependencies": {
7 | "@material-ui/core": "^4.12.3",
8 | "@material-ui/icons": "^4.11.2",
9 | "@testing-library/jest-dom": "^5.14.1",
10 | "@testing-library/react": "^12.0.0",
11 | "@testing-library/user-event": "^13.2.1",
12 | "gh-pages": "^3.2.3",
13 | "prop-types": "^15.7.2",
14 | "react": "^17.0.2",
15 | "react-dom": "^17.0.2",
16 | "react-scripts": "4.0.3",
17 | "uniqid": "^5.4.0",
18 | "web-vitals": "^2.1.0"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start --openssl-legacy-provider",
22 | "build": "react-scripts build --openssl-legacy-provider",
23 | "test": "react-scripts test --openssl-legacy-provider",
24 | "eject": "react-scripts eject --openssl-legacy-provider",
25 | "lint": "eslint .",
26 | "format": "prettier --write \"**/*.+(js|jsx|json|css|md)\"",
27 | "deploy": "gh-pages -d build"
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | },
41 | "devDependencies": {
42 | "eslint": "^7.32.0",
43 | "eslint-config-airbnb": "18.2.1",
44 | "eslint-config-prettier": "^8.3.0",
45 | "eslint-plugin-import": "^2.24.2",
46 | "eslint-plugin-jsx-a11y": "^6.4.1",
47 | "eslint-plugin-prettier": "^4.0.0",
48 | "eslint-plugin-react": "^7.25.1",
49 | "eslint-plugin-react-hooks": "^4.2.0",
50 | "prettier": "^2.3.2"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cleanfolio
2 |
3 | Cleanfolio is a portfolio template built with React. However, if you prefer a template built with HTML, CSS, and JavaScript, you can check out [Cleanfolio Minimal](https://github.com/rjshkhr/cleanfolio-minimal).
4 |
5 | ## Preview
6 |
7 | [](https://rjshkhr.github.io/cleanfolio)
8 |
9 | [Live Demo](https://rjshkhr.github.io/cleanfolio)
10 |
11 | ## Instructions
12 |
13 | ### Setup
14 |
15 | ```shell
16 | git clone https://github.com/rjshkhr/cleanfolio
17 | cd cleanfolio
18 | ```
19 |
20 | If you use [nvm](https://github.com/nvm-sh/nvm) or [fnm](https://github.com/Schniz/fnm), execute:
21 |
22 | ```shell
23 | nvm install
24 | nvm use
25 | ```
26 |
27 | Or:
28 |
29 | ```shell
30 | fnm install
31 | fnm use
32 | ```
33 |
34 | To install and launch the project, run these commands:
35 |
36 | ```shell
37 | yarn
38 | yarn start
39 | ```
40 |
41 | ### How to Use
42 |
43 | - Open the `public/index.html` file and replace:
44 |
45 | `John Smith` with `Your Name`.
46 |
47 | - Open the `src/portfolio.js` file and make the necessary changes.
48 |
49 | - Optionally, you can add an image or logo for each project in two ways:
50 | 1. **Local image**
51 | - Put your image inside the `public/images/` folder
52 | - In `portfolio.js`, just use the file name:
53 | ```js
54 | image: "cleanfolio.png"
55 | ```
56 |
57 | 2. **Web image**
58 | - If your image is already hosted online, just paste the URL:
59 | ```js
60 | image: "https://example.com/my-logo.png"
61 | ```
62 |
63 |
64 |
65 | ### Deployment
66 |
67 | - In the `package.json` file, update:
68 |
69 | `"homepage": "https://rjshkhr.github.io/cleanfolio"`
70 |
71 | to `"homepage": "https://yourusername.github.io"`.
72 |
73 | - Push the changes to your repository.
74 |
75 | - To build and deploy, run the following commands:
76 |
77 | ```shell
78 | yarn build
79 | yarn deploy
80 | ```
81 |
82 | ## License
83 |
84 | [MIT](https://choosealicense.com/licenses/mit/)
85 |
--------------------------------------------------------------------------------
/src/components/About/About.js:
--------------------------------------------------------------------------------
1 | import GitHubIcon from '@material-ui/icons/GitHub'
2 | import LinkedInIcon from '@material-ui/icons/LinkedIn'
3 | import { about } from '../../portfolio'
4 | import './About.css'
5 |
6 | const About = () => {
7 | const { name, role, description, resume, social, picture } = about
8 |
9 | return (
10 |
11 |
12 | {picture && (
13 |

22 | )}
23 |
24 |
25 | {name && (
26 |
27 | Hi, I am {name}.
28 |
29 | )}
30 |
31 | {role &&
A {role}.
}
32 |
{description && description}
33 |
34 |
35 |
36 |
69 |
70 | )
71 | }
72 |
73 | export default About
74 |
--------------------------------------------------------------------------------
/src/components/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react'
2 | import Brightness2Icon from '@material-ui/icons/Brightness2'
3 | import WbSunnyRoundedIcon from '@material-ui/icons/WbSunnyRounded'
4 | import MenuIcon from '@material-ui/icons/Menu'
5 | import CloseIcon from '@material-ui/icons/Close'
6 | import { ThemeContext } from '../../contexts/theme'
7 | import { projects, skills, contact } from '../../portfolio'
8 | import './Navbar.css'
9 |
10 | const Navbar = () => {
11 | const [{ themeName, toggleTheme }] = useContext(ThemeContext)
12 | const [showNavList, setShowNavList] = useState(false)
13 |
14 | const toggleNavList = () => setShowNavList(!showNavList)
15 |
16 | return (
17 |
77 | )
78 | }
79 |
80 | export default Navbar
81 |
--------------------------------------------------------------------------------
/src/portfolio.js:
--------------------------------------------------------------------------------
1 | const header = {
2 | // all the properties are optional - can be left empty or deleted
3 | homepage: 'https://rjshkhr.github.io/cleanfolio',
4 | title: 'JS.',
5 | }
6 |
7 | const about = {
8 | // all the properties are optional - can be left empty or deleted
9 | name: 'John Smith',
10 | role: 'Front End Engineer',
11 | picture: 'https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png',
12 |
13 | description:
14 | 'Adipisicing sit fugit ullam unde aliquid sequi Facilis soluta facilis perspiciatis corporis nulla aspernatur. Autem eligendi rerum delectus modi quisquam? Illo ut quasi nemo ipsa cumque perspiciatis! Maiores minima consectetur.',
15 | resume: 'https://example.com',
16 | social: {
17 | linkedin: 'https://linkedin.com',
18 | github: 'https://github.com',
19 | },
20 | }
21 |
22 | const projects = [
23 | // projects can be added an removed
24 | // if there are no projects, Projects section won't show up
25 | {
26 | name: 'Project 1',
27 | description:
28 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
29 | stack: ['SASS', 'TypeScript', 'React'],
30 | sourceCode: 'https://github.com',
31 | livePreview: 'https://github.com',
32 | image: 'cleanfolio.png',
33 | },
34 | {
35 | name: 'Project 2',
36 | description:
37 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
38 | stack: ['SASS', 'TypeScript', 'React'],
39 | sourceCode: 'https://github.com',
40 | livePreview: 'https://github.com',
41 | image: 'https://github.githubassets.com/assets/GitHub-Logo-ee398b662d42.png',
42 | },
43 | {
44 | name: 'Project 3',
45 | description:
46 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
47 | stack: ['SASS', 'TypeScript', 'React'],
48 | sourceCode: 'https://github.com',
49 | livePreview: 'https://github.com',
50 | },
51 | ]
52 |
53 | const skills = [
54 | // skills can be added or removed
55 | // if there are no skills, Skills section won't show up
56 | 'HTML',
57 | 'CSS',
58 | 'JavaScript',
59 | 'TypeScript',
60 | 'React',
61 | 'Redux',
62 | 'SASS',
63 | 'Material UI',
64 | 'Git',
65 | 'CI/CD',
66 | 'Jest',
67 | 'Enzyme',
68 | ]
69 |
70 | const contact = {
71 | // email is optional - if left empty Contact section won't show up
72 | email: 'johnsmith@mail.com',
73 | }
74 |
75 | export { header, about, projects, skills, contact }
76 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .app {
2 | font-family: 'Poppins', sans-serif;
3 | line-height: 1.5;
4 | color: var(--clr-fg);
5 | background-color: var(--clr-bg);
6 | }
7 |
8 | .light {
9 | --clr-bg: #fcfcfc;
10 | --clr-bg-alt: #fff;
11 | --clr-fg: #555;
12 | --clr-fg-alt: #444;
13 | --clr-primary: #2978b5;
14 | --shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
15 | }
16 |
17 | .dark {
18 | --clr-bg: #23283e;
19 | --clr-bg-alt: #2a2f4c;
20 | --clr-fg: #bdbddd;
21 | --clr-fg-alt: #cdcdff;
22 | --clr-primary: #90a0d9;
23 | --shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px,
24 | rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;
25 | }
26 |
27 | main {
28 | max-width: 1100px;
29 | width: 95%;
30 | margin: 0 auto;
31 | }
32 |
33 | .section {
34 | margin-top: 5em;
35 | }
36 |
37 | .section__title {
38 | text-align: center;
39 | margin-bottom: 1em;
40 | text-transform: uppercase;
41 | }
42 |
43 | .center {
44 | display: flex;
45 | align-items: center;
46 | }
47 |
48 | .link {
49 | color: var(--clr-primary);
50 | padding: 0 0 0.3em 0;
51 | position: relative;
52 | }
53 |
54 | .link:hover {
55 | color: var(--clr-primary);
56 | }
57 |
58 | .link::before {
59 | content: '';
60 | display: inline;
61 | width: 0%;
62 | height: 0.2em;
63 | position: absolute;
64 | bottom: 0;
65 | background-color: var(--clr-primary);
66 | transition: width 0.2s ease-in;
67 | }
68 |
69 | .link:hover::before,
70 | .link:focus::before {
71 | width: 100%;
72 | }
73 |
74 | .link--nav {
75 | color: var(--clr-fg);
76 | text-transform: lowercase;
77 | font-weight: 500;
78 | }
79 |
80 | .link--icon {
81 | color: var(--clr-fg);
82 | }
83 |
84 | .btn {
85 | display: block;
86 | cursor: pointer;
87 | padding: 0.8em 1.4em;
88 | font-weight: 500;
89 | font-size: 0.9rem;
90 | text-transform: lowercase;
91 | transition: transform 0.2s ease-in-out;
92 | }
93 |
94 | .btn--outline {
95 | color: var(--clr-primary);
96 | border: 2px solid var(--clr-primary);
97 | position: relative;
98 | overflow: hidden;
99 | z-index: 1;
100 | }
101 |
102 | .btn--outline:hover,
103 | .btn--outline:focus {
104 | color: var(--clr-bg);
105 | }
106 |
107 | .btn--outline:before {
108 | content: '';
109 | position: absolute;
110 | background-color: var(--clr-primary);
111 | right: 100%;
112 | bottom: 0;
113 | left: 0;
114 | top: 0;
115 | z-index: -1;
116 | transition: right 0.2s ease-in-out;
117 | }
118 |
119 | .btn--outline:hover:before,
120 | .btn--outline:focus:before {
121 | right: 0;
122 | }
123 |
124 | .btn--plain {
125 | text-transform: initial;
126 | background-color: var(--clr-bg-alt);
127 | box-shadow: rgba(0, 0, 0, 0.15) 0px 3px 3px 0px;
128 | border: 0;
129 | }
130 |
131 | .btn--plain:hover {
132 | transform: translateY(-4px);
133 | }
134 |
135 | .btn--icon {
136 | padding: 0;
137 | }
138 |
139 | .btn--icon:hover,
140 | .btn--icon:focus {
141 | color: var(--clr-primary);
142 | }
143 |
144 | .btn--icon:active {
145 | transform: translateY(-5px);
146 | }
147 |
148 | @media (max-width: 600px) {
149 | .section {
150 | margin-top: 4em;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------