srcdev-nuxt-components 9.5.6 → 9.6.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 (113) hide show
  1. package/.claude/skills/component-compliance-checklist.md +6 -2
  2. package/.claude/skills/components/breadcrumb.md +5 -0
  3. package/.claude/skills/components/display-pill.md +2 -0
  4. package/.claude/skills/components/input-button.md +3 -0
  5. package/.claude/skills/components/navigation-items.md +1 -0
  6. package/.claude/skills/components/opening-hours.md +12 -0
  7. package/.claude/skills/components/price-list.md +10 -1
  8. package/.claude/skills/components/responsive-header.md +74 -0
  9. package/.claude/skills/components/service-detail.md +32 -0
  10. package/.claude/skills/components/service-summary.md +8 -0
  11. package/.claude/skills/components/services-card-grid.md +5 -3
  12. package/.claude/skills/components/services-card.md +22 -12
  13. package/.claude/skills/components/site-header.md +2 -0
  14. package/.claude/skills/components/slider-gallery.md +10 -4
  15. package/.claude/skills/components/tab-navigation.md +25 -9
  16. package/.claude/skills/index.md +1 -2
  17. package/.claude/skills/storybook-add-story.md +14 -0
  18. package/.vscode/srcdev-component-responsive-header.code-snippets +13 -1
  19. package/.vscode/srcdev-component-service-detail.code-snippets +1 -0
  20. package/.vscode/srcdev-component-service-summary.code-snippets +3 -1
  21. package/.vscode/srcdev-component-services-card.code-snippets +38 -36
  22. package/.vscode/srcdev-component-services-grids.code-snippets +2 -1
  23. package/.vscode/srcdev-component-site-header.code-snippets +3 -1
  24. package/.vscode/srcdev-component-tab-navigation.code-snippets +1 -1
  25. package/app/components/01.atoms/display-pill/CONSUMER-STYLING.md +3 -0
  26. package/app/components/01.atoms/display-pill/DisplayPill.vue +5 -2
  27. package/app/components/01.atoms/navigation/breadcrumb/Breadcrumb.vue +16 -4
  28. package/app/components/01.atoms/navigation/breadcrumb/CONSUMER-STYLING.md +28 -5
  29. package/app/components/01.atoms/navigation/breadcrumb/stories/Breadcrumb.stories.ts +94 -0
  30. package/app/components/01.atoms/navigation/breadcrumb/tests/Breadcrumb.spec.ts +27 -0
  31. package/app/components/02.molecules/navigation/tab-navigation/CONSUMER-STYLING.md +52 -11
  32. package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +55 -23
  33. package/app/components/02.molecules/navigation/tab-navigation/stories/TabNavigation.stories.ts +176 -257
  34. package/app/components/02.molecules/navigation/tab-navigation/tests/TabNavigation.spec.ts +42 -2
  35. package/app/components/02.molecules/opening-hours/CONSUMER-STYLING.md +11 -1
  36. package/app/components/02.molecules/opening-hours/OpeningHours.vue +71 -43
  37. package/app/components/02.molecules/opening-hours/stories/OpeningHours.stories.ts +138 -6
  38. package/app/components/02.molecules/opening-hours/tests/OpeningHours.spec.ts +53 -0
  39. package/app/components/02.molecules/opening-hours/tests/__snapshots__/OpeningHours.spec.ts.snap +1 -1
  40. package/app/components/02.molecules/opening-hours/tests/buildOpeningHoursSpecification.spec.ts +37 -1
  41. package/app/components/02.molecules/price-list/CONSUMER-STYLING.md +17 -5
  42. package/app/components/02.molecules/price-list/PriceList.vue +32 -9
  43. package/app/components/02.molecules/price-list/stories/PriceList.stories.ts +125 -29
  44. package/app/components/02.molecules/price-list/tests/PriceList.spec.ts +20 -0
  45. package/app/components/03.organisms/google-reviews/stories/GoogleReviews.stories.ts +9 -4
  46. package/app/components/03.organisms/image-galleries/slider-gallery/CONSUMER-STYLING.md +19 -1
  47. package/app/components/03.organisms/image-galleries/slider-gallery/SliderGallery.vue +65 -7
  48. package/app/components/03.organisms/image-galleries/slider-gallery/stories/SliderGallery.stories.ts +201 -63
  49. package/app/components/03.organisms/image-galleries/slider-gallery/tests/SliderGallery.spec.ts +41 -2
  50. package/app/components/03.organisms/image-galleries/slider-gallery/tests/__snapshots__/SliderGallery.spec.ts.snap +6 -6
  51. package/app/components/03.organisms/responsive-header/CONSUMER-STYLING.md +18 -0
  52. package/app/components/03.organisms/responsive-header/NavigationItems.vue +56 -18
  53. package/app/components/03.organisms/responsive-header/ResponsiveHeader.vue +106 -31
  54. package/app/components/03.organisms/responsive-header/stories/ResponsiveHeader.stories.ts +201 -18
  55. package/app/components/03.organisms/responsive-header/tests/.nuxt/app.config.mjs +21 -0
  56. package/app/components/03.organisms/responsive-header/tests/.nuxt/components.d.ts +66 -0
  57. package/app/components/03.organisms/responsive-header/tests/.nuxt/imports.d.ts +30 -0
  58. package/app/components/03.organisms/responsive-header/tests/.nuxt/manifest/meta/test.json +1 -0
  59. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/app.config.d.ts +35 -0
  60. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/build.d.ts +23 -0
  61. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/builder-env.d.ts +1 -0
  62. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/components.d.ts +71 -0
  63. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/imports.d.ts +386 -0
  64. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/middleware.d.ts +11 -0
  65. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/modules.d.ts +79 -0
  66. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-layouts.d.ts +17 -0
  67. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-nuxt.d.ts +64 -0
  68. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/plugins.d.ts +29 -0
  69. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/runtime-config.d.ts +32 -0
  70. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/shared-imports.d.ts +10 -0
  71. package/app/components/03.organisms/responsive-header/tests/.nuxt/types/vue-shim.d.ts +0 -0
  72. package/app/components/03.organisms/responsive-header/tests/NavigationItems.spec.ts +36 -0
  73. package/app/components/03.organisms/responsive-header/tests/ResponsiveHeader.spec.ts +131 -6
  74. package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +9 -9
  75. package/app/components/03.organisms/responsive-header/tests/node_modules/.cache/nuxt/chrome-workspace.json +1 -0
  76. package/app/components/03.organisms/services/service-detail/CONSUMER-STYLING.md +92 -41
  77. package/app/components/03.organisms/services/service-detail/ServiceDetail.vue +198 -73
  78. package/app/components/03.organisms/services/service-detail/stories/ServiceDetail.stories.ts +157 -9
  79. package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +58 -0
  80. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +4 -3
  81. package/app/components/03.organisms/services/service-summary/CONSUMER-STYLING.md +30 -0
  82. package/app/components/03.organisms/services/service-summary/ServiceSummary.vue +32 -8
  83. package/app/components/03.organisms/services/service-summary/stories/ServiceSummary.stories.ts +147 -0
  84. package/app/components/03.organisms/services/service-summary/tests/ServiceSummary.spec.ts +26 -0
  85. package/app/components/03.organisms/services/service-summary/tests/__snapshots__/ServiceSummary.spec.ts.snap +1 -1
  86. package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +104 -63
  87. package/app/components/03.organisms/services/services-card/ServicesCard.vue +92 -66
  88. package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +194 -27
  89. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +34 -8
  90. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +8 -8
  91. package/app/components/03.organisms/services/services-grids/CONSUMER-STYLING.md +9 -0
  92. package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +2 -1
  93. package/app/components/03.organisms/services/services-grids/stories/ServiceSummaryGrid.stories.ts +46 -6
  94. package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +89 -11
  95. package/app/components/03.organisms/services/services-grids/tests/ServicesCardGrid.spec.ts +8 -0
  96. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServiceSummaryGrid.spec.ts.snap +3 -3
  97. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +24 -24
  98. package/app/components/03.organisms/site-header/SiteHeader.vue +8 -0
  99. package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +1 -1
  100. package/app/components/05.forms/input-button/CONSUMER-STYLING.md +8 -0
  101. package/app/components/05.forms/input-button/InputButton.vue +3 -1
  102. package/app/composables/tests/useAnchorScroll.spec.ts +8 -0
  103. package/app/composables/useAnchorScroll.ts +1 -1
  104. package/app/utils/opening-hours.ts +23 -2
  105. package/nuxt.config.ts +1 -1
  106. package/package.json +1 -1
  107. package/.claude/skills/components/site-navigation.md +0 -145
  108. package/.vscode/srcdev-component-site-navigation.code-snippets +0 -38
  109. package/app/components/02.molecules/navigation/site-navigation/CONSUMER-STYLING.md +0 -109
  110. package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +0 -751
  111. package/app/components/02.molecules/navigation/site-navigation/stories/SiteNavigation.stories.ts +0 -356
  112. package/app/components/02.molecules/navigation/site-navigation/tests/SiteNavigation.spec.ts +0 -367
  113. package/app/components/02.molecules/navigation/site-navigation/tests/__snapshots__/SiteNavigation.spec.ts.snap +0 -30
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <nav
3
+ v-if="visibleItems.length"
3
4
  ref="navRef"
