├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .node-version
├── .prettierignore
├── .prettierrc.json
├── LICENSE
├── README.md
├── package.json
├── public
└── index.html
├── src
├── App.css
├── App.js
├── components
│ ├── About
│ │ ├── About.css
│ │ └── About.js
│ ├── Contact
│ │ ├── Contact.css
│ │ └── Contact.js
│ ├── Footer
│ │ ├── Footer.css
│ │ └── Footer.js
│ ├── Header
│ │ ├── Header.css
│ │ └── Header.js
│ ├── Navbar
│ │ ├── Navbar.css
│ │ └── Navbar.js
│ ├── ProjectContainer
│ │ ├── ProjectContainer.css
│ │ └── ProjectContainer.js
│ ├── Projects
│ │ ├── Projects.css
│ │ └── Projects.js
│ ├── ScrollToTop
│ │ ├── ScrollToTop.css
│ │ └── ScrollToTop.js
│ └── Skills
│ │ ├── Skills.css
│ │ └── Skills.js
├── contexts
│ └── theme.js
├── index.css
├── index.js
└── portfolio.js
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | v16.7.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "semi": false,
4 | "singleQuote": true,
5 | "jsxSingleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | ### Deployment
50 |
51 | - In the `package.json` file, update:
52 |
53 | `"homepage": "https://rjshkhr.github.io/cleanfolio"`
54 |
55 | to `"homepage": "https://yourusername.github.io"`.
56 |
57 | - Push the changes to your repository.
58 |
59 | - To build and deploy, run the following commands:
60 |
61 | ```shell
62 | yarn build
63 | yarn deploy
64 | ```
65 |
66 | ## License
67 |
68 | [MIT](https://choosealicense.com/licenses/mit/)
69 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 | John Smith
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 } = about
8 |
9 | return (
10 |
11 | {name && (
12 |
13 | Hi, I am {name}.
14 |
15 | )}
16 |
17 | {role &&
A {role}.
}
18 |
{description && description}
19 |
20 |
53 |
54 | )
55 | }
56 |
57 | export default About
58 |
--------------------------------------------------------------------------------
/src/components/Contact/Contact.css:
--------------------------------------------------------------------------------
1 | .contact {
2 | flex-direction: column;
3 | }
4 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import './Footer.css'
2 |
3 | const Footer = () => (
4 |
12 | )
13 |
14 | export default Footer
15 |
--------------------------------------------------------------------------------
/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
{project.name}
9 |
10 |
{project.description}
11 | {project.stack && (
12 |
13 | {project.stack.map((item) => (
14 | -
15 | {item}
16 |
17 | ))}
18 |
19 | )}
20 |
21 | {project.sourceCode && (
22 |
27 |
28 |
29 | )}
30 |
31 | {project.livePreview && (
32 |
37 |
38 |
39 | )}
40 |
41 | )
42 |
43 | export default ProjectContainer
44 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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/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/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/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 | description:
12 | '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.',
13 | resume: 'https://example.com',
14 | social: {
15 | linkedin: 'https://linkedin.com',
16 | github: 'https://github.com',
17 | },
18 | }
19 |
20 | const projects = [
21 | // projects can be added an removed
22 | // if there are no projects, Projects section won't show up
23 | {
24 | name: 'Project 1',
25 | description:
26 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
27 | stack: ['SASS', 'TypeScript', 'React'],
28 | sourceCode: 'https://github.com',
29 | livePreview: 'https://github.com',
30 | },
31 | {
32 | name: 'Project 2',
33 | description:
34 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
35 | stack: ['SASS', 'TypeScript', 'React'],
36 | sourceCode: 'https://github.com',
37 | livePreview: 'https://github.com',
38 | },
39 | {
40 | name: 'Project 3',
41 | description:
42 | 'Amet asperiores et impedit aliquam consectetur? Voluptates sed a nulla ipsa officia et esse aliquam',
43 | stack: ['SASS', 'TypeScript', 'React'],
44 | sourceCode: 'https://github.com',
45 | livePreview: 'https://github.com',
46 | },
47 | ]
48 |
49 | const skills = [
50 | // skills can be added or removed
51 | // if there are no skills, Skills section won't show up
52 | 'HTML',
53 | 'CSS',
54 | 'JavaScript',
55 | 'TypeScript',
56 | 'React',
57 | 'Redux',
58 | 'SASS',
59 | 'Material UI',
60 | 'Git',
61 | 'CI/CD',
62 | 'Jest',
63 | 'Enzyme',
64 | ]
65 |
66 | const contact = {
67 | // email is optional - if left empty Contact section won't show up
68 | email: 'johnsmith@mail.com',
69 | }
70 |
71 | export { header, about, projects, skills, contact }
72 |
--------------------------------------------------------------------------------