govuk_publishing_components 21.60.0 → 21.60.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_charts.scss +4 -2
  3. data/app/models/govuk_publishing_components/audit_components.rb +1 -1
  4. data/app/views/govuk_publishing_components/components/docs/govspeak.yml +45 -0
  5. data/lib/govuk_publishing_components/version.rb +1 -1
  6. data/node_modules/govuk-frontend/govuk/all.js +160 -57
  7. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +16 -8
  8. data/node_modules/govuk-frontend/govuk/components/character-count/macro-options.json +6 -0
  9. data/node_modules/govuk-frontend/govuk/components/character-count/template.njk +1 -0
  10. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +6 -5
  11. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +68 -21
  12. data/node_modules/govuk-frontend/govuk/components/date-input/macro-options.json +1 -1
  13. data/node_modules/govuk-frontend/govuk/components/file-upload/_index.scss +0 -27
  14. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +0 -3
  15. data/node_modules/govuk-frontend/govuk/components/footer/template.njk +1 -1
  16. data/node_modules/govuk-frontend/govuk/components/header/template.njk +1 -1
  17. data/node_modules/govuk-frontend/govuk/components/hint/_index.scss +2 -2
  18. data/node_modules/govuk-frontend/govuk/components/hint/template.njk +2 -2
  19. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +0 -3
  20. data/node_modules/govuk-frontend/govuk/components/input/macro-options.json +6 -0
  21. data/node_modules/govuk-frontend/govuk/components/input/template.njk +1 -0
  22. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +76 -28
  23. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +0 -3
  24. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +1 -0
  25. data/node_modules/govuk-frontend/govuk/components/textarea/_index.scss +0 -3
  26. data/node_modules/govuk-frontend/govuk/components/textarea/macro-options.json +6 -0
  27. data/node_modules/govuk-frontend/govuk/components/textarea/template.njk +1 -0
  28. data/node_modules/govuk-frontend/govuk/components/warning-text/_index.scss +4 -2
  29. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +1 -1
  30. data/node_modules/govuk-frontend/govuk/settings/_colours-applied.scss +2 -2
  31. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +1 -1
  32. data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +4 -1
  33. data/node_modules/govuk-frontend/package.json +1 -1
  34. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b8116f535e5bc9da283f603082fc02523e8676608726754358886cdb2567016
4
- data.tar.gz: 6f9d0752fe36925881d43c0d97738a987ea8196371f1830fbab336ef00c0f37a
3
+ metadata.gz: 3e9b30172e3f16dbfec024528c8e5b771e652000272cca8db4de6e3d5259a6e7
4
+ data.tar.gz: 1e0a1c48adc79cdcfe372368223bf39153ccdfd39ec3288692ab2614b4b9560c
5
5
  SHA512:
6
- metadata.gz: d439c10348f41bafc7a1d84b42c1d3b662fd1b227ca4575ef649df0b41b22f58291318874eb018443b14d5bb5c2ecc9dd235e2399b28eb0cb1749003a9c1a4fb
7
- data.tar.gz: 37af2ed2a8fa27fb8b515fb05947f987d686cce61a18a249e284d8879ef5b5d62086b2ba1d051236e537686ed50fc636de75f711d1410585a611dc586fbafad9
6
+ metadata.gz: 1a7f16065f473c0dfb009f2a5e19edce7467017beb7ae953fd046adace5afd82ba90d34f24ef4bf01e5f6d68dc1a16b965aa2802399e8695e3f3d7d4e526e322
7
+ data.tar.gz: 83774a0b06a6092303a514ace31c7cc37ac10726f6d769b88d57419867b114960aaf1ee9f7109fdb60448661fd71a2884cdb524a6e93c78bb7d2901bd5a06367
@@ -29,8 +29,9 @@
29
29
  $chart-border: govuk-colour("white"); // Chart border colour
30
30
  $key-border: govuk-colour("white"); // Key border colour
31
31
  $bar-colours: govuk-colour("blue"), govuk-colour("turquoise"), govuk-colour("green"), govuk-colour("light-green"), govuk-colour("yellow"), govuk-colour("orange"), govuk-colour("red"), govuk-colour("bright-purple", $legacy: "bright-red");
32
- $bar-cell-colour: #ebebeb;
33
- $bar-outdented-colour: #111111;
32
+ $bar-text-colours: govuk-colour("white"), govuk-colour("black"), govuk-colour("white"), govuk-colour("black"), govuk-colour("black"), govuk-colour("black"), govuk-colour("white"), govuk-colour("white");
33
+ $bar-cell-colour: govuk-colour("black");
34
+ $bar-outdented-colour: govuk-colour("black");
34
35
 
35
36
  // CHART STYLES
36
37
  .mc-chart {
@@ -140,6 +141,7 @@
140
141
  @for $i from 0 to length($bar-colours) {
141
142
  .mc-bar-#{$i + 1} {
142
143
  background-color: nth($bar-colours, $i + 1);
144
+ color: nth($bar-text-colours, $i + 1);
143
145
  }
144
146
  }
145
147
 
@@ -64,7 +64,7 @@ module GovukPublishingComponents
64
64
  end
65
65
 
66
66
  def get_component_name_from_full_path(path)
67
- path.gsub("/_", "/")
67
+ path.gsub(/.*\K\/_/, "/")
68
68
  .gsub(@templates_full_path, "")
69
69
  .gsub(".html.erb", "")
70
70
  .gsub(".erb", "")
@@ -190,6 +190,51 @@ examples:
190
190
  </tr>
191
191
  </tbody>
192
192
  </table>
