@ons/design-system 73.2.0 → 73.4.0

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 (31) hide show
  1. package/components/button/_button.scss +3 -6
  2. package/components/chart/annotations-options.js +15 -3
  3. package/components/chart/chart.js +27 -2
  4. package/components/chart/common-chart-options.js +10 -0
  5. package/components/chart/range-annotations-options.js +19 -3
  6. package/components/chart/reference-line-annotations-options.js +19 -0
  7. package/components/description-list/_description-list.scss +26 -0
  8. package/components/header/_header.scss +47 -4
  9. package/components/header/_macro.njk +168 -185
  10. package/components/header/_macro.spec.js +116 -14
  11. package/components/header/example-header-basic-with-search-no-heading.njk +22 -0
  12. package/components/header/header.spec.js +18 -1
  13. package/components/hero/example-hero-grey-with-last-updated.njk +27 -0
  14. package/components/navigation/navigation.js +20 -2
  15. package/components/navigation/navigation.spec.js +22 -0
  16. package/components/phase-banner/_macro.njk +10 -1
  17. package/components/phase-banner/_macro.spec.js +27 -0
  18. package/components/phase-banner/example-phase-banner-beta-with-feedback-link.njk +18 -0
  19. package/components/phase-banner/example-phase-banner-beta-without-feedback-link.njk +7 -0
  20. package/components/question/_question.scss +1 -0
  21. package/components/table/_macro.njk +6 -3
  22. package/components/table/_macro.spec.js +58 -0
  23. package/components/table/_table.scss +9 -1
  24. package/components/table/example-table-with-valign-property.njk +142 -0
  25. package/css/main.css +1 -1
  26. package/layout/_template.njk +1 -1
  27. package/package.json +1 -1
  28. package/scripts/main.es5.js +1 -1
  29. package/scripts/main.js +2 -2
  30. package/scss/utilities/_highlight.scss +6 -2
  31. package/scss/utilities/_typography.scss +4 -0
@@ -631,6 +631,7 @@ $button-shadow-size: 3px;
631
631
 
632
632
  &--menu {
633
633
  border-bottom: 4px solid rgb(0 0 0 / 0%);
634
+ height: 72px;
634
635
  .ons-icon {
635
636
  transform: rotate(90deg);
636
637
  }
@@ -644,12 +645,7 @@ $button-shadow-size: 3px;
644
645
  padding: 0;
645
646
  .ons-icon {
646
647
  fill: var(--ons-color-text-link);
647
- height: 1rem;
648
648
  margin-top: 0;
649
- width: 1rem;
650
- }
651
- .ons-btn__text {
652
- height: 24px;
653
649
  }
654
650
  }
655
651
  }
@@ -723,7 +719,8 @@ $button-shadow-size: 3px;
723
719
  &--close {
724
720
  align-items: center;
725
721
  display: flex;
726
- padding: 1.25rem 1rem 1rem;
722
+ height: 72px;
723
+ padding: 0 1.5rem;
727
724
  }
728
725
 
729
726
  &--search-icon &,
@@ -1,11 +1,20 @@
1
1
  import ChartConstants from './chart-constants';
2
2
 
3
3
  class AnnotationsOptions {
4
- constructor(annotations) {
4
+ constructor(annotations, xAxisCategories = []) {
5
5
  this.constants = ChartConstants.constants();
6
6
  this.annotations = annotations;
7
+ this.xAxisCategories = xAxisCategories;
7
8
  }
8
9
 
10
+ // Screen reader description that includes the annotated data point.
11
+ buildPointAccessibilityDescription = (annotation) => {
12
+ const x = annotation.point.x;
13
+ const y = annotation.point.y;
14
+ const xLabel = Number.isInteger(x) && this.xAxisCategories.length > x ? this.xAxisCategories[x] : x;
15
+ return `${annotation.text}. Related to ${xLabel}, ${y}`;
16
+ };
17
+
9
18
  getAnnotationsOptionsDesktop = () => {
10
19
  let annotations = [
11
20
  {
@@ -36,6 +45,9 @@ class AnnotationsOptions {
36
45
  text: annotation.text,
37
46
  x: annotation.labelOffsetX,
38
47
  y: annotation.labelOffsetY,
48
+ accessibility: {
49
+ description: this.buildPointAccessibilityDescription(annotation),
50
+ },
39
51
  });
40
52
  });
41
53
  return annotations;
@@ -66,9 +78,9 @@ class AnnotationsOptions {
66
78
  text: index + 1,
67
79
  x: 0,
68
80
  y: 0,
69
- // Ensures the full label is read out by screen readers
81
+ // Ensures the full label is read out by screen readers, including data point context
70
82
  accessibility: {
71
- description: annotation.text,
83
+ description: this.buildPointAccessibilityDescription(annotation),
72
84
  },
73
85
  });
74
86
  });
