From f44e0557ed3ac2c7307f7ed2cd8d70e25e32d6b2 Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Sun, 11 Aug 2019 21:47:22 +0200 Subject: [PATCH] add resume --- README.md | 11 + content/meta.yml | 12 +- content/resume.json | 237 ++++++++++++++++++ gatsby-config.js | 40 ++- jest/__fixtures__/meta.json | 26 -- src/components/Layout.jsx | 3 +- src/components/atoms/SEO.jsx | 11 +- src/components/atoms/Vcard.jsx | 28 ++- src/components/molecules/LogoUnit.jsx | 3 +- src/components/molecules/Networks.jsx | 37 ++- src/components/molecules/Networks.module.scss | 6 +- src/components/organisms/Header.jsx | 7 +- src/hooks/use-resume.js | 62 +++++ src/pages/__tests__/resume.test.jsx | 10 + src/pages/resume/ResumeItem.jsx | 45 ++++ src/pages/resume/ResumeItem.module.scss | 52 ++++ src/pages/resume/index.jsx | 70 ++++++ src/pages/resume/index.module.scss | 66 +++++ src/styles/_variables.scss | 8 +- static/robots.txt | 1 + 20 files changed, 640 insertions(+), 95 deletions(-) create mode 100644 content/resume.json create mode 100644 src/hooks/use-resume.js create mode 100644 src/pages/__tests__/resume.test.jsx create mode 100644 src/pages/resume/ResumeItem.jsx create mode 100644 src/pages/resume/ResumeItem.module.scss create mode 100644 src/pages/resume/index.jsx create mode 100644 src/pages/resume/index.module.scss diff --git a/README.md b/README.md index 8b90770..7d45f34 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ - [๐Ÿ’ One data file to rule all pages](#-one-data-file-to-rule-all-pages) - [๐Ÿฑ GitHub repositories](#-github-repositories) - [๐Ÿ’… Theme switcher](#-theme-switcher) + - [๐Ÿ—‚ Resume](#-resume) - [๐Ÿ† SEO component](#-seo-component) - [๐Ÿ“‡ Client-side vCard creation](#-client-side-vcard-creation) - [๐Ÿ’ซ Page transitions](#-page-transitions) @@ -76,6 +77,16 @@ If you want to know how, have a look at the respective components: - [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx) - [`src/hooks/use-dark-mode.jsx`](src/hooks/use-dark-mode.jsx) +### ๐Ÿ—‚ Resume + +Resume page based on [JSON Resume](https://jsonresume.org) standard. + +If you want to know how, have a look at the respective components: + +- [`src/pages/resume.jsx`](src/pages/resume.jsx) +- [`content/resume.json`](content/resume.json) +- [`src/hooks/use-resume.js`](src/hooks/use-resume.js) + ### ๐Ÿ† SEO component Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page. diff --git a/content/meta.yml b/content/meta.yml index 6dbe389..9b4e2b8 100644 --- a/content/meta.yml +++ b/content/meta.yml @@ -1,19 +1,11 @@ +# more personal metadata can be found in ./resume.json + - title: Matthias Kretschmann tagline: Designer & Developer description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann. url: https://matthiaskretschmann.com - email: m@kretschmann.io - avatar: ../src/images/avatar.jpg img: ../src/images/twitter-card.png - social: - Mail: mailto:m@kretschmann.io - Blog: https://kremalicious.com - Twitter: https://twitter.com/kremalicious - GitHub: https://github.com/kremalicious - Dribbble: https://dribbble.com/kremalicious - Keybase: https://keybase.io/kremalicious - availability: status: false available: '๐Ÿ‘” Available for new projects. Letโ€™s talk!' diff --git a/content/resume.json b/content/resume.json new file mode 100644 index 0000000..7e4e6a6 --- /dev/null +++ b/content/resume.json @@ -0,0 +1,237 @@ +{ + "basics": { + "name": "Matthias Kretschmann", + "label": "Designer & Developer", + "picture": "../src/images/avatar.jpg", + "email": "m@kretschmann.io", + "website": "https://matthiaskretschmann.com", + "summary": "", + "profiles": [ + { + "network": "Blog", + "url": "https://kremalicious.com" + }, + { + "network": "Twitter", + "username": "kremalicious", + "url": "https://twitter.com/kremalicious" + }, + { + "network": "GitHub", + "username": "kremalicious", + "url": "https://github.com/kremalicious" + }, + { + "network": "Dribbble", + "username": "kremalicious", + "url": "https://dribbble.com/kremalicious" + }, + { + "network": "Keybase", + "username": "kremalicious", + "url": "https://keybase.io/kremalicious" + } + ], + "location": { + "city": "Berlin", + "country": "Germany", + "countryCode": "DE" + } + }, + "work": [ + { + "company": "Ocean Protocol Foundation", + "position": "Lead UI Designer & Developer", + "website": "https://oceanprotocol.com", + "startDate": "2017-01-01", + "summary": "Leading the UI design & development of Ocean Protocol, iterating on a components-based UI design system spanning all of Ocean Protocol's web properties. Additionally, I conceptualize, execute and iterate on the creative and visual direction of the Ocean Protocol brand.", + "highlights": ["Started the company"] + }, + { + "company": "BigchainDB GmbH", + "position": "Lead UI Designer & Developer", + "website": "https://bigchaindb.com", + "startDate": "2016-12-01", + "endDate": "2018-12-31", + "summary": "Leading the UI design & development of all BigchainDB web properties. I created the initial BigchainDB brand and further conceptualized, executed and iterated on the creative and visual direction of BigchainDB. This included creating and iterating on a components-based UI design system for all of BigchainDB's web properties.", + "highlights": ["Started the company"] + }, + { + "company": "ascribe GmbH", + "position": "UI Designer & Developer", + "website": "https://ascribe.io", + "startDate": "2016-01-01", + "endDate": "2017-12-31", + "summary": "Description...", + "highlights": ["Started the company"] + }, + { + "company": "ChartMogul Ltd.", + "position": "Lead UI Engineer", + "website": "https://chartmogul.com", + "startDate": "2015-07-15", + "endDate": "2017-02-01", + "summary": "Co-designing and leading the UI design & development of various ChartMogul web properties. This included the creation of a components-based UI design system and implementing it across all web touch points. Besides designing and implementing new features, I maintained the front-end of the ChartMogul application and implemented the UI design system by refactoring most of its front-end codebase.", + "highlights": ["Started the company"] + }, + { + "company": "UN World Food Programme/ShareTheMeal", + "position": "UI Engineer", + "website": "https://sharethemeal.org", + "startDate": "2014-10-01", + "endDate": "2015-06-01", + "summary": "...", + "highlights": ["Started the company"] + }, + { + "company": "ezeep GmbH", + "position": "Lead Designer & Front End Developer", + "website": "https://ezeep.com", + "startDate": "2012-01-01", + "endDate": "2014-09-01", + "summary": "Creating an unprecedented, market-leading & award-winning user experience around printing based on the principles of emotional design way ahead of all competitors. This included defining the product based on user & market research in an iterative process and designing & building ezeepโ€™s numerous touch points, like the web app, web site, desktop apps for Windows & Mac OS X and apps for iOS & Android. On top of that I created the corporate identity and a consistent visual branding including the logo." + }, + { + "company": "Martin Luther University Halle-Wittenberg", + "position": "UI/UX Designer & Front End Developer", + "startDate": "2009-02-01", + "endDate": "2012-01-01", + "summary": "Conceptualizing & implementing numerous in-house and public facing interfaces for thousands of students and staff. Additionally, conceptualizing, creating and maintaining the blog network & community for all students & staff." + }, + { + "company": "Harz University of Applied Sciences", + "position": "Consultant & Teacher", + "startDate": "2011-02-01", + "endDate": "2011-05-01", + "summary": "Conceptualizing a web design & development university seminar and building a responsive & fluid grid framework with a basic HTML/CSS template for students of Media Informatics at the Harz University of Applied Sciences to learn and use." + }, + { + "company": "Martin Luther University Halle-Wittenberg", + "position": "Consultant & Teacher", + "startDate": "2011-02-01", + "endDate": "2011-05-01", + "summary": "Conceptualizing a WordPress-based web design university seminar and building a minimal starting theme for students of media & communication science at the MLU Halle-Wittenberg to learn and use." + }, + { + "company": "Shortmoves", + "position": "Web Designer & Developer", + "startDate": "2009-01-01", + "endDate": "2010-01-01", + "summary": "Creating & managing the web presence and marketing material of the International Shortfilm Festival Shortmoves in Halle (Saale), Germany." + }, + { + "company": "Agentur Ahron", + "position": "Co-Founder & Photojournalist & Photographer", + "startDate": "2005-01-01", + "endDate": "2008-12-31", + "summary": "Co-founded and built up a photo agency from the ground up and worked as a photographer ranging from journalistic works for news agencies & newspapers to photographic work for private clients." + }, + { + "company": "Freelance", + "position": "Designer & Developer", + "startDate": "2004-01-01", + "summary": "Numerous projects and clients as a UI/UX Designer, Front End Developer, Icon Designer & Photographer." + } + ], + "education": [ + { + "institution": "Self-taught", + "area": "UI Design & Web Development", + "studyType": "Autodidactic", + "startDate": "1999-01-01", + "endDate": "2004-01-01" + }, + { + "institution": "Martin Luther University Halle-Wittenberg", + "area": "Media/Communication Science & Art History", + "studyType": "Bachelor", + "startDate": "2008-01-01", + "endDate": "2012-01-01" + }, + { + "institution": "Martin Luther University Halle-Wittenberg", + "area": "Political Science & Sociology", + "studyType": "Magister Artium", + "startDate": "2006-01-01", + "endDate": "2008-01-01" + } + ], + "awards": [ + { + "title": "German Design Award", + "date": "2015-11-01", + "awarder": "ezeep GmbH" + }, + { + "title": "CeBIT Preview Award", + "date": "2013-11-01", + "awarder": "ezeep GmbH" + } + ], + "skills": [ + { + "name": "Design", + "level": "Master", + "keywords": [ + "Product Design", + "Service Design", + "Interface Design", + "User Experience Design", + "Communication Design", + "Interaction Design", + "Information Architecture", + "Icon Design", + "Web Design", + "Typography", + "Design management" + ] + }, + { + "name": "Web Development", + "level": "Master", + "keywords": [ + "HTML", + "CSS", + "Javascript", + "Node.js", + "npm ecosystem", + "SASS/SCSS", + "Less", + "Stylus", + "Gulp", + "Gatsby", + "React", + "Styled Components", + "JAMstack" + ] + }, + { + "name": "General Software Development", + "level": "Master", + "keywords": [ + "Git", + "GitHub", + "Bash", + "UNIX", + "Agile: Kanban & Scrum", + "Prototyping", + "Incremental" + ] + }, + { + "name": "DevOps", + "level": "Intermediate", + "keywords": ["AWS", "Now", "Serverless", "Cloudflare", "NGINX", "Apache"] + } + ], + "languages": [ + { + "language": "German", + "fluency": "Native speaker" + }, + { + "language": "English", + "fluency": "Advanced speaker" + } + ] +} diff --git a/gatsby-config.js b/gatsby-config.js index 9af4263..c2d295e 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -11,38 +11,20 @@ module.exports = { siteUrl: `${url}` }, plugins: [ + 'gatsby-transformer-yaml', + 'gatsby-transformer-json', { - resolve: 'gatsby-transformer-yaml', + resolve: 'gatsby-source-filesystem', options: { - plugins: [ - { - resolve: 'gatsby-source-filesystem', - options: { - name: 'content', - path: path.join(__dirname, 'content') - } - } - ] + name: 'content', + path: path.join(__dirname, 'content') } }, { - resolve: 'gatsby-transformer-json', + resolve: 'gatsby-source-filesystem', options: { - plugins: [ - { - resolve: 'gatsby-source-filesystem', - options: { - name: 'pkg', - path: path.join(__dirname, 'package.json') - } - } - ] - } - }, - { - resolve: 'gatsby-plugin-sass', - options: { - includePaths: [`${__dirname}/node_modules`, `${__dirname}/src/styles`] + name: 'pkg', + path: path.join(__dirname, 'package.json') } }, { @@ -52,6 +34,12 @@ module.exports = { path: path.join(__dirname, 'src', 'images') } }, + { + resolve: 'gatsby-plugin-sass', + options: { + includePaths: [`${__dirname}/node_modules`, `${__dirname}/src/styles`] + } + }, { resolve: 'gatsby-plugin-svgr', options: { diff --git a/jest/__fixtures__/meta.json b/jest/__fixtures__/meta.json index b330035..374c42d 100644 --- a/jest/__fixtures__/meta.json +++ b/jest/__fixtures__/meta.json @@ -4,32 +4,6 @@ "tagline": "Designer & Developer", "description": "Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.", "url": "https://matthiaskretschmann.com", - "email": "m@kretschmann.io", - "avatar": { - "childImageSharp": { - "fluid": { - "src": "/static/b45f45aa8d98d4e4019a242d38f2f248/bc3a8/avatar.jpg" - }, - "resize": { - "src": "/static/b45f45aa8d98d4e4019a242d38f2f248/bc3a8/avatar.jpg" - } - } - }, - "img": { - "childImageSharp": { - "resize": { - "src": "/static/5ecbb5694b0b2152aa71398164af38b2/da1a7/twitter-card.png" - } - } - }, - "social": { - "Email": "mailto:m@kretschmann.io", - "Blog": "https://kremalicious.com", - "Twitter": "https://twitter.com/kremalicious", - "GitHub": "https://github.com/kremalicious", - "Dribbble": "https://dribbble.com/kremalicious", - "Keybase": "https://keybase.io/kremalicious" - }, "availability": { "status": false, "available": "๐Ÿ‘” Available for new projects. Letโ€™s talk!", diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx index 6cf4441..dc434c1 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.jsx @@ -28,6 +28,7 @@ export default function Layout({ children, location }) { const isHomepage = location.pathname === '/' || location.pathname === '/offline-plugin-app-shell-fallback/' + const isResume = location.pathname === '/resume' return ( <> @@ -40,7 +41,7 @@ export default function Layout({ children, location }) { delay={timeout} delayChildren={timeout} > -
+
{children}
diff --git a/src/components/atoms/SEO.jsx b/src/components/atoms/SEO.jsx index 8512b8e..725e075 100644 --- a/src/components/atoms/SEO.jsx +++ b/src/components/atoms/SEO.jsx @@ -1,7 +1,14 @@ import React from 'react' import Helmet from 'react-helmet' import PropTypes from 'prop-types' -import { useMeta } from '../../hooks/use-meta' +import { useMeta } from '../../hooks/use-meta'` +import { useResume } from '../../hooks/use-resume' + +const MetaTags = ({ title, description, url, image, meta }) => { + const resume = useResume() + const twitterHandle = resume.basics.profiles.filter( + ({ network }) => network === 'Twitter' + )[0].username const MetaTags = ({ title, description, url, image, meta }) => ( ( {/* Twitter Card tags */} - + diff --git a/src/components/atoms/Vcard.jsx b/src/components/atoms/Vcard.jsx index e8f1472..b462868 100644 --- a/src/components/atoms/Vcard.jsx +++ b/src/components/atoms/Vcard.jsx @@ -2,20 +2,34 @@ import React from 'react' import saveAs from 'file-saver' import vCard from 'vcf' import { useMeta } from '../../hooks/use-meta' +import { useResume } from '../../hooks/use-resume' export default function Vcard() { const metaYaml = useMeta() + const { basics } = useResume() + const data = useStaticQuery(query) + const photoSrc = basics.picture.childImageSharp.fixed.src + const { name, label, email, profiles } = basics + + const meta = { + ...metaYaml, + photoSrc, + name, + label, + email, + profiles + } const handleAddressbookClick = e => { e.preventDefault() - init(metaYaml) + init(meta) } return ( Add to addressbook @@ -24,10 +38,8 @@ export default function Vcard() { } export const init = async meta => { - const photoSrc = meta.avatar.childImageSharp.resize.src - // first, convert the avatar to base64, then construct all vCard elements - const dataUrl = await toDataURL(photoSrc, 'image/jpeg') + const dataUrl = await toDataURL(meta.photoSrc, 'image/jpeg') const vcard = await constructVcard(dataUrl, meta) downloadVcard(vcard, meta) @@ -49,12 +61,12 @@ export const constructVcard = async (dataUrl, meta) => { // for vcard to actually display the image for whatever reason // const dataUrlCleaned = dataUrl.split('data:image/jpeg;base64,').join('') // contact.set('photo', dataUrlCleaned, { encoding: 'b', type: 'JPEG' }) - contact.set('fn', meta.title) - contact.set('title', meta.tagline) + contact.set('fn', meta.name) + contact.set('title', meta.label) contact.set('email', meta.email) + contact.set('nickname', 'kremalicious') contact.set('url', meta.url, { type: 'Portfolio' }) contact.add('url', meta.social.Blog, { type: 'Blog' }) - contact.set('nickname', 'kremalicious') contact.add('x-socialprofile', meta.social.Twitter, { type: 'twitter' }) contact.add('x-socialprofile', meta.social.GitHub, { type: 'GitHub' }) diff --git a/src/components/molecules/LogoUnit.jsx b/src/components/molecules/LogoUnit.jsx index 2aa52c4..74efc66 100644 --- a/src/components/molecules/LogoUnit.jsx +++ b/src/components/molecules/LogoUnit.jsx @@ -8,7 +8,8 @@ import { ReactComponent as Logo } from '../../images/logo.svg' import styles from './LogoUnit.module.scss' LogoUnit.propTypes = { - minimal: PropTypes.bool + minimal: PropTypes.bool, + isResume: PropTypes.bool } export default function LogoUnit({ minimal }) { diff --git a/src/components/molecules/Networks.jsx b/src/components/molecules/Networks.jsx index 96bcd44..c6430e8 100644 --- a/src/components/molecules/Networks.jsx +++ b/src/components/molecules/Networks.jsx @@ -4,8 +4,25 @@ import posed from 'react-pose' import { moveInTop } from '../atoms/Transitions' import Icon from '../atoms/Icon' import { useMeta } from '../../hooks/use-meta' +import { useResume } from '../../hooks/use-resume' import styles from './Networks.module.scss' +const NetworkLink = ({ name, url }) => ( + + + {name} + +) + +NetworkLink.propTypes = { + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired +} + export default function Networks({ small, hide }) { const { social } = useMeta() if (hide) return null @@ -17,17 +34,15 @@ export default function Networks({ small, hide }) { return ( - {Object.keys(social).map((key, i) => ( - - - {key} - - ))} + + + {profiles.map(profile => ( + + ))} ) } diff --git a/src/components/molecules/Networks.module.scss b/src/components/molecules/Networks.module.scss index c66ea40..1a679be 100644 --- a/src/components/molecules/Networks.module.scss +++ b/src/components/molecules/Networks.module.scss @@ -21,11 +21,11 @@ } .link { - margin-left: $spacer / 2; - margin-right: $spacer / 2; + margin-left: $spacer / $line-height; + margin-right: $spacer / $line-height; margin-bottom: $spacer / 2; text-align: center; - display: block; + display: inline-block; flex: 0 1; min-width: 2.5rem; diff --git a/src/components/organisms/Header.jsx b/src/components/organisms/Header.jsx index b13f24d..95e330d 100644 --- a/src/components/organisms/Header.jsx +++ b/src/components/organisms/Header.jsx @@ -8,16 +8,17 @@ import styles from './Header.module.scss' import { useMeta } from '../../hooks/use-meta' Header.propTypes = { - minimal: PropTypes.bool + minimal: PropTypes.bool, + isResume: PropTypes.bool } -export default function Header({ minimal }) { +export default function Header({ minimal, isResume }) { const { availability } = useMeta() return (
- +
diff --git a/src/hooks/use-resume.js b/src/hooks/use-resume.js new file mode 100644 index 0000000..ebbee65 --- /dev/null +++ b/src/hooks/use-resume.js @@ -0,0 +1,62 @@ +import { useStaticQuery, graphql } from 'gatsby' + +const query = graphql` + query Resume { + contentJson { + basics { + name + label + picture { + childImageSharp { + fixed(width: 256, height: 256) { + ...GatsbyImageSharpFixed_withWebp_noBase64 + } + } + } + email + website + summary + profiles { + network + url + username + } + location { + city + country + countryCode + } + } + education { + institution + area + studyType + startDate + endDate + } + languages { + language + fluency + } + skills { + name + level + keywords + } + work { + company + position + website + startDate + endDate + summary + highlights + } + } + } +` + +export const useResume = () => { + const { contentJson } = useStaticQuery(query) + return contentJson +} diff --git a/src/pages/__tests__/resume.test.jsx b/src/pages/__tests__/resume.test.jsx new file mode 100644 index 0000000..85c0b88 --- /dev/null +++ b/src/pages/__tests__/resume.test.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import { render } from '@testing-library/react' +import Resume from '../resume' + +describe('Resume', () => { + it('renders correctly from data file values', () => { + const { container } = render() + expect(container.firstChild).toBeInTheDocument() + }) +}) diff --git a/src/pages/resume/ResumeItem.jsx b/src/pages/resume/ResumeItem.jsx new file mode 100644 index 0000000..0b91189 --- /dev/null +++ b/src/pages/resume/ResumeItem.jsx @@ -0,0 +1,45 @@ +import React from 'react' +import PropTypes from 'prop-types' +import styles from './ResumeItem.module.scss' + +export default function ResumeItem({ workPlace, eduPlace }) { + const title = workPlace ? workPlace.company : eduPlace.institution + const subTitle = workPlace ? workPlace.position : eduPlace.area + const text = workPlace ? workPlace.summary : eduPlace.studyType + const { startDate, endDate } = workPlace || eduPlace + + const dateStart = new Date(startDate).getFullYear() + const dateEnd = endDate && new Date(endDate).getFullYear() + const isSameYear = dateStart === dateEnd + + return ( +
+ + {dateStart} + {dateEnd ? !isSameYear && `โ€“${dateEnd}` : 'โ€“present'}{' '} + +

{title}

+
{subTitle}
+

+ {text} +

+
+ ) +} + +ResumeItem.propTypes = { + workPlace: PropTypes.shape({ + startDate: PropTypes.string.isRequired, + endDate: PropTypes.string, + company: PropTypes.string.isRequired, + position: PropTypes.string.isRequired, + summary: PropTypes.string + }), + eduPlace: PropTypes.shape({ + startDate: PropTypes.string.isRequired, + endDate: PropTypes.string, + institution: PropTypes.string.isRequired, + area: PropTypes.string.isRequired, + studyType: PropTypes.string + }) +} diff --git a/src/pages/resume/ResumeItem.module.scss b/src/pages/resume/ResumeItem.module.scss new file mode 100644 index 0000000..b4e6dc1 --- /dev/null +++ b/src/pages/resume/ResumeItem.module.scss @@ -0,0 +1,52 @@ +@import 'variables'; + +.resumeItem { + padding-bottom: $spacer * 3; + padding-left: $spacer; + position: relative; + border-left: 1px solid $brand-grey-light; + + &::before { + content: ''; + display: block; + width: 1rem; + height: 1rem; + border-radius: 50%; + background: $brand-grey-light; + position: absolute; + left: -0.5rem; + } + + p:last-child { + margin-bottom: 0; + } +} + +.title { + margin-bottom: $spacer / 3; + font-size: $font-size-h4; + position: relative; + top: -($spacer / 6); +} + +.subTitle { + color: $brand-grey-light; + font-size: $font-size-h5; + + :global(.dark) & { + color: $brand-grey-dimmed; + } +} + +.time { + display: block; + margin-bottom: $spacer / 2; + white-space: nowrap; + + @media (min-width: $screen-md) { + text-align: right; + position: absolute; + top: -0.3rem; + right: 105%; + } +} diff --git a/src/pages/resume/index.jsx b/src/pages/resume/index.jsx new file mode 100644 index 0000000..eec1b5a --- /dev/null +++ b/src/pages/resume/index.jsx @@ -0,0 +1,70 @@ +import React from 'react' +import shortid from 'shortid' +import SEO from '../../components/atoms/SEO' +import LinkIcon from '../../components/atoms/LinkIcon' +import { useResume } from '../../hooks/use-resume' +import styles from './index.module.scss' +import ResumeItem from './ResumeItem' + +export default function Resume() { + const { basics, education, languages, work } = useResume() + + return ( + <> + + +
+
+

Rรฉsumรฉ

+

{basics.name}

+

{basics.label}

+
+ +
+ +
+ +
+

Work

+
+
+ {work.map(workPlace => ( + + ))} +
+ +
+

Education

+
+
+ {education.map(eduPlace => ( + + ))} +
+
+ + ) +} diff --git a/src/pages/resume/index.module.scss b/src/pages/resume/index.module.scss new file mode 100644 index 0000000..cffc176 --- /dev/null +++ b/src/pages/resume/index.module.scss @@ -0,0 +1,66 @@ +@import 'variables'; + +.resume { + padding: $spacer; + display: grid; + grid-gap: $spacer * 4; + grid-template-columns: 1fr; + + @media (min-width: $screen-md) { + grid-template-columns: 1fr 2fr; + max-width: calc(#{$projectImageMaxWidth} + #{$spacer * 2}); + margin: 0 auto; + padding: $spacer $spacer * 3; + } +} + +.title { + font-size: $font-size-h2; + margin-bottom: $spacer / 4; +} + +.label { + font-size: $font-size-h3; + color: $brand-grey-light; + margin-bottom: 0; + + :global(.dark) & { + color: $brand-grey-dimmed; + } +} + +.contact { + list-style: none; + padding: 0; + + svg { + fill: $brand-grey-light; + width: $font-size-small; + height: $font-size-small; + opacity: 0.5; + margin-right: $spacer / 4; + + :global(.dark) & { + fill: $brand-grey; + } + } + + @media (min-width: $screen-md) { + margin-top: $spacer * 2; + } +} + +.subTitle { + font-size: $font-size-h3; + margin-bottom: 0; +} + +@media print { + .resume { + grid-template-columns: 1fr; + } + + :global(html) { + font-size: 10pt; + } +} diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 24588c5..0d58290 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -40,10 +40,10 @@ $font-size-mini: 0.7rem; $font-size-h1: 2.5rem; $font-size-h2: 2rem; -$font-size-h3: 1.75rem; -$font-size-h4: $font-size-large; -$font-size-h5: $font-size-base; -$font-size-h6: $font-size-small; +$font-size-h3: 1.65rem; +$font-size-h4: 1.45rem; +$font-size-h5: $font-size-large; +$font-size-h6: $font-size-base; $line-height: 1.5; $line-height-small: 1.1428571429; diff --git a/static/robots.txt b/static/robots.txt index 214e411..2307ece 100644 --- a/static/robots.txt +++ b/static/robots.txt @@ -2,3 +2,4 @@ # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 User-agent: * +Disallow: /resume