193
+ chart_with_colours:
194
+ data:
195
+ block: |
196
+ <table class="js-barchart-table mc-auto-outdent">
197
+ <thead>
198
+ <tr>
199
+ <th scope="col">Number position</th>
200
+ <th scope="col">Apples</th>
201
+ <th scope="col">Oranges</th>
202
+ <th scope="col">Bananas</th>
203
+ <th scope="col">Pears</th>
204
+ <th scope="col">Grapes</th>
205
+ <th scope="col">Strawberries</th>
206
+ <th scope="col">Plums</th>
207
+ <th scope="col">Apricots</th>
208
+ <th scope="col">Pineapples</th>
209
+ </tr>
210
+ </thead>
211
+ <tbody>
212
+ <tr>
213
+ <td>Numbers inside bar</td>
214
+ <td>16</td>
215
+ <td>48</td>
216
+ <td>39</td>
217
+ <td>50</td>
218
+ <td>24</td>
219
+ <td>10</td>
220
+ <td>62</td>
221
+ <td>29</td>
222
+ <td>81</td>
223
+ </tr>
224
+ <tr>
225
+ <td>Numbers outside bar</td>
226
+ <td>2</td>
227
+ <td>1</td>
228
+ <td>2</td>
229
+ <td>1</td>
230
+ <td>1</td>
231
+ <td>3</td>
232
+ <td>3</td>
233
+ <td>1</td>
234
+ <td>2</td>
235
+ </tr>
236
+ </tbody>
237
+ </table>
193
238
  address:
194
239
  data:
195
240
  block: |
@@ -1,3 +1,3 @@
1
1
  module GovukPublishingComponents
2
- VERSION = "21.60.0".freeze
2
+ VERSION = "21.60.1".freeze
3
3
  end
@@ -1542,13 +1542,22 @@ CharacterCount.prototype.init = function () {
1542
1542
  // Remove hard limit if set
1543
1543
  $module.removeAttribute('maxlength');
1544
1544
 
1545
- // Bind event changes to the textarea
1546
- var boundChangeEvents = this.bindChangeEvents.bind(this);
1547
- boundChangeEvents();
1545
+ // When the page is restored after navigating 'back' in some browsers the
1546
+ // state of the character count is not restored until *after* the DOMContentLoaded
1547
+ // event is fired, so we need to sync after the pageshow event in browsers
1548
+ // that support it.
1549
+ if ('onpageshow' in window) {
1550
+ window.addEventListener('pageshow', this.sync.bind(this));
1551
+ } else {
1552
+ window.addEventListener('DOMContentLoaded', this.sync.bind(this));
1553
+ }
1554
+
1555
+ this.sync();
1556
+ };
1548
1557
 
1549
- // Update count message
1550
- var boundUpdateCountMessage = this.updateCountMessage.bind(this);
1551
- boundUpdateCountMessage();
1558
+ CharacterCount.prototype.sync = function () {
1559
+ this.bindChangeEvents();
1560
+ this.updateCountMessage();
1552
1561
  };
1553
1562
 
1554
1563
  // Read data attributes
@@ -1596,8 +1605,7 @@ CharacterCount.prototype.checkIfValueChanged = function () {
1596
1605
  if (!this.$textarea.oldValue) this.$textarea.oldValue = '';
1597
1606
  if (this.$textarea.value !== this.$textarea.oldValue) {
1598
1607
  this.$textarea.oldValue = this.$textarea.value;
1599
- var boundUpdateCountMessage = this.updateCountMessage.bind(this);
1600
- boundUpdateCountMessage();
1608
+ this.updateCountMessage();
1601
1609
  }
1602
1610
  };
1603
1611
 
@@ -1666,44 +1674,91 @@ function Checkboxes ($module) {
1666
1674
  this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
1667
1675
  }
1668
1676
 
1677
+ /**
1678
+ * Initialise Checkboxes
1679
+ *
1680
+ * Checkboxes can be associated with a 'conditionally revealed' content block –
1681
+ * for example, a checkbox for 'Phone' could reveal an additional form field for
1682
+ * the user to enter their phone number.
1683
+ *
1684
+ * These associations are made using a `data-aria-controls` attribute, which is
1685
+ * promoted to an aria-controls attribute during initialisation.
1686
+ *
1687
+ * We also need to restore the state of any conditional reveals on the page (for
1688
+ * example if the user has navigated back), and set up event handlers to keep
1689
+ * the reveal in sync with the checkbox state.
1690
+ */
1669
1691
  Checkboxes.prototype.init = function () {
1670
1692
  var $module = this.$module;
1671
1693
  var $inputs = this.$inputs;
1672
1694
 
1673
- /**
1674
- * Loop over all items with [data-controls]
1675
- * Check if they have a matching conditional reveal
1676
- * If they do, assign attributes.
1677
- **/
1678
1695
  nodeListForEach($inputs, function ($input) {
1679
- var controls = $input.getAttribute('data-aria-controls');
1696
+ var target = $input.getAttribute('data-aria-controls');
1680
1697
 
1681
- // Check if input controls anything
1682
- // Check if content exists, before setting attributes.
1683
- if (!controls || !$module.querySelector('#' + controls)) {
1698
+ // Skip checkboxes without data-aria-controls attributes, or where the
1699
+ // target element does not exist.
1700
+ if (!target || !$module.querySelector('#' + target)) {
1684
1701
  return
1685
1702
  }
1686
1703
 
1687
- // If we have content that is controlled, set attributes.
1688
- $input.setAttribute('aria-controls', controls);
1704
+ // Promote the data-aria-controls attribute to a aria-controls attribute
1705
+ // so that the relationship is exposed in the AOM
1706
+ $input.setAttribute('aria-controls', target);
1689
1707
  $input.removeAttribute('data-aria-controls');
1690
- this.setAttributes($input);
1691
- }.bind(this));
1708
+ });
1709
+
1710
+ // When the page is restored after navigating 'back' in some browsers the
1711
+ // state of form controls is not restored until *after* the DOMContentLoaded
1712
+ // event is fired, so we need to sync after the pageshow event in browsers
1713
+ // that support it.
1714
+ if ('onpageshow' in window) {
1715
+ window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
1716
+ } else {
1717
+ window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
1718
+ }
1719
+
1720
+ // Although we've set up handlers to sync state on the pageshow or
1721
+ // DOMContentLoaded event, init could be called after those events have fired,
1722
+ // for example if they are added to the page dynamically, so sync now too.
1723
+ this.syncAllConditionalReveals();
1692
1724
 
1693
- // Handle events
1694
1725
  $module.addEventListener('click', this.handleClick.bind(this));
1695
1726
  };