@@ -41,7 +41,7 @@ class HighchartsBaseChart {
41
41
  this.config = JSON.parse(this.node.querySelector(`[data-highcharts-config--${this.id}]`).textContent);
42
42
  if (this.node.querySelector(`[data-highcharts-annotations--${this.id}]`)) {
43
43
  this.annotations = JSON.parse(this.node.querySelector(`[data-highcharts-annotations--${this.id}]`).textContent);
44
- this.annotationsOptions = new AnnotationsOptions(this.annotations);
44
+ this.annotationsOptions = new AnnotationsOptions(this.annotations, this.config.xAxis?.categories ?? []);
45
45
  }
46
46
  if (this.node.querySelector(`[data-highcharts-range-annotations--${this.id}]`)) {
47
47
  this.rangeAnnotations = JSON.parse(this.node.querySelector(`[data-highcharts-range-annotations--${this.id}]`).textContent);
@@ -73,7 +73,6 @@ class HighchartsBaseChart {
73
73
  this.customReferenceLineValue = this.node.dataset.highchartsCustomReferenceLineValue
74
74
  ? parseFloat(this.node.dataset.highchartsCustomReferenceLineValue)
75
75
  : undefined;
76
-
77
76
  this.specificChartOptions = new SpecificChartOptions(
78
77
  this.theme,
79
78
  this.chartType,
@@ -98,6 +97,7 @@ class HighchartsBaseChart {
98
97
  }
99
98
  this.hideDataLabels = this.checkHideDataLabels();
100
99
  this.setSpecificChartOptions();
100
+ this.setAnnotationsAccessibilityDescription();
101
101
  this.setResponsiveOptions();
102
102
  this.setLoadEvent();
103
103
  this.setRenderEvent();
@@ -105,6 +105,25 @@ class HighchartsBaseChart {
105
105
  this.chart = Highcharts.chart(chartNode, this.config);
106
106
  }
107
107
 
108
+ // Making these annotation types accessible to screen readers. Point annotations are already
109
+ // handled by the Highcharts annotations module via {annotationsList}.
110
+ setAnnotationsAccessibilityDescription = () => {
111
+ const descriptions = [];
112
+ if (this.rangeAnnotationsOptions) {
113
+ descriptions.push(...this.rangeAnnotationsOptions.getAccessibilityDescriptions());
114
+ }
115
+ if (this.referenceLineAnnotationsOptions) {
116
+ descriptions.push(...this.referenceLineAnnotationsOptions.getAccessibilityDescriptions());
117
+ }
118
+ if (descriptions.length > 0) {
119
+ if (!this.config.accessibility) {
120
+ this.config.accessibility = {};
121
+ }
122
+ const listItems = descriptions.map((d) => `<li>${d}</li>`).join('');
123
+ this.config.accessibility.description = `<ul style="list-style-type: none">${listItems}</ul>`;
124
+ }
125
+ };
126
+
108
127
  // Check for the number of extra line series in the config
109
128
  checkForExtraLines = () => {
110
129
  return this.chartType === 'line' ? 0 : this.config.series.filter((series) => series.type === 'line').length;
@@ -319,6 +338,12 @@ class HighchartsBaseChart {
319
338
  }
320
339
  });
321
340
  }
341
+ // Clear the SVG <desc> element that Highcharts unconditionally
342
+ // injects with "Created with Highcharts x.x.x".
343
+ const svgDesc = currentChart.renderer.box.querySelector('desc');
344
+ if (svgDesc) {
345
+ svgDesc.textContent = '';
346
+ }
322
347
  currentChart.redraw(false);
323
348
  };
324
349
  };
@@ -55,6 +55,16 @@ class CommonChartOptions {
55
55
  // Remove Highcharts watermark
56
56
  enabled: false,
57
57
  },