4
5
  class="tab-navigation"
5
6
  :class="[
@@ -11,8 +12,8 @@
11
12
  >
12
13
  <ul v-if="!isCollapsed || !isLoaded" ref="navListRef" class="tab-nav-list" @mouseleave="hoveredItemHref = null">
13
14
  <li
14
- v-for="item in navItemData.main"
15
- :key="item.href"
15
+ v-for="(item, index) in visibleItems"
16
+ :key="`${index}-${item.href}`"
16
17
  :data-href="item.href"
17
18
  :class="[
18
19
  item.cssName,
@@ -55,9 +56,9 @@
55
56
  class="tab-nav-burger"
56
57
  :class="{ 'is-open': isMenuOpen }"
57
58
  variant="tertiary"
58
- :button-text="isMenuOpen ? 'Close navigation menu' : 'Open navigation menu'"
59
+ :button-text="isMenuOpen ? closeMenuLabel : openMenuLabel"
59
60
  :aria-expanded="String(isMenuOpen)"
60
- aria-controls="tab-nav-panel"
61
+ :aria-controls="panelId"
61
62
  @click="toggleMenu"
62
63
  >
63
64
  <template #iconOnly>
@@ -79,14 +80,14 @@
79
80
 
80
81
  <div
81
82
  v-if="showCollapsed"
