mirror of
https://github.com/Unkn0wnCat/KevinK.dev.js.git
synced 2025-04-29 18:26:52 +02:00
Add more structured metadata
This commit is contained in:
parent
df9e2b2bb8
commit
412ee72112
10 changed files with 148 additions and 5 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"i18n-ally.localesPaths": "locales"
|
"i18n-ally.localesPaths": "locales",
|
||||||
|
"i18n-ally.keystyle": "nested"
|
||||||
}
|
}
|
|
@ -10,9 +10,18 @@ module.exports = {
|
||||||
languages: ["en", "de"],
|
languages: ["en", "de"],
|
||||||
contactEmail: "kevin@kevink.dev",
|
contactEmail: "kevin@kevink.dev",
|
||||||
contactPhone: "+4941068068004",
|
contactPhone: "+4941068068004",
|
||||||
|
address: "25451 Quickborn, Schleswig-Holstein, Germany",
|
||||||
mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2",
|
mapsLink: "https://goo.gl/maps/KVq9z1PVaVP2",
|
||||||
contactTwitter: "Unkn0wnKevin",
|
contactTwitter: "Unkn0wnKevin",
|
||||||
contactGitHub: "Unkn0wnCat",
|
contactGitHub: "Unkn0wnCat",
|
||||||
contactMastodon: "@kevin@1in1.net",
|
contactMastodon: "@kevin@1in1.net",
|
||||||
contactMastodonHref: "https://mastodon.1in1.net/@kevin",
|
contactMastodonHref: "https://mastodon.1in1.net/@kevin",
|
||||||
|
givenName: "Kevin",
|
||||||
|
familyName: "Kandlbinder",
|
||||||
|
birthDate: "2001-03-11",
|
||||||
|
gender: "Male",
|
||||||
|
height: "168 cm",
|
||||||
|
nationality: "Germany",
|
||||||
|
personImage: "./content/images/kevin-kandlbinder-04.jpg",
|
||||||
|
sameAs: ["https://mastodon.1in1.net/@kevin", "https://github.com/Unkn0wnCat", "https://unkn0wncat.net/", "https://twitter.com/@Unkn0wnKevin", "https://unsplash.com/@unkn0wncat"]
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,10 +11,19 @@ module.exports = {
|
||||||
contactEmail: extConfig.contactEmail,
|
contactEmail: extConfig.contactEmail,
|
||||||
contactPhone: extConfig.contactPhone,
|
contactPhone: extConfig.contactPhone,
|
||||||
mapsLink: extConfig.mapsLink,
|
mapsLink: extConfig.mapsLink,
|
||||||
|
address: extConfig.address,
|
||||||
contactTwitter: extConfig.contactTwitter,
|
contactTwitter: extConfig.contactTwitter,
|
||||||
contactGitHub: extConfig.contactGitHub,
|
contactGitHub: extConfig.contactGitHub,
|
||||||
contactMastodon: extConfig.contactMastodon,
|
contactMastodon: extConfig.contactMastodon,
|
||||||
contactMastodonHref: extConfig.contactMastodonHref,
|
contactMastodonHref: extConfig.contactMastodonHref,
|
||||||
|
givenName: extConfig.givenName,
|
||||||
|
familyName: extConfig.familyName,
|
||||||
|
birthDate: extConfig.birthDate,
|
||||||
|
gender: extConfig.gender,
|
||||||
|
height: extConfig.height,
|
||||||
|
nationality: extConfig.nationality,
|
||||||
|
personImage: extConfig.personImage,
|
||||||
|
sameAs: extConfig.sameAs
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
`gatsby-plugin-eslint`,
|
`gatsby-plugin-eslint`,
|
||||||
|
|
|
@ -2,9 +2,11 @@ import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { Helmet } from "gatsby-plugin-react-i18next";
|
import { Helmet } from "gatsby-plugin-react-i18next";
|
||||||
import { useStaticQuery, graphql } from "gatsby";
|
import { useStaticQuery, graphql } from "gatsby";
|
||||||
|
import { useLocation } from "@reach/router"
|
||||||
import { useTranslation } from "gatsby-plugin-react-i18next";
|
import { useTranslation } from "gatsby-plugin-react-i18next";
|
||||||
|
import useSiteMetadata from "../helpers/useSiteMetadata";
|
||||||
|
|
||||||
function SEO({ description, meta, title }) {
|
function SEO({ description, meta, title, speakable, image, children }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { site } = useStaticQuery(
|
const { site } = useStaticQuery(
|
||||||
graphql`
|
graphql`
|
||||||
|
@ -22,6 +24,10 @@ function SEO({ description, meta, title }) {
|
||||||
|
|
||||||
const metaDescription = description || t("siteDescription");
|
const metaDescription = description || t("siteDescription");
|
||||||
|
|
||||||
|
const siteMeta = useSiteMetadata();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Helmet
|
<Helmet
|
||||||
title={title}
|
title={title}
|
||||||
|
@ -79,6 +85,45 @@ function SEO({ description, meta, title }) {
|
||||||
data-domain="kevink.dev"
|
data-domain="kevink.dev"
|
||||||
src="https://analytics.kevink.dev/js/plausible.js"
|
src="https://analytics.kevink.dev/js/plausible.js"
|
||||||
></script>
|
></script>
|
||||||
|
|
||||||
|
{
|
||||||
|
image && [
|
||||||
|
<meta name="twitter:image" content={meta.siteUrl + image} key="twimg"/>,
|
||||||
|
<meta name="og:image" content={meta.siteUrl + image} key="ogimg"/>,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{JSON.stringify({
|
||||||
|
"@context": "https://schema.org/",
|
||||||
|
"@type": "WebPage",
|
||||||
|
"name": title,
|
||||||
|
"url": siteMeta.siteUrl+location.pathname,
|
||||||
|
"speakable": speakable,
|
||||||
|
"image": meta.siteUrl + image,
|
||||||
|
"about": {
|
||||||
|
"@type": "Person",
|
||||||
|
"name": siteMeta.givenName + " " + siteMeta.familyName,
|
||||||
|
"givenName": siteMeta.givenName,
|
||||||
|
"familyName": siteMeta.familyName,
|
||||||
|
"birthDate": siteMeta.birthDate,
|
||||||
|
"address": siteMeta.address,
|
||||||
|
"email": siteMeta.contactEmail,
|
||||||
|
"telephone": siteMeta.contactPhone,
|
||||||
|
"gender": siteMeta.gender,
|
||||||
|
"height": siteMeta.height,
|
||||||
|
"nationality": {
|
||||||
|
"@type": "Country",
|
||||||
|
"name": siteMeta.nationality
|
||||||
|
},
|
||||||
|
"image": siteMeta.siteUrl + "/owner.jpg",
|
||||||
|
"sameAs": siteMeta.sameAs
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{children}
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -92,6 +137,9 @@ SEO.propTypes = {
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
meta: PropTypes.arrayOf(PropTypes.object),
|
meta: PropTypes.arrayOf(PropTypes.object),
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
speakable: PropTypes.any,
|
||||||
|
image: PropTypes.string,
|
||||||
|
children: PropTypes.any
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SEO;
|
export default SEO;
|
||||||
|
|
37
src/helpers/useSiteMetadata.js
Normal file
37
src/helpers/useSiteMetadata.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby';
|
||||||
|
|
||||||
|
const useSiteMetadata = () => {
|
||||||
|
const { site } = useStaticQuery(
|
||||||
|
graphql`
|
||||||
|
query {
|
||||||
|
site {
|
||||||
|
siteMetadata {
|
||||||
|
title
|
||||||
|
keywords
|
||||||
|
author
|
||||||
|
siteUrl
|
||||||
|
payPalMail
|
||||||
|
contactEmail
|
||||||
|
contactPhone
|
||||||
|
mapsLink
|
||||||
|
contactTwitter
|
||||||
|
contactGitHub
|
||||||
|
contactMastodon
|
||||||
|
contactMastodonHref
|
||||||
|
givenName
|
||||||
|
familyName
|
||||||
|
birthDate
|
||||||
|
address
|
||||||
|
gender
|
||||||
|
height
|
||||||
|
nationality
|
||||||
|
sameAs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
return site.siteMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useSiteMetadata;
|
|
@ -16,7 +16,9 @@ class Layout extends React.Component {
|
||||||
lang={this.props.lang}
|
lang={this.props.lang}
|
||||||
meta={this.props.meta}
|
meta={this.props.meta}
|
||||||
title={this.props.title}
|
title={this.props.title}
|
||||||
/>
|
image={this.props.image}
|
||||||
|
speakable={this.props.speakable}
|
||||||
|
>{this.props.seoAdditional ?? null}</SEO>
|
||||||
<Navigation isHome={this.props.transparentTopbar} />
|
<Navigation isHome={this.props.transparentTopbar} />
|
||||||
<div id="content" role="main">
|
<div id="content" role="main">
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -57,6 +59,9 @@ Layout.propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
transparentTopbar: PropTypes.bool,
|
transparentTopbar: PropTypes.bool,
|
||||||
children: PropTypes.any.isRequired,
|
children: PropTypes.any.isRequired,
|
||||||
|
seoAdditional: PropTypes.any,
|
||||||
|
image: PropTypes.string,
|
||||||
|
speakable: PropTypes.any
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
|
|
@ -112,7 +112,16 @@ const IndexPage = (props) => {
|
||||||
let file = props.data.file;
|
let file = props.data.file;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title="Kevin Kandlbinder" transparentTopbar={true} description={t("siteDescription")}>
|
<Layout title="Kevin Kandlbinder" transparentTopbar={true} description={t("siteDescription")}
|
||||||
|
image={meta.siteUrl + "/owner.jpg"}
|
||||||
|
speakable={{
|
||||||
|
"@type": "SpeakableSpecification",
|
||||||
|
"xPath": [
|
||||||
|
"/html/head/title",
|
||||||
|
"/html/head/meta[@name='description']/@content",
|
||||||
|
"article"
|
||||||
|
]
|
||||||
|
}}>
|
||||||
<section className={styles.heroSection}>
|
<section className={styles.heroSection}>
|
||||||
<div
|
<div
|
||||||
className={styles.heroSectionBg}
|
className={styles.heroSectionBg}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import PropTypes from "prop-types";
|
||||||
|
|
||||||
import * as styles from "./projects.module.scss";
|
import * as styles from "./projects.module.scss";
|
||||||
import { GatsbyImage } from "gatsby-plugin-image";
|
import { GatsbyImage } from "gatsby-plugin-image";
|
||||||
|
import useSiteMetadata from "../helpers/useSiteMetadata";
|
||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query GetProjects($language: String) {
|
query GetProjects($language: String) {
|
||||||
|
@ -21,6 +22,7 @@ export const query = graphql`
|
||||||
childImageSharp {
|
childImageSharp {
|
||||||
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
|
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
|
||||||
}
|
}
|
||||||
|
publicURL
|
||||||
}
|
}
|
||||||
shortDescription
|
shortDescription
|
||||||
}
|
}
|
||||||
|
@ -39,8 +41,29 @@ export const query = graphql`
|
||||||
|
|
||||||
const ProjectsPage = ({ data }) => {
|
const ProjectsPage = ({ data }) => {
|
||||||
const { t } = useI18next();
|
const { t } = useI18next();
|
||||||
|
const meta = useSiteMetadata();
|
||||||
return (
|
return (
|
||||||
<Layout title={t("projects")} description={t("projectsDescription")}>
|
<Layout title={t("projects")} description={t("projectsDescription")} seoAdditional={
|
||||||
|
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{JSON.stringify(
|
||||||
|
{
|
||||||
|
"@context":"https://schema.org",
|
||||||
|
"@type":"ItemList",
|
||||||
|
"itemListElement":data.allProjectsJson.nodes.map((project, i) => {
|
||||||
|
return {
|
||||||
|
"@type":"ListItem",
|
||||||
|
"position":i,
|
||||||
|
"url": meta.siteUrl+"/projects/"+project.urlname,
|
||||||
|
"image": project.image.publicURL,
|
||||||
|
"name": project.name,
|
||||||
|
"description": project.shortDescription
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</script>
|
||||||
|
}>
|
||||||
<section>
|
<section>
|
||||||
<article>
|
<article>
|
||||||
<h1>
|
<h1>
|
||||||
|
|
|
@ -26,6 +26,7 @@ export const query = graphql`
|
||||||
childImageSharp {
|
childImageSharp {
|
||||||
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
|
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
|
||||||
}
|
}
|
||||||
|
publicURL
|
||||||
}
|
}
|
||||||
shortDescription
|
shortDescription
|
||||||
}
|
}
|
||||||
|
@ -64,6 +65,7 @@ const ProjectTemplate = ({ data }) => {
|
||||||
description={project.shortDescription}
|
description={project.shortDescription}
|
||||||
title={t("project") + ": " + projectName}
|
title={t("project") + ": " + projectName}
|
||||||
transparentTopbar={true}
|
transparentTopbar={true}
|
||||||
|
image={project.image.publicURL}
|
||||||
>
|
>
|
||||||
<section className={styles.projectHeader}>
|
<section className={styles.projectHeader}>
|
||||||
<div style={{ paddingTop: 0 }}>
|
<div style={{ paddingTop: 0 }}>
|
||||||
|
|
BIN
static/owner.jpg
Normal file
BIN
static/owner.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
Loading…
Add table
Reference in a new issue