@carbon/vue 3.0.27 → 3.0.30

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "framework": "vue",
3
3
  "name": "@carbon/vue",
4
- "version": "3.0.26",
4
+ "version": "3.0.29",
5
5
  "contributions": {
6
6
  "html": {
7
7
  "description-markup": "markdown",
@@ -770,6 +770,14 @@
770
770
  "type": "string"
771
771
  },
772
772
  "default": "'main'"
773
+ },
774
+ {
775
+ "name": "id",
776
+ "value": {
777
+ "kind": "expression",
778
+ "type": "string"
779
+ },
780
+ "default": "'main-content'"
773
781
  }
774
782
  ],
775
783
  "slots": [
@@ -2280,7 +2288,7 @@
2280
2288
  "kind": "expression",
2281
2289
  "type": "string"
2282
2290
  },
2283
- "default": "undefined"
2291
+ "default": "'menuitem'"
2284
2292
  }
2285
2293
  ],
2286
2294
  "slots": [
@@ -2320,6 +2328,14 @@
2320
2328
  "name": "CvHeaderNav",
2321
2329
  "description": "",
2322
2330
  "attributes": [
2331
+ {
2332
+ "name": "ariaLabel",
2333
+ "value": {
2334
+ "kind": "expression",
2335
+ "type": "string"
2336
+ },
2337
+ "default": "undefined"
2338
+ },
2323
2339
  {
2324
2340
  "name": "ariaLabelledBy",
2325
2341
  "value": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/vue",
3
3
  "license": "Apache-2.0",
4
- "version": "3.0.27",
4
+ "version": "3.0.30",
5
5
  "description": "A collection of components for the Carbon Design System built using Vue.js",
6
6
  "packageManager": "yarn@3.2.0",
7
7
  "main": "dist/carbon-vue-3.umd.min.js",
@@ -36,8 +36,9 @@
36
36
  `${carbonPrefix}--label`,
37
37
  { [`${carbonPrefix}--label--disabled`]: disabled },
38
38
  ]"
39
- >{{ label }}</label
40
39
  >
40
+ {{ label }}
41
+ </label>
41
42
 
42
43
  <div
43
44
  ref="listBox"
@@ -110,7 +111,10 @@
110
111
  ref="dropList"
111
112
  :aria-hidden="!open ? 'true' : 'false'"
112
113
  :aria-labelledby="`${uid}-label`"
113
- :class="`${carbonPrefix}--list-box__menu`"
114
+ :class="[
115
+ `${carbonPrefix}--list-box__menu`,
116
+ { 'cv-dropdown__menu--transition-done': transitionDone },
117
+ ]"
114
118
  role="menu"
115
119
  tabindex="-1"
116
120
  >
@@ -268,6 +272,7 @@ provide('dropdown-caption', dataCaption);
268
272
  const itemsList = ref([]);
269
273
  provide('dropdown-items', itemsList);
270
274
  const open = ref(false);
275
+ const transitionDone = ref(false);
271
276
  const data = reactive({
272
277
  isHelper: false,
273
278
  isInvalid: undefined,
@@ -301,6 +306,15 @@ watch(dataValue, () => {
301
306
  });
302
307
  watch(open, () => {
303
308
  focusItem.value = '';
309
+ if (open.value) {
310
+ // Wait for the transition to complete before allowing overflow
311
+ transitionDone.value = false;
312
+ setTimeout(() => {
313
+ transitionDone.value = true;
314
+ }, 200); // Increase delay to ensure animation completes
315
+ } else {
316
+ transitionDone.value = false;
317
+ }
304
318
  });
305
319
  const ariaLabeledBy = computed(() => {
306
320
  if (props.label) {
@@ -323,6 +337,13 @@ const wrapperStyleOverride = computed(() => {
323
337
  });
324
338
  const el = ref(null);
325
339
  function onClickOut() {
340
+ // Move focus to button if focus is inside dropdown list
341
+ const dropListEl = dropList.value;
342
+ if (dropListEl && dropListEl.contains(document.activeElement)) {
343
+ if (button.value && typeof button.value.focus === 'function') {
344
+ button.value.focus();
345
+ }
346
+ }
326
347
  open.value = false;
327
348
  }
328
349
  onClickOutside(el, onClickOut);
@@ -372,8 +393,14 @@ function onUp() {
372
393
  }
373
394
  }
374
395
  function onEsc() {
396
+ // Move focus to button if focus is inside dropdown list
397
+ const dropListEl = dropList.value;
398
+ if (dropListEl && dropListEl.contains(document.activeElement)) {
399
+ if (button.value && typeof button.value.focus === 'function') {
400
+ button.value.focus();
401
+ }
402
+ }
375
403
  open.value = false;
376
- doFocus();
377
404
  }
378
405
  const button = ref(null);
379
406
  const listBox = ref(null);
@@ -384,8 +411,10 @@ function onTab(ev) {
384
411
  open.value = false;
385
412
  } else if (ev.target === null || listBox.value?.contains(ev.target)) {
386
413
  // list has focus, close and return focus to dropdown
414
+ if (button.value && typeof button.value.focus === 'function') {
415
+ button.value.focus();
416
+ }
387
417
  open.value = false;
388
- doFocus();
389
418
  ev.preventDefault();
390
419
  }
391
420
  }
@@ -394,9 +423,6 @@ function onClick(ev) {
394
423
  if (props.disabled) {
395
424
  ev.preventDefault();
396
425
  } else {
397
- open.value = !open.value;
398
- doFocus(); // open or close (Some browsers do not focus on button when clicked)
399
-
400
426
  let target = ev.target;
401
427
  while (
402
428
  !target.classList.contains(`${carbonPrefix}--dropdown-link`) &&
@@ -408,7 +434,25 @@ function onClick(ev) {
408
434
  if (target.classList.contains(`${carbonPrefix}--dropdown-link`)) {
409
435
  const targetItemEl = target.parentNode;
410
436
  dataValue.value = targetItemEl.getAttribute('data-value');
437
+ // Move focus to button before closing
438
+ if (button.value && typeof button.value.focus === 'function') {
439
+ button.value.focus();
440
+ }
441
+ open.value = false;
442
+ } else {
443
+ open.value = !open.value;
444
+ doFocus(); // open or close (Some browsers do not focus on button when clicked)
411
445
  }
412
446
  }
413
447
  }
414
448
  </script>
449
+
450
+ <style lang="scss">
451
+ .bx--list-box .bx--list-box__menu {
452
+ overflow-y: hidden !important;
453
+ }
454
+
455
+ .bx--list-box--expanded .bx--list-box__menu.cv-dropdown__menu--transition-done {
456
+ overflow-y: auto !important;
457
+ }
458
+ </style>
@@ -229,7 +229,8 @@ function doFocus() {
229
229
  !(
230
230
  tryOn.classList.contains('cv-overflow-menu__before-content') ||
231
231
  tryOn.classList.contains('cv-overflow-menu__after-content') ||
232
- tryOn.tabindex < 0
232
+ tryOn.tabindex < 0 ||
233
+ tryOn.disabled
233
234
  )
234
235
  ) {
235
236
  focusOn = tryOn;
@@ -11,14 +11,16 @@
11
11
  },
12
12
  ]"
13
13
  role="navigation"
14
- v-bind="$attrs"
14
+ :aria-label="attrs['aria-label'] || 'Tabs'"
15
+ v-bind="attrsWithoutAriaLabel"
15
16
  @keydown.right.prevent.stop="onRight"
16
17
  @keydown.left.prevent.stop="onLeft"
17
18
  >
18
19
  <button
19
20
  ref="elLeftOverflow"
20
- aria-hidden="true"
21
- aria-label="scroll left"
21
+ :aria-label="
22
+ leftOverflowNavButtonHidden ? undefined : 'Scroll tabs left'
23
+ "
22
24
  :class="[
23
25
  {
24
26
  [`${carbonPrefix}--tab--overflow-nav-button`]: horizontalOverflow,
@@ -26,7 +28,7 @@
26
28
  leftOverflowNavButtonHidden,
27
29
  },
28
30
  ]"
29
- tabIndex="-1"
31
+ :tabIndex="leftOverflowNavButtonHidden ? -1 : 0"
30
32
  type="button"
31
33
  @click.stop.prevent="e => onOverflowClick(e, { direction: -1 })"
32
34
  @mousedown.stop.prevent="e => onOverflowMouseDown(e, { direction: -1 })"
@@ -89,8 +91,9 @@
89
91
  />
90
92
  <button
91
93
  ref="elRightOverflow"
92
- aria-hidden="true"
93
- aria-label="scroll right"
94
+ :aria-label="
95
+ rightOverflowNavButtonHidden ? undefined : 'Scroll tabs right'
96
+ "
94
97
  :class="[
95
98
  {
96
99
  [`${carbonPrefix}--tab--overflow-nav-button`]: horizontalOverflow,
@@ -98,7 +101,7 @@
98
101
  rightOverflowNavButtonHidden,
99
102
  },
100
103
  ]"
101
- tabIndex="-1"
104
+ :tabIndex="rightOverflowNavButtonHidden ? -1 : 0"
102
105
  type="button"
103
106
  @click="e => onOverflowClick(e, { direction: 1 })"
104
107
  @mousedown="e => onOverflowMouseDown(e, { direction: 1 })"
@@ -117,15 +120,21 @@
117
120
  import { carbonPrefix } from '../../global/settings';
118
121
  import { ChevronLeft16, ChevronRight16 } from '@carbon/icons-vue';
119
122
  import {
123
+ computed,
120
124
  nextTick,
121
125
  onBeforeUnmount,
122
126
  onMounted,
123
127
  onUpdated,
124
128
  provide,
125
129
  ref,
130
+ useAttrs,
126
131
  watch,
127
132
  } from 'vue';
128
133
 
134
+ defineOptions({
135
+ inheritAttrs: false,
136
+ });
137
+
129
138
  const OVERFLOW_BUTTON_OFFSET = 40;
130
139
 
131
140
  const props = defineProps({
@@ -152,6 +161,12 @@ const props = defineProps({
152
161
  });
153
162
  const emit = defineEmits(['tab-selected', 'tab-selected-id']);
154
163
 
164
+ const attrs = useAttrs();
165
+ const attrsWithoutAriaLabel = computed(() => {
166
+ const { 'aria-label': _, ...rest } = attrs;
167
+ return rest;
168
+ });
169
+
155
170
  const selectedId = ref('');
156
171
  provide('tab-selected', selectedId);
157
172
  /**
@@ -1,8 +1,8 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`CvTabs CvTabs - test slotted tab buttons to match snapshot 1`] = `
4
- <div class="cv-tabs ABC-class-123" style="width: 100%;" aria-label="ABC-aria-label-123">
5
- <div data-tabs="" class="cv-tab bx--tabs--scrollable ABC-class-123" role="navigation" aria-label="ABC-aria-label-123"><button aria-hidden="true" aria-label="scroll left" class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
4
+ <div class="cv-tabs" style="width: 100%;">
5
+ <div data-tabs="" class="cv-tab bx--tabs--scrollable ABC-class-123" role="navigation" aria-label="ABC-aria-label-123"><button class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
6
6
  <path d="M5 8L10 3 10.7 3.7 6.4 8 10.7 12.3 10 13z"></path>
7
7
  </svg></button>
8
8
  <!--v-if-->
@@ -11,7 +11,7 @@ exports[`CvTabs CvTabs - test slotted tab buttons to match snapshot 1`] = `
11
11
  <li class="cv-tabs-button bx--tabs--scrollable__nav-item" role="presentation"><button id="tab-2-link" class="bx--tabs--scrollable__nav-link" role="tab" aria-controls="tab-2" aria-selected="false" tabindex="-1" type="button">Origin<strong style="color: orange;">(!)</strong></button></li>
12
12
  <li class="cv-tabs-button bx--tabs--scrollable__nav-item" role="presentation"><button id="tab-3-link" class="bx--tabs--scrollable__nav-link" role="tab" aria-controls="tab-3" aria-selected="false" tabindex="-1" type="button">Tab 3 label</button></li>
13
13
  </ul>
14
- <!--v-if--><button aria-hidden="true" aria-label="scroll right" class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
14
+ <!--v-if--><button class="bx--tab--overflow-nav-button--hidden" tabindex="-1" type="button"><svg focusable="false" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
15
15
  <path d="M11 8L6 13 5.3 12.3 9.6 8 5.3 3.7 6 3z"></path>
16
16
  </svg></button>
17
17
  </div>
@@ -2,11 +2,17 @@
2
2
  import { carbonPrefix } from '../../global/settings';
3
3
  defineProps({
4
4
  tagType: { type: String, default: 'main' },
5
+ id: { type: String, default: 'main-content' },
5
6
  });
6
7
  </script>
7
8
 
8
9
  <template>
9
- <component :is="tagType" :class="`${carbonPrefix}--content`">
10
+ <component
11
+ :is="tagType"
12
+ :id="id"
13
+ :class="`${carbonPrefix}--content`"
14
+ tabindex="-1"
15
+ >
10
16
  <slot />
11
17
  </component>
12
18
  </template>
@@ -2,6 +2,7 @@
2
2
  <li
3
3
  ref="el"
4
4
  :class="`cv-header-menu ${carbonPrefix}--header__submenu`"
5
+ role="menuitem"
5
6
  @mouseenter="doHoverToggle(true)"
6
7
  @mouseleave="doHoverToggle(false)"
7
8
  >
@@ -10,9 +11,9 @@
10
11
  :aria-expanded="data.expanded ? 'true' : 'false'"
11
12
  :class="`${carbonPrefix}--header__menu-item ${carbonPrefix}--header__menu-title`"
12
13
  href="javascript:void(0)"
13
- role="menuitem"
14
14
  tabindex="0"
15
15
  :aria-label="$attrs.ariaLabel"
16
+ role="button"
16
17
  @click="doToggle"
17
18
  @keydown.space.prevent
18
19
  @keyup.space.prevent="doToggle"
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <li class="cv-header-menu-item" :role="role">
2
+ <li class="cv-header-menu-item" role="none">
3
3
  <component
4
4
  :is="tagType"
5
5
  v-bind="{ ...$attrs, ...linkProps }"
@@ -7,8 +7,9 @@
7
7
  `${carbonPrefix}--header__menu-item`,
8
8
  { [`${carbonPrefix}--header__menu-item--current`]: activePage },
9
9
  ]"
10
- role="menuitem"
11
10
  :aria-current="ariaCurrent"
11
+ :role="role"
12
+ :tabindex="tabindex"
12
13
  >
13
14
  <span :class="`${carbonPrefix}--text-truncate--end`">
14
15
  <slot />
@@ -25,7 +26,7 @@ import { computed } from 'vue';
25
26
  const props = defineProps({
26
27
  active: Boolean,
27
28
  ariaCurrent: { type: String, default: undefined },
28
- role: { type: String, default: undefined },
29
+ role: { type: String, default: 'menuitem' },
29
30
  ...propsLink,
30
31
  });
31
32
 
@@ -35,4 +36,8 @@ const linkProps = useLinkProps(props);
35
36
  const activePage = computed(() => {
36
37
  return props.active && props.ariaCurrent !== 'page';
37
38
  });
39
+
40
+ const tabindex = computed(() => {
41
+ return props.role === 'menuitem' ? '0' : undefined;
42
+ });
38
43
  </script>
@@ -3,16 +3,25 @@
3
3
  <ul
4
4
  :class="`${carbonPrefix}--header__menu-bar`"
5
5
  role="menubar"
6
- :ariaLabeldBy="ariaLabelledBy"
6
+ aria-orientation="horizontal"
7
+ :aria-label="ariaLabel || $attrs['aria-label']"
8
+ :aria-labelledby="ariaLabelledBy || $attrs['aria-labelledby']"
7
9
  >
8
10
  <slot />
9
11
  </ul>
10
12
  </nav>
11
13
  </template>
12
14
 
15
+ <script>
16
+ export default {
17
+ inheritAttrs: false,
18
+ };
19
+ </script>
20
+
13
21
  <script setup>
14
22
  import { carbonPrefix } from '../../global/settings';
15
23
  defineProps({
24
+ ariaLabel: { type: String, default: undefined },
16
25
  ariaLabelledBy: { type: String, default: undefined },
17
26
  });
18
27
  </script>
@@ -8,6 +8,7 @@
8
8
  { [`${carbonPrefix}--header-panel--expanded`]: panelExpanded },
9
9
  ]"
10
10
  :aria-hidden="!panelExpanded ? 'true' : 'false'"
11
+ :inert="!panelExpanded"
11
12
  @focusout="onFocusout"
12
13
  @mousedown="onMouseDown"
13
14
  >
@@ -20,6 +21,7 @@ import { carbonPrefix } from '../../global/settings';
20
21
  import {
21
22
  computed,
22
23
  inject,
24
+ nextTick,
23
25
  onBeforeUnmount,
24
26
  onMounted,
25
27
  reactive,
@@ -99,9 +101,49 @@ watch(
99
101
  }
100
102
  );
101
103
 
104
+ function manageFocusableElements(isExpanded) {
105
+ if (!el.value) return;
106
+
107
+ const focusableElements = el.value.querySelectorAll(
108
+ 'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
109
+ );
110
+
111
+ focusableElements.forEach(element => {
112
+ if (isExpanded) {
113
+ // Restore original tabindex when expanded
114
+ const originalTabindex = element.getAttribute('data-original-tabindex');
115
+ if (originalTabindex !== null) {
116
+ if (originalTabindex === 'null') {
117
+ element.removeAttribute('tabindex');
118
+ } else {
119
+ element.setAttribute('tabindex', originalTabindex);
120
+ }
121
+ element.removeAttribute('data-original-tabindex');
122
+ }
123
+ } else {
124
+ // Store original tabindex and set to -1 when collapsed
125
+ const currentTabindex = element.getAttribute('tabindex');
126
+ element.setAttribute('data-original-tabindex', currentTabindex || 'null');
127
+ element.setAttribute('tabindex', '-1');
128
+ }
129
+ });
130
+ }
131
+
102
132
  const emit = defineEmits(['update:expanded', 'panel-resize']);
103
133
  watch(panelExpanded, current => {
104
134
  emit('update:expanded', current);
105
135
  emit('panel-resize', { id: props.id, expanded: current });
136
+
137
+ // Manage focusable elements
138
+ nextTick(() => {
139
+ manageFocusableElements(current);
140
+ });
141
+ });
142
+
143
+ // Initial setup of focusable elements
144
+ onMounted(() => {
145
+ nextTick(() => {
146
+ manageFocusableElements(panelExpanded.value);
147
+ });
106
148
  });
107
149
  </script>
@@ -14,6 +14,7 @@
14
14
  },
15
15
  ]"
16
16
  :aria-hidden="!panelExpanded && !fixed ? 'true' : 'false'"
17
+ :inert="!panelExpanded && !fixed"
17
18
  @focusout="onFocusout"
18
19
  @mousedown="onMouseDown"
19
20
  @mouseenter="onHoverToggle(true)"
@@ -33,6 +34,7 @@
33
34
  import {
34
35
  computed,
35
36
  inject,
37
+ nextTick,
36
38
  onBeforeUnmount,
37
39
  onMounted,
38
40
  reactive,
@@ -110,6 +112,35 @@ watch(
110
112
  panelExpanded.value = props.expanded;
111
113
  }
112
114
  );
115
+ // Manage focusable elements when panel visibility changes
116
+ function manageFocusableElements(isExpanded, isFixed) {
117
+ if (!el.value || isFixed) return;
118
+
119
+ const focusableElements = el.value.querySelectorAll(
120
+ 'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
121
+ );
122
+
123
+ focusableElements.forEach(element => {
124
+ if (isExpanded) {
125
+ // Restore original tabindex when expanded
126
+ const originalTabindex = element.getAttribute('data-original-tabindex');
127
+ if (originalTabindex !== null) {
128
+ if (originalTabindex === 'null') {
129
+ element.removeAttribute('tabindex');
130
+ } else {
131
+ element.setAttribute('tabindex', originalTabindex);
132
+ }
133
+ element.removeAttribute('data-original-tabindex');
134
+ }
135
+ } else {
136
+ // Store original tabindex and set to -1 when collapsed
137
+ const currentTabindex = element.getAttribute('tabindex');
138
+ element.setAttribute('data-original-tabindex', currentTabindex || 'null');
139
+ element.setAttribute('tabindex', '-1');
140
+ }
141
+ });
142
+ }
143
+
113
144
  const emit = defineEmits(['update:expanded', 'panel-resize']);
114
145
  const isPanelExpanded = computed(
115
146
  () => panelExpanded.value || expandedViaHoverState.value
@@ -117,6 +148,11 @@ const isPanelExpanded = computed(
117
148
  watch(isPanelExpanded, current => {
118
149
  emit('update:expanded', current);
119
150
  emit('panel-resize', { id: props.id, expanded: current });
151
+
152
+ // Manage focusable elements for non-fixed nav
153
+ nextTick(() => {
154
+ manageFocusableElements(current, props.fixed);
155
+ });
120
156
  });
121
157
 
122
158
  const isChildOfHeader = computed(() => {
@@ -19,6 +19,7 @@ import CvSideNavLink from './CvSideNavLink.vue';
19
19
  import CvSideNavMenu from './CvSideNavMenu.vue';
20
20
  import CvSideNavMenuDivider from './CvSideNavMenuDivider.vue';
21
21
  import CvSideNavMenuItem from './CvSideNavMenuItem.vue';
22
+ import CvContent from './CvContent.vue';
22
23
  import {
23
24
  Notification20,
24
25
  UserAvatar20,
@@ -55,6 +56,7 @@ export const Template = args => ({
55
56
  CvSideNavMenu,
56
57
  CvSideNavMenuDivider,
57
58
  CvSideNavMenuItem,
59
+ CvContent,
58
60
  Notification20,
59
61
  UserAvatar20,
60
62
  Login20,
@@ -89,22 +91,12 @@ const defaultTemplate = `
89
91
  [Platform]
90
92
  </cv-header-name>
91
93
  <cv-header-nav aria-label="Carbon nav">
92
- <cv-header-menu-item href="javascript:void(0)">
93
- Link 1
94
- </cv-header-menu-item>
95
- <cv-header-menu-item href="javascript:void(0)">
96
- Link 2
97
- </cv-header-menu-item>
94
+ <cv-header-menu-item href="javascript:void(0)">Link 1</cv-header-menu-item>
95
+ <cv-header-menu-item href="javascript:void(0)">Link 2</cv-header-menu-item>
98
96
  <cv-header-menu aria-label="Link 3" title="Link 3">
99
- <cv-header-menu-item href="javascript:void(0)">
100
- Submenu Link 1
101
- </cv-header-menu-item>
102
- <cv-header-menu-item href="javascript:void(0)">
103
- Submenu Link 2
104
- </cv-header-menu-item>
105
- <cv-header-menu-item href="javascript:void(0)">
106
- Submenu Link 3
107
- </cv-header-menu-item>
97
+ <cv-header-menu-item href="javascript:void(0)">Submenu Link 1</cv-header-menu-item>
98
+ <cv-header-menu-item href="javascript:void(0)">Submenu Link 2</cv-header-menu-item>
99
+ <cv-header-menu-item href="javascript:void(0)">Submenu Link 3</cv-header-menu-item>
108
100
  </cv-header-menu>
109
101
  </cv-header-nav>
110
102
  <template v-slot:header-global>
@@ -162,6 +154,10 @@ const defaultTemplate = `
162
154
  </cv-header-panel>
163
155
  </template>
164
156
  </cv-header>
157
+ <cv-content>
158
+ <h1>Main Content</h1>
159
+ <p>This is the main content area that the skip link targets.</p>
160
+ </cv-content>
165
161
  `;
166
162
  const railTemplate = `
167
163
  <cv-header aria-label="Carbon header">
@@ -196,6 +192,10 @@ const railTemplate = `
196
192
  </cv-side-nav>
197
193
  </template>
198
194
  </cv-header>
195
+ <cv-content>
196
+ <h1>Main Content</h1>
197
+ <p>This is the main content area that the skip link targets.</p>
198
+ </cv-content>
199
199
  `;
200
200
  const fixedSideNavTemplate = `
201
201
  <cv-header aria-label="Carbon header">
@@ -231,6 +231,10 @@ const fixedSideNavTemplate = `
231
231
  </cv-side-nav-link>
232
232
  </cv-side-nav-items>
233
233
  </cv-side-nav>
234
+ <cv-content>
235
+ <h1>Main Content</h1>
236
+ <p>This is the main content area that the skip link targets.</p>
237
+ </cv-content>
234
238
  `;
235
239
 
236
240
  # UI Shell components