├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── package.json ├── postcss.config.js ├── public ├── _redirects ├── favicon.png ├── files │ └── Stoman-Resume.pdf ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.js ├── __tests__ │ ├── Banner.test.js │ └── Modal.test.js ├── components │ ├── BackToTop.jsx │ ├── HireMeModal.jsx │ ├── ScrollToTop.jsx │ ├── about │ │ ├── AboutClientSingle.jsx │ │ ├── AboutClients.jsx │ │ ├── AboutCounter.jsx │ │ ├── AboutMeBio.jsx │ │ └── CounterItem.jsx │ ├── contact │ │ ├── ContactDetails.jsx │ │ └── ContactForm.jsx │ ├── projects │ │ ├── ProjectGallery.jsx │ │ ├── ProjectHeader.jsx │ │ ├── ProjectInfo.jsx │ │ ├── ProjectRelatedProjects.jsx │ │ ├── ProjectSingle.jsx │ │ ├── ProjectsFilter.jsx │ │ └── ProjectsGrid.jsx │ ├── reusable │ │ ├── Button.jsx │ │ └── FormInput.jsx │ └── shared │ │ ├── AppBanner.jsx │ │ ├── AppFooter.jsx │ │ ├── AppFooterCopyright.jsx │ │ └── AppHeader.jsx ├── context │ ├── AboutMeContext.jsx │ ├── ProjectsContext.jsx │ └── SingleProjectContext.jsx ├── css │ ├── App.css │ ├── main.css │ └── tailwind.css ├── data │ ├── aboutMeData.js │ ├── clientsData.js │ ├── projects.js │ └── singleProjectData.js ├── fonts │ ├── GeneralSans-Bold.eot │ ├── GeneralSans-Bold.ttf │ ├── GeneralSans-Bold.woff │ ├── GeneralSans-Bold.woff2 │ ├── GeneralSans-BoldItalic.eot │ ├── GeneralSans-BoldItalic.ttf │ ├── GeneralSans-BoldItalic.woff │ ├── GeneralSans-BoldItalic.woff2 │ ├── GeneralSans-Extralight.eot │ ├── GeneralSans-Extralight.ttf │ ├── GeneralSans-Extralight.woff │ ├── GeneralSans-Extralight.woff2 │ ├── GeneralSans-ExtralightItalic.eot │ ├── GeneralSans-ExtralightItalic.ttf │ ├── GeneralSans-ExtralightItalic.woff │ ├── GeneralSans-ExtralightItalic.woff2 │ ├── GeneralSans-Italic.eot │ ├── GeneralSans-Italic.ttf │ ├── GeneralSans-Italic.woff │ ├── GeneralSans-Italic.woff2 │ ├── GeneralSans-Light.eot │ ├── GeneralSans-Light.ttf │ ├── GeneralSans-Light.woff │ ├── GeneralSans-Light.woff2 │ ├── GeneralSans-LightItalic.eot │ ├── GeneralSans-LightItalic.ttf │ ├── GeneralSans-LightItalic.woff │ ├── GeneralSans-LightItalic.woff2 │ ├── GeneralSans-Medium.eot │ ├── GeneralSans-Medium.ttf │ ├── GeneralSans-Medium.woff │ ├── GeneralSans-Medium.woff2 │ ├── GeneralSans-MediumItalic.eot │ ├── GeneralSans-MediumItalic.ttf │ ├── GeneralSans-MediumItalic.woff │ ├── GeneralSans-MediumItalic.woff2 │ ├── GeneralSans-Regular.eot │ ├── GeneralSans-Regular.ttf │ ├── GeneralSans-Regular.woff │ ├── GeneralSans-Regular.woff2 │ ├── GeneralSans-Semibold.eot │ ├── GeneralSans-Semibold.ttf │ ├── GeneralSans-Semibold.woff │ ├── GeneralSans-Semibold.woff2 │ ├── GeneralSans-SemiboldItalic.eot │ ├── GeneralSans-SemiboldItalic.ttf │ ├── GeneralSans-SemiboldItalic.woff │ ├── GeneralSans-SemiboldItalic.woff2 │ ├── GeneralSans-Variable.eot │ ├── GeneralSans-Variable.ttf │ ├── GeneralSans-Variable.woff │ ├── GeneralSans-Variable.woff2 │ ├── GeneralSans-VariableItalic.eot │ ├── GeneralSans-VariableItalic.ttf │ ├── GeneralSans-VariableItalic.woff │ └── GeneralSans-VariableItalic.woff2 ├── hooks │ ├── useScrollToTop.jsx │ └── useThemeSwitcher.jsx ├── images │ ├── brands │ │ ├── adidas_color.png │ │ ├── adidas_gray.png │ │ ├── amazon_color.png │ │ ├── amazon_gray.png │ │ ├── canon_color.png │ │ ├── canon_gray.png │ │ ├── fila_color.png │ │ ├── fila_gray.png │ │ ├── nb_color.png │ │ ├── nb_gray.png │ │ ├── puma_color.png │ │ ├── puma_gray.png │ │ ├── samsung_color.png │ │ ├── samsung_gray.png │ │ ├── sony_color.png │ │ └── sony_gray.png │ ├── developer-dark.svg │ ├── developer.svg │ ├── logo-dark.svg │ ├── logo-light.svg │ ├── mobile-project-1.jpg │ ├── mobile-project-2.jpg │ ├── profile.jpeg │ ├── ui-project-1.jpg │ ├── ui-project-2.jpg │ ├── web-project-1.jpg │ └── web-project-2.jpg ├── index.js ├── pages │ ├── AboutMe.jsx │ ├── Contact.jsx │ ├── Home.jsx │ ├── ProjectSingle.jsx │ └── Projects.jsx ├── reportWebVitals.js └── setupTests.js ├── tailwind.config.js └── yarn.lock /.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 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["compat", "craco", "tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | realstoman@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering to contribute to `react-tailwindcss-portfolio` 💖 4 | 5 | Please note that this project is released with a [Code of Conduct](https://github.com/realstoman/react-tailwindcss-portfolio/blob/main/CODE_OF_CONDUCT.md). By participating you agree to abide by its terms. 6 | 7 | ## Setup 8 | 9 | ### Fork this repo 10 | 11 | [https://github.com/realstoman/react-tailwindcss-portfolio](https://github.com/realstoman/react-tailwindcss-portfolio) 12 | 13 | ### Check the issues section if there are already issues and see if you can fix them 14 | 15 | 16 | [https://github.com/realstoman/react-tailwindcss-portfolio/issues](https://github.com/realstoman/react-tailwindcss-portfolio/issues) 17 | 18 | ### If the issue is new, add the code to the fork of the repository and then create a pull request 19 | 20 | ### Add Feat or Fix to the pull request title so maintainer understand what this issue is 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Nangialai Stoman 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 | # React & TailwindCSS Portfolio - With Dark Mode 2 | 3 | A simple portfolio starter theme built with React and Tailwind CSS. This is the React version of [vuejs-tailwindcss-portfolio](https://github.com/realstoman/vuejs-tailwindcss-portfolio). 4 | 5 | ![React-TailwindCSS-Portfolio](https://user-images.githubusercontent.com/16396664/146666086-28e88beb-c2f0-431f-adfb-2396d8f64c80.png) 6 | 7 | ## Demo URL 8 | 9 | [https://react-tailwindcss-portfolio.netlify.app](https://react-tailwindcss-portfolio.netlify.app) 10 | 11 | ## Other versions of this project 12 | 13 | - Next.js Version: [https://github.com/realstoman/nextjs-tailwindcss-portfolio](https://github.com/realstoman/nextjs-tailwindcss-portfolio) 14 | - Vue.js Version: [https://github.com/realstoman/vuejs-tailwindcss-portfolio](https://github.com/realstoman/vuejs-tailwindcss-portfolio) 15 | - Nuxt.js Version: [https://github.com/realstoman/nuxtjs-tailwindcss-portfolio](https://github.com/realstoman/nuxtjs-tailwindcss-portfolio) 16 | 17 | ## Features 18 | 19 | - [React v18](https://reactjs.org) with [React Router v6](https://reactrouter.com) 20 | - [Tailwind CSS v3](https://tailwindcss.com) 21 | - Context API For State Management 22 | - Custom Hooks 23 | - Unit Testing 24 | - Framer Motion transitions & animations 25 | - Reusable components 26 | - Dark mode 27 | - Projects filter by category 28 | - Projects filter by search 29 | - Smooth scroll 30 | - Counter 31 | - Dynamic forms 32 | - Back to top button 33 | - Download file button 34 | - Simple and responsive design 35 | 36 | ### To Contribute to this project, read the [Contribution Guidlines](https://github.com/realstoman/react-tailwindcss-portfolio/blob/main/CONTRIBUTING.md) 37 | 38 | ## Setup 39 | 40 | 1. Make sure you have Node JS installed. If you don't have it: 41 | 42 | - [Download it from nodejs.org](https://nodejs.org) 43 | - [Install it using NVM ](https://github.com/nvm-sh/nvm) 44 | - If you're on Mac, Homebrew is a good option too: 45 | 46 | ``` 47 | brew install node 48 | ``` 49 | 50 | 2. Clone the repo: 51 | 52 | ``` 53 | git clone https://github.com/realstoman/react-tailwindcss-portfolio.git 54 | ``` 55 | 56 | 3. Open the project folder: 57 | 58 | ``` 59 | cd react-tailwindcss-portfolio 60 | ``` 61 | 62 | 4. Install packages and dependencies: 63 | 64 | ``` 65 | yarn 66 | ``` 67 | 68 | 4. NOTE: If you don't have yarn installed, you can install it globally using npm: 69 | 70 | ``` 71 | npm install --global yarn 72 | ``` 73 | 74 | 5. Start a local dev server at `http://localhost:3000`: 75 | 76 | ``` 77 | yarn start 78 | ``` 79 | 80 | 6. ##### Run tests: 81 | 82 | ``` 83 | yarn test 84 | ``` 85 | 86 | ## Notes 87 | 88 | - Always run `yarn install` after pulling new changes 89 | - I'll be constantly updating this repo as I'll be adding more sections to it, so please always check the projects section of this repo to see what tasks are under todo and in progress 90 | - Coming Soon [I'll be doing a screencast](https://www.youtube.com/realstoman). Soon I'll be uploading a video to my YouTube channel where I'll be going through the process of creating this portoflio 91 | - Illustrations from [unDraw](https://undraw.co) and [Freepik](https://freepik.com) 92 | - Images from [Unsplash](https://unsplash.com) 93 | - Feel free to use it as your own portfolio 94 | - Contributions are welcome 95 | 96 | ### License 97 | 98 | [MIT](https://github.com/realstoman/react-tailwindcss-portfolio/blob/main/LICENSE) 99 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security issues to `realstoman@gmail.com` 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-tailwindcss-portfolio", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^14.4.3", 9 | "framer-motion": "4.1.17", 10 | "postcss-cli": "^10.1.0", 11 | "react": "^18.1.0", 12 | "react-countup": "^6.1.1", 13 | "react-dom": "^18.1.0", 14 | "react-icons": "^4.3.1", 15 | "react-router-dom": "^6.0.2", 16 | "react-scripts": "4.0.3", 17 | "react-scroll": "^1.8.4", 18 | "styled-components": "^5.3.3", 19 | "web-vitals": "^1.0.1" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts --openssl-legacy-provider start", 23 | "build": "yarn run react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject", 26 | "build:css": "postcss src/css/tailwind.css -o src/css/main.css" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "@tailwindcss/forms": "^0.5.2", 48 | "@testing-library/dom": "^8.20.0", 49 | "autoprefixer": "^10.4.7", 50 | "postcss": "^8.4.21", 51 | "tailwindcss": "^3.0.24" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | module.exports = { 3 | plugins: [tailwindcss('./tailwind.config.js'), require('autoprefixer')], 4 | }; 5 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/public/favicon.png -------------------------------------------------------------------------------- /public/files/Stoman-Resume.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/public/files/Stoman-Resume.pdf -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Stoman 28 | 29 | 30 | 31 |
32 | 42 | 43 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/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 { AnimatePresence } from 'framer-motion'; 2 | import { lazy, Suspense } from 'react'; 3 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; 4 | import ScrollToTop from './components/ScrollToTop'; 5 | import AppFooter from './components/shared/AppFooter'; 6 | import AppHeader from './components/shared/AppHeader'; 7 | import './css/App.css'; 8 | import UseScrollToTop from './hooks/useScrollToTop'; 9 | 10 | const About = lazy(() => import('./pages/AboutMe')); 11 | const Contact = lazy(() => import('./pages/Contact.jsx')); 12 | const Home = lazy(() => import('./pages/Home')); 13 | const Projects = lazy(() => import('./pages/Projects')); 14 | const ProjectSingle = lazy(() => import('./pages/ProjectSingle.jsx')); 15 | 16 | 17 | function App() { 18 | return ( 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | } /> 27 | } /> 28 | } 31 | /> 32 | 33 | } /> 34 | } /> 35 | 36 | 37 | 38 | 39 | 40 |
41 |
42 | ); 43 | } 44 | 45 | export default App; 46 | -------------------------------------------------------------------------------- /src/__tests__/Banner.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import AppBanner from '../components/shared/AppBanner'; 3 | import userEvent from '@testing-library/user-event'; 4 | 5 | // This runs before each test. This is good instead of having the component render in each test case 6 | const setup = () => render(); 7 | 8 | // Get user event 9 | function setupUserEvent(jsx) { 10 | return { 11 | user: userEvent.setup(), 12 | ...render(jsx), 13 | }; 14 | } 15 | 16 | test('it shows the title in the banner', () => { 17 | setup(); 18 | // We expect that the title 'Hi, Iam Stoman' is in the banner component 19 | expect(screen.getByText(/Hi, Iam Stoman/i)).toBeInTheDocument(); 20 | }); 21 | 22 | test('can download cv when clicked on download cv button', async () => { 23 | const { user } = setupUserEvent(); 24 | 25 | const downloadCV = screen.getByText(/Download CV/i); 26 | 27 | expect(downloadCV).toBeInTheDocument(); 28 | 29 | const downloadCVButton = downloadCV.parentElement.parentElement; 30 | 31 | expect(downloadCVButton).toBeInTheDocument(); 32 | 33 | await user.click(downloadCVButton); 34 | 35 | // const downloadLink = { 36 | // click: await user.click(downloadCVButton), 37 | // }; 38 | // jest.spyOn(document, 'createElement').mockImplementation( 39 | // () => downloadLink 40 | // ); 41 | 42 | // expect(downloadLink.download).toEqual('Stoman-Resume.pdf'); 43 | // expect(downloadLink.href).toEqual('/files/Stoman-Resume.pdf'); 44 | // expect(downloadLink.click).toHaveBeenCalledTimes(1); 45 | }); 46 | -------------------------------------------------------------------------------- /src/__tests__/Modal.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import userEvent from '@testing-library/user-event'; 3 | import HireMeModal from '../components/HireMeModal'; 4 | 5 | // Get user event 6 | function setupUserEvent(jsx) { 7 | return { 8 | user: userEvent.setup(), 9 | ...render(jsx), 10 | }; 11 | } 12 | 13 | test('modal shows the children and a close button', async () => { 14 | const { user } = setupUserEvent(); 15 | 16 | expect( 17 | screen.getByText(/What project are you looking for?/i) 18 | ).toBeInTheDocument(); 19 | 20 | const closeModal = screen.getByText(/Close/i); 21 | expect(closeModal).toBeInTheDocument(); 22 | 23 | const closeModalButton = closeModal.parentElement; 24 | expect(closeModalButton).toBeInTheDocument(); 25 | }); 26 | -------------------------------------------------------------------------------- /src/components/BackToTop.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | const BackToTop = () => { 4 | const userScrollPosition = 0; 5 | 6 | useEffect(() => { 7 | window.scrollTo(0, 0); 8 | }, [userScrollPosition]); 9 | 10 | return
; 11 | }; 12 | 13 | export default BackToTop; 14 | -------------------------------------------------------------------------------- /src/components/HireMeModal.jsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion'; 2 | import { FiX } from 'react-icons/fi'; 3 | import Button from './reusable/Button'; 4 | 5 | const selectOptions = [ 6 | 'Web Application', 7 | 'Mobile Application', 8 | 'UI/UX Design', 9 | 'Branding', 10 | ]; 11 | 12 | const HireMeModal = ({ onClose, onRequest }) => { 13 | return ( 14 | 20 | {/* Modal Backdrop */} 21 |
22 | 23 | {/* Modal Content */} 24 |
25 |
26 |
27 |
28 |
29 | What project are you looking for? 30 |
31 | 37 |
38 |
39 |
{ 41 | e.preventDefault(); 42 | }} 43 | className="max-w-xl m-4 text-left" 44 | > 45 |
46 | 55 |
56 |
57 | 66 |
67 |
68 | 85 |
86 | 87 |
88 | 97 |
98 | 99 |
100 | 114 |
117 |
118 |
119 |
120 | 130 |
133 |
134 |
135 |
136 |
137 | ); 138 | }; 139 | 140 | export default HireMeModal; 141 | -------------------------------------------------------------------------------- /src/components/ScrollToTop.jsx: -------------------------------------------------------------------------------- 1 | // NOTE: This scroll to top is the default react scroll to top behavior when visiting a new route. 2 | // For the scroll to top behavior on a click event I have created a custom hook with the name of useScrollToTop under the hooks folder that scrolls use to the top of the page when they scroll down on the page. 3 | 4 | import { useEffect } from 'react'; 5 | import { useLocation } from 'react-router-dom'; 6 | 7 | const ScrollToTop = () => { 8 | const { pathname } = useLocation(); 9 | 10 | useEffect(() => { 11 | window.scrollTo(0, 0); 12 | }, [pathname]); 13 | return null; 14 | }; 15 | 16 | export default ScrollToTop; 17 | -------------------------------------------------------------------------------- /src/components/about/AboutClientSingle.jsx: -------------------------------------------------------------------------------- 1 | const AboutClientSingle = ({ title, image }) => { 2 | return ( 3 | <> 4 | {title} 9 | 10 | ); 11 | }; 12 | 13 | export default AboutClientSingle; 14 | -------------------------------------------------------------------------------- /src/components/about/AboutClients.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import AboutMeContext from '../../context/AboutMeContext'; 3 | import AboutClientSingle from './AboutClientSingle'; 4 | 5 | const AboutClients = () => { 6 | const { clientsData, clientsHeading } = useContext(AboutMeContext); 7 | 8 | return ( 9 |
10 |

