├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .husky ├── .gitignore └── pre-commit ├── .nvmrc ├── LICENSE ├── README.md ├── content ├── featured │ ├── CarPricePredictor │ │ ├── CarPricePrediction.png │ │ ├── demo.png │ │ └── index.md │ ├── InstantMD │ │ ├── InstantMD.png │ │ ├── demo.png │ │ └── index.md │ └── StockPortfolio │ │ ├── HomePage.png │ │ └── index.md ├── jobs │ ├── AUV │ │ └── index.md │ ├── Accenture │ │ └── index.md │ ├── Achievements │ │ └── index.md │ ├── HOPS │ │ └── index.md │ ├── Robocup │ │ └── index.md │ └── TacitPal │ │ └── index.md ├── posts │ ├── clickable-cards │ │ └── index.md │ ├── dark-mode-toggle │ │ └── index.md │ ├── docker-compose-error │ │ └── index.md │ ├── markdown-playground │ │ ├── image.jpg │ │ └── index.md │ └── wordpress-publish-error │ │ ├── console-errors.png │ │ ├── draft-fail.png │ │ ├── index.md │ │ └── publish-error.png └── projects │ ├── CLIQuiz.md │ ├── DjangoChat.md │ ├── FlightPricePrediction.md │ ├── PredictingShares.md │ ├── SalaryPredictor.md │ ├── SentimentAnalysis.md │ ├── ToDoList.md │ └── Xenith Space Shooter.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── package.json ├── prettier.config.js ├── src ├── components │ ├── email.js │ ├── footer.js │ ├── head.js │ ├── icons │ │ ├── appstore.js │ │ ├── bookmark.js │ │ ├── codepen.js │ │ ├── external.js │ │ ├── folder.js │ │ ├── fork.js │ │ ├── github.js │ │ ├── icon.js │ │ ├── index.js │ │ ├── instagram.js │ │ ├── linkedin.js │ │ ├── loader.js │ │ ├── logo.js │ │ ├── playstore.js │ │ ├── star.js │ │ └── twitter.js │ ├── index.js │ ├── layout.js │ ├── loader.js │ ├── menu.js │ ├── nav.js │ ├── sections │ │ ├── about.js │ │ ├── contact.js │ │ ├── featured.js │ │ ├── hero.js │ │ ├── jobs.js │ │ └── projects.js │ ├── side.js │ └── social.js ├── config.js ├── fonts │ ├── Calibre │ │ ├── Calibre-Light.ttf │ │ ├── Calibre-Light.woff │ │ ├── Calibre-Light.woff2 │ │ ├── Calibre-LightItalic.ttf │ │ ├── Calibre-LightItalic.woff │ │ ├── Calibre-LightItalic.woff2 │ │ ├── Calibre-Medium.ttf │ │ ├── Calibre-Medium.woff │ │ ├── Calibre-Medium.woff2 │ │ ├── Calibre-MediumItalic.ttf │ │ ├── Calibre-MediumItalic.woff │ │ ├── Calibre-MediumItalic.woff2 │ │ ├── Calibre-Regular.ttf │ │ ├── Calibre-Regular.woff │ │ ├── Calibre-Regular.woff2 │ │ ├── Calibre-RegularItalic.ttf │ │ ├── Calibre-RegularItalic.woff │ │ ├── Calibre-RegularItalic.woff2 │ │ ├── Calibre-Semibold.ttf │ │ ├── Calibre-Semibold.woff │ │ ├── Calibre-Semibold.woff2 │ │ ├── Calibre-SemiboldItalic.ttf │ │ ├── Calibre-SemiboldItalic.woff │ │ └── Calibre-SemiboldItalic.woff2 │ └── SFMono │ │ ├── SFMono-Medium.ttf │ │ ├── SFMono-Medium.woff │ │ ├── SFMono-Medium.woff2 │ │ ├── SFMono-MediumItalic.ttf │ │ ├── SFMono-MediumItalic.woff │ │ ├── SFMono-MediumItalic.woff2 │ │ ├── SFMono-Regular.ttf │ │ ├── SFMono-Regular.woff │ │ ├── SFMono-Regular.woff2 │ │ ├── SFMono-RegularItalic.ttf │ │ ├── SFMono-RegularItalic.woff │ │ ├── SFMono-RegularItalic.woff2 │ │ ├── SFMono-Semibold.ttf │ │ ├── SFMono-Semibold.woff │ │ ├── SFMono-Semibold.woff2 │ │ ├── SFMono-SemiboldItalic.ttf │ │ ├── SFMono-SemiboldItalic.woff │ │ └── SFMono-SemiboldItalic.woff2 ├── hooks │ ├── index.js │ ├── useOnClickOutside.js │ ├── usePrefersReducedMotion.js │ └── useScrollDirection.js ├── images │ ├── Google Lighthouse Performance Metrtics.png │ ├── demo.png │ ├── favicons │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png │ ├── logo.png │ └── profile.jpeg ├── pages │ ├── 404.js │ ├── archive.js │ ├── index.js │ └── pensieve │ │ ├── index.js │ │ └── tags.js ├── styles │ ├── GlobalStyle.js │ ├── PrismStyles.js │ ├── TransitionStyles.js │ ├── fonts.js │ ├── index.js │ ├── mixins.js │ ├── theme.js │ └── variables.js ├── templates │ ├── post.js │ └── tag.js └── utils │ ├── index.js │ └── sr.js ├── static ├── og.png ├── og@2x.png └── resume.pdf └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-react", 4 | "babel-preset-gatsby" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "@upstatement/eslint-config/react" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | .cache 3 | node_modules 4 | yarn-error.log 5 | package-lock.json 6 | 7 | # Build directory 8 | /public 9 | .DS_Store 10 | 11 | .vscode/ 12 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14.16.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Parth Desai 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 |
2 | Logo 3 |
4 | 5 |

6 | Personal Portfolio 7 |

8 | 9 |

10 | This is my personal portfolio website hosted here built with Gatsby and hosted with Vercel 11 |

12 | 13 |

14 | It was originally designed by Brittany Chiang and can be found here 15 |

