srcdev-nuxt-components 9.1.50 → 9.1.52

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 (34) hide show
  1. package/.claude/settings.json +8 -27
  2. package/.claude/settings.local.json +24 -52
  3. package/.claude/skills/components/action-menu.md +141 -0
  4. package/.claude/skills/components/stepper-list.md +52 -18
  5. package/.claude/skills/css-nesting-conventions.md +121 -0
  6. package/.claude/skills/index.md +3 -0
  7. package/.claude/skills/pull-request-description.md +48 -0
  8. package/.claude/skills/testing-add-unit-test.md +40 -2
  9. package/README.md +26 -5
  10. package/app/components/02.molecules/action-menu/ActionMenu.vue +218 -0
  11. package/app/components/02.molecules/action-menu/ActionMenuItemCore.vue +123 -0
  12. package/app/components/02.molecules/action-menu/CONSUMER-STYLING.md +125 -0
  13. package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +326 -0
  14. package/app/components/02.molecules/action-menu/tests/ActionMenu.spec.ts +458 -0
  15. package/app/components/02.molecules/action-menu/tests/ActionMenuItemCore.spec.ts +199 -0
  16. package/app/components/02.molecules/action-menu/tests/__snapshots__/ActionMenu.spec.ts.snap +28 -0
  17. package/app/components/02.molecules/action-menu/tests/__snapshots__/ActionMenuItemCore.spec.ts.snap +27 -0
  18. package/app/components/02.molecules/display-chip/tests/DisplayChip.spec.ts +5 -1
  19. package/app/components/02.molecules/navigation/tab-navigation/TabNavigation.vue +2 -1
  20. package/app/components/02.molecules/stepper-list/CONSUMER-STYLING.md +138 -0
  21. package/app/components/02.molecules/stepper-list/StepperList.vue +50 -24
  22. package/app/components/03.organisms/image-galleries/slider-gallery/tests/SliderGallery.spec.ts +5 -1
  23. package/app/components/05.forms/input-button/InputButtonCore.vue +1 -1
  24. package/app/components/carousels/tests/CarouselFlip.spec.ts +1 -0
  25. package/app/components/responsive-header/NavigationItems.vue +302 -81
  26. package/app/components/responsive-header/ResponsiveHeader.vue +360 -56
  27. package/app/layouts/default.vue +43 -352
  28. package/app/layouts/site-navigation-demo.vue +36 -34
  29. package/app/pages/page-hero-highlights.vue +3 -3
  30. package/app/pages/ui/scroll-reveal-image.vue +3 -3
  31. package/app/pages/ui/simple-grid.vue +9 -20
  32. package/modules/colour-scheme.ts +1 -1
  33. package/nuxt.config.ts +11 -2
  34. package/package.json +17 -16
@@ -0,0 +1,27 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`ActionMenuItemCore > Snapshots > as external link 1`] = `
4
+ "<a href="https://example.com" class="action-menu-item-core" role="menuitem">
5
+ <!--v-if--><span class="action-menu-item-label">Edit item</span><span class="action-menu-item-arrow" aria-hidden="true"><span class="iconify i-lucide:arrow-right action-menu-item-arrow-icon" aria-hidden="true"></span></span>
6
+ </a>"
7
+ `;
8
+
9
+ exports[`ActionMenuItemCore > Snapshots > as internal link 1`] = `
10
+ "<a href="/about" class="action-menu-item-core" role="menuitem">
11
+ <!--v-if--><span class="action-menu-item-label">Edit item</span><span class="action-menu-item-arrow" aria-hidden="true"><span class="iconify i-lucide:arrow-right action-menu-item-arrow-icon" aria-hidden="true"></span></span>
12
+ </a>"
13
+ `;
14
+
15
+ exports[`ActionMenuItemCore > Snapshots > default button (no href) 1`] = `
16
+ "<button type="button" class="action-menu-item-core" role="menuitem">
17
+ <!--v-if--><span class="action-menu-item-label">Edit item</span><span class="action-menu-item-arrow" aria-hidden="true"><span class="iconify i-lucide:arrow-right action-menu-item-arrow-icon" aria-hidden="true"></span></span>
18
+ </button>"
19
+ `;
20
+
21
+ exports[`ActionMenuItemCore > Snapshots > with icon slot 1`] = `"<button type="button" class="action-menu-item-core" role="menuitem"><span class="action-menu-item-icon" aria-hidden="true"><span data-testid="test-icon">⚡</span></span><span class="action-menu-item-label">Edit item</span><span class="action-menu-item-arrow" aria-hidden="true"><span class="iconify i-lucide:arrow-right action-menu-item-arrow-icon" aria-hidden="true"></span></span></button>"`;
22
+
23
+ exports[`ActionMenuItemCore > Snapshots > with styleClassPassthrough 1`] = `
24
+ "<button type="button" class="action-menu-item-core custom-class another-class" role="menuitem">
25
+ <!--v-if--><span class="action-menu-item-label">Edit item</span><span class="action-menu-item-arrow" aria-hidden="true"><span class="iconify i-lucide:arrow-right action-menu-item-arrow-icon" aria-hidden="true"></span></span>
26
+ </button>"
27
+ `;
@@ -1,8 +1,12 @@
1
- import { describe, it, expect, vi } from "vitest";
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
2
  import { mountSuspended } from "@nuxt/test-utils/runtime";
