@x-govuk/govuk-eleventy-plugin 7.0.1 → 7.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +6 -5
  2. package/src/application.scss +2 -2
  3. package/src/components/_index.scss +0 -1
  4. package/src/components/aside/_index.scss +4 -2
  5. package/src/components/contents-list/_index.scss +6 -4
  6. package/src/components/document-header/_index.scss +2 -1
  7. package/src/components/document-header/template.njk +2 -2
  8. package/src/components/document-list/_index.scss +6 -3
  9. package/src/components/document-list/template.njk +1 -1
  10. package/src/components/footnotes-list/_index.scss +6 -3
  11. package/src/components/prose-scope/_index.scss +11 -27
  12. package/src/components/service-navigation/template.njk +1 -1
  13. package/src/components/site-search/_index.scss +11 -28
  14. package/src/components/site-search/site-search.js +6 -5
  15. package/src/data/eleventy-computed.js +3 -37
  16. package/src/data/options.js +13 -11
  17. package/src/events/generate-govuk-assets.js +1 -1
  18. package/src/filters/date.js +41 -25
  19. package/src/filters/index.js +1 -2
  20. package/src/filters/items-from-navigation.js +23 -1
  21. package/src/filters/tokenize.js +8 -8
  22. package/src/index.js +2 -3
  23. package/src/layouts/base.njk +2 -3
  24. package/src/layouts/product.njk +5 -8
  25. package/src/layouts/search-index.njk +3 -3
  26. package/src/layouts/sub-navigation.njk +1 -1
  27. package/src/layouts/tags.njk +4 -0
  28. package/src/markdown-it/deflist.js +7 -1
  29. package/src/markdown-it.js +10 -3
  30. package/src/utils.js +36 -0
  31. package/src/components/definition-list/_index.scss +0 -19
  32. package/src/filters/items-from-collection.js +0 -15
  33. package/src/markdown-it/table-of-contents.js +0 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-govuk/govuk-eleventy-plugin",
3
- "version": "7.0.1",
3
+ "version": "7.1.1",
4
4
  "description": "Write documentation using Markdown and publish it using GOV.UK styles",