16 | 17 |
18 | 19 | ![Gatsby](https://img.shields.io/badge/Gatsby-663399.svg?style=for-the-badge&logo=Gatsby&logoColor=white) 20 | ![Vercel](https://img.shields.io/badge/Vercel-000000.svg?style=for-the-badge&logo=Vercel&logoColor=white) 21 | ![React](https://img.shields.io/badge/React-61DAFB.svg?style=for-the-badge&logo=React&logoColor=black) 22 | ![Node.js](https://img.shields.io/badge/Node.js-339933.svg?style=for-the-badge&logo=nodedotjs&logoColor=white) 23 | 24 |
25 | 26 | ![demo](https://raw.githubusercontent.com/pycoder2000/portfolio-v4/main/src/images/demo.png) 27 | 28 | ## 🛠 Installation & Set Up 29 | 30 | 1. Install the Gatsby CLI 31 | 32 | ```sh 33 | npm install -g gatsby-cli 34 | ``` 35 | 36 | 2. Install and use the correct version of Node using [NVM](https://github.com/nvm-sh/nvm) 37 | 38 | ```sh 39 | nvm install 40 | ``` 41 | 42 | 3. Install dependencies 43 | 44 | ```sh 45 | yarn 46 | ``` 47 | 48 | 4. Start the development server 49 | 50 | ```sh 51 | npm start 52 | ``` 53 | 54 | ## 🚀 Building and Running for Production 55 | 56 | 1. Generate a full static production build 57 | 58 | ```sh 59 | npm run build 60 | ``` 61 | 62 | 2. Preview the site as it will appear once deployed 63 | 64 | ```sh 65 | npm run serve 66 | ``` 67 | 68 | ## 📊 Google Lighthouse Performance Metrics 69 | 70 | ![Google Lighthouse Performance Metrics](https://raw.githubusercontent.com/pycoder2000/portfolio-v4/main/src/images/Google%20Lighthouse%20Performance%20Metrtics.png) 71 | 72 | ## 🚨 Forking this repo 73 | 74 | If you fork this website, **please attribute it** to the original author. 75 | 76 | ## 🎨 Color Reference 77 | 78 | | Color | Hex | 79 | | -------------- | ------------------------------------------------------------------ | 80 | | Navy | ![#0a192f](https://via.placeholder.com/10/0a192f?text=+) `#0a192f` | 81 | | Light Navy | ![#112240](https://via.placeholder.com/10/0a192f?text=+) `#112240` | 82 | | Lightest Navy | ![#233554](https://via.placeholder.com/10/303C55?text=+) `#233554` | 83 | | Slate | ![#8892b0](https://via.placeholder.com/10/8892b0?text=+) `#8892b0` | 84 | | Light Slate | ![#a8b2d1](https://via.placeholder.com/10/a8b2d1?text=+) `#a8b2d1` | 85 | | Lightest Slate | ![#ccd6f6](https://via.placeholder.com/10/ccd6f6?text=+) `#ccd6f6` | 86 | | White | ![#e6f1ff](https://via.placeholder.com/10/e6f1ff?text=+) `#e6f1ff` | 87 | | Green | ![#64ffda](https://via.placeholder.com/10/64ffda?text=+) `#64ffda` | 88 | -------------------------------------------------------------------------------- /content/featured/CarPricePredictor/CarPricePrediction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/featured/CarPricePredictor/CarPricePrediction.png -------------------------------------------------------------------------------- /content/featured/CarPricePredictor/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/featured/CarPricePredictor/demo.png -------------------------------------------------------------------------------- /content/featured/CarPricePredictor/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2' 3 | title: 'Car Price Predictor' 4 | cover: './CarPricePrediction.png' 5 | github: 'https://github.com/pycoder2000/Car-Price-Prediction' 6 | external: 'https://allysoncaragency.herokuapp.com/' 7 | tech: 8 | - Python 9 | - Scipy 10 | - HTML 11 | - Javascript 12 | - Linear Regression 13 | --- 14 | 15 | The [Car Price Predictor](https://allysoncaragency.herokuapp.com/) is a web application which predicts car prices based on given independent features like Car Name, Year, Selling Price, Present Price, Kms Driven, Fuel Type etc. The dataset is available at [Kaggle](https://www.kaggle.com/code/mdejazulhassan/vehicle-dataset-from-cardekho/data), and it's provided by [cardekho.com](https://www.cardekho.com/). It is made using **Flask** and deployed on **Heroku**. 16 | -------------------------------------------------------------------------------- /content/featured/InstantMD/InstantMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/featured/InstantMD/InstantMD.png -------------------------------------------------------------------------------- /content/featured/InstantMD/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/featured/InstantMD/demo.png -------------------------------------------------------------------------------- /content/featured/InstantMD/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '1' 3 | title: 'InstantMD' 4 | cover: './InstantMD.png' 5 | github: 'https://github.com/pycoder2000/InstantMD' 6 | #external: 'https://halcyon-theme.netlify.com/' 7 | tech: 8 | - Regex 9 | - Semantic UI 10 | - Flask 11 | - Python 12 | --- 13 | 14 | **Instant MD is an Investigation, Medication and Chief complaint recognition application using Natural Language Processing.** 15 | 16 | _🏆 Winner of the HealthCare Track at the MINeD Hackathon_ 17 | 18 | Being able to have machines understand unstructured textual content already plays a big part nowadays in our life. NLP can contribute largely to the advancement of medical science. NLP is used to extract information from free text narratives written by a variety of healthcare providers. 19 | -------------------------------------------------------------------------------- /content/featured/StockPortfolio/HomePage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/featured/StockPortfolio/HomePage.png -------------------------------------------------------------------------------- /content/featured/StockPortfolio/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '3' 3 | title: 'Stock Portfolio Management' 4 | cover: './HomePage.png' 5 | cta: 'https://github.com/pycoder2000/Stock-Management' 6 | external: '' 7 | tech: 8 | - Django 9 | - HTML 10 | - Python 11 | - CSS 12 | - SQLite 13 | --- 14 | 15 | It is a web application for managing your investment portfolio. Users can get real-time stock data and market news via Yahoo Finance, IEX Cloud, and Quandl APIs. They can also compare the performance of the stocks with charts and predict the future behaviour for the same. Users are given the ability to create a customizable stock portfolio and watchlist to track both current and prospective holdings. The application uses RESTful Web Services to pull live/historical stock data. 16 | -------------------------------------------------------------------------------- /content/jobs/AUV/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2019-01-15' 3 | title: 'ROS Developer' 4 | company: 'Team AUV' 5 | location: 'Nirma University, Ahmedabad, India' 6 | range: 'January - July 2019' 7 | url: 'https://in.linkedin.com/company/auv-nirma-university' 8 | --- 9 | 10 | - The Autonomous Underwater Vehicle Team (Team AUV) is a student group working in the field of autonomous underwater robotics. 11 | - Built the framework for communication between various controllers and sensors using TCP/IP protocol with Nvidia Jetson TX2. 12 | - Programmed controller board for motor control, odometry and serial interface with ROS Interfaced IMU sensor with Arduino Mega microcontroller board for stability and IR sensor for obstacle avoidance. 13 | - Collaborated with team members on design and integration of the vehicle. 14 | - **Technology: Python, C++, ROS, Arduino C** 15 | -------------------------------------------------------------------------------- /content/jobs/Accenture/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2022-01-14' 3 | title: 'Data Engineer' 4 | company: 'Accenture' 5 | location: 'Ahmedabad, India' 6 | range: 'January - May 2022' 7 | url: 'https://www.accenture.com/gr-en/about/consulting-index' 8 | --- 9 | 10 | - Helped with the digital transformation of BOAT by working on their platform called ByteETL. 11 | - Built the encryption component for the platform using Scala and Spark which included 2 encoding functionalities : ENCRYPT and HASH. 12 | - Developed ETL pipelines for BOAT based on the pre-decided KPIs. 13 | - Created JSON wrappers for the encryption components. 14 | - Automated and triggered the pipelines to run at specific intervals using CronJob. 15 | - Wrote tests to ensure full coverage of the code. 16 | - **Technology: Python, Scala, Spark, Azure Datafactory** 17 | - [Certificate](https://drive.google.com/file/d/1b9ElekbpbHspyAq441eMRyumLYN5SZOY/view?usp=sharing) 18 | -------------------------------------------------------------------------------- /content/jobs/Achievements/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2010-01-01' 3 | title: '' 4 | company: 'Achievements' 5 | location: 'Ahmedabad, India' 6 | range: 'January 2018 - May 2022' 7 | url: '' 8 | --- 9 | 10 | - Selected for Facebook School of Innovation, Spark AR program amongst **more than 10000 participants** and received industrial training in Augmented Reality for 6 months. 11 | - **Winner of the HealthCare Track** at Mined Hackathon. Mined is a Nationwide Hackathon organized by Nirma University in collaboration with Binghamton University where **629 students participated from over 20 colleges.** 12 | - Python National Scholarship Quiz Winner 13 | - Second Place in a inter-university “Apti-Brain” Competition Aptitude Test 14 | - Received Best Actor Male award in inter institute one-act drama competition 15 | -------------------------------------------------------------------------------- /content/jobs/HOPS/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2021-03-11' 3 | title: 'Software Engineer Analytics' 4 | company: 'HOPS Healthcare' 5 | location: 'Ahmedabad, India' 6 | range: 'March - June 2021' 7 | url: 'https://hops.healthcare/' 8 | --- 9 | 10 | - Guided by Mr. Vivek Patel, to create a pipeline that extracts essential information from informal conversations 11 | - Worked on creating a web app for their healthcare system for doctors to analyze and save reports. 12 | - Involved in initial design, database design and creating basic models. 13 | - Developed functions for automatically extracting important elements from reports using BioBERT and Regex. 14 | - **Technology: Python, Django, BERT, Javascript, Regex, SQL** 15 | - [Certificate](https://drive.google.com/file/d/1tsucTLZ9PdeiXpehBGHbxCHDcMLS396T/view?usp=sharing) 16 | -------------------------------------------------------------------------------- /content/jobs/Robocup/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2019-07-15' 3 | title: 'Backend Developer' 4 | company: 'Robocup' 5 | location: 'Ahmedabad, India' 6 | range: 'August - September 2019' 7 | url: 'https://technology.nirmauni.ac.in/news-events/robocup-2019/' 8 | --- 9 | 10 | - Developed a Score Managing system for National Level Robocup event organized by Nirma University. 11 | - Implemented GUI user functions and components in Tkinter and integrated the front - end with SQLite database. 12 | - Features Included : Accept participant registrations, automated draws with seeding options, winners and losers advance to next round based on configuration, automated scheduling of matches based on team availability, live scores and results available for public viewing. 13 | - **Technology: Python, SQLite, Tkinter** 14 | -------------------------------------------------------------------------------- /content/jobs/TacitPal/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2019-12-07' 3 | title: 'Flutter Developer' 4 | company: 'Tacitpal' 5 | location: 'Ahmedabad, India' 6 | range: 'January - April 2020' 7 | url: '' 8 | --- 9 | 10 | - Developed a cross - platform mobile app using Flutter and Dart for increasing productivity by creating an automated schedule for the users based on priority and time slot allotted to task. 11 | - Conducted day-to-day project coordination, planning, and implementation between a team of 5 Flutter Developers. 12 | - Application Integration using NodeJS RESTful service. 13 | - **Technology: NodeJS, Flutter, Dart** 14 | -------------------------------------------------------------------------------- /content/posts/clickable-cards/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Accessible Clickable Cards 3 | description: Clickable cards with multiple child links 4 | date: 2021-04-21 5 | draft: false 6 | slug: /pensieve/clickable-cards 7 | tags: 8 | - Accessibility 9 | - CSS 10 | --- 11 | 12 | [Codepen Demo](https://codepen.io/bchiang7/pen/xxRBvgd?editors=1100) 13 | 14 | Card layout where the card itself isn't an anchor link, but the whole card is clickable (with a `:before` pseudo element on the main ``). Links inside of the card are still clickable. 15 | 16 | ## CSS 17 | 18 | ```css 19 | .grid__item { 20 | &:hover, 21 | &:focus-within { 22 | background-color: #eee; 23 | } 24 | 25 | a { 26 | position: relative; 27 | z-index: 1; 28 | } 29 | 30 | h2 { 31 | a { 32 | position: static; 33 | 34 | &:hover, 35 | &:focus { 36 | color: blue; 37 | } 38 | 39 | &:before { 40 | content: ''; 41 | display: block; 42 | position: absolute; 43 | z-index: 0; 44 | width: 100%; 45 | height: 100%; 46 | top: 0; 47 | left: 0; 48 | transition: background-color 0.1s ease-out; 49 | background-color: transparent; 50 | } 51 | } 52 | } 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /content/posts/dark-mode-toggle/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dark Mode Toggle 3 | description: Dark mode without the flash of default theme 4 | date: 2021-04-21 5 | draft: false 6 | slug: /pensieve/dark-mode-toggle 7 | tags: 8 | - Theming 9 | - Dark Mode 10 | --- 11 | 12 | Dark mode toggle without the flash of default theme. Important bits: 13 | 14 | - CSS variables for color theming 15 | - Put `data-theme` attribute on ``, not ``, so we can run the JS before the DOM finishes rendering 16 | - Run local storage check in the `` 17 | - JS for toggle button click handler can come after render 18 | 19 | ## HTML 20 | 21 | ```html 22 | 23 | 24 | 25 | 26 | 27 | ... 28 | 35 | 36 | 37 |
38 | 52 |
53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | ## CSS Variables 60 | 61 | ```css 62 | :root { 63 | --bg: #ffffff; 64 | --text: #000000; 65 | } 66 | 67 | [data-theme='dark'] { 68 | --bg: #000000; 69 | --text: #ffffff; 70 | } 71 | ``` 72 | 73 | ## JavaScript 74 | 75 | ```js:title=app.js 76 | const themeToggleBtn = document.querySelector('.js-theme-toggle'); 77 | 78 | themeToggleBtn.addEventListener('click', () => onToggleClick()); 79 | 80 | const onToggleClick = () => { 81 | const { theme } = document.documentElement.dataset; 82 | const themeTo = theme && theme === 'light' ? 'dark' : 'light'; 83 | const label = `Activate ${theme} mode`; 84 | 85 | document.documentElement.setAttribute('data-theme', themeTo); 86 | localStorage.setItem('theme', themeTo); 87 | 88 | themeToggleBtn.setAttribute('aria-label', label); 89 | themeToggleBtn.setAttribute('title', label); 90 | }; 91 | ``` 92 | 93 | ## Resources 94 | 95 | - 96 | - 97 | - 98 | - 99 | - 100 | -------------------------------------------------------------------------------- /content/posts/docker-compose-error/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker Compose Error 3 | description: docker-compose version discrepancies 4 | date: '2019-12-13' 5 | draft: false 6 | slug: '/pensieve/docker-error' 7 | tags: 8 | - WordPress 9 | - Docker 10 | --- 11 | 12 | ## Problem 13 | 14 | Recently while updating with [Skela](https://github.com/Upstatement/skela-wp-theme) with webpack, I encountered a weird error where I wasn't able to run a simple script: 15 | 16 | ```shell:title=bin/composer 17 | #!/bin/bash 18 | docker-compose exec -w /var/www/html/wp-content/themes/skela wordpress composer "$@" 19 | ``` 20 | 21 | When trying to run this script via `./bin/composer install`, I got this error in my terminal: 22 | 23 | ```shell 24 | ERROR: Setting workdir for exec is not supported in API < 1.35 (1.30) 25 | ``` 26 | 27 | The error was coming from the `-w` flag in the `docker-compose exec` command in the `composer` script. 28 | 29 | ## Solution 30 | 31 | Turns The fix was to update the version in my `docker-compose.yml` file to from version `3.5` to `3.6`. It's strange because 3.5 isn't anywhere close to the API version `1.35` from the error message 🤷‍♀️ 32 | 33 | ```yaml:title=docker-compose.yml 34 | version: '3.6' # highlight-line 35 | services: 36 | wordpress: 37 | build: 38 | ``` 39 | -------------------------------------------------------------------------------- /content/posts/markdown-playground/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/posts/markdown-playground/image.jpg -------------------------------------------------------------------------------- /content/posts/markdown-playground/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown Test File 3 | description: abc234 4 | date: 2019-12-07 5 | draft: true 6 | slug: /pensieve/markdown-playground 7 | tags: 8 | - Testing 9 | --- 10 | 11 | ![Image Alt](./image.jpg) 12 | 13 | ```jsx 14 | class FlavorForm extends React.Component { // highlight-line 15 | constructor(props) { 16 | super(props); 17 | this.state = {value: 'coconut'}; 18 | 19 | this.handleChange = this.handleChange.bind(this); 20 | this.handleSubmit = this.handleSubmit.bind(this); 21 | } 22 | 23 | handleChange(event) { 24 | // highlight-next-line 25 | this.setState({value: event.target.value}); 26 | } 27 | 28 | // highlight-start 29 | handleSubmit(event) { 30 | alert('Your favorite flavor is: ' + this.state.value); 31 | event.preventDefault(); 32 | } 33 | // highlight-end 34 | 35 | render() { 36 | return ( 37 | { /* highlight-range{1,4-9,12} */ } 38 |
39 | 48 | 49 |
50 | ); 51 | } 52 | } 53 | ``` 54 | 55 | ```javascript:title=highlight.js 56 | // Here is a comment 57 | function $initHighlight(block, cls) { 58 | try { 59 | if (cls.search(/\bno\-highlight\b/) != -1) 60 | return process(block, true, 0x0F) + 61 | ` class="${cls}"`; 62 | } catch (e) { 63 | /* handle exception */ 64 | } 65 | for (var i = 0 / 2; i < classes.length; i++) { 66 | if (checkCondition(classes[i]) === undefined) { 67 | console.log('undefined'); 68 | } 69 | } 70 | 71 | return ( 72 |
73 | {block} 74 |
75 | ) 76 | } 77 | 78 | export $initHighlight; 79 | ``` 80 | 81 | This is a paragraph. 82 | 83 | This is a paragraph. 84 | 85 | # Header 1 86 | 87 | ## Header 2 88 | 89 | Header 1 90 | ======== 91 | 92 | Header 2 93 | -------- 94 | 95 | ```css 96 | @import 'compass/reset'; 97 | 98 | // variables 99 | $colorGreen: #008000; 100 | $colorGreenDark: darken($colorGreen, 10); 101 | 102 | @mixin container { 103 | max-width: 980px; 104 | } 105 | 106 | // mixins with parameters 107 | @mixin button($color: green) { 108 | @if ($color == green) { 109 | background-color: #008000; 110 | } @else if ($color == red) { 111 | background-color: #b22222; 112 | } 113 | } 114 | 115 | button { 116 | @include button(red); 117 | } 118 | 119 | div, 120 | .navbar, 121 | #header, 122 | input[type='input'] { 123 | font-family: 'Helvetica Neue', Arial, sans-serif; 124 | width: auto; 125 | margin: 0 auto; 126 | display: block; 127 | } 128 | 129 | .row-12 > [class*='spans'] { 130 | border-left: 1px solid #b5c583; 131 | } 132 | 133 | // nested definitions 134 | ul { 135 | width: 100%; 136 | padding: { 137 | left: 5px; 138 | right: 5px; 139 | } 140 | li { 141 | float: left; 142 | margin-right: 10px; 143 | .home { 144 | background: url('http://placehold.it/20') scroll no-repeat 0 0; 145 | } 146 | } 147 | } 148 | 149 | .banner { 150 | @extend .container; 151 | } 152 | 153 | a { 154 | color: $colorGreen; 155 | &:hover { 156 | color: $colorGreenDark; 157 | } 158 | &:visited { 159 | color: #c458cb; 160 | } 161 | } 162 | 163 | @for $i from 1 through 5 { 164 | .span#{$i} { 165 | width: 20px * $i; 166 | } 167 | } 168 | 169 | @mixin mobile { 170 | @media screen and (max-width: 600px) { 171 | @content; 172 | } 173 | } 174 | ``` 175 | 176 | ```markdown 177 | # hello world 178 | 179 | you can write text [with links](http://example.com) inline or [link references][1]. 180 | 181 | - one _thing_ has *em*phasis 182 | - two **things** are **bold** 183 | 184 | [1]: http://example.com 185 | 186 | --- 187 | 188 | # hello world 189 | 190 | 191 | 192 | > markdown is so cool 193 | 194 | so are code segments 195 | 196 | 1. one thing (yeah!) 197 | 2. two thing `i can write code`, and `more` wipee! 198 | ``` 199 | 200 | # Header 1 201 | 202 | ## Header 2 203 | 204 | ### Header 3 205 | 206 | #### Header 4 207 | 208 | ##### Header 5 209 | 210 | ###### Header 6 211 | 212 | # Header 1 213 | ## Header 2 214 | ### Header 3 215 | #### Header 4 216 | ##### Header 5 217 | ###### Header 6 218 | 219 | # Header 1 220 | 221 | ## Header 2 222 | 223 | ### Header 3 224 | 225 | #### Header 4 226 | 227 | ##### Header 5 228 | 229 | ###### Header 6 230 | 231 | # Header 1 # 232 | ## Header 2 ## 233 | ### Header 3 ### 234 | #### Header 4 #### 235 | ##### Header 5 ##### 236 | ###### Header 6 ###### 237 | 238 | > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 239 | 240 | > Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 241 | 242 | > ## This is a header 243 | > 244 | > 1. This is the first list item. 245 | > 2. This is the second list item. 246 | > 247 | > Here's some example code: 248 | > 249 | > Markdown.generate(); 250 | 251 | > ## This is a header. 252 | > 1. This is the first list item. 253 | > 2. This is the second list item. 254 | > 255 | > Here's some example code: 256 | > 257 | > Markdown.generate(); 258 | 259 | - Red 260 | - Green 261 | - Blue 262 | 263 | - Red 264 | - Green 265 | - Blue 266 | 267 | - Red 268 | - Green 269 | - Blue 270 | 271 | ```markdown 272 | - Red 273 | - Green 274 | - Blue 275 | 276 | * Red 277 | * Green 278 | * Blue 279 | 280 | - Red 281 | - Green 282 | - Blue 283 | ``` 284 | 285 | 1. Buy flour and salt 286 | 2. Mix together with water 287 | 3. Bake 288 | 289 | ```markdown 290 | 1. Buy flour and salt 291 | 1. Mix together with water 292 | 1. Bake 293 | ``` 294 | 295 | Paragraph: 296 | 297 | Code 298 | 299 | 300 | 301 | Paragraph: 302 | 303 | Code 304 | 305 | --- 306 | 307 | --- 308 | 309 | --- 310 | 311 | --- 312 | 313 | --- 314 | 315 | * * * 316 | 317 | *** 318 | 319 | ***** 320 | 321 | - - - 322 | 323 | --------------------------------------- 324 | 325 | This is [an example](http://example.com 'Example') link. 326 | 327 | [This link](http://example.com) has no title attr. 328 | 329 | This is [an example][id] reference-style link. 330 | 331 | [id]: http://example.com 'Optional Title' 332 | 333 | This is [an example](http://example.com "Example") link. 334 | 335 | [This link](http://example.com) has no title attr. 336 | 337 | This is [an example] [id] reference-style link. 338 | 339 | [id]: http://example.com "Optional Title" 340 | 341 | _single asterisks_ 342 | 343 | _single underscores_ 344 | 345 | **double asterisks** 346 | 347 | **double underscores** 348 | 349 | *single asterisks* 350 | 351 | _single underscores_ 352 | 353 | **double asterisks** 354 | 355 | __double underscores__ 356 | 357 | This paragraph has some `code` in it. 358 | 359 | This paragraph has some `code` in it. 360 | -------------------------------------------------------------------------------- /content/posts/wordpress-publish-error/console-errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/posts/wordpress-publish-error/console-errors.png -------------------------------------------------------------------------------- /content/posts/wordpress-publish-error/draft-fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/posts/wordpress-publish-error/draft-fail.png -------------------------------------------------------------------------------- /content/posts/wordpress-publish-error/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: WordPress Publishing Error 3 | description: Trying to create a simple post in WordPress 4 | date: 2019-12-03 5 | draft: false 6 | slug: /pensieve/wordpress-publish-error 7 | tags: 8 | - WordPress 9 | --- 10 | 11 | ## Problem 12 | 13 | Recently while working on a WordPress project with [Ups Dock](https://github.com/Upstatement/ups-dock), I encountered a weird error where I wasn't able to update or publish a simple post in my local WP admin. 14 | 15 | It looked something like this: 16 | 17 | ![Draft fail](./draft-fail.png) 18 | 19 | Sometimes the error message would be slightly more helpful: `Publishing failed. Error message: The response is not a valid JSON response.` 20 | 21 | ![Publish error](./publish-error.png) 22 | 23 | And if I popped open the console, I saw these errors: 24 | 25 | ![Console errors](./console-errors.png) 26 | 27 | ## Solution 28 | 29 | Since the error message had to do with a JSON response, I initially thought it was a Gutenberg or ACF issue. But it turned out this was happening because I was on the https WP admin (i.e. [https://project.ups.dock/wp-admin](https://project.ups.dock/wp-admin)), not the unsecure WP admin ([http://project.ups.dock/wp-admin](http://project.ups.dock/wp-admin)). 30 | 31 | It was a CORS error!! I was trying to modify a non-https domain from a https domain. Switching to a non-https WP admin allowed me to publish posts with no problem. 32 | -------------------------------------------------------------------------------- /content/posts/wordpress-publish-error/publish-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pycoder2000/portfolio-v4/108af3f210879860f83bea6ab9e70b93e5286769/content/posts/wordpress-publish-error/publish-error.png -------------------------------------------------------------------------------- /content/projects/CLIQuiz.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2022-01-05' 3 | title: 'CLI Quiz' 4 | github: 'https://github.com/pycoder2000/CLI-Quiz' 5 | external: 'https://libraries.io/npm/cliquiz-millionaire' 6 | tech: 7 | - Node 8 | - Javascript 9 | company: '' 10 | showInProjects: true 11 | ios: '' 12 | android: '' 13 | --- 14 | 15 | "Who wants to be a Millionaire? (JavaScript Edition)" Terminal Game that I made to practice my JavaScript skills. 16 | -------------------------------------------------------------------------------- /content/projects/DjangoChat.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2022-03-12' 3 | title: 'Django Chat App' 4 | github: 'https://github.com/pycoder2000/djangochat' 5 | external: '' 6 | tech: 7 | - Django 8 | - HTML 9 | - CSS 10 | - Channels 11 | - Vanilla Javascript 12 | - Web Sockets 13 | company: '' 14 | showInProjects: true 15 | ios: '' 16 | android: '' 17 | --- 18 | 19 | A small real time chat application built using Django. It also uses Channels and Vanilla Javascript with Web Sockets. 20 | -------------------------------------------------------------------------------- /content/projects/FlightPricePrediction.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2019-03-25' 3 | title: 'Flight Price Prediction' 4 | github: 'https://github.com/pycoder2000/Flight-Price-Prediction' 5 | external: '' 6 | tech: 7 | - Tensorflow 8 | - Python 9 | - Flask 10 | - Scipy 11 | company: '' 12 | showInProjects: true 13 | ios: '' 14 | android: '' 15 | --- 16 | 17 | Flight Price Prediction with Tensorflow. Here you will be provided with prices of flight tickets for various airlines between the months of March and June of 2019 and between various cities. 18 | -------------------------------------------------------------------------------- /content/projects/PredictingShares.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2018-10-01' 3 | title: 'Predicting number of Shares' 4 | github: 'https://github.com/pycoder2000/Predicting-Number-of-Shares' 5 | external: '' 6 | tech: 7 | - Scipy 8 | - Machine Learning 9 | - LinearRegression 10 | company: '' 11 | showInProjects: true 12 | ios: '' 13 | android: '' 14 | --- 15 | 16 | Predicting the number of shares based on how popular the article is. 17 | -------------------------------------------------------------------------------- /content/projects/SalaryPredictor.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2022-03-25' 3 | title: 'Salary Predictor' 4 | github: 'https://github.com/pycoder2000/Salary' 5 | external: 'https://salary-predictor-stream.herokuapp.com/' 6 | tech: 7 | - Python 8 | - Streamlit 9 | - Javascript 10 | company: '' 11 | showInProjects: true 12 | ios: '' 13 | android: '' 14 | --- 15 | 16 | Salary Prediction App made with StreamLit just to practice the Streamlit framework 17 | 18 | A [Streamlit](https://streamlit.io/) demo [written in pure Python](https://github.com/pycoder2000/Salary/blob/main/app.py) to predict salary based on your years of experience. 19 | -------------------------------------------------------------------------------- /content/projects/SentimentAnalysis.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2021-12-03' 3 | title: 'Sentiment Analysis' 4 | github: 'https://github.com/pycoder2000/sentiment-analysis' 5 | external: '' 6 | tech: 7 | - Flask 8 | - HTML 9 | - NLTK 10 | - TfidfVectorizer 11 | - Python 12 | company: 'Nirma University' 13 | showInProjects: true 14 | ios: '' 15 | android: '' 16 | --- 17 | 18 | This is a web app which can be used to analyze users' sentiments across different platforms using REST Apis. Made with Python, Flask, HTML, Javascript and deployed using Vercel. The model was trained using tweets from Sentiment140 dataset with 1.6 million tweets. 19 | -------------------------------------------------------------------------------- /content/projects/ToDoList.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2022-02-23' 3 | title: 'To Do List' 4 | github: 'https://github.com/pycoder2000/Todo-List' 5 | external: '' 6 | tech: 7 | - Django 8 | - HTML 9 | - CSS 10 | - SQL 11 | company: '' 12 | showInProjects: true 13 | ios: '' 14 | android: '' 15 | --- 16 | 17 | Clean and simple to-do list application made with Django framework. 18 | 19 | Features: 20 | 21 | - Easy add, delete and edit tasks 22 | - Reorder items 23 | - Login and register 24 | - Mark tasks as completed 25 | - Clean UI 26 | -------------------------------------------------------------------------------- /content/projects/Xenith Space Shooter.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: '2020-10-23' 3 | title: 'Xenith Space Shooter' 4 | github: 'https://github.com/pycoder2000/Xenith-Space_Shooter' 5 | external: '' 6 | tech: 7 | - Pygame 8 | - Python 9 | - tkinter 10 | company: '' 11 | showInProjects: true 12 | ios: '' 13 | android: '' 14 | --- 15 | 16 | A Knock-off of a retro space shooter game that I made with Pygame. 17 | 18 | Xenith is a knock-off of a popular game called Space Shooters that I made for my Girlfriend on our 1st Anniversary using Pygame. 19 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Browser APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 5 | */ 6 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | const config = require("./src/config"); 2 | 3 | module.exports = { 4 | siteMetadata: { 5 | title: "Portfolio | Parth Desai", 6 | description: 7 | "I am a Data Engineer who is passionate about Data Science and Automation. In my free time, I like developing.", 8 | siteUrl: "https://parthdesai.vercel.app", // No trailing slash allowed! 9 | image: "/og.png", // Path to your image you placed in the 'static' folder 10 | twitterUsername: "@lone_Musk", 11 | }, 12 | plugins: [ 13 | `gatsby-plugin-react-helmet`, 14 | `gatsby-plugin-styled-components`, 15 | `gatsby-plugin-image`, 16 | `gatsby-plugin-sharp`, 17 | `gatsby-transformer-sharp`, 18 | `gatsby-plugin-sitemap`, 19 | `gatsby-plugin-robots-txt`, 20 | { 21 | resolve: `gatsby-plugin-manifest`, 22 | options: { 23 | name: "ParthDesai", 24 | short_name: "ParthDesai", 25 | start_url: "/", 26 | background_color: config.colors.darkNavy, 27 | theme_color: config.colors.navy, 28 | display: "minimal-ui", 29 | icon: "src/images/logo.png", 30 | }, 31 | }, 32 | `gatsby-plugin-offline`, 33 | { 34 | resolve: `gatsby-source-filesystem`, 35 | options: { 36 | name: `images`, 37 | path: `${__dirname}/src/images`, 38 | }, 39 | }, 40 | { 41 | resolve: "gatsby-source-filesystem", 42 | options: { 43 | name: "content", 44 | path: `${__dirname}/content/`, 45 | }, 46 | }, 47 | { 48 | resolve: `gatsby-source-filesystem`, 49 | options: { 50 | name: `posts`, 51 | path: `${__dirname}/content/posts`, 52 | }, 53 | }, 54 | { 55 | resolve: `gatsby-source-filesystem`, 56 | options: { 57 | name: `projects`, 58 | path: `${__dirname}/content/projects`, 59 | }, 60 | }, 61 | { 62 | resolve: `gatsby-transformer-remark`, 63 | options: { 64 | plugins: [ 65 | { 66 | // https://www.gatsbyjs.org/packages/gatsby-remark-external-links 67 | resolve: "gatsby-remark-external-links", 68 | options: { 69 | target: "_blank", 70 | rel: "nofollow noopener noreferrer", 71 | }, 72 | }, 73 | { 74 | // https://www.gatsbyjs.org/packages/gatsby-remark-images 75 | resolve: "gatsby-remark-images", 76 | options: { 77 | maxWidth: 700, 78 | linkImagesToOriginal: true, 79 | quality: 90, 80 | tracedSVG: { color: config.colors.green }, 81 | }, 82 | }, 83 | { 84 | // https://www.gatsbyjs.org/packages/gatsby-remark-code-titles/ 85 | resolve: "gatsby-remark-code-titles", 86 | }, // IMPORTANT: this must be ahead of other plugins that use code blocks 87 | { 88 | // https://www.gatsbyjs.org/packages/gatsby-remark-prismjs 89 | resolve: `gatsby-remark-prismjs`, 90 | options: { 91 | // Class prefix for
 tags containing syntax highlighting;
 92 |               // defaults to 'language-' (e.g. 
).
 93 |               // If your site loads Prism into the browser at runtime,
 94 |               // (e.g. for use with libraries like react-live),
 95 |               // you may use this to prevent Prism from re-processing syntax.
 96 |               // This is an uncommon use-case though;
 97 |               // If you're unsure, it's best to use the default value.
 98 |               classPrefix: "language-",
 99 |               // This is used to allow setting a language for inline code