11 | {clientsHeading} 12 |

13 |
14 | {clientsData.map((client) => ( 15 | 20 | ))} 21 |
22 |
23 | ); 24 | }; 25 | 26 | export default AboutClients; 27 | -------------------------------------------------------------------------------- /src/components/about/AboutCounter.jsx: -------------------------------------------------------------------------------- 1 | import { useCountUp } from 'react-countup'; 2 | import CounterItem from './CounterItem'; 3 | 4 | const AboutCounter = () => { 5 | useCountUp({ ref: 'experienceCounter', end: 12, duration: 2 }); 6 | useCountUp({ ref: 'githubStarsCounter', end: 20, duration: 2 }); 7 | useCountUp({ ref: 'feedbackCounter', end: 92, duration: 2 }); 8 | useCountUp({ ref: 'projectsCounter', end: 77, duration: 2 }); 9 | 10 | return ( 11 |
12 |
13 | } 16 | measurement="" 17 | /> 18 | 19 | } 22 | measurement="k+" 23 | /> 24 | 25 | } 28 | measurement="%" 29 | /> 30 | 31 | } 34 | measurement="%" 35 | /> 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default AboutCounter; 42 | -------------------------------------------------------------------------------- /src/components/about/AboutMeBio.jsx: -------------------------------------------------------------------------------- 1 | import profileImage from '../../images/profile.jpeg'; 2 | import { useContext } from 'react'; 3 | import AboutMeContext from '../../context/AboutMeContext'; 4 | 5 | const AboutMeBio = () => { 6 | const { aboutMe } = useContext(AboutMeContext); 7 | 8 | return ( 9 |
10 |
11 | 12 |
13 | 14 |
15 | {aboutMe.map((bio) => ( 16 |

20 | {bio.bio} 21 |

22 | ))} 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default AboutMeBio; 29 | -------------------------------------------------------------------------------- /src/components/about/CounterItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const CounterItem = ({ title, counter, measurement }) => { 4 | return ( 5 |
6 |

7 | {counter} {measurement} 8 |

9 | 10 | {title} 11 | 12 |
13 | ); 14 | }; 15 | 16 | export default CounterItem; 17 | -------------------------------------------------------------------------------- /src/components/contact/ContactDetails.jsx: -------------------------------------------------------------------------------- 1 | import { FiPhone, FiMapPin, FiMail } from 'react-icons/fi'; 2 | 3 | const contacts = [ 4 | { 5 | id: 1, 6 | name: 'Your Address, Your City, Your Country', 7 | icon: , 8 | }, 9 | { 10 | id: 2, 11 | name: 'email@domain.com', 12 | icon: , 13 | }, 14 | { 15 | id: 3, 16 | name: '555 8888 888', 17 | icon: , 18 | }, 19 | ]; 20 | 21 | const ContactDetails = () => { 22 | return ( 23 |
24 |
25 |

26 | Contact details 27 |

28 |
    29 | {contacts.map((contact) => ( 30 |
  • 31 | 32 | {contact.icon} 33 | 34 | 35 | {contact.name} 36 | 37 |
  • 38 | ))} 39 |
40 |
41 |
42 | ); 43 | }; 44 | 45 | export default ContactDetails; 46 | -------------------------------------------------------------------------------- /src/components/contact/ContactForm.jsx: -------------------------------------------------------------------------------- 1 | import Button from '../reusable/Button'; 2 | import FormInput from '../reusable/FormInput'; 3 | 4 | const ContactForm = () => { 5 | return ( 6 |
7 |
8 |
{ 10 | e.preventDefault(); 11 | }} 12 | className="max-w-xl m-4 p-6 sm:p-10 bg-secondary-light dark:bg-secondary-dark rounded-xl shadow-xl text-left" 13 | > 14 |

15 | Contact Form 16 |

17 | 26 | 35 | 44 | 45 |
46 | 52 | 60 |
61 | 62 |
63 |
69 | 70 |
71 |
72 | ); 73 | }; 74 | 75 | export default ContactForm; 76 | -------------------------------------------------------------------------------- /src/components/projects/ProjectGallery.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import SingleProjectContext from '../../context/SingleProjectContext'; 3 | 4 | const ProjectGallery = () => { 5 | const { singleProjectData } = useContext(SingleProjectContext); 6 | 7 | return ( 8 |
9 | {singleProjectData.ProjectImages.map((project) => { 10 | return ( 11 |
12 | {project.title} 18 |
19 | ); 20 | })} 21 |
22 | ); 23 | }; 24 | 25 | export default ProjectGallery; 26 | -------------------------------------------------------------------------------- /src/components/projects/ProjectHeader.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { FiClock, FiTag } from 'react-icons/fi'; 3 | import SingleProjectContext from '../../context/SingleProjectContext'; 4 | 5 | const ProjectSingleHeader = () => { 6 | const { singleProjectData } = useContext(SingleProjectContext); 7 | 8 | return ( 9 |
10 |

11 | {singleProjectData.ProjectHeader.title} 12 |

13 |
14 |
15 | 16 | 17 | {singleProjectData.ProjectHeader.publishDate} 18 | 19 |
20 |
21 | 22 | 23 | {singleProjectData.ProjectHeader.tags} 24 | 25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default ProjectSingleHeader; 32 | -------------------------------------------------------------------------------- /src/components/projects/ProjectInfo.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import SingleProjectContext from '../../context/SingleProjectContext'; 3 | 4 | const ProjectInfo = () => { 5 | const { singleProjectData } = useContext(SingleProjectContext); 6 | 7 | return ( 8 |
9 |
10 | {/* Single project client details */} 11 |
12 |

13 | {singleProjectData.ProjectInfo.ClientHeading} 14 |

15 |
    16 | {singleProjectData.ProjectInfo.CompanyInfo.map( 17 | (info) => { 18 | return ( 19 |
  • 23 | {info.title}: 24 | 34 | {info.details} 35 | 36 |
  • 37 | ); 38 | } 39 | )} 40 |
41 |
42 | 43 | {/* Single project objectives */} 44 |
45 |

46 | {singleProjectData.ProjectInfo.ObjectivesHeading} 47 |

48 |

49 | {singleProjectData.ProjectInfo.ObjectivesDetails} 50 |

51 |
52 | 53 | {/* Single project technologies */} 54 |
55 |

56 | {singleProjectData.ProjectInfo.Technologies[0].title} 57 |

58 |

59 | {singleProjectData.ProjectInfo.Technologies[0].techs.join( 60 | ', ' 61 | )} 62 |

63 |
64 | 65 | {/* Single project social sharing */} 66 |
67 |

68 | {singleProjectData.ProjectInfo.SocialSharingHeading} 69 |

70 |
71 | {singleProjectData.ProjectInfo.SocialSharing.map( 72 | (social) => { 73 | return ( 74 | 81 | 82 | {social.icon} 83 | 84 | 85 | ); 86 | } 87 | )} 88 |
89 |
90 |
91 | 92 | {/* Single project right section */} 93 |
94 |

95 | {singleProjectData.ProjectInfo.ProjectDetailsHeading} 96 |

97 | {singleProjectData.ProjectInfo.ProjectDetails.map((details) => { 98 | return ( 99 |

103 | {details.details} 104 |

105 | ); 106 | })} 107 |
108 |
109 | ); 110 | }; 111 | 112 | export default ProjectInfo; 113 | -------------------------------------------------------------------------------- /src/components/projects/ProjectRelatedProjects.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import SingleProjectContext from '../../context/SingleProjectContext'; 3 | 4 | const ProjectRelatedProjects = () => { 5 | const { singleProjectData } = useContext(SingleProjectContext); 6 | 7 | return ( 8 |
9 |

10 | {singleProjectData.RelatedProject.title} 11 |

12 | 13 |
14 | {singleProjectData.RelatedProject.Projects.map((project) => { 15 | return ( 16 | {project.title} 22 | ); 23 | })} 24 |
25 |
26 | ); 27 | }; 28 | 29 | export default ProjectRelatedProjects; 30 | -------------------------------------------------------------------------------- /src/components/projects/ProjectSingle.jsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const ProjectSingle = ({ title, category, image }) => { 5 | return ( 6 | 15 | 16 |
17 |
18 | Single Project 23 |
24 |
25 |

26 | {title} 27 |

28 | 29 | {category} 30 | 31 |
32 |
33 | 34 |
35 | ); 36 | }; 37 | 38 | export default ProjectSingle; 39 | -------------------------------------------------------------------------------- /src/components/projects/ProjectsFilter.jsx: -------------------------------------------------------------------------------- 1 | const selectOptions = [ 2 | 'Web Application', 3 | 'Mobile Application', 4 | 'UI/UX Design', 5 | 'Branding', 6 | ]; 7 | 8 | const ProjectsFilter = ({ setSelectProject }) => { 9 | return ( 10 | 40 | ); 41 | }; 42 | 43 | export default ProjectsFilter; 44 | -------------------------------------------------------------------------------- /src/components/projects/ProjectsGrid.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { FiSearch } from 'react-icons/fi'; 3 | import ProjectSingle from './ProjectSingle'; 4 | import { ProjectsContext } from '../../context/ProjectsContext'; 5 | import ProjectsFilter from './ProjectsFilter'; 6 | 7 | const ProjectsGrid = () => { 8 | const { 9 | projects, 10 | searchProject, 11 | setSearchProject, 12 | searchProjectsByTitle, 13 | selectProject, 14 | setSelectProject, 15 | selectProjectsByCategory, 16 | } = useContext(ProjectsContext); 17 | 18 | return ( 19 |
20 |
21 |

22 | Projects portfolio 23 |

24 |
25 | 26 |
27 |

36 | Search projects by title or filter by category 37 |

38 |
48 |
49 | 61 | 62 | 63 | { 65 | setSearchProject(e.target.value); 66 | }} 67 | className="font-general-medium 68 | pl-3 69 | pr-1 70 | sm:px-4 71 | py-2 72 | border 73 | border-gray-200 74 | dark:border-secondary-dark 75 | rounded-lg 76 | text-sm 77 | sm:text-md 78 | bg-secondary-light 79 | dark:bg-ternary-dark 80 | text-primary-dark 81 | dark:text-ternary-light 82 | " 83 | id="name" 84 | name="name" 85 | type="search" 86 | required="" 87 | placeholder="Search Projects" 88 | aria-label="Name" 89 | /> 90 |
91 | 92 | 93 |
94 |
95 | 96 |
97 | {selectProject 98 | ? selectProjectsByCategory.map((project) => ( 99 | 105 | )) 106 | : searchProject 107 | ? searchProjectsByTitle.map((project) => ( 108 | 114 | )) 115 | : projects.map((project) => ( 116 | 122 | ))} 123 |
124 |
125 | ); 126 | }; 127 | 128 | export default ProjectsGrid; 129 | -------------------------------------------------------------------------------- /src/components/reusable/Button.jsx: -------------------------------------------------------------------------------- 1 | function Button({ title }) { 2 | return ; 3 | } 4 | 5 | export default Button; 6 | -------------------------------------------------------------------------------- /src/components/reusable/FormInput.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const FormInput = ({ 4 | inputLabel, 5 | labelFor, 6 | inputType, 7 | inputId, 8 | inputName, 9 | placeholderText, 10 | ariaLabelName, 11 | }) => { 12 | return ( 13 |
14 | 20 | 29 |
30 | ); 31 | }; 32 | 33 | export default FormInput; 34 | -------------------------------------------------------------------------------- /src/components/shared/AppBanner.jsx: -------------------------------------------------------------------------------- 1 | import useThemeSwitcher from '../../hooks/useThemeSwitcher'; 2 | import { FiArrowDownCircle } from 'react-icons/fi'; 3 | import developerLight from '../../images/developer.svg'; 4 | import developerDark from '../../images/developer-dark.svg'; 5 | import { motion } from 'framer-motion'; 6 | 7 | const AppBanner = () => { 8 | const [activeTheme] = useThemeSwitcher(); 9 | 10 | return ( 11 | 17 |
18 | 28 | Hi, Iam Stoman 29 | 30 | 40 | A Full-Stack Developer & Design Enthusiast 41 | 42 | 52 | 58 | 59 | 60 | Download CV 61 | 62 | 63 | 64 |
65 | 71 | Developer 77 | 78 |
79 | ); 80 | }; 81 | 82 | export default AppBanner; 83 | -------------------------------------------------------------------------------- /src/components/shared/AppFooter.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | FiGithub, 3 | FiTwitter, 4 | FiLinkedin, 5 | FiGlobe, 6 | FiYoutube, 7 | } from 'react-icons/fi'; 8 | import AppFooterCopyright from './AppFooterCopyright'; 9 | 10 | const socialLinks = [ 11 | { 12 | id: 1, 13 | icon: , 14 | url: 'https://www.stoman.me/', 15 | }, 16 | { 17 | id: 2, 18 | icon: , 19 | url: 'https://github.com/realstoman', 20 | }, 21 | { 22 | id: 3, 23 | icon: , 24 | url: 'https://twitter.com/realstoman', 25 | }, 26 | { 27 | id: 4, 28 | icon: , 29 | url: 'https://www.linkedin.com/in/realstoman', 30 | }, 31 | { 32 | id: 5, 33 | icon: , 34 | url: 'https://www.youtube.com/c/realstoman', 35 | }, 36 | ]; 37 | 38 | const AppFooter = () => { 39 | return ( 40 |
41 |
42 | {/* Footer social links */} 43 |
44 |

45 | Follow me 46 |

47 | 61 |
62 | 63 | 64 |
65 |
66 | ); 67 | }; 68 | 69 | export default AppFooter; 70 | -------------------------------------------------------------------------------- /src/components/shared/AppFooterCopyright.jsx: -------------------------------------------------------------------------------- 1 | function AppFooterCopyright() { 2 | return ( 3 |
4 |
5 | © {new Date().getFullYear()} 6 | 11 | React & Tailwind CSS Portfolio 12 | 13 | . 14 | 19 | Stoman 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default AppFooterCopyright; 27 | -------------------------------------------------------------------------------- /src/components/shared/AppHeader.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { FiMenu, FiMoon, FiSun, FiX } from 'react-icons/fi'; 3 | import { Link } from 'react-router-dom'; 4 | import useThemeSwitcher from '../../hooks/useThemeSwitcher'; 5 | import HireMeModal from '../HireMeModal'; 6 | import logoLight from '../../images/logo-light.svg'; 7 | import logoDark from '../../images/logo-dark.svg'; 8 | import { motion } from 'framer-motion'; 9 | import Button from '../reusable/Button'; 10 | 11 | const AppHeader = () => { 12 | const [showMenu, setShowMenu] = useState(false); 13 | const [showModal, setShowModal] = useState(false); 14 | const [activeTheme, setTheme] = useThemeSwitcher(); 15 | 16 | function toggleMenu() { 17 | if (!showMenu) { 18 | setShowMenu(true); 19 | } else { 20 | setShowMenu(false); 21 | } 22 | } 23 | 24 | function showHireMeModal() { 25 | if (!showModal) { 26 | document 27 | .getElementsByTagName('html')[0] 28 | .classList.add('overflow-y-hidden'); 29 | setShowModal(true); 30 | } else { 31 | document 32 | .getElementsByTagName('html')[0] 33 | .classList.remove('overflow-y-hidden'); 34 | setShowModal(false); 35 | } 36 | } 37 | 38 | return ( 39 | 45 |
46 | {/* Header menu links and small screen hamburger menu */} 47 |
48 |
49 | 50 | {activeTheme === 'dark' ? ( 51 | Dark Logo 56 | ) : ( 57 | Dark Logo 62 | )} 63 | 64 |
65 | 66 | {/* Theme switcher small screen */} 67 |
setTheme(activeTheme)} 69 | aria-label="Theme Switcher" 70 | className="block sm:hidden ml-0 bg-primary-light dark:bg-ternary-dark p-3 shadow-sm rounded-xl cursor-pointer" 71 | > 72 | {activeTheme === 'dark' ? ( 73 | 74 | ) : ( 75 | 76 | )} 77 |
78 | 79 | {/* Small screen hamburger menu */} 80 |
81 | 99 |
100 |
101 | 102 | {/* Header links small screen */} 103 |
110 | 115 | Projects 116 | 117 | 122 | About Me 123 | 124 | 129 | Contact 130 | 131 |
132 | 137 |
140 |
141 | 142 | {/* Header links large screen */} 143 |
144 | 149 | Projects 150 | 151 | 156 | About Me 157 | 158 | 163 | Contact 164 | 165 |
166 | 167 | {/* Header right section buttons */} 168 |
169 |
170 | 175 |
178 | 179 | {/* Theme switcher large screen */} 180 |
setTheme(activeTheme)} 182 | aria-label="Theme Switcher" 183 | className="ml-8 bg-primary-light dark:bg-ternary-dark p-3 shadow-sm rounded-xl cursor-pointer" 184 | > 185 | {activeTheme === 'dark' ? ( 186 | 187 | ) : ( 188 | 189 | )} 190 |
191 |
192 |
193 | {/* Hire me modal */} 194 |
195 | {showModal ? ( 196 | 200 | ) : null} 201 | {showModal ? showHireMeModal : null} 202 |
203 |
204 | ); 205 | }; 206 | 207 | export default AppHeader; 208 | -------------------------------------------------------------------------------- /src/context/AboutMeContext.jsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext } from 'react'; 2 | import { aboutMeData } from '../data/aboutMeData'; 3 | import { clientsHeading as clientsPageHeading } from '../data/clientsData'; 4 | import { clientsData as clientsDataJson } from '../data/clientsData'; 5 | 6 | const AboutMeContext = createContext(); 7 | 8 | export const AboutMeProvider = ({ children }) => { 9 | const [aboutMe, setAboutMe] = useState(aboutMeData); 10 | 11 | const clientsHeading = clientsPageHeading; 12 | 13 | const [clientsData, setClientsData] = useState(clientsDataJson); 14 | 15 | return ( 16 | 25 | {children} 26 | 27 | ); 28 | }; 29 | 30 | export default AboutMeContext; 31 | -------------------------------------------------------------------------------- /src/context/ProjectsContext.jsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext } from 'react'; 2 | import { projectsData } from '../data/projects'; 3 | 4 | // Create projects context 5 | export const ProjectsContext = createContext(); 6 | 7 | // Create the projects context provider 8 | export const ProjectsProvider = (props) => { 9 | const [projects, setProjects] = useState(projectsData); 10 | const [searchProject, setSearchProject] = useState(''); 11 | const [selectProject, setSelectProject] = useState(''); 12 | 13 | // Search projects by project title 14 | const searchProjectsByTitle = projects.filter((item) => { 15 | const result = item.title 16 | .toLowerCase() 17 | .includes(searchProject.toLowerCase()) 18 | ? item 19 | : searchProject === '' 20 | ? item 21 | : ''; 22 | return result; 23 | }); 24 | 25 | // Select projects by project category 26 | const selectProjectsByCategory = projects.filter((item) => { 27 | let category = 28 | item.category.charAt(0).toUpperCase() + item.category.slice(1); 29 | return category.includes(selectProject); 30 | }); 31 | 32 | return ( 33 | 45 | {props.children} 46 | 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /src/context/SingleProjectContext.jsx: -------------------------------------------------------------------------------- 1 | import { useState, createContext } from 'react'; 2 | import { singleProjectData as singleProjectDataJson } from '../data/singleProjectData'; 3 | 4 | const SingleProjectContext = createContext(); 5 | 6 | export const SingleProjectProvider = ({ children }) => { 7 | const [singleProjectData, setSingleProjectData] = useState( 8 | singleProjectDataJson 9 | ); 10 | 11 | return ( 12 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | export default SingleProjectContext; 21 | -------------------------------------------------------------------------------- /src/css/App.css: -------------------------------------------------------------------------------- 1 | /*== 2 | * React & TailwindCSS Portfolio Main CSS File 3 | * Powered by: @realstoman 4 | */ 5 | 6 | body { 7 | font-family: 'GeneralSans-Variable'; 8 | } 9 | 10 | /* Fonts */ 11 | @font-face { 12 | font-family: 'GeneralSans-Variable'; 13 | src: url('../fonts/GeneralSans-Variable.woff2') format('woff2'), 14 | url('../fonts/GeneralSans-Variable.woff') format('woff'), 15 | url('../fonts/GeneralSans-Variable.ttf') format('truetype'); 16 | font-weight: 200 700; 17 | font-display: swap; 18 | font-style: normal; 19 | } 20 | 21 | .font-general-variable { 22 | font-family: 'GeneralSans-Variable'; 23 | } 24 | 25 | @font-face { 26 | font-family: 'GeneralSans-VariableItalic'; 27 | src: url('../fonts/GeneralSans-VariableItalic.woff2') format('woff2'), 28 | url('../fonts/GeneralSans-VariableItalic.woff') format('woff'), 29 | url('../fonts/GeneralSans-VariableItalic.ttf') format('truetype'); 30 | font-weight: 200 700; 31 | font-display: swap; 32 | font-style: italic; 33 | } 34 | 35 | .font-general-variable-italic { 36 | font-family: 'GeneralSans-VariableItalic'; 37 | } 38 | 39 | @font-face { 40 | font-family: 'GeneralSans-Extralight'; 41 | src: url('../fonts/GeneralSans-Extralight.woff2') format('woff2'), 42 | url('../fonts/GeneralSans-Extralight.woff') format('woff'), 43 | url('../fonts/GeneralSans-Extralight.ttf') format('truetype'); 44 | font-weight: 200; 45 | font-display: swap; 46 | font-style: normal; 47 | } 48 | 49 | .font-general-extralight { 50 | font-family: 'GeneralSans-Extralight'; 51 | } 52 | 53 | @font-face { 54 | font-family: 'GeneralSans-ExtralightItalic'; 55 | src: url('../fonts/GeneralSans-ExtralightItalic.woff2') format('woff2'), 56 | url('../fonts/GeneralSans-ExtralightItalic.woff') format('woff'), 57 | url('../fonts/GeneralSans-ExtralightItalic.ttf') format('truetype'); 58 | font-weight: 200; 59 | font-display: swap; 60 | font-style: italic; 61 | } 62 | 63 | .font-general-extralight-italic { 64 | font-family: 'GeneralSans-ExtralightItalic'; 65 | } 66 | 67 | @font-face { 68 | font-family: 'GeneralSans-Light'; 69 | src: url('../fonts/GeneralSans-Light.woff2') format('woff2'), 70 | url('../fonts/GeneralSans-Light.woff') format('woff'), 71 | url('../fonts/GeneralSans-Light.ttf') format('truetype'); 72 | font-weight: 300; 73 | font-display: swap; 74 | font-style: normal; 75 | } 76 | 77 | .font-general-light { 78 | font-family: 'GeneralSans-Light'; 79 | } 80 | 81 | @font-face { 82 | font-family: 'GeneralSans-LightItalic'; 83 | src: url('../fonts/GeneralSans-LightItalic.woff2') format('woff2'), 84 | url('../fonts/GeneralSans-LightItalic.woff') format('woff'), 85 | url('../fonts/GeneralSans-LightItalic.ttf') format('truetype'); 86 | font-weight: 300; 87 | font-display: swap; 88 | font-style: italic; 89 | } 90 | 91 | .font-general-light-italic { 92 | font-family: 'GeneralSans-LightItalic'; 93 | } 94 | 95 | @font-face { 96 | font-family: 'GeneralSans-Regular'; 97 | src: url('../fonts/GeneralSans-Regular.woff2') format('woff2'), 98 | url('../fonts/GeneralSans-Regular.woff') format('woff'), 99 | url('../fonts/GeneralSans-Regular.ttf') format('truetype'); 100 | font-weight: 400; 101 | font-display: swap; 102 | font-style: normal; 103 | } 104 | 105 | .font-general-regular { 106 | font-family: 'GeneralSans-Regular'; 107 | } 108 | 109 | @font-face { 110 | font-family: 'GeneralSans-Italic'; 111 | src: url('../fonts/GeneralSans-Italic.woff2') format('woff2'), 112 | url('../fonts/GeneralSans-Italic.woff') format('woff'), 113 | url('../fonts/GeneralSans-Italic.ttf') format('truetype'); 114 | font-weight: 400; 115 | font-display: swap; 116 | font-style: italic; 117 | } 118 | 119 | .font-general-italic { 120 | font-family: 'GeneralSans-Italic'; 121 | } 122 | 123 | @font-face { 124 | font-family: 'GeneralSans-Medium'; 125 | src: url('../fonts/GeneralSans-Medium.woff2') format('woff2'), 126 | url('../fonts/GeneralSans-Medium.woff') format('woff'), 127 | url('../fonts/GeneralSans-Medium.ttf') format('truetype'); 128 | font-weight: 500; 129 | font-display: swap; 130 | font-style: normal; 131 | } 132 | 133 | .font-general-medium { 134 | font-family: 'GeneralSans-Medium'; 135 | } 136 | 137 | @font-face { 138 | font-family: 'GeneralSans-MediumItalic'; 139 | src: url('../fonts/GeneralSans-MediumItalic.woff2') format('woff2'), 140 | url('../fonts/GeneralSans-MediumItalic.woff') format('woff'), 141 | url('../fonts/GeneralSans-MediumItalic.ttf') format('truetype'); 142 | font-weight: 500; 143 | font-display: swap; 144 | font-style: italic; 145 | } 146 | 147 | .font-general-medium-italic { 148 | font-family: 'GeneralSans-MediumItalic'; 149 | } 150 | 151 | @font-face { 152 | font-family: 'GeneralSans-Semibold'; 153 | src: url('../fonts/GeneralSans-Semibold.woff2') format('woff2'), 154 | url('../fonts/GeneralSans-Semibold.woff') format('woff'), 155 | url('../fonts/GeneralSans-Semibold.ttf') format('truetype'); 156 | font-weight: 600; 157 | font-display: swap; 158 | font-style: normal; 159 | } 160 | 161 | .font-general-semibold { 162 | font-family: 'GeneralSans-Semibold'; 163 | } 164 | 165 | @font-face { 166 | font-family: 'GeneralSans-SemiboldItalic'; 167 | src: url('../fonts/GeneralSans-SemiboldItalic.woff2') format('woff2'), 168 | url('../fonts/GeneralSans-SemiboldItalic.woff') format('woff'), 169 | url('../fonts/GeneralSans-SemiboldItalic.ttf') format('truetype'); 170 | font-weight: 600; 171 | font-display: swap; 172 | font-style: italic; 173 | } 174 | 175 | .font-general-semibold-italic { 176 | font-family: 'GeneralSans-SemiboldItalic'; 177 | } 178 | 179 | @font-face { 180 | font-family: 'GeneralSans-Bold'; 181 | src: url('../fonts/GeneralSans-Bold.woff2') format('woff2'), 182 | url('../fonts/GeneralSans-Bold.woff') format('woff'), 183 | url('../fonts/GeneralSans-Bold.ttf') format('truetype'); 184 | font-weight: 700; 185 | font-display: swap; 186 | font-style: normal; 187 | } 188 | 189 | .font-general-bold { 190 | font-family: 'GeneralSans-Bold'; 191 | } 192 | 193 | @font-face { 194 | font-family: 'GeneralSans-BoldItalic'; 195 | src: url('../fonts/GeneralSans-BoldItalic.woff2') format('woff2'), 196 | url('../fonts/GeneralSans-BoldItalic.woff') format('woff'), 197 | url('../fonts/GeneralSans-BoldItalic.ttf') format('truetype'); 198 | font-weight: 700; 199 | font-display: swap; 200 | font-style: italic; 201 | } 202 | 203 | .font-general-bold-italic { 204 | font-family: 'GeneralSans-BoldItalic'; 205 | } 206 | 207 | /* Scroll to top style */ 208 | .scrollToTop { 209 | @apply bg-indigo-600; 210 | @apply text-white; 211 | position: fixed; 212 | width: 100%; 213 | align-items: center; 214 | height: 20px; 215 | justify-content: center; 216 | z-index: 999; 217 | cursor: pointer; 218 | animation: fadeIn 0.3s; 219 | transition: opacity 0.4s; 220 | opacity: 0.9; 221 | } 222 | 223 | .scrollToTop:hover { 224 | opacity: 1; 225 | } 226 | 227 | @keyframes fadeIn { 228 | 0% { 229 | opacity: 0; 230 | } 231 | 100% { 232 | opacity: 0.5; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | ! tailwindcss v3.0.24 | MIT License | https://tailwindcss.com 3 | *//* 4 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 5 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 6 | */ 7 | 8 | *, 9 | ::before, 10 | ::after { 11 | box-sizing: border-box; /* 1 */ 12 | border-width: 0; /* 2 */ 13 | border-style: solid; /* 2 */ 14 | border-color: #e5e7eb; /* 2 */ 15 | } 16 | 17 | ::before, 18 | ::after { 19 | --tw-content: ''; 20 | } 21 | 22 | /* 23 | 1. Use a consistent sensible line-height in all browsers. 24 | 2. Prevent adjustments of font size after orientation changes in iOS. 25 | 3. Use a more readable tab size. 26 | 4. Use the user's configured `sans` font-family by default. 27 | */ 28 | 29 | html { 30 | line-height: 1.5; /* 1 */ 31 | -webkit-text-size-adjust: 100%; /* 2 */ /* 3 */ 32 | tab-size: 4; /* 3 */ 33 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ 34 | } 35 | 36 | /* 37 | 1. Remove the margin in all browsers. 38 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 39 | */ 40 | 41 | body { 42 | margin: 0; /* 1 */ 43 | line-height: inherit; /* 2 */ 44 | } 45 | 46 | /* 47 | 1. Add the correct height in Firefox. 48 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 49 | 3. Ensure horizontal rules are visible by default. 50 | */ 51 | 52 | hr { 53 | height: 0; /* 1 */ 54 | color: inherit; /* 2 */ 55 | border-top-width: 1px; /* 3 */ 56 | } 57 | 58 | /* 59 | Add the correct text decoration in Chrome, Edge, and Safari. 60 | */ 61 | 62 | abbr:where([title]) { 63 | text-decoration: underline dotted; 64 | } 65 | 66 | /* 67 | Remove the default font size and weight for headings. 68 | */ 69 | 70 | h1, 71 | h2, 72 | h3, 73 | h4, 74 | h5, 75 | h6 { 76 | font-size: inherit; 77 | font-weight: inherit; 78 | } 79 | 80 | /* 81 | Reset links to optimize for opt-in styling instead of opt-out. 82 | */ 83 | 84 | a { 85 | color: inherit; 86 | text-decoration: inherit; 87 | } 88 | 89 | /* 90 | Add the correct font weight in Edge and Safari. 91 | */ 92 | 93 | b, 94 | strong { 95 | font-weight: bolder; 96 | } 97 | 98 | /* 99 | 1. Use the user's configured `mono` font family by default. 100 | 2. Correct the odd `em` font sizing in all browsers. 101 | */ 102 | 103 | code, 104 | kbd, 105 | samp, 106 | pre { 107 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ 108 | font-size: 1em; /* 2 */ 109 | } 110 | 111 | /* 112 | Add the correct font size in all browsers. 113 | */ 114 | 115 | small { 116 | font-size: 80%; 117 | } 118 | 119 | /* 120 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 121 | */ 122 | 123 | sub, 124 | sup { 125 | font-size: 75%; 126 | line-height: 0; 127 | position: relative; 128 | vertical-align: baseline; 129 | } 130 | 131 | sub { 132 | bottom: -0.25em; 133 | } 134 | 135 | sup { 136 | top: -0.5em; 137 | } 138 | 139 | /* 140 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 141 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 142 | 3. Remove gaps between table borders by default. 143 | */ 144 | 145 | table { 146 | text-indent: 0; /* 1 */ 147 | border-color: inherit; /* 2 */ 148 | border-collapse: collapse; /* 3 */ 149 | } 150 | 151 | /* 152 | 1. Change the font styles in all browsers. 153 | 2. Remove the margin in Firefox and Safari. 154 | 3. Remove default padding in all browsers. 155 | */ 156 | 157 | button, 158 | input, 159 | optgroup, 160 | select, 161 | textarea { 162 | font-family: inherit; /* 1 */ 163 | font-size: 100%; /* 1 */ 164 | line-height: inherit; /* 1 */ 165 | color: inherit; /* 1 */ 166 | margin: 0; /* 2 */ 167 | padding: 0; /* 3 */ 168 | } 169 | 170 | /* 171 | Remove the inheritance of text transform in Edge and Firefox. 172 | */ 173 | 174 | button, 175 | select { 176 | text-transform: none; 177 | } 178 | 179 | /* 180 | 1. Correct the inability to style clickable types in iOS and Safari. 181 | 2. Remove default button styles. 182 | */ 183 | 184 | button, 185 | [type='button'], 186 | [type='reset'], 187 | [type='submit'] { 188 | -webkit-appearance: button; /* 1 */ 189 | background-color: transparent; /* 2 */ 190 | background-image: none; /* 2 */ 191 | } 192 | 193 | /* 194 | Use the modern Firefox focus style for all focusable elements. 195 | */ 196 | 197 | :-moz-focusring { 198 | outline: auto; 199 | } 200 | 201 | /* 202 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 203 | */ 204 | 205 | :-moz-ui-invalid { 206 | box-shadow: none; 207 | } 208 | 209 | /* 210 | Add the correct vertical alignment in Chrome and Firefox. 211 | */ 212 | 213 | progress { 214 | vertical-align: baseline; 215 | } 216 | 217 | /* 218 | Correct the cursor style of increment and decrement buttons in Safari. 219 | */ 220 | 221 | ::-webkit-inner-spin-button, 222 | ::-webkit-outer-spin-button { 223 | height: auto; 224 | } 225 | 226 | /* 227 | 1. Correct the odd appearance in Chrome and Safari. 228 | 2. Correct the outline style in Safari. 229 | */ 230 | 231 | [type='search'] { 232 | -webkit-appearance: textfield; /* 1 */ 233 | outline-offset: -2px; /* 2 */ 234 | } 235 | 236 | /* 237 | Remove the inner padding in Chrome and Safari on macOS. 238 | */ 239 | 240 | ::-webkit-search-decoration { 241 | -webkit-appearance: none; 242 | } 243 | 244 | /* 245 | 1. Correct the inability to style clickable types in iOS and Safari. 246 | 2. Change font properties to `inherit` in Safari. 247 | */ 248 | 249 | ::-webkit-file-upload-button { 250 | -webkit-appearance: button; /* 1 */ 251 | font: inherit; /* 2 */ 252 | } 253 | 254 | /* 255 | Add the correct display in Chrome and Safari. 256 | */ 257 | 258 | summary { 259 | display: list-item; 260 | } 261 | 262 | /* 263 | Removes the default spacing and border for appropriate elements. 264 | */ 265 | 266 | blockquote, 267 | dl, 268 | dd, 269 | h1, 270 | h2, 271 | h3, 272 | h4, 273 | h5, 274 | h6, 275 | hr, 276 | figure, 277 | p, 278 | pre { 279 | margin: 0; 280 | } 281 | 282 | fieldset { 283 | margin: 0; 284 | padding: 0; 285 | } 286 | 287 | legend { 288 | padding: 0; 289 | } 290 | 291 | ol, 292 | ul, 293 | menu { 294 | list-style: none; 295 | margin: 0; 296 | padding: 0; 297 | } 298 | 299 | /* 300 | Prevent resizing textareas horizontally by default. 301 | */ 302 | 303 | textarea { 304 | resize: vertical; 305 | } 306 | 307 | /* 308 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 309 | 2. Set the default placeholder color to the user's configured gray 400 color. 310 | */ 311 | 312 | input::placeholder, 313 | textarea::placeholder { 314 | opacity: 1; /* 1 */ 315 | color: #9ca3af; /* 2 */ 316 | } 317 | 318 | /* 319 | Set the default cursor for buttons. 320 | */ 321 | 322 | button, 323 | [role="button"] { 324 | cursor: pointer; 325 | } 326 | 327 | /* 328 | Make sure disabled buttons don't get the pointer cursor. 329 | */ 330 | :disabled { 331 | cursor: default; 332 | } 333 | 334 | /* 335 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 336 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 337 | This can trigger a poorly considered lint error in some tools but is included by design. 338 | */ 339 | 340 | img, 341 | svg, 342 | video, 343 | canvas, 344 | audio, 345 | iframe, 346 | embed, 347 | object { 348 | display: block; /* 1 */ 349 | vertical-align: middle; /* 2 */ 350 | } 351 | 352 | /* 353 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 354 | */ 355 | 356 | img, 357 | video { 358 | max-width: 100%; 359 | height: auto; 360 | } 361 | 362 | /* 363 | Ensure the default browser behavior of the `hidden` attribute. 364 | */ 365 | 366 | [hidden] { 367 | display: none; 368 | } 369 | 370 | *, ::before, ::after { 371 | --tw-translate-x: 0; 372 | --tw-translate-y: 0; 373 | --tw-rotate: 0; 374 | --tw-skew-x: 0; 375 | --tw-skew-y: 0; 376 | --tw-scale-x: 1; 377 | --tw-scale-y: 1; 378 | --tw-pan-x: ; 379 | --tw-pan-y: ; 380 | --tw-pinch-zoom: ; 381 | --tw-scroll-snap-strictness: proximity; 382 | --tw-ordinal: ; 383 | --tw-slashed-zero: ; 384 | --tw-numeric-figure: ; 385 | --tw-numeric-spacing: ; 386 | --tw-numeric-fraction: ; 387 | --tw-ring-inset: ; 388 | --tw-ring-offset-width: 0px; 389 | --tw-ring-offset-color: #fff; 390 | --tw-ring-color: rgb(59 130 246 / 0.5); 391 | --tw-ring-offset-shadow: 0 0 #0000; 392 | --tw-ring-shadow: 0 0 #0000; 393 | --tw-shadow: 0 0 #0000; 394 | --tw-shadow-colored: 0 0 #0000; 395 | --tw-blur: ; 396 | --tw-brightness: ; 397 | --tw-contrast: ; 398 | --tw-grayscale: ; 399 | --tw-hue-rotate: ; 400 | --tw-invert: ; 401 | --tw-saturate: ; 402 | --tw-sepia: ; 403 | --tw-drop-shadow: ; 404 | --tw-backdrop-blur: ; 405 | --tw-backdrop-brightness: ; 406 | --tw-backdrop-contrast: ; 407 | --tw-backdrop-grayscale: ; 408 | --tw-backdrop-hue-rotate: ; 409 | --tw-backdrop-invert: ; 410 | --tw-backdrop-opacity: ; 411 | --tw-backdrop-saturate: ; 412 | --tw-backdrop-sepia: ; 413 | } 414 | .container { 415 | width: 100%; 416 | padding-right: 1rem; 417 | padding-left: 1rem; 418 | } 419 | @media (min-width: 640px) { 420 | 421 | .container { 422 | max-width: 640px; 423 | padding-right: 2rem; 424 | padding-left: 2rem; 425 | } 426 | } 427 | @media (min-width: 768px) { 428 | 429 | .container { 430 | max-width: 768px; 431 | } 432 | } 433 | @media (min-width: 1024px) { 434 | 435 | .container { 436 | max-width: 1024px; 437 | padding-right: 5rem; 438 | padding-left: 5rem; 439 | } 440 | } 441 | @media (min-width: 1280px) { 442 | 443 | .container { 444 | max-width: 1280px; 445 | padding-right: 6rem; 446 | padding-left: 6rem; 447 | } 448 | } 449 | @media (min-width: 1536px) { 450 | 451 | .container { 452 | max-width: 1536px; 453 | padding-right: 8rem; 454 | padding-left: 8rem; 455 | } 456 | } 457 | .fixed { 458 | position: fixed; 459 | } 460 | .relative { 461 | position: relative; 462 | } 463 | .inset-0 { 464 | top: 0px; 465 | right: 0px; 466 | bottom: 0px; 467 | left: 0px; 468 | } 469 | .z-30 { 470 | z-index: 30; 471 | } 472 | .z-20 { 473 | z-index: 20; 474 | } 475 | .z-10 { 476 | z-index: 10; 477 | } 478 | .float-right { 479 | float: right; 480 | } 481 | .m-4 { 482 | margin: 1rem; 483 | } 484 | .m-0 { 485 | margin: 0px; 486 | } 487 | .mx-5 { 488 | margin-left: 1.25rem; 489 | margin-right: 1.25rem; 490 | } 491 | .mx-auto { 492 | margin-left: auto; 493 | margin-right: auto; 494 | } 495 | .mt-6 { 496 | margin-top: 1.5rem; 497 | } 498 | .mt-2 { 499 | margin-top: 0.5rem; 500 | } 501 | .mt-8 { 502 | margin-top: 2rem; 503 | } 504 | .mt-5 { 505 | margin-top: 1.25rem; 506 | } 507 | .mb-8 { 508 | margin-bottom: 2rem; 509 | } 510 | .mt-10 { 511 | margin-top: 2.5rem; 512 | } 513 | .mb-20 { 514 | margin-bottom: 5rem; 515 | } 516 | .mb-2 { 517 | margin-bottom: 0.5rem; 518 | } 519 | .mb-7 { 520 | margin-bottom: 1.75rem; 521 | } 522 | .mb-4 { 523 | margin-bottom: 1rem; 524 | } 525 | .mt-12 { 526 | margin-top: 3rem; 527 | } 528 | .mr-4 { 529 | margin-right: 1rem; 530 | } 531 | .mb-10 { 532 | margin-bottom: 2.5rem; 533 | } 534 | .mt-14 { 535 | margin-top: 3.5rem; 536 | } 537 | .mr-10 { 538 | margin-right: 2.5rem; 539 | } 540 | .ml-2 { 541 | margin-left: 0.5rem; 542 | } 543 | .mb-5 { 544 | margin-bottom: 1.25rem; 545 | } 546 | .mb-1 { 547 | margin-bottom: 0.25rem; 548 | } 549 | .mb-3 { 550 | margin-bottom: 0.75rem; 551 | } 552 | .mt-4 { 553 | margin-top: 1rem; 554 | } 555 | .mb-6 { 556 | margin-bottom: 1.5rem; 557 | } 558 | .mr-2 { 559 | margin-right: 0.5rem; 560 | } 561 | .mt-20 { 562 | margin-top: 5rem; 563 | } 564 | .mb-12 { 565 | margin-bottom: 3rem; 566 | } 567 | .ml-1 { 568 | margin-left: 0.25rem; 569 | } 570 | .ml-0 { 571 | margin-left: 0px; 572 | } 573 | .ml-8 { 574 | margin-left: 2rem; 575 | } 576 | .block { 577 | display: block; 578 | } 579 | .flex { 580 | display: flex; 581 | } 582 | .grid { 583 | display: grid; 584 | } 585 | .hidden { 586 | display: none; 587 | } 588 | .h-full { 589 | height: 100%; 590 | } 591 | .h-5 { 592 | height: 1.25rem; 593 | } 594 | .h-7 { 595 | height: 1.75rem; 596 | } 597 | .max-h-screen { 598 | max-height: 100vh; 599 | } 600 | .w-full { 601 | width: 100%; 602 | } 603 | .w-64 { 604 | width: 16rem; 605 | } 606 | .w-96 { 607 | width: 24rem; 608 | } 609 | .w-40 { 610 | width: 10rem; 611 | } 612 | .w-5 { 613 | width: 1.25rem; 614 | } 615 | .w-36 { 616 | width: 9rem; 617 | } 618 | .w-7 { 619 | width: 1.75rem; 620 | } 621 | .w-24 { 622 | width: 6rem; 623 | } 624 | .max-w-md { 625 | max-width: 28rem; 626 | } 627 | .max-w-xl { 628 | max-width: 36rem; 629 | } 630 | .max-w-screen-lg { 631 | max-width: 1024px; 632 | } 633 | .cursor-pointer { 634 | cursor: pointer; 635 | } 636 | .grid-cols-2 { 637 | grid-template-columns: repeat(2, minmax(0, 1fr)); 638 | } 639 | .grid-cols-1 { 640 | grid-template-columns: repeat(1, minmax(0, 1fr)); 641 | } 642 | .flex-row { 643 | flex-direction: row; 644 | } 645 | .flex-col { 646 | flex-direction: column; 647 | } 648 | .flex-col-reverse { 649 | flex-direction: column-reverse; 650 | } 651 | .items-center { 652 | align-items: center; 653 | } 654 | .justify-center { 655 | justify-content: center; 656 | } 657 | .justify-between { 658 | justify-content: space-between; 659 | } 660 | .gap-10 { 661 | gap: 2.5rem; 662 | } 663 | .gap-2 { 664 | gap: 0.5rem; 665 | } 666 | .gap-0 { 667 | gap: 0px; 668 | } 669 | .gap-3 { 670 | gap: 0.75rem; 671 | } 672 | .gap-4 { 673 | gap: 1rem; 674 | } 675 | .overflow-y-hidden { 676 | overflow-y: hidden; 677 | } 678 | .rounded-lg { 679 | border-radius: 0.5rem; 680 | } 681 | .rounded-md { 682 | border-radius: 0.375rem; 683 | } 684 | .rounded-xl { 685 | border-radius: 0.75rem; 686 | } 687 | .rounded-sm { 688 | border-radius: 0.125rem; 689 | } 690 | .rounded-t-xl { 691 | border-top-left-radius: 0.75rem; 692 | border-top-right-radius: 0.75rem; 693 | } 694 | .border { 695 | border-width: 1px; 696 | } 697 | .border-b { 698 | border-bottom-width: 1px; 699 | } 700 | .border-t-2 { 701 | border-top-width: 2px; 702 | } 703 | .border-none { 704 | border-style: none; 705 | } 706 | .border-ternary-light { 707 | --tw-border-opacity: 1; 708 | border-color: rgb(246 247 248 / var(--tw-border-opacity)); 709 | } 710 | .border-gray-300 { 711 | --tw-border-opacity: 1; 712 | border-color: rgb(209 213 219 / var(--tw-border-opacity)); 713 | } 714 | .border-primary-light { 715 | --tw-border-opacity: 1; 716 | border-color: rgb(247 248 252 / var(--tw-border-opacity)); 717 | } 718 | .border-gray-200 { 719 | --tw-border-opacity: 1; 720 | border-color: rgb(229 231 235 / var(--tw-border-opacity)); 721 | } 722 | .border-indigo-200 { 723 | --tw-border-opacity: 1; 724 | border-color: rgb(199 210 254 / var(--tw-border-opacity)); 725 | } 726 | .border-opacity-50 { 727 | --tw-border-opacity: 0.5; 728 | } 729 | .bg-secondary-light { 730 | --tw-bg-opacity: 1; 731 | background-color: rgb(255 255 255 / var(--tw-bg-opacity)); 732 | } 733 | .bg-black { 734 | --tw-bg-opacity: 1; 735 | background-color: rgb(0 0 0 / var(--tw-bg-opacity)); 736 | } 737 | .bg-indigo-500 { 738 | --tw-bg-opacity: 1; 739 | background-color: rgb(99 102 241 / var(--tw-bg-opacity)); 740 | } 741 | .bg-gray-600 { 742 | --tw-bg-opacity: 1; 743 | background-color: rgb(75 85 99 / var(--tw-bg-opacity)); 744 | } 745 | .bg-primary-light { 746 | --tw-bg-opacity: 1; 747 | background-color: rgb(247 248 252 / var(--tw-bg-opacity)); 748 | } 749 | .bg-ternary-light { 750 | --tw-bg-opacity: 1; 751 | background-color: rgb(246 247 248 / var(--tw-bg-opacity)); 752 | } 753 | .bg-indigo-50 { 754 | --tw-bg-opacity: 1; 755 | background-color: rgb(238 242 255 / var(--tw-bg-opacity)); 756 | } 757 | .bg-gray-50 { 758 | --tw-bg-opacity: 1; 759 | background-color: rgb(249 250 251 / var(--tw-bg-opacity)); 760 | } 761 | .bg-opacity-50 { 762 | --tw-bg-opacity: 0.5; 763 | } 764 | .fill-current { 765 | fill: currentColor; 766 | } 767 | .p-5 { 768 | padding: 1.25rem; 769 | } 770 | .p-6 { 771 | padding: 1.5rem; 772 | } 773 | .p-2 { 774 | padding: 0.5rem; 775 | } 776 | .p-2\.5 { 777 | padding: 0.625rem; 778 | } 779 | .p-4 { 780 | padding: 1rem; 781 | } 782 | .p-3 { 783 | padding: 0.75rem; 784 | } 785 | .px-4 { 786 | padding-left: 1rem; 787 | padding-right: 1rem; 788 | } 789 | .px-5 { 790 | padding-left: 1.25rem; 791 | padding-right: 1.25rem; 792 | } 793 | .py-2 { 794 | padding-top: 0.5rem; 795 | padding-bottom: 0.5rem; 796 | } 797 | .py-5 { 798 | padding-top: 1.25rem; 799 | padding-bottom: 1.25rem; 800 | } 801 | .px-8 { 802 | padding-left: 2rem; 803 | padding-right: 2rem; 804 | } 805 | .px-6 { 806 | padding-left: 1.5rem; 807 | padding-right: 1.5rem; 808 | } 809 | .py-3 { 810 | padding-top: 0.75rem; 811 | padding-bottom: 0.75rem; 812 | } 813 | .px-10 { 814 | padding-left: 2.5rem; 815 | padding-right: 2.5rem; 816 | } 817 | .py-20 { 818 | padding-top: 5rem; 819 | padding-bottom: 5rem; 820 | } 821 | .py-2\.5 { 822 | padding-top: 0.625rem; 823 | padding-bottom: 0.625rem; 824 | } 825 | .py-6 { 826 | padding-top: 1.5rem; 827 | padding-bottom: 1.5rem; 828 | } 829 | .pb-4 { 830 | padding-bottom: 1rem; 831 | } 832 | .pt-10 { 833 | padding-top: 2.5rem; 834 | } 835 | .pb-3 { 836 | padding-bottom: 0.75rem; 837 | } 838 | .pl-3 { 839 | padding-left: 0.75rem; 840 | } 841 | .pr-1 { 842 | padding-right: 0.25rem; 843 | } 844 | .pt-20 { 845 | padding-top: 5rem; 846 | } 847 | .pb-8 { 848 | padding-bottom: 2rem; 849 | } 850 | .pt-3 { 851 | padding-top: 0.75rem; 852 | } 853 | .text-left { 854 | text-align: left; 855 | } 856 | .text-center { 857 | text-align: center; 858 | } 859 | .text-right { 860 | text-align: right; 861 | } 862 | .text-xl { 863 | font-size: 1.25rem; 864 | line-height: 1.75rem; 865 | } 866 | .text-3xl { 867 | font-size: 1.875rem; 868 | line-height: 2.25rem; 869 | } 870 | .text-lg { 871 | font-size: 1.125rem; 872 | line-height: 1.75rem; 873 | } 874 | .text-2xl { 875 | font-size: 1.5rem; 876 | line-height: 2rem; 877 | } 878 | .text-4xl { 879 | font-size: 2.25rem; 880 | line-height: 2.5rem; 881 | } 882 | .text-sm { 883 | font-size: 0.875rem; 884 | line-height: 1.25rem; 885 | } 886 | .font-bold { 887 | font-weight: 700; 888 | } 889 | .font-medium { 890 | font-weight: 500; 891 | } 892 | .font-semibold { 893 | font-weight: 600; 894 | } 895 | .uppercase { 896 | text-transform: uppercase; 897 | } 898 | .leading-loose { 899 | line-height: 2; 900 | } 901 | .leading-none { 902 | line-height: 1; 903 | } 904 | .leading-normal { 905 | line-height: 1.5; 906 | } 907 | .tracking-wider { 908 | letter-spacing: 0.05em; 909 | } 910 | .text-primary-dark { 911 | --tw-text-opacity: 1; 912 | color: rgb(13 36 56 / var(--tw-text-opacity)); 913 | } 914 | .text-white { 915 | --tw-text-opacity: 1; 916 | color: rgb(255 255 255 / var(--tw-text-opacity)); 917 | } 918 | .text-primary-light { 919 | --tw-text-opacity: 1; 920 | color: rgb(247 248 252 / var(--tw-text-opacity)); 921 | } 922 | .text-secondary-dark { 923 | --tw-text-opacity: 1; 924 | color: rgb(16 45 68 / var(--tw-text-opacity)); 925 | } 926 | .text-ternary-dark { 927 | --tw-text-opacity: 1; 928 | color: rgb(30 56 81 / var(--tw-text-opacity)); 929 | } 930 | .text-gray-500 { 931 | --tw-text-opacity: 1; 932 | color: rgb(107 114 128 / var(--tw-text-opacity)); 933 | } 934 | .text-gray-400 { 935 | --tw-text-opacity: 1; 936 | color: rgb(156 163 175 / var(--tw-text-opacity)); 937 | } 938 | .text-gray-200 { 939 | --tw-text-opacity: 1; 940 | color: rgb(229 231 235 / var(--tw-text-opacity)); 941 | } 942 | .shadow-lg { 943 | --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 944 | --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); 945 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 946 | } 947 | .shadow-sm { 948 | --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); 949 | --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); 950 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 951 | } 952 | .shadow-xl { 953 | --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); 954 | --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); 955 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 956 | } 957 | .filter { 958 | filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); 959 | } 960 | .transition { 961 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; 962 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 963 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; 964 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 965 | transition-duration: 150ms; 966 | } 967 | .transition-all { 968 | transition-property: all; 969 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 970 | transition-duration: 150ms; 971 | } 972 | .duration-300 { 973 | transition-duration: 300ms; 974 | } 975 | .duration-500 { 976 | transition-duration: 500ms; 977 | } 978 | .duration-100 { 979 | transition-duration: 100ms; 980 | } 981 | .hover\:bg-indigo-600:hover { 982 | --tw-bg-opacity: 1; 983 | background-color: rgb(79 70 229 / var(--tw-bg-opacity)); 984 | } 985 | .hover\:bg-ternary-dark:hover { 986 | --tw-bg-opacity: 1; 987 | background-color: rgb(30 56 81 / var(--tw-bg-opacity)); 988 | } 989 | .hover\:bg-indigo-500:hover { 990 | --tw-bg-opacity: 1; 991 | background-color: rgb(99 102 241 / var(--tw-bg-opacity)); 992 | } 993 | .hover\:bg-gray-100:hover { 994 | --tw-bg-opacity: 1; 995 | background-color: rgb(243 244 246 / var(--tw-bg-opacity)); 996 | } 997 | .hover\:text-indigo-500:hover { 998 | --tw-text-opacity: 1; 999 | color: rgb(99 102 241 / var(--tw-text-opacity)); 1000 | } 1001 | .hover\:text-primary-dark:hover { 1002 | --tw-text-opacity: 1; 1003 | color: rgb(13 36 56 / var(--tw-text-opacity)); 1004 | } 1005 | .hover\:text-white:hover { 1006 | --tw-text-opacity: 1; 1007 | color: rgb(255 255 255 / var(--tw-text-opacity)); 1008 | } 1009 | .hover\:text-indigo-600:hover { 1010 | --tw-text-opacity: 1; 1011 | color: rgb(79 70 229 / var(--tw-text-opacity)); 1012 | } 1013 | .hover\:text-gray-400:hover { 1014 | --tw-text-opacity: 1; 1015 | color: rgb(156 163 175 / var(--tw-text-opacity)); 1016 | } 1017 | .hover\:text-gray-50:hover { 1018 | --tw-text-opacity: 1; 1019 | color: rgb(249 250 251 / var(--tw-text-opacity)); 1020 | } 1021 | .hover\:text-secondary-dark:hover { 1022 | --tw-text-opacity: 1; 1023 | color: rgb(16 45 68 / var(--tw-text-opacity)); 1024 | } 1025 | .hover\:underline:hover { 1026 | text-decoration-line: underline; 1027 | } 1028 | .hover\:shadow-xl:hover { 1029 | --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); 1030 | --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); 1031 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1032 | } 1033 | .focus\:outline-none:focus { 1034 | outline: 2px solid transparent; 1035 | outline-offset: 2px; 1036 | } 1037 | .focus\:ring-1:focus { 1038 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1039 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1040 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1041 | } 1042 | .focus\:ring-indigo-900:focus { 1043 | --tw-ring-opacity: 1; 1044 | --tw-ring-color: rgb(49 46 129 / var(--tw-ring-opacity)); 1045 | } 1046 | .dark .dark\:border-ternary-dark { 1047 | --tw-border-opacity: 1; 1048 | border-color: rgb(30 56 81 / var(--tw-border-opacity)); 1049 | } 1050 | .dark .dark\:border-secondary-dark { 1051 | --tw-border-opacity: 1; 1052 | border-color: rgb(16 45 68 / var(--tw-border-opacity)); 1053 | } 1054 | .dark .dark\:border-primary-dark { 1055 | --tw-border-opacity: 1; 1056 | border-color: rgb(13 36 56 / var(--tw-border-opacity)); 1057 | } 1058 | .dark .dark\:bg-primary-dark { 1059 | --tw-bg-opacity: 1; 1060 | background-color: rgb(13 36 56 / var(--tw-bg-opacity)); 1061 | } 1062 | .dark .dark\:bg-ternary-dark { 1063 | --tw-bg-opacity: 1; 1064 | background-color: rgb(30 56 81 / var(--tw-bg-opacity)); 1065 | } 1066 | .dark .dark\:bg-gray-200 { 1067 | --tw-bg-opacity: 1; 1068 | background-color: rgb(229 231 235 / var(--tw-bg-opacity)); 1069 | } 1070 | .dark .dark\:bg-secondary-dark { 1071 | --tw-bg-opacity: 1; 1072 | background-color: rgb(16 45 68 / var(--tw-bg-opacity)); 1073 | } 1074 | .dark .dark\:font-medium { 1075 | font-weight: 500; 1076 | } 1077 | .dark .dark\:text-primary-light { 1078 | --tw-text-opacity: 1; 1079 | color: rgb(247 248 252 / var(--tw-text-opacity)); 1080 | } 1081 | .dark .dark\:text-ternary-light { 1082 | --tw-text-opacity: 1; 1083 | color: rgb(246 247 248 / var(--tw-text-opacity)); 1084 | } 1085 | .dark .dark\:text-secondary-dark { 1086 | --tw-text-opacity: 1; 1087 | color: rgb(16 45 68 / var(--tw-text-opacity)); 1088 | } 1089 | .dark .dark\:text-secondary-light { 1090 | --tw-text-opacity: 1; 1091 | color: rgb(255 255 255 / var(--tw-text-opacity)); 1092 | } 1093 | .dark .dark\:text-gray-400 { 1094 | --tw-text-opacity: 1; 1095 | color: rgb(156 163 175 / var(--tw-text-opacity)); 1096 | } 1097 | .dark .dark\:text-gray-200 { 1098 | --tw-text-opacity: 1; 1099 | color: rgb(229 231 235 / var(--tw-text-opacity)); 1100 | } 1101 | .dark .dark\:hover\:bg-primary-light:hover { 1102 | --tw-bg-opacity: 1; 1103 | background-color: rgb(247 248 252 / var(--tw-bg-opacity)); 1104 | } 1105 | .dark .dark\:hover\:text-indigo-400:hover { 1106 | --tw-text-opacity: 1; 1107 | color: rgb(129 140 248 / var(--tw-text-opacity)); 1108 | } 1109 | .dark .dark\:hover\:text-primary-light:hover { 1110 | --tw-text-opacity: 1; 1111 | color: rgb(247 248 252 / var(--tw-text-opacity)); 1112 | } 1113 | .dark .dark\:hover\:text-indigo-300:hover { 1114 | --tw-text-opacity: 1; 1115 | color: rgb(165 180 252 / var(--tw-text-opacity)); 1116 | } 1117 | .dark .dark\:hover\:text-secondary-light:hover { 1118 | --tw-text-opacity: 1; 1119 | color: rgb(255 255 255 / var(--tw-text-opacity)); 1120 | } 1121 | @media (min-width: 640px) { 1122 | 1123 | .sm\:container { 1124 | width: 100%; 1125 | padding-right: 1rem; 1126 | padding-left: 1rem; 1127 | } 1128 | 1129 | @media (min-width: 640px) { 1130 | 1131 | .sm\:container { 1132 | max-width: 640px; 1133 | padding-right: 2rem; 1134 | padding-left: 2rem; 1135 | } 1136 | } 1137 | 1138 | @media (min-width: 768px) { 1139 | 1140 | .sm\:container { 1141 | max-width: 768px; 1142 | } 1143 | } 1144 | 1145 | @media (min-width: 1024px) { 1146 | 1147 | .sm\:container { 1148 | max-width: 1024px; 1149 | padding-right: 5rem; 1150 | padding-left: 5rem; 1151 | } 1152 | } 1153 | 1154 | @media (min-width: 1280px) { 1155 | 1156 | .sm\:container { 1157 | max-width: 1280px; 1158 | padding-right: 6rem; 1159 | padding-left: 6rem; 1160 | } 1161 | } 1162 | 1163 | @media (min-width: 1536px) { 1164 | 1165 | .sm\:container { 1166 | max-width: 1536px; 1167 | padding-right: 8rem; 1168 | padding-left: 8rem; 1169 | } 1170 | } 1171 | 1172 | .sm\:mx-auto { 1173 | margin-left: auto; 1174 | margin-right: auto; 1175 | } 1176 | 1177 | .sm\:mx-4 { 1178 | margin-left: 1rem; 1179 | margin-right: 1rem; 1180 | } 1181 | 1182 | .sm\:mt-0 { 1183 | margin-top: 0px; 1184 | } 1185 | 1186 | .sm\:mt-10 { 1187 | margin-top: 2.5rem; 1188 | } 1189 | 1190 | .sm\:mt-20 { 1191 | margin-top: 5rem; 1192 | } 1193 | 1194 | .sm\:mt-14 { 1195 | margin-top: 3.5rem; 1196 | } 1197 | 1198 | .sm\:mb-0 { 1199 | margin-bottom: 0px; 1200 | } 1201 | 1202 | .sm\:mb-14 { 1203 | margin-bottom: 3.5rem; 1204 | } 1205 | 1206 | .sm\:mt-16 { 1207 | margin-top: 4rem; 1208 | } 1209 | 1210 | .sm\:mr-3 { 1211 | margin-right: 0.75rem; 1212 | } 1213 | 1214 | .sm\:mb-28 { 1215 | margin-bottom: 7rem; 1216 | } 1217 | 1218 | .sm\:ml-4 { 1219 | margin-left: 1rem; 1220 | } 1221 | 1222 | .sm\:mt-3 { 1223 | margin-top: 0.75rem; 1224 | } 1225 | 1226 | .sm\:block { 1227 | display: block; 1228 | } 1229 | 1230 | .sm\:flex { 1231 | display: flex; 1232 | } 1233 | 1234 | .sm\:hidden { 1235 | display: none; 1236 | } 1237 | 1238 | .sm\:h-6 { 1239 | height: 1.5rem; 1240 | } 1241 | 1242 | .sm\:w-1\/4 { 1243 | width: 25%; 1244 | } 1245 | 1246 | .sm\:w-3\/4 { 1247 | width: 75%; 1248 | } 1249 | 1250 | .sm\:w-1\/3 { 1251 | width: 33.333333%; 1252 | } 1253 | 1254 | .sm\:w-2\/3 { 1255 | width: 66.666667%; 1256 | } 1257 | 1258 | .sm\:w-48 { 1259 | width: 12rem; 1260 | } 1261 | 1262 | .sm\:grid-cols-4 { 1263 | grid-template-columns: repeat(4, minmax(0, 1fr)); 1264 | } 1265 | 1266 | .sm\:grid-cols-3 { 1267 | grid-template-columns: repeat(3, minmax(0, 1fr)); 1268 | } 1269 | 1270 | .sm\:grid-cols-2 { 1271 | grid-template-columns: repeat(2, minmax(0, 1fr)); 1272 | } 1273 | 1274 | .sm\:flex-row { 1275 | flex-direction: row; 1276 | } 1277 | 1278 | .sm\:items-center { 1279 | align-items: center; 1280 | } 1281 | 1282 | .sm\:justify-between { 1283 | justify-content: space-between; 1284 | } 1285 | 1286 | .sm\:gap-10 { 1287 | gap: 2.5rem; 1288 | } 1289 | 1290 | .sm\:gap-8 { 1291 | gap: 2rem; 1292 | } 1293 | 1294 | .sm\:border-t-0 { 1295 | border-top-width: 0px; 1296 | } 1297 | 1298 | .sm\:p-10 { 1299 | padding: 2.5rem; 1300 | } 1301 | 1302 | .sm\:p-0 { 1303 | padding: 0px; 1304 | } 1305 | 1306 | .sm\:px-6 { 1307 | padding-left: 1.5rem; 1308 | padding-right: 1.5rem; 1309 | } 1310 | 1311 | .sm\:py-2\.5 { 1312 | padding-top: 0.625rem; 1313 | padding-bottom: 0.625rem; 1314 | } 1315 | 1316 | .sm\:py-2 { 1317 | padding-top: 0.5rem; 1318 | padding-bottom: 0.5rem; 1319 | } 1320 | 1321 | .sm\:py-10 { 1322 | padding-top: 2.5rem; 1323 | padding-bottom: 2.5rem; 1324 | } 1325 | 1326 | .sm\:px-4 { 1327 | padding-left: 1rem; 1328 | padding-right: 1rem; 1329 | } 1330 | 1331 | .sm\:py-3 { 1332 | padding-top: 0.75rem; 1333 | padding-bottom: 0.75rem; 1334 | } 1335 | 1336 | .sm\:px-0 { 1337 | padding-left: 0px; 1338 | padding-right: 0px; 1339 | } 1340 | 1341 | .sm\:pb-1 { 1342 | padding-bottom: 0.25rem; 1343 | } 1344 | 1345 | .sm\:pt-14 { 1346 | padding-top: 3.5rem; 1347 | } 1348 | 1349 | .sm\:pt-2 { 1350 | padding-top: 0.5rem; 1351 | } 1352 | 1353 | .sm\:pt-0 { 1354 | padding-top: 0px; 1355 | } 1356 | 1357 | .sm\:text-left { 1358 | text-align: left; 1359 | } 1360 | 1361 | .sm\:text-xl { 1362 | font-size: 1.25rem; 1363 | line-height: 1.75rem; 1364 | } 1365 | 1366 | .sm\:text-3xl { 1367 | font-size: 1.875rem; 1368 | line-height: 2.25rem; 1369 | } 1370 | 1371 | .sm\:text-4xl { 1372 | font-size: 2.25rem; 1373 | line-height: 2.5rem; 1374 | } 1375 | 1376 | .sm\:text-lg { 1377 | font-size: 1.125rem; 1378 | line-height: 1.75rem; 1379 | } 1380 | 1381 | .sm\:text-2xl { 1382 | font-size: 1.5rem; 1383 | line-height: 2rem; 1384 | } 1385 | 1386 | .sm\:shadow-none { 1387 | --tw-shadow: 0 0 #0000; 1388 | --tw-shadow-colored: 0 0 #0000; 1389 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1390 | } 1391 | } 1392 | @media (min-width: 768px) { 1393 | 1394 | .md\:mt-2 { 1395 | margin-top: 0.5rem; 1396 | } 1397 | 1398 | .md\:flex { 1399 | display: flex; 1400 | } 1401 | 1402 | .md\:w-1\/3 { 1403 | width: 33.333333%; 1404 | } 1405 | 1406 | .md\:max-w-xl { 1407 | max-width: 36rem; 1408 | } 1409 | 1410 | .md\:flex-row { 1411 | flex-direction: row; 1412 | } 1413 | 1414 | .md\:text-xl { 1415 | font-size: 1.25rem; 1416 | line-height: 1.75rem; 1417 | } 1418 | 1419 | .md\:text-3xl { 1420 | font-size: 1.875rem; 1421 | line-height: 2.25rem; 1422 | } 1423 | } 1424 | @media (min-width: 1024px) { 1425 | 1426 | .lg\:mt-10 { 1427 | margin-top: 2.5rem; 1428 | } 1429 | 1430 | .lg\:w-1\/2 { 1431 | width: 50%; 1432 | } 1433 | 1434 | .lg\:max-w-xl { 1435 | max-width: 36rem; 1436 | } 1437 | 1438 | .lg\:grid-cols-3 { 1439 | grid-template-columns: repeat(3, minmax(0, 1fr)); 1440 | } 1441 | 1442 | .lg\:flex-row { 1443 | flex-direction: row; 1444 | } 1445 | 1446 | .lg\:py-10 { 1447 | padding-top: 2.5rem; 1448 | padding-bottom: 2.5rem; 1449 | } 1450 | 1451 | .lg\:text-2xl { 1452 | font-size: 1.5rem; 1453 | line-height: 2rem; 1454 | } 1455 | 1456 | .lg\:text-3xl { 1457 | font-size: 1.875rem; 1458 | line-height: 2.25rem; 1459 | } 1460 | } 1461 | @media (min-width: 1280px) { 1462 | 1463 | .xl\:max-w-xl { 1464 | max-width: 36rem; 1465 | } 1466 | 1467 | .xl\:max-w-screen-xl { 1468 | max-width: 1280px; 1469 | } 1470 | 1471 | .xl\:text-4xl { 1472 | font-size: 2.25rem; 1473 | line-height: 2.5rem; 1474 | } 1475 | 1476 | .xl\:text-3xl { 1477 | font-size: 1.875rem; 1478 | line-height: 2.25rem; 1479 | } 1480 | } 1481 | -------------------------------------------------------------------------------- /src/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/data/aboutMeData.js: -------------------------------------------------------------------------------- 1 | export const aboutMeData = [ 2 | { 3 | id: 1, 4 | bio: 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta? Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt totam dolorum, ducimus obcaecati, voluptas facilis molestias nobis ut quam natus similique inventore excepturi optio ipsa deleniti fugit illo. Unde, amet! Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum illo necessitatibus perspiciatis! Aperiam perferendis labore temporibus, eos culpa corporis recusandae quas, fuga voluptatibus nesciunt odit libero tenetur neque consequatur ea.', 5 | }, 6 | { 7 | id: 2, 8 | bio: 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta?', 9 | }, 10 | ]; 11 | -------------------------------------------------------------------------------- /src/data/clientsData.js: -------------------------------------------------------------------------------- 1 | // Import images 2 | import AmazonImage from '../images/brands/amazon_gray.png'; 3 | import SonyImage from '../images/brands/sony_gray.png'; 4 | import AdidasImage from '../images/brands/adidas_gray.png'; 5 | import FilaImage from '../images/brands/fila_gray.png'; 6 | import NBImage from '../images/brands/nb_gray.png'; 7 | import SamsungImage from '../images/brands/samsung_gray.png'; 8 | import CanonImage from '../images/brands/canon_gray.png'; 9 | import PumaImage from '../images/brands/puma_gray.png'; 10 | 11 | export const clientsHeading = 'Some of the brands that trust me'; 12 | 13 | export const clientsData = [ 14 | { 15 | id: 1, 16 | title: 'Amazon', 17 | img: AmazonImage, 18 | }, 19 | { 20 | id: 2, 21 | title: 'Sony', 22 | img: SonyImage, 23 | }, 24 | { 25 | id: 3, 26 | title: 'Adidas', 27 | img: AdidasImage, 28 | }, 29 | { 30 | id: 4, 31 | title: 'FILA', 32 | img: FilaImage, 33 | }, 34 | { 35 | id: 5, 36 | title: 'NB', 37 | img: NBImage, 38 | }, 39 | { 40 | id: 6, 41 | title: 'SAMSUNG', 42 | img: SamsungImage, 43 | }, 44 | { 45 | id: 7, 46 | title: 'CANON', 47 | img: CanonImage, 48 | }, 49 | { 50 | id: 8, 51 | title: 'PUMA', 52 | img: PumaImage, 53 | }, 54 | ]; 55 | -------------------------------------------------------------------------------- /src/data/projects.js: -------------------------------------------------------------------------------- 1 | // Import images 2 | import WebImage1 from '../images/web-project-1.jpg'; 3 | import WebImage2 from '../images/web-project-2.jpg'; 4 | import MobileImage1 from '../images/mobile-project-1.jpg'; 5 | import MobileImage2 from '../images/mobile-project-2.jpg'; 6 | import UIImage1 from '../images/ui-project-1.jpg'; 7 | import UIImage2 from '../images/ui-project-2.jpg'; 8 | 9 | export const projectsData = [ 10 | { 11 | id: 1, 12 | title: 'Google Health Platform', 13 | category: 'Web Application', 14 | img: WebImage2, 15 | ProjectHeader: { 16 | title: 'Project Management UI - From Context', 17 | publishDate: 'Jul 26, 2021', 18 | tags: 'UI / Frontend', 19 | }, 20 | }, 21 | { 22 | id: 2, 23 | title: 'Phoenix Digital Agency', 24 | category: 'Mobile Application', 25 | img: MobileImage2, 26 | }, 27 | { 28 | id: 3, 29 | title: 'Project Management UI', 30 | category: 'UI/UX Design', 31 | img: UIImage1, 32 | }, 33 | { 34 | id: 4, 35 | title: 'Cloud Storage Platform', 36 | category: 'UI/UX Design', 37 | img: UIImage2, 38 | }, 39 | { 40 | id: 5, 41 | title: 'React Social App', 42 | category: 'Mobile Application', 43 | img: MobileImage1, 44 | }, 45 | { 46 | id: 6, 47 | title: 'Apple Design System', 48 | category: 'Web Application', 49 | img: WebImage1, 50 | }, 51 | ]; 52 | -------------------------------------------------------------------------------- /src/data/singleProjectData.js: -------------------------------------------------------------------------------- 1 | // Import images 2 | import Image1 from '../images/ui-project-1.jpg'; 3 | import Image2 from '../images/web-project-2.jpg'; 4 | import Image3 from '../images/mobile-project-2.jpg'; 5 | import Image4 from '../images/mobile-project-1.jpg'; 6 | import Image5 from '../images/web-project-1.jpg'; 7 | import Image6 from '../images/ui-project-2.jpg'; 8 | // Import icons 9 | import { 10 | FiFacebook, 11 | FiInstagram, 12 | FiLinkedin, 13 | FiTwitter, 14 | FiYoutube, 15 | } from 'react-icons/fi'; 16 | 17 | export const singleProjectData = { 18 | ProjectHeader: { 19 | title: 'Project Management UI', 20 | publishDate: 'Jul 26, 2021', 21 | tags: 'UI / Frontend', 22 | }, 23 | ProjectImages: [ 24 | { 25 | id: 1, 26 | title: 'Kabul Project Management UI', 27 | img: Image1, 28 | }, 29 | { 30 | id: 2, 31 | title: 'Kabul Project Management UI', 32 | img: Image2, 33 | }, 34 | { 35 | id: 3, 36 | title: 'Kabul Project Management UI', 37 | img: Image3, 38 | }, 39 | ], 40 | ProjectInfo: { 41 | ClientHeading: 'About Client', 42 | CompanyInfo: [ 43 | { 44 | id: 1, 45 | title: 'Name', 46 | details: 'Company Ltd', 47 | }, 48 | { 49 | id: 2, 50 | title: 'Services', 51 | details: 'UI Design & Frontend Development', 52 | }, 53 | { 54 | id: 3, 55 | title: 'Website', 56 | details: 'https://company.com', 57 | }, 58 | { 59 | id: 4, 60 | title: 'Phone', 61 | details: '555 8888 888', 62 | }, 63 | ], 64 | ObjectivesHeading: 'Objective', 65 | ObjectivesDetails: 66 | 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Optio, natus! Quibusdam enim quod in esse, mollitia molestias incidunt quas ipsa accusamus veniam.', 67 | Technologies: [ 68 | { 69 | title: 'Tools & Technologies', 70 | techs: [ 71 | 'HTML', 72 | 'CSS', 73 | 'JavaScript', 74 | 'Vue.js', 75 | 'TailwindCSS', 76 | 'AdobeXD', 77 | ], 78 | }, 79 | ], 80 | ProjectDetailsHeading: 'Challenge', 81 | ProjectDetails: [ 82 | { 83 | id: 1, 84 | details: 85 | 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta? Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt totam dolorum, ducimus obcaecati, voluptas facilis molestias nobis ut quam natus similique inventore excepturi optio ipsa deleniti fugit illo. Unde, amet! Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum illo necessitatibus perspiciatis! Aperiam perferendis labore temporibus, eos culpa corporis recusandae quas, fuga voluptatibus nesciunt odit libero tenetur neque consequatur ea.', 86 | }, 87 | { 88 | id: 2, 89 | details: 90 | 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta?', 91 | }, 92 | { 93 | id: 3, 94 | details: 95 | 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta?', 96 | }, 97 | { 98 | id: 4, 99 | details: 100 | 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nihil vel illum asperiores dignissimos cumque quibusdam et fugiat voluptatem nobis suscipit explicabo, eaque consequatur nesciunt, fugit eligendi corporis laudantium adipisci soluta? Lorem ipsum, dolor sit amet consectetur adipisicing elit. Incidunt totam dolorum, ducimus obcaecati, voluptas facilis molestias nobis ut quam natus similique inventore excepturi optio ipsa deleniti fugit illo. Unde, amet! Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsum illo necessitatibus perspiciatis! Aperiam perferendis labore temporibus, eos culpa corporis recusandae quas, fuga voluptatibus nesciunt odit libero tenetur neque consequatur ea.', 101 | }, 102 | ], 103 | SocialSharingHeading: 'Share This', 104 | SocialSharing: [ 105 | { 106 | id: 1, 107 | name: 'Twitter', 108 | icon: , 109 | url: 'https://twitter.com/realstoman', 110 | }, 111 | { 112 | id: 2, 113 | name: 'Instagram', 114 | icon: , 115 | url: 'https://instagram.com/realstoman', 116 | }, 117 | { 118 | id: 3, 119 | name: 'Facebook', 120 | icon: , 121 | url: 'https://facebook.com/', 122 | }, 123 | { 124 | id: 4, 125 | name: 'LinkedIn', 126 | icon: , 127 | url: 'https://linkedin.com/', 128 | }, 129 | { 130 | id: 5, 131 | name: 'Youtube', 132 | icon: , 133 | url: 'https://www.youtube.com/c/StomanStudio', 134 | }, 135 | ], 136 | }, 137 | RelatedProject: { 138 | title: 'Related Projects', 139 | Projects: [ 140 | { 141 | id: 1, 142 | title: 'Mobile UI', 143 | img: Image4, 144 | }, 145 | { 146 | id: 2, 147 | title: 'Web Application', 148 | img: Image5, 149 | }, 150 | { 151 | id: 3, 152 | title: 'UI Design', 153 | img: Image6, 154 | }, 155 | { 156 | id: 4, 157 | title: 'Kabul Mobile App UI', 158 | img: Image3, 159 | }, 160 | ], 161 | }, 162 | }; 163 | -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Bold.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Bold.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Bold.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Bold.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-BoldItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-BoldItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Extralight.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Extralight.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Extralight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Extralight.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Extralight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Extralight.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Extralight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Extralight.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-ExtralightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-ExtralightItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-ExtralightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-ExtralightItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-ExtralightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-ExtralightItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-ExtralightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-ExtralightItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Italic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Italic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Italic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Italic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Light.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Light.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Light.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Light.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-LightItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-LightItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-LightItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Medium.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Medium.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Medium.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Medium.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-MediumItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-MediumItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-MediumItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-MediumItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-MediumItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Regular.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Regular.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Regular.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Regular.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Semibold.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Semibold.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Semibold.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Semibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Semibold.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-SemiboldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-SemiboldItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-SemiboldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-SemiboldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-SemiboldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-SemiboldItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-SemiboldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-SemiboldItalic.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Variable.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Variable.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Variable.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Variable.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Variable.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Variable.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-Variable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-Variable.woff2 -------------------------------------------------------------------------------- /src/fonts/GeneralSans-VariableItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-VariableItalic.eot -------------------------------------------------------------------------------- /src/fonts/GeneralSans-VariableItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-VariableItalic.ttf -------------------------------------------------------------------------------- /src/fonts/GeneralSans-VariableItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-VariableItalic.woff -------------------------------------------------------------------------------- /src/fonts/GeneralSans-VariableItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/fonts/GeneralSans-VariableItalic.woff2 -------------------------------------------------------------------------------- /src/hooks/useScrollToTop.jsx: -------------------------------------------------------------------------------- 1 | // NOTE: This scroll to top is the actual working scroll to to when user clicks on the circle arrow that appears when use scrolls down. 2 | // The other `ScrollToTop` component in components folder is for the default react scroll to top behavior on route visit. 3 | 4 | import { useState, useEffect } from 'react'; 5 | import { FiChevronUp } from 'react-icons/fi'; 6 | 7 | const useScrollToTop = () => { 8 | const [showScroll, setShowScroll] = useState(false); 9 | 10 | useEffect(() => { 11 | window.addEventListener('scroll', scrollToTop); 12 | return function cleanup() { 13 | window.removeEventListener('scroll', scrollToTop); 14 | }; 15 | }); 16 | 17 | const scrollToTop = () => { 18 | if (!showScroll && window.pageYOffset > 400) { 19 | setShowScroll(true); 20 | } else if (showScroll && window.pageYOffset <= 400) { 21 | setShowScroll(false); 22 | } 23 | }; 24 | 25 | const backToTop = () => { 26 | window.scrollTo({ 27 | top: 0, 28 | behavior: 'smooth', 29 | }); 30 | }; 31 | 32 | window.addEventListener('scroll', scrollToTop); 33 | 34 | return ( 35 | <> 36 | 49 | 50 | ); 51 | }; 52 | 53 | export default useScrollToTop; 54 | -------------------------------------------------------------------------------- /src/hooks/useThemeSwitcher.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | const useThemeSwitcher = () => { 4 | const [theme, setTheme] = useState(localStorage.theme); 5 | const activeTheme = theme === 'dark' ? 'light' : 'dark'; 6 | 7 | useEffect(() => { 8 | const root = window.document.documentElement; 9 | 10 | root.classList.remove(activeTheme); 11 | root.classList.add(theme); 12 | localStorage.setItem('theme', theme); 13 | }, [theme, activeTheme]); 14 | 15 | return [activeTheme, setTheme]; 16 | }; 17 | 18 | export default useThemeSwitcher; 19 | -------------------------------------------------------------------------------- /src/images/brands/adidas_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/adidas_color.png -------------------------------------------------------------------------------- /src/images/brands/adidas_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/adidas_gray.png -------------------------------------------------------------------------------- /src/images/brands/amazon_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/amazon_color.png -------------------------------------------------------------------------------- /src/images/brands/amazon_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/amazon_gray.png -------------------------------------------------------------------------------- /src/images/brands/canon_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/canon_color.png -------------------------------------------------------------------------------- /src/images/brands/canon_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/canon_gray.png -------------------------------------------------------------------------------- /src/images/brands/fila_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/fila_color.png -------------------------------------------------------------------------------- /src/images/brands/fila_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/fila_gray.png -------------------------------------------------------------------------------- /src/images/brands/nb_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/nb_color.png -------------------------------------------------------------------------------- /src/images/brands/nb_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/nb_gray.png -------------------------------------------------------------------------------- /src/images/brands/puma_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/puma_color.png -------------------------------------------------------------------------------- /src/images/brands/puma_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/puma_gray.png -------------------------------------------------------------------------------- /src/images/brands/samsung_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/samsung_color.png -------------------------------------------------------------------------------- /src/images/brands/samsung_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/samsung_gray.png -------------------------------------------------------------------------------- /src/images/brands/sony_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/sony_color.png -------------------------------------------------------------------------------- /src/images/brands/sony_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/brands/sony_gray.png -------------------------------------------------------------------------------- /src/images/developer-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 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 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 92 | 94 | 96 | 98 | 100 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 136 | 138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 244 | 245 | 247 | 249 | 251 | 252 | 253 | 254 | 255 | 262 | 269 | 270 | 271 | 272 | 274 | 275 | 276 | 277 | 278 | 279 | 285 | 286 | 287 | 289 | 290 | 291 | 292 | 294 | 297 | 299 | 300 | 302 | 304 | 305 | 306 | 307 | 308 | 310 | 311 | 312 | 313 | 315 | 317 | 318 | 319 | 320 | 321 | 322 | 325 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /src/images/developer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 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 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 92 | 94 | 96 | 98 | 100 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 136 | 138 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 244 | 245 | 247 | 249 | 251 | 252 | 253 | 254 | 255 | 262 | 269 | 270 | 271 | 272 | 274 | 275 | 276 | 277 | 278 | 279 | 285 | 286 | 287 | 289 | 290 | 291 | 292 | 294 | 297 | 299 | 300 | 302 | 304 | 305 | 306 | 307 | 308 | 310 | 311 | 312 | 313 | 315 | 317 | 318 | 319 | 320 | 321 | 322 | 325 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 337 | 338 | 339 | 340 | 341 | 342 | -------------------------------------------------------------------------------- /src/images/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 13 | 20 | 21 | 22 | 26 | 28 | 31 | 34 | 38 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/images/logo-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 13 | 20 | 21 | 22 | 26 | 28 | 31 | 34 | 38 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/images/mobile-project-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/mobile-project-1.jpg -------------------------------------------------------------------------------- /src/images/mobile-project-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/mobile-project-2.jpg -------------------------------------------------------------------------------- /src/images/profile.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/profile.jpeg -------------------------------------------------------------------------------- /src/images/ui-project-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/ui-project-1.jpg -------------------------------------------------------------------------------- /src/images/ui-project-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/ui-project-2.jpg -------------------------------------------------------------------------------- /src/images/web-project-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/web-project-1.jpg -------------------------------------------------------------------------------- /src/images/web-project-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/realstoman/react-tailwindcss-portfolio/4994be394863b2e261f283a7f95faba167779298/src/images/web-project-2.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './css/main.css'; 2 | import App from './App'; 3 | import reportWebVitals from './reportWebVitals'; 4 | 5 | import { createRoot } from 'react-dom/client'; 6 | const container = document.getElementById('root'); 7 | const root = createRoot(container); 8 | root.render(); 9 | 10 | reportWebVitals(); 11 | -------------------------------------------------------------------------------- /src/pages/AboutMe.jsx: -------------------------------------------------------------------------------- 1 | import AboutMeBio from '../components/about/AboutMeBio'; 2 | import AboutCounter from '../components/about/AboutCounter'; 3 | import AboutClients from '../components/about/AboutClients'; 4 | import { AboutMeProvider } from '../context/AboutMeContext'; 5 | import { motion } from 'framer-motion'; 6 | 7 | const About = () => { 8 | return ( 9 | 10 | 16 | 17 | 18 | 19 | {/** Counter without paddings */} 20 | 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default About; 41 | -------------------------------------------------------------------------------- /src/pages/Contact.jsx: -------------------------------------------------------------------------------- 1 | import { motion } from 'framer-motion'; 2 | import ContactDetails from '../components/contact/ContactDetails'; 3 | import ContactForm from '../components/contact/ContactForm'; 4 | 5 | const Contact = () => { 6 | return ( 7 | 17 | 18 | 19 | 20 | ); 21 | }; 22 | 23 | export default Contact; 24 | -------------------------------------------------------------------------------- /src/pages/Home.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import AppBanner from '../components/shared/AppBanner'; 3 | import ProjectsGrid from '../components/projects/ProjectsGrid'; 4 | import { ProjectsProvider } from '../context/ProjectsContext'; 5 | import Button from '../components/reusable/Button'; 6 | 7 | const Home = () => { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 22 |
25 |
26 | ); 27 | }; 28 | 29 | export default Home; 30 | -------------------------------------------------------------------------------- /src/pages/ProjectSingle.jsx: -------------------------------------------------------------------------------- 1 | import ProjectGallery from '../components/projects/ProjectGallery'; 2 | import ProjectHeader from '../components/projects/ProjectHeader'; 3 | import ProjectInfo from '../components/projects/ProjectInfo'; 4 | import ProjectRelatedProjects from '../components/projects/ProjectRelatedProjects'; 5 | import { SingleProjectProvider } from '../context/SingleProjectContext'; 6 | import { motion } from 'framer-motion'; 7 | 8 | const ProjectSingle = () => { 9 | return ( 10 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | export default ProjectSingle; 31 | -------------------------------------------------------------------------------- /src/pages/Projects.jsx: -------------------------------------------------------------------------------- 1 | import ProjectsGrid from '../components/projects/ProjectsGrid'; 2 | import { ProjectsProvider } from '../context/ProjectsContext'; 3 | 4 | const Projects = () => { 5 | return ( 6 | 7 |
8 | 9 |
10 |
11 | ); 12 | }; 13 | 14 | export default Projects; 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors'); 2 | 3 | module.exports = { 4 | content: ['./src/**/*.{js,jsx,ts,tsx}'], 5 | darkMode: 'class', 6 | theme: { 7 | extend: { 8 | colors: { 9 | 'primary-light': '#F7F8FC', 10 | 'secondary-light': '#FFFFFF', 11 | 'ternary-light': '#f6f7f8', 12 | 13 | 'primary-dark': '#0D2438', 14 | 'secondary-dark': '#102D44', 15 | 'ternary-dark': '#1E3851', 16 | }, 17 | container: { 18 | padding: { 19 | DEFAULT: '1rem', 20 | sm: '2rem', 21 | lg: '5rem', 22 | xl: '6rem', 23 | '2xl': '8rem', 24 | }, 25 | }, 26 | }, 27 | }, 28 | variants: { 29 | extend: { opacity: ['disabled'] }, 30 | }, 31 | plugins: ['@tailwindcss/forms'], 32 | }; 33 | --------------------------------------------------------------------------------