diff --git a/content/actions/guides/building-and-testing-nodejs.md b/content/actions/guides/building-and-testing-nodejs.md
index a9b60700ff2b..b24bc1e181db 100644
--- a/content/actions/guides/building-and-testing-nodejs.md
+++ b/content/actions/guides/building-and-testing-nodejs.md
@@ -33,7 +33,7 @@ We recommend that you have a basic understanding of Node.js, YAML, workflow conf
To get started quickly, add the template to the `.github/workflows` directory of your repository.
{% raw %}
-```yaml
+```yaml{:copy}
name: Node.js CI
on: [push]
diff --git a/content/actions/quickstart.md b/content/actions/quickstart.md
index 27c500c0b7bd..5dbd3b07c203 100644
--- a/content/actions/quickstart.md
+++ b/content/actions/quickstart.md
@@ -21,7 +21,7 @@ You only need an existing {% data variables.product.prodname_dotcom %} repositor
1. From your repository on {% data variables.product.prodname_dotcom %}, create a new file in the `.github/workflows` directory named `superlinter.yml`. For more information, see "[Creating new files](/github/managing-files-in-a-repository/creating-new-files)."
2. Copy the following YAML contents into the `superlinter.yml` file. **Note:** If your default branch is not `main`, update the value of `DEFAULT_BRANCH` to match your repository's default branch name.
{% raw %}
- ```yaml
+ ```yaml{:copy}
name: Super-Linter
# Run this workflow every time a new commit pushed to your repository
diff --git a/contributing/content-markup-reference.md b/contributing/content-markup-reference.md
index 61676986e0e0..9a0636a84058 100644
--- a/contributing/content-markup-reference.md
+++ b/contributing/content-markup-reference.md
@@ -40,14 +40,22 @@ To render syntax highlighting in command line instructions, we use triple backti
### Usage
-\`\`\`shell
-git init YOUR_REPO
-\`\`\`
+ ```shell
+ git init YOUR_REPO
+ ```
This syntax highlighting renders light text on a dark background, and should be reserved for command line instructions.
Within the command-line syntax, you can also use the `` helper tag to indicate content that varies for each user, such as a user or repository name.
+**Copy-able code blocks**
+
+You can also add a header that includes the name of the language and a button to copy the contents of the code block:
+
+ ```js{:copy}
+ const copyMe = true
+ ```
+
## Octicons
Octicons are icons used across GitHub’s interface. We reference Octicons when documenting the user interface. Find the name of the Octicon on the [Octicons site](https://primer.style/octicons). For accessibility purposes, use [the `aria-label` option](https://primer.style/octicons/packages/javascript#aria-label) to describe the Octicon.
diff --git a/javascripts/copy-code.js b/javascripts/copy-code.js
new file mode 100644
index 000000000000..a92c48fc794a
--- /dev/null
+++ b/javascripts/copy-code.js
@@ -0,0 +1,15 @@
+import Clipboard from 'clipboard'
+
+export default () => {
+ const clipboard = new Clipboard('button.js-btn-copy')
+
+ clipboard.on('success', evt => {
+ const btn = evt.trigger
+ const beforeTooltip = btn.getAttribute('aria-label')
+ btn.setAttribute('aria-label', 'Copied!')
+
+ setTimeout(() => {
+ btn.setAttribute('aria-label', beforeTooltip)
+ }, 2000)
+ })
+}
diff --git a/javascripts/index.js b/javascripts/index.js
index b9bfb3ee8468..12bf1f341ac1 100644
--- a/javascripts/index.js
+++ b/javascripts/index.js
@@ -13,6 +13,7 @@ import print from './print'
import localization from './localization'
import helpfulness from './helpfulness'
import experiment from './experiment'
+import copyCode from './copy-code'
import { fillCsrf } from './get-csrf'
import initializeEvents from './events'
@@ -31,5 +32,6 @@ document.addEventListener('DOMContentLoaded', async () => {
await fillCsrf() // this must complete before any POST calls
helpfulness()
experiment()
+ copyCode()
initializeEvents()
})
diff --git a/package-lock.json b/package-lock.json
index cfef88742bfd..1c7787a125e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1131,16 +1131,28 @@
}
},
"@github-docs/render-content": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@github-docs/render-content/-/render-content-5.0.0.tgz",
- "integrity": "sha512-HXSk8B4Yjp7NyaDcq8DPwK33O81Bsd29aSkyWwfrsdJiaLx3D0m5woa3qE5XZRvC5DSiyVZS5xr8eyXL0OAc2w==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@github-docs/render-content/-/render-content-5.1.0.tgz",
+ "integrity": "sha512-d0Is3zPvumal5MYRYcZv0X2jwzEV3e5/OTwWTi2s9ZVWK53TwoHvO0r+I2Z0XQfHDKZgM6V0h2lZhRIJxcYsMg==",
"requires": {
+ "@primer/octicons": "^11.0.0",
"cheerio": "^1.0.0-rc.3",
+ "hast-util-from-parse5": "^6.0.0",
+ "hastscript": "^6.0.0",
"html-entities": "^1.2.1",
"hubdown": "^2.6.0",
"liquid": "^5.0.0",
+ "parse5": "^6.0.1",
+ "remark-code-extra": "^1.0.1",
"semver": "^5.7.1",
"strip-html-comments": "^1.0.0"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ }
}
},
"@github/rest-api-operations": {
@@ -2740,6 +2752,14 @@
"@types/node": "*"
}
},
+ "@types/hast": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz",
+ "integrity": "sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==",
+ "requires": {
+ "@types/unist": "*"
+ }
+ },
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
@@ -2813,6 +2833,11 @@
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
+ "@types/parse5": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz",
+ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw=="
+ },
"@types/prettier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.0.tgz",
@@ -4181,7 +4206,7 @@
},
"browserify-aes": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"requires": {
"buffer-xor": "^1.0.3",
@@ -4215,7 +4240,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
- "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"requires": {
"bn.js": "^4.1.0",
@@ -4908,6 +4933,16 @@
"integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
"dev": true
},
+ "clipboard": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
+ "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
+ "requires": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -5622,7 +5657,7 @@
},
"create-hash": {
"version": "1.2.0",
- "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"requires": {
"cipher-base": "^1.0.1",
@@ -5634,7 +5669,7 @@
},
"create-hmac": {
"version": "1.1.7",
- "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"requires": {
"cipher-base": "^1.0.3",
@@ -6183,6 +6218,11 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
+ "delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+ },
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -6356,7 +6396,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
- "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"requires": {
"bn.js": "^4.1.0",
@@ -9484,6 +9524,14 @@
"pinkie-promise": "^2.0.0"
}
},
+ "good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+ "requires": {
+ "delegate": "^3.1.2"
+ }
+ },
"got": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
@@ -9693,15 +9741,29 @@
}
},
"hast-util-from-parse5": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz",
- "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.0.tgz",
+ "integrity": "sha512-3ZYnfKenbbkhhNdmOQqgH10vnvPivTdsOJCri+APn0Kty+nRkDHArnaX9Hiaf8H+Ig+vkNptL+SRY/6RwWJk1Q==",
"requires": {
- "ccount": "^1.0.3",
+ "@types/parse5": "^5.0.0",
+ "ccount": "^1.0.0",
"hastscript": "^5.0.0",
"property-information": "^5.0.0",
- "web-namespaces": "^1.1.2",
- "xtend": "^4.0.1"
+ "vfile": "^4.0.0",
+ "web-namespaces": "^1.0.0"
+ },
+ "dependencies": {
+ "hastscript": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
+ "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==",
+ "requires": {
+ "comma-separated-tokens": "^1.0.0",
+ "hast-util-parse-selector": "^2.0.0",
+ "property-information": "^5.0.0",
+ "space-separated-tokens": "^1.0.0"
+ }
+ }
}
},
"hast-util-has-property": {
@@ -9734,6 +9796,29 @@
"zwitch": "^1.0.0"
},
"dependencies": {
+ "hast-util-from-parse5": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz",
+ "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==",
+ "requires": {
+ "ccount": "^1.0.3",
+ "hastscript": "^5.0.0",
+ "property-information": "^5.0.0",
+ "web-namespaces": "^1.1.2",
+ "xtend": "^4.0.1"
+ }
+ },
+ "hastscript": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
+ "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==",
+ "requires": {
+ "comma-separated-tokens": "^1.0.0",
+ "hast-util-parse-selector": "^2.0.0",
+ "property-information": "^5.0.0",
+ "space-separated-tokens": "^1.0.0"
+ }
+ },
"parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
@@ -9791,10 +9876,11 @@
"integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A=="
},
"hastscript": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz",
- "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
+ "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
+ "@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
@@ -9877,9 +9963,9 @@
"integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg=="
},
"highlight.js": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.0.tgz",
- "integrity": "sha512-OryzPiqqNCfO/wtFo619W+nPYALM6u7iCQkum4bqRmmlcTikOkmlL06i009QelynBPAlNByTQU6cBB2cOBQtCw=="
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
+ "integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
},
"highlightjs-graphql": {
"version": "1.0.2",
@@ -17605,9 +17691,9 @@
"dev": true
},
"property-information": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.5.0.tgz",
- "integrity": "sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA==",
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
+ "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"requires": {
"xtend": "^4.0.0"
}
@@ -18206,6 +18292,14 @@
"resolved": "https://registry.npmjs.org/relative-date/-/relative-date-1.1.3.tgz",
"integrity": "sha1-EgkDBAWI7HpKOZxlR/0B0OPS3GM="
},
+ "remark-code-extra": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/remark-code-extra/-/remark-code-extra-1.0.1.tgz",
+ "integrity": "sha512-MHKTd2zsKc9ayrLBaUzyiIw7rNY4Q/aNSx1L1xss7ZthQ/oSEoMvtA5wpXALVKTWOL5JQXDf/Q9lP8IWbP3cig==",
+ "requires": {
+ "unist-util-visit": "^1.4.1"
+ }
+ },
"remark-gemoji-to-emoji": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remark-gemoji-to-emoji/-/remark-gemoji-to-emoji-1.1.0.tgz",
@@ -18845,6 +18939,11 @@
}
}
},
+ "select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+ },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -18977,7 +19076,7 @@
},
"sha.js": {
"version": "2.4.11",
- "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"requires": {
"inherits": "^2.0.1",
@@ -20427,6 +20526,11 @@
"setimmediate": "^1.0.4"
}
},
+ "tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"title-case": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz",
diff --git a/package.json b/package.json
index 57c59b46a5b7..9a5bbe381dd2 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"@babel/runtime": "^7.11.2",
"@github-docs/data-directory": "^1.2.0",
"@github-docs/frontmatter": "^1.3.1",
- "@github-docs/render-content": "^5.0.0",
+ "@github-docs/render-content": "^5.1.0",
"@github/rest-api-operations": "^3.1.3",
"@octokit/rest": "^16.38.1",
"@primer/css": "^15.1.0",
@@ -26,6 +26,7 @@
"browser-date-formatter": "^3.0.3",
"change-case": "^3.1.0",
"cheerio": "^1.0.0-rc.3",
+ "clipboard": "^2.0.6",
"compression": "^1.7.4",
"connect-slashes": "^1.4.0",
"cookie-parser": "^1.4.5",
diff --git a/script/check-deps.js b/script/check-deps.js
index 71f036cc08e5..36ac0052df67 100644
--- a/script/check-deps.js
+++ b/script/check-deps.js
@@ -47,7 +47,8 @@ const main = async () => {
'search-with-your-keyboard',
'uuid',
'imurmurhash',
- 'js-cookie'
+ 'js-cookie',
+ 'clipboard'
]
})
diff --git a/stylesheets/syntax-highlighting.scss b/stylesheets/syntax-highlighting.scss
index c743657747af..0e144bb8f79f 100644
--- a/stylesheets/syntax-highlighting.scss
+++ b/stylesheets/syntax-highlighting.scss
@@ -5,6 +5,19 @@ from https://unpkg.com/highlight.js@9.15.8/styles/github.css
*/
+.markdown-body .code-extra {
+ margin-top: $spacer-4;
+
+ pre {
+ margin-top: 0 !important;
+ border-top-left-radius: 0 !important;
+ border-top-right-radius: 0 !important;
+ border-left: 1px $gray-200 solid !important;
+ border-bottom: 1px $gray-200 solid !important;
+ border-right: 1px $gray-200 solid !important;
+ }
+}
+
.hljs {
display: block;
overflow-x: auto;