100 |               // (i.e. single backticks) by creating a separator.
101 |               // This separator is a string and will do no white-space
102 |               // stripping.
103 |               // A suggested value for English speakers is the non-ascii
104 |               // character '›'.
105 |               inlineCodeMarker: null,
106 |               // This lets you set up language aliases.  For example,
107 |               // setting this to '{ sh: "bash" }' will let you use
108 |               // the language "sh" which will highlight using the
109 |               // bash highlighter.
110 |               aliases: {},
111 |               // This toggles the display of line numbers globally alongside the code.
112 |               // To use it, add the following line in gatsby-browser.js
113 |               // right after importing the prism color scheme:
114 |               //  require("prismjs/plugins/line-numbers/prism-line-numbers.css")
115 |               // Defaults to false.
116 |               // If you wish to only show line numbers on certain code blocks,
117 |               // leave false and use the {numberLines: true} syntax below
118 |               showLineNumbers: false,
119 |               // If setting this to true, the parser won't handle and highlight inline
120 |               // code used in markdown i.e. single backtick code like `this`.
121 |               noInlineHighlight: false,
122 |               // This adds a new language definition to Prism or extend an already
123 |               // existing language definition. More details on this option can be
124 |               // found under the header "Add new language definition or extend an
125 |               // existing language" below.
126 |               languageExtensions: [
127 |                 {
128 |                   language: "superscript",
129 |                   extend: "javascript",
130 |                   definition: {
131 |                     superscript_types: /(SuperType)/,
132 |                   },
133 |                   insertBefore: {
134 |                     function: {
135 |                       superscript_keywords: /(superif|superelse)/,
136 |                     },
137 |                   },
138 |                 },
139 |               ],
140 |               // Customize the prompt used in shell output
141 |               // Values below are default
142 |               prompt: {
143 |                 user: "root",
144 |                 host: "localhost",
145 |                 global: false,
146 |               },
147 |             },
148 |           },
149 |         ],
150 |       },
151 |     },
152 |     {
153 |       resolve: `gatsby-plugin-google-analytics`,
154 |       options: {
155 |         trackingId: "G-X3DVJHL358",
156 |       },
157 |     },
158 |   ],
159 | };
160 | 