58
+ lang: {
59
+ accessibility: {
60
+ // We use only the title so screen readers don't hear Highcharts interactive chart text.
61
+ chartContainerLabel: '{title}',
62
+ // The default "Interactive chart" creates a redundant landmark on
63
+ // the SVG element. The outer chart region already has the title
64
+ // label, so we clear this to avoid duplicate announcements.
65
+ svgContainerLabel: '',
66
+ },
67
+ },
58
68
  accessibility: {
59
69
  enabled: true,
60
70
  keyboardNavigation: {
@@ -6,6 +6,14 @@ class RangeAnnotationsOptions {
6
6
  this.rangeAnnotations = rangeAnnotations;
7
7
  }
8
8
 
9
+ buildRangeAccessibilityDescription = (rangeAnnotation) => {
10
+ const axisLabel = rangeAnnotation.axis === 'x' ? 'x-axis' : 'y-axis';
11
+ const fromValue = rangeAnnotation.range?.axisValue1;
12
+ const toValue = rangeAnnotation.range?.axisValue2;
13
+ const rangeText = fromValue !== undefined && toValue !== undefined ? `from ${fromValue} to ${toValue}` : 'for a range';
14
+ return `Range annotation on the ${axisLabel} ${rangeText}: ${rangeAnnotation.text}`;
15
+ };
16
+
9
17
  getRangeAnnotationsOptionsDesktop = (chartType) => {
10
18
  let xAxisPlotBands = [];
11
19
  let yAxisPlotBands = [];
@@ -25,6 +33,9 @@ class RangeAnnotationsOptions {
25
33
  ? `ons-chart__range-annotation-label ons-chart__range-annotation-label--${rangeAnnotation.axis}`
26
34
  : 'ons-chart__range-annotation-label--outside',
27
35
  allowOverlap: true,
36
+ accessibility: {
37
+ description: this.buildRangeAccessibilityDescription(rangeAnnotation),
38
+ },
28
39
  style: {
29
40
  color: this.constants.labelColor,
30
41
  fontSize: this.constants.defaultFontSize,
@@ -67,6 +78,9 @@ class RangeAnnotationsOptions {
67
78
  useHTML: true,
68
79
  className: 'ons-chart__annotations-footnotes-number',
69
80
  allowOverlap: true,
81
+ accessibility: {
82
+ description: this.buildRangeAccessibilityDescription(rangeAnnotation),
83
+ },
70
84
  style: {
71
85
  color: this.constants.labelColor,
72
86
  fontSize: this.constants.defaultFontSize,
@@ -204,9 +218,11 @@ class RangeAnnotationsOptions {
204
218
  });
205
219
  };
206
220
 
207
- // For bar and column charts, we want the range to
208
- // start and end flush with the edges of the columns / bars,
209
- // not halfway through as is the Highcharts default.
221
+ // Returns an array of plain-text descriptions for all range annotations.
222
+ getAccessibilityDescriptions = () => {
223
+ return this.rangeAnnotations.map((rangeAnnotation) => this.buildRangeAccessibilityDescription(rangeAnnotation));
224
+ };
225
+
210
226
  adjustRangeForCategoryAxis = (rangeAnnotation, chartType) => {
211
227
  let axisValue1 = rangeAnnotation.range.axisValue1;
212
228
  let axisValue2 = rangeAnnotation.range.axisValue2;
@@ -6,6 +6,19 @@ class ReferenceLineAnnotationsOptions {
6
6
  this.referenceLineAnnotations = referenceLineAnnotations;
7
7
  }
8
8
 
9
+ buildReferenceLineAccessibilityDescription = (referenceLineAnnotation) => {
10
+ const axisLabel = referenceLineAnnotation.axis === 'x' ? 'x-axis' : 'y-axis';
11
+ const valueText = referenceLineAnnotation.value !== undefined ? `at ${referenceLineAnnotation.value}` : 'at a value';
12
+ return `Reference line annotation on the ${axisLabel} ${valueText}: ${referenceLineAnnotation.text}`;
13
+ };
14
+
15
+ // Returns an array of plain-text descriptions for all reference line annotations
16
+ getAccessibilityDescriptions = () => {
17
+ return this.referenceLineAnnotations.map((referenceLineAnnotation) =>
18
+ this.buildReferenceLineAccessibilityDescription(referenceLineAnnotation),
19
+ );
20
+ };
21
+
9
22
  getReferenceLineAnnotationsOptionsDesktop = () => {
10
23
  let xAxisPlotLines = [];
11
24
  let yAxisPlotLines = [];
@@ -24,6 +37,9 @@ class ReferenceLineAnnotationsOptions {
24
37
  rotation: 0,
25
38
  align: 'left',
26
39
  textAlign: 'left',
40
+ accessibility: {
41
+ description: this.buildReferenceLineAccessibilityDescription(referenceLineAnnotation),
42
+ },
27
43
  },
28
44
  color: this.constants.zeroLineColor,
29
45
  // note this works to give a dashed line with 4px and a 4px gap, but
@@ -61,6 +77,9 @@ class ReferenceLineAnnotationsOptions {
61
77
  useHTML: true,
62
78
  className: 'ons-chart__annotations-footnotes-number',
63
79
  allowOverlap: true,
80
+ accessibility: {
81
+ description: this.buildReferenceLineAccessibilityDescription(referenceLineAnnotation),
82
+ },
64
83
  style: {
65
84
  color: this.constants.labelColor,
66
85
  fontSize: this.constants.defaultFontSize,
@@ -41,6 +41,32 @@
41
41
  }
42
42
 
43
43
  &--inline {
44
+ @include mq(600px, l, $to-operator: '<') {
45
+ .ons-description-list__item {
46
+ align-items: baseline;
47
+ column-gap: 0.25rem;
48
+ display: grid;
49
+ grid-template-columns: 10rem 1fr;
50
+ }
51
+
52
+ .ons-description-list__term,
53
+ .ons-description-list__value {
54
+ clear: none;
55
+ float: none;
56
+ max-width: none;
57
+ width: auto;
58
+ }
59
+
60
+ .ons-description-list__term {
61
+ grid-column: 1;
62
+ grid-row: 1;
63
+ }
64
+
65
+ .ons-description-list__value {
66
+ grid-column: 2;
67
+ }
68
+ }
69
+
44
70
  @include mq(l) {
45
71
  display: grid;
46
72
  grid-template-columns: repeat(3, 1fr);
@@ -246,6 +246,20 @@
246
246
  }
247
247
  }
248
248
 
249
+ &--basic {
250
+ .ons-header__links--language-section {
251
+ padding: 0 1.5rem !important;
252
+ }
253
+
254
+ .ons-header__language {
255
+ margin-bottom: 4px !important;
256
+ }
257
+
258
+ .ons-language-links__item {
259
+ margin: 0 !important;
260
+ }
261
+ }
262
+
249
263
  &--basic & {
250
264
  &__grid-top {
251
265
  padding: 0;
@@ -254,6 +268,8 @@
254
268
 
255
269
  #{$root}__menu-links {
256
270
  width: 100%;
271
+ display: flex;
272
+ align-items: center;
257
273
 
258
274
  #{$root}__language {
259
275
  display: block;
@@ -284,7 +300,8 @@
284
300
  position: relative;
285
301
  width: 100%;
286
302
 
287
- &__key-list {
303
+ &__key-list,
304
+ &__language {
288
305
  border-bottom: 1px solid var(--ons-color-ocean-blue);
289
306
  margin-bottom: 1.25rem;
290
307
  padding-bottom: 1rem;
@@ -298,6 +315,11 @@
298
315
  &__description {
299
316
  line-height: 1.714 !important;
300
317
  }
318
+ &__language {
319
+ .ons-language-links__item {
320
+ margin-left: 0 !important;
321
+ }
322
+ }
301
323
 
302
324
  &__groupItem-list:not(:last-of-type) {
303
325
  margin-bottom: 2rem !important;
@@ -312,6 +334,7 @@
312
334
  padding-bottom: 1rem;
313
335
  @include mq(m) {
314
336
  padding-bottom: 0;
337
+ padding-right: 2.5rem;
315
338
  }
316
339
  }
317
340
  }
@@ -325,15 +348,35 @@
325
348
  &-nav-search {
326
349
  background-color: var(--ons-color-branded-tint);
327
350
 
351
+ // Prevent flash of the non-JS, always-visible panels when JS is enabled.
352
+ // Panels are shown only when NavigationToggle sets aria-hidden="false".
353
+ body.ons-js-enabled .ons-header.ons-header--basic &.ons-js-nav-menu:not([aria-hidden='false']),
354
+ body.ons-js-enabled .ons-header.ons-header--basic &.ons-js-header-search:not([aria-hidden='false']) {
355
+ display: none !important;
356
+ }
357
+
358
+ body.ons-js-enabled .ons-header.ons-header--basic &.ons-js-nav-menu[aria-hidden='false'],
359
+ body.ons-js-enabled .ons-header.ons-header--basic &.ons-js-header-search[aria-hidden='false'] {
360
+ display: block !important;
361
+ }
362
+
328
363
  // updates styles when js is enabled
329
364
  .ons-js-enabled & {
330
- position: absolute;
331
- top: 100%;
365
+ position: relative;
332
366
  left: 50%;
333
367
  width: 100vw;
334
368
  transform: translateX(-50%);
335
369
  border-top: 0;
336
- z-index: 500;
370
+ padding-left: env(safe-area-inset-left);
371
+ padding-right: env(safe-area-inset-right);
372
+
373
+ // iOS Edge can render 100vw breakout panels under the Dynamic Island in landscape.
374
+ // Keep panels in normal flow on touch landscape viewports so body safe-area insets apply.
375
+ @media (orientation: landscape) and (hover: none) and (pointer: coarse) {
376
+ left: 0;
377
+ width: 100%;
378
+ transform: none;
379
+ }
337
380
  }
338
381
  }
339
382