diff --git a/gatsby-config.js b/gatsby-config.js
index ed0799f34f5..456396f9272 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -55,7 +55,7 @@ module.exports = {
maxWidth: 840,
},
},
- 'gatsby-remark-autolink-headers',
+ 'gatsby-remark-header-custom-ids',
{
resolve: 'gatsby-remark-code-repls',
options: {
diff --git a/package.json b/package.json
index a9930ccc89b..d050abaf59c 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,6 @@
"gatsby-plugin-react-helmet": "^3.0.0",
"gatsby-plugin-sharp": "^2.0.0",
"gatsby-plugin-twitter": "^2.0.0",
- "gatsby-remark-autolink-headers": "^2.0.12",
"gatsby-remark-code-repls": "^2.0.0",
"gatsby-remark-copy-linked-files": "^2.0.0",
"gatsby-remark-embed-snippet": "^3.0.0",
@@ -40,8 +39,10 @@
"gatsby-source-filesystem": "^2.0.0",
"gatsby-transformer-remark": "^2.0.0",
"gatsby-transformer-sharp": "^2.0.0",
+ "github-slugger": "^1.2.1",
"glamor": "^2.20.40",
"hex2rgba": "^0.0.1",
+ "mdast-util-to-string": "^1.0.5",
"normalize.css": "^8.0.0",
"prettier": "^1.7.4",
"prismjs": "^1.15.0",
diff --git a/plugins/gatsby-remark-header-custom-ids/gatsby-client.js b/plugins/gatsby-remark-header-custom-ids/gatsby-client.js
new file mode 100644
index 00000000000..6b24d0b13a8
--- /dev/null
+++ b/plugins/gatsby-remark-header-custom-ids/gatsby-client.js
@@ -0,0 +1,30 @@
+let offsetY = 0;
+
+const getTargetOffset = hash => {
+ const id = window.decodeURI(hash.replace(`#`, ``));
+ if (id !== ``) {
+ const element = document.getElementById(id);
+ if (element) {
+ return element.offsetTop - offsetY;
+ }
+ }
+ return null;
+};
+
+exports.onInitialClientRender = (_, pluginOptions) => {
+ if (pluginOptions.offsetY) {
+ offsetY = pluginOptions.offsetY;
+ }
+
+ requestAnimationFrame(() => {
+ const offset = getTargetOffset(window.location.hash);
+ if (offset !== null) {
+ window.scrollTo(0, offset);
+ }
+ });
+};
+
+exports.shouldUpdateScroll = ({routerProps: {location}}) => {
+ const offset = getTargetOffset(location.hash);
+ return offset !== null ? [0, offset] : true;
+};
diff --git a/plugins/gatsby-remark-header-custom-ids/gatsby-ssr.js b/plugins/gatsby-remark-header-custom-ids/gatsby-ssr.js
new file mode 100644
index 00000000000..c616fc289e4
--- /dev/null
+++ b/plugins/gatsby-remark-header-custom-ids/gatsby-ssr.js
@@ -0,0 +1,76 @@
+const React = require(`react`);
+
+const pluginDefaults = {
+ className: `anchor`,
+ icon: true,
+ offsetY: 0,
+};
+
+exports.onRenderBody = ({setHeadComponents}, pluginOptions) => {
+ const {className, icon, offsetY} = Object.assign(
+ pluginDefaults,
+ pluginOptions,
+ );
+
+ const styles = `
+ .${className} {
+ float: left;
+ padding-right: 4px;
+ margin-left: -20px;
+ }
+ h1 .${className} svg,
+ h2 .${className} svg,
+ h3 .${className} svg,
+ h4 .${className} svg,
+ h5 .${className} svg,
+ h6 .${className} svg {
+ visibility: hidden;
+ }
+ h1:hover .${className} svg,
+ h2:hover .${className} svg,
+ h3:hover .${className} svg,
+ h4:hover .${className} svg,
+ h5:hover .${className} svg,
+ h6:hover .${className} svg,
+ h1 .${className}:focus svg,
+ h2 .${className}:focus svg,
+ h3 .${className}:focus svg,
+ h4 .${className}:focus svg,
+ h5 .${className}:focus svg,
+ h6 .${className}:focus svg {
+ visibility: visible;
+ }
+ `;
+
+ const script = `
+ document.addEventListener("DOMContentLoaded", function(event) {
+ var hash = window.decodeURI(location.hash.replace('#', ''))
+ if (hash !== '') {
+ var element = document.getElementById(hash)
+ if (element) {
+ var offset = element.offsetTop
+ // Wait for the browser to finish rendering before scrolling.
+ setTimeout((function() {
+ window.scrollTo(0, offset - ${offsetY})
+ }), 0)
+ }
+ }
+ })
+ `;
+
+ const style = icon ? (
+
+ ) : (
+ undefined
+ );
+
+ return setHeadComponents([
+ style,
+ ,
+ ]);
+};
diff --git a/plugins/gatsby-remark-header-custom-ids/index.js b/plugins/gatsby-remark-header-custom-ids/index.js
new file mode 100644
index 00000000000..df645707368
--- /dev/null
+++ b/plugins/gatsby-remark-header-custom-ids/index.js
@@ -0,0 +1,69 @@
+/*!
+ * Based on 'gatsby-remark-autolink-headers'
+ * Original Author: Kyle Mathews
+ * Copyright (c) 2015 Gatsbyjs
+ */
+
+const toString = require('mdast-util-to-string');
+const visit = require('unist-util-visit');
+const slugs = require('github-slugger')();
+
+function patch(context, key, value) {
+ if (!context[key]) {
+ context[key] = value;
+ }
+
+ return context[key];
+}
+
+const svgIcon = ``;
+
+module.exports = (
+ {markdownAST},
+ {icon = svgIcon, className = `anchor`, maintainCase = false},
+) => {
+ slugs.reset();
+
+ visit(markdownAST, 'heading', node => {
+ // Support custom-id syntax.
+ const rawHeader = toString(node);
+ const match = /^.+(\s*\{#([a-z0-9\-_]+?)\}\s*)$/.exec(rawHeader);
+ const id = match ? match[2] : slugs.slug(rawHeader, maintainCase);
+ if (match) {
+ // Remove the custom ID part from the text node.
+ const lastNode = node.children[node.children.length - 1];
+ lastNode.value = lastNode.value.replace(match[1], '');
+ }
+
+ const data = patch(node, 'data', {});
+
+ patch(data, 'id', id);
+ patch(data, 'htmlAttributes', {});
+ patch(data, 'hProperties', {});
+ patch(data.htmlAttributes, 'id', id);
+ patch(data.hProperties, 'id', id);
+
+ if (icon !== false) {
+ node.children.unshift({
+ type: 'link',
+ url: `#${id}`,
+ title: null,
+ data: {
+ hProperties: {
+ 'aria-hidden': true,
+ class: className,
+ },
+ hChildren: [
+ {
+ type: 'raw',
+ // The Octicon link icon is the default. But users can set their own icon via the "icon" option.
+ value: icon,
+ },
+ ],
+ },
+ });
+ }
+ });
+
+ return markdownAST;
+};
diff --git a/plugins/gatsby-remark-header-custom-ids/package.json b/plugins/gatsby-remark-header-custom-ids/package.json
new file mode 100644
index 00000000000..f146c90c1e1
--- /dev/null
+++ b/plugins/gatsby-remark-header-custom-ids/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "gatsby-remark-header-custom-ids",
+ "version": "0.0.1"
+}
diff --git a/yarn.lock b/yarn.lock
index 5beaed7f472..35a394271ff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5463,16 +5463,6 @@ gatsby-react-router-scroll@^2.0.0:
scroll-behavior "^0.9.9"
warning "^3.0.0"
-gatsby-remark-autolink-headers@^2.0.12:
- version "2.0.12"
- resolved "https://registry.yarnpkg.com/gatsby-remark-autolink-headers/-/gatsby-remark-autolink-headers-2.0.12.tgz#96830bd7496ace7d1b7e07444aa09ecbe2ac59d2"
- integrity sha512-TI7dpdHOUohIWCW7i8ta4zZAdWG5C2ZmrHMC9flWGWQ62oq6Q2eW+I1I/v//UZ+vJ1dRaSX2lmsVFmt87rLVHQ==
- dependencies:
- "@babel/runtime" "^7.0.0"
- github-slugger "^1.1.1"
- mdast-util-to-string "^1.0.2"
- unist-util-visit "^1.3.0"
-
gatsby-remark-code-repls@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/gatsby-remark-code-repls/-/gatsby-remark-code-repls-2.0.0.tgz#b3290987046f1d325e6e93dbe9295f3cbb3df34a"
@@ -5814,6 +5804,13 @@ github-slugger@^1.1.1:
dependencies:
emoji-regex ">=6.0.0 <=6.1.1"
+github-slugger@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508"
+ integrity sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ==
+ dependencies:
+ emoji-regex ">=6.0.0 <=6.1.1"
+
glamor@^2.20.40:
version "2.20.40"
resolved "https://registry.yarnpkg.com/glamor/-/glamor-2.20.40.tgz#f606660357b7cf18dface731ad1a2cfa93817f05"
@@ -8184,7 +8181,7 @@ mdast-util-to-nlcst@^3.2.0:
unist-util-position "^3.0.0"
vfile-location "^2.0.0"
-mdast-util-to-string@^1.0.2:
+mdast-util-to-string@^1.0.2, mdast-util-to-string@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.0.5.tgz#3552b05428af22ceda34f156afe62ec8e6d731ca"
integrity sha512-2qLt/DEOo5F6nc2VFScQiHPzQ0XXcabquRJxKMhKte8nt42o08HUxNDPk7tt0YPxnWjAT11I1SYi0X0iPnfI5A==