--------------------------------------------------------------------------------
/gatsby-node.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * Implement Gatsby's Node APIs in this file.
  3 |  *
  4 |  * See: https://www.gatsbyjs.org/docs/node-apis/
  5 |  */
  6 | 
  7 | const path = require('path');
  8 | const _ = require('lodash');
  9 | 
 10 | exports.createPages = async ({ actions, graphql, reporter }) => {
 11 |   const { createPage } = actions;
 12 |   const postTemplate = path.resolve(`src/templates/post.js`);
 13 |   const tagTemplate = path.resolve('src/templates/tag.js');
 14 | 
 15 |   const result = await graphql(`
 16 |     {
 17 |       postsRemark: allMarkdownRemark(
 18 |         filter: { fileAbsolutePath: { regex: "/content/posts/" } }
 19 |         sort: { order: DESC, fields: [frontmatter___date] }
 20 |         limit: 1000
 21 |       ) {
 22 |         edges {
 23 |           node {
 24 |             frontmatter {
 25 |               slug
 26 |             }
 27 |           }
 28 |         }
 29 |       }
 30 |       tagsGroup: allMarkdownRemark(limit: 2000) {
 31 |         group(field: frontmatter___tags) {
 32 |           fieldValue
 33 |         }
 34 |       }
 35 |     }
 36 |   `);
 37 | 
 38 |   // Handle errors
 39 |   if (result.errors) {
 40 |     reporter.panicOnBuild(`Error while running GraphQL query.`);
 41 |     return;
 42 |   }
 43 | 
 44 |   // Create post detail pages
 45 |   const posts = result.data.postsRemark.edges;
 46 | 
 47 |   posts.forEach(({ node }) => {
 48 |     createPage({
 49 |       path: node.frontmatter.slug,
 50 |       component: postTemplate,
 51 |       context: {},
 52 |     });
 53 |   });
 54 | 
 55 |   // Extract tag data from query
 56 |   const tags = result.data.tagsGroup.group;
 57 |   // Make tag pages
 58 |   tags.forEach(tag => {
 59 |     createPage({
 60 |       path: `/pensieve/tags/${_.kebabCase(tag.fieldValue)}/`,
 61 |       component: tagTemplate,
 62 |       context: {
 63 |         tag: tag.fieldValue,
 64 |       },
 65 |     });
 66 |   });
 67 | };
 68 | 
 69 | // https://www.gatsbyjs.org/docs/node-apis/#onCreateWebpackConfig
 70 | exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
 71 |   // https://www.gatsbyjs.org/docs/debugging-html-builds/#fixing-third-party-modules
 72 |   if (stage === 'build-html' || stage === 'develop-html') {
 73 |     actions.setWebpackConfig({
 74 |       module: {
 75 |         rules: [
 76 |           {
 77 |             test: /scrollreveal/,
 78 |             use: loaders.null(),
 79 |           },
 80 |           {
 81 |             test: /animejs/,
 82 |             use: loaders.null(),
 83 |           },
 84 |           {
 85 |             test: /miniraf/,
 86 |             use: loaders.null(),
 87 |           },
 88 |         ],
 89 |       },
 90 |     });
 91 |   }
 92 | 
 93 |   actions.setWebpackConfig({
 94 |     resolve: {
 95 |       alias: {
 96 |         '@components': path.resolve(__dirname, 'src/components'),
 97 |         '@config': path.resolve(__dirname, 'src/config'),
 98 |         '@fonts': path.resolve(__dirname, 'src/fonts'),
 99 |         '@hooks': path.resolve(__dirname, 'src/hooks'),
100 |         '@images': path.resolve(__dirname, 'src/images'),
101 |         '@pages': path.resolve(__dirname, 'src/pages'),
102 |         '@styles': path.resolve(__dirname, 'src/styles'),
103 |         '@utils': path.resolve(__dirname, 'src/utils'),
104 |       },
105 |     },
106 |   });
107 | };
108 | 


--------------------------------------------------------------------------------
/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | /**
2 |  * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
3 |  *
4 |  * See: https://www.gatsbyjs.org/docs/ssr-apis/
5 |  */
6 | 
7 | // You can delete this file if you're not using it
8 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "v4",
 3 |   "description": "Personal Website",
 4 |   "version": "1.0.0",
 5 |   "author": "Parth Desai ",
 6 |   "repository": {
 7 |     "type": "git",
 8 |     "url": "https://github.com/pycoder2000/portfolio-v4"
 9 |   },
10 |   "keywords": [
11 |     "gatsby"
12 |   ],
13 |   "license": "MIT",
14 |   "browserslist": "> 0.25%, not dead",
15 |   "scripts": {
16 |     "build": "gatsby build",
17 |     "develop": "gatsby develop",
18 |     "format": "prettier --write \"**/*.{js,jsx,json,md}\"",
19 |     "start": "npm run develop",
20 |     "serve": "gatsby serve",
21 |     "clean": "gatsby clean",
22 |     "prepare": "husky install",
23 |     "lint-staged": "lint-staged"
24 |   },
25 |   "lint-staged": {
26 |     "*.{js,css,json,md}": [
27 |       "prettier --write"
28 |     ],
29 |     "*.js": [
30 |       "eslint --fix"
31 |     ]
32 |   },
33 |   "dependencies": {
34 |     "animejs": "^3.1.0",
35 |     "babel-plugin-styled-components": "^1.12.0",
36 |     "gatsby": "^3.4.1",
37 |     "gatsby-plugin-google-analytics": "^3.4.0",
38 |     "gatsby-plugin-image": "^1.4.0",
39 |     "gatsby-plugin-manifest": "^3.4.0",
40 |     "gatsby-plugin-netlify": "^3.4.0",
41 |     "gatsby-plugin-offline": "^4.4.0",
42 |     "gatsby-plugin-react-helmet": "^4.4.0",
43 |     "gatsby-plugin-robots-txt": "^1.5.6",
44 |     "gatsby-plugin-sharp": "^3.4.1",
45 |     "gatsby-plugin-sitemap": "^4.0.0",
46 |     "gatsby-plugin-styled-components": "^4.4.0",
47 |     "gatsby-remark-external-links": "0.0.4",
48 |     "gatsby-remark-images": "^5.1.0",
49 |     "gatsby-remark-prismjs": "^5.1.0",
50 |     "gatsby-source-filesystem": "^3.4.0",
51 |     "gatsby-transformer-remark": "^4.1.0",
52 |     "gatsby-transformer-sharp": "^3.4.0",
53 |     "lodash": "^4.17.19",
54 |     "prismjs": "^1.25.0",
55 |     "prop-types": "^15.7.2",
56 |     "react": "^17.0.2",
57 |     "react-dom": "^17.0.2",
58 |     "react-helmet": "^6.1.0",
59 |     "react-scroll-progress-bar": "^1.1.13",
60 |     "react-transition-group": "^4.3.0",
61 |     "scrollreveal": "^4.0.5",
62 |     "styled-components": "^5.3.0"
63 |   },
64 |   "devDependencies": {
65 |     "@babel/core": "^7.14.0",
66 |     "@babel/eslint-parser": "^7.13.14",
67 |     "@babel/preset-react": "^7.13.13",
68 |     "@upstatement/eslint-config": "^1.0.0",
69 |     "@upstatement/prettier-config": "^1.0.0",
70 |     "babel-preset-gatsby": "^1.4.0",
71 |     "eslint": "^7.25.0",
72 |     "eslint-config-prettier": "^8.3.0",
73 |     "eslint-plugin-jsx-a11y": "^6.4.1",
74 |     "eslint-plugin-react": "^7.23.2",
75 |     "gatsby-remark-code-titles": "^1.1.0",
76 |     "husky": "^6.0.0",
77 |     "lint-staged": "^10.1.2",
78 |     "prettier": "^2.2.1"
79 |   }
80 | }
81 | 


--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@upstatement/prettier-config');
2 | 


--------------------------------------------------------------------------------
/src/components/email.js:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import PropTypes from 'prop-types';
 3 | import styled from 'styled-components';
 4 | import { email } from '@config';
 5 | import { Side } from '@components';
 6 | 
 7 | const StyledLinkWrapper = styled.div`
 8 |   display: flex;
 9 |   flex-direction: column;
10 |   align-items: center;
11 |   position: relative;
12 | 
13 |   &:after {
14 |     content: '';
15 |     display: block;
16 |     width: 1px;
17 |     height: 90px;
18 |     margin: 0 auto;
19 |     background-color: var(--light-slate);
20 |   }
21 | 
22 |   a {
23 |     margin: 20px auto;
24 |     padding: 10px;
25 |     font-family: var(--font-mono);
26 |     font-size: var(--fz-xxs);
27 |     line-height: var(--fz-lg);
28 |     letter-spacing: 0.1em;
29 |     writing-mode: vertical-rl;
30 | 
31 |     &:hover,
32 |     &:focus {
33 |       transform: translateY(-3px);
34 |       text-decoration: underline;
35 |       text-underline-offset: 50%;
36 |     }
37 |   }
38 | `;
39 | 
40 | const Email = ({ isHome }) => (
41 |   
42 |     
43 |       {email}
44 |     
45 |   
46 | );
47 | 
48 | Email.propTypes = {
49 |   isHome: PropTypes.bool,
50 | };
51 | 
52 | export default Email;
53 | 


