srcdev-nuxt-components 9.6.4 → 9.6.6

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 (49) hide show
  1. package/.claude/rules/consumer/a11y-no-focus-steal.md +14 -0
  2. package/.claude/skills/component-compliance-checklist.md +14 -2
  3. package/.claude/skills/components/display-toast.md +6 -3
  4. package/.claude/skills/components/progress-tracker.md +140 -0
  5. package/.claude/skills/components/social-icons-list.md +32 -5
  6. package/.claude/skills/components/stepper-list.md +10 -3
  7. package/.claude/skills/components/tabbed-content.md +4 -0
  8. package/.claude/skills/css-dynamic-border-radius.md +114 -0
  9. package/.claude/skills/index.md +4 -2
  10. package/.claude/skills/storybook-add-story.md +12 -0
  11. package/.vscode/srcdev-component-display-toast.code-snippets +6 -8
  12. package/.vscode/srcdev-component-progress-tracker.code-snippets +45 -0
  13. package/.vscode/srcdev-component-social-icons-list.code-snippets +14 -0
  14. package/.vscode/srcdev-component-stepper-list.code-snippets +3 -2
  15. package/.vscode/srcdev-component-tabbed-content.code-snippets +1 -0
  16. package/.vscode/srcdev-utility-dynamic-border-radius.code-snippets +31 -0
  17. package/app/assets/styles/setup/06.utility-classes/_dynamic-border-radius.css +27 -0
  18. package/app/assets/styles/setup/06.utility-classes/index.css +1 -0
  19. package/app/components/01.atoms/tabbed-content/CONSUMER-STYLING.md +5 -1
  20. package/app/components/01.atoms/tabbed-content/TabbedContent.vue +2 -2
  21. package/app/components/01.atoms/tabbed-content/stories/TabbedContent.stories.ts +26 -0
  22. package/app/components/02.molecules/contact-section/tests/__snapshots__/ContactSection.spec.ts.snap +28 -28
  23. package/app/components/02.molecules/progress-tracker/CONSUMER-STYLING.md +116 -0
  24. package/app/components/02.molecules/progress-tracker/ProgressTracker.vue +275 -0
  25. package/app/components/02.molecules/progress-tracker/stories/ProgressTracker.stories.ts +424 -0
  26. package/app/components/02.molecules/progress-tracker/tests/ProgressTracker.spec.ts +253 -0
  27. package/app/components/02.molecules/progress-tracker/tests/__snapshots__/ProgressTracker.spec.ts.snap +46 -0
  28. package/app/components/02.molecules/social-icons-list/CONSUMER-STYLING.md +97 -8
  29. package/app/components/02.molecules/social-icons-list/SocialIconsList.vue +80 -14
  30. package/app/components/02.molecules/social-icons-list/stories/SocialIconsList.stories.ts +230 -2
  31. package/app/components/02.molecules/social-icons-list/stories/social-icons-list-recipes.css +108 -0
  32. package/app/components/02.molecules/social-icons-list/tests/SocialIconsList.spec.ts +123 -52
  33. package/app/components/02.molecules/social-icons-list/tests/__snapshots__/SocialIconsList.spec.ts.snap +10 -4
  34. package/app/components/02.molecules/stepper-list/CONSUMER-STYLING.md +42 -7
  35. package/app/components/02.molecules/stepper-list/StepperList.vue +42 -19
  36. package/app/components/02.molecules/stepper-list/stories/StepperList.stories.ts +288 -2
  37. package/app/components/02.molecules/stepper-list/tests/StepperList.spec.ts +161 -34
  38. package/app/components/02.molecules/stepper-list/tests/__snapshots__/StepperList.spec.ts.snap +40 -40
  39. package/app/components/02.molecules/toast/CONSUMER-STYLING.md +9 -3
  40. package/app/components/02.molecules/toast/DisplayToast.vue +10 -10
  41. package/app/components/02.molecules/toast/DisplayToastProvider.vue +12 -4
  42. package/app/components/02.molecules/toast/stories/DisplayToast.stories.ts +200 -2
  43. package/app/components/02.molecules/toast/tests/DisplayToast.spec.ts +33 -12
  44. package/app/components/02.molecules/toast/tests/DisplayToastProvider.spec.ts +20 -0
  45. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +7 -7
  46. package/app/types/components/index.ts +1 -0
  47. package/app/types/components/progress-tracker.d.ts +6 -0
  48. package/app/types/components/social-icons-list.d.ts +9 -0
  49. package/package.json +1 -1
