srcdev-nuxt-components 9.1.0 → 9.1.2
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.
- package/.claude/settings.json +2 -1
- package/.claude/skills/components/page-hero-highlights.md +60 -0
- package/.claude/skills/components/services-card-grid.md +110 -0
- package/.claude/skills/components/services-card.md +65 -30
- package/.claude/skills/components/site-navigation.md +120 -0
- package/.claude/skills/index.md +3 -2
- package/app/components/02.molecules/navigation/site-navigation/SiteNavigation.vue +780 -0
- package/app/components/02.molecules/navigation/site-navigation/stories/SiteNavigation.stories.ts +335 -0
- package/app/components/02.molecules/navigation/site-navigation/tests/SiteNavigation.spec.ts +328 -0
- package/app/components/02.molecules/navigation/site-navigation/tests/__snapshots__/SiteNavigation.spec.ts.snap +30 -0
- package/app/components/03.organisms/services/services-card/ServicesCard.vue +26 -10
- package/app/components/03.organisms/services/services-grids/ServicesCardGrid.vue +27 -3
- package/app/components/04.templates/page-hero-highlights/PageHeroHighlights.vue +36 -21
- package/app/components/04.templates/page-hero-highlights/PageHeroHighlightsHeader.vue +66 -0
- package/app/components/04.templates/page-hero-highlights/stories/PageHeroHighlights.stories.ts +50 -3
- package/app/components/04.templates/page-hero-highlights/stories/PageHeroHighlightsHeader.stories.ts +77 -0
- package/app/components/04.templates/page-hero-highlights/tests/PageHeroHighlights.spec.ts +15 -7
- package/app/components/04.templates/page-hero-highlights/tests/PageHeroHighlightsHeader.spec.ts +51 -0
- package/app/components/04.templates/page-hero-highlights/tests/__snapshots__/PageHeroHighlights.spec.ts.snap +1 -1
- package/app/layouts/default.vue +2 -0
- package/app/layouts/site-navigation-demo.vue +69 -0
- package/app/pages/page-hero-highlights.vue +15 -11
- package/app/pages/ui/navigation/site-navigation/about.vue +11 -0
- package/app/pages/ui/navigation/site-navigation/contact.vue +11 -0
- package/app/pages/ui/navigation/site-navigation/index.vue +11 -0
- package/app/pages/ui/navigation/site-navigation/portfolio.vue +11 -0
- package/app/pages/ui/navigation/site-navigation/services.vue +11 -0
- package/app/pages/ui/services/services-cards.vue +16 -1
- package/nuxt.config.ts +7 -0
- package/package.json +6 -6
- package/.claude/skills/components/treatment-consultant.md +0 -128
|
@@ -3,18 +3,26 @@
|
|
|
3
3
|
<NuxtLayout name="default">
|
|
4
4
|
<template #layout-content>
|
|
5
5
|
<PageHeroHighlights
|
|
6
|
+
tag="section"
|
|
6
7
|
max-width="1064px"
|
|
7
8
|
content-align="start"
|
|
9
|
+
:content-panel="true"
|
|
8
10
|
:highlights-equal-widths="false"
|
|
9
11
|
highlights-justify="start"
|
|
10
12
|
:highlight-title-baseline="true"
|
|
11
13
|
:style-class-passthrough="['mbe-32']"
|
|
12
14
|
>
|
|
13
|
-
<template #header>
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
<template #header="{ headingId }">
|
|
16
|
+
<PageHeroHighlightsHeader>
|
|
17
|
+
<template #start>
|
|
18
|
+
<h1 :id="headingId" class="page-heading-1">Dashboard</h1>
|
|
19
|
+
<p class="page-body-normal">Overview of your account activity and key metrics.</p>
|
|
20
|
+
</template>
|
|
21
|
+
<template #end>
|
|
22
|
+
<h1 :id="headingId" class="page-heading-1">Dashboard</h1>
|
|
23
|
+
<p class="page-body-normal">Overview of your account activity and key metrics.</p>
|
|
24
|
+
</template>
|
|
25
|
+
</PageHeroHighlightsHeader>
|
|
18
26
|
</template>
|
|
19
27
|
<template #highlights>
|
|
20
28
|
<div class="highlight">
|
|
@@ -59,12 +67,8 @@ useHead({
|
|
|
59
67
|
<style lang="css">
|
|
60
68
|
.page-hero-highlights-page {
|
|
61
69
|
.page-hero-highlights {
|
|
62
|
-
.header
|
|
63
|
-
|
|
64
|
-
.some-header-content {
|
|
65
|
-
padding-block: 4rem 3rem;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
70
|
+
.page-hero-highlights-header {
|
|
71
|
+
padding-block: 4rem 3rem;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
.content-row {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>About</h1>
|
|
4
|
+
<p>This is the About page. The active indicator in the navigation above should be sitting under "About".</p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
definePageMeta({ layout: "site-navigation-demo" });
|
|
10
|
+
useHead({ title: "SiteNavigation Demo — About" });
|
|
11
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Contact</h1>
|
|
4
|
+
<p>This is the Contact page. The active indicator in the navigation above should be sitting under "Contact".</p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
definePageMeta({ layout: "site-navigation-demo" });
|
|
10
|
+
useHead({ title: "SiteNavigation Demo — Contact" });
|
|
11
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Home</h1>
|
|
4
|
+
<p>Welcome to the SiteNavigation component demo. Resize the window to see the burger menu collapse, and navigate between pages to see the active indicator update.</p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
definePageMeta({ layout: "site-navigation-demo" });
|
|
10
|
+
useHead({ title: "SiteNavigation Demo — Home" });
|
|
11
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Portfolio</h1>
|
|
4
|
+
<p>This is the Portfolio page. The active indicator in the navigation above should be sitting under "Portfolio".</p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
definePageMeta({ layout: "site-navigation-demo" });
|
|
10
|
+
useHead({ title: "SiteNavigation Demo — Portfolio" });
|
|
11
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Services</h1>
|
|
4
|
+
<p>This is the Services page. The active indicator in the navigation above should be sitting under "Services".</p>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
definePageMeta({ layout: "site-navigation-demo" });
|
|
10
|
+
useHead({ title: "SiteNavigation Demo — Services" });
|
|
11
|
+
</script>
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
</LayoutRow>
|
|
9
9
|
|
|
10
10
|
<LayoutRow tag="div" variant="content" :style-class-passthrough="['mbe-20']">
|
|
11
|
-
<ServicesCardGrid
|
|
11
|
+
<ServicesCardGrid
|
|
12
|
+
:services-data="servicesData ?? []"
|
|
13
|
+
:eyebrow-config="{ fontSize: 'large' }"
|
|
14
|
+
:hero-config="{ tag: 'h2', fontSize: 'heading' }"
|
|
15
|
+
/>
|
|
12
16
|
</LayoutRow>
|
|
13
17
|
</template>
|
|
14
18
|
</NuxtLayout>
|
|
@@ -38,5 +42,16 @@ if (servicesData.value.length === 0) {
|
|
|
38
42
|
|
|
39
43
|
<style lang="css">
|
|
40
44
|
.page-services-card-grid {
|
|
45
|
+
/* Page specific styles here */
|
|
46
|
+
.services-card-grid {
|
|
47
|
+
--_gap: 4rem;
|
|
48
|
+
--_column-min-width: 250px;
|
|
49
|
+
|
|
50
|
+
.services-card {
|
|
51
|
+
--_eyebrow-text-margin-block: 0.8rem 0;
|
|
52
|
+
--_hero-text-margin-block: 2rem 1rem;
|
|
53
|
+
--_description-text-colour: var(--colour-text-secondary);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
41
56
|
}
|
|
42
57
|
</style>
|
package/nuxt.config.ts
CHANGED
|
@@ -5,6 +5,8 @@ const { resolve } = createResolver(import.meta.url);
|
|
|
5
5
|
const isStandalone = !!process.env.SRCDEV_STANDALONE;
|
|
6
6
|
|
|
7
7
|
export default defineNuxtConfig({
|
|
8
|
+
// debug: !isProduction,
|
|
9
|
+
debug: false,
|
|
8
10
|
devtools: { enabled: true },
|
|
9
11
|
|
|
10
12
|
// Server-only secrets — Nuxt reads matching NUXT_* env vars automatically.
|
|
@@ -104,6 +106,11 @@ export default defineNuxtConfig({
|
|
|
104
106
|
// (avoids /_vercel/image which has no source images in storybook-static/)
|
|
105
107
|
provider: process.env.STORYBOOK ? "none" : undefined,
|
|
106
108
|
},
|
|
109
|
+
vite: {
|
|
110
|
+
optimizeDeps: {
|
|
111
|
+
include: ["@oddbird/css-anchor-positioning"],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
107
114
|
vue: {
|
|
108
115
|
runtimeCompiler: true,
|
|
109
116
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcdev-nuxt-components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.1.
|
|
4
|
+
"version": "9.1.2",
|
|
5
5
|
"main": "nuxt.config.ts",
|
|
6
6
|
"types": "types.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -84,10 +84,10 @@
|
|
|
84
84
|
"@iconify-json/akar-icons": "1.2.7",
|
|
85
85
|
"@iconify-json/bi": "1.2.7",
|
|
86
86
|
"@iconify-json/bitcoin-icons": "1.2.4",
|
|
87
|
-
"@iconify-json/gravity-ui": "1.2.
|
|
87
|
+
"@iconify-json/gravity-ui": "1.2.12",
|
|
88
88
|
"@iconify-json/ic": "*",
|
|
89
89
|
"@iconify-json/lucide": "*",
|
|
90
|
-
"@iconify-json/material-symbols": "1.2.
|
|
90
|
+
"@iconify-json/material-symbols": "1.2.64",
|
|
91
91
|
"@iconify-json/mdi": "1.2.3",
|
|
92
92
|
"@iconify-json/radix-icons": "*",
|
|
93
93
|
"@nuxt/eslint": "1.13.0",
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@storybook/addon-docs": "10.0.7",
|
|
99
99
|
"@storybook/builder-vite": "10.0.7",
|
|
100
100
|
"@vue/test-utils": "2.4.6",
|
|
101
|
+
"@vueuse/core": "14.2.1",
|
|
101
102
|
"eslint": "9.39.2",
|
|
102
103
|
"eslint-plugin-storybook": "10.0.7",
|
|
103
104
|
"happy-dom": "20.5.0",
|
|
@@ -125,10 +126,9 @@
|
|
|
125
126
|
"@nuxt/image": "2.0.0",
|
|
126
127
|
"@oddbird/css-anchor-positioning": "0.9.0",
|
|
127
128
|
"@pinia/nuxt": "0.11.3",
|
|
128
|
-
"@vueuse/
|
|
129
|
-
"@vueuse/motion": "^3.0.3",
|
|
129
|
+
"@vueuse/motion": "3.0.3",
|
|
130
130
|
"focus-trap-vue": "4.1.0",
|
|
131
|
-
"nuxt": "4.
|
|
131
|
+
"nuxt": "4.4.2",
|
|
132
132
|
"pinia": "3.0.4",
|
|
133
133
|
"pinia-plugin-persistedstate": "4.7.1",
|
|
134
134
|
"zod": "4.3.6"
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
# TreatmentConsultant Component
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
`TreatmentConsultant` is a self-contained 5-step hair consultation wizard (`03.organisms`). It collects a user's hair profile across four steps (hair type, natural colour, dream colour, style & treatments) and renders a personalised recommendation on the final step.
|
|
6
|
-
|
|
7
|
-
All data — hair types, colour swatches, treatments, and the recommendation matrix — is hard-coded inside the component. There are no external data dependencies.
|
|
8
|
-
|
|
9
|
-
## Props
|
|
10
|
-
|
|
11
|
-
| Prop | Type | Default | Purpose |
|
|
12
|
-
|------|------|---------|---------|
|
|
13
|
-
| `autoAdvance` | `boolean` | `false` | Automatically advances to the next step immediately after each selection (steps 0–2). On step 3, a "View Results" button still appears when `allowMultipleTreatments` is also `true`. |
|
|
14
|
-
| `allowMultipleTreatments` | `boolean` | `false` | Enables multi-select on the treatments step. When `false`, selecting a treatment deselects any previous choice. |
|
|
15
|
-
| `styleClassPassthrough` | `string \| string[]` | `[]` | Standard passthrough prop for HOC styling. |
|
|
16
|
-
|
|
17
|
-
## Basic usage
|
|
18
|
-
|
|
19
|
-
```vue
|
|
20
|
-
<TreatmentConsultant :auto-advance="true" :allow-multiple-treatments="true" />
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Steps
|
|
24
|
-
|
|
25
|
-
| Index | Label | Behaviour |
|
|
26
|
-
|-------|-------|-----------|
|
|
27
|
-
| 0 | Hair Type | Single select — straight, wavy, curly, coily |
|
|
28
|
-
| 1 | Your Colour | Single select — 7 natural colour swatches |
|
|
29
|
-
| 2 | Dream Colour | Single select — 8 desired colours (incl. "none") |
|
|
30
|
-
| 3 | Style & Treatments | Single or multi-select (see `allowMultipleTreatments`) |
|
|
31
|
-
| 4 | Results | Read-only — recommendation, treatment cards, summary, CTA |
|
|
32
|
-
|
|
33
|
-
## Navigation rules
|
|
34
|
-
|
|
35
|
-
- Completed steps (index < current) are clickable to go back.
|
|
36
|
-
- Future steps are disabled via `aria-disabled` + `tabindex="-1"`.
|
|
37
|
-
- Steps 0–2 require a selection before proceeding (`canProceed`).
|
|
38
|
-
- Step 3 is always passable — treatments are optional.
|
|
39
|
-
- `reset()` clears all state and returns to step 0.
|
|
40
|
-
|
|
41
|
-
## Treatment selection logic
|
|
42
|
-
|
|
43
|
-
- Selecting `"none"` clears all other treatments (and vice versa).
|
|
44
|
-
- In multi-select mode, selecting a treatment automatically removes any conflicting ones (defined by each treatment's `excludes` array).
|
|
45
|
-
- Conflicting treatments are visually marked with a `lucide:ban` icon and a "Conflicts with X" label.
|
|
46
|
-
|
|
47
|
-
## Treatment–Colour compatibility
|
|
48
|
-
|
|
49
|
-
Some treatments clash with same-day colour services: `keratin-smoothing`, `perm`, `relaxer`, `japanese-straightening`. These show a "Not same-day as colour" badge in the results view when a colour change was also selected.
|
|
50
|
-
|
|
51
|
-
## Recommendation matrix
|
|
52
|
-
|
|
53
|
-
`getColourRecommendation(naturalColour, desiredColour, hairType)` maps the combination to:
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
interface Recommendation {
|
|
57
|
-
suitability: "great" | "possible" | "difficult" | "not-recommended";
|
|
58
|
-
method: string; // e.g. "Semi-Permanent", "Bleach Required"
|
|
59
|
-
notes: string; // one-line summary
|
|
60
|
-
details: string[]; // bullet points
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Curly or coily hair type appends an extra conditioning note to `details`. Unmapped combinations fall back to `suitability: "possible"` with a consultation prompt.
|
|
65
|
-
|
|
66
|
-
## Image assets
|
|
67
|
-
|
|
68
|
-
Swatch JPEGs live at `public/images/treatment-consultant/`:
|
|
69
|
-
- `swatch-{light-blonde,dark-blonde,light-brown,dark-brown,red,black,grey-white}.jpeg` (natural colours)
|
|
70
|
-
- `swatch-dream-{blonde,brown,red,black,grey-silver,vivid,balayage}.jpeg` (desired colours)
|
|
71
|
-
|
|
72
|
-
All `NuxtImg` usages include explicit `width` and `height` (128×128 in options, 96×96 in results summary).
|
|
73
|
-
|
|
74
|
-
## Styling
|
|
75
|
-
|
|
76
|
-
Amber-toned dark theme. Tokens scoped to `.treatment-consultant`:
|
|
77
|
-
|
|
78
|
-
```css
|
|
79
|
-
.treatment-consultant {
|
|
80
|
-
--_canvas-color: var(--amber-09);
|
|
81
|
-
--_canvas-text: var(--amber-02);
|
|
82
|
-
--_surface-active: var(--amber-10);
|
|
83
|
-
--_surface-checked: var(--green-09);
|
|
84
|
-
--_surface-excluded: color-mix(in srgb, var(--red-07) 10%, transparent);
|
|
85
|
-
/* … */
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Fonts: Inter (body), Playfair Display (accent).
|
|
90
|
-
Step transitions: `<Transition name="slide" mode="out-in">`.
|
|
91
|
-
Results cards use `v-motion` with staggered enter animations.
|
|
92
|
-
|
|
93
|
-
## Local style override scaffold
|
|
94
|
-
|
|
95
|
-
```vue
|
|
96
|
-
<TreatmentConsultant :style-class-passthrough="['my-consultant']" />
|
|
97
|
-
|
|
98
|
-
<style>
|
|
99
|
-
/* ─── TreatmentConsultant local overrides ──────────────────────────
|
|
100
|
-
Colours, borders, geometry only — do not override behaviour.
|
|
101
|
-
Delete this block if no overrides are needed.
|
|
102
|
-
─────────────────────────────────────────────────────────────────── */
|
|
103
|
-
.treatment-consultant {
|
|
104
|
-
&.my-consultant {
|
|
105
|
-
/* Canvas background */
|
|
106
|
-
/* --_canvas-color: var(--brand-surface); */
|
|
107
|
-
|
|
108
|
-
/* Option selected state */
|
|
109
|
-
/* --_surface-checked: var(--brand-accent); */
|
|
110
|
-
/* --_border-checked: var(--brand-accent-border); */
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
</style>
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
See [component-local-style-override.md](../component-local-style-override.md) for the full pattern.
|
|
117
|
-
|
|
118
|
-
## CTA (results step)
|
|
119
|
-
|
|
120
|
-
- "Book Consultation" links to `/#contact` (hardcoded anchor).
|
|
121
|
-
- "Start Again" calls `reset()`.
|
|
122
|
-
|
|
123
|
-
## Notes
|
|
124
|
-
|
|
125
|
-
- Component is auto-imported in Nuxt — no import needed.
|
|
126
|
-
- No slots — entirely self-contained UI and data.
|
|
127
|
-
- `styleClassPassthrough` is accepted but not currently wired to `useStyleClassPassthrough` inside the component.
|
|
128
|
-
- Storybook story at `Organisms/TreatmentConsultant` — controls for both props.
|