--------------------------------------------------------------------------------
/src/components/footer.js:
--------------------------------------------------------------------------------
  1 | import React, { useState, useEffect } from 'react';
  2 | import PropTypes from 'prop-types';
  3 | import styled from 'styled-components';
  4 | import { Icon } from '@components/icons';
  5 | import { socialMedia } from '@config';
  6 | 
  7 | const StyledFooter = styled.footer`
  8 |   ${({ theme }) => theme.mixins.flexCenter};
  9 |   flex-direction: column;
 10 |   height: auto;
 11 |   min-height: 70px;
 12 |   padding: 15px;
 13 |   text-align: center;
 14 | `;
 15 | 
 16 | const StyledSocialLinks = styled.div`
 17 |   display: none;
 18 | 
 19 |   @media (max-width: 768px) {
 20 |     display: block;
 21 |     width: 100%;
 22 |     max-width: 270px;
 23 |     margin: 0 auto 10px;
 24 |     color: var(--light-slate);
 25 |   }
 26 | 
 27 |   ul {
 28 |     ${({ theme }) => theme.mixins.flexBetween};
 29 |     padding: 0;
 30 |     margin: 0;
 31 |     list-style: none;
 32 | 
 33 |     a {
 34 |       padding: 10px;
 35 |       svg {
 36 |         width: 20px;
 37 |         height: 20px;
 38 |       }
 39 |     }
 40 |   }
 41 | `;
 42 | 
 43 | const StyledCredit = styled.div`
 44 |   color: var(--light-slate);
 45 |   font-family: var(--font-mono);
 46 |   font-size: var(--fz-xxs);
 47 |   line-height: 1;
 48 | 
 49 |   a {
 50 |     padding: 10px;
 51 |   }
 52 | 
 53 |   .github-stats {
 54 |     margin-top: 10px;
 55 | 
 56 |     & > span {
 57 |       display: inline-flex;
 58 |       align-items: center;
 59 |       margin: 0 7px;
 60 |     }
 61 |     svg {
 62 |       display: inline-block;
 63 |       margin-right: 5px;
 64 |       width: 14px;
 65 |       height: 14px;
 66 |     }
 67 |   }
 68 | `;
 69 | 
 70 | const Footer = () => {
 71 |   const [githubInfo, setGitHubInfo] = useState({
 72 |     stars: null,
 73 |     forks: null,
 74 |   });
 75 | 
 76 |   useEffect(() => {
 77 |     if (process.env.NODE_ENV !== 'production') {
 78 |       return;
 79 |     }
 80 |     fetch('https://api.github.com/repos/bchiang7/v4')
 81 |       .then(response => response.json())
 82 |       .then(json => {
 83 |         const { stargazers_count, forks_count } = json;
 84 |         setGitHubInfo({
 85 |           stars: stargazers_count,
 86 |           forks: forks_count,
 87 |         });
 88 |       })
 89 |       .catch(e => console.error(e));
 90 |   }, []);
 91 | 
 92 |   return (
 93 |     
 94 |       
 95 |         
    96 | {socialMedia && 97 | socialMedia.map(({ name, url }, i) => ( 98 |
  • 99 | 100 | 101 | 102 |
  • 103 | ))} 104 |
105 |
106 | 107 | 108 | 109 |
Designed & Built by Brittany Chiang
110 |

111 |
Revised by Parth Desai
112 | {githubInfo.stars && githubInfo.forks && ( 113 |
114 | 115 | 116 | {githubInfo.stars.toLocaleString()} 117 | 118 | 119 | 120 | {githubInfo.forks.toLocaleString()} 121 | 122 |
123 | )} 124 |
125 |
126 |
127 | ); 128 | }; 129 | 130 | Footer.propTypes = { 131 | githubInfo: PropTypes.object, 132 | }; 133 | 134 | export default Footer; 135 | -------------------------------------------------------------------------------- /src/components/head.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Helmet } from 'react-helmet'; 4 | import { useLocation } from '@reach/router'; 5 | import { useStaticQuery, graphql } from 'gatsby'; 6 | 7 | // https://www.gatsbyjs.com/docs/add-seo-component/ 8 | 9 | const Head = ({ title, description, image }) => { 10 | const { pathname } = useLocation(); 11 | 12 | const { site } = useStaticQuery( 13 | graphql` 14 | query { 15 | site { 16 | siteMetadata { 17 | defaultTitle: title 18 | defaultDescription: description 19 | siteUrl 20 | defaultImage: image 21 | twitterUsername 22 | } 23 | } 24 | } 25 | `, 26 | ); 27 | 28 | const { 29 | defaultTitle, 30 | defaultDescription, 31 | siteUrl, 32 | defaultImage, 33 | twitterUsername, 34 | } = site.siteMetadata; 35 | 36 | const seo = { 37 | title: title || defaultTitle, 38 | description: description || defaultDescription, 39 | image: `${siteUrl}${image || defaultImage}`, 40 | url: `${siteUrl}${pathname}`, 41 | }; 42 | 43 | return ( 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ); 64 | }; 65 | 66 | export default Head; 67 | 68 | Head.propTypes = { 69 | title: PropTypes.string, 70 | description: PropTypes.string, 71 | image: PropTypes.string, 72 | }; 73 | 74 | Head.defaultProps = { 75 | title: null, 76 | description: null, 77 | image: null, 78 | }; 79 | -------------------------------------------------------------------------------- /src/components/icons/appstore.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconAppStore = () => ( 4 | 11 | Apple App Store 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | 41 | 42 | 46 | 47 | 48 | 49 | ); 50 | 51 | export default IconAppStore; 52 | -------------------------------------------------------------------------------- /src/components/icons/bookmark.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconBookmark = () => ( 4 | 13 | Bookmark 14 | 15 | 16 | ); 17 | 18 | export default IconBookmark; 19 | -------------------------------------------------------------------------------- /src/components/icons/codepen.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconCodepen = () => ( 4 | 14 | CodePen 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | 23 | export default IconCodepen; 24 | -------------------------------------------------------------------------------- /src/components/icons/external.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconExternal = () => ( 4 | 14 | External Link 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | export default IconExternal; 22 | -------------------------------------------------------------------------------- /src/components/icons/folder.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconFolder = () => ( 4 | 14 | Folder 15 | 16 | 17 | ); 18 | 19 | export default IconFolder; 20 | -------------------------------------------------------------------------------- /src/components/icons/fork.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconFork = () => ( 4 | 12 | Git Fork 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | export default IconFork; 21 | -------------------------------------------------------------------------------- /src/components/icons/github.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconGitHub = () => ( 4 | 14 | GitHub 15 | 16 | 17 | ); 18 | 19 | export default IconGitHub; 20 | -------------------------------------------------------------------------------- /src/components/icons/icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | IconAppStore, 5 | IconBookmark, 6 | IconCodepen, 7 | IconExternal, 8 | IconFolder, 9 | IconFork, 10 | IconGitHub, 11 | IconInstagram, 12 | IconLinkedin, 13 | IconLoader, 14 | IconLogo, 15 | IconPlayStore, 16 | IconStar, 17 | IconTwitter, 18 | } from '@components/icons'; 19 | 20 | const Icon = ({ name }) => { 21 | switch (name) { 22 | case 'AppStore': 23 | return ; 24 | case 'Bookmark': 25 | return ; 26 | case 'Codepen': 27 | return ; 28 | case 'External': 29 | return ; 30 | case 'Folder': 31 | return ; 32 | case 'Fork': 33 | return ; 34 | case 'GitHub': 35 | return ; 36 | case 'Instagram': 37 | return ; 38 | case 'Linkedin': 39 | return ; 40 | case 'Loader': 41 | return ; 42 | case 'Logo': 43 | return ; 44 | case 'PlayStore': 45 | return ; 46 | case 'Star': 47 | return ; 48 | case 'Twitter': 49 | return ; 50 | default: 51 | return ; 52 | } 53 | }; 54 | 55 | Icon.propTypes = { 56 | name: PropTypes.string.isRequired, 57 | }; 58 | 59 | export default Icon; 60 | -------------------------------------------------------------------------------- /src/components/icons/index.js: -------------------------------------------------------------------------------- 1 | export { default as IconAppStore } from './appstore'; 2 | export { default as IconBookmark } from './bookmark'; 3 | export { default as IconCodepen } from './codepen'; 4 | export { default as IconExternal } from './external'; 5 | export { default as IconFolder } from './folder'; 6 | export { default as IconFork } from './fork'; 7 | export { default as Icon } from './icon'; 8 | export { default as IconGitHub } from './github'; 9 | export { default as IconInstagram } from './instagram'; 10 | export { default as IconLinkedin } from './linkedin'; 11 | export { default as IconLoader } from './loader'; 12 | export { default as IconLogo } from './logo'; 13 | export { default as IconPlayStore } from './playstore'; 14 | export { default as IconStar } from './star'; 15 | export { default as IconTwitter } from './twitter'; 16 | -------------------------------------------------------------------------------- /src/components/icons/instagram.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconInstagram = () => ( 4 | 14 | Instagram 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | export default IconInstagram; 22 | -------------------------------------------------------------------------------- /src/components/icons/linkedin.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconLinkedin = () => ( 4 | 14 | LinkedIn 15 | 16 | 17 | 18 | 19 | ); 20 | 21 | export default IconLinkedin; 22 | -------------------------------------------------------------------------------- /src/components/icons/loader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconLoader = () => ( 4 | 25 | ); 26 | 27 | export default IconLoader; 28 | -------------------------------------------------------------------------------- /src/components/icons/logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconLogo = () => ( 4 | 22 | ); 23 | 24 | export default IconLogo; 25 | -------------------------------------------------------------------------------- /src/components/icons/playstore.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconPlayStore = () => ( 4 | 5 | Google Play Store 6 | 14 | 15 | ); 16 | 17 | export default IconPlayStore; 18 | -------------------------------------------------------------------------------- /src/components/icons/star.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconStar = () => ( 4 | 12 | Star 13 | 14 | 15 | ); 16 | 17 | export default IconStar; 18 | -------------------------------------------------------------------------------- /src/components/icons/twitter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const IconTwitter = () => ( 4 | 14 | Twitter 15 | 16 | 17 | ); 18 | 19 | export default IconTwitter; 20 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Head } from './head'; 2 | export { default as Layout } from './layout'; 3 | export { default as Loader } from './loader'; 4 | export { default as Nav } from './nav'; 5 | export { default as Menu } from './menu'; 6 | export { default as Side } from './side'; 7 | export { default as Social } from './social'; 8 | export { default as Email } from './email'; 9 | export { default as Footer } from './footer'; 10 | export { default as Hero } from './sections/hero'; 11 | export { default as About } from './sections/about'; 12 | export { default as Jobs } from './sections/jobs'; 13 | export { default as Featured } from './sections/featured'; 14 | export { default as Projects } from './sections/projects'; 15 | export { default as Contact } from './sections/contact'; 16 | -------------------------------------------------------------------------------- /src/components/layout.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import styled, { ThemeProvider } from 'styled-components'; 4 | import { Head, Loader, Nav, Social, Email, Footer } from '@components'; 5 | import { GlobalStyle, theme } from '@styles'; 6 | 7 | const StyledContent = styled.div` 8 | display: flex; 9 | flex-direction: column; 10 | min-height: 100vh; 11 | `; 12 | 13 | const Layout = ({ children, location }) => { 14 | const isHome = location.pathname === '/'; 15 | const [isLoading, setIsLoading] = useState(isHome); 16 | 17 | // Sets target="_blank" rel="noopener noreferrer" on external links 18 | const handleExternalLinks = () => { 19 | const allLinks = Array.from(document.querySelectorAll('a')); 20 | if (allLinks.length > 0) { 21 | allLinks.forEach(link => { 22 | if (link.host !== window.location.host) { 23 | link.setAttribute('rel', 'noopener noreferrer'); 24 | link.setAttribute('target', '_blank'); 25 | } 26 | }); 27 | } 28 | }; 29 | 30 | useEffect(() => { 31 | if (isLoading) { 32 | return; 33 | } 34 | 35 | if (location.hash) { 36 | const id = location.hash.substring(1); // location.hash without the '#' 37 | setTimeout(() => { 38 | const el = document.getElementById(id); 39 | if (el) { 40 | el.scrollIntoView(); 41 | el.focus(); 42 | } 43 | }, 0); 44 | } 45 | 46 | handleExternalLinks(); 47 | }, [isLoading]); 48 | 49 | return ( 50 | <> 51 | 52 | 53 |
54 | 55 | 56 | 57 | 58 | Skip to Content 59 | 60 | 61 | {isLoading && isHome ? ( 62 | setIsLoading(false)} /> 63 | ) : ( 64 | 65 |
77 | 78 | ); 79 | }; 80 | 81 | Layout.propTypes = { 82 | children: PropTypes.node.isRequired, 83 | location: PropTypes.object.isRequired, 84 | }; 85 | 86 | export default Layout; 87 | -------------------------------------------------------------------------------- /src/components/loader.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import PropTypes from 'prop-types'; 4 | import anime from 'animejs'; 5 | import styled from 'styled-components'; 6 | import { IconLoader } from '@components/icons'; 7 | 8 | const StyledLoader = styled.div` 9 | ${({ theme }) => theme.mixins.flexCenter}; 10 | position: fixed; 11 | top: 0; 12 | bottom: 0; 13 | left: 0; 14 | right: 0; 15 | width: 100%; 16 | height: 100%; 17 | background-color: var(--dark-navy); 18 | z-index: 99; 19 | 20 | .logo-wrapper { 21 | width: max-content; 22 | max-width: 100px; 23 | transition: var(--transition); 24 | opacity: ${props => (props.isMounted ? 1 : 0)}; 25 | svg { 26 | display: block; 27 | width: 100%; 28 | height: 100%; 29 | margin: 0 auto; 30 | fill: none; 31 | user-select: none; 32 | #B { 33 | opacity: 0; 34 | } 35 | } 36 | } 37 | `; 38 | 39 | const Loader = ({ finishLoading }) => { 40 | const [isMounted, setIsMounted] = useState(false); 41 | 42 | const animate = () => { 43 | const loader = anime.timeline({ 44 | complete: () => finishLoading(), 45 | }); 46 | 47 | loader 48 | .add({ 49 | targets: '#logo path', 50 | delay: 300, 51 | duration: 1500, 52 | easing: 'easeInOutQuart', 53 | strokeDashoffset: [anime.setDashoffset, 0], 54 | }) 55 | .add({ 56 | targets: '#logo #B', 57 | duration: 700, 58 | easing: 'easeInOutQuart', 59 | opacity: 1, 60 | }) 61 | .add({ 62 | targets: '#logo', 63 | delay: 500, 64 | duration: 300, 65 | easing: 'easeInOutQuart', 66 | opacity: 0, 67 | scale: 0.1, 68 | }) 69 | .add({ 70 | targets: '.loader', 71 | duration: 200, 72 | easing: 'easeInOutQuart', 73 | opacity: 0, 74 | zIndex: -1, 75 | }); 76 | }; 77 | 78 | useEffect(() => { 79 | const timeout = setTimeout(() => setIsMounted(true), 10); 80 | animate(); 81 | return () => clearTimeout(timeout); 82 | }, []); 83 | 84 | return ( 85 | 86 | 87 | 88 |
89 | 90 |
91 |
92 | ); 93 | }; 94 | 95 | Loader.propTypes = { 96 | finishLoading: PropTypes.func.isRequired, 97 | }; 98 | 99 | export default Loader; 100 | -------------------------------------------------------------------------------- /src/components/menu.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { Helmet } from 'react-helmet'; 3 | import { Link } from 'gatsby'; 4 | import styled from 'styled-components'; 5 | import { navLinks } from '@config'; 6 | import { KEY_CODES } from '@utils'; 7 | import { useOnClickOutside } from '@hooks'; 8 | 9 | const StyledMenu = styled.div` 10 | display: none; 11 | 12 | @media (max-width: 768px) { 13 | display: block; 14 | } 15 | `; 16 | 17 | const StyledHamburgerButton = styled.button` 18 | display: none; 19 | 20 | @media (max-width: 768px) { 21 | ${({ theme }) => theme.mixins.flexCenter}; 22 | position: relative; 23 | z-index: 10; 24 | margin-right: -15px; 25 | padding: 15px; 26 | border: 0; 27 | background-color: transparent; 28 | color: inherit; 29 | text-transform: none; 30 | transition-timing-function: linear; 31 | transition-duration: 0.15s; 32 | transition-property: opacity, filter; 33 | } 34 | 35 | .ham-box { 36 | display: inline-block; 37 | position: relative; 38 | width: var(--hamburger-width); 39 | height: 24px; 40 | } 41 | 42 | .ham-box-inner { 43 | position: absolute; 44 | top: 50%; 45 | right: 0; 46 | width: var(--hamburger-width); 47 | height: 2px; 48 | border-radius: var(--border-radius); 49 | background-color: var(--green); 50 | transition-duration: 0.22s; 51 | transition-property: transform; 52 | transition-delay: ${props => (props.menuOpen ? `0.12s` : `0s`)}; 53 | transform: rotate(${props => (props.menuOpen ? `225deg` : `0deg`)}); 54 | transition-timing-function: cubic-bezier( 55 | ${props => (props.menuOpen ? `0.215, 0.61, 0.355, 1` : `0.55, 0.055, 0.675, 0.19`)} 56 | ); 57 | &:before, 58 | &:after { 59 | content: ''; 60 | display: block; 61 | position: absolute; 62 | left: auto; 63 | right: 0; 64 | width: var(--hamburger-width); 65 | height: 2px; 66 | border-radius: 4px; 67 | background-color: var(--green); 68 | transition-timing-function: ease; 69 | transition-duration: 0.15s; 70 | transition-property: transform; 71 | } 72 | &:before { 73 | width: ${props => (props.menuOpen ? `100%` : `120%`)}; 74 | top: ${props => (props.menuOpen ? `0` : `-10px`)}; 75 | opacity: ${props => (props.menuOpen ? 0 : 1)}; 76 | transition: ${({ menuOpen }) => 77 | menuOpen ? 'var(--ham-before-active)' : 'var(--ham-before)'}; 78 | } 79 | &:after { 80 | width: ${props => (props.menuOpen ? `100%` : `80%`)}; 81 | bottom: ${props => (props.menuOpen ? `0` : `-10px`)}; 82 | transform: rotate(${props => (props.menuOpen ? `-90deg` : `0`)}); 83 | transition: ${({ menuOpen }) => (menuOpen ? 'var(--ham-after-active)' : 'var(--ham-after)')}; 84 | } 85 | } 86 | `; 87 | 88 | const StyledSidebar = styled.aside` 89 | display: none; 90 | 91 | @media (max-width: 768px) { 92 | ${({ theme }) => theme.mixins.flexCenter}; 93 | position: fixed; 94 | top: 0; 95 | bottom: 0; 96 | right: 0; 97 | padding: 50px 10px; 98 | width: min(75vw, 400px); 99 | height: 100vh; 100 | outline: 0; 101 | background-color: var(--light-navy); 102 | box-shadow: -10px 0px 30px -15px var(--navy-shadow); 103 | z-index: 9; 104 | transform: translateX(${props => (props.menuOpen ? 0 : 100)}vw); 105 | visibility: ${props => (props.menuOpen ? 'visible' : 'hidden')}; 106 | transition: var(--transition); 107 | } 108 | 109 | nav { 110 | ${({ theme }) => theme.mixins.flexBetween}; 111 | width: 100%; 112 | flex-direction: column; 113 | color: var(--lightest-slate); 114 | font-family: var(--font-mono); 115 | text-align: center; 116 | } 117 | 118 | ol { 119 | padding: 0; 120 | margin: 0; 121 | list-style: none; 122 | width: 100%; 123 | 124 | li { 125 | position: relative; 126 | margin: 0 auto 20px; 127 | counter-increment: item 1; 128 | font-size: clamp(var(--fz-sm), 4vw, var(--fz-lg)); 129 | 130 | @media (max-width: 600px) { 131 | margin: 0 auto 10px; 132 | } 133 | 134 | &:before { 135 | content: '0' counter(item) '.'; 136 | display: block; 137 | margin-bottom: 5px; 138 | color: var(--green); 139 | font-size: var(--fz-sm); 140 | } 141 | } 142 | 143 | a { 144 | ${({ theme }) => theme.mixins.link}; 145 | width: 100%; 146 | padding: 3px 20px 20px; 147 | } 148 | } 149 | 150 | .resume-link { 151 | ${({ theme }) => theme.mixins.bigButton}; 152 | padding: 18px 50px; 153 | margin: 10% auto 0; 154 | width: max-content; 155 | } 156 | `; 157 | 158 | const Menu = () => { 159 | const [menuOpen, setMenuOpen] = useState(false); 160 | 161 | const toggleMenu = () => setMenuOpen(!menuOpen); 162 | 163 | const buttonRef = useRef(null); 164 | const navRef = useRef(null); 165 | 166 | let menuFocusables; 167 | let firstFocusableEl; 168 | let lastFocusableEl; 169 | 170 | const setFocusables = () => { 171 | menuFocusables = [buttonRef.current, ...Array.from(navRef.current.querySelectorAll('a'))]; 172 | firstFocusableEl = menuFocusables[0]; 173 | lastFocusableEl = menuFocusables[menuFocusables.length - 1]; 174 | }; 175 | 176 | const handleBackwardTab = e => { 177 | if (document.activeElement === firstFocusableEl) { 178 | e.preventDefault(); 179 | lastFocusableEl.focus(); 180 | } 181 | }; 182 | 183 | const handleForwardTab = e => { 184 | if (document.activeElement === lastFocusableEl) { 185 | e.preventDefault(); 186 | firstFocusableEl.focus(); 187 | } 188 | }; 189 | 190 | const onKeyDown = e => { 191 | switch (e.key) { 192 | case KEY_CODES.ESCAPE: 193 | case KEY_CODES.ESCAPE_IE11: { 194 | setMenuOpen(false); 195 | break; 196 | } 197 | 198 | case KEY_CODES.TAB: { 199 | if (menuFocusables && menuFocusables.length === 1) { 200 | e.preventDefault(); 201 | break; 202 | } 203 | if (e.shiftKey) { 204 | handleBackwardTab(e); 205 | } else { 206 | handleForwardTab(e); 207 | } 208 | break; 209 | } 210 | 211 | default: { 212 | break; 213 | } 214 | } 215 | }; 216 | 217 | const onResize = e => { 218 | if (e.currentTarget.innerWidth > 768) { 219 | setMenuOpen(false); 220 | } 221 | }; 222 | 223 | useEffect(() => { 224 | document.addEventListener('keydown', onKeyDown); 225 | window.addEventListener('resize', onResize); 226 | 227 | setFocusables(); 228 | 229 | return () => { 230 | document.removeEventListener('keydown', onKeyDown); 231 | window.removeEventListener('resize', onResize); 232 | }; 233 | }, []); 234 | 235 | const wrapperRef = useRef(); 236 | useOnClickOutside(wrapperRef, () => setMenuOpen(false)); 237 | 238 | return ( 239 | 240 | 241 | 242 | 243 | 244 |
245 | 250 |
251 |
252 |
253 | 254 | 255 | 256 | 273 | 274 |
275 | 276 | ); 277 | }; 278 | 279 | export default Menu; 280 | -------------------------------------------------------------------------------- /src/components/nav.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Link } from 'gatsby'; 3 | import PropTypes from 'prop-types'; 4 | import { CSSTransition, TransitionGroup } from 'react-transition-group'; 5 | import styled, { css } from 'styled-components'; 6 | import { navLinks } from '@config'; 7 | import { loaderDelay } from '@utils'; 8 | import { useScrollDirection, usePrefersReducedMotion } from '@hooks'; 9 | import { Menu } from '@components'; 10 | import { IconLogo } from '@components/icons'; 11 | 12 | const StyledHeader = styled.header` 13 | ${({ theme }) => theme.mixins.flexBetween}; 14 | position: fixed; 15 | top: 0; 16 | z-index: 11; 17 | padding: 0px 50px; 18 | width: 100%; 19 | height: var(--nav-height); 20 | background-color: rgba(10, 25, 47, 0.85); 21 | filter: none !important; 22 | pointer-events: auto !important; 23 | user-select: auto !important; 24 | backdrop-filter: blur(10px); 25 | transition: var(--transition); 26 | 27 | @media (max-width: 1080px) { 28 | padding: 0 40px; 29 | } 30 | @media (max-width: 768px) { 31 | padding: 0 25px; 32 | } 33 | 34 | @media (prefers-reduced-motion: no-preference) { 35 | ${props => 36 | props.scrollDirection === 'up' && 37 | !props.scrolledToTop && 38 | css` 39 | height: var(--nav-scroll-height); 40 | transform: translateY(0px); 41 | background-color: rgba(10, 25, 47, 0.85); 42 | box-shadow: 0 10px 30px -10px var(--navy-shadow); 43 | `}; 44 | 45 | ${props => 46 | props.scrollDirection === 'down' && 47 | !props.scrolledToTop && 48 | css` 49 | box-shadow: 0 10px 30px -10px var(--navy-shadow); 50 | `}; 51 | } 52 | `; 53 | 54 | const StyledNav = styled.nav` 55 | ${({ theme }) => theme.mixins.flexBetween}; 56 | position: relative; 57 | width: 100%; 58 | color: var(--lightest-slate); 59 | font-family: var(--font-mono); 60 | counter-reset: item 0; 61 | z-index: 12; 62 | 63 | .logo { 64 | ${({ theme }) => theme.mixins.flexCenter}; 65 | 66 | a { 67 | color: var(--green); 68 | width: 42px; 69 | height: 42px; 70 | 71 | &:hover, 72 | &:focus { 73 | svg { 74 | fill: var(--green-tint); 75 | } 76 | } 77 | 78 | svg { 79 | fill: none; 80 | transition: var(--transition); 81 | user-select: none; 82 | } 83 | } 84 | } 85 | `; 86 | 87 | const StyledLinks = styled.div` 88 | display: flex; 89 | align-items: center; 90 | 91 | @media (max-width: 768px) { 92 | display: none; 93 | } 94 | 95 | ol { 96 | ${({ theme }) => theme.mixins.flexBetween}; 97 | padding: 0; 98 | margin: 0; 99 | list-style: none; 100 | 101 | li { 102 | margin: 0 5px; 103 | position: relative; 104 | counter-increment: item 1; 105 | font-size: var(--fz-xs); 106 | 107 | a { 108 | padding: 10px; 109 | 110 | &:before { 111 | content: '0' counter(item) '.'; 112 | margin-right: 5px; 113 | color: var(--green); 114 | font-size: var(--fz-xxs); 115 | text-align: right; 116 | } 117 | } 118 | } 119 | } 120 | 121 | .resume-button { 122 | ${({ theme }) => theme.mixins.smallButton}; 123 | margin-left: 15px; 124 | font-size: var(--fz-xs); 125 | } 126 | `; 127 | 128 | const Nav = ({ isHome }) => { 129 | const [isMounted, setIsMounted] = useState(!isHome); 130 | const scrollDirection = useScrollDirection('down'); 131 | const [scrolledToTop, setScrolledToTop] = useState(true); 132 | const prefersReducedMotion = usePrefersReducedMotion(); 133 | 134 | const handleScroll = () => { 135 | setScrolledToTop(window.pageYOffset < 50); 136 | }; 137 | 138 | useEffect(() => { 139 | if (prefersReducedMotion) { 140 | return; 141 | } 142 | 143 | const timeout = setTimeout(() => { 144 | setIsMounted(true); 145 | }, 100); 146 | 147 | window.addEventListener('scroll', handleScroll); 148 | 149 | return () => { 150 | clearTimeout(timeout); 151 | window.removeEventListener('scroll', handleScroll); 152 | }; 153 | }, []); 154 | 155 | const timeout = isHome ? loaderDelay : 0; 156 | const fadeClass = isHome ? 'fade' : ''; 157 | const fadeDownClass = isHome ? 'fadedown' : ''; 158 | 159 | const Logo = ( 160 |
161 | {isHome ? ( 162 | 163 | 164 | 165 | ) : ( 166 | 167 | 168 | 169 | )} 170 |
171 | ); 172 | 173 | const ResumeLink = ( 174 | 175 | Resume 176 | 177 | ); 178 | 179 | return ( 180 | 181 | 182 | {prefersReducedMotion ? ( 183 | <> 184 | {Logo} 185 | 186 | 187 |
    188 | {navLinks && 189 | navLinks.map(({ url, name }, i) => ( 190 |
  1. 191 | {name} 192 |
  2. 193 | ))} 194 |
195 |
{ResumeLink}
196 |
197 | 198 | 199 | 200 | ) : ( 201 | <> 202 | 203 | {isMounted && ( 204 | 205 | <>{Logo} 206 | 207 | )} 208 | 209 | 210 | 211 |
    212 | 213 | {isMounted && 214 | navLinks && 215 | navLinks.map(({ url, name }, i) => ( 216 | 217 |
  1. 218 | {name} 219 |
  2. 220 |
    221 | ))} 222 |
    223 |
224 | 225 | 226 | {isMounted && ( 227 | 228 |
229 | {ResumeLink} 230 |
231 |
232 | )} 233 |
234 |
235 | 236 | 237 | {isMounted && ( 238 | 239 | 240 | 241 | )} 242 | 243 | 244 | )} 245 | 246 | 247 | ); 248 | }; 249 | 250 | Nav.propTypes = { 251 | isHome: PropTypes.bool, 252 | }; 253 | 254 | export default Nav; 255 | -------------------------------------------------------------------------------- /src/components/sections/about.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { StaticImage } from 'gatsby-plugin-image'; 3 | import styled from 'styled-components'; 4 | import { srConfig } from '@config'; 5 | import sr from '@utils/sr'; 6 | import { usePrefersReducedMotion } from '@hooks'; 7 | 8 | const StyledAboutSection = styled.section` 9 | max-width: 900px; 10 | 11 | .inner { 12 | display: grid; 13 | grid-template-columns: 3fr 2fr; 14 | grid-gap: 50px; 15 | 16 | @media (max-width: 768px) { 17 | display: block; 18 | } 19 | } 20 | `; 21 | const StyledText = styled.div` 22 | ul.skills-list { 23 | display: grid; 24 | grid-template-columns: repeat(2, minmax(140px, 200px)); 25 | grid-gap: 0 10px; 26 | padding: 0; 27 | margin: 20px 0 0 0; 28 | overflow: hidden; 29 | list-style: none; 30 | 31 | li { 32 | position: relative; 33 | margin-bottom: 10px; 34 | padding-left: 20px; 35 | font-family: var(--font-mono); 36 | font-size: var(--fz-xs); 37 | 38 | &:before { 39 | content: '▹'; 40 | position: absolute; 41 | left: 0; 42 | color: var(--green); 43 | font-size: var(--fz-sm); 44 | line-height: 12px; 45 | } 46 | } 47 | } 48 | `; 49 | const StyledPic = styled.div` 50 | position: relative; 51 | max-width: 300px; 52 | 53 | @media (max-width: 768px) { 54 | margin: 50px auto 0; 55 | width: 70%; 56 | } 57 | 58 | .wrapper { 59 | ${({ theme }) => theme.mixins.boxShadow}; 60 | display: block; 61 | position: relative; 62 | width: 100%; 63 | border-radius: var(--border-radius); 64 | background-color: var(--green); 65 | 66 | &:hover, 67 | &:focus { 68 | outline: 0; 69 | 70 | &:after { 71 | top: 15px; 72 | left: 15px; 73 | } 74 | 75 | .img { 76 | filter: none; 77 | mix-blend-mode: normal; 78 | } 79 | } 80 | 81 | .img { 82 | position: relative; 83 | border-radius: var(--border-radius); 84 | mix-blend-mode: multiply; 85 | filter: grayscale(100%) contrast(1); 86 | transition: var(--transition); 87 | } 88 | 89 | &:before, 90 | &:after { 91 | content: ''; 92 | display: block; 93 | position: absolute; 94 | width: 100%; 95 | height: 100%; 96 | border-radius: var(--border-radius); 97 | transition: var(--transition); 98 | } 99 | 100 | &:before { 101 | top: 0; 102 | left: 0; 103 | background-color: var(--navy); 104 | mix-blend-mode: screen; 105 | } 106 | 107 | &:after { 108 | border: 2px solid var(--green); 109 | top: 20px; 110 | left: 20px; 111 | z-index: -1; 112 | } 113 | } 114 | `; 115 | 116 | const About = () => { 117 | const revealContainer = useRef(null); 118 | const prefersReducedMotion = usePrefersReducedMotion(); 119 | 120 | useEffect(() => { 121 | if (prefersReducedMotion) { 122 | return; 123 | } 124 | 125 | sr.reveal(revealContainer.current, srConfig()); 126 | }, []); 127 | 128 | const skills = ['Python', 'Scala', 'Django', 'Flutter', 'C', 'Keras', 'Spark', 'SQL']; 129 | 130 | return ( 131 | 132 |

About Me

133 | 134 |
135 | 136 |
137 |

138 | I finished my bachelors from{' '} 139 | 140 | Nirma University 141 | {' '} 142 | in 2022. I was introduced to Data Science in my 5th semester and have been 143 | interested ever since. Besides studying and programming, I love participating in 144 | debates, extempores or general discussions. On an off day you'll find me bundled up in 145 | a corner reading something. 146 |

147 | 148 |

149 | I am always looking to learn new things. I am currently working on a few projects 150 | related to Natural Language Processing and Machine Learning. At the same 151 | time I am actively on the lookout for remote internships which I can pursue in field 152 | of Data Science. 153 |

154 | 155 |

156 | I am a strong advocate for open source and I am always interested in working on new 157 | projects with new people. Do check out my{' '} 158 | 163 | repositories 164 | {' '} 165 | and feel free to reach out on{' '} 166 | 171 | Whatsapp 172 | {' '} 173 | or{' '} 174 | 175 | email 176 | {' '} 177 | if you would like to collaborate on any project. 178 |

179 | 180 |

Here are a few technologies I’ve been working with recently:

181 |
182 | 183 |
    184 | {skills && skills.map((skill, i) =>
  • {skill}
  • )} 185 |
186 |
187 | 188 | 189 |
190 | 198 |
199 |
200 |
201 |
202 | ); 203 | }; 204 | 205 | export default About; 206 | -------------------------------------------------------------------------------- /src/components/sections/contact.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import styled from 'styled-components'; 3 | import { srConfig, email } from '@config'; 4 | import sr from '@utils/sr'; 5 | import { usePrefersReducedMotion } from '@hooks'; 6 | 7 | const StyledContactSection = styled.section` 8 | max-width: 600px; 9 | margin: 0 auto 100px; 10 | text-align: center; 11 | 12 | @media (max-width: 768px) { 13 | margin: 0 auto 50px; 14 | } 15 | 16 | .overline { 17 | display: block; 18 | margin-bottom: 20px; 19 | color: var(--green); 20 | font-family: var(--font-mono); 21 | font-size: var(--fz-md); 22 | font-weight: 400; 23 | 24 | &:before { 25 | bottom: 0; 26 | font-size: var(--fz-sm); 27 | } 28 | 29 | &:after { 30 | display: none; 31 | } 32 | } 33 | 34 | .title { 35 | font-size: clamp(40px, 5vw, 60px); 36 | } 37 | 38 | .email-link { 39 | ${({ theme }) => theme.mixins.bigButton}; 40 | margin-top: 50px; 41 | } 42 | `; 43 | 44 | const Contact = () => { 45 | const revealContainer = useRef(null); 46 | const prefersReducedMotion = usePrefersReducedMotion(); 47 | 48 | useEffect(() => { 49 | if (prefersReducedMotion) { 50 | return; 51 | } 52 | 53 | sr.reveal(revealContainer.current, srConfig()); 54 | }, []); 55 | 56 | return ( 57 | 58 |

What’s Next?

59 | 60 |

Get In Touch

61 | 62 |

63 | My inbox is always open. Whether you have a question or just want to say hello, I'll try my 64 | best to get back to you! Feel free to mail me about any relevant job oppurtunities. 65 |

66 | 67 | 68 | Say Hello 69 | 70 |
71 | ); 72 | }; 73 | 74 | export default Contact; 75 | -------------------------------------------------------------------------------- /src/components/sections/featured.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { useStaticQuery, graphql } from 'gatsby'; 3 | import { GatsbyImage, getImage } from 'gatsby-plugin-image'; 4 | import styled from 'styled-components'; 5 | import sr from '@utils/sr'; 6 | import { srConfig } from '@config'; 7 | import { Icon } from '@components/icons'; 8 | import { usePrefersReducedMotion } from '@hooks'; 9 | 10 | const StyledProjectsGrid = styled.ul` 11 | ${({ theme }) => theme.mixins.resetList}; 12 | 13 | a { 14 | position: relative; 15 | z-index: 1; 16 | } 17 | `; 18 | 19 | const StyledProject = styled.li` 20 | position: relative; 21 | display: grid; 22 | grid-gap: 10px; 23 | grid-template-columns: repeat(12, 1fr); 24 | align-items: center; 25 | 26 | @media (max-width: 768px) { 27 | ${({ theme }) => theme.mixins.boxShadow}; 28 | } 29 | 30 | &:not(:last-of-type) { 31 | margin-bottom: 100px; 32 | 33 | @media (max-width: 768px) { 34 | margin-bottom: 70px; 35 | } 36 | 37 | @media (max-width: 480px) { 38 | margin-bottom: 30px; 39 | } 40 | } 41 | 42 | &:nth-of-type(odd) { 43 | .project-content { 44 | grid-column: 7 / -1; 45 | text-align: right; 46 | 47 | @media (max-width: 1080px) { 48 | grid-column: 5 / -1; 49 | } 50 | @media (max-width: 768px) { 51 | grid-column: 1 / -1; 52 | padding: 40px 40px 30px; 53 | text-align: left; 54 | } 55 | @media (max-width: 480px) { 56 | padding: 25px 25px 20px; 57 | } 58 | } 59 | .project-tech-list { 60 | justify-content: flex-end; 61 | 62 | @media (max-width: 768px) { 63 | justify-content: flex-start; 64 | } 65 | 66 | li { 67 | margin: 0 0 5px 20px; 68 | 69 | @media (max-width: 768px) { 70 | margin: 0 10px 5px 0; 71 | } 72 | } 73 | } 74 | .project-links { 75 | justify-content: flex-end; 76 | margin-left: 0; 77 | margin-right: -10px; 78 | 79 | @media (max-width: 768px) { 80 | justify-content: flex-start; 81 | margin-left: -10px; 82 | margin-right: 0; 83 | } 84 | } 85 | .project-image { 86 | grid-column: 1 / 8; 87 | 88 | @media (max-width: 768px) { 89 | grid-column: 1 / -1; 90 | } 91 | } 92 | } 93 | 94 | .project-content { 95 | position: relative; 96 | grid-column: 1 / 7; 97 | grid-row: 1 / -1; 98 | 99 | @media (max-width: 1080px) { 100 | grid-column: 1 / 9; 101 | } 102 | 103 | @media (max-width: 768px) { 104 | display: flex; 105 | flex-direction: column; 106 | justify-content: center; 107 | height: 100%; 108 | grid-column: 1 / -1; 109 | padding: 40px 40px 30px; 110 | z-index: 5; 111 | } 112 | 113 | @media (max-width: 480px) { 114 | padding: 30px 25px 20px; 115 | } 116 | } 117 | 118 | .project-overline { 119 | margin: 10px 0; 120 | color: var(--green); 121 | font-family: var(--font-mono); 122 | font-size: var(--fz-xs); 123 | font-weight: 400; 124 | } 125 | 126 | .project-title { 127 | color: var(--lightest-slate); 128 | font-size: clamp(24px, 5vw, 28px); 129 | 130 | @media (min-width: 768px) { 131 | margin: 0 0 20px; 132 | } 133 | 134 | @media (max-width: 768px) { 135 | color: var(--white); 136 | 137 | a { 138 | position: static; 139 | 140 | &:before { 141 | content: ''; 142 | display: block; 143 | position: absolute; 144 | z-index: 0; 145 | width: 100%; 146 | height: 100%; 147 | top: 0; 148 | left: 0; 149 | } 150 | } 151 | } 152 | } 153 | 154 | .project-description { 155 | ${({ theme }) => theme.mixins.boxShadow}; 156 | position: relative; 157 | z-index: 2; 158 | padding: 25px; 159 | border-radius: var(--border-radius); 160 | background-color: var(--light-navy); 161 | color: var(--light-slate); 162 | font-size: var(--fz-lg); 163 | 164 | @media (max-width: 768px) { 165 | padding: 20px 0; 166 | background-color: transparent; 167 | box-shadow: none; 168 | 169 | &:hover { 170 | box-shadow: none; 171 | } 172 | } 173 | 174 | a { 175 | ${({ theme }) => theme.mixins.inlineLink}; 176 | } 177 | 178 | strong { 179 | color: var(--white); 180 | font-weight: normal; 181 | } 182 | } 183 | 184 | .project-tech-list { 185 | display: flex; 186 | flex-wrap: wrap; 187 | position: relative; 188 | z-index: 2; 189 | margin: 25px 0 10px; 190 | padding: 0; 191 | list-style: none; 192 | 193 | li { 194 | margin: 0 20px 5px 0; 195 | color: var(--light-slate); 196 | font-family: var(--font-mono); 197 | font-size: var(--fz-xs); 198 | white-space: nowrap; 199 | } 200 | 201 | @media (max-width: 768px) { 202 | margin: 10px 0; 203 | 204 | li { 205 | margin: 0 10px 5px 0; 206 | color: var(--lightest-slate); 207 | } 208 | } 209 | } 210 | 211 | .project-links { 212 | display: flex; 213 | align-items: center; 214 | position: relative; 215 | margin-top: 10px; 216 | margin-left: -10px; 217 | color: var(--lightest-slate); 218 | 219 | a { 220 | ${({ theme }) => theme.mixins.flexCenter}; 221 | padding: 10px; 222 | 223 | &.external { 224 | svg { 225 | width: 22px; 226 | height: 22px; 227 | margin-top: -4px; 228 | } 229 | } 230 | 231 | svg { 232 | width: 20px; 233 | height: 20px; 234 | } 235 | } 236 | 237 | .cta { 238 | ${({ theme }) => theme.mixins.smallButton}; 239 | margin: 10px; 240 | } 241 | } 242 | 243 | .project-image { 244 | ${({ theme }) => theme.mixins.boxShadow}; 245 | grid-column: 6 / -1; 246 | grid-row: 1 / -1; 247 | position: relative; 248 | z-index: 1; 249 | 250 | @media (max-width: 768px) { 251 | grid-column: 1 / -1; 252 | height: 100%; 253 | opacity: 0.25; 254 | } 255 | 256 | a { 257 | width: 100%; 258 | height: 100%; 259 | background-color: var(--green); 260 | border-radius: var(--border-radius); 261 | vertical-align: middle; 262 | 263 | &:hover, 264 | &:focus { 265 | background: transparent; 266 | outline: 0; 267 | 268 | &:before, 269 | .img { 270 | background: transparent; 271 | filter: none; 272 | } 273 | } 274 | 275 | &:before { 276 | content: ''; 277 | position: absolute; 278 | width: 100%; 279 | height: 100%; 280 | top: 0; 281 | left: 0; 282 | right: 0; 283 | bottom: 0; 284 | z-index: 3; 285 | transition: var(--transition); 286 | background-color: var(--navy); 287 | mix-blend-mode: screen; 288 | } 289 | } 290 | 291 | .img { 292 | border-radius: var(--border-radius); 293 | mix-blend-mode: multiply; 294 | filter: grayscale(100%) contrast(1) brightness(90%); 295 | 296 | @media (max-width: 768px) { 297 | object-fit: cover; 298 | width: auto; 299 | height: 100%; 300 | filter: grayscale(100%) contrast(1) brightness(50%); 301 | } 302 | } 303 | } 304 | `; 305 | 306 | const Featured = () => { 307 | const data = useStaticQuery(graphql` 308 | { 309 | featured: allMarkdownRemark( 310 | filter: { fileAbsolutePath: { regex: "/content/featured/" } } 311 | sort: { fields: [frontmatter___date], order: ASC } 312 | ) { 313 | edges { 314 | node { 315 | frontmatter { 316 | title 317 | cover { 318 | childImageSharp { 319 | gatsbyImageData(width: 700, placeholder: BLURRED, formats: [AUTO, WEBP, AVIF]) 320 | } 321 | } 322 | tech 323 | github 324 | external 325 | cta 326 | } 327 | html 328 | } 329 | } 330 | } 331 | } 332 | `); 333 | 334 | const featuredProjects = data.featured.edges.filter(({ node }) => node); 335 | const revealTitle = useRef(null); 336 | const revealProjects = useRef([]); 337 | const prefersReducedMotion = usePrefersReducedMotion(); 338 | 339 | useEffect(() => { 340 | if (prefersReducedMotion) { 341 | return; 342 | } 343 | 344 | sr.reveal(revealTitle.current, srConfig()); 345 | revealProjects.current.forEach((ref, i) => sr.reveal(ref, srConfig(i * 100))); 346 | }, []); 347 | 348 | return ( 349 |
350 |

351 | Some Things I’ve Built 352 |

353 | 354 | 355 | {featuredProjects && 356 | featuredProjects.map(({ node }, i) => { 357 | const { frontmatter, html } = node; 358 | const { external, title, tech, github, cover, cta } = frontmatter; 359 | const image = getImage(cover); 360 | 361 | return ( 362 | (revealProjects.current[i] = el)}> 363 |
364 |
365 |

Featured Project

366 | 367 |

368 | {title} 369 |

370 | 371 |
375 | 376 | {tech.length && ( 377 |
    378 | {tech.map((tech, i) => ( 379 |
  • {tech}
  • 380 | ))} 381 |
382 | )} 383 | 384 |
385 | {cta && ( 386 | 387 | Learn More 388 | 389 | )} 390 | {github && ( 391 | 392 | 393 | 394 | )} 395 | {external && !cta && ( 396 | 397 | 398 | 399 | )} 400 |
401 |
402 |
403 | 404 |
405 | 406 | 407 | 408 |
409 | 410 | ); 411 | })} 412 | 413 |
414 | ); 415 | }; 416 | 417 | export default Featured; 418 | -------------------------------------------------------------------------------- /src/components/sections/hero.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { CSSTransition, TransitionGroup } from 'react-transition-group'; 3 | import styled from 'styled-components'; 4 | import { navDelay, loaderDelay } from '@utils'; 5 | import { usePrefersReducedMotion } from '@hooks'; 6 | // import { email } from '@config'; 7 | 8 | const StyledHeroSection = styled.section` 9 | ${({ theme }) => theme.mixins.flexCenter}; 10 | flex-direction: column; 11 | align-items: flex-start; 12 | min-height: 100vh; 13 | padding: 0; 14 | 15 | @media (max-width: 480px) and (min-height: 700px) { 16 | padding-bottom: 10vh; 17 | } 18 | 19 | h1 { 20 | margin: 0 0 30px 4px; 21 | color: var(--green); 22 | font-family: var(--font-mono); 23 | font-size: clamp(var(--fz-sm), 5vw, var(--fz-md)); 24 | font-weight: 400; 25 | 26 | @media (max-width: 480px) { 27 | margin: 0 0 20px 2px; 28 | } 29 | } 30 | 31 | h3 { 32 | margin-top: 10px; 33 | color: var(--slate); 34 | line-height: 0.9; 35 | } 36 | 37 | p { 38 | margin: 20px 0 0; 39 | max-width: 540px; 40 | } 41 | 42 | .email-link { 43 | ${({ theme }) => theme.mixins.bigButton}; 44 | margin-top: 50px; 45 | } 46 | `; 47 | 48 | const Hero = () => { 49 | const [isMounted, setIsMounted] = useState(false); 50 | const prefersReducedMotion = usePrefersReducedMotion(); 51 | 52 | useEffect(() => { 53 | if (prefersReducedMotion) { 54 | return; 55 | } 56 | 57 | const timeout = setTimeout(() => setIsMounted(true), navDelay); 58 | return () => clearTimeout(timeout); 59 | }, []); 60 | 61 | const one =