1696
1727
 
1697
- Checkboxes.prototype.setAttributes = function ($input) {
1698
- var inputIsChecked = $input.checked;
1699
- $input.setAttribute('aria-expanded', inputIsChecked);
1728
+ /**
1729
+ * Sync the conditional reveal states for all inputs in this $module.
1730
+ */
1731
+ Checkboxes.prototype.syncAllConditionalReveals = function () {
1732
+ nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
1733
+ };
1734
+
1735
+ /**
1736
+ * Sync conditional reveal with the input state
1737
+ *
1738
+ * Synchronise the visibility of the conditional reveal, and its accessible
1739
+ * state, with the input's checked state.
1740
+ *
1741
+ * @param {HTMLInputElement} $input Checkbox input
1742
+ */
1743
+ Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
1744
+ var $target = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1700
1745
 
1701
- var $content = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1702
- if ($content) {
1703
- $content.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1746
+ if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
1747
+ var inputIsChecked = $input.checked;
1748
+
1749
+ $input.setAttribute('aria-expanded', inputIsChecked);
1750
+ $target.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1704
1751
  }
1705
1752
  };
1706
1753
 
1754
+ /**
1755
+ * Click event handler
1756
+ *
1757
+ * Handle a click within the $module – if the click occurred on a checkbox, sync
1758
+ * the state of any associated conditional reveal with the checkbox state.
1759
+ *
1760
+ * @param {MouseEvent} event Click event
1761
+ */
1707
1762
  Checkboxes.prototype.handleClick = function (event) {
1708
1763
  var $target = event.target;
1709
1764
 
@@ -1711,7 +1766,7 @@ Checkboxes.prototype.handleClick = function (event) {
1711
1766
  var isCheckbox = $target.getAttribute('type') === 'checkbox';
1712
1767
  var hasAriaControls = $target.getAttribute('aria-controls');
1713
1768
  if (isCheckbox && hasAriaControls) {
1714
- this.setAttributes($target);
1769
+ this.syncConditionalRevealWithInputState($target);
1715
1770
  }
1716
1771
  };
1717
1772
 
@@ -1959,67 +2014,115 @@ Header.prototype.handleClick = function (event) {
1959
2014
 
1960
2015
  function Radios ($module) {
1961
2016
  this.$module = $module;
2017
+ this.$inputs = $module.querySelectorAll('input[type="radio"]');
1962
2018
  }
1963
2019
 
2020
+ /**
2021
+ * Initialise Radios
2022
+ *
2023
+ * Radios can be associated with a 'conditionally revealed' content block – for
2024
+ * example, a radio for 'Phone' could reveal an additional form field for the
2025
+ * user to enter their phone number.
2026
+ *
2027
+ * These associations are made using a `data-aria-controls` attribute, which is
2028
+ * promoted to an aria-controls attribute during initialisation.
2029
+ *
2030
+ * We also need to restore the state of any conditional reveals on the page (for
2031
+ * example if the user has navigated back), and set up event handlers to keep
2032
+ * the reveal in sync with the radio state.
2033
+ */
1964
2034
  Radios.prototype.init = function () {
1965
2035
  var $module = this.$module;
1966
- var $inputs = $module.querySelectorAll('input[type="radio"]');
2036
+ var $inputs = this.$inputs;
1967
2037
 
1968
- /**
1969
- * Loop over all items with [data-controls]
1970
- * Check if they have a matching conditional reveal
1971
- * If they do, assign attributes.
1972
- **/
1973
2038
  nodeListForEach($inputs, function ($input) {
1974
- var controls = $input.getAttribute('data-aria-controls');
2039
+ var target = $input.getAttribute('data-aria-controls');
1975
2040
 
1976
- // Check if input controls anything
1977
- // Check if content exists, before setting attributes.
1978
- if (!controls || !$module.querySelector('#' + controls)) {
2041
+ // Skip radios without data-aria-controls attributes, or where the
2042
+ // target element does not exist.
2043
+ if (!target || !$module.querySelector('#' + target)) {
1979
2044
  return
1980
2045
  }
1981
2046
 
1982
- // If we have content that is controlled, set attributes.
1983
- $input.setAttribute('aria-controls', controls);
2047
+ // Promote the data-aria-controls attribute to a aria-controls attribute
2048
+ // so that the relationship is exposed in the AOM
2049
+ $input.setAttribute('aria-controls', target);
1984
2050
  $input.removeAttribute('data-aria-controls');
1985
- this.setAttributes($input);
1986
- }.bind(this));
2051
+ });
2052
+
2053
+ // When the page is restored after navigating 'back' in some browsers the
2054
+ // state of form controls is not restored until *after* the DOMContentLoaded
2055
+ // event is fired, so we need to sync after the pageshow event in browsers
2056
+ // that support it.
2057
+ if ('onpageshow' in window) {
2058
+ window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
2059
+ } else {
2060
+ window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
2061
+ }
2062
+
2063
+ // Although we've set up handlers to sync state on the pageshow or
2064
+ // DOMContentLoaded event, init could be called after those events have fired,
2065
+ // for example if they are added to the page dynamically, so sync now too.
2066
+ this.syncAllConditionalReveals();
1987
2067
 
1988
2068
  // Handle events
1989
2069
  $module.addEventListener('click', this.handleClick.bind(this));
1990
2070
  };
1991
2071
 
1992
- Radios.prototype.setAttributes = function ($input) {
1993
- var $content = document.querySelector('#' + $input.getAttribute('aria-controls'));
2072
+ /**
2073
+ * Sync the conditional reveal states for all inputs in this $module.
2074
+ */
2075
+ Radios.prototype.syncAllConditionalReveals = function () {
2076
+ nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
2077
+ };
1994
2078
 
1995
- if ($content && $content.classList.contains('govuk-radios__conditional')) {
2079
+ /**
2080
+ * Sync conditional reveal with the input state
2081
+ *
2082
+ * Synchronise the visibility of the conditional reveal, and its accessible
2083
+ * state, with the input's checked state.
2084
+ *
2085
+ * @param {HTMLInputElement} $input Radio input
2086
+ */
2087
+ Radios.prototype.syncConditionalRevealWithInputState = function ($input) {
2088
+ var $target = document.querySelector('#' + $input.getAttribute('aria-controls'));
2089
+
2090
+ if ($target && $target.classList.contains('govuk-radios__conditional')) {
1996
2091
  var inputIsChecked = $input.checked;
1997
2092
 
1998
2093
  $input.setAttribute('aria-expanded', inputIsChecked);
1999
-
2000
- $content.classList.toggle('govuk-radios__conditional--hidden', !inputIsChecked);
2094
+ $target.classList.toggle('govuk-radios__conditional--hidden', !inputIsChecked);
2001
2095
  }
2002
2096
  };
2003
2097
 
2098
+ /**
2099
+ * Click event handler
2100
+ *
2101
+ * Handle a click within the $module – if the click occurred on a radio, sync
2102
+ * the state of the conditional reveal for all radio buttons in the same form
2103
+ * with the same name (because checking one radio could have un-checked a radio
2104
+ * in another $module)
2105
+ *
2106
+ * @param {MouseEvent} event Click event
2107
+ */
2004
2108
  Radios.prototype.handleClick = function (event) {
2005
2109
  var $clickedInput = event.target;
2006
- // We only want to handle clicks for radio inputs
2110
+
2111
+ // Ignore clicks on things that aren't radio buttons
2007
2112
  if ($clickedInput.type !== 'radio') {
2008
2113
  return
2009
2114
  }
2010
- // Because checking one radio can uncheck a radio in another $module,
2011
- // we need to call set attributes on all radios in the same form, or document if they're not in a form.
2012
- //
2013
- // We also only want radios which have aria-controls, as they support conditional reveals.
2115
+
2116
+ // We only need to consider radios with conditional reveals, which will have
2117
+ // aria-controls attributes.
2014
2118
  var $allInputs = document.querySelectorAll('input[type="radio"][aria-controls]');
2119
+
2015
2120
  nodeListForEach($allInputs, function ($input) {
2016
- // Only inputs with the same form owner should change.
2017
2121
  var hasSameFormOwner = ($input.form === $clickedInput.form);
2018
-
2019
- // In radios, only radios with the same name will affect each other.
2020
2122
  var hasSameName = ($input.name === $clickedInput.name);
2123
+
2021
2124
  if (hasSameName && hasSameFormOwner) {
2022
- this.setAttributes($input);
2125
+ this.syncConditionalRevealWithInputState($input);
2023
2126
  }
2024
2127
  }.bind(this));
2025
2128
  };
@@ -1062,13 +1062,22 @@ CharacterCount.prototype.init = function () {
1062
1062
  // Remove hard limit if set
1063
1063
  $module.removeAttribute('maxlength');
1064
1064
 
1065
- // Bind event changes to the textarea
1066
- var boundChangeEvents = this.bindChangeEvents.bind(this);
1067
- boundChangeEvents();
1065
+ // When the page is restored after navigating 'back' in some browsers the
1066
+ // state of the character count is not restored until *after* the DOMContentLoaded
1067
+ // event is fired, so we need to sync after the pageshow event in browsers
1068
+ // that support it.
1069
+ if ('onpageshow' in window) {
1070
+ window.addEventListener('pageshow', this.sync.bind(this));
1071
+ } else {
1072
+ window.addEventListener('DOMContentLoaded', this.sync.bind(this));
1073
+ }
1074
+
1075
+ this.sync();
1076
+ };
1068
1077
 
1069
- // Update count message
1070
- var boundUpdateCountMessage = this.updateCountMessage.bind(this);
1071
- boundUpdateCountMessage();
1078
+ CharacterCount.prototype.sync = function () {
1079
+ this.bindChangeEvents();
1080
+ this.updateCountMessage();
1072
1081
  };
1073
1082
 
1074
1083
  // Read data attributes
@@ -1116,8 +1125,7 @@ CharacterCount.prototype.checkIfValueChanged = function () {
1116
1125
  if (!this.$textarea.oldValue) this.$textarea.oldValue = '';
1117
1126
  if (this.$textarea.value !== this.$textarea.oldValue) {
1118
1127
  this.$textarea.oldValue = this.$textarea.value;
1119
- var boundUpdateCountMessage = this.updateCountMessage.bind(this);
1120
- boundUpdateCountMessage();
1128
+ this.updateCountMessage();
1121
1129
  }
1122
1130
  };
1123
1131
 
@@ -88,6 +88,12 @@
88
88
  "required": false,
89
89
  "description": "HTML attributes (for example data attributes) to add to the textarea."
90
90
  },
91
+ {
92
+ "name": "spellcheck",
93
+ "type": "boolean",
94
+ "required": false,
95
+ "description": "Optional field to enable or disable the spellcheck attribute on the character count."
96
+ },
91
97
  {
92
98
  "name": "countMessage",
93
99
  "type": "object",
@@ -10,6 +10,7 @@
10
10
  name: params.name,
11
11
  describedBy: params.id + '-info',
12
12
  rows: params.rows,
13
+ spellcheck: params.spellcheck,
13
14
  value: params.value,
14
15
  formGroup: params.formGroup,
15
16
  classes: 'govuk-js-character-count' + (' govuk-textarea--error' if params.errorMessage) + (' ' + params.classes if params.classes),
@@ -92,12 +92,13 @@
92
92
  // rotated 45 degrees
93
93
  .govuk-checkboxes__label::after {
94
94
  content: "";
95
+ box-sizing: border-box;
95
96
 
96
97
  position: absolute;
97
98
  top: 11px;
98
99
  left: 9px;
99
- width: 18px;
100
- height: 7px;
100
+ width: 23px;
101
+ height: 12px;
101
102
 
102
103
  -webkit-transform: rotate(-45deg);
103
104
 
@@ -105,7 +106,7 @@
105
106
 
106
107
  transform: rotate(-45deg);
107
108
  border: solid;
108
- border-width: 0 0 $govuk-border-width $govuk-border-width;
109
+ border-width: 0 0 5px 5px;
109
110
  // Fix bug in IE11 caused by transform rotate (-45deg).
110
111
  // See: alphagov/govuk_elements/issues/518
111
112
  border-top-color: transparent;
@@ -238,8 +239,8 @@
238
239
  .govuk-checkboxes__label::after {
239
240
  top: 15px;
240
241
  left: 6px;
241
- width: 9px;
242
- height: 3.5px;
242
+ width: 12px;
243
+ height: 6.5px;
243
244
  border-width: 0 0 3px 3px;
244
245
  }
245
246
 
@@ -1033,44 +1033,91 @@ function Checkboxes ($module) {
1033
1033
  this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
1034
1034
  }
1035
1035
 
1036
+ /**
1037
+ * Initialise Checkboxes
1038
+ *
1039
+ * Checkboxes can be associated with a 'conditionally revealed' content block –
1040
+ * for example, a checkbox for 'Phone' could reveal an additional form field for
1041
+ * the user to enter their phone number.
1042
+ *
1043
+ * These associations are made using a `data-aria-controls` attribute, which is
1044
+ * promoted to an aria-controls attribute during initialisation.
1045
+ *
1046
+ * We also need to restore the state of any conditional reveals on the page (for
1047
+ * example if the user has navigated back), and set up event handlers to keep
1048
+ * the reveal in sync with the checkbox state.
1049
+ */
1036
1050
  Checkboxes.prototype.init = function () {
1037
1051
  var $module = this.$module;
1038
1052
  var $inputs = this.$inputs;
1039
1053
 
1040
- /**
1041
- * Loop over all items with [data-controls]
1042
- * Check if they have a matching conditional reveal
1043
- * If they do, assign attributes.
1044
- **/
1045
1054
  nodeListForEach($inputs, function ($input) {
1046
- var controls = $input.getAttribute('data-aria-controls');
1055
+ var target = $input.getAttribute('data-aria-controls');
1047
1056
 
1048
- // Check if input controls anything
1049
- // Check if content exists, before setting attributes.
1050
- if (!controls || !$module.querySelector('#' + controls)) {
1057
+ // Skip checkboxes without data-aria-controls attributes, or where the
1058
+ // target element does not exist.
1059
+ if (!target || !$module.querySelector('#' + target)) {
1051
1060
  return
1052
1061
  }
1053
1062
 
1054
- // If we have content that is controlled, set attributes.
1055
- $input.setAttribute('aria-controls', controls);
1063
+ // Promote the data-aria-controls attribute to a aria-controls attribute
1064
+ // so that the relationship is exposed in the AOM
1065
+ $input.setAttribute('aria-controls', target);
1056
1066
  $input.removeAttribute('data-aria-controls');
1057
- this.setAttributes($input);
1058
- }.bind(this));
1067
+ });
1068
+
1069
+ // When the page is restored after navigating 'back' in some browsers the
1070
+ // state of form controls is not restored until *after* the DOMContentLoaded
1071
+ // event is fired, so we need to sync after the pageshow event in browsers
1072
+ // that support it.
1073
+ if ('onpageshow' in window) {
1074
+ window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
1075
+ } else {
1076
+ window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
1077
+ }
1078
+
1079
+ // Although we've set up handlers to sync state on the pageshow or
1080
+ // DOMContentLoaded event, init could be called after those events have fired,
1081
+ // for example if they are added to the page dynamically, so sync now too.
1082
+ this.syncAllConditionalReveals();
1059
1083
 
1060
- // Handle events
1061
1084
  $module.addEventListener('click', this.handleClick.bind(this));
1062
1085
  };
1063
1086
 
1064
- Checkboxes.prototype.setAttributes = function ($input) {
1065
- var inputIsChecked = $input.checked;
1066
- $input.setAttribute('aria-expanded', inputIsChecked);
1087
+ /**
1088
+ * Sync the conditional reveal states for all inputs in this $module.
1089
+ */
1090
+ Checkboxes.prototype.syncAllConditionalReveals = function () {
1091
+ nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
1092
+ };
1093
+
1094
+ /**
1095
+ * Sync conditional reveal with the input state
1096
+ *
1097
+ * Synchronise the visibility of the conditional reveal, and its accessible
1098
+ * state, with the input's checked state.
1099
+ *
1100
+ * @param {HTMLInputElement} $input Checkbox input
1101
+ */
1102
+ Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
1103
+ var $target = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1067
1104
 
1068
- var $content = this.$module.querySelector('#' + $input.getAttribute('aria-controls'));
1069
- if ($content) {
1070
- $content.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1105
+ if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
1106
+ var inputIsChecked = $input.checked;
1107
+
1108
+ $input.setAttribute('aria-expanded', inputIsChecked);
1109
+ $target.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
1071
1110
  }
1072
1111
  };
1073
1112
 
1113
+ /**
1114
+ * Click event handler
1115
+ *
1116
+ * Handle a click within the $module – if the click occurred on a checkbox, sync
1117
+ * the state of any associated conditional reveal with the checkbox state.
1118
+ *
1119
+ * @param {MouseEvent} event Click event
1120
+ */
1074
1121
  Checkboxes.prototype.handleClick = function (event) {
1075
1122
  var $target = event.target;
1076
1123
 
@@ -1078,7 +1125,7 @@ Checkboxes.prototype.handleClick = function (event) {
1078
1125
  var isCheckbox = $target.getAttribute('type') === 'checkbox';
1079
1126
  var hasAriaControls = $target.getAttribute('aria-controls');
1080
1127
  if (isCheckbox && hasAriaControls) {
1081
- this.setAttributes($target);
1128
+ this.syncConditionalRevealWithInputState($target);
1082
1129
  }
1083
1130
  };
1084
1131
 
@@ -45,7 +45,7 @@
45
45
  "name": "autocomplete",
46
46
  "type": "string",
47
47
  "required": false,
48
- "description": "Attribute to [identify input purpose](https://www.w3.org/WAI/WCAG21/Understanding/identify-input-purpose.html), for instance \"postal-code\" or \"username\". See [autofill](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill) for full list of attributes that can be used."
48
+ "description": "Attribute to [identify input purpose](https://www.w3.org/WAI/WCAG21/Understanding/identify-input-purpose.html), for instance \"bday-day\". See [autofill](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill) for full list of attributes that can be used."
49
49
  },
50
50
  {
51
51
  "name": "pattern",
@@ -47,31 +47,4 @@
47
47
  box-shadow: inset 0 0 0 4px $govuk-input-border-colour;
48
48
  }
49
49
  }
50
-
51
- .govuk-file-upload--error {
52
- // As `upload--error` has border, it needs to have the same padding as
53
- // the standard focused element.
54
- margin-right: -$component-padding;
55
- margin-left: -$component-padding;
56
- padding-right: $component-padding;
57
- padding-left: $component-padding;
58
- border: $govuk-border-width-form-element-error solid $govuk-error-colour;
59
-
60
- &:focus {
61
- border-color: $govuk-input-border-colour;
62
- // Remove `box-shadow` inherited from `:focus` as `file-upload--error`
63
- // already has the thicker border.
64
- box-shadow: none;
65
- }
66
-
67
- // Repeat `:focus` styles to prevent error styles from being applied when
68
- // input button is pressed as this moves the focus to "within".
69
- // This can't be set together with `:focus` as all versions of IE fail
70
- // to recognise `focus-within` and don't set any styles from the block
71
- // when it's a selector.
72
- &:focus-within {
73
- border-color: $govuk-input-border-colour;
74
- box-shadow: none;
75
- }
76
- }
77
50
  }
@@ -210,20 +210,17 @@
210
210
  padding: 0;
211
211
  list-style: none;
212
212
  -webkit-column-gap: $govuk-gutter;
213
- -moz-column-gap: $govuk-gutter;
214
213
  column-gap: $govuk-gutter; // Support: Columns
215
214
  }
