mirror of
https://github.com/kremalicious/portfolio.git
synced 2025-01-03 18:35:00 +01:00
get most site metadata from the resume
This commit is contained in:
parent
b46b006dd7
commit
e84de746c7
52
README.md
52
README.md
@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
- [🎉 Features](#-features)
|
- [🎉 Features](#-features)
|
||||||
- [⛵️ Lighthouse score](#️-lighthouse-score)
|
- [⛵️ Lighthouse score](#️-lighthouse-score)
|
||||||
|
- [🗂 JSON Resume](#-json-resume)
|
||||||
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
|
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
|
||||||
- [🐱 GitHub repositories](#-github-repositories)
|
- [🐱 GitHub repositories](#-github-repositories)
|
||||||
- 🗂 JSON Resume](#-json-resume)
|
|
||||||
- [💅 Theme switcher](#-theme-switcher)
|
- [💅 Theme switcher](#-theme-switcher)
|
||||||
- [🏆 SEO component](#-seo-component)
|
- [🏆 SEO component](#-seo-component)
|
||||||
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
|
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
|
||||||
@ -38,37 +38,17 @@
|
|||||||
|
|
||||||
## 🎉 Features
|
## 🎉 Features
|
||||||
|
|
||||||
The whole [portfolio](https://matthiaskretschmann.com) is a React-based Single Page App built with [Gatsby v2](https://www.gatsbyjs.org).
|
The whole [portfolio](https://matthiaskretschmann.com) is a React-based single page app built with [Gatsby v2](https://www.gatsbyjs.org).
|
||||||
|
|
||||||
|
Most metadata is powered by one `resume.json` file based on [🗂 JSON Resume](#-json-resume), and one `projects.yml` file to [define the displayed projects](#-one-data-file-to-rule-all-pages).
|
||||||
|
|
||||||
### ⛵️ Lighthouse score
|
### ⛵️ Lighthouse score
|
||||||
|
|
||||||
![Lighthouse scores](https://lighthouse.now.sh/?perf=100&pwa=100&a11y=100&bp=100&seo=100)
|
![Lighthouse scores](https://lighthouse.now.sh/?perf=100&pwa=100&a11y=100&bp=100&seo=100)
|
||||||
|
|
||||||
### 💍 One data file to rule all pages
|
|
||||||
|
|
||||||
All content is powered by one YAML file where all the portfolio's projects are defined. The project description itself is transformed from Markdown written inside the YAML file into HTML on build time.
|
|
||||||
|
|
||||||
Gatsby automatically creates pages from each item in that file utilizing the [`Project.jsx`](src/templates/Project.jsx) template.
|
|
||||||
|
|
||||||
- [`gatsby-node.js`](gatsby-node.js)
|
|
||||||
- [`content/projects.yml`](content/projects.yml)
|
|
||||||
- [`src/templates/Project.jsx`](src/templates/Project.jsx)
|
|
||||||
|
|
||||||
### 🐱 GitHub repositories
|
|
||||||
|
|
||||||
The open source section at the bottom of the front page shows selected GitHub repositories, sourced from GitHub.
|
|
||||||
|
|
||||||
On build time, all my public repositories are fetched from GitHub, then filtered against the ones defined in `content/repos.yml`, sorted by the last push date, and provided via the page context of the front page.
|
|
||||||
|
|
||||||
If you want to know how, have a look at the respective components:
|
|
||||||
|
|
||||||
- [`gatsby-node.js`](gatsby-node.js)
|
|
||||||
- [`content/repos.yml`](content/repos.yml)
|
|
||||||
- [`src/components/molecules/Repository.jsx`](src/components/molecules/Repository.jsx)
|
|
||||||
|
|
||||||
### 🗂 JSON Resume
|
### 🗂 JSON Resume
|
||||||
|
|
||||||
Resume page based on [JSON Resume](https://jsonresume.org) standard. Most site metadata and social profiles are defined in [`content/resume.json`](content/resume.json) and used throughout the site.
|
Most site metadata and social profiles are defined in [`content/resume.json`](content/resume.json) based on the [JSON Resume](https://jsonresume.org) standard and used throughout the site as a custom React hook. Additionally, a resume page is created under `/resume`.
|
||||||
|
|
||||||
If you want to know how, have a look at the respective components:
|
If you want to know how, have a look at the respective components:
|
||||||
|
|
||||||
@ -76,6 +56,28 @@ If you want to know how, have a look at the respective components:
|
|||||||
- [`src/pages/resume/index.jsx`](src/pages/resume/index.jsx)
|
- [`src/pages/resume/index.jsx`](src/pages/resume/index.jsx)
|
||||||
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
|
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
|
||||||
|
|
||||||
|
### 💍 One data file to rule all pages
|
||||||
|
|
||||||
|
All displayed project content is powered by one YAML file where all the portfolio's projects are defined. The project description itself is transformed from Markdown written inside the YAML file into HTML on build time.
|
||||||
|
|
||||||
|
Gatsby automatically creates pages from each item in that file utilizing the [`Project.jsx`](src/templates/Project.jsx) template.
|
||||||
|
|
||||||
|
- [`content/projects.yml`](content/projects.yml)
|
||||||
|
- [`gatsby-node.js`](gatsby-node.js)
|
||||||
|
- [`src/templates/Project.jsx`](src/templates/Project.jsx)
|
||||||
|
|
||||||
|
### 🐱 GitHub repositories
|
||||||
|
|
||||||
|
The open source section at the bottom of the front page shows selected GitHub repositories, sourced from GitHub.
|
||||||
|
|
||||||
|
On build time, all my public repositories are fetched from GitHub, then filtered against the ones defined in `content/repos.yml`, sorted by the last push date, and provided via the `pageContext` of the front page.
|
||||||
|
|
||||||
|
If you want to know how, have a look at the respective components:
|
||||||
|
|
||||||
|
- [`gatsby-node.js`](gatsby-node.js)
|
||||||
|
- [`content/repos.yml`](content/repos.yml)
|
||||||
|
- [`src/components/molecules/Repository.jsx`](src/components/molecules/Repository.jsx)
|
||||||
|
|
||||||
### 💅 Theme switcher
|
### 💅 Theme switcher
|
||||||
|
|
||||||
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's local sunset and sunrise times. Uses Cloudflare's geo location HTTP header functionality.
|
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's local sunset and sunrise times. Uses Cloudflare's geo location HTTP header functionality.
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
# more personal metadata can be found in ./resume.json
|
# most personal metadata can be found in ./resume.json
|
||||||
|
|
||||||
- title: Matthias Kretschmann
|
- description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
|
||||||
tagline: Designer & Developer
|
|
||||||
description: Portfolio of web & ui designer/developer hybrid Matthias Kretschmann.
|
|
||||||
url: https://matthiaskretschmann.com
|
|
||||||
img: ../src/images/twitter-card.png
|
img: ../src/images/twitter-card.png
|
||||||
|
|
||||||
availability:
|
availability:
|
||||||
|
@ -2,13 +2,15 @@ const path = require('path')
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const yaml = require('js-yaml')
|
const yaml = require('js-yaml')
|
||||||
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8'))
|
const meta = yaml.load(fs.readFileSync('./content/meta.yml', 'utf8'))
|
||||||
const { title, url, matomoSite, matomoUrl } = meta[0]
|
const resume = require('./content/resume.json')
|
||||||
|
const { matomoSite, matomoUrl } = meta[0]
|
||||||
|
const { name, website } = resume.basics
|
||||||
|
|
||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
siteMetadata: {
|
siteMetadata: {
|
||||||
siteUrl: `${url}`
|
siteUrl: `${website}`
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
'gatsby-transformer-yaml',
|
'gatsby-transformer-yaml',
|
||||||
@ -50,7 +52,7 @@ module.exports = {
|
|||||||
resolve: 'gatsby-plugin-matomo',
|
resolve: 'gatsby-plugin-matomo',
|
||||||
options: {
|
options: {
|
||||||
siteId: `${matomoSite}`,
|
siteId: `${matomoSite}`,
|
||||||
siteUrl: `${url}`,
|
siteUrl: `${website}`,
|
||||||
matomoUrl: `${matomoUrl}`,
|
matomoUrl: `${matomoUrl}`,
|
||||||
localScript: '/piwik.js'
|
localScript: '/piwik.js'
|
||||||
}
|
}
|
||||||
@ -58,7 +60,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
resolve: 'gatsby-plugin-manifest',
|
resolve: 'gatsby-plugin-manifest',
|
||||||
options: {
|
options: {
|
||||||
name: title.toLowerCase(),
|
name: name.toLowerCase(),
|
||||||
short_name: 'mk',
|
short_name: 'mk',
|
||||||
start_url: '/',
|
start_url: '/',
|
||||||
background_color: '#e7eef4',
|
background_color: '#e7eef4',
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -12308,9 +12308,9 @@
|
|||||||
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ=="
|
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ=="
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.5.2",
|
"version": "4.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
|
||||||
"integrity": "sha512-29Zxv/cynYB7mkT1rVWQnV7mGX6v7H/miQ6dbEpYTKq5eJBN7PsRB+ViYJlcT6JINTSu4dVB9kOqEun78h6Exg==",
|
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
|
@ -5,36 +5,36 @@ import { useMeta } from '../../hooks/use-meta'
|
|||||||
import { useResume } from '../../hooks/use-resume'
|
import { useResume } from '../../hooks/use-resume'
|
||||||
|
|
||||||
const MetaTags = ({ title, description, url, image, meta }) => {
|
const MetaTags = ({ title, description, url, image, meta }) => {
|
||||||
const resume = useResume()
|
const { basics } = useResume()
|
||||||
const twitterHandle = resume.basics.profiles.filter(
|
const twitterHandle = basics.profiles.filter(
|
||||||
({ network }) => network === 'Twitter'
|
({ network }) => network === 'Twitter'
|
||||||
)[0].username
|
)[0].username
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Helmet
|
<Helmet
|
||||||
defaultTitle={`${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`}
|
defaultTitle={`${basics.name.toLowerCase()} { ${basics.label.toLowerCase()} }`}
|
||||||
titleTemplate={`%s // ${meta.title.toLowerCase()} { ${meta.tagline.toLowerCase()} }`}
|
titleTemplate={`%s // ${basics.name.toLowerCase()} { ${basics.label.toLowerCase()} }`}
|
||||||
title={title}
|
title={title}
|
||||||
>
|
>
|
||||||
<html lang="en" />
|
<html lang="en" />
|
||||||
|
|
||||||
{/* General tags */}
|
{/* General tags */}
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<meta name="image" content={`${meta.url}${image}`} />
|
<meta name="image" content={`${basics.website}${image}`} />
|
||||||
<link rel="canonical" href={url} />
|
<link rel="canonical" href={url} />
|
||||||
|
|
||||||
{/* OpenGraph tags */}
|
{/* OpenGraph tags */}
|
||||||
<meta property="og:url" content={url} />
|
<meta property="og:url" content={url} />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:image" content={`${meta.url}${image}`} />
|
<meta property="og:image" content={`${basics.website}${image}`} />
|
||||||
|
|
||||||
{/* Twitter Card tags */}
|
{/* Twitter Card tags */}
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="twitter:creator" content={twitterHandle} />
|
<meta name="twitter:creator" content={twitterHandle} />
|
||||||
<meta name="twitter:title" content={title} />
|
<meta name="twitter:title" content={title} />
|
||||||
<meta name="twitter:description" content={description} />
|
<meta name="twitter:description" content={description} />
|
||||||
<meta name="twitter:image" content={`${meta.url}${image}`} />
|
<meta name="twitter:image" content={`${basics.website}${image}`} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -53,12 +53,13 @@ SEO.propTypes = {
|
|||||||
|
|
||||||
export default function SEO({ project }) {
|
export default function SEO({ project }) {
|
||||||
const meta = useMeta()
|
const meta = useMeta()
|
||||||
|
const { basics } = useResume()
|
||||||
const title = (project && project.title) || null
|
const title = (project && project.title) || null
|
||||||
const description = (project && project.fields.excerpt) || meta.description
|
const description = (project && project.fields.excerpt) || meta.description
|
||||||
const image =
|
const image =
|
||||||
(project && project.img.childImageSharp.twitterImage.src) ||
|
(project && project.img.childImageSharp.twitterImage.src) ||
|
||||||
meta.img.childImageSharp.resize.src
|
meta.img.childImageSharp.resize.src
|
||||||
const url = (project && `${meta.url}${project.slug}`) || meta.url
|
const url = (project && `${basics.website}${project.slug}`) || basics.website
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MetaTags
|
<MetaTags
|
||||||
|
@ -2,10 +2,10 @@ import React from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import posed from 'react-pose'
|
import posed from 'react-pose'
|
||||||
import { useMeta } from '../../hooks/use-meta'
|
|
||||||
import { moveInBottom } from '../atoms/Transitions'
|
import { moveInBottom } from '../atoms/Transitions'
|
||||||
import { ReactComponent as Logo } from '../../images/logo.svg'
|
import { ReactComponent as Logo } from '../../images/logo.svg'
|
||||||
import styles from './LogoUnit.module.scss'
|
import styles from './LogoUnit.module.scss'
|
||||||
|
import { useResume } from '../../hooks/use-resume'
|
||||||
|
|
||||||
LogoUnit.propTypes = {
|
LogoUnit.propTypes = {
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool,
|
||||||
@ -13,16 +13,18 @@ LogoUnit.propTypes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function LogoUnit({ minimal }) {
|
export default function LogoUnit({ minimal }) {
|
||||||
const { title, tagline } = useMeta()
|
const { basics } = useResume()
|
||||||
const Animation = posed.div(moveInBottom)
|
const Animation = posed.div(moveInBottom)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animation>
|
<Animation>
|
||||||
<Link className={minimal ? styles.minimal : styles.logounit} to={'/'}>
|
<Link className={minimal ? styles.minimal : styles.logounit} to={'/'}>
|
||||||
<Logo className={styles.logo} />
|
<Logo className={styles.logo} />
|
||||||
<h1 className={`p-name ${styles.title}`}>{title.toLowerCase()}</h1>
|
<h1 className={`p-name ${styles.title}`}>
|
||||||
|
{basics.name.toLowerCase()}
|
||||||
|
</h1>
|
||||||
<p className={`p-job-title ${styles.description}`}>
|
<p className={`p-job-title ${styles.description}`}>
|
||||||
{tagline.toLowerCase()}
|
{basics.label.toLowerCase()}
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
</Animation>
|
</Animation>
|
||||||
|
@ -3,10 +3,7 @@ import { useStaticQuery, graphql } from 'gatsby'
|
|||||||
const query = graphql`
|
const query = graphql`
|
||||||
query Meta {
|
query Meta {
|
||||||
metaYaml {
|
metaYaml {
|
||||||
title
|
|
||||||
tagline
|
|
||||||
description
|
description
|
||||||
url
|
|
||||||
img {
|
img {
|
||||||
childImageSharp {
|
childImageSharp {
|
||||||
resize(width: 980) {
|
resize(width: 980) {
|
||||||
|
Loading…
Reference in New Issue
Block a user