5
5
  "keywords": [
6
6
  "govuk",
@@ -34,6 +34,7 @@
34
34
  "build:example": "eleventy --config=example.config.js",
35
35
  "predev": "npm run build",
36
36
  "dev": "eleventy --serve --watch",
37
+ "dev:example": "eleventy --serve --watch --config=example.config.js",
37
38
  "start": "eleventy --serve",
38
39
  "lint:prettier": "prettier . --check",
39
40
  "lint:prettier:fix": "prettier . --write",
@@ -57,15 +58,15 @@
57
58
  "@rollup/plugin-commonjs": "^28.0.0",
58
59
  "@rollup/plugin-node-resolve": "^16.0.0",
59
60
  "@rollup/plugin-terser": "^0.4.4",
60
- "@x-govuk/govuk-prototype-components": "^4.0.0",
61
+ "@x-govuk/govuk-prototype-components": "^5.0.0",
61
62
  "deepmerge": "^4.2.2",
62
- "govuk-frontend": "^5.6.0",
63
- "luxon": "^3.0.1",
63
+ "govuk-frontend": "^5.11.0",
64
64
  "markdown-it-abbr": "^2.0.0",
65
65
  "markdown-it-anchor": "^9.0.0",
66
+ "markdown-it-attrs": "^4.3.1",
66
67
  "markdown-it-deflist": "^3.0.0",
67
68
  "markdown-it-footnote": "^4.0.0",
68
- "markdown-it-govuk": "^0.5.0",
69
+ "markdown-it-govuk": "^0.6.0",
69
70
  "markdown-it-image-figures": "^2.0.0",
70
71
  "markdown-it-ins": "^4.0.0",
71
72
  "markdown-it-mark": "^4.0.0",
@@ -1,9 +1,9 @@
1
- // GOV.UK Frontend components
1
+ // Use GOV.UK Frontend
2
2
  @use "pkg:govuk-frontend/dist/govuk" with (
3
3
  $govuk-global-styles: true,
4
4
  $govuk-new-organisation-colours: true,
5
5
  $govuk-new-typography-scale: true
6
6
  );
7
7
 
8
- // GOV.UK Eleventy Plugin components
8
+ // Use GOV.UK Eleventy Plugin components
9
9
  @forward "pkg:@x-govuk/govuk-eleventy-plugin";
@@ -1,6 +1,5 @@
1
1
  @forward "aside";
2
2
  @forward "contents-list";
3
- @forward "definition-list";
4
3
  @forward "document-header";
5
4
  @forward "document-list";
6
5
  @forward "footnotes-list";
@@ -2,15 +2,17 @@
2
2
 
3
3
  @include govuk-exports("govuk-eleventy-plugin/component/aside") {
4
4
  .app-aside {
5
+ border-top: 2px solid $govuk-brand-colour;
6
+
5
7
  @include govuk-responsive-margin(6, "bottom");
6
8
  @include govuk-text-colour;
7
- border-top: 2px solid $govuk-brand-colour;
8
9
  }
9
10
 
10
11
  .app-aside__heading {
11
- @include govuk-font(19, $weight: bold);
12
12
  margin-bottom: govuk-spacing(2);
13
13
  margin-top: govuk-spacing(3);
14
+
15
+ @include govuk-font(19, $weight: bold);
14
16
  }
15
17
 
16
18
  .app-aside__body {
@@ -6,20 +6,21 @@
6
6
  .app-contents-list {
7
7
  @include govuk-responsive-margin(8, "bottom");
8
8
 
9
- ul {
9
+ ol {
10
10
  list-style-type: none;
11
11
  margin-top: 0;
12
12
  padding-left: 0;
13
13
  }
14
14
 
15
- ul ul {
15
+ ol ol {
16
16
  margin-bottom: 0;
17
17
  }
18
18
 
19
19
  li {
20
+ padding-top: govuk-spacing(2);
21
+
20
22
  @include govuk-font($size: 16, $weight: bold);
21
23
  @include govuk-text-colour;
22
- padding-top: govuk-spacing(2);
23
24
 
24
25
  @include govuk-media-query($from: tablet) {
25
26
  padding-top: govuk-spacing(6) / 4;
@@ -48,8 +49,9 @@
48
49
  }
49
50
 
50
51
  .app-contents-list__title {
52
+ margin: 0;
53
+
51
54
  @include govuk-text-colour;
52
55
  @include govuk-font($size: 16, $weight: regular, $line-height: 1.5);
53
- margin: 0;
54
56
  }
55
57
  }
@@ -2,9 +2,10 @@
2
2
 
3
3
  @include govuk-exports("govuk-eleventy-plugin/component/document-header") {
4
4
  .app-document-header__metadata {
5
- @include govuk-font($size: 16, $line-height: 1.5);
6
5
  color: $govuk-secondary-text-colour;
7
6
  max-width: 45em;
7
+
8
+ @include govuk-font($size: 16, $line-height: 1.5);
8
9
  }
9
10
 
10
11
  .app-document-header__description {
@@ -30,10 +30,10 @@
30
30
  <span aria-hidden="true">&ensp;•&ensp;</span>
31
31
  {%- endif -%}
32
32
  {%- if params.date -%}
33
- <span class="govuk-visually-hidden">Posted on </span><time datetime="{{ params.date | date }}">{{ params.date | date("d LLLL y") }}</time>
33
+ <span class="govuk-visually-hidden">Posted on </span><time datetime="{{ params.date | isoDate }}">{{ params.date | govukDate}}</time>
34
34
  {%- endif -%}
35
35
  {%- if params.modified -%}
36
- <span aria-hidden="true">&ensp;•&ensp;</span>Last updated <time datetime="{{ params.modified | date }}">{{ params.modified | date("d LLLL y") }}</time>
36
+ <span aria-hidden="true">&ensp;•&ensp;</span>Last updated <time datetime="{{ params.modified | isoDate }}">{{ params.modified | govukDate }}</time>
37
37
  {%- endif -%}
38
38
  {%- if params.tags | length > 0 -%}
39
39
  <span aria-hidden="true">&ensp;•&ensp;</span>Tags:
@@ -14,8 +14,9 @@
14
14
  }
15
15
 
16
16
  .app-document-list__item-title {
17
- @include govuk-font($size: 19, $weight: "bold");
18
17
  margin: 0 0 govuk-spacing(1);
18
+
19
+ @include govuk-font($size: 19, $weight: "bold");
19
20
  }
20
21
 
21
22
  .app-document-list__item-metadata {
@@ -24,16 +25,18 @@
24
25
  }
25
26
 
26
27
  .app-document-list__item-description {
27
- @include govuk-font($size: 16);
28
28
  margin: govuk-spacing(1) 0;
29
+
30
+ @include govuk-font($size: 16);
29
31
  }
30
32
 
31
33
  .app-document-list__attribute {
32
- @include govuk-font($size: 16);
33
34
  color: $govuk-secondary-text-colour;
34
35
  display: inline-block;
35
36
  margin: 0;
36
37
 
38
+ @include govuk-font($size: 16);
39
+
37
40
  & + .app-document-list__attribute::before {
38
41
  content: "\2002•\2002";
39
42
  }
@@ -16,7 +16,7 @@
16
16
  <ul class="app-document-list__item-metadata">
17
17
  {% if item.data.date %}
18
18
  <li class="app-document-list__attribute">
19
- <time datetime="{{ item.date | date }}">{{ item.date | date("d LLLL y") }}</time>
19
+ <time datetime="{{ item.date | isoDate }}">{{ item.date | govukDate }}</time>
20
20
  </li>
21
21
  {% endif %}
22
22
  {% if item.data.tags %}
@@ -19,11 +19,12 @@
19
19
 
20
20
  // Footnotes
21
21
  .app-footnotes-list {
22
- @include govuk-font($size: 19);
23
22
  counter-reset: footnotes;
24
23
  list-style-type: none;
25
24
  padding-left: 0;
26
25
 
26
+ @include govuk-font($size: 19);
27
+
27
28
  li {
28
29
  counter-increment: footnotes;
29
30
  padding: govuk-spacing(3);
@@ -32,17 +33,19 @@
32
33
  }
33
34
 
34
35
  li::before {
35
- @include govuk-font(19, $tabular: true);
36
36
  content: counter(footnotes) ".";
37
37
  left: govuk-spacing(4);
38
38
  position: absolute;
39
+
40
+ @include govuk-font(19, $tabular: true);
39
41
  }
40
42
 
41
43
  a[aria-label="Back to content"] {
42
- @include govuk-link-style-no-visited-state;
43
44
  position: relative;
44
45
  text-underline-offset: 0;
45
46
  top: 2px;
47
+
48
+ @include govuk-link-style-no-visited-state;
46
49
  }
47
50
 
48
51
  :target {
@@ -35,12 +35,6 @@
35
35
  max-width: 38em;
36
36
  }
37
37
 
38
- // Indicate abbreviations which have a title
39
- abbr {
40
- border-bottom: 1px dotted $govuk-secondary-text-colour;
41
- cursor: help;
42
- }
43
-
44
38
  // Distinguish inserted text from that inside links
45
39
  ins {
46
40
  text-decoration-style: double;
@@ -67,18 +61,21 @@
67
61
  bottom: -0.4em;
68
62
  }
69
63
 
70
- img {
64
+ iframe,
65
+ img,
66
+ video {
71
67
  height: auto;
72
68
  max-width: 100%;
73
69
  width: auto;
74
70
  }
75
71
 
76
72
  figure {
77
- @include govuk-responsive-margin(6, "bottom");
78
73
  margin-left: 0;
79
74
  margin-right: 0;
80
75
  width: 100%;
81
76
 
77
+ @include govuk-responsive-margin(6, "bottom");
78
+
82
79
  // Show outline around linked images within figures
83
80
  img {
84
81
  display: block;
@@ -101,9 +98,10 @@
101
98
  }
102
99
 
103
100
  figcaption {
104
- @include govuk-font($size: 19);
105
101
  color: $govuk-secondary-text-colour;
106
102
  margin-top: govuk-spacing(1);
103
+
104
+ @include govuk-font($size: 19);
107
105
  }
108
106
 
109
107
  // Responsive tables
@@ -111,17 +109,10 @@
111
109
  display: block;
112
110
  overflow-x: auto;
113
111
  scrollbar-color: $govuk-border-colour govuk-colour("light-grey");
112
+ scrollbar-width: thin;
114
113
 
115
- &::-webkit-scrollbar {
116
- height: govuk-spacing(1);
117
- width: govuk-spacing(1);
118
- }
119
-
120
- &::-webkit-scrollbar-thumb {
121
- background: $govuk-border-colour;
122
- }
123
-
124
- &:focus {
114
+ &:focus,
115
+ &:focus-visible {
125
116
  box-shadow:
126
117
  0 0 0 #{$govuk-focus-width * 3} govuk-colour("black"),
127
118
  0 0 0 #{$govuk-focus-width * 4} $govuk-focus-colour;
@@ -132,17 +123,10 @@
132
123
  box-shadow: none;
133
124
  outline: none;
134
125
  }
135
-
136
- &:focus-visible {
137
- box-shadow:
138
- 0 0 0 #{$govuk-focus-width * 3} govuk-colour("black"),
139
- 0 0 0 #{$govuk-focus-width * 4} $govuk-focus-colour;
140
- outline: #{$govuk-focus-width * 2} solid govuk-colour("white");
141
- }
142
126
  }
143
127
 
144
128
  // Reverse the colour of code that uses shell highlighting
145
- .x-govuk-code__language--shell {
129
+ .app-code__language--shell {
146
130
  background-color: govuk-colour("black");
147
131
  border: $govuk-focus-width solid govuk-colour("white");
148
132
  -webkit-font-smoothing: auto;
@@ -2,7 +2,7 @@
2
2
  {% from "govuk/components/service-navigation/macro.njk" import govukServiceNavigation %}
3
3
 
4
4
  {{ govukServiceNavigation({
5
- classes: "app-service-navigation" + (" " + params.classes if params.classes),
5
+ classes: "app-service-navigation" + (" govuk-service-navigation--inverse" if inverseMasthead) + (" " + params.classes if params.classes),
6
6
  attributes: params.attributes,
7
7
  ariaLabel: params.ariaLabel,
8
8
  menuButtonText: params.menuButtonText,
@@ -26,13 +26,13 @@ $_icon-size: 40px;
26
26
  }
27
27
 
28
28
  .app-site-search__link {
29
- @include govuk-link-style-inverse;
30
-
31
29
  display: inline-block;
32
30
  padding-bottom: govuk-spacing(1);
33
31
  padding-top: govuk-spacing(1);
34
32
  text-decoration: none;
35
33
 
34
+ @include govuk-link-style-inverse;
35
+
36
36
  &:hover {
37
37
  text-decoration: underline;
38
38
  text-decoration-thickness: 3px;
@@ -44,24 +44,23 @@ $_icon-size: 40px;
44
44
  }
45
45
 
46
46
  .app-site-search__wrapper {
47
+ background-color: govuk-colour("white");
47
48
  display: block;
48
49
  position: relative;
49
50
 
50
- .app-service-navigation & {
51
+ .govuk-service-navigation & {
51
52
  outline: 1px solid $govuk-border-colour;
52
53
  }
54
+
55
+ .govuk-service-navigation--inverse & {
56
+ outline: 1px solid transparent;
57
+ }
53
58
  }
54
59
 
55
60
  .app-site-search__hint,
56
61
  .app-site-search__input {
57
- -webkit-appearance: none;
58
62
  appearance: none;
59
- border: $govuk-border-width-form-element solid transparent;
60
- border-radius: 0; // Safari 10 on iOS adds implicit border rounding.
61
- box-sizing: border-box;
62
- height: govuk-px-to-rem($_icon-size);
63
- margin-bottom: 0; // BUG: Safari 10 on macOS seems to add an implicit margin.
64
- padding: govuk-spacing(1);
63
+ border-color: transparent;
65
64
  padding-left: $_icon-size - govuk-spacing(1);
66
65
  width: 100%;
67
66
  }
@@ -72,6 +71,7 @@ $_icon-size: 40px;
72
71
  }
73
72
 
74
73
  .app-site-search__input {
74
+ background-color: transparent;
75
75
  background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36' width='40' height='40'><path d='M25.7 24.8L21.9 21c.7-1 1.1-2.2 1.1-3.5 0-3.6-2.9-6.5-6.5-6.5S10 13.9 10 17.5s2.9 6.5 6.5 6.5c1.6 0 3-.6 4.1-1.5l3.7 3.7 1.4-1.4zM12 17.5c0-2.5 2-4.5 4.5-4.5s4.5 2 4.5 4.5-2 4.5-4.5 4.5-4.5-2-4.5-4.5z' fill='%23505a5f'></path></svg>");
76
76
  background-position: center left -2px;
77
77
  background-repeat: no-repeat;
@@ -189,26 +189,9 @@ $_icon-size: 40px;
189
189
  }
190
190
 
191
191
  .app-site-search--section {
192
- @include govuk-font($size: 16);
193
192
  color: $govuk-secondary-text-colour;
194
193
  display: block;
195
- }
196
194
 
197
- .app-site-search__aliases {
198
- margin-left: govuk-spacing(1);
199
-
200
- &::before {
201
- content: "(";
202
- }
203
-
204
- &::after {
205
- content: ")";
206
- }
207
- }
208
-
209
- .app-site-search__separator {
210
- display: inline-block;
211
- margin-left: govuk-spacing(1);
212
- margin-right: govuk-spacing(1);
195
+ @include govuk-font($size: 16);
213
196
  }
214
197
  }
@@ -1,4 +1,4 @@
1
- import accessibleAutocomplete from 'accessible-autocomplete/dist/accessible-autocomplete.min.js'
1
+ import accessibleAutocomplete from 'accessible-autocomplete'
2
2
 
3
3
  export class SiteSearchElement extends HTMLElement {
4
4
  constructor() {
@@ -83,17 +83,17 @@ export class SiteSearchElement extends HTMLElement {
83
83
  return searchElement
84
84
  }
85
85
 
86
- resultTemplate(result) {
86
+ suggestionTemplate(result) {
87
87
  if (result) {
88
88
  const element = document.createElement('span')
89
89
  element.textContent = result.title
90
90
 
91
- if (result.hasFrontmatterDate || result.section) {
91
+ if (result.hasFrontMatterDate || result.section) {
92
92
  const section = document.createElement('span')
93
93
  section.className = 'app-site-search--section'
94
94
 
95
95
  section.innerHTML =
96
- result.hasFrontmatterDate && result.section
96
+ result.hasFrontMatterDate && result.section
97
97
  ? `${result.section}<br>${result.date}`
98
98
  : result.section || result.date
99
99
 
@@ -119,6 +119,7 @@ export class SiteSearchElement extends HTMLElement {
119
119
  accessibleAutocomplete({
120
120
  element: search,
121
121
  id: this.searchInputId,
122
+ inputClasses: 'govuk-input',
122
123
  cssNamespace: 'app-site-search',
123
124
  displayMenu: 'overlay',
124
125
  minLength: 2,
@@ -129,7 +130,7 @@ export class SiteSearchElement extends HTMLElement {
129
130
  onConfirm: this.handleOnConfirm,
130
131
  templates: {
131
132
  inputValue: this.inputValueTemplate,
132
- suggestion: this.resultTemplate
133
+ suggestion: this.suggestionTemplate
133
134
  },
134
135
  tNoResults: this.handleNoResults.bind(this)
135
136
  })
@@ -1,38 +1,4 @@
1
- /**
2
- * Get navigation key for page
3
- *
4
- * @param {object} data - Page data
5
- * @returns {string|undefined} Page navigation key
6
- */
7
- const getKey = (data) => {
8
- const { homepage, eleventyExcludeFromCollections, eleventyNavigation } = data
9
-
10
- if (homepage) {
11
- // Use explicit navigation `key`, or `homeKey` set in plugin options
12
- return eleventyNavigation.key || data.options.homeKey
13
- } else if (!eleventyExcludeFromCollections) {
14
- // Use explicit navigation `key`, or page title
15
- return eleventyNavigation.key || data.title
16
- }
17
- }
18
-
19
- /**
20
- * Get navigation parent for page
21
- *
22
- * @param {object} data - Page data
23
- * @returns {string|undefined} Parent page key
24
- */
25
- const getParent = (data) => {
26
- const { homepage, eleventyExcludeFromCollections, eleventyNavigation } = data
27
-
28
- if (homepage) {
29
- // The homepage has no parent
30
- return false
31
- } else if (!eleventyExcludeFromCollections) {
32
- // Use explicit navigation `parent`, or `homeKey` set in plugin options
33
- return eleventyNavigation.parent || data.options.homeKey
34
- }
35
- }
1
+ import { getNavigationKey, getNavigationParent } from '../utils.js'
36
2
 
37
3
  /**
38
4
  * Set sensible defaults for eleventyNavigation
@@ -41,8 +7,8 @@ const getParent = (data) => {
41
7
  */
42
8
  export const eleventyComputed = {
43
9
  eleventyNavigation: {
44
- key: (data) => getKey(data),
45
- parent: (data) => getParent(data),
10
+ key: (data) => getNavigationKey(data),
11
+ parent: (data) => getNavigationParent(data),
46
12
  excerpt: (data) => data.eleventyNavigation.excerpt || data.description
47
13
  }
48
14
  }
@@ -16,6 +16,7 @@ const defaults = {
16
16
  },
17
17
  homeKey: 'Home',
18
18
  parentSite: false,
19
+ rebrand: true,
19
20
  stylesheets: [],
20
21
  titleSuffix: 'GOV.UK',
21
22
  url: false
@@ -30,17 +31,18 @@ export function defaultPluginOptions(options, pathPrefix) {
30
31
  }
31
32
 
32
33
  // Rewrite default GOV.UK icon asset paths if rebrand enabled
33
- defaults.icons = options.rebrand
34
- ? {
35
- mask: '/assets/rebrand/images/govuk-mask-icon.svg',
36
- shortcut: '/assets/rebrand/images/favicon.ico',
37
- touch: '/assets/rebrand/images/govuk-icon-180.png'
38
- }
39
- : {
40
- mask: '/assets/images/govuk-mask-icon.svg',
41
- shortcut: '/assets/images/favicon.ico',
42
- touch: '/assets/images/govuk-icon-180.png'
43
- }
34
+ defaults.icons =
35
+ options.rebrand !== false
36
+ ? {
37
+ mask: '/assets/rebrand/images/govuk-mask-icon.svg',
38
+ shortcut: '/assets/rebrand/images/favicon.ico',
39
+ touch: '/assets/rebrand/images/govuk-icon-180.png'
40
+ }
41
+ : {
42
+ mask: '/assets/images/govuk-mask-icon.svg',
43
+ shortcut: '/assets/images/favicon.ico',
44
+ touch: '/assets/images/govuk-icon-180.png'
45
+ }
44
46
 
45
47
  // Rewrite default GOV.UK Opengraph asset path if rebrand enabled
46
48
  defaults.opengraphImageUrl = options.rebrand
@@ -48,7 +48,7 @@ export async function generateAssets(dir, options) {
48
48
  ]
49
49
  })
50
50
 
51
- const { output } = await bundle.generate({ format: 'iife' })
51
+ const { output } = await bundle.generate({ format: 'es' })
52
52
  const { code } = output[0]
53
53
  fs.writeFile(jsFile, code)
54
54
  } catch (error) {
@@ -1,32 +1,48 @@
1
- import { DateTime } from 'luxon'
1
+ import { normalise } from '../utils.js'
2
2
 
3
3
  /**
4
- * Format a data using tokens
4
+ * Format ISO 8601 date string into a date with the GOV.UK style
5
5
  *
6
- * @param {string} string - Date to convert
7
- * @param {string} [format] - Optional token-based formatting
8
- * @returns {string} Formatted date
9
- * @example date('2022-03-16', 'yyyy') // 2022
6
+ * @param {string} string - Date
7
+ * @returns {string} `string` as a date with the GOV.UK style
10
8
  */
11
- export function date(string, format) {
12
- // Enable special `now` value
13
- const dateObject = string === 'now' ? DateTime.local().toJSDate() : string
14
-
15
- // Convert dateObj to Luxon DateTime object, using UTC
16
- // See: https://11ty.dev/docs/dates/#dates-off-by-one-day
17
- let date = DateTime.fromJSDate(dateObject, {
18
- locale: 'en-GB',
19
- zone: 'utc'
20
- })
21
-
22
- if (format) {
23
- // Format date if formatting tokens provided
24
- // See: https://moment.github.io/luxon/#/formatting?id=table-of-tokens
25
- date = DateTime.fromISO(date).toFormat(format)
26
- } else {
27
- // Format date as ISO 8601
28
- date = date.toISO()
9
+ export function govukDate(string) {
10
+ string = normalise(string, '')
11
+
12
+ try {
13
+ const date = Date.parse(string)
14
+
15
+ // 2021-08-17 => 17 August 2021
16
+ const formattedDate = new Intl.DateTimeFormat('en-GB', {
17
+ year: 'numeric',
18
+ month: 'long',
19
+ day: 'numeric'
20
+ }).format(date)
21
+
22
+ return formattedDate
23
+ } catch (error) {
24
+ return error.message.split(':')[0]
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Format string into an ISO 8601 date
30
+ *
31
+ * @param {string} string - String
32
+ * @returns {string} ISO 8601 date
33
+ */
34
+ export function isoDate(string) {
35
+ string = normalise(string, '')
36
+
37
+ if (!string) {
38
+ return
29
39
  }
30
40
 
31
- return date
41
+ try {
42
+ const date = new Date(string)
43
+
44
+ return date.toISOString()
45
+ } catch (error) {
46
+ return error.message.split(':')[0]
47
+ }
32
48
  }
@@ -1,5 +1,5 @@
1
1
  export { canonicalUrl } from './canonical-url.js'
2
- export { date } from './date.js'
2
+ export { govukDate, isoDate } from './date.js'
3
3
  export {
4
4
  dateToRfc3339,
5
5
  getNewestCollectionItemDate
@@ -7,7 +7,6 @@ export {
7
7
  export { currentPage } from './current-page.js'
8
8
  export { includes } from './includes.js'
9
9
  export { sliceFromCollection } from './slice-from-collection.js'
10
- export { itemsFromCollection } from './items-from-collection.js'
11
10
  export { itemsFromNavigation } from './items-from-navigation.js'
12
11
  export { itemsFromPagination } from './items-from-pagination.js'
13
12
  export { markdown } from './markdown.js'
@@ -7,12 +7,14 @@ import { smart } from './smart.js'
7
7
  * @param {Array} eleventyNavigation - Eleventy navigation data
8
8
  * @param {string} [pageUrl] - URL of current page
9
9
  * @param {object} [options] - Plugin options
10
+ * @param {boolean} [sort] - Sort navigation items
10
11
  * @returns {Array} `items` array
11
12
  */
12
13
  export function itemsFromNavigation(
13
14
  eleventyNavigation,
14
15
  pageUrl = false,
15
- options = {}
16
+ options = {},
17
+ sort = false
16
18
  ) {
17
19
  const items = []
18
20
 
@@ -23,6 +25,8 @@ export function itemsFromNavigation(
23
25
  parent: pageUrl && pageUrl.startsWith(item.url),
24
26
  href: item.url,
25
27
  text: smart(item.title),
28
+ order: item.data?.order,
29
+ theme: item.data?.theme,
26
30
  children: item.children
27
31
  ? item.children.map((child) => ({
28
32
  current: pageUrl && child.url === pageUrl,
@@ -47,5 +51,23 @@ export function itemsFromNavigation(
47
51
  })
48
52
  }
49
53
 
54
+ if (sort) {
55
+ items.sort((a, b) => {
56
+ if (typeof a.order !== 'undefined' && typeof b.order !== 'undefined') {
57
+ // Sort by order value, if given
58
+ return (a.order || 0) - (b.order || 0)
59
+ }
60
+
61
+ // Sort by title
62
+ if (a.text < b.text) {
63
+ return -1
64
+ } else if (a.text > b.text) {
65
+ return 1
66
+ }
67
+
68
+ return 0
69
+ })
70
+ }
71
+
50
72
  return items
51
73
  }
@@ -12,18 +12,18 @@ export function tokenize(string) {
12
12
  content = content.toLowerCase()
13
13
 
14
14
  // Remove HTML elements
15
- // Remove words with apostrophes (’)
16
- // Remove ampersands (&amp;), punctuation and newlines
17
- // Remove short and less meaningful words
18
15
  let tokens = content.replace(
19
16
  /<script.*?<\/script>|<!--.*?-->|<style.*?<\/style>|<.*?>/g,
20
17
  ' '
21
18
  )
22
- tokens = tokens.replace(/(?=\S*[’])([a-zA-Z’]+)/gi, '')
23
- tokens = tokens.replace(
24
- /\.\s|,|;|‘|“|”|\?|\(|\)|\[|\]|\/|-|–|§|&amp;|\n/g,
25
- ' '
26
- )
19
+
20
+ // Remove HTML entities
21
+ tokens = tokens.replace(/&[a-zA-Z0-9#]+;/gi, ' ')
22
+
23
+ // Remove special characters
24
+ tokens = tokens.replace(/[^0-9a-zA-Z]+/gi, ' ')
25
+
26
+ // Remove short and less meaningful words
27
27
  tokens = tokens.replace(
28
28
  /\b(the|a|an|and|am|you|I|to|if|of|off|me|my|on|in|it|is|at|as|we|do|be|has|but|was|so|no|not|or|up|for)\b/gi,
29
29
  ' '
package/src/index.js CHANGED
@@ -52,9 +52,8 @@ export async function govukEleventyPlugin(eleventyConfig, pluginOptions = {}) {
52
52
  eleventyConfig.addPlugin(EleventyHtmlBasePlugin)
53
53
  eleventyConfig.addPlugin(eleventyNavigation)
54
54
 
55
- // GOV.UK rebrand
56
- // Defaults to `false`; will change to `true` in a release after 25 June 2025
57
- eleventyConfig.addNunjucksGlobal('govukRebrand', options.rebrand || false)
55
+ // GOV.UK rebrand (defaults to `true`)
56
+ eleventyConfig.addNunjucksGlobal('govukRebrand', options.rebrand)
58
57
 
59
58
  // Events
60
59
  eleventyConfig.on('eleventy.after', async ({ dir }) => {
@@ -45,7 +45,7 @@
45
45
  {% endblock %}
46
46
 
47
47
  {% block head %}
48
- <script src="/assets/application.js" type="module" defer></script>
48
+ <script src="/assets/application.js" type="module"></script>
49
49
 
50
50
  {% for stylesheet in options.stylesheets %}
51
51
  <link rel="stylesheet" href="{{ stylesheet | canonicalUrl }}">
@@ -58,8 +58,7 @@
58
58
  {% if options.url %}<meta property="og:url" content="{{ page.url | canonicalUrl }}">{% endif %}
59
59
  <meta property="og:title" content="{{ title }}">
60
60
  {% if description %}<meta property="og:description" name="description" content="{{ description | markdown("inline") | striptags(true) }}">{% endif %}
61
- {% if opengraphImage %}<meta name="twitter:card" content="summary_large_image">{% endif %}
62
- {% if opengraphImage.src %}<meta name="twitter:image" content="{{ opengraphImage.src | canonicalUrl }}">
61
+ {% if opengraphImage.src %}<meta name="og:image" content="{{ opengraphImage.src | canonicalUrl }}">
63
62
  {% endif %}
64
63
  {% if opengraphImage.alt %}<meta property="og:image:alt" content="{{ opengraphImage.alt }}">{% endif %}
65
64
  {% endblock %}
@@ -1,22 +1,19 @@
1
1
  {% extends "layouts/base.njk" %}
2
2
 
3
+ {% set inverseMasthead = inverseMasthead | default(true) %}
4
+
3
5
  {% block main %}
4
6
  <main id="main-content" role="main" {%- if mainLang %} lang="{{ mainLang }}"{% endif %}>
5
7
  {{ xGovukMasthead({
8
+ inverse: inverseMasthead,
6
9
  title: {
7
10
  html: title | smart
8
11
  } if title,
9
12
  description: {
10
13
  html: description | markdown("inline") | noOrphans
11
14
  } if description,
12
- startButton: {
13
- href: startButton.href,
14
- text: startButton.text
15
- } if startButton,
16
- image: {
17
- alt: image.alt,
18
- src: image.src
19
- } if image,
15
+ startButton: startButton,
16
+ image: image,
20
17
  breadcrumbs: {
21
18
  classes: "govuk-!-display-none-print",
22
19
  items: breadcrumbItems
@@ -2,10 +2,10 @@
2
2
  {
3
3
  "title": {{ item.data.title | default("") | dump | safe }},
4
4
  {% if item.data.description %}"description": {{ item.data.description | dump | safe }},{% endif %}
5
- {% if item.data.eleventyNavigation.parent %}"section": {{ item.data.eleventyNavigation.parent | dump | safe }},{% endif %}
5
+ {% if item.data.eleventyNavigation.parent and item.data.eleventyNavigation.parent != item.data.options.homeKey %}"section": {{ item.data.eleventyNavigation.parent | dump | safe }},{% endif %}
6
6
  "layout": {{ item.data.layout | default("") | dump | safe }},
7
- "hasFrontmatterDate": {{ item.data.date !== undefined }},
8
- "date": {{ item.date | date("d LLLL y") | default("") | dump | safe }},
7
+ "hasFrontMatterDate": {{ item.data.date !== undefined }},
8
+ "date": {{ item.date | govukDate | default("") | dump | safe }},
9
9
  "templateContent": {{ item.templateContent | tokenize | default([]) | dump | safe }},
10
10
  "url": {{ item.url | canonicalUrl | pretty | default("") | dump | safe }}
11
11
  }{% if not loop.last %},{% endif %}
@@ -11,7 +11,7 @@
11
11
  <div class="govuk-grid-row">
12
12
  <div class="govuk-grid-column-one-quarter-from-desktop">
13
13
  {{ xGovukSubNavigation({
14
- items: collections.navigation | eleventyNavigation(sectionKey or options.homeKey) | itemsFromNavigation(page.url, { pathPrefix: options.pathPrefix })
14
+ items: collections.navigation | eleventyNavigation(sectionKey or options.homeKey) | itemsFromNavigation(page.url, { pathPrefix: options.pathPrefix }, true)
15
15
  }) }}
16
16
  </div>
17
17
  <div class="govuk-grid-column-three-quarters-from-desktop">
@@ -14,6 +14,7 @@
14
14
  title: title or "Tags"
15
15
  }) }}
16
16
 
17
+ {% if collections.tags | length > 0 %}
17
18
  <ul class="govuk-list govuk-list--bullet">
18
19
  {% for tag in collections.tags %}
19
20
  <li>
@@ -22,6 +23,9 @@
22
23
  </li>
23
24
  {% endfor %}
24
25
  </ul>
26
+ {% else %}
27
+ <p class="govuk-body">No tags found.</p>
28
+ {% endif %}
25
29
  </div>
26
30
  </div>
27
31
  {% endblock %}
@@ -6,5 +6,11 @@
6
6
  export function defListRules(md) {
7
7
  const { rules } = md.renderer
8
8
 
9
- rules.dl_open = () => '<dl class="app-definition-list">\n'
9
+ rules.dl_open = () => '<dl class="govuk-body">\n'
10
+
11
+ rules.dt_open = () =>
12
+ '<dt class="govuk-!-font-weight-bold govuk-!-margin-bottom-2">\n'
13
+
14
+ rules.dd_open = () =>
15
+ '<dd class="govuk-!-margin-bottom-2 govuk-!-margin-left-4">\n'
10
16
  }
@@ -2,6 +2,7 @@ import slugify from '@sindresorhus/slugify'
2
2
  import MarkdownIt from 'markdown-it'
3
3
  import markdownItAbbr from 'markdown-it-abbr'
4
4
  import markdownItAnchor from 'markdown-it-anchor'
5
+ import markdownItAttrs from 'markdown-it-attrs'
5
6
  import markdownItDeflist from 'markdown-it-deflist'
6
7
  import markdownItFootnote from 'markdown-it-footnote'
7
8
  import markdownItGovuk from 'markdown-it-govuk'
@@ -15,7 +16,6 @@ import markdownItTableOfContents from 'markdown-it-table-of-contents'
15
16
 
16
17
  import { defListRules } from './markdown-it/deflist.js'
17
18
  import { footnotesRules } from './markdown-it/footnote.js'
18
- import { tableOfContentsRules } from './markdown-it/table-of-contents.js'
19
19
  import { tableRules } from './markdown-it/table.js'
20
20
 
21
21
  /**
@@ -49,6 +49,7 @@ export function md(options = {}) {
49
49
  : false,
50
50
  slugify: (string) => slugify(string).replaceAll(/[*+~.()'"!:@]/g, '')
51
51
  })
52
+ .use(markdownItAttrs)
52
53
  .use(markdownItDeflist)
53
54
  .use(defListRules)
54
55
  .use(markdownItFootnote)
@@ -62,9 +63,15 @@ export function md(options = {}) {
62
63
  .use(markdownItSup)
63
64
  .use(tableRules)
64
65
  .use(markdownItTableOfContents, {
65
- includeLevel: [2, 3]
66
+ includeLevel: [2, 3],
67
+ listType: 'ol',
68
+ transformContainerOpen: () => {
69
+ return '<nav class="app-contents-list"><h2 class="app-contents-list__title">Contents</h2>'
70
+ },
71
+ transformContainerClose: () => {
72
+ return '</nav>'
73
+ }
66
74
  })
67
- .use(tableOfContentsRules)
68
75
 
69
76
  return md
70
77
  }
package/src/utils.js CHANGED
@@ -27,6 +27,42 @@ export const layoutNames = [
27
27
  'tags'
28
28
  ]
29
29
 
30
+ /**
31
+ * Get navigation key for page
32
+ *
33
+ * @param {object} data - Page data
34
+ * @returns {string|undefined} Page navigation key
35
+ */
36
+ export const getNavigationKey = (data) => {
37
+ const { homepage, eleventyExcludeFromCollections, eleventyNavigation } = data
38
+
39
+ if (homepage) {
40
+ // Use explicit navigation `key`, or `homeKey` set in plugin options
41
+ return eleventyNavigation.key || data.options.homeKey
42
+ } else if (!eleventyExcludeFromCollections) {
43
+ // Use explicit navigation `key`, or page title
44
+ return eleventyNavigation.key || data.title
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Get navigation parent for page
50
+ *
51
+ * @param {object} data - Page data
52
+ * @returns {string|undefined} Parent page key
53
+ */
54
+ export const getNavigationParent = (data) => {
55
+ const { homepage, eleventyExcludeFromCollections, eleventyNavigation } = data
56
+
57
+ if (homepage) {
58
+ // The homepage has no parent
59
+ return false
60
+ } else if (!eleventyExcludeFromCollections) {
61
+ // Use explicit navigation `parent`, or `homeKey` set in plugin options
62
+ return eleventyNavigation.parent || data.options.homeKey
63
+ }
64
+ }
65
+
30
66
  /**
31
67
  * Get virtual templates
32
68
  * Uses users own named layout if exists, else provides a virtual template
@@ -1,19 +0,0 @@
1
- @use "govuk-frontend/dist/govuk" as *;
2
-
3
- @include govuk-exports("govuk-eleventy-plugin/component/definition-list") {
4
- .app-definition-list {
5
- dt {
6
- @include govuk-font($size: 19, $weight: bold);
7
- margin-bottom: govuk-spacing(1);
8
- }
9
-
10
- dd + dt {
11
- margin-top: govuk-spacing(4);
12
- }
13
-
14
- dd {
15
- @include govuk-font($size: 19);
16
- margin-left: govuk-spacing(5);
17
- }
18
- }
19
- }
@@ -1,15 +0,0 @@
1
- import { smart } from './smart.js'
2
-
3
- /**
4
- * Transform list of posts in a collection to `items` array that can be
5
- * consumed by GOV.UK Frontend components
6
- *
7
- * @param {Array} array - Eleventy collection data
8
- * @returns {Array} `items` array
9
- */
10
- export function itemsFromCollection(array) {
11
- return array.map((item) => ({
12
- text: smart(item.data.title),
13
- href: item.data.url
14
- }))
15
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Render a table of contents
3
- *
4
- * @param {Function} md - markdown-it instance
5
- */
6
- export function tableOfContentsRules(md) {
7
- const { rules } = md.renderer
8
-
9
- rules.toc_open =
10
- () => `<nav class="app-contents-list" aria-label="Contents" role="navigation">
11
- <h2 class="app-contents-list__title">Contents</h2>\n`
12
-
13
- rules.toc_close = () => '</nav>'
14
- }