216
215
 
217
216
  @include govuk-media-query ($from: desktop) {
218
217
  .govuk-footer__list--columns-2 {
219
218
  -webkit-column-count: 2;
220
- -moz-column-count: 2;
221
219
  column-count: 2; // Support: Columns
222
220
  }
223
221
 
224
222
  .govuk-footer__list--columns-3 {
225
223
  -webkit-column-count: 3;
226
- -moz-column-count: 3;
227
224
  column-count: 3; // Support: Columns
228
225
  }
229
226
  }
@@ -58,7 +58,7 @@
58
58
  focusable="false"
59
59
  class="govuk-footer__licence-logo"
60
60
  xmlns="http://www.w3.org/2000/svg"
61
- viewbox="0 0 483.2 195.7"
61
+ viewBox="0 0 483.2 195.7"
62
62
  height="17"
63
63
  width="41"
64
64
  >
@@ -21,7 +21,7 @@
21
21
  focusable="false"
22
22
  class="govuk-header__logotype-crown"
23
23
  xmlns="http://www.w3.org/2000/svg"
24
- viewbox="0 0 132 97"
24
+ viewBox="0 0 132 97"
25
25
  height="30"
26
26
  width="36"
27
27
  >
@@ -16,7 +16,7 @@
16
16
  // is unlikely that the default or govuk-label--s class would be used in this