Hi, my name is

; 62 | const two =

Parth Desai.

; 63 | const three =

I design and code simple things.

; 64 | const four = ( 65 | <> 66 |

67 | I am an India based Data Engineer with a bachelors in Computer Science. I am passionate 68 | about Data Science and Automation. I am also fascinated with Mathematics and wish to make a 69 | career out of it someday. Currently, I’m focused on building data pipelines and automating 70 | them at{' '} 71 | 76 | Accenture 77 | 78 | . 79 |

80 | 81 | ); 82 | const five = ( 83 | 89 | Contact me 90 | 91 | ); 92 | 93 | const items = [one, two, three, four, five]; 94 | 95 | return ( 96 | 97 | {prefersReducedMotion ? ( 98 | <> 99 | {items.map((item, i) => ( 100 |
{item}
101 | ))} 102 | 103 | ) : ( 104 | 105 | {isMounted && 106 | items.map((item, i) => ( 107 | 108 |
{item}
109 |
110 | ))} 111 |
112 | )} 113 |
114 | ); 115 | }; 116 | 117 | export default Hero; 118 | -------------------------------------------------------------------------------- /src/components/sections/jobs.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | import { useStaticQuery, graphql } from 'gatsby'; 3 | import { CSSTransition } from 'react-transition-group'; 4 | import styled from 'styled-components'; 5 | import { srConfig } from '@config'; 6 | import { KEY_CODES } from '@utils'; 7 | import sr from '@utils/sr'; 8 | import { usePrefersReducedMotion } from '@hooks'; 9 | 10 | const StyledJobsSection = styled.section` 11 | max-width: 700px; 12 | 13 | .inner { 14 | display: flex; 15 | 16 | @media (max-width: 600px) { 17 | display: block; 18 | } 19 | 20 | // Prevent container from jumping 21 | @media (min-width: 700px) { 22 | min-height: 340px; 23 | } 24 | } 25 | `; 26 | 27 | const StyledTabList = styled.div` 28 | position: relative; 29 | z-index: 3; 30 | width: max-content; 31 | padding: 0; 32 | margin: 0; 33 | list-style: none; 34 | 35 | @media (max-width: 600px) { 36 | display: flex; 37 | overflow-x: auto; 38 | width: calc(100% + 100px); 39 | padding-left: 50px; 40 | margin-left: -50px; 41 | margin-bottom: 30px; 42 | } 43 | @media (max-width: 480px) { 44 | width: calc(100% + 50px); 45 | padding-left: 25px; 46 | margin-left: -25px; 47 | } 48 | 49 | li { 50 | &:first-of-type { 51 | @media (max-width: 600px) { 52 | margin-left: 50px; 53 | } 54 | @media (max-width: 480px) { 55 | margin-left: 25px; 56 | } 57 | } 58 | &:last-of-type { 59 | @media (max-width: 600px) { 60 | padding-right: 50px; 61 | } 62 | @media (max-width: 480px) { 63 | padding-right: 25px; 64 | } 65 | } 66 | } 67 | `; 68 | 69 | const StyledTabButton = styled.button` 70 | ${({ theme }) => theme.mixins.link}; 71 | display: flex; 72 | align-items: center; 73 | width: 100%; 74 | height: var(--tab-height); 75 | padding: 0 20px 2px; 76 | border-left: 2px solid var(--lightest-navy); 77 | background-color: transparent; 78 | color: ${({ isActive }) => (isActive ? 'var(--green)' : 'var(--slate)')}; 79 | font-family: var(--font-mono); 80 | font-size: var(--fz-xs); 81 | text-align: left; 82 | white-space: nowrap; 83 | 84 | @media (max-width: 768px) { 85 | padding: 0 15px 2px; 86 | } 87 | @media (max-width: 600px) { 88 | ${({ theme }) => theme.mixins.flexCenter}; 89 | min-width: 120px; 90 | padding: 0 15px; 91 | border-left: 0; 92 | border-bottom: 2px solid var(--lightest-navy); 93 | text-align: center; 94 | } 95 | 96 | &:hover, 97 | &:focus { 98 | background-color: var(--light-navy); 99 | } 100 | `; 101 | 102 | const StyledHighlight = styled.div` 103 | position: absolute; 104 | top: 0; 105 | left: 0; 106 | z-index: 10; 107 | width: 2px; 108 | height: var(--tab-height); 109 | border-radius: var(--border-radius); 110 | background: var(--green); 111 | transform: translateY(calc(${({ activeTabId }) => activeTabId} * var(--tab-height))); 112 | transition: transform 0.25s cubic-bezier(0.645, 0.045, 0.355, 1); 113 | transition-delay: 0.1s; 114 | 115 | @media (max-width: 600px) { 116 | top: auto; 117 | bottom: 0; 118 | width: 100%; 119 | max-width: var(--tab-width); 120 | height: 2px; 121 | margin-left: 50px; 122 | transform: translateX(calc(${({ activeTabId }) => activeTabId} * var(--tab-width))); 123 | } 124 | @media (max-width: 480px) { 125 | margin-left: 25px; 126 | } 127 | `; 128 | 129 | const StyledTabPanels = styled.div` 130 | position: relative; 131 | width: 100%; 132 | margin-left: 20px; 133 | 134 | @media (max-width: 600px) { 135 | margin-left: 0; 136 | } 137 | `; 138 | 139 | const StyledTabPanel = styled.div` 140 | width: 100%; 141 | height: auto; 142 | padding: 10px 5px; 143 | 144 | ul { 145 | ${({ theme }) => theme.mixins.fancyList}; 146 | } 147 | 148 | h3 { 149 | margin-bottom: 2px; 150 | font-size: var(--fz-xxl); 151 | font-weight: 500; 152 | line-height: 1.3; 153 | 154 | .company { 155 | color: var(--green); 156 | } 157 | } 158 | 159 | .range { 160 | margin-bottom: 25px; 161 | color: var(--light-slate); 162 | font-family: var(--font-mono); 163 | font-size: var(--fz-xs); 164 | } 165 | `; 166 | 167 | const Jobs = () => { 168 | const data = useStaticQuery(graphql` 169 | query { 170 | jobs: allMarkdownRemark( 171 | filter: { fileAbsolutePath: { regex: "/content/jobs/" } } 172 | sort: { fields: [frontmatter___date], order: DESC } 173 | ) { 174 | edges { 175 | node { 176 | frontmatter { 177 | title 178 | company 179 | location 180 | range 181 | url 182 | } 183 | html 184 | } 185 | } 186 | } 187 | } 188 | `); 189 | 190 | const jobsData = data.jobs.edges; 191 | 192 | const [activeTabId, setActiveTabId] = useState(0); 193 | const [tabFocus, setTabFocus] = useState(null); 194 | const tabs = useRef([]); 195 | const revealContainer = useRef(null); 196 | const prefersReducedMotion = usePrefersReducedMotion(); 197 | 198 | useEffect(() => { 199 | if (prefersReducedMotion) { 200 | return; 201 | } 202 | 203 | sr.reveal(revealContainer.current, srConfig()); 204 | }, []); 205 | 206 | const focusTab = () => { 207 | if (tabs.current[tabFocus]) { 208 | tabs.current[tabFocus].focus(); 209 | return; 210 | } 211 | // If we're at the end, go to the start 212 | if (tabFocus >= tabs.current.length) { 213 | setTabFocus(0); 214 | } 215 | // If we're at the start, move to the end 216 | if (tabFocus < 0) { 217 | setTabFocus(tabs.current.length - 1); 218 | } 219 | }; 220 | 221 | // Only re-run the effect if tabFocus changes 222 | useEffect(() => focusTab(), [tabFocus]); 223 | 224 | // Focus on tabs when using up & down arrow keys 225 | const onKeyDown = e => { 226 | switch (e.key) { 227 | case KEY_CODES.ARROW_UP: { 228 | e.preventDefault(); 229 | setTabFocus(tabFocus - 1); 230 | break; 231 | } 232 | 233 | case KEY_CODES.ARROW_DOWN: { 234 | e.preventDefault(); 235 | setTabFocus(tabFocus + 1); 236 | break; 237 | } 238 | 239 | default: { 240 | break; 241 | } 242 | } 243 | }; 244 | 245 | return ( 246 | 247 |

Where I’ve Worked

248 | 249 |
250 | onKeyDown(e)}> 251 | {jobsData && 252 | jobsData.map(({ node }, i) => { 253 | const { company } = node.frontmatter; 254 | return ( 255 | setActiveTabId(i)} 259 | ref={el => (tabs.current[i] = el)} 260 | id={`tab-${i}`} 261 | role="tab" 262 | tabIndex={activeTabId === i ? '0' : '-1'} 263 | aria-selected={activeTabId === i ? true : false} 264 | aria-controls={`panel-${i}`}> 265 | {company} 266 | 267 | ); 268 | })} 269 | 270 | 271 | 272 | 273 | {jobsData && 274 | jobsData.map(({ node }, i) => { 275 | const { frontmatter, html } = node; 276 | const { title, url, company, range } = frontmatter; 277 | 278 | return ( 279 | 280 |