3
3
  import DisplayChip from "../DisplayChip.vue";
4
4
 
5
5
  describe("DisplayChip", () => {
6
+ afterEach(() => {
7
+ vi.restoreAllMocks();
8
+ });
9
+
6
10
  // ─── Mount ───────────────────────────────────────────────────────────────
7
11
 
8
12
  it("mounts without error", async () => {
@@ -139,7 +139,8 @@ const props = withDefaults(defineProps<Props>(), {
139
139
  anchorScrollOffset: undefined,
140
140
  });
141
141
 
142
- const { isCollapsed, isLoaded, isMenuOpen, isActiveItem, toggleMenu, closeMenu } = useNavCollapse("tab-nav-loaded");
142
+ const { navRef, navListRef, isCollapsed, isLoaded, isMenuOpen, isActiveItem, toggleMenu, closeMenu } =
143
+ useNavCollapse("tab-nav-loaded");
143
144
 
144
145
  const { handleNavClick, activeHash } = useAnchorScroll({ offset: props.anchorScrollOffset });
145
146
 
@@ -0,0 +1,138 @@
1
+ # StepperList — Consumer Styling Guide
2
+
3
+ ## Public token API
4
+
5
+ All `--stepper-list-*` tokens are the stable override surface. Set them globally in a theme
6
+ file, scoped to a page wrapper, or per-instance via `styleClassPassthrough`.
7
+
8
+ ### Layout & sizing
9
+
10
+ | Token | Default | Controls |
11
+ |---|---|---|
12
+ | `--stepper-list-padding-block` | `1.2rem` | Vertical padding for each list item |
13
+ | `--stepper-list-gap` | `2.2rem` | Horizontal gap between indicator and content |
14
+ | `--stepper-list-counter-size` | prop `indicatorSize` (`3rem`) | Width and height of the counter bubble or custom icon |
15
+ | `--stepper-list-counter-font-size` | `1.4rem` | Counter number font size |
16
+ | `--stepper-list-counter-font-weight` | `600` | Counter number font weight |
17
+ | `--stepper-list-connector-width` | `0.2rem` | Connector line thickness |
18
+
19
+ ### Colours
20
+
21
+ | Token | Default | Controls |
22
+ |---|---|---|
23
+ | `--stepper-list-connector-color` | `currentColor` | Connector line colour |
24
+ | `--stepper-list-icon-color` | `currentColor` | Icon colour for custom `indicator-icon` elements |
25
+
26
+ ### Circle indicator
27
+
28
+ | Token | Default | Controls |
29
+ |---|---|---|
30
+ | `--stepper-list-counter-circle-background` | `transparent` | Bubble fill |
31
+ | `--stepper-list-counter-circle-text` | `currentColor` | Counter number colour |
32
+ | `--stepper-list-counter-circle-border` | `currentColor` | Bubble border colour |
33
+ | `--stepper-list-counter-circle-border-radius` | `100vw` | Bubble corner rounding |
34
+
35
+ ### Disc indicator
36
+
37
+ | Token | Default | Controls |
38
+ |---|---|---|
39
+ | `--stepper-list-counter-disc-background` | `transparent` | Bubble fill |
40
+ | `--stepper-list-counter-disc-text` | `currentColor` | Counter number colour |
41
+ | `--stepper-list-counter-disc-border` | `transparent` | Bubble border colour |
42
+ | `--stepper-list-counter-disc-border-radius` | `100vw` | Bubble corner rounding |
43
+
44
+ ### Square indicator
45
+
46
+ | Token | Default | Controls |
47
+ |---|---|---|
48
+ | `--stepper-list-counter-square-background` | `transparent` | Bubble fill |
49
+ | `--stepper-list-counter-square-text` | `currentColor` | Counter number colour |
50
+ | `--stepper-list-counter-square-border` | `transparent` | Bubble border colour |
51
+ | `--stepper-list-counter-square-border-radius` | `0.25rem` | Bubble corner rounding |
52
+
53
+ ---
54
+
55
+ ## Global theming — recommended approach
56
+
57
+ Create `assets/styles/setup/07.components/stepper-list.css` in the consuming app and set
58
+ tokens on `:root`. This applies to every `StepperList` across the site.
59
+
60
+ ```css
61
+ /* assets/styles/setup/07.components/stepper-list.css */
62
+ :root {
63
+ --stepper-list-counter-disc-background: var(--brand-primary);
64
+ --stepper-list-counter-disc-text: var(--brand-primary-contrast);
65
+ --stepper-list-counter-disc-border: transparent;
66
+
67
+ --stepper-list-counter-circle-text: var(--brand-primary);
68
+ --stepper-list-counter-circle-border: var(--brand-primary);
69
+
70
+ --stepper-list-counter-square-background: var(--brand-primary);
71
+ --stepper-list-counter-square-text: var(--brand-primary-contrast);
72
+ --stepper-list-counter-square-border: transparent;
73
+
74
+ --stepper-list-connector-color: var(--brand-border);
75
+ --stepper-list-icon-color: var(--brand-primary);
76
+ }
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Page-scoped overrides
82
+
83
+ Override tokens for a specific section by scoping them under the page or layout wrapper.
84
+ No `:deep()` is required (component styles are unscoped).
85
+
86
+ ```css
87
+ /* In the consuming page's unscoped <style> block */
88
+ .how-it-works {
89
+ .stepper-list {
90
+ --stepper-list-padding-block: 2rem;
91
+ --stepper-list-gap: 3.2rem;
92
+ --stepper-list-counter-size: 4rem;
93
+ --stepper-list-counter-font-size: 1.6rem;
94
+ --stepper-list-connector-color: var(--brand-border-subtle);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Per-instance overrides via styleClassPassthrough
102
+
103
+ Use sparingly — prefer global or page-scoped CSS. When a single instance needs a distinct
104
+ visual style, pass a modifier class:
105
+
106
+ ```vue
107
+ <StepperList
108
+ :style-class-passthrough="['completed-steps']"
109
+ indicator-variant="circle"
110
+ :item-count="3"
111
+ >
112
+ ...
113
+ </StepperList>
114
+ ```
115
+
116
+ ```css
117
+ .stepper-list.completed-steps {
118
+ --stepper-list-counter-circle-background: var(--color-success);
119
+ --stepper-list-counter-circle-text: white;
120
+ --stepper-list-counter-circle-border: transparent;
121
+ --stepper-list-connector-color: var(--color-success);
122
+ --stepper-list-icon-color: var(--color-success);
123
+ }
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Notes
129
+
130
+ - `--stepper-list-counter-size` takes precedence over the `indicatorSize` prop. Use the prop
131
+ for programmatic/template control; use the CSS token for pure-CSS theme overrides.
132
+ - The connector line is positioned by JS (`ResizeObserver`) on each `<li>` as
133
+ `--_connector-top` and `--_connector-height`. These are internal — do not set them.
134
+ - `--stepper-list-connector-color` defaults to `currentColor`, inheriting from the nearest
135
+ ancestor's text colour. Set it explicitly when that behaviour is undesirable.
136
+ - All border tokens (`--stepper-list-counter-*-border`) control the **colour** only — the
137
+ border is always `2px solid`. Override the border shorthand directly on `.stepper-list li
138
+ .stepper-list__indicator-counter::before` if you need a different width.
@@ -100,9 +100,35 @@ watch(
100
100
  <style lang="css">
101
101
  @layer components {
102
102
  .stepper-list {
103
- --_list-padding-block: 1.2rem;
104
- --_counter-size: v-bind(indicatorSize);
105
- --_stepper-list-connector-width: 0.2rem;
103
+ /* Layout */
104
+ --_padding-block: var(--stepper-list-padding-block, 1.2rem);
105
+ --_gap: var(--stepper-list-gap, 2.2rem);
106
+ --_counter-size: var(--stepper-list-counter-size, v-bind(indicatorSize));
107
+ --_counter-font-size: var(--stepper-list-counter-font-size, 1.4rem);
108
+ --_counter-font-weight: var(--stepper-list-counter-font-weight, 600);
109
+ --_connector-width: var(--stepper-list-connector-width, 0.2rem);
110
+
111
+ /* Colours */
112
+ --_connector-color: var(--stepper-list-connector-color, currentColor);
113
+ --_icon-color: var(--stepper-list-icon-color, currentColor);
114
+
115
+ /* Circle indicator */
116
+ --_counter-circle-background: var(--stepper-list-counter-circle-background, transparent);
117
+ --_counter-circle-text: var(--stepper-list-counter-circle-text, currentColor);
118
+ --_counter-circle-border-color: var(--stepper-list-counter-circle-border, currentColor);
119
+ --_counter-circle-border-radius: var(--stepper-list-counter-circle-border-radius, 100vw);
120
+
121
+ /* Disc indicator */
122
+ --_counter-disc-background: var(--stepper-list-counter-disc-background, transparent);
123
+ --_counter-disc-text: var(--stepper-list-counter-disc-text, currentColor);
124
+ --_counter-disc-border-color: var(--stepper-list-counter-disc-border, transparent);
125
+ --_counter-disc-border-radius: var(--stepper-list-counter-disc-border-radius, 100vw);
126
+
127
+ /* Square indicator */
128
+ --_counter-square-background: var(--stepper-list-counter-square-background, transparent);
129
+ --_counter-square-text: var(--stepper-list-counter-square-text, currentColor);
130
+ --_counter-square-border-color: var(--stepper-list-counter-square-border, transparent);
131
+ --_counter-square-border-radius: var(--stepper-list-counter-square-border-radius, 0.25rem);
106
132
 
107
133
  list-style: none;
108
134
  counter-reset: stepper-list;
@@ -111,10 +137,10 @@ watch(
111
137
  li {
112
138
  display: grid;
113
139
  grid-template-columns: auto 1fr;
114
- gap: 2.2rem;
140
+ gap: var(--_gap);
115
141
  counter-increment: stepper-list;
116
142
  padding-inline-start: 0rem;
117
- padding-block: var(--_list-padding-block);
143
+ padding-block: var(--_padding-block);
118
144
  position: relative;
119
145
 
120
146
  &.indicator-top {
@@ -132,29 +158,29 @@ watch(
132
158
  place-content: center;
133
159
  width: var(--_counter-size);
134
160
  height: var(--_counter-size);
135
- font-size: 1.4rem;
136
- font-weight: 600;
161
+ font-size: var(--_counter-font-size);
162
+ font-weight: var(--_counter-font-weight);
137
163
  }
138
164
 
139
165
  &.indicator-circle .stepper-list__indicator-counter::before {
140
- background-color: var(--stepper-list-counter-circle-background);
141
- color: var(--stepper-list-counter-circle-text);
142
- border: 2px solid var(--stepper-list-counter-circle-border);
143
- border-radius: 100vw;
166
+ background-color: var(--_counter-circle-background);
167
+ color: var(--_counter-circle-text);
168
+ border: 2px solid var(--_counter-circle-border-color);
169
+ border-radius: var(--_counter-circle-border-radius);
144
170
  }
145
171
 
146
172
  &.indicator-disc .stepper-list__indicator-counter::before {
147
- background-color: var(--stepper-list-counter-disc-background);
148
- color: var(--stepper-list-counter-disc-text);
149
- border: 2px solid var(--stepper-list-counter-disc-border);
150
- border-radius: 100vw;
173
+ background-color: var(--_counter-disc-background);
174
+ color: var(--_counter-disc-text);
175
+ border: 2px solid var(--_counter-disc-border-color);
176
+ border-radius: var(--_counter-disc-border-radius);
151
177
  }
152
178
 
153
179
  &.indicator-square .stepper-list__indicator-counter::before {
154
- background-color: var(--stepper-list-counter-square-background);
155
- color: var(--stepper-list-counter-square-text);
156
- border: 2px solid var(--stepper-list-counter-square-border);
157
- border-radius: 0.25rem;
180
+ background-color: var(--_counter-square-background);
181
+ color: var(--_counter-square-text);
182
+ border: 2px solid var(--_counter-square-border-color);
183
+ border-radius: var(--_counter-square-border-radius);
158
184
  }
159
185
 
160
186
  &.has-indicator::before {
@@ -172,7 +198,7 @@ watch(
172
198
  justify-content: center;
173
199
 
174
200
  .indicator-icon {
175
- color: var(--stepper-list-icon);
201
+ color: var(--_icon-color);
176
202
  width: var(--_counter-size) !important;
177
203
  height: var(--_counter-size) !important;
178
204
  }
@@ -187,11 +213,11 @@ watch(
187
213
  transform: translateX(-50%);
188
214
  /* JS sets --_connector-top and --_connector-height from measured indicator positions.
189
215
  The fallback values handle the indicator-top case before JS runs. */
190
- top: var(--_connector-top, calc(var(--_list-padding-block) + var(--_counter-size)));
216
+ top: var(--_connector-top, calc(var(--_padding-block) + var(--_counter-size)));
191
217
  height: var(--_connector-height, auto);
192
- bottom: calc(-1 * var(--_list-padding-block));
193
- width: var(--_stepper-list-connector-width);
194
- background-color: var(--stepper-list-connector-color, currentColor);
218
+ bottom: calc(-1 * var(--_padding-block));
219
+ width: var(--_connector-width);
220
+ background-color: var(--_connector-color);
195
221
  }
196
222
  }
197
223
  </style>
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import { mountSuspended } from "@nuxt/test-utils/runtime";
3
3
  import { nextTick } from "vue";
4
4
  import SliderGallery from "../SliderGallery.vue";
@@ -67,6 +67,10 @@ describe("SliderGallery", () => {
67
67
  );
68
68
  });
69
69
 
70
+ afterEach(() => {
71
+ vi.restoreAllMocks();
72
+ });
73
+
70
74
  // ─── Mount ───────────────────────────────────────────────────────────────
71
75
 
72
76
  it("mounts without error", async () => {
@@ -27,7 +27,6 @@
27
27
 
28
28
  <script setup lang="ts">
29
29
  import type { InputTypesButton, FormUiTheme, InputButtonVariant } from "~/types/forms/types.forms";
30
- import { NuxtLink } from "#components";
31
30
  interface Props {
32
31
  type?: InputTypesButton;
33
32
  href?: string;
@@ -55,6 +54,7 @@ const props = withDefaults(defineProps<Props>(), {
55
54
  });
56
55
 
57
56
  const slots = useSlots();
57
+ const NuxtLink = resolveComponent("NuxtLink");
58
58
 
59
59
  // If href is undefined tag is button, else it's a link
60
60
  const isLink = computed(() => Boolean(props.href));
@@ -119,6 +119,7 @@ describe("CarouselFlip", () => {
119
119
 
120
120
  afterEach(() => {
121
121
  wrapper?.unmount();
122
+ vi.restoreAllMocks();
122
123
  });
123
124
 
124
125
  describe("Component Rendering", () => {