17
17
  // case.
18
18
 
19
- // This adjustment will not work in browsers that do not support :not().
19
+ // This adjustment will not work in browsers that do not support :not().
20
20
  // Users with these browsers will see the default size margin (5px larger).
21
21
 
22
22
  .govuk-label:not(.govuk-label--m):not(.govuk-label--l):not(.govuk-label--xl) + .govuk-hint {
@@ -30,7 +30,7 @@
30
30
  // it is unlikely that the default or govuk-fieldset__legend--s class would be
31
31
  // used in this case.
32
32
 
33
- // This adjustment will not work in browsers that do not support :not().
33
+ // This adjustment will not work in browsers that do not support :not().
34
34
  // Users with these browsers will see the default size margin (5px larger).
35
35
 
36
36
  .govuk-fieldset__legend:not(.govuk-fieldset__legend--m):not(.govuk-fieldset__legend--l):not(.govuk-fieldset__legend--xl) + .govuk-hint {
@@ -1,4 +1,4 @@
1
- <span {%- if params.id %} id="{{ params.id }}"{% endif %} class="govuk-hint {%- if params.classes %} {{ params.classes }}{% endif %}"
1
+ <div {%- if params.id %} id="{{ params.id }}"{% endif %} class="govuk-hint {%- if params.classes %} {{ params.classes }}{% endif %}"
2
2
  {%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}>
3
3
  {{ params.html | safe if params.html else params.text }}
4
- </span>
4
+ </div>
@@ -58,9 +58,6 @@
58
58
 
59
59
  &:focus {
60
60
  border-color: $govuk-input-border-colour;
61
- // Remove `box-shadow` inherited from `:focus` as `input--error`
62
- // already has the thicker border.
63
- box-shadow: none;
64
61
  }
65
62
  }
66
63
 
@@ -88,6 +88,12 @@
88
88
  "required": false,
89
89
  "description": "Attribute to [provide a regular expression pattern](https://www.w3.org/TR/html51/sec-forms.html#the-pattern-attribute), used to match allowed character combinations for the input value."
90
90
  },
