├── .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 | 
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 | You need to enable JavaScript to run this app.
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 |
35 |
36 |
37 |
38 |
119 |
120 |
130 |
131 |
132 |
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 |
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 |
50 | Message
51 |
52 |
60 |
61 |
62 |
63 |
68 |
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 |
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 |
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 |
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 |
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 | {
12 | setSelectProject(e.target.value);
13 | }}
14 | className="font-general-medium
15 | px-4
16 | sm:px-6
17 | py-2
18 | border
19 | dark:border-secondary-dark
20 | rounded-lg
21 | text-sm
22 | sm:text-md
23 | dark:font-medium
24 | bg-secondary-light
25 | dark:bg-ternary-dark
26 | text-primary-dark
27 | dark:text-ternary-light
28 | "
29 | >
30 |
31 | All Projects
32 |
33 |
34 | {selectOptions.map((option) => (
35 |
36 | {option}
37 |
38 | ))}
39 |
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 {title} ;
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 |
18 | {inputLabel}
19 |
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 |
65 |
71 |
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 |
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 |
56 | ) : (
57 |
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 |
87 |
92 | {showMenu ? (
93 |
94 | ) : (
95 |
96 | )}
97 |
98 |
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 |
138 |
139 |
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 |
176 |
177 |
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 |
23 |
24 |
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 |
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 |
--------------------------------------------------------------------------------