├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public ├── about.jpg ├── back-button.png ├── facebook.png ├── favicon.ico ├── homepage.jpg ├── index.html ├── logo.png ├── robots.txt └── twitter.png └── src ├── App.js ├── App.test.js ├── app.css ├── components ├── about │ ├── socials.jsx │ └── styles │ │ └── socials.css ├── articles │ ├── article.jsx │ └── style │ │ └── article.css ├── common │ ├── card.jsx │ ├── footer.jsx │ ├── logo.jsx │ ├── navBar.jsx │ └── styles │ │ ├── card.css │ │ ├── footer.css │ │ ├── logo.css │ │ └── navBar.css ├── homepage │ ├── article.jsx │ ├── styles │ │ ├── article.css │ │ └── works.css │ └── works.jsx └── projects │ ├── allProjects.jsx │ ├── project.jsx │ └── styles │ ├── allProjects.css │ └── project.css ├── data ├── articles.js ├── seo.js ├── styles.css ├── tracking.js └── user.js ├── index.css ├── index.js ├── pages ├── 404.jsx ├── about.jsx ├── articles.jsx ├── contact.jsx ├── homepage.jsx ├── projects.jsx ├── readArticle.jsx └── styles │ ├── 404.css │ ├── about.css │ ├── articles.css │ ├── contact.css │ ├── homepage.css │ ├── projects.css │ └── readArticle.css ├── reportWebVitals.js └── setupTests.js /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | mail@tharindu.dev. 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 Guidelines 2 | 3 | Thank you for your interest in contributing to our project! We welcome contributions from everyone. By participating in this project, you agree to abide by the following guidelines: 4 | 5 | Code of Conduct: 6 | Please review and adhere to our Code of Conduct, which establishes a welcoming and inclusive environment for all contributors. 7 | 8 | Types of Contributions: 9 | We welcome different types of contributions, including but not limited to: 10 | 11 | - Bug fixes 12 | - New features or enhancements 13 | - Documentation improvements 14 | - Test cases 15 | - Translations 16 | - Feedback and suggestions 17 | 18 | Getting Started: 19 | To get started with contributing, follow these steps: 20 | 21 | 1. Fork the repository and clone it to your local machine. 22 | 2. Install the necessary dependencies. 23 | 3. Create a new branch for your contribution. 24 | 4. Make your changes or additions. 25 | 5. Run tests and ensure that everything is working as expected. 26 | 6. Commit your changes with a clear and descriptive commit message. 27 | 7. Push your changes to your forked repository. 28 | 8. Submit a pull request (PR) to our main repository, clearly explaining the purpose and details of your contribution. 29 | 30 | Code Style and Standards: 31 | Please ensure that your code adheres to our coding style and standards. Review our style guide and follow the established conventions for formatting, naming, and documentation. 32 | 33 | Documentation: 34 | Update relevant documentation to reflect your changes. This includes README files, code comments, and any other supporting documentation. 35 | 36 | Issue Reporting: 37 | If you encounter any bugs or issues, please submit a detailed bug report, including steps to reproduce and any relevant information. You can also search for existing issues and contribute to their resolution. 38 | 39 | Community Feedback: 40 | We value feedback and suggestions from the community. Join our discussion forums, participate in conversations, and share your ideas for improving the project. 41 | 42 | Thank you for your contributions and for helping make this project better! 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Tharindu N. Madhusanka 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 | # Reactfolio V1.2 👩🏽‍🚀 2 | 3 | Reactfolio is a modern and customizable personal portfolio web template built using the popular React library. It provides an easy way for developers, designers, and creatives to showcase their work, skills, and achievements in a professiona and visually appealing way. With its responsive design and clean code, Reactfolio can be easily tailored to suit individual needs and preferences, making it an ideal choice for anyone looking to create a stunning online portfolio. 4 | 5 |
6 | Reactfolio 7 |
8 | 9 | Live demo here: reactfolio.tharindu.dev 10 | 11 | - [Features](#-features) 12 | - [Getting started](#-getting-started) 13 | - [Installation and Setup Instructions](#-installation-and-setup-instructions) 14 | - [Folder structure](#-folder-structure) 15 | - [Configurations](#-configurations) 16 | - [Google Analytics](#-google-analytics) 17 | - [Building the React App](#-building-the-react-app) 18 | - [FAQ](#-faq) 19 | - [Contribution](#-contribution) 20 | 21 | ## 📙 Features 22 | 23 | - 📖 Multi-Page Layout 24 | - Home 25 | - About 26 | - Projects 27 | - Articles 28 | - Contact 29 | - 📱 Fully Responsive 30 | - 🛠 Easy configurations 31 | 32 | ## 📚 Getting started 33 | 34 | Clone down this repository. You will need `NodeJS` and `git` installed globally on your machine. 35 | 36 | ## 🛠 Installation and Setup Instructions 37 | 38 | 1. Installation: `npm install` 39 | 40 | 2. Run the project: `npm start` 41 | 42 | Runs the app in the development mode.\ 43 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 44 | The page will reload if you make edits. 45 | 46 | ## 📁 Folder structure 47 | 48 | - `/public`: publicly accessible contents (ex: images, media). 49 | - `/src`: all the components used in this project. 50 | - `/src/components/`: each reusable components of each pages. 51 | - `/src/data`: configurations of the web app. 52 | - `/src/pages`: pages that include in the web app. 53 | 54 | ## ⚙️ Configurations 55 | 56 | Since this is a public and open source project, you can make any changes to it. If you are a ReactJS developer you can do it easily. But if you are a beginner, I have extracted the configurable data into a single directory, so you can edit the files in that directory to configure the entire web application. 57 | 58 | Directory: `/src/data/` 59 | 60 | - `user.js` 61 | 62 | From this you can change the content of each page of the web application. 63 | 64 | - `articles.js` 65 | 66 | From this you can add your articles to the web application. 67 | 68 | Instructions: 69 | 70 | - Add new article 71 | 72 | 1. Create a new function starts with `article_`. For example you can add new function named `article_3`. 73 | 74 | 2. Then add the data accordingly. 75 | 76 | - Add `` tag and it's closing tags in body. 77 | - In React there has no keyword `class`, so you should use `className` to define html classes. 78 | 79 | ```js 80 | function article_3() { 81 | return { 82 | date: "7 May 2023", 83 | title: "The Benefits of Cloud Computing", 84 | description: "Why businesses are turning to the cloud.", 85 | style: ` 86 | .random-image { 87 | align-self: center; 88 | outline: 2px solid red; 89 | } 90 | `, 91 | body: ( 92 | 93 |
94 |
95 | Content of article 1 96 |
97 | random 102 |
103 |
104 | ), 105 | }; 106 | } 107 | ``` 108 | 109 | 3. In the last lines you will see an array to which you need to add your new `articles` function. 110 | 111 | ```js 112 | const myArticles = [article_1, article_2, article_3]; 113 | ``` 114 | 115 | - `seo.js` 116 | 117 | The SEO.js file is a module that contains an array of objects, with each object representing metadata for a specific page of a React website. The purpose of this file is to centralize and manage the SEO (Search Engine Optimization) information for different pages. 118 | 119 | Each object in the SEO array has the following properties: 120 | 121 | `page`: Represents the page name or identifier. It helps in mapping the SEO data to the appropriate page. 122 | 123 | `title`: Specifies the title of the page. This title is typically displayed in the browser's title bar and is an important element for search engines. 124 | 125 | `description`: Provides a concise and informative description of the page content. This description is often displayed in search engine results and can greatly influence click-through rates. 126 | keywords: Contains an array of keywords relevant to the page's content. Keywords can help search engines understand the topics covered on the page and can impact its visibility in search results. 127 | By storing the SEO information in the SEO.js file, you can easily manage and update the metadata for different pages of your React website. This approach allows you to keep the SEO data separate from the components and reuse it across the application, ensuring consistent and optimized metadata for each page. 128 | 129 | Example: 130 | 131 | ```js 132 | const SEO = [ 133 | { 134 | page: "home", 135 | description: 136 | "I am a backend developer with expertise in Node.js. I have experience in building scalable, secure and reliable web applications using various frameworks and technologies.", 137 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 138 | }, 139 | ]; 140 | ``` 141 | 142 | - `styles.css` 143 | 144 | From this you can change the font colors and font families of the web application. 145 | 146 | ```css 147 | :root { 148 | /* ------- colors ------- */ 149 | --primary-color: #27272a; 150 | --secondary-color: #65656d; 151 | --tertiary-color: #acacb4; 152 | --quaternary-color: #e4e4e7; 153 | --link-color: #14b8a6; 154 | /* ---------------------- */ 155 | 156 | /* ------- fonts ------- */ 157 | --primary-font: "Heebo", sans-serif; 158 | --secondary-font: "Roboto", sans-serif; 159 | /* --------------------- */ 160 | } 161 | ``` 162 | 163 | ## 📈 Google Analytics 164 | 165 | Add your Google Analytics 4 MEASUREMENT ID to `/src/data/tracking.js`. 166 | 167 | How to find the Google Analytics 4 MEASUREMENT ID ? 168 | 169 | [https://support.google.com/analytics/answer/9539598?hl=en](https://support.google.com/analytics/answer/9539598?hl=en) 170 | 171 | ## 🚀 Building the React App 172 | 173 | To build the React app, you can use the `npm run build` command. This will create a production-ready build of your app in the `build/` directory. 174 | 175 | Here are the steps to follow: 176 | 177 | 1. Open a terminal window and navigate to the root directory of your React app. 178 | 2. Run the `npm run build` command to create a production build of your app. This will generate a static bundle of your app in the `build/` directory. 179 | 3. Copy the contents of the `build/` directory to your server's public directory. You can do this using an FTP client or by running a command like `scp` to transfer the files to your server. Make sure to replace `example.com` and `/var/www/html` with your server's domain name and public directory, respectively: 180 | 181 | ```bash 182 | scp -r build/* user@example.com:/var/www/html 183 | ``` 184 | 185 | 4. Your portfolio app should now be accessible from your server's domain name. You can verify this by opening a web browser and navigating to http://example.com (replace example.com with your server's domain name). 186 | 187 | That's it! Your React portfolio app should now be up and running on your server. Note that you may need to configure your server's web server (e.g., Apache or Nginx) to serve the index.html file in the build/ directory as the default page for your domain. 188 | 189 | ## 🤔 FAQ 190 | 191 | **Q1. Subpages can only be accessed through links on the homepage or navigation bar, but those pages are not accessible through direct links.** 192 | 193 | If you are using Apache as your web server, you can insert this into your .htaccess file: 194 | 195 | ```c 196 | 197 | RewriteEngine On 198 | RewriteBase / 199 | RewriteRule ^index\.html$ - [L] 200 | RewriteCond %{REQUEST_FILENAME} !-f 201 | RewriteCond %{REQUEST_FILENAME} !-d 202 | RewriteCond %{REQUEST_FILENAME} !-l 203 | RewriteRule . /index.html [L] 204 | 205 | ``` 206 | 207 | ## 🌱 Contribution 208 | 209 | If you have any suggestions on what to improve in Reactfolio and would like to share them, feel free to leave an issue or fork project to implement your own ideas 210 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactfolio", 3 | "version": "1.1.0", 4 | "description": "Personal portfolio template built using Reactjs", 5 | "author": { 6 | "name": "Tharindu N. Madhusanka", 7 | "email": "mail@tharindu.dev", 8 | "url": "https://tharindu.dev" 9 | }, 10 | "license": "MIT", 11 | "private": true, 12 | "dependencies": { 13 | "@fortawesome/fontawesome-svg-core": "^6.4.0", 14 | "@fortawesome/free-brands-svg-icons": "^6.4.0", 15 | "@fortawesome/free-regular-svg-icons": "^6.4.0", 16 | "@fortawesome/free-solid-svg-icons": "^6.4.0", 17 | "@fortawesome/react-fontawesome": "^0.2.0", 18 | "@testing-library/jest-dom": "^5.16.5", 19 | "@testing-library/react": "^13.4.0", 20 | "@testing-library/user-event": "^13.5.0", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0", 23 | "react-ga4": "^2.1.0", 24 | "react-helmet": "^6.1.0", 25 | "react-router-dom": "^6.11.1", 26 | "react-scripts": "5.0.1", 27 | "styled-components": "^5.3.10", 28 | "web-vitals": "^2.1.4" 29 | }, 30 | "scripts": { 31 | "start": "react-scripts start", 32 | "build": "react-scripts build", 33 | "test": "react-scripts test", 34 | "eject": "react-scripts eject" 35 | }, 36 | "eslintConfig": { 37 | "extends": [ 38 | "react-app", 39 | "react-app/jest" 40 | ] 41 | }, 42 | "browserslist": { 43 | "production": [ 44 | ">0.2%", 45 | "not dead", 46 | "not op_mini all" 47 | ], 48 | "development": [ 49 | "last 1 chrome version", 50 | "last 1 firefox version", 51 | "last 1 safari version" 52 | ] 53 | }, 54 | "prettier": { 55 | "useTabs": true, 56 | "tabWidth": 4 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /public/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/about.jpg -------------------------------------------------------------------------------- /public/back-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/back-button.png -------------------------------------------------------------------------------- /public/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/facebook.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/favicon.ico -------------------------------------------------------------------------------- /public/homepage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/homepage.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Reactfolio 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/logo.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/truethari/reactfolio/7429ed872c8b843b01ab84326272ddca758c487a/public/twitter.png -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { Routes, Route } from "react-router-dom"; 3 | import ReactGA from "react-ga4"; 4 | 5 | import Homepage from "./pages/homepage"; 6 | import About from "./pages/about"; 7 | import Projects from "./pages/projects"; 8 | import Articles from "./pages/articles"; 9 | import ReadArticle from "./pages/readArticle"; 10 | import Contact from "./pages/contact"; 11 | import Notfound from "./pages/404"; 12 | 13 | import { TRACKING_ID } from "./data/tracking"; 14 | import "./app.css"; 15 | 16 | function App() { 17 | useEffect(() => { 18 | if (TRACKING_ID !== "") { 19 | ReactGA.initialize(TRACKING_ID); 20 | } 21 | }, []); 22 | 23 | return ( 24 |
25 | 26 | } /> 27 | } /> 28 | } /> 29 | } /> 30 | } /> 31 | } /> 32 | } /> 33 | 34 |
35 | ); 36 | } 37 | 38 | export default App; 39 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import "/src/data/styles.css"; 2 | 3 | ::-webkit-scrollbar { 4 | width: 10px; 5 | } 6 | 7 | ::-webkit-scrollbar-track { 8 | background-color: #f1f1f1; 9 | } 10 | 11 | ::-webkit-scrollbar-thumb { 12 | background-color: #383636; 13 | border-radius: 5px; 14 | } 15 | 16 | ::-webkit-scrollbar-thumb:hover { 17 | background-color: rgb(0, 0, 0); 18 | } 19 | 20 | .page-content { 21 | background-color: white; 22 | position: relative; 23 | } 24 | 25 | .page-content:before, 26 | .page-content:after { 27 | content: ""; 28 | position: absolute; 29 | top: 0; 30 | bottom: 0; 31 | width: calc((100% - 1200px) / 2); 32 | background-color: #fafafa; 33 | border: 1px solid #f4f4f5; 34 | } 35 | 36 | .page-content:before { 37 | left: 0; 38 | z-index: 1; 39 | } 40 | 41 | .page-content:after { 42 | right: 0; 43 | z-index: 1; 44 | } 45 | 46 | .content-wrapper { 47 | max-width: 1000px; 48 | margin: 0 auto; 49 | } 50 | 51 | .title { 52 | color: var(--primary-color); 53 | font-family: var(--secondary-font); 54 | font-size: 45px; 55 | font-weight: 700; 56 | width: 70%; 57 | } 58 | 59 | .subtitle { 60 | padding-top: 10px; 61 | color: var(--secondary-color); 62 | font-size: 16px; 63 | line-height: 28px; 64 | width: 70%; 65 | margin-top: 25px; 66 | } 67 | 68 | .page-footer { 69 | position: block; 70 | } 71 | 72 | .page-footer::before { 73 | content: ""; 74 | position: absolute; 75 | left: 0; 76 | right: 0; 77 | border-top: 2px solid var(--tertiary-color); 78 | } 79 | 80 | @media (max-width: 1270px) { 81 | .page-content:before, 82 | .page-content:after { 83 | width: calc((100% - 90%) / 2); 84 | } 85 | 86 | .content-wrapper { 87 | max-width: 90%; 88 | padding-left: 10%; 89 | padding-right: 10%; 90 | } 91 | } 92 | 93 | @media (max-width: 1024px) { 94 | .page-content:before, 95 | .page-content:after { 96 | width: calc((100% - 950px) / 2); 97 | } 98 | 99 | .content-wrapper { 100 | max-width: 980px; 101 | } 102 | } 103 | 104 | @media (max-width: 800px) { 105 | .content-wrapper { 106 | max-width: 90%; 107 | margin: 0 auto; 108 | padding: 0 10px; 109 | } 110 | 111 | .page-content:before, 112 | .page-content:after { 113 | display: none; 114 | } 115 | 116 | .title { 117 | width: 100%; 118 | } 119 | 120 | .subtitle { 121 | width: 100%; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/components/about/socials.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | import { faEnvelope } from "@fortawesome/free-solid-svg-icons"; 4 | import { 5 | faTwitter, 6 | faGithub, 7 | faLinkedin, 8 | faInstagram, 9 | } from "@fortawesome/free-brands-svg-icons"; 10 | 11 | import INFO from "../../data/user"; 12 | 13 | import "./styles/socials.css"; 14 | 15 | const Socials = () => { 16 | return ( 17 |
18 |
19 | 20 |
21 | 25 |
26 |
Follow on Twitter
27 |
28 |
29 | 30 |
31 | 32 |
33 | 37 |
38 |
Follow on GitHub
39 |
40 |
41 | 42 |
43 | 48 |
49 | 53 |
54 |
Follow on LinkedIn
55 |
56 |
57 | 58 |
59 | 64 |
65 | 69 |
70 |
Follow on Instagram
71 |
72 |
73 | 74 |
75 |
76 | 81 |
82 | 83 |
84 | 85 |
{INFO.main.email}
86 |
87 |
88 |
89 |
90 | ); 91 | }; 92 | 93 | export default Socials; 94 | -------------------------------------------------------------------------------- /src/components/about/styles/socials.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | .social, 3 | .email-wrapper { 4 | display: flex; 5 | padding-bottom: 20px; 6 | } 7 | 8 | .social a, 9 | .email-wrapper a { 10 | text-decoration: none; 11 | } 12 | 13 | .social-icon { 14 | font-size: 15px; 15 | color: var(--secondary-color); 16 | } 17 | 18 | .social-text { 19 | color: var(--secondary-color); 20 | padding-left: 30px; 21 | margin-top: -20px; 22 | font-weight: 600; 23 | font-size: 14px; 24 | } 25 | 26 | .email::before { 27 | content: ""; 28 | display: block; 29 | padding-top: 20px; 30 | border-top: 3px solid #f4f4f5; 31 | margin-top: 20px; 32 | } 33 | 34 | .email-wrapper { 35 | padding-top: 20px; 36 | } 37 | -------------------------------------------------------------------------------- /src/components/articles/article.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 4 | import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; 5 | 6 | import "./style/article.css"; 7 | 8 | const Article = (props) => { 9 | const { date, title, description, link } = props; 10 | 11 | return ( 12 | 13 |
14 |
15 |
{date}
16 |
17 | 18 | 19 |
20 |
{title}
21 |
{description}
22 |
23 | Read Article{" "} 24 | 28 |
29 |
30 | 31 |
32 |
33 | ); 34 | }; 35 | 36 | export default Article; 37 | -------------------------------------------------------------------------------- /src/components/articles/style/article.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .article { 4 | display: flex; 5 | } 6 | 7 | .article a { 8 | text-decoration: none; 9 | } 10 | 11 | .article-left-side { 12 | min-width: 20%; 13 | max-width: 20%; 14 | } 15 | 16 | .article-right-side { 17 | flex-basis: 80%; 18 | min-width: 700px; 19 | mix-blend-mode: normal; 20 | border-radius: 20px; 21 | opacity: 0.8; 22 | padding: 30px; 23 | } 24 | 25 | .article-right-side:hover { 26 | background: #fafafa; 27 | opacity: 1; 28 | transition: background-color 0.3s ease-in-out; 29 | } 30 | 31 | .article-date { 32 | padding-top: 30px; 33 | font-size: 15px; 34 | color: var(--tertiary-color); 35 | } 36 | 37 | .article-title { 38 | color: #000000; 39 | font-size: 16px; 40 | font-weight: 600; 41 | } 42 | 43 | .article-description { 44 | padding-top: 10px; 45 | font-size: 12px; 46 | color: var(--secondary-color); 47 | font-weight: 480; 48 | line-height: 25px; 49 | } 50 | 51 | .article-link { 52 | padding-top: 10px; 53 | font-size: 14px; 54 | color: var(--link-color); 55 | font-weight: 700; 56 | } 57 | 58 | @media (max-width: 1024px) { 59 | .article-left-side { 60 | min-width: 10%; 61 | max-width: 10%; 62 | } 63 | 64 | .article-right-side { 65 | flex-basis: 90%; 66 | min-width: 85%; 67 | max-width: 85%; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/common/card.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | 4 | import "./styles/card.css"; 5 | 6 | const Card = (props) => { 7 | const { icon, title, body } = props; 8 | return ( 9 |
10 |
11 |
12 |
13 | 14 |
15 |
{title}
16 |
17 |
18 |
{body}
19 |
20 |
21 |
22 | ); 23 | }; 24 | 25 | export default Card; 26 | -------------------------------------------------------------------------------- /src/components/common/footer.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import "./styles/footer.css"; 5 | 6 | const Footer = () => { 7 | return ( 8 | 9 |
10 |
11 |
    12 |
  • 13 | Home 14 |
  • 15 |
  • 16 | About 17 |
  • 18 |
  • 19 | Projects 20 |
  • 21 |
  • 22 | Articles 23 |
  • 24 |
  • 25 | Contact 26 |
  • 27 |
28 |
29 | 30 |
31 |
32 | © 2023 Tharindu.dev. All Rights Reserved. 33 |
34 |
35 |
36 |
37 | ); 38 | }; 39 | 40 | export default Footer; 41 | -------------------------------------------------------------------------------- /src/components/common/logo.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import INFO from "../../data/user"; 5 | 6 | import "./styles/logo.css"; 7 | 8 | const Logo = (props) => { 9 | let { width, link } = props; 10 | 11 | if (link === undefined) { 12 | link = true; 13 | } 14 | 15 | const imageElement = ( 16 | logo 17 | ); 18 | 19 | return ( 20 | 21 | {link ? {imageElement} : imageElement} 22 | 23 | ); 24 | }; 25 | 26 | export default Logo; 27 | -------------------------------------------------------------------------------- /src/components/common/navBar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import "./styles/navBar.css"; 5 | 6 | const NavBar = (props) => { 7 | const { active } = props; 8 | 9 | return ( 10 | 11 |
12 | 63 |
64 |
65 | ); 66 | }; 67 | 68 | export default NavBar; 69 | -------------------------------------------------------------------------------- /src/components/common/styles/card.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .card { 4 | mix-blend-mode: normal; 5 | border-radius: 20px; 6 | outline: 2px solid #f4f4f5; 7 | } 8 | 9 | .card-container { 10 | padding: 30px; 11 | padding-bottom: 5px; 12 | } 13 | 14 | .card-header { 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .card-icon { 20 | font-size: 15px; 21 | color: var(--tertiary-color); 22 | } 23 | 24 | .card-title { 25 | color: var(--secondary-color); 26 | font-size: 14px; 27 | padding-left: 20px; 28 | font-weight: 650; 29 | } 30 | 31 | .card-body { 32 | padding-top: 40px; 33 | font-size: 15px; 34 | } 35 | -------------------------------------------------------------------------------- /src/components/common/styles/footer.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .footer { 4 | display: flex; 5 | padding-top: 25px; 6 | margin-top: 50px; 7 | padding-bottom: 25px; 8 | align-items: center; 9 | } 10 | 11 | .footer-links, 12 | .footer-credits { 13 | flex-basis: 50%; 14 | } 15 | 16 | .footer-links { 17 | margin-left: -40px; 18 | } 19 | 20 | .footer-nav-link-list { 21 | display: flex; 22 | list-style: none; 23 | justify-content: space-between; 24 | align-items: center; 25 | } 26 | 27 | .footer-nav-link-item { 28 | font-weight: bold; 29 | font-size: 80%; 30 | } 31 | 32 | .footer-nav-link-list a { 33 | text-decoration: none; 34 | color: var(--secondary-color); 35 | } 36 | 37 | .footer-nav-link-list a:hover { 38 | color: var(--link-color); 39 | } 40 | 41 | .footer-credits-text { 42 | justify-content: flex-end; 43 | color: var(--tertiary-color); 44 | font-size: 14px; 45 | text-align: right; 46 | } 47 | 48 | @media (max-width: 600px) { 49 | .footer { 50 | flex-direction: column; 51 | height: 85px; 52 | } 53 | 54 | .footer-links { 55 | display: flex; 56 | width: 100%; 57 | justify-content: center; 58 | } 59 | 60 | .footer-nav-link-list { 61 | width: 100%; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/common/styles/logo.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | border-radius: 50%; 3 | display: block; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/common/styles/navBar.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .nav-container { 4 | margin: 0; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .navbar { 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | position: fixed; 15 | top: 3vh; 16 | z-index: 999; 17 | } 18 | 19 | .nav-background { 20 | width: 380px; 21 | height: 40px; 22 | padding-left: 0%; 23 | padding-right: 0%; 24 | background: #fff; 25 | box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); 26 | border-radius: 40px; 27 | } 28 | 29 | .nav-list { 30 | display: flex; 31 | justify-content: space-between; 32 | list-style: none; 33 | align-items: center; 34 | margin-left: -16px; 35 | margin-right: 20px; 36 | margin-top: 11px; 37 | } 38 | 39 | .nav-item { 40 | font-weight: bold; 41 | font-size: 80%; 42 | } 43 | 44 | .nav-item.active a { 45 | color: var(--link-color) !important; 46 | } 47 | 48 | .nav-item a { 49 | text-decoration: none; 50 | color: var(--primary-color); 51 | } 52 | 53 | .nav-item a:hover { 54 | color: var(--link-color); 55 | transition: color 0.3s ease-in-out; 56 | } 57 | 58 | @media (max-width: 600px) { 59 | .navbar { 60 | margin-left: 25%; 61 | margin-right: 25%; 62 | width: 80%; 63 | font-size: 80%; 64 | } 65 | 66 | .nav-background { 67 | width: 80%; 68 | height: 40px; 69 | } 70 | 71 | .nav-item { 72 | font-weight: bold; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/components/homepage/article.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 5 | import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; 6 | 7 | import "./styles/article.css"; 8 | 9 | const Article = (props) => { 10 | const { title, description, date, link } = props; 11 | 12 | return ( 13 | 14 |
15 |
16 |
17 | |   {date} 18 |
19 |
{title}
20 |
21 | {description} 22 |
23 |
24 | 25 | Read article{" "} 26 | 30 | 31 |
32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default Article; 39 | -------------------------------------------------------------------------------- /src/components/homepage/styles/article.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .homepage-article { 4 | width: 90%; 5 | mix-blend-mode: normal; 6 | border-radius: 20px; 7 | opacity: 0.8; 8 | } 9 | 10 | .homepage-article:hover { 11 | background: #fafafa; 12 | opacity: 1; 13 | transition: background-color 0.3s ease-in-out; 14 | } 15 | 16 | .homepage-article-content { 17 | padding: 30px; 18 | } 19 | 20 | .homepage-article-date { 21 | opacity: 1; 22 | color: var(--tertiary-color); 23 | font-size: 12px; 24 | margin-bottom: 1em; 25 | } 26 | 27 | .homepage-article-title { 28 | opacity: 1; 29 | color: var(--primary-color); 30 | font-size: 16px; 31 | margin-bottom: 1em; 32 | font-weight: 700; 33 | } 34 | 35 | .homepage-article-description { 36 | opacity: 1; 37 | color: var(--secondary-color); 38 | font-size: 12px; 39 | line-height: 1.5; 40 | } 41 | 42 | .homepage-article-link { 43 | opacity: 1; 44 | padding-top: 20px; 45 | font-size: 15px; 46 | font-weight: 700; 47 | } 48 | 49 | .homepage-article-link a { 50 | color: var(--link-color); 51 | text-decoration: none; 52 | } 53 | 54 | @media (max-width: 600px) { 55 | .homepage-article { 56 | width: 100%; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/homepage/styles/works.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .works-body { 4 | margin-bottom: 10px; 5 | } 6 | 7 | .work { 8 | display: flex; 9 | padding-bottom: 40px; 10 | width: 400px; 11 | } 12 | 13 | .work-image { 14 | height: 30px; 15 | width: 30px; 16 | border-radius: 50%; 17 | outline: 6px solid white; 18 | box-shadow: 0px 7px 10px rgba(0, 0, 0, 0.25); 19 | } 20 | 21 | .work-title { 22 | font-size: 15px; 23 | font-weight: 700; 24 | padding-left: 20px; 25 | margin-top: -3px; 26 | color: var(--secondary-color); 27 | } 28 | 29 | .work-subtitle { 30 | position: absolute; 31 | font-size: 12px; 32 | color: var(--secondary-color); 33 | padding-top: 22px; 34 | padding-left: 50px; 35 | } 36 | 37 | .work-duration { 38 | position: absolute; 39 | width: 350px; 40 | font-size: 12px; 41 | padding-top: 20px; 42 | text-align: right; 43 | color: var(--tertiary-color); 44 | } 45 | -------------------------------------------------------------------------------- /src/components/homepage/works.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { faBriefcase } from "@fortawesome/free-solid-svg-icons"; 3 | 4 | import Card from "../common/card"; 5 | 6 | import "./styles/works.css"; 7 | 8 | const Works = () => { 9 | return ( 10 |
11 | 16 |
17 | facebook 22 |
Facebook
23 |
24 | Software Engineer 25 |
26 |
2019 - Present
27 |
28 | 29 |
30 | twitter 35 |
Twitter
36 |
37 | Software Engineer 38 |
39 |
2019 - Present
40 |
41 |
42 | } 43 | /> 44 | 45 | ); 46 | }; 47 | 48 | export default Works; 49 | -------------------------------------------------------------------------------- /src/components/projects/allProjects.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Project from "./project"; 4 | 5 | import INFO from "../../data/user"; 6 | 7 | import "./styles/allProjects.css"; 8 | 9 | const AllProjects = () => { 10 | return ( 11 |
12 | {INFO.projects.map((project, index) => ( 13 |
14 | 21 |
22 | ))} 23 |
24 | ); 25 | }; 26 | 27 | export default AllProjects; 28 | -------------------------------------------------------------------------------- /src/components/projects/project.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 4 | import { faLink } from "@fortawesome/free-solid-svg-icons"; 5 | 6 | import "./styles/project.css"; 7 | 8 | const Project = (props) => { 9 | const { logo, title, description, linkText, link } = props; 10 | 11 | return ( 12 | 13 |
14 | 15 |
16 |
17 | logo 18 |
19 |
{title}
20 |
{description}
21 |
22 |
23 | 24 |
25 | 26 |
{linkText}
27 |
28 |
29 | 30 |
31 |
32 | ); 33 | }; 34 | 35 | export default Project; 36 | -------------------------------------------------------------------------------- /src/components/projects/styles/allProjects.css: -------------------------------------------------------------------------------- 1 | .all-projects-container { 2 | display: flex; 3 | flex-wrap: wrap; 4 | align-items: stretch; 5 | padding-top: 30px; 6 | padding-bottom: 30px; 7 | margin-left: -35px; 8 | margin-right: -35px; 9 | } 10 | 11 | .all-projects-project { 12 | width: calc(100% / 3); 13 | box-sizing: border-box; 14 | padding: 10px; 15 | overflow: hidden; 16 | } 17 | 18 | @media (max-width: 600px) { 19 | .all-projects-project { 20 | width: calc(100% / 2); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/projects/styles/project.css: -------------------------------------------------------------------------------- 1 | @import "../../../data/styles.css"; 2 | 3 | .project { 4 | mix-blend-mode: normal; 5 | border-radius: 20px; 6 | opacity: 0.8; 7 | height: 100%; 8 | } 9 | 10 | .project a { 11 | text-decoration: none; 12 | } 13 | 14 | .project:hover { 15 | background: #fafafa; 16 | opacity: 1; 17 | transition: background-color 0.3s ease-in-out; 18 | } 19 | 20 | .project:hover .project-link { 21 | color: var(--link-color); 22 | transition: color 0.3s ease-in-out; 23 | } 24 | 25 | .project-container { 26 | padding: 30px; 27 | } 28 | 29 | .project-logo { 30 | width: 30px; 31 | } 32 | 33 | .project-logo img { 34 | width: 100%; 35 | } 36 | 37 | .project-title { 38 | font-family: var(--secondary-font); 39 | color: var(--primary-color); 40 | font-size: 16px; 41 | padding-top: 15px; 42 | margin-bottom: 1em; 43 | font-weight: 700; 44 | } 45 | 46 | .project-description { 47 | color: var(--secondary-color); 48 | } 49 | 50 | .project-link { 51 | display: flex; 52 | align-items: center; 53 | color: var(--secondary-color); 54 | font-size: 12px; 55 | padding-top: 20px; 56 | } 57 | 58 | .project-link-icon { 59 | padding-left: 5px; 60 | font-size: 13px; 61 | } 62 | 63 | .project-link-text { 64 | padding-left: 20px; 65 | font-weight: 700; 66 | } 67 | 68 | @media (max-width: 600px) { 69 | .project { 70 | height: 300px; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/data/articles.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function article_1() { 4 | return { 5 | date: "7 May 2023", 6 | title: "The Benefits of Cloud Computing", 7 | description: 8 | "Cloud computing offers a range of benefits, including cost savings and increased flexibility. Find out why more businesses are turning to the cloud.", 9 | keywords: [ 10 | "The Benefits of Cloud Computing", 11 | "Tharindu", 12 | "Tharindu N", 13 | "Tharindu Nayanajith", 14 | ], 15 | style: ` 16 | .article-content { 17 | display: flex; 18 | flex-direction: column; 19 | align-items: center; 20 | } 21 | 22 | .randImage { 23 | align-self: center; 24 | outline: 2px solid red; 25 | } 26 | `, 27 | body: ( 28 | 29 |
30 |
Content of article 1
31 | random 36 |
37 |
38 | ), 39 | }; 40 | } 41 | 42 | function article_2() { 43 | return { 44 | date: "7 May 2023", 45 | title: "Artificial Intelligence in Healthcare", 46 | description: 47 | "AI is transforming the healthcare industry, from improving patient outcomes to streamlining operations. Discover the latest applications of this game-changing technology.", 48 | style: ``, 49 | keywords: [ 50 | "Artificial Intelligence in Healthcare", 51 | "Tharindu", 52 | "Tharindu N", 53 | "Tharindu Nayanajith", 54 | ], 55 | body: ( 56 | 57 |

Content of article 2

58 |
59 | ), 60 | }; 61 | } 62 | 63 | const myArticles = [article_1, article_2]; 64 | 65 | export default myArticles; 66 | -------------------------------------------------------------------------------- /src/data/seo.js: -------------------------------------------------------------------------------- 1 | const SEO = [ 2 | { 3 | page: "home", 4 | description: 5 | "I am a backend developer with expertise in Node.js. I have experience in building scalable, secure and reliable web applications using various frameworks and technologies.", 6 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 7 | }, 8 | 9 | { 10 | page: "about", 11 | description: 12 | "I am a backend developer with expertise in Node.js. I have experience in building scalable, secure and reliable web applications using various frameworks and technologies.", 13 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 14 | }, 15 | 16 | { 17 | page: "articles", 18 | description: 19 | "Chronological collection of my long-form thoughts on programming, leadership, product design, and more.", 20 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 21 | }, 22 | 23 | { 24 | page: "projects", 25 | description: 26 | "I've worked on a variety of projects over the years and I'm proud of the progress I've made. Many of these projects are open-source and available for others to explore and contribute to.", 27 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 28 | }, 29 | 30 | { 31 | page: "contact", 32 | description: 33 | "If you're interested in collaborating on a project, feel free to reach out to me. I'm always open to new ideas and opportunities.", 34 | keywords: ["Tharindu", "Tharindu N", "Tharindu Nayanajith"], 35 | }, 36 | ]; 37 | 38 | export default SEO; 39 | -------------------------------------------------------------------------------- /src/data/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* ------- colors ------- */ 3 | --primary-color: #27272a; 4 | --secondary-color: #65656d; 5 | --tertiary-color: #acacb4; 6 | --quaternary-color: #e4e4e7; 7 | --link-color: #14b8a6; 8 | /* ---------------------- */ 9 | 10 | /* ------- fonts ------- */ 11 | --primary-font: "Heebo", sans-serif; 12 | --secondary-font: "Roboto", sans-serif; 13 | /* --------------------- */ 14 | } 15 | -------------------------------------------------------------------------------- /src/data/tracking.js: -------------------------------------------------------------------------------- 1 | // Google Analytics Tracking ID 2 | // https://github.com/truethari/reactfolio#-google-analytics 3 | export const TRACKING_ID = ""; 4 | -------------------------------------------------------------------------------- /src/data/user.js: -------------------------------------------------------------------------------- 1 | const INFO = { 2 | main: { 3 | title: "Reactfolio by truethari", 4 | name: "Tharindu N.", 5 | email: "mail@tharindu.dev", 6 | logo: "../logo.png", 7 | }, 8 | 9 | socials: { 10 | twitter: "https://twitter.com/", 11 | github: "https://github.com/", 12 | linkedin: "https://linkedin.com/", 13 | instagram: "https://instagram.com/", 14 | stackoverflow: "https://stackoverflow.com/", 15 | facebook: "https://facebook.com/", 16 | }, 17 | 18 | homepage: { 19 | title: "Full-stack web and mobile app developer, and amateur astronaut.", 20 | description: 21 | "I am a backend developer with expertise in Node.js. I have experience in building scalable, secure and reliable web applications using various frameworks and technologies. I enjoy solving complex problems and learning new skills. I am passionate about creating high-quality code that follows best practices and industry standards. I am always looking for new challenges and opportunities to grow as a developer.", 22 | }, 23 | 24 | about: { 25 | title: "I’m Tharindu N. I live in Sri Lanka, where I design the future.", 26 | description: 27 | "I've worked on a variety of projects over the years and I'm proud of the progress I've made. Many of these projects are open-source and available for others to explore and contribute to. If you're interested in any of the projects I've worked on, please feel free to check out the code and suggest any improvements or enhancements you might have in mind. Collaborating with others is a great way to learn and grow, and I'm always open to new ideas and feedback.", 28 | }, 29 | 30 | articles: { 31 | title: "I'm passionate about pushing the boundaries of what's possible and inspiring the next generation of innovators.", 32 | description: 33 | "Chronological collection of my long-form thoughts on programming, leadership, product design, and more.", 34 | }, 35 | 36 | projects: [ 37 | { 38 | title: "Project 1", 39 | description: 40 | "Lorem ipsum dolor sit amet. Et incidunt voluptatem ex tempore repellendus qui dicta nemo sit deleniti minima.", 41 | logo: "https://cdn.jsdelivr.net/npm/programming-languages-logos/src/javascript/javascript.png", 42 | linkText: "View Project", 43 | link: "https://github.com", 44 | }, 45 | 46 | { 47 | title: "Project 2", 48 | description: 49 | "Lorem ipsum dolor sit amet. Et incidunt voluptatem ex tempore repellendus qui dicta nemo sit deleniti minima.", 50 | logo: "https://cdn.jsdelivr.net/npm/programming-languages-logos/src/python/python.png", 51 | linkText: "View Project", 52 | link: "https://github.com", 53 | }, 54 | 55 | { 56 | title: "Project 3", 57 | description: 58 | "Lorem ipsum dolor sit amet. Et incidunt voluptatem ex tempore repellendus qui dicta nemo sit deleniti minima.", 59 | logo: "https://cdn.jsdelivr.net/npm/programming-languages-logos/src/html/html.png", 60 | linkText: "View Project", 61 | link: "https://github.com", 62 | }, 63 | 64 | { 65 | title: "Project 4", 66 | description: 67 | "Lorem ipsum dolor sit amet. Et incidunt voluptatem ex tempore repellendus qui dicta nemo sit deleniti minima.", 68 | logo: "https://cdn.jsdelivr.net/npm/programming-languages-logos/src/javascript/javascript.png", 69 | linkText: "View Project", 70 | link: "https://github.com", 71 | }, 72 | 73 | { 74 | title: "Project 5", 75 | description: 76 | "Lorem ipsum dolor sit amet. Et incidunt voluptatem ex tempore repellendus qui dicta nemo sit deleniti minima.", 77 | logo: "https://cdn.jsdelivr.net/npm/programming-languages-logos/src/javascript/javascript.png", 78 | linkText: "View Project", 79 | link: "https://github.com", 80 | }, 81 | ], 82 | }; 83 | 84 | export default INFO; 85 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Heebo:wght@300&family=Roboto:wght@500&display=swap"); 2 | @import "./data/styles.css"; 3 | 4 | body { 5 | margin: 0; 6 | font-family: var(--primary-font); 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | import { BrowserRouter } from "react-router-dom"; 7 | 8 | const root = ReactDOM.createRoot(document.getElementById("root")); 9 | root.render( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | 17 | // If you want to start measuring performance in your app, pass a function 18 | // to log results (for example: reportWebVitals(console.log)) 19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 20 | reportWebVitals(); 21 | -------------------------------------------------------------------------------- /src/pages/404.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 3 | 4 | import { faFaceSadTear } from "@fortawesome/free-regular-svg-icons"; 5 | 6 | import NavBar from "../components/common/navBar"; 7 | import Logo from "../components/common/logo"; 8 | 9 | import INFO from "../data/user"; 10 | 11 | import "./styles/404.css"; 12 | 13 | const Notfound = () => { 14 | useEffect(() => { 15 | document.title = `404 | ${INFO.main.title}`; 16 | }, []); 17 | 18 | return ( 19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 |
31 | Oops! 32 |
33 |
34 | We can't seem to find the page you're looking 35 | for. 36 |
37 | The requested URL "{window.location.href}" was 38 | not found on this server. 39 |
40 | 41 | Go back to the home page 42 | 43 |
44 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | 51 | export default Notfound; 52 | -------------------------------------------------------------------------------- /src/pages/about.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Helmet } from "react-helmet"; 3 | 4 | import NavBar from "../components/common/navBar"; 5 | import Footer from "../components/common/footer"; 6 | import Logo from "../components/common/logo"; 7 | import Socials from "../components/about/socials"; 8 | 9 | import INFO from "../data/user"; 10 | import SEO from "../data/seo"; 11 | 12 | import "./styles/about.css"; 13 | 14 | const About = () => { 15 | useEffect(() => { 16 | window.scrollTo(0, 0); 17 | }, []); 18 | 19 | const currentSEO = SEO.find((item) => item.page === "about"); 20 | 21 | return ( 22 | 23 | 24 | {`About | ${INFO.main.title}`} 25 | 26 | 30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 |
44 |
45 | {INFO.about.title} 46 |
47 | 48 |
49 | {INFO.about.description} 50 |
51 |
52 | 53 |
54 |
55 |
56 | about 61 |
62 |
63 | 64 |
65 | 66 |
67 |
68 |
69 |
70 | 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | ); 80 | }; 81 | 82 | export default About; 83 | -------------------------------------------------------------------------------- /src/pages/articles.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Helmet } from "react-helmet"; 3 | 4 | import NavBar from "../components/common/navBar"; 5 | import Footer from "../components/common/footer"; 6 | import Logo from "../components/common/logo"; 7 | import Article from "../components/articles/article"; 8 | 9 | import INFO from "../data/user"; 10 | import SEO from "../data/seo"; 11 | import myArticles from "../data/articles"; 12 | 13 | import "./styles/articles.css"; 14 | 15 | const Articles = () => { 16 | useEffect(() => { 17 | window.scrollTo(0, 0); 18 | }, []); 19 | 20 | const currentSEO = SEO.find((item) => item.page === "articles"); 21 | 22 | return ( 23 | 24 | 25 | {`Articles | ${INFO.main.title}`} 26 | 27 | 31 | 32 | 33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 | {INFO.articles.title} 45 |
46 | 47 |
48 | {INFO.articles.description} 49 |
50 | 51 |
52 |
53 | {myArticles.map((article, index) => ( 54 |
58 |
65 |
66 | ))} 67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | ); 77 | }; 78 | 79 | export default Articles; 80 | -------------------------------------------------------------------------------- /src/pages/contact.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Helmet } from "react-helmet"; 3 | 4 | import NavBar from "../components/common/navBar"; 5 | import Footer from "../components/common/footer"; 6 | import Logo from "../components/common/logo"; 7 | import Socials from "../components/about/socials"; 8 | 9 | import INFO from "../data/user"; 10 | import SEO from "../data/seo"; 11 | 12 | import "./styles/contact.css"; 13 | 14 | const Contact = () => { 15 | useEffect(() => { 16 | window.scrollTo(0, 0); 17 | }, []); 18 | 19 | const currentSEO = SEO.find((item) => item.page === "contact"); 20 | 21 | return ( 22 | 23 | 24 | {`Contact | ${INFO.main.title}`} 25 | 26 | 30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 | Let's Get in Touch: Ways to Connect with Me 44 |
45 | 46 |
47 | Thank you for your interest in getting in touch with 48 | me. I welcome your feedback, questions, and 49 | suggestions. If you have a specific question or 50 | comment, please feel free to email me directly at 51 |  {" "} 52 | 53 | {INFO.main.email} 54 | 55 | . I make an effort to respond to all messages within 56 | 24 hours, although it may take me longer during busy 57 | periods. Alternatively, you can use the contact form 58 | on my website to get in touch. Simply fill out the 59 | required fields and I'll get back to you as soon as 60 | possible. Finally, if you prefer to connect on 61 | social media, you can find me on{" "} 62 | 67 | {INFO.socials.instagram} 68 | 69 | . I post regular updates and engage with my 70 | followers there, so don't hesitate to reach out. 71 | Thanks again for your interest, and I look forward 72 | to hearing from you! 73 |
74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 |
87 |
88 | ); 89 | }; 90 | 91 | export default Contact; 92 | -------------------------------------------------------------------------------- /src/pages/homepage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Helmet } from "react-helmet"; 3 | 4 | import { faMailBulk } from "@fortawesome/free-solid-svg-icons"; 5 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 6 | import { 7 | faTwitter, 8 | faGithub, 9 | faStackOverflow, 10 | faInstagram, 11 | } from "@fortawesome/free-brands-svg-icons"; 12 | 13 | import Logo from "../components/common/logo"; 14 | import Footer from "../components/common/footer"; 15 | import NavBar from "../components/common/navBar"; 16 | import Article from "../components/homepage/article"; 17 | import Works from "../components/homepage/works"; 18 | import AllProjects from "../components/projects/allProjects"; 19 | 20 | import INFO from "../data/user"; 21 | import SEO from "../data/seo"; 22 | import myArticles from "../data/articles"; 23 | 24 | import "./styles/homepage.css"; 25 | 26 | const Homepage = () => { 27 | const [stayLogo, setStayLogo] = useState(false); 28 | const [logoSize, setLogoSize] = useState(80); 29 | const [oldLogoSize, setOldLogoSize] = useState(80); 30 | 31 | useEffect(() => { 32 | window.scrollTo(0, 0); 33 | }, []); 34 | 35 | useEffect(() => { 36 | const handleScroll = () => { 37 | let scroll = Math.round(window.pageYOffset, 2); 38 | 39 | let newLogoSize = 80 - (scroll * 4) / 10; 40 | 41 | if (newLogoSize < oldLogoSize) { 42 | if (newLogoSize > 40) { 43 | setLogoSize(newLogoSize); 44 | setOldLogoSize(newLogoSize); 45 | setStayLogo(false); 46 | } else { 47 | setStayLogo(true); 48 | } 49 | } else { 50 | setLogoSize(newLogoSize); 51 | setStayLogo(false); 52 | } 53 | }; 54 | 55 | window.addEventListener("scroll", handleScroll); 56 | return () => window.removeEventListener("scroll", handleScroll); 57 | }, [logoSize, oldLogoSize]); 58 | 59 | const currentSEO = SEO.find((item) => item.page === "home"); 60 | 61 | const logoStyle = { 62 | display: "flex", 63 | position: stayLogo ? "fixed" : "relative", 64 | top: stayLogo ? "3vh" : "auto", 65 | zIndex: 999, 66 | border: stayLogo ? "1px solid white" : "none", 67 | borderRadius: stayLogo ? "50%" : "none", 68 | boxShadow: stayLogo ? "0px 4px 10px rgba(0, 0, 0, 0.25)" : "none", 69 | }; 70 | 71 | return ( 72 | 73 | 74 | {INFO.main.title} 75 | 76 | 80 | 81 | 82 |
83 | 84 |
85 |
86 |
87 | 88 |
89 |
90 | 91 |
92 |
93 |
94 |
95 | {INFO.homepage.title} 96 |
97 | 98 |
99 | {INFO.homepage.description} 100 |
101 |
102 | 103 |
104 |
105 |
106 | about 111 |
112 |
113 |
114 |
115 | 116 | 168 | 169 |
170 | 171 |
172 | 173 |
174 |
175 | {myArticles.map((article, index) => ( 176 |
180 |
187 |
188 | ))} 189 |
190 | 191 |
192 | 193 |
194 |
195 | 196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 | ); 204 | }; 205 | 206 | export default Homepage; 207 | -------------------------------------------------------------------------------- /src/pages/projects.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Helmet } from "react-helmet"; 3 | 4 | import NavBar from "../components/common/navBar"; 5 | import Footer from "../components/common/footer"; 6 | import Logo from "../components/common/logo"; 7 | import AllProjects from "../components/projects/allProjects"; 8 | 9 | import INFO from "../data/user"; 10 | import SEO from "../data/seo"; 11 | 12 | import "./styles/projects.css"; 13 | 14 | const Projects = () => { 15 | useEffect(() => { 16 | window.scrollTo(0, 0); 17 | }, []); 18 | 19 | const currentSEO = SEO.find((item) => item.page === "projects"); 20 | 21 | return ( 22 | 23 | 24 | {`Projects | ${INFO.main.title}`} 25 | 26 | 30 | 31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | Things I’ve made trying to put my dent in the 43 | universe. 44 |
45 | 46 |
47 | I've worked on a variety of projects over the years 48 | and I'm proud of the progress I've made. Many of 49 | these projects are open-source and available for 50 | others to explore and contribute to. If you're 51 | interested in any of the projects I've worked on, 52 | please feel free to check out the code and suggest 53 | any improvements or enhancements you might have in 54 | mind. Collaborating with others is a great way to 55 | learn and grow, and I'm always open to new ideas and 56 | feedback. 57 |
58 | 59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | ); 70 | }; 71 | 72 | export default Projects; 73 | -------------------------------------------------------------------------------- /src/pages/readArticle.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { useNavigate, useParams } from "react-router-dom"; 3 | import { Helmet } from "react-helmet"; 4 | import styled from "styled-components"; 5 | 6 | import NavBar from "../components/common/navBar"; 7 | import Footer from "../components/common/footer"; 8 | import Logo from "../components/common/logo"; 9 | 10 | import INFO from "../data/user"; 11 | import myArticles from "../data/articles"; 12 | 13 | import "./styles/readArticle.css"; 14 | 15 | let ArticleStyle = styled.div``; 16 | 17 | const ReadArticle = () => { 18 | const navigate = useNavigate(); 19 | let { slug } = useParams(); 20 | 21 | const article = myArticles[slug - 1]; 22 | 23 | useEffect(() => { 24 | window.scrollTo(0, 0); 25 | }, [article]); 26 | 27 | ArticleStyle = styled.div` 28 | ${article().style} 29 | `; 30 | 31 | return ( 32 | 33 | 34 | {`${article().title} | ${INFO.main.title}`} 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 |
43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
51 | back navigate(-1)} 56 | /> 57 |
58 | 59 |
60 |
61 |
62 | {article().date} 63 |
64 |
65 | 66 |
67 | {article().title} 68 |
69 | 70 |
71 | {article().body} 72 |
73 |
74 |
75 |
76 |
78 |
79 |
80 |
81 | ); 82 | }; 83 | 84 | export default ReadArticle; 85 | -------------------------------------------------------------------------------- /src/pages/styles/404.css: -------------------------------------------------------------------------------- 1 | .not-found { 2 | overflow: hidden; 3 | height: 100vh; 4 | } 5 | 6 | .notfound-logo-container { 7 | display: flex; 8 | justify-content: left; 9 | padding-top: 25px; 10 | } 11 | 12 | .notfound-logo { 13 | display: flex; 14 | position: fixed; 15 | border: 1px solid white; 16 | border-radius: 50%; 17 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 18 | top: 4vh; 19 | z-index: 999; 20 | } 21 | 22 | .notfound-container { 23 | display: flex; 24 | flex-direction: column; 25 | height: 100%; 26 | margin: 0; 27 | padding-top: 120px; 28 | } 29 | 30 | .notfound-message { 31 | position: fixed; 32 | display: flex; 33 | flex-direction: column; 34 | justify-content: center; 35 | align-items: center; 36 | top: 50%; 37 | left: 50%; 38 | transform: translate(-50%, -50%); 39 | } 40 | 41 | .notfound-title { 42 | font-family: var(--secondary-font); 43 | color: var(--primary-color); 44 | font-size: 80px; 45 | padding-top: 15px; 46 | font-weight: 700; 47 | } 48 | 49 | .not-found-message { 50 | color: var(--secondary-color); 51 | font-size: 2rem; 52 | margin: 2rem 0; 53 | text-align: center; 54 | } 55 | 56 | .not-found-link { 57 | font-size: 1.5rem; 58 | text-decoration: none; 59 | color: #fff; 60 | background-color: var(--link-color); 61 | padding: 1rem 2rem; 62 | border-radius: 50px; 63 | transition: all 0.3s ease-in-out; 64 | } 65 | 66 | .not-found-link:hover { 67 | background-color: #fff; 68 | color: var(--link-color); 69 | box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3); 70 | } 71 | 72 | @media (max-width: 500px) { 73 | .notfound-message { 74 | position: absolute; 75 | top: auto; 76 | left: auto; 77 | width: 100%; 78 | transform: none; 79 | } 80 | 81 | .notfound-title { 82 | font-size: 50px; 83 | } 84 | 85 | .not-found-message { 86 | width: 100%; 87 | font-size: 15px; 88 | margin-top: 10px; 89 | margin-left: -5px; 90 | margin-right: 32px; 91 | } 92 | 93 | .not-found-link { 94 | font-size: 15px; 95 | padding: 15px 15px; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/pages/styles/about.css: -------------------------------------------------------------------------------- 1 | .about-logo-container { 2 | display: flex; 3 | justify-content: left; 4 | padding-top: 25px; 5 | } 6 | 7 | .about-logo { 8 | display: flex; 9 | position: fixed; 10 | border: 1px solid white; 11 | border-radius: 50%; 12 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 13 | top: 4vh; 14 | } 15 | 16 | .about-container { 17 | display: flex; 18 | flex-direction: column; 19 | height: 100%; 20 | margin: 0; 21 | padding-top: 120px; 22 | } 23 | 24 | .about-main { 25 | display: flex; 26 | } 27 | 28 | .about-right-side { 29 | flex-basis: 900px; 30 | } 31 | 32 | .about-title { 33 | width: 100% !important; 34 | } 35 | 36 | .about-subtitle { 37 | width: 80% !important; 38 | } 39 | 40 | .about-left-side { 41 | display: flex; 42 | flex-direction: column; 43 | flex-basis: 300px; 44 | } 45 | 46 | .about-image-container { 47 | width: 370px; 48 | } 49 | 50 | .about-image-wrapper { 51 | display: inline-block; 52 | overflow: hidden; 53 | width: 100%; 54 | border-radius: 10%; 55 | transform: rotate(3deg); 56 | } 57 | 58 | .about-image-wrapper img { 59 | display: block; 60 | width: 100%; 61 | } 62 | 63 | .about-socials { 64 | padding-top: 50px; 65 | padding-bottom: 50px; 66 | } 67 | 68 | .about-socials-mobile { 69 | display: none; 70 | } 71 | 72 | @media (max-width: 1024px) { 73 | .about-logo { 74 | z-index: 100; 75 | } 76 | 77 | .about-main { 78 | flex-direction: column; 79 | } 80 | 81 | .about-subtitle { 82 | padding-top: 20px !important; 83 | } 84 | 85 | .about-left-side { 86 | flex-basis: 100%; 87 | order: 1; 88 | } 89 | 90 | .about-right-side { 91 | flex-basis: 100%; 92 | order: 2; 93 | } 94 | 95 | .about-image-container { 96 | padding-bottom: 40px; 97 | } 98 | 99 | .about-socials { 100 | display: none; 101 | } 102 | 103 | .about-socials-mobile { 104 | padding-top: 30px; 105 | display: flex; 106 | } 107 | } 108 | 109 | @media (max-width: 600px) { 110 | .about-container { 111 | padding-top: 60px; 112 | } 113 | 114 | .about-image-container { 115 | width: 80%; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/pages/styles/articles.css: -------------------------------------------------------------------------------- 1 | .articles-logo-container { 2 | display: flex; 3 | justify-content: left; 4 | padding-top: 25px; 5 | } 6 | 7 | .articles-logo { 8 | display: flex; 9 | position: fixed; 10 | border: 1px solid white; 11 | border-radius: 50%; 12 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 13 | top: 4vh; 14 | } 15 | 16 | .articles-main-container { 17 | display: flex; 18 | flex-direction: column; 19 | height: 100%; 20 | margin: 0; 21 | padding-top: 120px; 22 | } 23 | 24 | .articles-container { 25 | display: flex; 26 | flex-direction: column; 27 | } 28 | 29 | .articles-wrapper { 30 | padding-top: 50px; 31 | padding-bottom: 50px; 32 | display: flex; 33 | flex-direction: column; 34 | } 35 | 36 | .articles-article { 37 | padding-top: 0px; 38 | padding-left: 35px; 39 | padding-bottom: 20px; 40 | border-left: 2px solid #f4f4f5; 41 | width: 100%; 42 | } 43 | 44 | @media (max-width: 1024px) { 45 | .articles-title { 46 | width: 100% !important; 47 | } 48 | 49 | .articles-subtitle { 50 | width: 100% !important; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/pages/styles/contact.css: -------------------------------------------------------------------------------- 1 | .contact-logo-container { 2 | display: flex; 3 | justify-content: left; 4 | padding-top: 25px; 5 | } 6 | 7 | .contact-logo { 8 | display: flex; 9 | position: fixed; 10 | border: 1px solid white; 11 | border-radius: 50%; 12 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 13 | top: 4vh; 14 | } 15 | 16 | .contact-subtitle { 17 | width: 100% !important; 18 | } 19 | 20 | .contact-container { 21 | display: flex; 22 | flex-direction: column; 23 | justify-content: space-around; 24 | margin-top: 120px; 25 | } 26 | 27 | 28 | .socials-container { 29 | display: flex; 30 | flex-direction: column; 31 | margin-top: 80px; 32 | } 33 | 34 | .contact-socials { 35 | display: flex; 36 | justify-content: flex-start; 37 | align-items: center; 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/styles/homepage.css: -------------------------------------------------------------------------------- 1 | @import "../../data/styles.css"; 2 | 3 | .homepage-logo-container { 4 | display: flex; 5 | justify-content: left; 6 | align-items: center; 7 | padding-top: 130px; 8 | } 9 | 10 | .homepage-container { 11 | padding-top: 25px; 12 | } 13 | 14 | .homepage-first-area { 15 | display: flex; 16 | justify-content: space-between; 17 | align-items: center; 18 | } 19 | 20 | .homepage-first-area-left-side { 21 | display: flex; 22 | flex-direction: column; 23 | align-items: flex-start; 24 | } 25 | 26 | .homepage-title { 27 | width: 85% !important; 28 | } 29 | 30 | .homepage-subtitle { 31 | width: 85% !important; 32 | } 33 | 34 | .homepage-first-area-right-side { 35 | display: flex; 36 | align-items: center; 37 | } 38 | 39 | .homepage-image-container { 40 | width: 370px; 41 | height: 370px; 42 | } 43 | 44 | .homepage-image-wrapper { 45 | overflow: hidden; 46 | border-radius: 10%; 47 | transform: rotate(3deg); 48 | } 49 | 50 | .homepage-image-wrapper img { 51 | width: 100%; 52 | } 53 | 54 | .homepage-socials { 55 | display: flex; 56 | justify-content: left; 57 | align-items: center; 58 | padding-top: 30px; 59 | } 60 | 61 | .homepage-social-icon { 62 | padding-right: 20px; 63 | font-size: 25px; 64 | transition: color 0.2s ease-in-out; 65 | color: var(--secondary-color); 66 | } 67 | 68 | .homepage-social-icon:hover { 69 | color: var(--primary-color); 70 | } 71 | 72 | .homepage-after-title { 73 | display: flex; 74 | } 75 | 76 | .homepage-projects { 77 | flex-basis: 300px; 78 | padding-top: 40px; 79 | } 80 | 81 | .homepage-articles { 82 | flex-basis: 900px; 83 | padding-top: 30px; 84 | margin-left: -30px; 85 | } 86 | 87 | .homepage-article { 88 | padding-bottom: 5px; 89 | } 90 | 91 | .homepage-works { 92 | flex-basis: 600px; 93 | padding-top: 30px; 94 | } 95 | 96 | @media (max-width: 1024px) { 97 | .homepage-first-area { 98 | flex-direction: column; 99 | } 100 | 101 | .homepage-first-area-left-side { 102 | display: flex; 103 | flex-direction: column; /* Changed: set direction to column */ 104 | align-items: flex-start; 105 | width: 100%; 106 | order: 2; 107 | } 108 | 109 | .homepage-title { 110 | width: 100% !important; 111 | margin-top: 20px; 112 | order: 2; 113 | } 114 | 115 | .homepage-subtitle { 116 | width: 100% !important; 117 | margin-top: 10px; 118 | order: 3; 119 | } 120 | 121 | .homepage-first-area-right-side { 122 | width: 100%; 123 | order: 1; 124 | } 125 | 126 | .homepage-image-container { 127 | padding-top: 50px; 128 | padding-bottom: 50px; 129 | margin-left: 10px; 130 | order: 1; 131 | } 132 | 133 | .homepage-after-title { 134 | flex-direction: column; 135 | } 136 | 137 | .homepage-projects { 138 | flex-basis: auto; 139 | } 140 | 141 | .homepage-articles { 142 | flex-basis: auto; 143 | } 144 | 145 | .homepage-works { 146 | flex-basis: auto; 147 | margin-left: -25px; 148 | margin-right: -25px; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/pages/styles/projects.css: -------------------------------------------------------------------------------- 1 | .projects-logo-container { 2 | display: flex; 3 | justify-content: left; 4 | padding-top: 25px; 5 | } 6 | 7 | .projects-logo { 8 | display: flex; 9 | position: fixed; 10 | border: 1px solid white; 11 | border-radius: 50%; 12 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 13 | top: 4vh; 14 | z-index: 999; 15 | } 16 | 17 | .projects-container { 18 | display: flex; 19 | flex-direction: column; 20 | height: 100%; 21 | margin: 0; 22 | padding-top: 120px; 23 | } 24 | 25 | @media (max-width: 600px) { 26 | .projects-container { 27 | padding-top: 60px; 28 | } 29 | 30 | .projects-project { 31 | width: calc(100% / 2); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/styles/readArticle.css: -------------------------------------------------------------------------------- 1 | @import "../../data/styles.css"; 2 | 3 | .read-article-logo-container { 4 | display: flex; 5 | justify-content: left; 6 | padding-top: 25px; 7 | } 8 | 9 | .read-article-logo { 10 | display: flex; 11 | position: fixed; 12 | border: 1px solid white; 13 | border-radius: 50%; 14 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 15 | top: 4vh; 16 | } 17 | 18 | .read-article-container { 19 | display: flex; 20 | height: 100%; 21 | margin: 0; 22 | padding-top: 120px; 23 | } 24 | 25 | .read-article-back { 26 | padding-top: 68px; 27 | padding-right: 50px; 28 | width: 40px; 29 | height: 40px; 30 | } 31 | 32 | .read-article-back-button { 33 | width: 50px; 34 | border-radius: 50%; 35 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 36 | } 37 | 38 | .read-article-wrapper { 39 | width: 80%; 40 | padding-top: 30px; 41 | } 42 | 43 | .read-article-date-container { 44 | border-left: 2px solid var(--quaternary-color); 45 | color: var(--tertiary-color); 46 | font-size: 16 px; 47 | align-items: center; 48 | height: 22px; 49 | } 50 | 51 | .read-article-date { 52 | font-family: var(--primary-font); 53 | padding-left: 15px; 54 | } 55 | 56 | .read-article-title { 57 | padding-top: 50px; 58 | padding-bottom: 50px; 59 | width: 100% !important; 60 | } 61 | 62 | .read-article-body { 63 | padding-bottom: 50px; 64 | font-size: 16px; 65 | line-height: 30px; 66 | font-family: system-ui; 67 | color: #52525b; 68 | } 69 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------