82
- id="tab-nav-panel"
83
+ :id="panelId"
83
84
  class="tab-nav-panel"
84
85
  :class="{ 'is-open': isMenuOpen }"
85
86
  :inert="!isMenuOpen ? true : undefined"
86
87
  >
87
88
  <div class="tab-nav-panel-inner">
88
89
  <ul class="tab-nav-panel-list">
89
- <li v-for="item in navItemData.main" :key="item.href" :class="item.cssName">
90
+ <li v-for="(item, index) in visibleItems" :key="`${index}-${item.href}`" :class="item.cssName">
90
91
  <a
91
92
  v-if="item.href?.startsWith('#')"
92
93
  :href="item.href"
@@ -133,6 +134,10 @@ interface Props {
133
134
  anchorScrollOffset?: number | (() => number);
134
135
  /** aria-label on the nav landmark — override for localisation. */
135
136
  ariaLabel?: string;
137
+ /** Burger button label while the menu is closed — override for localisation. */
138
+ openMenuLabel?: string;
139
+ /** Burger button label while the menu is open — override for localisation. */
140
+ closeMenuLabel?: string;
136
141
  }
137
142
 
138
143
  const props = withDefaults(defineProps<Props>(), {
@@ -140,8 +145,22 @@ const props = withDefaults(defineProps<Props>(), {
140
145
  styleClassPassthrough: () => [],
141
146
  anchorScrollOffset: undefined,
142
147
  ariaLabel: "Site navigation",
148
+ openMenuLabel: "Open navigation menu",
149
+ closeMenuLabel: "Close navigation menu",
143
150
  });
144
151
 
152
+ if (import.meta.dev) {
153
+ console.warn(
154
+ "TabNavigation is deprecated: use ResponsiveHeader, which also handles #anchor links " +
155
+ "(anchorScrollOffset) and collapses item by item. See \"Migrating from TabNavigation\" in " +
156
+ ".claude/skills/components/responsive-header.md."
157
+ );
158
+ }
159
+
160
+ // Items with no text would render links with no accessible name.
161
+ const visibleItems = computed(() => (props.navItemData.main ?? []).filter((item) => item.text?.trim()));
162
+ const panelId = useId();
163
+
145
164
  const { navRef, navListRef, isCollapsed, isLoaded, isMenuOpen, isActiveItem, toggleMenu, closeMenu } =
146
165
  useNavCollapse("tab-nav-loaded");
147
166
 
@@ -149,7 +168,7 @@ const { handleNavClick, activeHash } = useAnchorScroll({ offset: props.anchorScr
149
168
 
150
169
  onMounted(() => {
151
170
  if (!activeHash.value) {
152
- const firstHashItem = props.navItemData.main?.find((item) => item.href?.startsWith("#"));
171
+ const firstHashItem = visibleItems.value.find((item) => item.href?.startsWith("#"));
153
172
  if (firstHashItem?.href) activeHash.value = firstHashItem.href;
154
173
  }
155
174
  });
@@ -209,25 +228,22 @@ watch(
209
228
  /* ─── Public token API ────────────────────────────────────────────── */
210
229
 
211
230
  /* Horizontal nav */
212
- --_link-color: var(--tab-nav-link-color, var(--slate-01, currentColor));
231
+ --_link-color: var(--tab-nav-link-color, var(--theme-text));
232
+ --_link-hover-color: var(--tab-nav-link-hover-color, var(--theme-accent));
213
233
  --_link-size: var(--tab-nav-link-size, 1.6rem);
214
234
  --_link-tracking: var(--tab-nav-link-tracking, 0.06em);
215
235
  --_link-weight: var(--tab-nav-link-weight, 400);
216
236
  --_nav-transition: var(--tab-nav-transition, 250ms ease);
217
237
 
218
238
  /* Panel */
219
- --_panel-border-color: var(
220
- --tab-nav-panel-border-color,
221
- color-mix(in oklch, var(--slate-01, #c0847a) 35%, transparent)
222
- );
223
- --_panel-link-color: var(--tab-nav-panel-link-color, var(--slate-01, currentColor));
239
+ --_panel-link-color: var(--tab-nav-panel-link-color, var(--_link-color));
224
240
  --_panel-slide-duration: var(--tab-nav-panel-slide-duration, 350ms);
225
241
  --_panel-slide-easing: var(--tab-nav-panel-slide-easing, cubic-bezier(0.4, 0, 0.2, 1));
226
242
 
227
243
  /* Burger */
228
244
  --_burger-bar-height: var(--tab-nav-burger-height, 1.5px);
229
245
  --_burger-bar-gap: var(--tab-nav-burger-gap, 5px);
230
- --_burger-color: var(--tab-nav-burger-color, var(--slate-01, currentColor));
246
+ --_burger-color: var(--tab-nav-burger-color, var(--_link-color));
231
247
  --_burger-transition: var(--tab-nav-burger-transition, 300ms ease);
232
248
 
233
249
  /* ─────────────────────────────────────────────────────────────────── */
@@ -287,12 +303,17 @@ watch(
287
303
 
288
304
  &:hover,
289
305
  &:focus-visible {
290
- color: var(--tab-nav-link-hover-color, var(--slate-04, var(--_link-color)));
306
+ color: var(--_link-hover-color);
291
307
  outline: none;
292
308
  }
293
309
 
310
+ &:focus-visible {
311
+ outline: var(--tab-nav-focus-ring-width, 2px) solid var(--tab-nav-focus-ring-colour, currentColor);
312
+ outline-offset: var(--tab-nav-focus-ring-offset, 2px);
313
+ }
314
+
294
315
  &.router-link-exact-active {
295
- color: var(--tab-nav-link-active-color, var(--slate-01, var(--_link-color)));
316
+ color: var(--tab-nav-link-active-color, var(--_link-color));
296
317
  }
297
318
  }
298
319
  }
@@ -390,12 +411,12 @@ watch(
390
411
 
391
412
  &.is-open {
392
413
  grid-template-rows: 1fr;
393
- border-block-start-color: var(--_panel-border-color);
414
+ border-block-start-color: var(--tab-nav-panel-border-color, color-mix(in oklch, var(--_link-color) 20%, transparent));
394
415
  }
395
416
 
396
417
  .tab-nav-panel-inner {
397
418
  overflow: hidden;
398
- background-color: var(--tab-nav-panel-bg, var(--page-bg, #1a1614));
419
+ background-color: var(--tab-nav-panel-bg, var(--page-bg, var(--theme-surface-subtle)));
399
420
  }
400
421
 
401
422
  .tab-nav-panel-list {
@@ -404,14 +425,18 @@ watch(
404
425
  padding: 0;
405
426
 
406
427
  li {
407
- border-block-end: 1px solid var(--tab-nav-panel-item-border, color-mix(in oklch, var(--slate-01, white) 8%, transparent));
428
+ border-block-end: 1px solid
429
+ var(--tab-nav-panel-item-border, color-mix(in oklch, var(--_panel-link-color) 12%, transparent));
408
430
 
409
431
  &:last-child {
410
432
  border-block-end: none;
411
433
  }
412
434
 
413
435
  &:hover {
414
- background-color: color-mix(in oklch, var(--slate-01, white) 5%, transparent);
436
+ background-color: var(
437
+ --tab-nav-panel-item-hover-bg,
438
+ color-mix(in oklch, var(--_panel-link-color) 5%, transparent)
439
+ );
415
440
  }
416
441
  }
417
442
 
@@ -426,18 +451,25 @@ watch(
426
451
  text-decoration: none;
427
452
  padding-block: var(--tab-nav-panel-padding-block, 1.4rem);
428
453
  padding-inline: var(--tab-nav-panel-padding-inline, 1.5rem);
454
+ overflow-wrap: anywhere;
455
+ min-inline-size: 0;
429
456
  position: relative;
430
457
  z-index: 1;
431
458
  transition: color var(--_nav-transition);
432
459
 
433
460
  &:hover,
434
461
  &:focus-visible {
435
- color: var(--tab-nav-panel-link-hover-color, var(--slate-04, var(--_panel-link-color)));
462
+ color: var(--tab-nav-panel-link-hover-color, var(--_link-hover-color));
436
463
  outline: none;
437
464
  }
438
465
 
466
+ &:focus-visible {
467
+ outline: var(--tab-nav-focus-ring-width, 2px) solid var(--tab-nav-focus-ring-colour, currentColor);
468
+ outline-offset: calc(-1 * var(--tab-nav-focus-ring-width, 2px));
469
+ }
470
+
439
471
  &.router-link-exact-active {
440
- color: var(--tab-nav-panel-link-active-color, var(--slate-01, var(--_panel-link-color)));
472
+ color: var(--tab-nav-panel-link-active-color, var(--_panel-link-color));
441
473
  }
442
474
  }
443
475
  }
@@ -504,7 +536,7 @@ watch(
504
536
  bottom: 0;
505
537
  height: 2px;
506
538
  pointer-events: none;
507
- background-color: var(--tab-nav-decorator-indicator-color, var(--slate-01, currentColor));
539
+ background-color: var(--tab-nav-decorator-indicator-color, var(--theme-accent));
508
540
  z-index: 3;
509
541
  }
510
542