@@ -0,0 +1,275 @@
1
+ <template>
2
+ <div class="progress-tracker" :class="[elementClasses, { 'is-complete': isComplete }]">
3
+ <div v-if="total" class="progress-tracker-header">
4
+ <p ref="labelEl" class="progress-tracker-label" aria-live="polite" tabindex="-1">{{ headerLabel }}</p>
5
+ <div
6
+ class="progress-tracker-bar"
7
+ role="progressbar"
8
+ aria-valuemin="0"
9
+ :aria-valuemax="total"
10
+ :aria-valuenow="current"
11
+ :aria-valuetext="headerLabel"
12
+ :style="{ '--_progress-tracker-progress': String(current / total) }"
13
+ >
14
+ <span class="progress-tracker-bar-fill"></span>
15
+ </div>
16
+ </div>
17
+
18
+ <StepperList
19
+ v-if="total"
20
+ tag="ol"
21
+ :item-count="total"
22
+ indicator-alignment="top"
23
+ :connected="true"
24
+ class="progress-tracker-list"
25
+ >
26
+ <template v-for="(_, index) in steps" :key="`indicator-${index}`" #[`indicator-${index}`]>
27
+ <span class="progress-tracker-indicator" :class="`is-${stepState(index)}`" aria-hidden="true">
28
+ <Icon v-if="stepState(index) === 'done'" :name="doneIcon" class="progress-tracker-indicator-icon" />
29
+ <template v-else>{{ index + 1 }}</template>
30
+ </span>
31
+ </template>
32
+
33
+ <template v-for="(step, index) in steps" :key="`item-${index}`" #[`item-${index}`]>
34
+ <div class="progress-tracker-step" :class="`is-${stepState(index)}`">
35
+ <component
36
+ :is="headingTag"
37
+ :id="headingId(index)"
38
+ class="progress-tracker-title"
39
+ tabindex="-1"
40
+ :aria-current="stepState(index) === 'current' ? 'step' : undefined"
41
+ >
42
+ {{ step.title }}<span v-if="stepState(index) === 'done'" class="sr-only">, {{ doneLabel }}</span>
43
+ </component>
44
+ <p v-if="step.body" class="progress-tracker-body">{{ step.body }}</p>
45
+ <div v-if="stepState(index) === 'current' && $slots[`step-${index}`]" class="progress-tracker-slot">
46
+ <slot :name="`step-${index}`" :step="step" :index="index" :heading-id="headingId(index)"></slot>
47
+ </div>
48
+ </div>
49
+ </template>
50
+ </StepperList>
51
+ </div>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ import type { ProgressTrackerStep, ProgressTrackerStepState } from "~/types/components";
56
+
57
+ interface Props {
58
+ steps: ProgressTrackerStep[];
59
+ headingTag?: "h2" | "h3" | "h4" | "h5" | "h6";
60
+ stepLabel?: string;
61
+ completeLabel?: string;
62
+ doneLabel?: string;
63
+ doneIcon?: string;
64
+ focusOnChange?: boolean;
65
+ styleClassPassthrough?: string | string[];
66
+ }
67
+
68
+ const props = withDefaults(defineProps<Props>(), {
69
+ headingTag: "h3",
70
+ stepLabel: "Step {current} of {total}",
71
+ completeLabel: "All steps complete",
72
+ doneLabel: "Completed",
73
+ doneIcon: "lucide:check",
74
+ focusOnChange: true,
75
+ styleClassPassthrough: () => [],
76
+ });
77
+
78
+ const model = defineModel<number>({ required: true });
79
+
80
+ const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
81
+
82
+ watch(
83
+ () => props.styleClassPassthrough,
84
+ () => resetElementClasses(props.styleClassPassthrough)
85
+ );
86
+
87
+ const baseId = useId();
88
+ const labelEl = ref<HTMLElement | null>(null);
89
+
90
+ const total = computed(() => props.steps.length);
91
+ const current = computed(() => Math.min(total.value, Math.max(0, Math.floor(Number(model.value)) || 0)));
92
+ const isComplete = computed(() => total.value > 0 && current.value === total.value);
93
+
94
+ const headerLabel = computed(() =>
95
+ isComplete.value
96
+ ? props.completeLabel
97
+ : props.stepLabel.replaceAll("{current}", String(current.value + 1)).replaceAll("{total}", String(total.value))
98
+ );
99
+
100
+ const headingId = (index: number) => `${baseId}-step-${index}`;
101
+
102
+ const stepState = (index: number): ProgressTrackerStepState =>
103
+ index < current.value ? "done" : index === current.value ? "current" : "upcoming";
104
+
105
+ watch(current, async () => {
106
+ if (!props.focusOnChange || !total.value) return;
107
+ await nextTick();
108
+ const target = isComplete.value ? labelEl.value : document.getElementById(headingId(current.value));
109
+ target?.focus();
110
+ });
111
+ </script>
112
+
113
+ <style lang="css">
114
+ @layer components {
115
+ .progress-tracker {
116
+ overflow-wrap: anywhere;
117
+
118
+ .progress-tracker-header {
119
+ display: flex;
120
+ flex-wrap: wrap;
121
+ align-items: center;
122
+ justify-content: space-between;
123
+ gap: 1.2rem 2.4rem;
124
+ margin-block-end: var(--progress-tracker-header-gap, 2.4rem);
125
+ }
126
+
127
+ .progress-tracker-label {
128
+ margin: 0;
129
+ min-inline-size: 0;
130
+ font-family: var(--progress-tracker-label-font-family, inherit);
131
+ font-size: var(--progress-tracker-label-font-size, var(--step-4));
132
+ font-weight: var(--progress-tracker-label-font-weight, 600);
133
+ letter-spacing: var(--progress-tracker-label-letter-spacing, 0.12em);
134
+ text-transform: var(--progress-tracker-label-text-transform, uppercase);
135
+ color: var(--progress-tracker-label-colour, color-mix(in oklch, var(--theme-text) 70%, transparent));
136
+ }
137
+
138
+ .progress-tracker-bar {
139
+ flex: 0 1 var(--progress-tracker-bar-inline-size, 16rem);
140
+ block-size: var(--progress-tracker-bar-height, 0.6rem);
141
+ border-radius: var(--progress-tracker-bar-radius, 99rem);
142
+ background: var(--progress-tracker-track, color-mix(in oklch, var(--theme-text) 15%, transparent));
143
+ overflow: hidden;
144
+ }
145
+
146
+ .progress-tracker-bar-fill {
147
+ display: block;
148
+ block-size: 100%;
149
+ inline-size: calc(var(--_progress-tracker-progress, 0) * 100%);
150
+ border-radius: inherit;
151
+ background: var(--progress-tracker-accent, var(--theme-accent));
152
+ }
153
+
154
+ .progress-tracker-list {
155
+ --stepper-list-gap: var(--progress-tracker-gap, 4rem);
156
+ --stepper-list-padding-block: var(--progress-tracker-step-padding-block, 1.2rem);
157
+ --stepper-list-connector-width: var(--progress-tracker-connector-width, 0.2rem);
158
+ --stepper-list-connector-color: var(
159
+ --progress-tracker-track,
160
+ color-mix(in oklch, var(--theme-text) 15%, transparent)
161
+ );
162
+ --stepper-list-connector-gap: 0;
163
+ --stepper-list-connector-image: none;
164
+ margin: 0;
165
+
166
+ .stepper-list-item:has(.progress-tracker-indicator.is-done) {
167
+ --stepper-list-connector-color: var(--progress-tracker-accent, var(--theme-accent));
168
+ }
169
+ }
170
+
171
+ .progress-tracker-indicator {
172
+ display: grid;
173
+ place-items: center;
174
+ box-sizing: border-box;
175
+ inline-size: var(--progress-tracker-indicator-size, 4.4rem);
176
+ block-size: var(--progress-tracker-indicator-size, 4.4rem);
177
+ border: var(--progress-tracker-indicator-border-width, 0.2rem) solid
178
+ var(--progress-tracker-track, color-mix(in oklch, var(--theme-text) 15%, transparent));
179
+ border-radius: var(--progress-tracker-indicator-radius, 50%);
180
+ font-size: var(--progress-tracker-indicator-font-size, var(--step-4));
181
+ font-weight: 700;
182
+ font-variant-numeric: tabular-nums;
183
+ color: var(--progress-tracker-upcoming-text, color-mix(in oklch, var(--theme-text) 60%, transparent));
184
+
185
+ &.is-done {
186
+ border-color: var(--progress-tracker-accent, var(--theme-accent));
187
+ background: var(--progress-tracker-accent, var(--theme-accent));
188
+ color: var(--progress-tracker-on-accent, var(--theme-on-surface));
189
+ }
190
+
191
+ &.is-current {
192
+ margin-block-start: var(--progress-tracker-card-padding-block, 1.6rem);
193
+ border-color: var(--progress-tracker-accent, var(--theme-accent));
194
+ color: var(--progress-tracker-accent, var(--theme-accent));
195
+ box-shadow: 0 0 0 var(--progress-tracker-ring-width, 0.6rem)
196
+ var(--progress-tracker-ring-colour, color-mix(in oklch, var(--theme-accent) 20%, transparent));
197
+ }
198
+
199
+ .progress-tracker-indicator-icon {
200
+ inline-size: 55%;
201
+ block-size: 55%;
202
+ }
203
+ }
204
+
205
+ .progress-tracker-step {
206
+ display: flex;
207
+ flex-direction: column;
208
+ gap: var(--progress-tracker-content-gap, 0.8rem);
209
+ min-inline-size: 0;
210
+
211
+ &.is-current {
212
+ margin-inline-start: calc(-1 * var(--progress-tracker-card-padding-inline, 2rem));
213
+ padding-block: var(--progress-tracker-card-padding-block, 1.6rem);
214
+ padding-inline: var(--progress-tracker-card-padding-inline, 2rem);
215
+ border-radius: var(--progress-tracker-card-radius, 1.6rem);
216
+ background: var(--progress-tracker-card-surface, var(--theme-surface-subtle));
217
+ box-shadow: var(
218
+ --progress-tracker-card-shadow,
219
+ 0 0.2rem 0.4rem rgb(0 0 0 / 0.04),
220
+ 0 1.2rem 3.2rem -1.2rem rgb(0 0 0 / 0.18)
221
+ );
222
+ }
223
+
224
+ &.is-upcoming {
225
+ .progress-tracker-title,
226
+ .progress-tracker-body {
227
+ color: var(--progress-tracker-upcoming-text, color-mix(in oklch, var(--theme-text) 60%, transparent));
228
+ }
229
+ }
230
+ }
231
+
232
+ .progress-tracker-title {
233
+ display: -webkit-box;
234
+ -webkit-box-orient: vertical;
235
+ -webkit-line-clamp: var(--progress-tracker-title-line-clamp, none);
236
+ line-clamp: var(--progress-tracker-title-line-clamp, none);
237
+ overflow: hidden;
238
+ margin: 0;
239
+ margin-block-start: max(0px, calc((var(--progress-tracker-indicator-size, 4.4rem) - 1lh) / 2));
240
+ font-size: var(--progress-tracker-title-font-size, var(--step-5));
241
+ font-weight: var(--progress-tracker-title-font-weight, 600);
242
+ line-height: 1.3;
243
+ color: var(--progress-tracker-title-colour, var(--theme-text));
244
+ }
245
+
246
+ .progress-tracker-body {
247
+ display: -webkit-box;
248
+ -webkit-box-orient: vertical;
249
+ -webkit-line-clamp: var(--progress-tracker-body-line-clamp, none);
250
+ line-clamp: var(--progress-tracker-body-line-clamp, none);
251
+ overflow: hidden;
252
+ margin: 0;
253
+ font-size: var(--progress-tracker-body-font-size, var(--step-4));
254
+ line-height: 1.5;
255
+ color: var(--progress-tracker-body-colour, color-mix(in oklch, var(--theme-text) 80%, transparent));
256
+ }
257
+
258
+ .progress-tracker-slot {
259
+ margin-block-start: var(--progress-tracker-slot-gap, 0.8rem);
260
+ }
261
+
262
+ @media (prefers-reduced-motion: no-preference) {
263
+ .progress-tracker-bar-fill {
264
+ transition: inline-size var(--progress-tracker-transition-duration, 200ms) ease;
265
+ }
266
+
267
+ .progress-tracker-indicator,
268
+ .progress-tracker-step {
269
+ transition-property: background-color, border-color, box-shadow, color;
270
+ transition-duration: var(--progress-tracker-transition-duration, 200ms);
271
+ }
272
+ }
273
+ }
274
+ }
275
+ </style>