91
+ {
92
+ "name": "spellcheck",
93
+ "type": "boolean",
94
+ "required": false,
95
+ "description": "Optional field to enable or disable the spellcheck attribute on the input."
96
+ },
91
97
  {
92
98
  "name": "attributes",
93
99
  "type": "object",
@@ -38,6 +38,7 @@
38
38
  }) | indent(2) | trim }}
39
39
  {% endif %}
40
40
  <input class="govuk-input {%- if params.classes %} {{ params.classes }}{% endif %} {%- if params.errorMessage %} govuk-input--error{% endif %}" id="{{ params.id }}" name="{{ params.name }}" type="{{ params.type | default('text') }}"
41
+ {%- if (params.spellcheck === false) or (params.spellcheck === true) %} spellcheck="{{ params.spellcheck }}"{% endif %}
41
42
  {%- if params.value %} value="{{ params.value}}"{% endif %}
42
43
  {%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
43
44
  {%- if params.autocomplete %} autocomplete="{{ params.autocomplete}}"{% endif %}
@@ -1030,67 +1030,115 @@ function nodeListForEach (nodes, callback) {
1030
1030
 
1031
1031
  function Radios ($module) {
1032
1032
  this.$module = $module;
1033
+ this.$inputs = $module.querySelectorAll('input[type="radio"]');
1033
1034
  }
1034
1035
 
1036
+ /**
1037
+ * Initialise Radios
1038
+ *
1039
+ * Radios can be associated with a 'conditionally revealed' content block – for
1040
+ * example, a radio for 'Phone' could reveal an additional form field for the
1041
+ * user to enter their phone number.
1042
+ *
1043
+ * These associations are made using a `data-aria-controls` attribute, which is
1044
+ * promoted to an aria-controls attribute during initialisation.
1045
+ *
1046
+ * We also need to restore the state of any conditional reveals on the page (for
1047
+ * example if the user has navigated back), and set up event handlers to keep
1048
+ * the reveal in sync with the radio state.
1049
+ */
1035
1050
  Radios.prototype.init = function () {
1036
1051
  var $module = this.$module;
1037
- var $inputs = $module.querySelectorAll('input[type="radio"]');
1052
+ var $inputs = this.$inputs;
1038
1053
 
1039
- /**
1040
- * Loop over all items with [data-controls]
1041
- * Check if they have a matching conditional reveal
1042
- * If they do, assign attributes.
1043
- **/
1044
1054
  nodeListForEach($inputs, function ($input) {
1045
- var controls = $input.getAttribute('data-aria-controls');
1055
+ var target = $input.getAttribute('data-aria-controls');
1046
1056
 
1047
- // Check if input controls anything
1048
- // Check if content exists, before setting attributes.
1049
- if (!controls || !$module.querySelector('#' + controls)) {
1057
+ // Skip radios without data-aria-controls attributes, or where the
1058
+ // target element does not exist.
1059
+ if (!target || !$module.querySelector('#' + target)) {
1050
1060
  return
1051
1061
  }
1052
1062
 
1053
- // If we have content that is controlled, set attributes.
1054
- $input.setAttribute('aria-controls', controls);
1063
+ // Promote the data-aria-controls attribute to a aria-controls attribute
1064
+ // so that the relationship is exposed in the AOM
1065
+ $input.setAttribute('aria-controls', target);
1055
1066
  $input.removeAttribute('data-aria-controls');
1056
- this.setAttributes($input);
1057
- }.bind(this));
1067
+ });
1068
+
1069
+ // When the page is restored after navigating 'back' in some browsers the
1070
+ // state of form controls is not restored until *after* the DOMContentLoaded
1071
+ // event is fired, so we need to sync after the pageshow event in browsers
1072
+ // that support it.
1073
+ if ('onpageshow' in window) {
1074
+ window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
1075
+ } else {
1076
+ window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
1077
+ }
1078
+
1079
+ // Although we've set up handlers to sync state on the pageshow or
1080
+ // DOMContentLoaded event, init could be called after those events have fired,
1081
+ // for example if they are added to the page dynamically, so sync now too.
1082
+ this.syncAllConditionalReveals();
1058
1083
 
1059
1084
  // Handle events
1060
1085
  $module.addEventListener('click', this.handleClick.bind(this));
1061
1086
  };
1062
1087
 
1063
- Radios.prototype.setAttributes = function ($input) {
1064
- var $content = document.querySelector('#' + $input.getAttribute('aria-controls'));
1088
+ /**
1089
+ * Sync the conditional reveal states for all inputs in this $module.
1090
+ */
1091
+ Radios.prototype.syncAllConditionalReveals = function () {
1092
+ nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
1093
+ };
1065
1094
 
1066
- if ($content && $content.classList.contains('govuk-radios__conditional')) {
1095
+ /**
1096
+ * Sync conditional reveal with the input state
1097
+ *
1098
+ * Synchronise the visibility of the conditional reveal, and its accessible
1099
+ * state, with the input's checked state.
1100
+ *
1101
+ * @param {HTMLInputElement} $input Radio input
1102
+ */
1103
+ Radios.prototype.syncConditionalRevealWithInputState = function ($input) {
1104
+ var $target = document.querySelector('#' + $input.getAttribute('aria-controls'));
1105
+
1106
+ if ($target && $target.classList.contains('govuk-radios__conditional')) {
1067
1107
  var inputIsChecked = $input.checked;
1068
1108
 
1069
1109
  $input.setAttribute('aria-expanded', inputIsChecked);
1070
-
1071
- $content.classList.toggle('govuk-radios__conditional--hidden', !inputIsChecked);
1110
+ $target.classList.toggle('govuk-radios__conditional--hidden', !inputIsChecked);
1072
1111
  }
1073
1112
  };
1074
1113
 
1114
+ /**
1115
+ * Click event handler
1116
+ *
1117
+ * Handle a click within the $module – if the click occurred on a radio, sync
1118
+ * the state of the conditional reveal for all radio buttons in the same form
1119
+ * with the same name (because checking one radio could have un-checked a radio
1120
+ * in another $module)
1121
+ *
1122
+ * @param {MouseEvent} event Click event
1123
+ */
1075
1124
  Radios.prototype.handleClick = function (event) {
1076
1125
  var $clickedInput = event.target;
1077
- // We only want to handle clicks for radio inputs
1126
+
1127
+ // Ignore clicks on things that aren't radio buttons
1078
1128
  if ($clickedInput.type !== 'radio') {
1079
1129
  return
1080
1130
  }
1081
- // Because checking one radio can uncheck a radio in another $module,
1082
- // we need to call set attributes on all radios in the same form, or document if they're not in a form.
1083
- //
1084
- // We also only want radios which have aria-controls, as they support conditional reveals.
1131
+
1132
+ // We only need to consider radios with conditional reveals, which will have
1133
+ // aria-controls attributes.
1085
1134
  var $allInputs = document.querySelectorAll('input[type="radio"][aria-controls]');
1135
+
1086
1136
  nodeListForEach($allInputs, function ($input) {
1087
- // Only inputs with the same form owner should change.
1088
1137
  var hasSameFormOwner = ($input.form === $clickedInput.form);
1089
-
1090
- // In radios, only radios with the same name will affect each other.
1091
1138
  var hasSameName = ($input.name === $clickedInput.name);
1139
+
1092
1140
  if (hasSameName && hasSameFormOwner) {
1093
- this.setAttributes($input);
1141
+ this.syncConditionalRevealWithInputState($input);
1094
1142
  }
1095
1143
  }.bind(this));
1096
1144
  };
@@ -44,9 +44,6 @@
44
44
 
45
45
  &:focus {
46
46
  border-color: $govuk-input-border-colour;
47
- // Remove `box-shadow` inherited from `:focus` as `select--error`
48
- // already has the thicker border.
49
- box-shadow: none;
50
47
  }
51
48
  }
52
49
 
@@ -21,6 +21,7 @@
21
21
 
22
22
  &:focus {
23
23
  outline: $govuk-focus-width solid $govuk-focus-colour;
24
+ outline-offset: 0;
24
25
  background-color: $govuk-focus-colour;
25
26
 
26
27
  // Undo unwanted changes when global styles are enabled
@@ -42,9 +42,6 @@
42
42
 
43
43
  &:focus {
44
44
  border-color: $govuk-input-border-colour;
45
- // Remove `box-shadow` inherited from `:focus` as `textarea--error`
46
- // already has the thicker border.
47
- box-shadow: none;
48
45
  }
49
46
 
50
47
  }
@@ -11,6 +11,12 @@
11
11
  "required": true,
12
12
  "description": "The name of the textarea, which is submitted with the form data."
13
13
  },
14
+ {
15
+ "name": "spellcheck",
16
+ "type": "boolean",
17
+ "required": false,
18
+ "description": "Optional field to enable or disable the spellcheck attribute on the textarea."
19
+ },
14
20
  {
15
21
  "name": "rows",
16
22
  "type": "string",
@@ -38,6 +38,7 @@
38
38
  }) | indent(2) | trim }}
