├── public ├── CNAME ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── src ├── assets │ ├── fonts │ │ ├── Menlo-Regular.ttf │ │ ├── SF-Pro-Display-Bold.otf │ │ ├── SF-Pro-Display-Medium.otf │ │ └── SF-Pro-Display-Regular.otf │ ├── images │ │ ├── ap_code_icon.png │ │ ├── portfolio │ │ │ ├── chess-1.png │ │ │ ├── invesddit-1.png │ │ │ ├── taasskkrr-1.png │ │ │ ├── sudoku-solver-1.png │ │ │ └── inventory-tracker-1.png │ │ ├── gitignore │ │ │ └── gitignore-1.jpeg │ │ └── file_types │ │ │ ├── favicon.svg │ │ │ ├── html.svg │ │ │ ├── markdown.svg │ │ │ ├── default.svg │ │ │ ├── css.svg │ │ │ ├── image.svg │ │ │ ├── javascript.svg │ │ │ ├── ruby.svg │ │ │ ├── fileIcons.js │ │ │ ├── git_ignore.svg │ │ │ ├── info.svg │ │ │ └── json.svg │ └── documents │ │ └── Ayrton_Parkinson_CV.pdf ├── contexts │ ├── level │ │ └── LevelContext.js │ └── tab │ │ ├── TabContext.js │ │ └── TabProvider.js ├── hooks │ ├── useLevelContext.js │ ├── useToggle.js │ ├── useTabContext.js │ └── useHorizontalScroll.js ├── components │ ├── portfolio │ │ ├── Project.js │ │ ├── ProjectPreview.js │ │ └── ProjectText.js │ ├── FileIcon.js │ ├── File.js │ ├── GitIgnore.js │ ├── LineBreak.js │ ├── Section.js │ ├── sidebar │ │ ├── Sidebar.js │ │ ├── DirectoryList.js │ │ ├── Drawer.js │ │ ├── DirectoryItem.js │ │ └── Directory.js │ ├── Comment.js │ ├── KeyboardIcon.js │ ├── Text.js │ ├── MarkdownHeader.js │ ├── TabLink.js │ ├── MarkdownButton.js │ ├── Link.js │ ├── Navbar.js │ ├── tabs │ │ ├── TabBar.js │ │ └── Tab.js │ ├── NoTabs.js │ ├── Contact.js │ ├── IndexCSS.js │ ├── Home.js │ ├── About.js │ ├── Dashboard.js │ └── Portfolio.js ├── index.js ├── App.js ├── utils │ └── directory.js └── index.css ├── .gitignore ├── package.json ├── tailwind.config.js └── README.md /public/CNAME: -------------------------------------------------------------------------------- 1 | ayrtonparkinson.dev -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/assets/fonts/Menlo-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/fonts/Menlo-Regular.ttf -------------------------------------------------------------------------------- /src/assets/images/ap_code_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/ap_code_icon.png -------------------------------------------------------------------------------- /src/assets/fonts/SF-Pro-Display-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/fonts/SF-Pro-Display-Bold.otf -------------------------------------------------------------------------------- /src/assets/images/portfolio/chess-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/portfolio/chess-1.png -------------------------------------------------------------------------------- /src/contexts/level/LevelContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const LevelContext = createContext(0); 4 | -------------------------------------------------------------------------------- /src/assets/fonts/SF-Pro-Display-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/fonts/SF-Pro-Display-Medium.otf -------------------------------------------------------------------------------- /src/assets/fonts/SF-Pro-Display-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/fonts/SF-Pro-Display-Regular.otf -------------------------------------------------------------------------------- /src/assets/images/portfolio/invesddit-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/portfolio/invesddit-1.png -------------------------------------------------------------------------------- /src/assets/images/portfolio/taasskkrr-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/portfolio/taasskkrr-1.png -------------------------------------------------------------------------------- /src/assets/documents/Ayrton_Parkinson_CV.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/documents/Ayrton_Parkinson_CV.pdf -------------------------------------------------------------------------------- /src/assets/images/gitignore/gitignore-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/gitignore/gitignore-1.jpeg -------------------------------------------------------------------------------- /src/assets/images/portfolio/sudoku-solver-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/portfolio/sudoku-solver-1.png -------------------------------------------------------------------------------- /src/contexts/tab/TabContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const TabContext = createContext(); 4 | export default TabContext; 5 | -------------------------------------------------------------------------------- /src/assets/images/portfolio/inventory-tracker-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayrt-n/portfolio/HEAD/src/assets/images/portfolio/inventory-tracker-1.png -------------------------------------------------------------------------------- /src/assets/images/file_types/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/html.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/hooks/useLevelContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { LevelContext } from '../contexts/level/LevelContext'; 3 | 4 | export default function useLevelContext() { 5 | return useContext(LevelContext); 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/images/file_types/markdown.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/default.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/hooks/useToggle.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react'; 2 | 3 | export function useToggle(initial=false) { 4 | const [state, setState] = useState(initial); 5 | const toggle = useCallback(() => setState(prev => !prev), []); 6 | 7 | return [state, toggle]; 8 | } -------------------------------------------------------------------------------- /src/components/portfolio/Project.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Project({ children }) { 4 | return ( 5 |
6 | {children} 7 |
8 | ) 9 | } 10 | 11 | export default Project; 12 | -------------------------------------------------------------------------------- /src/assets/images/file_types/css.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/image.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/FileIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { fileIcons } from '../assets/images/file_types/fileIcons'; 3 | 4 | function FileIcon({ fileType="defaultIcon" }) { 5 | return ( 6 | 7 | ); 8 | } 9 | 10 | export default FileIcon; 11 | -------------------------------------------------------------------------------- /src/components/portfolio/ProjectPreview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function ProjectPreview({ src, alt, ...props }) { 4 | return ( 5 | {alt} 6 | ); 7 | } 8 | 9 | export default ProjectPreview; 10 | -------------------------------------------------------------------------------- /src/components/File.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import FileIcon from './FileIcon'; 3 | 4 | function File({ name, extension }) { 5 | return ( 6 |
7 | 8 | {name}.{extension} 9 |
10 | ); 11 | } 12 | 13 | export default File; 14 | -------------------------------------------------------------------------------- /src/components/GitIgnore.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import gitignoreMeme from '../assets/images/gitignore/gitignore-1.jpeg' 3 | 4 | function GitIgnore() { 5 | return ( 6 |
7 | gitignore meme 8 |
9 | ); 10 | } 11 | 12 | export default GitIgnore; 13 | -------------------------------------------------------------------------------- /src/hooks/useTabContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import TabContext from '../contexts/tab/TabContext'; 3 | 4 | export default function useTabContext() { 5 | const tabs = useContext(TabContext) 6 | 7 | if (!tabs) { 8 | throw new Error('useTabContext must be used within TabProvider') 9 | } 10 | 11 | return tabs; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/LineBreak.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | 4 | function LineBreak({ className }) { 5 | const defaultClasses = "border-dark-200 my-3" 6 | const mergedClasses = classNames(defaultClasses, className); 7 | 8 | return ( 9 |
10 | ) 11 | } 12 | 13 | export default LineBreak; 14 | -------------------------------------------------------------------------------- /src/components/Section.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | 4 | function Section({ className, children }) { 5 | const defaultClasses = "space-y-3 mb-8" 6 | const mergedClasses = classNames(defaultClasses, className); 7 | 8 | return ( 9 |
10 | {children} 11 |
12 | ); 13 | } 14 | 15 | export default Section; 16 | -------------------------------------------------------------------------------- /src/components/sidebar/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function Sidebar({ open, children }) { 4 | return ( 5 |
6 |
7 | {children} 8 |
9 |
10 | ); 11 | } 12 | 13 | export default Sidebar; 14 | -------------------------------------------------------------------------------- /src/components/Comment.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React from 'react'; 3 | import Text from './Text'; 4 | 5 | function Comment({ className, children, ...props }) { 6 | const mergedClasses = classNames("text-comment", className); 7 | 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | } 14 | 15 | export default Comment; 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | import './index.css'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/components/KeyboardIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function KeyboardIcon({ children }) { 4 | return ( 5 |
6 | {children} 7 | 8 |
9 | ); 10 | } 11 | 12 | export default KeyboardIcon; 13 | -------------------------------------------------------------------------------- /src/components/Text.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React from 'react'; 3 | 4 | function Text({ as, className, children }) { 5 | const TextTag = as || 'p' 6 | 7 | const defaultClasses = "break-words" 8 | const mergedClasses = classNames(defaultClasses, className); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | 17 | export default Text; 18 | -------------------------------------------------------------------------------- /src/assets/images/file_types/javascript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/ruby.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/fileIcons.js: -------------------------------------------------------------------------------- 1 | import css from './css.svg'; 2 | import defaultIcon from './default.svg'; 3 | import ico from './favicon.svg'; 4 | import gitignore from './git_ignore.svg'; 5 | import html from './html.svg'; 6 | import md from './info.svg'; 7 | import js from './javascript.svg'; 8 | import rb from './ruby.svg'; 9 | 10 | export const fileIcons = { 11 | css, 12 | defaultIcon, 13 | ico, 14 | gitignore, 15 | html, 16 | js, 17 | md, 18 | rb, 19 | } 20 | -------------------------------------------------------------------------------- /src/components/MarkdownHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | 4 | function MarkdownHeader({ headingLevel, className, text }) { 5 | const Tag = `h${headingLevel}`; 6 | 7 | const defaultClasses = "text-purple"; 8 | const mergedClasses = classNames(defaultClasses, className); 9 | 10 | return ( 11 | 12 | {`${"#".repeat(headingLevel)} ${text}`} 13 | 14 | ); 15 | } 16 | 17 | export default MarkdownHeader; 18 | -------------------------------------------------------------------------------- /src/components/TabLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useTabContext from '../hooks/useTabContext'; 3 | 4 | function TabLink({ tab, className, children }) { 5 | const tabs = useTabContext(); 6 | 7 | const handleClick = () => { 8 | tabs.addTab({name: tab.name, extension: tab.extension, component: tab.component }); 9 | }; 10 | 11 | return ( 12 | 15 | ); 16 | } 17 | 18 | export default TabLink; 19 | -------------------------------------------------------------------------------- /src/components/sidebar/DirectoryList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Directroy from './Directory'; 3 | import DirectoryItem from './DirectoryItem'; 4 | 5 | function DirectoryList({ directory, callback }) { 6 | const list = directory.map(item => { 7 | if (item.type === 'folder') return 8 | 9 | return 10 | }); 11 | 12 | return (<>{list}); 13 | } 14 | 15 | export default DirectoryList; 16 | -------------------------------------------------------------------------------- /src/components/MarkdownButton.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React from 'react'; 3 | 4 | function MarkdownButton({ as, children, className, ...props }) { 5 | const ButtonTag = as || 'button'; 6 | 7 | const defaultClasses = 'font-bold text-green hover:bg-green-highlight outline-none focus:bg-green-highlight'; 8 | const mergedClasses = classNames(defaultClasses, className); 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | 17 | export default MarkdownButton; 18 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Dashboard from './components/Dashboard'; 3 | import TabProvider from './contexts/tab/TabProvider'; 4 | import Home from './components/Home'; 5 | import About from './components/About'; 6 | 7 | function App() { 8 | const tabs = [ 9 | { name: 'Home', type: 'file', extension: 'js', component: }, 10 | { name: 'About', type: 'file', extension: 'js', component: }, 11 | ]; 12 | 13 | return ( 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "AJP Portfolio", 3 | "name": "Ayrton Parkinson Portfolio", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Link.js: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import React from 'react'; 3 | 4 | function Link({ as, className, children, targetBlank, ...props }) { 5 | const LinkTag = as || 'a' 6 | 7 | const defaultClasses = "text-blue hover:bg-blue-highlight underline outline-none focus:bg-blue-highlight" 8 | const mergedClasses = classNames(defaultClasses, className); 9 | 10 | const targetProps = targetBlank ? { target: '_blank', rel: 'noreferrer' } : {}; 11 | 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | 19 | export default Link; 20 | -------------------------------------------------------------------------------- /src/assets/images/file_types/git_ignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Bars3Icon } from '@heroicons/react/20/solid'; 3 | import TabBar from './tabs/TabBar'; 4 | 5 | function Navbar({ toggleDrawer }) { 6 | return ( 7 |
8 |
9 | 12 | 13 | 14 |
15 |
16 | ); 17 | } 18 | 19 | export default Navbar; 20 | -------------------------------------------------------------------------------- /src/hooks/useHorizontalScroll.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | export default function useHorizontalScroll() { 4 | const elRef = useRef(null); 5 | 6 | useEffect(() => { 7 | const el = elRef.current 8 | 9 | if (el) { 10 | const handleScrollWheel = (e) => { 11 | if (e.deltaY === 0) return; 12 | e.preventDefault(); 13 | 14 | el.scrollBy({ 15 | left: e.deltaY < 0 ? -50 : 50, 16 | behaviour: 'smooth' 17 | }); 18 | } 19 | 20 | el.addEventListener('wheel', handleScrollWheel); 21 | return () => el.removeEventListener('wheel', handleScrollWheel); 22 | } 23 | }, []); 24 | 25 | return elRef; 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/images/file_types/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/images/file_types/json.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/sidebar/Drawer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Bars3Icon } from '@heroicons/react/20/solid'; 3 | import { Dialog } from '@headlessui/react'; 4 | 5 | function Drawer({ isOpen, toggle, children }) { 6 | return ( 7 | 8 | 18 | ); 19 | } 20 | 21 | export default Drawer; 22 | -------------------------------------------------------------------------------- /src/components/tabs/TabBar.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import Tab from './Tab'; 3 | import useTabContext from '../../hooks/useTabContext'; 4 | import useHorizontalScroll from '../../hooks/useHorizontalScroll'; 5 | 6 | function TabBar() { 7 | const scrollRef = useHorizontalScroll(); 8 | const tabs = useTabContext(); 9 | const selectedTab = tabs.current ? tabs.current.name : null 10 | 11 | useEffect(() => { 12 | if (selectedTab) { 13 | const tab = document.querySelector(`[data-tab="${selectedTab}"]`); 14 | tab.scrollIntoView(); 15 | } 16 | }, [selectedTab]); 17 | 18 | if (tabs.tabList.length < 1) return; 19 | 20 | return ( 21 |
22 | {tabs.tabList.map((tab) => ())} 23 |
24 | ) 25 | } 26 | 27 | export default TabBar; 28 | -------------------------------------------------------------------------------- /src/components/NoTabs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import apIcon from '../assets/images/ap_code_icon.png'; 3 | import KeyboardIcon from './KeyboardIcon'; 4 | 5 | function NoTabs() { 6 | return ( 7 |
8 | 9 |
10 | Toggle Menu 11 |
12 | 13 | B 14 |
15 | Throw Confetti!! 16 |
17 | 18 | 19 | K 20 |
21 |
22 |
23 | ); 24 | } 25 | 26 | export default NoTabs; 27 | -------------------------------------------------------------------------------- /src/components/portfolio/ProjectText.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MarkdownHeader from '../MarkdownHeader'; 3 | import Section from '../Section'; 4 | import Comment from '../Comment'; 5 | import Text from '../Text'; 6 | import MarkdownButton from '../MarkdownButton'; 7 | 8 | function ProjectText({ title, technologies, description, githubLink, liveLink }) { 9 | return ( 10 |
11 | 12 | {technologies} 13 | {description} 14 |
15 | {githubLink && 16 | 17 | [GitHub] 18 | 19 | } 20 | {liveLink && 21 | 22 | [Live Preview] 23 | 24 | } 25 |
26 |
27 | ); 28 | } 29 | 30 | export default ProjectText; 31 | -------------------------------------------------------------------------------- /src/components/sidebar/DirectoryItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import File from '../File'; 3 | import useLevelContext from '../../hooks/useLevelContext'; 4 | import useTabContext from '../../hooks/useTabContext'; 5 | import classNames from 'classnames'; 6 | 7 | function DirectoryItem({ item, callback }) { 8 | const level = useLevelContext(); 9 | const paddingLeft = `${level * 10}px`; 10 | 11 | const tabs = useTabContext(); 12 | const selected = (tabs.current && tabs.current.name) === item.name 13 | const handleClick = () => { 14 | tabs.addTab({name: item.name, extension: item.extension, component: item.component }); 15 | if (callback) { callback() } 16 | }; 17 | 18 | const defaultClasses = "hover:bg-dark-400 py-1 font-medium block w-full outline-none focus:shadow-item-selected focus:z-20 relative"; 19 | const itemClass = classNames(defaultClasses, { 'bg-dark-400' : selected, 'focus:bg-white-10a': !selected }); 20 | 21 | return ( 22 | 25 | ); 26 | } 27 | 28 | export default DirectoryItem; 29 | -------------------------------------------------------------------------------- /src/utils/directory.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Home from '../components/Home'; 3 | import About from '../components/About'; 4 | import Contact from '../components/Contact'; 5 | import IndexCSS from '../components/IndexCSS'; 6 | import Portfolio from '../components/Portfolio'; 7 | 8 | const about = { name: 'About', type: 'file', extension: 'js', component: } 9 | const portfolio = { name: 'Portfolio', type: 'file', extension: 'js', component: } 10 | const home = { name: 'Home', type: 'file', extension: 'js', component: } 11 | const index = { name: 'index', type: 'file', extension: 'css', component: } 12 | const contact = { name: 'Contact', type: 'file', extension: 'md', component: } 13 | 14 | const directory = [{ 15 | name: 'ayrton_parkinson_portfolio', 16 | type: 'folder', 17 | children: [ 18 | { 19 | name: 'src', 20 | type: 'folder', 21 | children: [ 22 | { 23 | name: 'components', 24 | type: 'folder', 25 | children: [ 26 | about, 27 | portfolio, 28 | ] 29 | }, 30 | home, 31 | index, 32 | ] 33 | }, 34 | contact, 35 | ] 36 | }]; 37 | 38 | export { 39 | about, 40 | portfolio, 41 | home, 42 | index, 43 | contact, 44 | directory 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/Contact.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MarkdownHeader from './MarkdownHeader'; 3 | import LineBreak from './LineBreak'; 4 | import Section from './Section'; 5 | import Text from './Text'; 6 | import Link from './Link'; 7 | 8 | function Contact() { 9 | return ( 10 |
11 |
12 | 13 | 14 | 15 | If you're interested in reaching out and want to chat, please just reach out via any of the links below! 16 | 17 |
18 | 19 |
20 | 21 | [Email] (mailto:ayrton.parkinson1@gmail.com) 22 | 23 | 24 | [Github] (https://github.com/ayrt-n) 25 | 26 | 27 | [LinkedIn] (https://www.linkedin.com/in/ayrton-parkinson-ab6015ba/) 28 | 29 |
30 |
31 | ) 32 | } 33 | 34 | export default Contact; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "personal-portfolio-client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@headlessui/react": "^1.7.14", 7 | "@headlessui/tailwindcss": "^0.1.3", 8 | "@heroicons/react": "^2.0.18", 9 | "@testing-library/jest-dom": "^5.16.5", 10 | "@testing-library/react": "^13.4.0", 11 | "@testing-library/user-event": "^13.5.0", 12 | "classnames": "^2.3.2", 13 | "gh-pages": "^5.0.0", 14 | "js-confetti": "^0.11.0", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-router-dom": "^6.11.1", 18 | "react-scripts": "5.0.1", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "scripts": { 22 | "predeploy": "npm run build", 23 | "deploy": "gh-pages -d build", 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | }, 47 | "devDependencies": { 48 | "tailwindcss": "^3.3.2" 49 | }, 50 | "homepage": "https://ayrtonparkinson.dev" 51 | } 52 | -------------------------------------------------------------------------------- /src/components/IndexCSS.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LineBreak from './LineBreak'; 3 | import MarkdownHeader from './MarkdownHeader'; 4 | import Section from './Section'; 5 | import Text from './Text'; 6 | import Comment from './Comment'; 7 | import Link from './Link'; 8 | 9 | function IndexCSS() { 10 | return ( 11 |
12 |
13 | 14 | 15 |
16 | 17 |
18 | Helpful styling links and sources! 19 |
20 | 21 |
22 | 23 | User interface built with React + TailwindCSS + HeadlessUI 24 | 25 | 26 | Color palette / theme based off of Dracula 27 | 28 | 29 | File icons from VSCode Seti Theme 30 | 31 | 32 | Utility icons from Heroicons 33 | 34 |
35 |
36 | ); 37 | } 38 | 39 | export default IndexCSS; -------------------------------------------------------------------------------- /src/components/tabs/Tab.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classNames from 'classnames'; 3 | import File from '../File'; 4 | import useTabContext from '../../hooks/useTabContext'; 5 | 6 | function Tab({ tab }) { 7 | const tabs = useTabContext(); 8 | const selected = tabs.current.name === tab.name; 9 | 10 | const defaultClasses = "relative min-w-[175px] px-4 mr-[2px] h-full flex justify-center items-center border-t-2 border-transparent focus-within:bg-dark-500"; 11 | const selectedClasses = { 'border-t-pink': selected, 'bg-dark-500': selected, 'bg-dark-600': !selected }; 12 | const mergedClasses = classNames(defaultClasses, selectedClasses); 13 | 14 | return ( 15 | 16 | 24 | 25 | ); 26 | } 27 | 28 | export default Tab; 29 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Ayrton Parkinson | Full Stack Software Engineer 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/sidebar/Directory.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import useLevelContent from '../../hooks/useLevelContext'; 3 | import { ChevronRightIcon } from '@heroicons/react/20/solid'; 4 | import { Disclosure } from '@headlessui/react'; 5 | import classNames from 'classnames'; 6 | import { LevelContext } from '../../contexts/level/LevelContext'; 7 | import DirectoryList from './DirectoryList'; 8 | 9 | function Directroy({ directory, callback }) { 10 | const level = useLevelContent(); 11 | 12 | const paddingLeft = `${level * 10}px` 13 | const marginLeft = `${(level + 1) * 10}px` 14 | 15 | const defaultClasses = "w-full py-1 outline-none focus:shadow-item-selected focus:bg-white-10a focus:z-20 relative" 16 | const conditionalClasses = level === 0 ? "bg-dark-500 font-bold uppercase" : "hover:bg-dark-400 font-medium" 17 | const mergedClasses = classNames(defaultClasses, conditionalClasses); 18 | 19 | return ( 20 | 21 | {({ open }) => ( 22 | <> 23 | 24 | 25 | 26 | {directory.name} 27 | 28 | 29 | 30 | 31 | {level > 0 &&
} 32 | 33 | 34 | 35 | 36 | )} 37 | 38 | ); 39 | } 40 | 41 | export default Directroy; 42 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Section from './Section'; 3 | import Text from './Text'; 4 | import { about, portfolio, contact } from '../utils/directory' 5 | import LineBreak from './LineBreak'; 6 | import TabLink from './TabLink'; 7 | import Link from './Link'; 8 | 9 | function Home() { 10 | return ( 11 |
12 |
13 |

14 | Hi! I'm Ayrton! 15 |

16 |

17 | A Full Stack Software Engineer from Canada 18 | | 19 |

20 | 21 |
22 | 23 |
24 | 25 | Experienced using Ruby, Ruby on Rails, Python, JavaScript, React, and more! 26 | 27 | 28 | This website, inspired by VS Code, is an interactive overview of who I am and some the things that I've built. I encourage visitors to click around and explore (the sidebar and tabs at the top are fully functioning). 29 | 30 | 31 | For more information on my background and previous work, check out some of the helpful links below! 32 | 33 |
34 | 35 |
36 |

37 | Helpful Links 38 |

39 |
40 | About 41 | ~/background_information 42 |
43 |
44 | Portfolio 45 | ~/prior_work 46 |
47 |
48 | Contact 49 | ~/lets_chat 50 |
51 |
52 |
53 | ) 54 | } 55 | 56 | export default Home; 57 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @import url(https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&display=swap); 6 | @import url(https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap); 7 | 8 | @font-face { 9 | font-family: 'Menlo'; 10 | src: local('Menlo-Regular'), url(./assets/fonts/Menlo-Regular.ttf); 11 | } 12 | 13 | @font-face { 14 | font-family: 'SF Pro Display'; 15 | src: local('SF-Pro-Display-Regular'), url(./assets/fonts/SF-Pro-Display-Regular.otf); 16 | font-weight: 100 400; 17 | } 18 | 19 | @font-face { 20 | font-family: 'SF Pro Display'; 21 | src: local('SF-Pro-Display-Medium'), url(./assets/fonts/SF-Pro-Display-Medium.otf); 22 | font-weight: 500 600; 23 | } 24 | 25 | @font-face { 26 | font-family: 'SF Pro Display'; 27 | src: local('SF-Pro-Display-Bold'), url(./assets/fonts/SF-Pro-Display-Bold.otf); 28 | font-weight: 700 1000; 29 | } 30 | 31 | :root { 32 | --dark-700: #191A21; 33 | --dark-600: #21222C; 34 | --dark-500: #282A36; 35 | --dark-400: #3d4051; 36 | --dark-300: #44475A; 37 | } 38 | 39 | body { 40 | margin: 0; 41 | -webkit-font-smoothing: antialiased; 42 | -moz-osx-font-smoothing: grayscale; 43 | background: #282A36; 44 | color: #F8F8F2; 45 | } 46 | 47 | /* Scroll bar */ 48 | /* Firefox */ 49 | * { 50 | scrollbar-color: rgba(256, 256, 256, 0.2) var(--dark-500); 51 | } 52 | 53 | /* Chrome, Edge, and Safari */ 54 | *::-webkit-scrollbar { 55 | width: 15px; 56 | } 57 | 58 | *::-webkit-scrollbar-track { 59 | background: var(--dark-500); 60 | border-radius: 5px; 61 | } 62 | 63 | *::-webkit-scrollbar-thumb { 64 | background-color: rgba(256, 256, 256, 0.2); 65 | } 66 | 67 | /* Misc */ 68 | ::selection { 69 | background: #505262; 70 | } 71 | 72 | @keyframes blink { 73 | 50% { 74 | opacity: 0.0; 75 | } 76 | } 77 | 78 | .animate-blink { 79 | animation: blink 1s step-start 0s infinite; 80 | } 81 | 82 | .scrollbar-hide::-webkit-scrollbar { 83 | display: none; 84 | } 85 | 86 | /* For IE, Edge and Firefox */ 87 | .scrollbar-hide { 88 | -ms-overflow-style: none; /* IE and Edge */ 89 | scrollbar-width: none; /* Firefox */ 90 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./src/**/*.{js,jsx,ts,tsx}", 5 | ], 6 | theme: { 7 | extend: { 8 | 'colors': { 9 | 'dark-700': '#191A21', 10 | 'dark-600': '#21222C', 11 | 'dark-500': '#282A36', 12 | 'dark-400': '#3d4051', 13 | 'dark-300': '#44475A', 14 | 'dark-200': '#505262', 15 | 'dark-100': '#5A6795', 16 | 'comment': '#6272a4', 17 | 'white': '#F8F8F2', 18 | 'white-75a': 'hsla(0,0%,100%,.75)', 19 | 'white-50a': 'hsla(0,0%,100%,.5)', 20 | 'white-10a': 'hsla(0,0%,100%,.1)', 21 | 'white-5a': 'hsla(0,0%,100%,.05)', 22 | 'blue': '#8be9fd', 23 | 'blue-highlight': 'rgba(139, 233, 253, 0.25)', 24 | 'lightGreen': '#b1ffc5', 25 | 'green': '#50fa7b', 26 | 'green-highlight': 'rgba(80, 250, 123, 0.25)', 27 | 'red': '#ff5555', 28 | 'red-highlight': 'rgba(255, 85, 85, 0.25)', 29 | 'lightRed': '#ff6a6c', 30 | 'darkRed': '#e83538', 31 | 'orange': '#ffb86c', 32 | 'pink': '#ff79c6', 33 | 'yellow': '#f1fa8c', 34 | 'purple': '#bd93f9', 35 | 'link': '#0078D7', 36 | 'cta': '#5865F2', 37 | 'ctaDark': '#4a57e4', 38 | 'ctaLight': '#6470f3', 39 | 'dangerColor': '#ff4d4f', 40 | 'dangerBg': '#301923', 41 | 'dangerBorder': '#8c0e2e', 42 | 'successColor': '#0be881', 43 | 'successBg': '#17312a', 44 | 'successBorder': '#0d8951', 45 | 'warningColor': '#ffcf00', 46 | 'warningBg': '#302c1d', 47 | 'warningBorder': '#8e7710', 48 | 'primaryColor': '#1890ff', 49 | 'primaryBg': '#172636', 50 | 'primaryBorder': '#0d518f', 51 | }, 52 | fontFamily: { 53 | 'mono': ['Menlo', 'Fira Mono', 'Courier New'], 54 | 'sans': ['SF Pro Display', 'Roboto', 'Arial', 'sans-serif'] 55 | }, 56 | gridTemplateColumns: { 57 | 'commands': '1fr 1fr' 58 | }, 59 | boxShadow: { 60 | 'item-selected': '0 0 0 1px #4a57e4', 61 | 'project': 'rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px', 62 | } 63 | }, 64 | }, 65 | plugins: [ 66 | require('@headlessui/tailwindcss') 67 | ], 68 | } 69 | -------------------------------------------------------------------------------- /src/contexts/tab/TabProvider.js: -------------------------------------------------------------------------------- 1 | import { useState, useMemo, useCallback, useEffect } from 'react'; 2 | import TabContext from './TabContext'; 3 | 4 | function TabProvider({ initialTabs, children }) { 5 | const [tabHistory, setTabHistory] = useState(initialTabs); 6 | const [tabList, setTabList] = useState(initialTabs); 7 | 8 | // Set initial home tab name in history for handling popstate events 9 | useEffect(() => { 10 | window.history.replaceState({ tabName: 'Home' }, '', ''); 11 | }, []); 12 | 13 | // On popstate (i.e., back/forward history event) check if tab is open 14 | // If tab is not open go back further, switch to that tab 15 | useEffect(() => { 16 | const handlePop = () => { 17 | const tabName = window.history.state.tabName 18 | if (tabName) { 19 | const tab = tabList.find(t => t.name === tabName) 20 | if (tab) { 21 | setTabHistory(prev => ([tab, ...prev.filter(t => t.name !== tab.name)])); 22 | } else { 23 | window.history.back(); 24 | } 25 | } 26 | }; 27 | 28 | window.addEventListener('popstate', handlePop); 29 | return () => window.removeEventListener('popstate', handlePop); 30 | }); 31 | 32 | // Check if tab is already open (e.g., in tabList), if not add it 33 | // Then switch to that tab 34 | const addTab = useCallback((tab) => { 35 | if (!tabList.find(t => (t.name === tab.name))) { 36 | setTabList(prev => ([...prev, tab])); 37 | } 38 | 39 | switchTabs(tab); 40 | }, [tabList]); 41 | 42 | // Remove tab from tab list and tab history 43 | const removeTab = (tab) => { 44 | setTabList(prev => (prev.filter(t => t.name !== tab.name))); 45 | setTabHistory(prev => (prev.filter(t => t.name !== tab.name))); 46 | }; 47 | 48 | // Push tab name to window history to allow navigation via popstate 49 | // Then change tab history to switch to new tab 50 | const switchTabs = (tab) => { 51 | window.history.pushState({ tabName: tab.name }, '', ''); 52 | setTabHistory(prev => ([tab, ...prev.filter(t => t.name !== tab.name)])); 53 | }; 54 | 55 | const value = useMemo(() => ( 56 | { 57 | current: tabHistory[0], 58 | tabList, 59 | addTab, 60 | removeTab, 61 | switchTabs 62 | } 63 | ), [tabList, tabHistory, addTab]) 64 | 65 | return ( 66 | 67 | {children} 68 | 69 | ); 70 | } 71 | 72 | export default TabProvider; 73 | -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MarkdownHeader from './MarkdownHeader'; 3 | import LineBreak from './LineBreak'; 4 | import Section from './Section'; 5 | import Text from './Text'; 6 | import MarkdownButton from './MarkdownButton'; 7 | import Comment from './Comment'; 8 | import { portfolio, contact } from '../utils/directory' 9 | import TabLink from './TabLink'; 10 | import Link from './Link'; 11 | import ResumePDF from '../assets/documents/Ayrton_Parkinson_CV.pdf'; 12 | 13 | function About() { 14 | return ( 15 |
16 |
17 | 18 | 19 | Hi! My name is Ayrton Parkinson. I am a full stack software engineer currently working at FullScript as a backend Ruby developer, helping to build the future of whole person care! 20 | While originally going to school for economics and finance, I started coding as a hobby through 2021 and from that point on, I was hooked. Since then I have been learning and building every opportunity I get. For some examples of recent work, see Portfolio.js. 21 | For information on how to reach out, see Contact.md. 22 |
23 | 24 |
25 | 26 | [Download Resume] 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | While these are some of the languages I am the most comfortable with, I am constantly looking to expand my toolkit and am open to working with and learning new skills! 35 | 36 | 37 | Languages and Frameworks: Ruby, Ruby on Rails, Python, Java, GraphQL, SQL, React, JavaScript, HTML, CSS 38 | 39 | 40 | Miscellaneous: Git, Webpack, Object-Oriented Programming and Design, Test-Driven Development, Data Structures and Algorithms, Full Stack Web Development, REST APIs, Amazon S3 41 | 42 |
43 |
44 | ) 45 | } 46 | 47 | export default About; 48 | -------------------------------------------------------------------------------- /src/components/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import Sidebar from './sidebar/Sidebar'; 3 | import NoTabs from './NoTabs'; 4 | import useTabContext from '../hooks/useTabContext'; 5 | import Drawer from './sidebar/Drawer'; 6 | import { useToggle } from '../hooks/useToggle'; 7 | import { Bars3Icon } from '@heroicons/react/20/solid'; 8 | import Navbar from './Navbar'; 9 | import DirectoryList from './sidebar/DirectoryList'; 10 | import { directory } from '../utils/directory'; 11 | import JSConfetti from 'js-confetti'; 12 | 13 | function Dashboard() { 14 | const tabs = useTabContext(); 15 | const tabRef = useRef(null); 16 | const [drawerOpen, toggleDrawer] = useToggle(false); 17 | const [sidebarOpen, toggleSidebar] = useToggle(true); 18 | 19 | // Reset Tab Component scroll when tab changes 20 | useEffect(() => { 21 | if (tabRef.current) { 22 | tabRef.current.scrollTo({ top: 0, behaviour: 'smooth' }); 23 | } 24 | }, [tabs]); 25 | 26 | // Keyboard shortcut listeners 27 | useEffect(() => { 28 | const keyboardShortcuts = (e) => { 29 | if (e.key === 'b' && e.metaKey) { 30 | window.innerWidth < 768 ? toggleDrawer() : toggleSidebar(); 31 | } 32 | 33 | if (e.key === 'k' && e.metaKey && e.shiftKey) { 34 | const jsConfetti = new JSConfetti(); 35 | jsConfetti.addConfetti({ confettiColors: ['#8be9fd', '#50fa7b', '#ff5555', '#ff79c6', '#bd93f9', '#f1fa8c'] }) 36 | } 37 | }; 38 | 39 | window.addEventListener('keydown', keyboardShortcuts); 40 | 41 | return window.addEventListener('keydown', keyboardShortcuts); 42 | }, [toggleSidebar, toggleDrawer]); 43 | 44 | return ( 45 |
46 | {/* Desktop Sidebar */} 47 |
48 | 51 | 52 | 53 | 54 |
55 | 56 | {/* Mobile drawer */} 57 | 58 | 59 | 60 | 61 | {/* Main content and navbar */} 62 |
63 | 64 | 65 | {tabs.current ? 66 |
67 |
68 | {tabs.current.component} 69 |
70 |
: 71 | 72 | } 73 |
74 |
75 | ); 76 | } 77 | 78 | export default Dashboard; 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /src/components/Portfolio.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MarkdownHeader from './MarkdownHeader'; 3 | import LineBreak from './LineBreak'; 4 | import Text from './Text'; 5 | import Section from './Section'; 6 | import Link from './Link'; 7 | import Project from './portfolio/Project'; 8 | import ProjectText from './portfolio/ProjectText'; 9 | import ProjectPreview from './portfolio/ProjectPreview'; 10 | import invesdditPrev from '../assets/images/portfolio/invesddit-1.png'; 11 | import taasskkrrPrev from '../assets/images/portfolio/taasskkrr-1.png'; 12 | import inventoryPrev from '../assets/images/portfolio/inventory-tracker-1.png'; 13 | import chessPrev from '../assets/images/portfolio/chess-1.png'; 14 | import sudokuPrev from '../assets/images/portfolio/sudoku-solver-1.png' 15 | 16 | function Portfolio() { 17 | return ( 18 |
19 |
20 | 21 | 22 | 23 | Below is a selection of some recent and larger projects that I have worked on. For more detail on everything that I have done, as well as what I am currently working on, check out my GitHub at https://github.com/ayrt-n 24 | 25 |
26 | 27 |
28 | 29 | 35 | 36 | 37 |
38 | 39 |
40 | 41 | 48 | 49 | 50 |
51 | 52 |
53 | 54 | 61 | 62 | 63 |
64 | 65 |
66 | 67 | 73 | 74 | 75 |
76 | 77 |
78 | 79 | 85 | 86 | 87 |
88 |
89 | ) 90 | } 91 | 92 | export default Portfolio; 93 | --------------------------------------------------------------------------------