diff --git a/.eslintrc.js b/.eslintrc.js
index dcc83f2b5e..d5ab59e3de 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -7,7 +7,10 @@ module.exports = {
// Enable dotfile linting
'!.*',
'node_modules',
- 'node_modules/.*'
+ 'node_modules/.*',
+
+ // Prevent CHANGELOG history changes
+ 'CHANGELOG.md'
],
overrides: [
{
@@ -18,7 +21,13 @@ module.exports = {
'plugin:n/recommended',
'plugin:promise/recommended'
],
- files: ['**/*.{cjs,js,mjs}'],
+ files: [
+ '**/*.{cjs,js,mjs}',
+
+ // Check markdown `*.md` contains valid code blocks
+ // https://github.com/eslint/eslint-plugin-markdown#advanced-configuration
+ '**/*.md/*.{cjs,js,mjs}'
+ ],
parserOptions: {
ecmaVersion: 'latest'
},
@@ -98,6 +107,31 @@ module.exports = {
env: {
jest: true
}
+ },
+ {
+ // Add plugin for markdown `*.md` code blocks
+ extends: ['plugin:markdown/recommended'],
+ files: ['**/*.md'],
+ plugins: ['markdown'],
+ processor: 'markdown/markdown'
+ },
+ {
+ files: ['**/coding-standards/js.md/*.{cjs,js,mjs}'],
+ env: {
+ browser: true
+ },
+ rules: {
+ // Ignore unused example code
+ 'no-undef': 'off',
+ 'no-unused-vars': 'off',
+
+ // Ignore paths to example modules
+ 'import/no-unresolved': 'off',
+ 'n/no-missing-import': 'off',
+
+ // Allow `var` in example code
+ 'no-var': 'off'
+ }
}
],
parserOptions: {
diff --git a/.lintstagedrc.js b/.lintstagedrc.js
index 53c3dee85b..ff1e38e872 100644
--- a/.lintstagedrc.js
+++ b/.lintstagedrc.js
@@ -1,6 +1,6 @@
const { ESLint } = require('eslint')
-module.exports = {
+const commands = {
// ESLint's configuration makes it ignore built files in `dist` or `packages/govuk-frontend/dist`
// that we want left alone, as well as the polyfills.
// The glob used by lint-staged to trigger the linting on commit isn't aware
@@ -11,9 +11,16 @@ module.exports = {
// as recommended by lint-staged.
//
// https://github.com/okonet/lint-staged#how-can-i-ignore-files-from-eslintignore
- '*.{cjs,js,mjs}': filterTask('npm run lint:js:cli -- --fix'),
- '*.{json,md,yaml,yml}': 'npm run lint:prettier:cli -- --write',
- '*.scss': 'npm run lint:scss:cli -- --fix --allow-empty-input'
+ eslint: filterTask('npm run lint:js:cli -- --fix'),
+ prettier: 'npm run lint:prettier:cli -- --write',
+ stylelint: 'npm run lint:scss:cli -- --fix --allow-empty-input'
+}
+
+module.exports = {
+ '*.{cjs,js,mjs}': commands.eslint,
+ '*.{json,yaml,yml}': commands.prettier,
+ '*.md': [commands.eslint, commands.stylelint, commands.prettier],
+ '*.scss': commands.stylelint
}
// Configure paths to ignore
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 330e54455f..8476105665 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -57,7 +57,7 @@ const Button = require('govuk-frontend/dist/govuk/components/button/button')
For example using `import`:
-```js
+```mjs
import Button from 'govuk-frontend/dist/govuk-esm/components/button/button.mjs'
```
diff --git a/docs/contributing/coding-standards/css.md b/docs/contributing/coding-standards/css.md
index c0d55114b4..50f42172ad 100644
--- a/docs/contributing/coding-standards/css.md
+++ b/docs/contributing/coding-standards/css.md
@@ -24,9 +24,9 @@ The naming convention follows this pattern:
.block__element {}
.block--modifier {}
-.govuk-card // Block - the root of a component
-.govuk-card__body // Element - a part of the block
-.govuk-card--active // Modifier - a variant of the block
+.govuk-card {} // Block - the root of a component
+.govuk-card__body {} // Element - a part of the block
+.govuk-card--active {} // Modifier - a variant of the block
```
It uses double hyphens (`--`) and underscores (`__`) so that the block, element
@@ -35,9 +35,9 @@ or modifiers themselves can be hyphen delimited without causing ambiguity.
For example:
```scss
-.govuk-phase-banner
-.govuk-phase-banner__phase-tag
-.govuk-phase-banner__phase-tag--light-blue
+.govuk-phase-banner {}
+.govuk-phase-banner__phase-tag {}
+.govuk-phase-banner__phase-tag--light-blue {}
```
### Further reading:
@@ -56,24 +56,24 @@ given class name. It also discourages excessive nesting.
Bad:
-```
+```scss
.govuk-breadcrumb {
- ...
+ // ...
&__item {
- ...
+ // ...
}
}
```
Good:
-```
+```scss
.govuk-breadcrumb {
- ...
+ // ...
}
.govuk-breadcrumb__item {
- ...
+ // ...
}
```
@@ -132,13 +132,13 @@ We use the following rules when linting files:
Bad:
-```
+```scss
.selector {padding: 0; border: 0;}
```
Good:
-```
+```scss
.selector {
padding: 0;
border: 0;
@@ -149,7 +149,7 @@ Good:
Bad:
-```
+```scss
.selector {
color: #005ea5;
}
@@ -157,7 +157,7 @@ Bad:
Good:
-```
+```scss
.selector {
color: $govuk-blue;
}
@@ -167,13 +167,13 @@ Good:
Bad:
-```
+```scss
$white: #FFF;
```
Good:
-```
+```scss
$white: #ffffff;
```
@@ -181,37 +181,17 @@ $white: #ffffff;
Bad:
-```
+```scss
#content {
- ...
+ // ...
}
```
Good:
-```
+```scss
.govuk-wrapper {
- ...
-}
-```
-
-### Use single colons for pseudo-element selectors
-
-This is to ensure compatibility with Internet Explorer 8, which doesn't support the double colon syntax.
-
-Bad:
-
-```
-.selector::before {
- content: "foo";
-}
-```
-
-Good:
-
-```
-.selector:before {
- content: "foo";
+ // ...
}
```
@@ -219,31 +199,31 @@ Good:
Bad:
-```
+```scss
p {
margin: 0;
em {
- ...
+ // ...
}
}
a {
- ...
+ // ...
}
```
Good:
-```
+```scss
p {
margin: 0;
em {
- ...
+ // ...
}
}
a {
- ...
+ // ...
}
```
@@ -251,24 +231,24 @@ a {
Bad:
-```
+```scss
.govuk-breadcrumb {
- ...
+ // ...
&__item {
- ...
+ // ...
}
}
```
Good:
-```
+```scss
.govuk-breadcrumb {
- ...
+ // ...
}
.govuk-breadcrumb__item {
- ...
+ // ...
}
```
@@ -276,13 +256,13 @@ Good:
Bad:
-```
+```scss
@extend %contain-floats;
```
Good:
-```
+```scss
@include clearfix;
```
@@ -290,13 +270,13 @@ Good:
Bad:
-```
+```scss
margin: 1px 2px 3px 2px;
```
Good:
-```
+```scss
margin: 1px 2px 3px;
```
@@ -304,7 +284,7 @@ margin: 1px 2px 3px;
Bad:
-```
+```scss
@import 'foo';
$govuk-font-family-gds-transport: 'GDS Transport', arial, sans-serif;
@@ -316,7 +296,7 @@ $govuk-font-family-gds-transport: 'GDS Transport', arial, sans-serif;
Good:
-```
+```scss
@import "foo";
$govuk-font-family-gds-transport: "GDS Transport", arial, sans-serif;
@@ -332,14 +312,14 @@ $govuk-font-family-gds-transport: "GDS Transport", arial, sans-serif;
Bad:
-```
+```scss
@import "_foo.scss";
@import "_bar/foo.scss";
```
Good:
-```
+```scss
@import "foo";
@import "bar/foo";
```
@@ -348,7 +328,7 @@ Good:
Bad:
-```
+```scss
.foo {
content:"bar";
}
@@ -356,7 +336,7 @@ Bad:
Good:
-```
+```scss
.foo {
content: "bar";
}
@@ -366,7 +346,7 @@ Good:
Bad:
-```
+```scss
@if ($foo == $bar) {
$baz: 1;
}
@@ -374,7 +354,7 @@ Bad:
Good:
-```
+```scss
@if $foo == $bar {
$baz: 1;
}
@@ -384,7 +364,7 @@ Good:
Bad:
-```
+```scss
@if $foo == null {
$baz: 1;
}
@@ -392,7 +372,7 @@ Bad:
Good:
-```
+```scss
@if not $foo {
$baz: 1;
}
@@ -402,15 +382,15 @@ Good:
Bad:
-```
-.selector {
+```scss
+.selector-1 {
margin: 5px+15px;
}
$foo: 1;
$bar: 3;
-.selector {
+.selector-2 {
margin: $foo+$bar+"px";
}
@@ -428,15 +408,15 @@ $bar: 2-1;
Good:
-```
-.selector {
+```scss
+.selector-1 {
margin: 5px + 15px;
}
$foo: 1;
$bar: 3;
-.selector {
+.selector-2 {
margin: $foo + $bar + "px";
}
@@ -456,7 +436,7 @@ $bar: 2 - 1;
Bad:
-```
+```scss
@mixin FONT_STACK() {
font-family: $govuk-font-stack;
}
@@ -464,7 +444,7 @@ Bad:
Good:
-```
+```scss
@mixin font-stack() {
font-family: $govuk-font-stack;
}
@@ -474,7 +454,7 @@ Good:
Bad:
-```
+```scss
.selector {
margin: 0px;
}
@@ -482,7 +462,7 @@ Bad:
Good:
-```
+```scss
.selector {
margin: 0;
}
@@ -492,7 +472,7 @@ Good:
Bad:
-```
+```scss
.selector {
margin: 0
}
@@ -502,7 +482,7 @@ $my-example-var: value
Good:
-```
+```scss
.selector {
margin: 0;
}
@@ -514,7 +494,7 @@ $my-example-var: value;
Bad:
-```
+```scss
.selector {
font-size: 0.50em;
}
@@ -522,7 +502,7 @@ Bad:
Good:
-```
+```scss
.selector {
font-size: .5em;
}
diff --git a/docs/contributing/coding-standards/js.md b/docs/contributing/coding-standards/js.md
index ff8cbb2e2b..9f70f8b0e5 100644
--- a/docs/contributing/coding-standards/js.md
+++ b/docs/contributing/coding-standards/js.md
@@ -4,7 +4,7 @@
JavaScript files have the same name as the component's folder name. Test files have a `.test` suffix placed before the file extension.
-```
+```console
component
├── component.mjs
└── component.test.js
@@ -12,7 +12,7 @@ component
## Skeleton
-```js
+```mjs
/**
* Component name
*
@@ -39,6 +39,7 @@ Example.prototype.init = function () {
}
// Code goes here
+ var $module = this.$module
}
export default Example
@@ -64,7 +65,7 @@ class="govuk-js-header-toggle"
Use `/** ... */` for multi-line comments. Include a description, and specify types and values for all parameters and return values.
-```js
+```mjs
/**
* Get the first descendent (child) of an HTML element that matches a given tag name
*
@@ -72,7 +73,7 @@ Use `/** ... */` for multi-line comments. Include a description, and specify typ
* @param {string} tagName - Tag name (for example 'div')
* @returns {Element} Ancestor element
*/
-function ($element, tagName) {
+function exampleHelper ($element, tagName) {
// Code goes here
return $element.querySelector(tagName)
}
@@ -90,7 +91,7 @@ Use the prototype design pattern to structure your code.
Create a constructor and define any variables that the object needs.
-```js
+```mjs
function Example ($module) {
// Code goes here
}
@@ -98,7 +99,7 @@ function Example ($module) {
Assign methods to the prototype object. Do not overwrite the prototype with a new object as this makes inheritance impossible.
-```js
+```mjs
// Bad
Example.prototype = {
init: function () {
@@ -114,19 +115,19 @@ Example.prototype.init = function () {
When initialising an object, use the `new` keyword.
-```js
+```mjs
// Bad
-var myExample = Example()
+var myExample1 = Example()
// Good
-var myExample = new Example()
+var myExample2 = new Example()
```
## Modules
Use ECMAScript (ES) modules (`import`/`export`) over CommonJS and other formats. You can always transpile to your preferred module system.
-```js
+```mjs
import { closestAttributeValue } from '../common/index.mjs'
// Code goes here
diff --git a/docs/contributing/coding-standards/nunjucks-api.md b/docs/contributing/coding-standards/nunjucks-api.md
index 1e078d88d1..6ba3116d5d 100644
--- a/docs/contributing/coding-standards/nunjucks-api.md
+++ b/docs/contributing/coding-standards/nunjucks-api.md
@@ -19,13 +19,19 @@ When providing _content_ to a macro, say for a label or a button, we accept two
Example:
-`{{ govukButton({"text": "Button text"}) }}`
+```njk
+{{ govukButton({ text: "Button text" }) }}
+```
-`{{ govukButton({"html": "Button text"}) }}`
+```njk
+{{ govukButton({ html: "Button text" }) }}
+```
Example of implementing logic in a component template:
-`{{ params.html | safe if params.html else params.text }}`
+```njk
+{{ params.html | safe if params.html else params.text }}
+```
Example shows that if `html` and `text` options are present, then `html` takes precedence over `text` and we are not escaping it.
@@ -37,25 +43,25 @@ If a component depends on another component, we group the options for the depend
Example of a component depending on another component
-```
+```njk
{{ govukLabel({
- "text": "Label text",
- "errorMessage": {
- "text": "Error message"
+ text: "Label text",
+ errorMessage: {
+ text: "Error message"
}
}) }}
```
Example of a component depending on two other components
-```
+```njk
{{ govukInput({
- "name": "example-input",
- "label": {
- "text": "Label text"
+ name: "example-input",
+ label: {
+ text: "Label text"
},
- "errorMessage": {
- "text": "Error message"
+ errorMessage: {
+ text: "Error message"
}
}) }}
```
@@ -66,9 +72,13 @@ When there is a need to specify html attributes, such as _checked, disabled, id,
Example:
-`{{ govukButton({"disabled": true}) }}`
+```njk
+{{ govukButton({ disabled: true }) }}
+```
-`{{ govukCheckbox({"checked": true}) }}`
+```njk
+{{ govukCheckbox({ checked: true }) }}
+```
## Defining additional HTML attributes
@@ -78,10 +88,10 @@ You cannot use this to set attributes that are already defined, such as class
Example:
-```
+```njk
{{ govukButton({
- "attributes" : {
- "data-target" : "contact-by-text",
+ attributes: {
+ "data-target": "contact-by-text",
"aria-labelledby": "error-summary-heading-example-1",
"tabindex": "-1"
}
@@ -94,16 +104,16 @@ When a component accepts a _single array of items_ for an output, such as checkb
Example:
-```
+```njk
{{ govukCheckbox({
- "items": [
+ items: [
{
- "value": "checkbox value",
- "text": "Checkbox text"
+ value: "checkbox value",
+ text: "Checkbox text"
},
{
- "value": "checkbox value 2",
- "text": "Checkbox text 2"
+ value: "checkbox value 2",
+ text: "Checkbox text 2"
}
]
}) }}
@@ -115,17 +125,17 @@ When a component has multiple visual presentations, such default button vs start
Default button example:
-```
+```njk
{{ govukButton({
- "text" : "Continue"
+ text: "Continue"
}) }}
```
Start button example:
-```
+```njk
{{ govukButton({
- "text" : "Start",
- "classes" : "govuk-button--start"
+ text: "Start",
+ classes: "govuk-button--start"
}) }}
```
diff --git a/docs/contributing/managing-change.md b/docs/contributing/managing-change.md
index 735da414f9..cf8df7967a 100644
--- a/docs/contributing/managing-change.md
+++ b/docs/contributing/managing-change.md
@@ -81,8 +81,9 @@ Mixins cannot be invoked within functions, so we use the `_should-warn` and `_wa
/// See https://github.com/alphagov/govuk-frontend/issues/1234
@function govuk-double($number) {
@if _should-warn("double") {
- @warn _warning-message("double", "govuk-double($number) is deprecated. Use govuk-multiply($number, 2) instead.")
+ @warn _warning-message("double", "govuk-double($number) is deprecated. Use govuk-multiply($number, 2) instead.");
}
+
@return govuk-multiply($number, 2);
}
```
@@ -102,7 +103,7 @@ If possible, update the mixin or function to maintain the existing functionality
/// @param {Number} $angle Angle to reticulate by
/// @param {Boolean} $rightAngle Deprecated. Use $angle: 90 instead.
@mixin govuk-reticulate-splines($spline, $angle: 180, $rightAngle: false) {
- @if ($rightAngle != false) {
+ @if $rightAngle != false {
@include _warning("right-angle", "Passing $rightAngle to govuk-reticulate-splines is deprecated. Pass $angle: 90 instead.");
$angle: 90;
@@ -117,7 +118,7 @@ If possible, update the mixin or function to maintain the existing functionality
```scss
// @deprecated
.govuk-foo-old-class-name {
- foo: bar;
+ content: "foo";
}
```
@@ -167,7 +168,7 @@ Add 'Deprecated.' to the description for the parameter.
/// @param {String} $spline Spline to reticulate
/// @param {String} $spilne Deprecated. Use $spline instead.
@function govuk-reticulate-splines($spline, $spilne: false) {
- @if ($spilne != false) {
+ @if $spilne != false {
@include _warning("spilne", "Passing $spilne to govuk-reticulate-splines is deprecated. Pass $spline instead.");
$spline: $spilne;
@@ -185,7 +186,7 @@ Keep the old name in the selector list, and mark it as deprecated.
// govuk-old-class-name is deprecated. Use govuk-new-class-name instead.
.govuk-old-class-name,
.govuk-new-class-name {
- foo: bar;
+ content: "foo";
}
```
diff --git a/docs/contributing/running-locally.md b/docs/contributing/running-locally.md
index 2d50158a1b..5dd0070195 100644
--- a/docs/contributing/running-locally.md
+++ b/docs/contributing/running-locally.md
@@ -11,7 +11,7 @@ If you're an external contributor make sure to [fork this project first](https:/
## 2. Clone repository
-```
+```shell
git clone git@github.com:alphagov/govuk-frontend.git # or clone your own fork
cd govuk-frontend
@@ -30,7 +30,7 @@ To enable this we use [nvm (Node Version Manager)](https://github.com/creationix
We use [npm](https://docs.npmjs.com/getting-started/what-is-npm) to manage the dependencies in development.
-```
+```shell
npm install
```
@@ -38,7 +38,7 @@ npm install
This will build sources, serve pages and watch for changes.
-```
+```shell
npm start
```
diff --git a/docs/contributing/testing.md b/docs/contributing/testing.md
index ce77e7aed0..4071c48797 100644
--- a/docs/contributing/testing.md
+++ b/docs/contributing/testing.md
@@ -135,7 +135,7 @@ Where `` is the name of the component you've changed.
To make sure your changes work in the Design System, use `npm link` to test before publishing, as follows:
-```bash
+```shell
cd ../govuk-design-system
git checkout main
git pull
@@ -145,7 +145,7 @@ npm link ../govuk-frontend/packages/govuk-frontend/
When you've finished testing, run this command to unlink the package:
-```bash
+```shell
npm unlink ../govuk-frontend/packages/govuk-frontend/
```
diff --git a/docs/releasing/publishing-from-a-support-branch.md b/docs/releasing/publishing-from-a-support-branch.md
index 3c88db587f..b1ba0cf9ae 100644
--- a/docs/releasing/publishing-from-a-support-branch.md
+++ b/docs/releasing/publishing-from-a-support-branch.md
@@ -63,7 +63,7 @@ Read the docs for [what to do before publishing a release](/docs/releasing/befor
6. Apply the new version number by running:
- ```
+ ```shell
npm version --no-git-tag-version --workspace govuk-frontend
```
diff --git a/docs/releasing/publishing.md b/docs/releasing/publishing.md
index 0f2b91c71a..d5100ac510 100644
--- a/docs/releasing/publishing.md
+++ b/docs/releasing/publishing.md
@@ -22,7 +22,7 @@ Developers should pair on releases. When remote working, it can be useful to be
6. Apply the new version number by running:
- ```
+ ```shell
npm version --no-git-tag-version --workspace govuk-frontend
```
@@ -32,7 +32,7 @@ Developers should pair on releases. When remote working, it can be useful to be
7. Update browser data from ["Can I use"](https://caniuse.com) by running:
- ```
+ ```shell
npx update-browserslist-db@latest
```
diff --git a/docs/releasing/testing-and-linting.md b/docs/releasing/testing-and-linting.md
index d18a33e683..ec6bf38a7a 100644
--- a/docs/releasing/testing-and-linting.md
+++ b/docs/releasing/testing-and-linting.md
@@ -20,7 +20,7 @@ Visual regression tests help us check for any unintended visual changes to our c
To test the whole codebase, run:
-```
+```shell
npm test
```
@@ -42,7 +42,7 @@ Note: There's a watch mode that keeps a testing session open waiting for changes
To lint the whole codebase, run:
-```
+```shell
npm run lint
```
@@ -52,7 +52,7 @@ See [Tasks](../contributing/tasks.md) for details of what `npm run lint` does.
### Running only Sass linting
-```
+```shell
npm run lint:scss
```
@@ -60,7 +60,7 @@ See [CSS Coding Standards](/docs/contributing/coding-standards/css.md#linting) f
### Running only JavaScript linting
-```
+```shell
npm run lint:js
```
@@ -78,7 +78,7 @@ If a component uses JavaScript, we also write functional tests in a `[component
If you want to inspect a test that's running in the browser, configure Jest Puppeteer in non-headless mode with the environment variable `HEADLESS=false` and then use [Jest Puppeteer's debug mode](https://github.com/argos-ci/jest-puppeteer/blob/main/README.md#debug-mode) to pause the test execution.
-```
+```shell
HEADLESS=false npx jest --watch src/govuk/components/tag/accessibility.test.mjs
```
@@ -102,7 +102,9 @@ For components, the snapshots are stored in `[component-name directory]/_snapsho
If a snapshot test fails, review the difference in the console. If the change is the correct change to make, run:
-`npm test -- -u packages/govuk-frontend/src/govuk/components/button`
+```shell
+npm test -- -u packages/govuk-frontend/src/govuk/components/button
+```
This will update the snapshot file. Commit this file separately with a commit message that explains you're updating the snapshot file and an explanation of what caused the change.
@@ -118,7 +120,7 @@ When you run the tests locally (for example, using `npm run test:screenshots --w
When Github Actions is running against a PR from a fork, the Percy secret is not available and Percy does not generate any screenshots. Other tests will continue to run as normal. You will see the following messages in the output:
-```
+```console
[percy] Skipping visual tests
[percy] Error: Missing Percy token
```
diff --git a/jest.config.mjs b/jest.config.mjs
index b0a584414d..9a0fb23c19 100644
--- a/jest.config.mjs
+++ b/jest.config.mjs
@@ -53,7 +53,7 @@ const config = {
*
* @type {import('@jest/types').Config.InitialOptions}
* @example
- * ```console
+ * ```shell
* npx jest --selectProjects "Nunjucks macro tests"
* npx jest --selectProjects "JavaScript unit tests"
* ```
diff --git a/package-lock.json b/package-lock.json
index 06fe177eb5..e7f5b32407 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"eslint-plugin-es-x": "^7.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^46.2.4",
+ "eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-promise": "^6.1.1",
"govuk-frontend-config": "*",
@@ -40,6 +41,8 @@
"jest-puppeteer": "^9.0.0",
"jest-serializer-html": "^7.1.0",
"lint-staged": "^13.2.2",
+ "postcss-markdown": "^1.2.0",
+ "postcss-scss": "^4.0.6",
"prettier": "^2.8.8",
"standard": "^17.1.0",
"stylelint": "^14.16.1",
@@ -4659,6 +4662,15 @@
"integrity": "sha512-YcZe50jhltsCq7rc9MNZC/4QB/OnA2Pd6hrOSTOFajtabN+38slqgDDCeE/0F83SjkKBQcsZUj7VLWR0H5cKRA==",
"optional": true
},
+ "node_modules/@types/mdast": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz",
+ "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
"node_modules/@types/micromatch": {
"version": "2.3.31",
"resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-2.3.31.tgz",
@@ -4822,6 +4834,12 @@
"integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==",
"optional": true
},
+ "node_modules/@types/unist": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
+ "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==",
+ "dev": true
+ },
"node_modules/@types/vinyl": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.7.tgz",
@@ -7408,6 +7426,36 @@
"node": ">=10"
}
},
+ "node_modules/character-entities": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
+ "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
+ "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
+ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -10593,6 +10641,21 @@
"node": ">=10"
}
},
+ "node_modules/eslint-plugin-markdown": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-markdown/-/eslint-plugin-markdown-3.0.0.tgz",
+ "integrity": "sha512-hRs5RUJGbeHDLfS7ELanT0e29Ocyssf/7kBM+p7KluY5AwngGkDf8Oyu4658/NZSGTTq05FZeWbkxXtbVyHPwg==",
+ "dev": true,
+ "dependencies": {
+ "mdast-util-from-markdown": "^0.8.5"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/eslint-plugin-n": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.0.0.tgz",
@@ -11605,6 +11668,19 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fault": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
+ "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
+ "dev": true,
+ "dependencies": {
+ "format": "^0.2.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/faye-websocket": {
"version": "0.11.4",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
@@ -12181,6 +12257,15 @@
"node": ">= 6"
}
},
+ "node_modules/format": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
+ "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/formidable": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz",
@@ -14197,6 +14282,30 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-alphabetical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
+ "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
+ "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
+ "dev": true,
+ "dependencies": {
+ "is-alphabetical": "^1.0.0",
+ "is-decimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -14344,6 +14453,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-decimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
+ "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@@ -14418,6 +14537,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-hexadecimal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
+ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/is-installed-globally": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz",
@@ -18410,6 +18539,46 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/mdast-util-from-markdown": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz",
+ "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/mdast": "^3.0.0",
+ "mdast-util-to-string": "^2.0.0",
+ "micromark": "~2.11.0",
+ "parse-entities": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-frontmatter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-0.2.0.tgz",
+ "integrity": "sha512-FHKL4w4S5fdt1KjJCwB0178WJ0evnyyQr5kXTM3wrOVpytD0hrkvd+AOOjU9Td8onOejCkmZ+HQRT3CZ3coHHQ==",
+ "dev": true,
+ "dependencies": {
+ "micromark-extension-frontmatter": "^0.2.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz",
+ "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/mdn-data": {
"version": "2.0.28",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
@@ -18517,6 +18686,39 @@
"node": ">= 0.6"
}
},
+ "node_modules/micromark": {
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz",
+ "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "dependencies": {
+ "debug": "^4.0.0",
+ "parse-entities": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-frontmatter": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-0.2.2.tgz",
+ "integrity": "sha512-q6nPLFCMTLtfsctAuS0Xh4vaolxSFUWUWR6PZSrXXiRy+SANGllpcqdXFv2z07l0Xz/6Hl40hK0ffNCJPH2n1A==",
+ "dev": true,
+ "dependencies": {
+ "fault": "^1.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
@@ -20025,6 +20227,24 @@
"node": ">=6"
}
},
+ "node_modules/parse-entities": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
+ "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
+ "dev": true,
+ "dependencies": {
+ "character-entities": "^1.0.0",
+ "character-entities-legacy": "^1.0.0",
+ "character-reference-invalid": "^1.0.0",
+ "is-alphanumerical": "^1.0.0",
+ "is-decimal": "^1.0.0",
+ "is-hexadecimal": "^1.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/parse-filepath": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
@@ -20621,6 +20841,22 @@
"node": ">=10"
}
},
+ "node_modules/postcss-markdown": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-markdown/-/postcss-markdown-1.2.0.tgz",
+ "integrity": "sha512-sO7eeu6pq5F0lx3XavY/rBVmifXbMTd6fGRuXaT/Q7wEuIAWTi0E2t747nQ57iVz99WynTPls4mw5wlLvZLFzw==",
+ "dev": true,
+ "dependencies": {
+ "mdast-util-from-markdown": "^0.8.5",
+ "mdast-util-frontmatter": "^0.2.0",
+ "micromark-extension-frontmatter": "^0.2.2",
+ "postcss": "^8.4.0",
+ "postcss-safe-parser": "^6.0.0"
+ },
+ "engines": {
+ "node": "^12 || >=14"
+ }
+ },
"node_modules/postcss-media-query-parser": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
@@ -25757,6 +25993,19 @@
"node": ">=8"
}
},
+ "node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
diff --git a/package.json b/package.json
index 3865e56019..c33b9c7df0 100644
--- a/package.json
+++ b/package.json
@@ -36,11 +36,11 @@
"lint": "npm run lint:editorconfig && npm run lint:prettier && npm run lint:js && npm run lint:scss",
"lint:editorconfig": "npm run lint:editorconfig:cli",
"lint:editorconfig:cli": "editorconfig-checker",
- "lint:js": "npm run lint:js:cli -- \"**/*.{cjs,js,mjs}\"",
+ "lint:js": "npm run lint:js:cli -- \"**/*.{cjs,js,md,mjs}\"",
"lint:js:cli": "eslint --cache --cache-location .cache/eslint --cache-strategy content --color --ignore-path .gitignore --max-warnings 0",
"lint:prettier": "npm run lint:prettier:cli -- \"**/*.{json,md,yaml,yml}\"",
"lint:prettier:cli": "prettier --cache --cache-location .cache/prettier --cache-strategy content --check",
- "lint:scss": "npm run lint:scss:cli -- \"**/*.scss\"",
+ "lint:scss": "npm run lint:scss:cli -- \"**/*.{md,scss}\"",
"lint:scss:cli": "stylelint --cache --cache-location .cache/stylelint --cache-strategy content --color --ignore-path .gitignore --max-warnings 0",
"prepare": "node -e \"try { require('husky').install() } catch (e) {if (e.code !== 'MODULE_NOT_FOUND') throw e}\""
},
@@ -56,6 +56,7 @@
"eslint-plugin-es-x": "^7.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^46.2.4",
+ "eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-promise": "^6.1.1",
"govuk-frontend-config": "*",
@@ -70,6 +71,8 @@
"jest-puppeteer": "^9.0.0",
"jest-serializer-html": "^7.1.0",
"lint-staged": "^13.2.2",
+ "postcss-markdown": "^1.2.0",
+ "postcss-scss": "^4.0.6",
"prettier": "^2.8.8",
"standard": "^17.1.0",
"stylelint": "^14.16.1",
diff --git a/packages/govuk-frontend-review/tasks/watch.mjs b/packages/govuk-frontend-review/tasks/watch.mjs
index 59c3d60200..ce91210f0e 100644
--- a/packages/govuk-frontend-review/tasks/watch.mjs
+++ b/packages/govuk-frontend-review/tasks/watch.mjs
@@ -42,7 +42,7 @@ export const watch = (options) => gulp.parallel(
task.name('lint:js watch', () =>
gulp.watch([
`${slash(paths.app)}/src/javascripts/**/*.mjs`
- ], npm.script('lint:js:cli', [slash(join(options.workspace, '**/*.{cjs,js,mjs}'))]))
+ ], npm.script('lint:js:cli', [slash(join(options.workspace, '**/*.{cjs,js,md,mjs}'))]))
),
/**
diff --git a/packages/govuk-frontend/src/govuk/components/details/implementation.md b/packages/govuk-frontend/src/govuk/components/details/implementation.md
index 6e9f7cd1ff..ed982510ff 100644
--- a/packages/govuk-frontend/src/govuk/components/details/implementation.md
+++ b/packages/govuk-frontend/src/govuk/components/details/implementation.md
@@ -11,8 +11,8 @@ outline around the summary text, which means that the arrow no longer shows up.
Previously in GOV.UK Elements we resolved this by targeting Firefox specifically
and reverting to `display: list-item`:
-```
-@-moz-document regexp('.*') {
+```scss
+@-moz-document regexp(".*") {
details summary:not([tabindex]) {
// Allow duplicate properties, override the summary display property
// scss-lint:disable DuplicateProperty
diff --git a/packages/govuk-frontend/tasks/watch.mjs b/packages/govuk-frontend/tasks/watch.mjs
index 89741d69be..3a2b11f4d1 100644
--- a/packages/govuk-frontend/tasks/watch.mjs
+++ b/packages/govuk-frontend/tasks/watch.mjs
@@ -36,7 +36,7 @@ export const watch = (options) => gulp.parallel(
gulp.watch([
`${slash(options.srcPath)}/govuk/**/*.mjs`
], gulp.parallel(
- npm.script('lint:js:cli', [slash(join(options.workspace, '**/*.{cjs,js,mjs}'))]),
+ npm.script('lint:js:cli', [slash(join(options.workspace, '**/*.{cjs,js,md,mjs}'))]),
scripts(options)
))
),
diff --git a/stylelint.config.js b/stylelint.config.js
index e6a5098be4..dc93459836 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -5,9 +5,46 @@ module.exports = {
'**/vendor/**',
// Ignore CSS-in-JS (including dotfiles)
- '**/?(.)*.{cjs,js,mjs}'
+ '**/?(.)*.{cjs,js,mjs}',
+
+ // Prevent CHANGELOG history changes
+ 'CHANGELOG.md'
],
overrides: [
+ {
+ customSyntax: 'postcss-markdown',
+ files: ['**/*.md']
+ },
+ {
+ customSyntax: 'postcss-markdown',
+ files: ['**/coding-standards/css.md'],
+ rules: {
+ // Allow markdown `*.md` CSS bad examples
+ 'block-closing-brace-space-before': null,
+ 'block-no-empty': null,
+ 'block-opening-brace-space-after': null,
+ 'color-hex-case': null,
+ 'color-hex-length': null,
+ 'declaration-block-single-line-max-declarations': null,
+ 'declaration-block-trailing-semicolon': null,
+ 'declaration-colon-space-after': null,
+ 'length-zero-no-unit': null,
+ 'number-leading-zero': null,
+ 'number-no-trailing-zeros': null,
+ 'rule-empty-line-before': null,
+ 'selector-max-id': null,
+ 'string-quotes': null,
+ 'unit-no-unknown': null,
+
+ // Allow markdown `*.md` Sass bad examples
+ 'scss/at-if-no-null': null,
+ 'scss/at-import-no-partial-leading-underscore': null,
+ 'scss/at-import-partial-extension': null,
+ 'scss/at-mixin-pattern': null,
+ 'scss/at-rule-conditional-no-parentheses': null,
+ 'scss/operator-no-unspaced': null
+ }
+ },
{
customSyntax: 'postcss-scss',
files: ['**/*.scss']