39
39
  {% endif %}
40
40
  <textarea class="govuk-textarea {{- ' govuk-textarea--error' if params.errorMessage }} {{- ' ' + params.classes if params.classes}}" id="{{ params.id }}" name="{{ params.name }}" rows="{%if params.rows %} {{- params.rows -}} {% else %}5{%endif %}"
41
+ {%- if (params.spellcheck === false) or (params.spellcheck === true) %} spellcheck="{{ params.spellcheck }}"{% endif %}
41
42
  {%- if describedBy %} aria-describedby="{{ describedBy }}"{% endif %}
42
43
  {%- if params.autocomplete %} autocomplete="{{ params.autocomplete}}"{% endif %}
43
44
  {%- for attribute, value in params.attributes %} {{attribute}}="{{value}}"{% endfor %}>{{ params.value }}</textarea>
@@ -13,13 +13,15 @@
13
13
  .govuk-warning-text__icon {
14
14
  @include govuk-font($size: false, $weight: bold);
15
15
 
16
+ box-sizing: border-box;
17
+
16
18
  display: inline-block;
17
19
 
18
20
  position: absolute;
19
21
  left: 0;
20
22
 
21
- min-width: 29px;
22
- min-height: 29px;
23
+ min-width: 35px;
24
+ min-height: 35px;
23
25
  margin-top: -7px;
24
26
 
25
27
  @include govuk-media-query($from: tablet) {
@@ -52,7 +52,7 @@
52
52
  /// colour will be returned which meets contrast requirements . If you want to
53
53
  /// use the non-websafe version you can set this to `false` but your should
54
54
  /// ensure that you still meets contrast requirements for accessibility - for
55
- /// example, don't use the non-websafe version for text.
55
+ /// example, do not use the non-websafe version for text.
56
56
  ///
57
57
  /// @return {Colour} Representation of colour for organisation
58
58
  /// @throw if `$organisation` is not a known organisation
@@ -52,7 +52,7 @@ $govuk-print-text-colour: #000000 !default;
52
52
 
53
53
  /// Secondary text colour
54
54
  ///
55
- /// Used for 'muted' text, help text, etc.
55
+ /// Used in for example 'muted' text and help text.
56
56
  ///
57
57
  /// @type Colour
58
58
  /// @access public
@@ -90,7 +90,7 @@ $govuk-error-colour: govuk-colour("red") !default;
90
90
 
91
91
  /// Border colour
92
92
  ///
93
- /// Used for borders, separators, rules, keylines etc.
93
+ /// Used in for example borders, separators, rules and keylines.
94
94
  ///
95
95
  /// @type Colour
96
96
  /// @access public
@@ -70,7 +70,7 @@ $_govuk-colour-palette-modern: (
70
70
  "purple": #4c2c92,
71
71
 
72
72
  "black": #0b0c0c,
73
- "dark-grey": #626a6e,
73
+ "dark-grey": #505a5f,
74
74
  "mid-grey": #b1b4b6,
75
75
  "light-grey": #f3f2f1,
76
76
  "white": #ffffff,
@@ -81,8 +81,11 @@ $govuk-border-width-form-element: 2px !default;
81
81
  ///
82
82
  /// @type Number
83
83
  /// @access public
84
+ /// @deprecated Use $govuk-border-width-form-element instead. There should be no
85
+ /// difference in thickness for inputs in the error state, in order to
86
+ /// maintain a distinct focus state.
84
87
 
85
- $govuk-border-width-form-element-error: 4px !default;
88
+ $govuk-border-width-form-element-error: 2px !default;
86
89
 
87
90
  /// Form group border width when in error state
88
91
  ///
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "govuk-frontend",
3
3
  "description": "GOV.UK Frontend contains the code you need to start building a user interface for government platforms and services.",
4
- "version": "3.7.0",
4
+ "version": "3.8.0",
5
5
  "main": "govuk/all.js",
6
6
  "sass": "govuk/all.scss",
7
7
  "engines": {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_publishing_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 21.60.0
4
+ version: 21.60.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-24 00:00:00.000000000 Z
11
+ date: 2020-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_app_config