From f39dc01e1af8b5c876ed51aaeb4d47794d6bfb5a Mon Sep 17 00:00:00 2001 From: plouc Date: Sun, 26 Dec 2021 12:15:46 +0900 Subject: [PATCH] feat(website): improve SEO --- website/gatsby-config.js | 72 +++---- website/package.json | 1 + website/src/components/Seo.tsx | 177 ++++++++++++------ .../components/ComponentTemplate.tsx | 6 +- yarn.lock | 64 ++++++- 5 files changed, 225 insertions(+), 95 deletions(-) diff --git a/website/gatsby-config.js b/website/gatsby-config.js index 1c6224be71..a336902bcb 100644 --- a/website/gatsby-config.js +++ b/website/gatsby-config.js @@ -1,36 +1,42 @@ +const siteUrl = process.env.SITE_URL || 'https://nivo.rocks' // no trailing slash + module.exports = { - siteMetadata: { - title: `nivo`, - description: `Supercharged React dataviz components, built on top of d3js.`, - author: `Raphaƫl Benitte`, - }, - plugins: [ - `gatsby-plugin-react-helmet`, - { - resolve: `gatsby-source-filesystem`, - options: { - name: `assets`, - path: `${__dirname}/src/assets`, - }, + siteMetadata: { + title: `nivo`, + description: `Supercharged React dataviz components.`, + siteUrl, + og: { + siteName: 'nivo', + twitterCreator: '@benitteraphael', + }, }, - `gatsby-transformer-sharp`, - `gatsby-plugin-sharp`, - { - resolve: `gatsby-plugin-styled-components`, - options: {}, - }, - { - resolve: `gatsby-plugin-manifest`, - options: { - name: `nivo`, - short_name: `nivo`, - start_url: `/`, - background_color: `#3c91e8`, - theme_color: `#3c91e8`, - display: `minimal-ui`, - icon: `src/assets/icons/nivo-icon.png`, // This path is relative to the root of the site. - }, - }, - `gatsby-plugin-offline`, - ], + plugins: [ + `gatsby-plugin-react-helmet`, + { + resolve: `gatsby-source-filesystem`, + options: { + name: `assets`, + path: `${__dirname}/src/assets`, + }, + }, + `gatsby-plugin-image`, + `gatsby-transformer-sharp`, + `gatsby-plugin-sharp`, + { + resolve: `gatsby-plugin-styled-components`, + options: {}, + }, + { + resolve: `gatsby-plugin-manifest`, + options: { + name: `nivo`, + short_name: `nivo`, + start_url: `/`, + background_color: `#3c91e8`, + theme_color: `#3c91e8`, + display: `minimal-ui`, + icon: `src/assets/icons/nivo-icon.png`, // This path is relative to the root of the site. + }, + }, + ], } diff --git a/website/package.json b/website/package.json index b6991da852..5a8d06540e 100644 --- a/website/package.json +++ b/website/package.json @@ -39,6 +39,7 @@ "dedent-js": "^1.0.1", "gatsby": "^4.4.0", "gatsby-image": "^3.11.0", + "gatsby-plugin-image": "^2.4.0", "gatsby-plugin-manifest": "^4.4.0", "gatsby-plugin-offline": "^5.4.0", "gatsby-plugin-react-helmet": "^5.4.0", diff --git a/website/src/components/Seo.tsx b/website/src/components/Seo.tsx index a2ca77b393..8fa0457651 100644 --- a/website/src/components/Seo.tsx +++ b/website/src/components/Seo.tsx @@ -1,87 +1,150 @@ import React from 'react' import Helmet from 'react-helmet' import { useStaticQuery, graphql } from 'gatsby' +import { IGatsbyImageData, getSrc } from 'gatsby-plugin-image' +// @ts-ignore +import { useLocation } from '@gatsbyjs/reach-router' + +interface Meta { + name: string + content: string +} interface SeoProps { title: string description?: string + image?: IGatsbyImageData lang?: string - meta?: object[] + meta?: Meta[] keywords?: string[] } +interface SiteData { + site: { + siteMetadata: { + title: string + description: string + siteUrl: string + og: { + siteName: string + twitterCreator: string + } + } + } +} + export const Seo = ({ - title, - description = '', + title: _title, + description: _description, + image, lang = 'en', - meta = [], - keywords = [], + keywords, }: SeoProps) => { - const { site } = useStaticQuery( + const location = useLocation() + + const { site } = useStaticQuery( graphql` query SiteMetaData { site { siteMetadata { title description - author + siteUrl + og { + siteName + twitterCreator + } } } } ` ) - const metaDescription = description || site.siteMetadata.description + const title = _title || site.siteMetadata.title + const description = _description || site.siteMetadata.description + + const metas: Meta[] = [ + // TITLE + { + name: 'og:title', + content: title, + }, + { + name: 'twitter:title', + content: title, + }, + // DESCRIPTION + { + name: 'description', + content: description, + }, + { + name: 'og:description', + content: description, + }, + { + name: 'twitter:description', + content: description, + }, + // OTHERS + { + name: 'og:type', + content: 'website', + }, + { + name: 'og:site_name', + content: site.siteMetadata.og.siteName, + }, + { + name: 'twitter:card', + content: 'summary_large_image', + }, + { + name: 'twitter:creator', + content: site.siteMetadata.og.twitterCreator, + }, + { + name: 'og:url', + content: `${site.siteMetadata.siteUrl}${location.pathname}`, + }, + ] + + if (keywords) { + metas.push({ + name: `keywords`, + content: keywords.join(`, `), + }) + } + + if (image) { + metas.push({ + name: 'og:image:width', + content: `${image.width}`, + }) + metas.push({ + name: 'og:image:height', + content: `${image.height}`, + }) + + const imageSrc = `${site.siteMetadata.siteUrl}${getSrc(image)!}` + metas.push({ + name: 'og:image:secure_url', + content: imageSrc, + }) + metas.push({ + name: 'twitter:image', + content: imageSrc, + }) + } return ( - 0 - ? { - name: `keywords`, - content: keywords.join(`, `), - } - : [] - )} - /> + + + + {title} + {metas.map(meta => ( + + ))} + ) } diff --git a/website/src/components/components/ComponentTemplate.tsx b/website/src/components/components/ComponentTemplate.tsx index 5db2d6be06..92f14bbebf 100644 --- a/website/src/components/components/ComponentTemplate.tsx +++ b/website/src/components/components/ComponentTemplate.tsx @@ -1,5 +1,6 @@ import React, { useState, useCallback, useMemo } from 'react' import { Theme as NivoTheme } from '@nivo/core' +import { startCase } from 'lodash' import { Seo } from '../Seo' import Layout from '../Layout' import { useTheme } from '../../theming/context' @@ -61,6 +62,7 @@ export const ComponentTemplate = < dataKey, getDataSize, getTabData = data => data, + image, children, }: ComponentTemplateProps) => { const theme = useTheme() @@ -93,6 +95,8 @@ export const ComponentTemplate = < const hasStories = meta.stories !== undefined && meta.stories.length > 0 + const title = `${startCase(name)} chart` + const description = `${meta.package} package ${startCase(name)} chart.` const tags = useMemo(() => [meta.package, ...meta.tags], [meta]) const flavorKeys = useMemo(() => flavors.map(flavor => flavor.flavor), [flavors]) @@ -100,7 +104,7 @@ export const ComponentTemplate = < return ( - + diff --git a/yarn.lock b/yarn.lock index ce97f6096e..7898d0ab1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4758,7 +4758,7 @@ "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*", "@types/eslint@7.29.0", "@types/eslint@^7.28.2": +"@types/eslint@*", "@types/eslint@^7.28.2": version "7.29.0" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== @@ -5095,7 +5095,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0.11", "@types/react@^17.0.37": +"@types/react@*", "@types/react@^17.0.37": version "17.0.37" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959" integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg== @@ -6429,6 +6429,11 @@ babel-jest@^27.4.5: graceful-fs "^4.2.4" slash "^3.0.0" +babel-jsx-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-jsx-utils/-/babel-jsx-utils-1.1.0.tgz#304ce4fce0c86cbeee849551a45eb4ed1036381a" + integrity sha512-Mh1j/rw4xM9T3YICkw22aBQ78FhsHdsmlb9NEk4uVAFBOg+Ez9ZgXXHugoBPCZui3XLomk/7/JBBH4daJqTkQQ== + babel-loader@^8.0.0, babel-loader@^8.2.3: version "8.2.3" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" @@ -11278,6 +11283,25 @@ gatsby-page-utils@^2.4.0: lodash "^4.17.21" micromatch "^4.0.4" +gatsby-plugin-image@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/gatsby-plugin-image/-/gatsby-plugin-image-2.4.0.tgz#fd960393043e856eb70d998dc34ad96a738162a8" + integrity sha512-PKhbefaamdahm1ysKO1d54vnzmIsjR+/qtyFnZumGu8E7O9lN5is0RlXgpNCvsckL+XzcqyMljytt/bYf8L5WA== + dependencies: + "@babel/code-frame" "^7.14.0" + "@babel/parser" "^7.15.5" + "@babel/runtime" "^7.15.4" + "@babel/traverse" "^7.15.4" + babel-jsx-utils "^1.1.0" + babel-plugin-remove-graphql-queries "^4.4.0" + camelcase "^5.3.1" + chokidar "^3.5.2" + common-tags "^1.8.2" + fs-extra "^10.0.0" + gatsby-core-utils "^3.4.0" + objectFitPolyfill "^2.3.5" + prop-types "^15.7.2" + gatsby-plugin-manifest@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/gatsby-plugin-manifest/-/gatsby-plugin-manifest-4.4.0.tgz#c27a6d63a5c74a117f8099dac4c6df324e4a605c" @@ -16622,6 +16646,11 @@ object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.5: define-properties "^1.1.3" es-abstract "^1.19.1" +objectFitPolyfill@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/objectFitPolyfill/-/objectFitPolyfill-2.3.5.tgz#be8c83064aabfa4e88780f776c2013c48ce1f745" + integrity sha512-8Quz071ZmGi0QWEG4xB3Bv5Lpw6K0Uca87FLoLMKMWjB6qIq9IyBegP3b/VLNxv2WYvIMGoeUQ+c6ibUkNa8TA== + objectorarray@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" @@ -18307,7 +18336,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@17.0.2, react-dom@^16.8.3, react-dom@^17.0.2: +react-dom@17.0.2, react-dom@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -18316,6 +18345,16 @@ react-dom@17.0.2, react-dom@^16.8.3, react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-dom@^16.8.3: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" + integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.19.1" + react-draggable@^4.4.3: version "4.4.4" resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.4.tgz#5b26d9996be63d32d285a426f41055de87e59b2f" @@ -18613,7 +18652,7 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react@17.0.2, react@^16.8.3, react@^17.0.2: +react@17.0.2, react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== @@ -18621,6 +18660,15 @@ react@17.0.2, react@^16.8.3, react@^17.0.2: loose-envify "^1.1.0" object-assign "^4.1.1" +react@^16.8.3: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -19511,6 +19559,14 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"