├── src ├── components │ ├── .gitkeep │ ├── Base.tsx │ ├── Icon │ │ ├── FacebookIcon.tsx │ │ ├── Icon.tsx │ │ ├── LinkedInIcon.tsx │ │ ├── StackOverflowIcon.tsx │ │ ├── TwitterIcon.tsx │ │ ├── QuoteIcon.tsx │ │ ├── InstagramIcon.tsx │ │ ├── GithubIcon.tsx │ │ └── DribbbleIcon.tsx │ ├── Layout │ │ ├── Section.tsx │ │ └── Page.tsx │ ├── Socials.tsx │ └── Sections │ │ ├── Resume │ │ ├── ResumeSection.tsx │ │ ├── TimelineItem.tsx │ │ ├── Skills.tsx │ │ └── index.tsx │ │ ├── Footer.tsx │ │ ├── About.tsx │ │ ├── Contact │ │ ├── ContactForm.tsx │ │ └── index.tsx │ │ ├── Hero.tsx │ │ ├── Portfolio.tsx │ │ ├── Testimonials.tsx │ │ └── Header.tsx ├── images │ ├── .gitkeep │ ├── profilepic.jpg │ ├── testimonial.webp │ ├── header-background.webp │ └── portfolio │ │ ├── portfolio-1.jpg │ │ ├── portfolio-2.jpg │ │ ├── portfolio-3.jpg │ │ ├── portfolio-4.jpg │ │ ├── portfolio-5.jpg │ │ ├── portfolio-6.jpg │ │ ├── portfolio-7.jpg │ │ ├── portfolio-8.jpg │ │ ├── portfolio-9.jpg │ │ ├── portfolio-10.jpg │ │ └── portfolio-11.jpg ├── pages │ ├── api │ │ └── .gitkeep │ ├── _app.tsx │ ├── _document.tsx │ └── index.tsx ├── config.ts ├── hooks │ ├── useInterval.ts │ ├── useWindow.ts │ ├── useDetectOutsideClick.ts │ └── useNavObserver.tsx ├── types.d.ts ├── globalStyles.scss └── data │ ├── dataDef.ts │ └── data.tsx ├── .prettierignore ├── .github └── FUNDING.yml ├── public ├── favicon.ico └── site.webmanifest ├── resume-screenshot.jpg ├── .vscode └── settings.json ├── next-env.d.ts ├── .prettierrc ├── postcss.config.js ├── stylelint.config.js ├── .gitignore ├── next-sitemap.js ├── tsconfig.json ├── tailwind.config.js ├── next.config.js ├── LICENSE ├── package.json ├── .eslintrc └── README.md /src/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/images/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/api/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | next/.next/ -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: tbakerx 4 | 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /resume-screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/resume-screenshot.jpg -------------------------------------------------------------------------------- /src/images/profilepic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/profilepic.jpg -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true 4 | } -------------------------------------------------------------------------------- /src/images/testimonial.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/testimonial.webp -------------------------------------------------------------------------------- /src/images/header-background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/header-background.webp -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-1.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-2.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-3.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-4.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-5.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-6.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-7.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-8.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-9.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-10.jpg -------------------------------------------------------------------------------- /src/images/portfolio/portfolio-11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whitehorse21/react-resume-template/HEAD/src/images/portfolio/portfolio-11.jpg -------------------------------------------------------------------------------- /src/components/Base.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | const Base: FC = memo(() => { 4 | return <>; 5 | }); 6 | 7 | Base.displayName = 'Base'; 8 | export default Base; 9 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "bracketSameLine": true, 4 | "printWidth": 120, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "all", 8 | "useTabs": false, 9 | "arrowParens": "avoid" 10 | } 11 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | plugins: { 5 | 'tailwindcss/nesting': {}, 6 | tailwindcss: {}, 7 | autoprefixer: {}, 8 | 'postcss-preset-env': { 9 | features: {'nesting-rules': false}, 10 | }, 11 | ...(process.env.NODE_ENV === 'production' ? {cssnano: {}} : {}), 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css'; 2 | import '../globalStyles.scss'; 3 | 4 | import type {AppProps} from 'next/app'; 5 | import {memo} from 'react'; 6 | 7 | const MyApp = memo(({Component, pageProps}: AppProps): JSX.Element => { 8 | return ( 9 | <> 10 | 11 | 12 | ); 13 | }); 14 | 15 | export default MyApp; 16 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-resume-template", 3 | "short_name": "react-resume-template", 4 | "icons": [ 5 | {"src": "/icon-192.png", "type": "image/png", "sizes": "192x192"}, 6 | {"src": "/icon-512.png", "type": "image/png", "sizes": "512x512"} 7 | ], 8 | "theme_color": "#515455", 9 | "background_color": "#515455", 10 | "display": "standalone" 11 | } 12 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | export const isBrowser = typeof window !== 'undefined'; 2 | export const isMobile = isBrowser ? window.matchMedia('(pointer: coarse)').matches : false; 3 | export const canUseDOM: boolean = 4 | typeof window !== 'undefined' && 5 | typeof window.document !== 'undefined' && 6 | typeof window.document.createElement !== 'undefined'; 7 | export const isApple: boolean = canUseDOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform); 8 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | extends: ['stylelint-config-recommended', 'stylelint-order'], 5 | plugins: ['stylelint-prettier', 'stylelint-order'], 6 | rules: { 7 | 'no-descending-specificity': null, 8 | 'font-family-no-missing-generic-family-keyword': null, 9 | 'at-rule-no-unknown': [ 10 | true, 11 | { 12 | ignoreAtRules: ['tailwind'], 13 | }, 14 | ], 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/hooks/useInterval.ts: -------------------------------------------------------------------------------- 1 | import {useEffect, useRef} from 'react'; 2 | 3 | function useInterval(callback: () => void, delay: number | null) { 4 | const savedCallback = useRef(callback); 5 | 6 | useEffect(() => { 7 | savedCallback.current = callback; 8 | }, [callback]); 9 | 10 | useEffect(() => { 11 | if (!delay && delay !== 0) { 12 | return; 13 | } 14 | 15 | const id = setInterval(() => savedCallback.current(), delay); 16 | 17 | return () => clearInterval(id); 18 | }, [delay]); 19 | } 20 | 21 | export default useInterval; 22 | -------------------------------------------------------------------------------- /.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 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | tsconfig.tsbuildinfo 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env 30 | .env.local 31 | .env.development.local 32 | .env.test.local 33 | .env.production.local 34 | 35 | # vercel 36 | .vercel 37 | -------------------------------------------------------------------------------- /src/components/Icon/FacebookIcon.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import Icon, {IconProps} from './Icon'; 4 | 5 | const FacebookIcon: FC = memo(props => ( 6 | 7 | 10 | 11 | )); 12 | 13 | export default FacebookIcon; 14 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | // Modules 2 | 3 | declare module '*.jpg' { 4 | const value: string; 5 | export default value; 6 | } 7 | declare module '*.webp' { 8 | const value: string; 9 | export default value; 10 | } 11 | 12 | declare module '*.svg' { 13 | const value: string; 14 | export default value; 15 | } 16 | 17 | declare module '*.png' { 18 | const value: string; 19 | export default value; 20 | } 21 | 22 | declare module '*.webm' { 23 | const value: string; 24 | export default value; 25 | } 26 | 27 | declare module '*.mp4' { 28 | const value: string; 29 | export default value; 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Icon/Icon.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | export interface IconProps extends React.HTMLAttributes { 4 | svgRef?: React.Ref; 5 | transform?: string; 6 | } 7 | 8 | const Icon: FC = memo(({children, className, svgRef, transform, ...props}) => ( 9 | 18 | {children} 19 | 20 | )); 21 | 22 | export default Icon; 23 | -------------------------------------------------------------------------------- /src/components/Icon/LinkedInIcon.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import Icon, {IconProps} from './Icon'; 4 | 5 | const LinkedInIcon: FC = memo(props => ( 6 | 7 | 10 | 11 | )); 12 | 13 | export default LinkedInIcon; 14 | -------------------------------------------------------------------------------- /next-sitemap.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | siteUrl: 'reactresume.com', 4 | exclude: ['/404*', '/500*'], 5 | transform: async (config, path) => { 6 | return { 7 | loc: path, 8 | changefreq: config.changefreq, 9 | priority: path === '/' ? 1 : config.priority, 10 | lastmod: config.autoLastmod ? new Date().toISOString() : undefined, 11 | }; 12 | }, 13 | generateRobotsTxt: true, 14 | robotsTxtOptions: { 15 | policies: [ 16 | { 17 | userAgent: '*', 18 | allow: '/', 19 | }, 20 | { 21 | userAgent: '*', 22 | disallow: ['/404', '/500'], 23 | }, 24 | ], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/components/Layout/Section.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames'; 2 | import {FC, memo} from 'react'; 3 | 4 | import {SectionId} from '../../data/data'; 5 | 6 | const Section: FC<{sectionId: SectionId; sectionTitle?: string; noPadding?: boolean; className?: string}> = memo( 7 | ({children, sectionId, noPadding = false, className}) => { 8 | return ( 9 |
10 |
{children}
11 |
12 | ); 13 | }, 14 | ); 15 | 16 | Section.displayName = 'Section'; 17 | export default Section; 18 | -------------------------------------------------------------------------------- /src/components/Socials.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import {socialLinks} from '../data/data'; 4 | 5 | const Socials: FC = memo(() => { 6 | return ( 7 | <> 8 | {socialLinks.map(({label, Icon, href}) => ( 9 | 14 | 15 | 16 | ))} 17 | 18 | ); 19 | }); 20 | 21 | Socials.displayName = 'Socials'; 22 | export default Socials; 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "noImplicitReturns": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "esModuleInterop": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "jsx": "preserve", 23 | "incremental": true 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ], 28 | "exclude": [ 29 | "node_modules" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Icon/StackOverflowIcon.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import Icon, {IconProps} from './Icon'; 4 | 5 | const StackOverflowIcon: FC = memo(props => ( 6 | 7 | 8 | 12 | 13 | )); 14 | 15 | export default StackOverflowIcon; 16 | -------------------------------------------------------------------------------- /src/components/Sections/Resume/ResumeSection.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo, PropsWithChildren} from 'react'; 2 | 3 | const ResumeSection: FC> = memo(({title, children}) => { 4 | return ( 5 |
6 |
7 |
8 |

{title}

9 | 10 |
11 |
12 |
{children}
13 |
14 | ); 15 | }); 16 | 17 | ResumeSection.displayName = 'ResumeSection'; 18 | export default ResumeSection; 19 | -------------------------------------------------------------------------------- /src/hooks/useWindow.ts: -------------------------------------------------------------------------------- 1 | import {useEffect, useState} from 'react'; 2 | 3 | interface WindowSize { 4 | width: number; 5 | height: number; 6 | } 7 | 8 | const useWindow = (): WindowSize => { 9 | const [windowSize, setWindowSize] = useState({ 10 | width: 0, 11 | height: 0, 12 | }); 13 | 14 | const handleSize = () => { 15 | setWindowSize({ 16 | width: window.innerWidth, 17 | height: window.innerHeight, 18 | }); 19 | }; 20 | 21 | // Set size at the first client-side load 22 | useEffect(() => { 23 | window.addEventListener('resize', handleSize); 24 | handleSize(); 25 | return () => window.removeEventListener('resize', handleSize); 26 | // eslint-disable-next-line react-hooks/exhaustive-deps 27 | }, []); 28 | 29 | return windowSize; 30 | }; 31 | 32 | export default useWindow; 33 | -------------------------------------------------------------------------------- /src/hooks/useDetectOutsideClick.ts: -------------------------------------------------------------------------------- 1 | import {RefObject, useEffect} from 'react'; 2 | 3 | const useDetectOutsideClick = (ref: RefObject, handler: (event: Event) => void) => { 4 | useEffect(() => { 5 | const listener = (event: Event) => { 6 | // Do nothing if clicking ref's element or descendent elements 7 | if (!ref.current || ref.current.contains((event?.target as Node) || null)) { 8 | return; 9 | } 10 | handler(event); 11 | }; 12 | document.addEventListener('mousedown', listener); 13 | document.addEventListener('touchstart', listener); 14 | return () => { 15 | document.removeEventListener('mousedown', listener); 16 | document.removeEventListener('touchstart', listener); 17 | }; 18 | }, [ref, handler]); 19 | }; 20 | 21 | export default useDetectOutsideClick; 22 | -------------------------------------------------------------------------------- /src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import {Head, Html, Main, NextScript} from 'next/document'; 2 | 3 | // next/document vs next/head 4 | // 5 | // next/document Head is rendered once on the server. This is different from next/head which will 6 | // rebuild the next/head fields each time it's called, and won't overwrite next/document's Head. 7 | 8 | export default function Document() { 9 | return ( 10 | 11 | 12 | 13 | {/* google translate breaks react: 14 | - https://github.com/facebook/react/issues/11538 15 | - https://bugs.chromium.org/p/chromium/issues/detail?id=872770 */} 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Sections/Resume/TimelineItem.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import {TimelineItem} from '../../../data/dataDef'; 4 | 5 | const TimelineItem: FC<{item: TimelineItem}> = memo(({item}) => { 6 | const {title, date, location, content} = item; 7 | return ( 8 |
9 |
10 |

{title}

11 |
12 | {location} 13 | 14 | {date} 15 |
16 |
17 | {content} 18 |
19 | ); 20 | }); 21 | 22 | TimelineItem.displayName = 'TimelineItem'; 23 | export default TimelineItem; 24 | -------------------------------------------------------------------------------- /src/components/Icon/TwitterIcon.tsx: -------------------------------------------------------------------------------- 1 | import {FC, memo} from 'react'; 2 | 3 | import Icon, {IconProps} from './Icon'; 4 | 5 | const TwitterIcon: FC = memo(props => ( 6 | 7 | 10 | 11 | )); 12 | 13 | export default TwitterIcon; 14 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = { 3 | content: ['./src/**/*.{js,ts,jsx,tsx,css,scss}'], 4 | // darkMode: 'media', // or 'media' or 'class' 5 | theme: { 6 | extend: { 7 | keyframes: { 8 | typing: { 9 | '0%, 100%': {width: '0%'}, 10 | '30%, 70%': {width: '100%'}, 11 | }, 12 | blink: { 13 | '0%': { 14 | opacity: 0, 15 | }, 16 | }, 17 | 'rotate-loader': { 18 | '0%': { 19 | transform: 'rotate(0deg)', 20 | strokeDashoffset: '360%', 21 | }, 22 | '100%': { 23 | transform: 'rotate(360deg)', 24 | strokeDashoffset: '-360%', 25 | }, 26 | }, 27 | }, 28 | screens: { 29 | touch: {raw: 'only screen and (pointer: coarse)'}, 30 | }, 31 | }, 32 | }, 33 | // eslint-disable-next-line no-undef 34 | plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')], 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/Sections/Footer.tsx: -------------------------------------------------------------------------------- 1 | import {ChevronUpIcon} from '@heroicons/react/outline'; 2 | import {FC, memo} from 'react'; 3 | 4 | import {SectionId} from '../../data/data'; 5 | import Socials from '../Socials'; 6 | 7 | const Footer: FC = memo(() => ( 8 |
9 |
10 | 13 | 14 | 15 |
16 |
17 |
18 | 19 |
20 | © Copyright 2022 Tim Baker 21 |
22 |
23 | )); 24 | 25 | Footer.displayName = 'Footer'; 26 | export default Footer; 27 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | // https://github.com/vercel/next.js/blob/master/packages/next/next-server/server/config.ts 4 | const nextConfig = { 5 | webpack: config => { 6 | const oneOfRule = config.module.rules.find(rule => rule.oneOf); 7 | 8 | // Next 12 has multiple TS loaders, and we need to update all of them. 9 | const tsRules = oneOfRule.oneOf.filter(rule => rule.test && rule.test.toString().includes('tsx|ts')); 10 | 11 | tsRules.forEach(rule => { 12 | // eslint-disable-next-line no-param-reassign 13 | rule.include = undefined; 14 | }); 15 | 16 | return config; 17 | }, 18 | compress: true, 19 | generateEtags: true, 20 | pageExtensions: ['tsx', 'mdx', 'ts'], 21 | poweredByHeader: false, 22 | productionBrowserSourceMaps: false, 23 | svgo: { 24 | multipass: true, 25 | plugins: ['removeDimensions'], 26 | }, 27 | strictMode: true, 28 | swcMinify: true, 29 | trailingSlash: false, 30 | images: { 31 | domains: ['images.unsplash.com', 'source.unsplash.com'], 32 | }, 33 | }; 34 | 35 | module.exports = nextConfig; 36 | -------------------------------------------------------------------------------- /src/globalStyles.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer utilities { 6 | /* Chrome, Safari and Opera */ 7 | .no-scrollbar::-webkit-scrollbar { 8 | display: none; 9 | } 10 | 11 | .no-scrollbar { 12 | -ms-overflow-style: none; /* IE and Edge */ 13 | scrollbar-width: none; /* Firefox */ 14 | } 15 | } 16 | 17 | @supports (-webkit-touch-callout: none) { 18 | .h-screen { 19 | height: -webkit-fill-available; 20 | } 21 | } 22 | 23 | /* Global styles */ 24 | * { 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | } 28 | 29 | html { 30 | -webkit-tap-highlight-color: transparent; 31 | scroll-behavior: smooth; 32 | height: 100vh; 33 | } 34 | 35 | body { 36 | position: relative; 37 | width: 100%; 38 | color: #444444; 39 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Ubuntu, 'Helvetica Neue', sans-serif; 40 | background-color: #fafafa; 41 | } 42 | 43 | td, 44 | th { 45 | padding: 4px 8px 4px 4px; 46 | text-align: left; 47 | } 48 | 49 | th { 50 | font-weight: 600; 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tim Baker 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 | -------------------------------------------------------------------------------- /src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from 'next/dynamic'; 2 | import {FC, memo} from 'react'; 3 | 4 | import Page from '../components/Layout/Page'; 5 | import About from '../components/Sections/About'; 6 | import Contact from '../components/Sections/Contact'; 7 | import Footer from '../components/Sections/Footer'; 8 | import Hero from '../components/Sections/Hero'; 9 | import Portfolio from '../components/Sections/Portfolio'; 10 | import Resume from '../components/Sections/Resume'; 11 | import Testimonials from '../components/Sections/Testimonials'; 12 | import {homePageMeta} from '../data/data'; 13 | 14 | // eslint-disable-next-line react-memo/require-memo 15 | const Header = dynamic(() => import('../components/Sections/Header'), {ssr: false}); 16 | 17 | const Home: FC = memo(() => { 18 | const {title, description} = homePageMeta; 19 | return ( 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |