srcdev-nuxt-components 9.6.2 → 9.6.3
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/rules/consumer/consumer-overrides.md +17 -0
- package/.claude/rules/consumer/css-comment-slash.md +13 -0
- package/.claude/rules/consumer/css-native-nesting.md +13 -0
- package/.claude/rules/consumer/css-no-deep.md +13 -0
- package/.claude/rules/consumer/css-rem-base.md +11 -0
- package/.claude/rules/consumer/css-surface-text-pairing.md +13 -0
- package/.claude/rules/consumer/css-webkit-support.md +15 -0
- package/.claude/rules/consumer/explicit-vue-imports.md +11 -0
- package/.claude/rules/consumer/nuxt-img-dimensions.md +12 -0
- package/.claude/rules/consumer/srcdev-skills-location.md +9 -0
- package/.claude/rules/consumer/story-args.md +12 -0
- package/.claude/rules/consumer/story-register-components.md +12 -0
- package/.claude/rules/consumer/vue-auto-imports.md +14 -0
- package/.claude/rules/consumer/vue-no-self-closing.md +11 -0
- package/.claude/rules/consumer/vue-prop-hyphenation.md +11 -0
- package/.claude/rules/consumer/vue-props-pattern.md +13 -0
- package/.claude/rules/consumer/vue-slots-no-computed.md +14 -0
- package/.claude/rules/consumer/vue-video-source.md +12 -0
- package/.claude/skills/component-compliance-checklist.md +23 -1
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/pricing-card.md +68 -84
- package/.claude/skills/index.md +9 -3
- package/.claude/skills/new-app-scaffold.md +4 -2
- package/.claude/skills/setup-postinstall.md +5 -4
- package/.claude/skills/storybook-add-story.md +36 -0
- package/.claude/skills/theming-component-token-pattern.md +46 -0
- package/.claude/skills/theming-partial-override.md +1 -1
- package/.vscode/srcdev-component-pricing-card.code-snippets +46 -84
- package/README.md +5 -5
- package/app/components/01.atoms/animations/scroll-reveal-frame/stories/ScrollRevealFrame.stories.ts +2 -0
- package/app/components/01.atoms/animations/scroll-reveal-image/stories/ScrollRevealImage.stories.ts +2 -0
- package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts +4 -0
- package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts +2 -0
- package/app/components/02.molecules/cookie-consent-banner/stories/CookieConsentBanner.stories.ts +2 -0
- package/app/components/02.molecules/input-copy/stories/InputCopy.stories.ts +0 -1
- package/app/components/02.molecules/pricing-card/CONSUMER-STYLING.md +96 -149
- package/app/components/02.molecules/pricing-card/PricingCard.vue +84 -44
- package/app/components/02.molecules/pricing-card/stories/PricingCard.stories.ts +241 -104
- package/app/components/02.molecules/pricing-card/tests/PricingCard.spec.ts +149 -123
- package/app/components/02.molecules/toast/stories/DisplayToast.stories.ts +4 -0
- package/app/components/02.molecules/toast/stories/DisplayToastProvider.stories.ts +4 -0
- package/app/components/05.forms/toggle-switch/ToggleSwitchWithLabel.vue +1 -1
- package/app/composables/useAriaDescribedById.ts +1 -1
- package/package.json +2 -1
|
@@ -287,6 +287,29 @@ prop and try to pass it. Make it unmistakable:
|
|
|
287
287
|
|
|
288
288
|
`PriceList.stories.ts` and `GoogleReviews.stories.ts` (line-clamp controls) follow this.
|
|
289
289
|
|
|
290
|
+
## Docs page
|
|
291
|
+
|
|
292
|
+
`.storybook/preview.ts` sets `tags: ["autodocs"]` globally (2026-10-14), so every story file gets a
|
|
293
|
+
generated Docs page and the meta doesn't need the tag. Before that, only two files opted in, and
|
|
294
|
+
the earlier nightly `@nuxtjs/storybook` didn't render Docs pages at all, so a lot of existing
|
|
295
|
+
`docs.description` text had never been visible. Check the page whenever you add or change a story
|
|
296
|
+
(compliance checklist item 6c):
|
|
297
|
+
|
|
298
|
+
- `parameters.docs.description.component` in the meta: what the component is for and its main
|
|
299
|
+
props, slots and events. `docs.description.story` on a story: what that story shows.
|
|
300
|
+
- The Docs page renders every story inline in one document. Overlays (`position: fixed`), dialogs,
|
|
301
|
+
toasts, fullscreen canvases and scroll-driven effects break or overlap there. Render them in
|
|
302
|
+
their own iframe instead:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
parameters: {
|
|
306
|
+
docs: { story: { inline: false, height: "480px" } },
|
|
307
|
+
},
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
- Opt a file out with `tags: ["!autodocs"]` only when it isn't a component reference (tooling, or
|
|
311
|
+
a file with its own attached `.mdx` page, like `ColourRamps.stories.ts` with `SetupGuide.mdx`).
|
|
312
|
+
|
|
290
313
|
## Link to the stories of components that use this one
|
|
291
314
|
|
|
292
315
|
When the component you're working on is a building block of other components (it's rendered
|
|
@@ -362,6 +385,19 @@ story: "See the [CookieConsentBanner](?path=/story/molecules-cookieconsentbanner
|
|
|
362
385
|
controls that make sense (hide irrelevant ones with `Story.argTypes = { x: { table: { disable: true } } }`),
|
|
363
386
|
or, for a fixed showcase (comparisons, all-themes grids, composition examples), add
|
|
364
387
|
`controls: { disable: true }` to that story's `parameters`.
|
|
388
|
+
- **Sidebar order** comes from the `storySort` function in `.storybook/preview.ts`: top-level
|
|
389
|
+
groups in tier order, everything below them alphabetical, entries within one component in file
|
|
390
|
+
order with Docs first. Storybook doesn't run that function from the compiled file. It extracts
|
|
391
|
+
the function's source and `eval`s it as plain JavaScript, so any TypeScript inside it
|
|
392
|
+
(annotations, `as` casts) or a reference to something outside its body fails the whole preview
|
|
393
|
+
build with `SyntaxError: Unexpected token ':'` (found 2026-10-15). It stays annotation-free
|
|
394
|
+
under a `// @ts-expect-error`. Check an edit without starting Storybook:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
node --input-type=module -e 'import { getStorySortParameter } from "storybook/internal/csf-tools"; import fs from "node:fs"; console.log(typeof getStorySortParameter(fs.readFileSync(".storybook/preview.ts", "utf8")));'
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
`function` means it parses; a syntax error is the same one Storybook would hit.
|
|
365
401
|
|
|
366
402
|
## Scroll/animation-driven effects need surrounding chrome, not just the bare component
|
|
367
403
|
|
|
@@ -42,6 +42,52 @@ overrides (2026-08-25):
|
|
|
42
42
|
Tier 1 is the one deliberate exception. Everything else gets a real component-prefixed name with
|
|
43
43
|
an inline fallback, even if that fallback happens to be a Tier 1 or Tier 2 token underneath.
|
|
44
44
|
|
|
45
|
+
## Public vs private tokens
|
|
46
|
+
|
|
47
|
+
Rule: `.claude/rules/css-public-private-tokens.md`.
|
|
48
|
+
|
|
49
|
+
The public/private decision is about **consumer relevance, not reuse
|
|
50
|
+
count**. A value a consumer could plausibly want to customise — colours, spacing, sizes, radii,
|
|
51
|
+
durations, and any per-state variant of these (a hover outline width, a focus border colour) — is
|
|
52
|
+
a **public token**, consumed directly at its point of use: `border-radius: var(--component-name-border-radius, 0.5rem)`.
|
|
53
|
+
This holds even when it's only used once — single-use is not a reason to demote a genuinely
|
|
54
|
+
consumer-facing value to private. Per-state variants get their own public token that falls back
|
|
55
|
+
to the resting one, rather than being derived privately: `--component-name-outline-width-hover`
|
|
56
|
+
falling back to `--component-name-outline-width`, not a private var with no override hook.
|
|
57
|
+
|
|
58
|
+
A value with **no plausible consumer relevance** — genuine internal implementation detail, e.g.
|
|
59
|
+
something composed from a `v-bind()` JS value, or an intermediate that exists purely to avoid
|
|
60
|
+
repeating a computation — is a **private token**, always prefixed `--_x`. Prefix it even when
|
|
61
|
+
it's used only once: the underscore is a grep-able, at-a-glance "this is not public API" signal,
|
|
62
|
+
and that signal shouldn't depend on how many places the value happens to appear.
|
|
63
|
+
|
|
64
|
+
```css
|
|
65
|
+
/* ✅ Single-use, consumer-facing — still a public token, consumed directly */
|
|
66
|
+
.component-name {
|
|
67
|
+
border-radius: var(--component-name-border-radius, 0.5rem);
|
|
68
|
+
transition: all var(--component-name-transition-duration, 300ms) ease;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ✅ Resting/hover pair — both public, hover falls back to resting; --_ only wraps the
|
|
72
|
+
genuinely-shared intermediate so the fallback chain isn't repeated */
|
|
73
|
+
.component-name {
|
|
74
|
+
--_border-colour: var(--component-name-border-colour, transparent);
|
|
75
|
+
border-color: var(--_border-colour);
|
|
76
|
+
|
|
77
|
+
&:hover {
|
|
78
|
+
border-color: var(--component-name-border-colour-hover, var(--_border-colour));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ✅ Private — internal only, no consumer would ever want to override this directly */
|
|
83
|
+
.component-name {
|
|
84
|
+
--_computed-offset: calc(v-bind(dynamicWidth) * 0.5px);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
CLAUDE.md pitfall #20 is the concrete case (`ServicesCard`'s private layer wrapped public tokens with
|
|
89
|
+
no composition or state-swap behind them: pure pass-through duplication, not genuine privacy).
|
|
90
|
+
|
|
45
91
|
## Naming convention
|
|
46
92
|
|
|
47
93
|
`--{component-kebab-name}-{variant-if-any}-{property}[-state]`
|
|
@@ -47,7 +47,7 @@ affects buttons, inputs, prompts, and toasts simultaneously:
|
|
|
47
47
|
| `--theme-border-focus` | `--colour-theme-4` / `3` | Focused border |
|
|
48
48
|
| `--theme-ring` | `--colour-theme-1` / `9` | Focus ring (outline) |
|
|
49
49
|
| `--theme-on-surface` | `--colour-theme-0` | Text/icon on filled/inverted surface (fixed light value in both modes — pair this with any dark-in-light-mode surface, not `--theme-text`) |
|
|
50
|
-
| `--theme-text` | `--colour-theme-9` / `2` | Text on page, outline element text (assumes a *light* surface — don't pair with `--theme-surface-hover`/`-inverted`, see pitfall #13 in `
|
|
50
|
+
| `--theme-text` | `--colour-theme-9` / `2` | Text on page, outline element text (assumes a *light* surface — don't pair with `--theme-surface-hover`/`-inverted`, see pitfall #13 in `CLAUDE.md`) |
|
|
51
51
|
| `--theme-text-inverted` | `--colour-theme-0` / `7` | Text on `--theme-surface-inverted` (fixed to a light value in light mode as of 2026-08-03 — was `--colour-theme-9`, identical to the surface it sits on) |
|
|
52
52
|
|
|
53
53
|
Input-specific tokens:
|
|
@@ -4,97 +4,65 @@
|
|
|
4
4
|
"scope": "vue,html",
|
|
5
5
|
"body": [
|
|
6
6
|
"<PricingCard",
|
|
7
|
-
"
|
|
7
|
+
" plan-name=\"$1Single Site\"",
|
|
8
8
|
" :price=\"$2199\"",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
" '$7Feature 3',",
|
|
15
|
-
" ]\"",
|
|
16
|
-
" @select=\"$8handleSelect\"",
|
|
9
|
+
" currency-symbol=\"$3£\"",
|
|
10
|
+
" billing-period=\"$4one-time\"",
|
|
11
|
+
" description=\"$5Perfect for freelancers and solo practitioners.\"",
|
|
12
|
+
" :features=\"['$6Feature 1', '$7Feature 2', '$8Feature 3']\"",
|
|
13
|
+
" @select=\"$9handleSelect\"",
|
|
17
14
|
"/>"
|
|
18
15
|
]
|
|
19
16
|
},
|
|
20
17
|
"SRCDEV PricingCard Highlighted": {
|
|
21
|
-
"description": "PricingCard with 'Most Popular' highlight
|
|
18
|
+
"description": "PricingCard with 'Most Popular' highlight",
|
|
22
19
|
"scope": "vue,html",
|
|
23
20
|
"body": [
|
|
24
21
|
"<PricingCard",
|
|
25
|
-
"
|
|
22
|
+
" plan-name=\"$1Agency\"",
|
|
26
23
|
" :price=\"$2499\"",
|
|
27
|
-
"
|
|
24
|
+
" billing-period=\"$3one-time\"",
|
|
28
25
|
" description=\"$4Best for growing teams.\"",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
" ]\"",
|
|
35
|
-
" @select=\"$8handleSelect\"",
|
|
36
|
-
">",
|
|
37
|
-
" <template #cta=\"{ ctaText, isDisabled, onSelect }\">",
|
|
38
|
-
" <InputButton",
|
|
39
|
-
" :button-text=\"ctaText\"",
|
|
40
|
-
" variant=\"$9primary\"",
|
|
41
|
-
" :readonly=\"isDisabled\"",
|
|
42
|
-
" @click=\"onSelect\"",
|
|
43
|
-
" />",
|
|
44
|
-
" </template>",
|
|
45
|
-
"</PricingCard>"
|
|
26
|
+
" is-highlighted",
|
|
27
|
+
" badge-text=\"$5Most Popular\"",
|
|
28
|
+
" :features=\"['$6Feature 1', '$7Feature 2', '$8Feature 3']\"",
|
|
29
|
+
" @select=\"$9handleSelect\"",
|
|
30
|
+
"/>"
|
|
46
31
|
]
|
|
47
32
|
},
|
|
48
33
|
"SRCDEV PricingCard Grid": {
|
|
49
|
-
"description": "
|
|
34
|
+
"description": "Pricing comparison grid from a plans array",
|
|
50
35
|
"scope": "vue,html",
|
|
51
36
|
"body": [
|
|
52
|
-
"<div class=\"
|
|
53
|
-
" <PricingCard",
|
|
54
|
-
" planName=\"Single Site\"",
|
|
55
|
-
" :price=\"$1199\"",
|
|
56
|
-
" billingPeriod=\"one-time\"",
|
|
57
|
-
" description=\"$2For freelancers.\"",
|
|
58
|
-
" :features=\"['$3Feature 1', '$4Feature 2']\"",
|
|
59
|
-
" @select=\"handleSelect\"",
|
|
60
|
-
" />",
|
|
61
|
-
" <PricingCard",
|
|
62
|
-
" planName=\"Agency\"",
|
|
63
|
-
" :price=\"$5499\"",
|
|
64
|
-
" billingPeriod=\"one-time\"",
|
|
65
|
-
" description=\"$6For growing teams.\"",
|
|
66
|
-
" :is-highlighted=\"true\"",
|
|
67
|
-
" :features=\"['$7Feature 1', '$8Feature 2', '$9Feature 3']\"",
|
|
68
|
-
" @select=\"handleSelect\"",
|
|
69
|
-
" />",
|
|
37
|
+
"<div class=\"$1pricing-grid\">",
|
|
70
38
|
" <PricingCard",
|
|
71
|
-
"
|
|
72
|
-
" :
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
" :
|
|
76
|
-
"
|
|
39
|
+
" v-for=\"plan in $2plans\"",
|
|
40
|
+
" :key=\"plan.id\"",
|
|
41
|
+
" :plan-name=\"plan.name\"",
|
|
42
|
+
" :price=\"plan.price\"",
|
|
43
|
+
" :description=\"plan.description\"",
|
|
44
|
+
" :features=\"plan.features\"",
|
|
45
|
+
" :is-highlighted=\"plan.id === '$3pro'\"",
|
|
46
|
+
" heading-tag=\"$4h2\"",
|
|
47
|
+
" @select=\"$5handleSelect\"",
|
|
77
48
|
" />",
|
|
78
49
|
"</div>"
|
|
79
50
|
]
|
|
80
51
|
},
|
|
81
52
|
"SRCDEV PricingCard Custom Button": {
|
|
82
|
-
"description": "PricingCard with custom button
|
|
53
|
+
"description": "PricingCard with custom button via #cta slot",
|
|
83
54
|
"scope": "vue,html",
|
|
84
55
|
"body": [
|
|
85
56
|
"<PricingCard",
|
|
86
|
-
"
|
|
57
|
+
" plan-name=\"$1Premium\"",
|
|
87
58
|
" :price=\"$2799\"",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
" :features=\"['$5Feature 1', '$6Feature 2']\"",
|
|
91
|
-
" @select=\"$7handleSelect\"",
|
|
59
|
+
" :features=\"['$3Feature 1', '$4Feature 2']\"",
|
|
60
|
+
" @select=\"$5handleSelect\"",
|
|
92
61
|
">",
|
|
93
62
|
" <template #cta=\"{ ctaText, isDisabled, onSelect }\">",
|
|
94
63
|
" <InputButton",
|
|
95
64
|
" :button-text=\"ctaText\"",
|
|
96
|
-
" variant=\"$
|
|
97
|
-
" theme=\"$9default\"",
|
|
65
|
+
" variant=\"$6secondary\"",
|
|
98
66
|
" :readonly=\"isDisabled\"",
|
|
99
67
|
" @click=\"onSelect\"",
|
|
100
68
|
" />",
|
|
@@ -107,17 +75,12 @@
|
|
|
107
75
|
"scope": "vue,html",
|
|
108
76
|
"body": [
|
|
109
77
|
"<PricingCard",
|
|
110
|
-
"
|
|
78
|
+
" plan-name=\"$1Annual\"",
|
|
111
79
|
" :price=\"$249\"",
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
" '$6Feature 1',",
|
|
117
|
-
" '$7Feature 2',",
|
|
118
|
-
" '$8Feature 3',",
|
|
119
|
-
" ]\"",
|
|
120
|
-
" @select=\"$9handleSelect\"",
|
|
80
|
+
" billing-period=\"$3per year\"",
|
|
81
|
+
" ribbon-text=\"$4EARLYBIRD50 · 50% off\"",
|
|
82
|
+
" :features=\"['$5Feature 1', '$6Feature 2']\"",
|
|
83
|
+
" @select=\"$7handleSelect\"",
|
|
121
84
|
"/>"
|
|
122
85
|
]
|
|
123
86
|
},
|
|
@@ -127,21 +90,20 @@
|
|
|
127
90
|
"body": [
|
|
128
91
|
".$1pricing-page {",
|
|
129
92
|
" .pricing-card {",
|
|
130
|
-
" --pricing-card-
|
|
131
|
-
" --pricing-card-
|
|
132
|
-
" --pricing-card-
|
|
133
|
-
" --pricing-card-
|
|
93
|
+
" --pricing-card-background: $2;",
|
|
94
|
+
" --pricing-card-border-radius: $3;",
|
|
95
|
+
" --pricing-card-padding: $4;",
|
|
96
|
+
" --pricing-card-gap: $5;",
|
|
134
97
|
" --pricing-card-name-color: $6;",
|
|
135
|
-
" --pricing-card-
|
|
136
|
-
" --pricing-card-
|
|
137
|
-
" --pricing-card-
|
|
138
|
-
" --
|
|
139
|
-
" --pricing-card-ribbon-text: $11;",
|
|
98
|
+
" --pricing-card-description-line-clamp: $7;",
|
|
99
|
+
" --pricing-card-feature-icon-color: $8;",
|
|
100
|
+
" --pricing-card-ribbon-bg: $9;",
|
|
101
|
+
" --input-button-primary-surface: $10;",
|
|
140
102
|
"",
|
|
141
103
|
" &.is-highlighted {",
|
|
142
|
-
" --pricing-card-highlight-border: $
|
|
143
|
-
" --pricing-card-highlight-
|
|
144
|
-
" --pricing-card-
|
|
104
|
+
" --pricing-card-highlight-border: $11;",
|
|
105
|
+
" --pricing-card-highlight-scale: $12;",
|
|
106
|
+
" --pricing-card-badge-bg: $13;",
|
|
145
107
|
" }",
|
|
146
108
|
" }",
|
|
147
109
|
"}"
|
package/README.md
CHANGED
|
@@ -25,12 +25,12 @@ defineNuxtConfig({
|
|
|
25
25
|
|
|
26
26
|
## Claude Code Skills
|
|
27
27
|
|
|
28
|
-
This package ships Claude Code skills
|
|
28
|
+
This package ships Claude Code skills (reference docs for components and development tasks) and a curated set of Claude Code rules (short constraints for working with the layer: CSS nesting, overrides, props, NuxtImg, Storybook args) in the `.claude/` directory.
|
|
29
29
|
|
|
30
30
|
To make them available in your project, add this script to your `package.json`:
|
|
31
31
|
|
|
32
32
|
```json
|
|
33
|
-
"setup:claude": "cp -r node_modules/srcdev-nuxt-components/.claude/skills .claude/skills/srcdev-nuxt-components"
|
|
33
|
+
"setup:claude": "rm -rf .claude/skills/srcdev-nuxt-components .claude/rules/srcdev-nuxt-components && mkdir -p .claude/skills/srcdev-nuxt-components .claude/rules/srcdev-nuxt-components && cp -r node_modules/srcdev-nuxt-components/.claude/skills/. .claude/skills/srcdev-nuxt-components && cp -r node_modules/srcdev-nuxt-components/.claude/rules/consumer/. .claude/rules/srcdev-nuxt-components"
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Then run it after install:
|
|
@@ -39,7 +39,7 @@ Then run it after install:
|
|
|
39
39
|
npm run setup:claude
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Skills are copied into `.claude/skills/srcdev-nuxt-components/` so they never conflict with or overwrite
|
|
42
|
+
Skills are copied into `.claude/skills/srcdev-nuxt-components/` and rules into `.claude/rules/srcdev-nuxt-components/`, so they never conflict with or overwrite your own project's. Re-running the script after a package update is safe: it replaces only those two `srcdev-nuxt-components` folders, so skills and rules dropped or renamed upstream are removed too, and your own skills and rules are never touched.
|
|
43
43
|
|
|
44
44
|
### Automate with postinstall (recommended)
|
|
45
45
|
|
|
@@ -47,7 +47,7 @@ To ensure skills are always up to date, VSCode snippets are installed, and `nuxt
|
|
|
47
47
|
|
|
48
48
|
```json
|
|
49
49
|
"scripts": {
|
|
50
|
-
"setup:claude": "mkdir -p .claude/skills/srcdev-nuxt-components && cp -r node_modules/srcdev-nuxt-components/.claude/skills/. .claude/skills/srcdev-nuxt-components",
|
|
50
|
+
"setup:claude": "rm -rf .claude/skills/srcdev-nuxt-components .claude/rules/srcdev-nuxt-components && mkdir -p .claude/skills/srcdev-nuxt-components .claude/rules/srcdev-nuxt-components && cp -r node_modules/srcdev-nuxt-components/.claude/skills/. .claude/skills/srcdev-nuxt-components && cp -r node_modules/srcdev-nuxt-components/.claude/rules/consumer/. .claude/rules/srcdev-nuxt-components",
|
|
51
51
|
"postinstall": "node node_modules/srcdev-nuxt-components/scripts/copy-snippets.mjs && nuxt prepare && npm run setup:claude"
|
|
52
52
|
}
|
|
53
53
|
```
|
|
@@ -56,7 +56,7 @@ To ensure skills are always up to date, VSCode snippets are installed, and `nuxt
|
|
|
56
56
|
|
|
57
57
|
1. `copy-snippets.mjs` — copies VSCode snippets (`.code-snippets` files) from the layer to your `.vscode/` folder
|
|
58
58
|
2. `nuxt prepare` — generates Nuxt type declarations
|
|
59
|
-
3. `setup:claude` — copies skills into `.claude/skills/srcdev-nuxt-components/`
|
|
59
|
+
3. `setup:claude` — copies skills into `.claude/skills/srcdev-nuxt-components/` and rules into `.claude/rules/srcdev-nuxt-components/`
|
|
60
60
|
|
|
61
61
|
> **Note**: The snippet copy must run from your consumer app's postinstall, not the layer's. npm doesn't invoke postinstall hooks for dependencies, so each consumer app is responsible for copying snippets into its own `.vscode/` folder. Don't set `SRCDEV_STANDALONE` in a consumer app: that flag is only for running this layer on its own (it enables dev-only modules and the ramp watcher).
|
|
62
62
|
|
package/app/components/01.atoms/animations/scroll-reveal-frame/stories/ScrollRevealFrame.stories.ts
CHANGED
|
@@ -25,7 +25,9 @@ const meta: Meta<typeof ScrollRevealFrame> = {
|
|
|
25
25
|
},
|
|
26
26
|
},
|
|
27
27
|
parameters: {
|
|
28
|
+
// Own iframe on the Docs page: the effect depends on page scroll.
|
|
28
29
|
docs: {
|
|
30
|
+
story: { inline: false, height: "600px" },
|
|
29
31
|
description: {
|
|
30
32
|
component:
|
|
31
33
|
"Generic clipping frame that pans any slot content vertically as it scrolls through the viewport — driven entirely by CSS Scroll-driven Animations. No scroll listeners, no JS. Use this when you need the parallax reveal with arbitrary content (grids, video, etc.). For a single NuxtImg with optimisation and focal-point control, use ScrollRevealImage instead.",
|
package/app/components/01.atoms/animations/scroll-reveal-image/stories/ScrollRevealImage.stories.ts
CHANGED
|
@@ -50,7 +50,9 @@ const meta: Meta<typeof ScrollRevealImage> = {
|
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
52
|
parameters: {
|
|
53
|
+
// Own iframe on the Docs page: the effect depends on page scroll.
|
|
53
54
|
docs: {
|
|
55
|
+
story: { inline: false, height: "600px" },
|
|
54
56
|
description: {
|
|
55
57
|
component:
|
|
56
58
|
"Single-image parallax reveal built on ScrollRevealFrame. Adds NuxtImg optimisation (src, alt, intrinsic dimensions) and a horizontal focal-point prop (focalX). For arbitrary slot content — grids, video, etc. — use ScrollRevealFrame directly.",
|
package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts
CHANGED
|
@@ -45,6 +45,10 @@ export default {
|
|
|
45
45
|
showOverlay: true,
|
|
46
46
|
overlayText: "A quiet moment.",
|
|
47
47
|
},
|
|
48
|
+
parameters: {
|
|
49
|
+
// Own iframe on the Docs page: the effect depends on page scroll and vh heights.
|
|
50
|
+
docs: { story: { inline: false, height: "600px" } },
|
|
51
|
+
},
|
|
48
52
|
} as Meta<StoryArgs>;
|
|
49
53
|
|
|
50
54
|
const Template: StoryFn<StoryArgs> = (args) => ({
|
package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts
CHANGED
|
@@ -20,7 +20,9 @@ const meta: Meta<typeof WipeAwayVertical> = {
|
|
|
20
20
|
styleClassPassthrough: { table: { disable: true } },
|
|
21
21
|
},
|
|
22
22
|
parameters: {
|
|
23
|
+
// Own iframe on the Docs page: the effect depends on page scroll and vh heights.
|
|
23
24
|
docs: {
|
|
25
|
+
story: { inline: false, height: "600px" },
|
|
24
26
|
description: {
|
|
25
27
|
component:
|
|
26
28
|
"Scroll-driven vertical wipe effect: each sticky panel wipes away to reveal the next as its paired scrolling section crosses the viewport. Pure CSS — a single-column grid overlays `.sticky-items-container` across all `.scrolling-section` rows via `animation-timeline: view()`, so there's no scroll listener or JS measurement involved. Consumers supply `stickyItem-{n}` slots for each of `itemCount` panels, and `scrollingItem-{n}` slots for only the first `itemCount - 1` — the last panel has nothing left to reveal, so it has no wipe animation or scrolling section of its own, just a small built-in trailing buffer to release cleanly. The sticky panel's own visible height is `--wipe-away-vertical-height` (default `100vh`) — leave the root's own `height` unset (`auto`) so it sizes itself from its children. Browsers without `animation-timeline: view()` support (via `@supports not (...)`) just get the sticky items stacked in normal document flow instead.",
|
package/app/components/02.molecules/cookie-consent-banner/stories/CookieConsentBanner.stories.ts
CHANGED
|
@@ -40,7 +40,9 @@ export default {
|
|
|
40
40
|
styleClassPassthrough: [],
|
|
41
41
|
},
|
|
42
42
|
parameters: {
|
|
43
|
+
// Own iframe on the Docs page: the banner is fixed to the viewport.
|
|
43
44
|
docs: {
|
|
45
|
+
story: { inline: false, height: "400px" },
|
|
44
46
|
description: {
|
|
45
47
|
// Drives itself from the real useCookieConsent()/useCookie() state, so
|
|
46
48
|
// once accepted/rejected in this browser it stays hidden across story
|