srcdev-nuxt-components 9.2.3 → 9.2.4
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/skills/components/input-copy.md +169 -0
- package/.claude/skills/components/pricing-card.md +163 -0
- package/.claude/skills/index.md +3 -1
- package/.claude/skills/setup-postinstall.md +5 -2
- package/.claude/skills/using-component-skills.md +215 -0
- package/.vscode/srcdev-component-pricing-card.code-snippets +35 -2
- package/package.json +2 -1
- package/scripts/copy-snippets.mjs +38 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# InputCopy Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`InputCopy` renders a readonly text input with a copy-to-clipboard button. Displays a copyable value (e.g., license key, API token) with optional description text below. Shows visual feedback ("Copied!") in the button after a successful copy. Emits `copy` and `copied` events for consumer-side toast/feedback handling.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Props reference
|
|
10
|
+
|
|
11
|
+
> **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:style-class-passthrough`.
|
|
12
|
+
|
|
13
|
+
| Prop (template form) | Type | Default | Notes |
|
|
14
|
+
| -------------------------- | ---------------------- | ---------------- | ------------------------------------------------------------ |
|
|
15
|
+
| `:value` | `string` | (required) | The text to copy to clipboard (e.g., license key, API token).|
|
|
16
|
+
| `:label` | `string` | (optional) | Short label for the field; used in aria-label if ariaLabel not set. |
|
|
17
|
+
| `:aria-label` | `string` | (optional) | Custom aria-label for the input; overrides label. |
|
|
18
|
+
| `:description` | `string` | (optional) | Helper text displayed below the input. |
|
|
19
|
+
| `:button-text` | `string` | `"Copy"` | Label on the copy button. |
|
|
20
|
+
| `:copied-text` | `string` | `"Copied!"` | Text shown in button after copy succeeds. |
|
|
21
|
+
| `:copied-duration` | `number` | `2000` | Milliseconds the "Copied!" state persists before resetting. |
|
|
22
|
+
| `:is-disabled` | `boolean` | `false` | Disables the copy button. |
|
|
23
|
+
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the root element. |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Events
|
|
28
|
+
|
|
29
|
+
| Event | Payload | Notes |
|
|
30
|
+
| -------- | -------------------- | ---------------------------------------------------------- |
|
|
31
|
+
| `copy` | `value: string` | Emitted when the copy button is clicked (before clipboard write). |
|
|
32
|
+
| `copied` | `value: string` | Emitted after successful clipboard write; fires after `copy`. |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Slots
|
|
37
|
+
|
|
38
|
+
None. Use events to customize feedback behavior.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## CSS Token Customization
|
|
43
|
+
|
|
44
|
+
All `--input-copy-*` tokens can be overridden at global, page, or instance scope. See `CONSUMER-STYLING.md` in the component directory for full token documentation and examples.
|
|
45
|
+
|
|
46
|
+
**Common tokens:**
|
|
47
|
+
- `--input-copy-input-bg` — input field background
|
|
48
|
+
- `--input-copy-input-border` — input field border
|
|
49
|
+
- `--input-copy-button-bg` — copy button background
|
|
50
|
+
- `--input-copy-button-bg-hover` — button background on hover
|
|
51
|
+
- `--input-copy-description-color` — description text color
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Usage Examples
|
|
56
|
+
|
|
57
|
+
### License key display (most common)
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<InputCopy
|
|
61
|
+
:value="licenseKey"
|
|
62
|
+
label="License key"
|
|
63
|
+
description="Your license key is ready to use. Copy it to embed in your application."
|
|
64
|
+
@copied="showSuccessToast('License key copied!')"
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### API token with custom button text
|
|
69
|
+
|
|
70
|
+
```vue
|
|
71
|
+
<InputCopy
|
|
72
|
+
:value="apiToken"
|
|
73
|
+
label="API token"
|
|
74
|
+
description="Do not share this token with anyone."
|
|
75
|
+
button-text="Copy token"
|
|
76
|
+
copied-text="Token copied!"
|
|
77
|
+
@copy="logCopy"
|
|
78
|
+
@copied="logCopied"
|
|
79
|
+
/>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Disabled state (e.g., loading or error)
|
|
83
|
+
|
|
84
|
+
```vue
|
|
85
|
+
<InputCopy
|
|
86
|
+
:value="temporaryCode"
|
|
87
|
+
label="Temporary code"
|
|
88
|
+
button-text="Unavailable"
|
|
89
|
+
:is-disabled="isGenerating"
|
|
90
|
+
description="Code is being generated. Please wait."
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### With custom styling via CSS tokens
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<InputCopy
|
|
98
|
+
:value="shareLink"
|
|
99
|
+
label="Share link"
|
|
100
|
+
description="Share this link with others"
|
|
101
|
+
style="
|
|
102
|
+
--input-copy-input-bg: #f0f0f0;
|
|
103
|
+
--input-copy-button-bg: var(--brand-primary);
|
|
104
|
+
--input-copy-button-text-color: white;
|
|
105
|
+
"
|
|
106
|
+
/>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With page-scoped styling
|
|
110
|
+
|
|
111
|
+
In your page's `<style>` block:
|
|
112
|
+
|
|
113
|
+
```css
|
|
114
|
+
.success-page .input-copy {
|
|
115
|
+
--input-copy-input-border: 2px solid var(--success-color);
|
|
116
|
+
--input-copy-button-bg: var(--success-color);
|
|
117
|
+
--input-copy-description-color: var(--slate-06);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then in the template:
|
|
122
|
+
|
|
123
|
+
```vue
|
|
124
|
+
<div class="success-page">
|
|
125
|
+
<InputCopy
|
|
126
|
+
:value="licenseKey"
|
|
127
|
+
label="Your license key"
|
|
128
|
+
description="Copy this key to activate the component in your application."
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### With toast feedback (consumer-side)
|
|
134
|
+
|
|
135
|
+
```vue
|
|
136
|
+
<template>
|
|
137
|
+
<InputCopy
|
|
138
|
+
:value="apiKey"
|
|
139
|
+
label="API key"
|
|
140
|
+
@copied="handleCopied"
|
|
141
|
+
/>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<script setup>
|
|
145
|
+
const handleCopied = (value: string) => {
|
|
146
|
+
// Use your app's toast library
|
|
147
|
+
useToast().success(`Copied: ${value.slice(0, 8)}...`);
|
|
148
|
+
};
|
|
149
|
+
</script>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Accessibility
|
|
155
|
+
|
|
156
|
+
- Input always has `aria-label` set from `label` or `aria-label` prop
|
|
157
|
+
- Button has descriptive aria-label: "Copy [label] to clipboard"
|
|
158
|
+
- Button is disabled during the "Copied!" feedback window to prevent repeated clicks
|
|
159
|
+
- Icon-only display on mobile (<480px); text shown on desktop for clarity
|
|
160
|
+
- Readonly input has `readonly` attribute; not editable
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Notes
|
|
165
|
+
|
|
166
|
+
- **Clipboard API**: Uses native `navigator.clipboard.writeText()`. Fails silently if unavailable (e.g., insecure context).
|
|
167
|
+
- **Button text hide on mobile**: By default, only the copy icon displays on screens <480px. Text appears on desktop (≥480px).
|
|
168
|
+
- **No toast built-in**: This component does not show toasts. Listen to `@copied` event and use your app's toast library for feedback.
|
|
169
|
+
- **Manual reset**: The "Copied!" state automatically resets after `copied-duration` milliseconds. No manual reset needed.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# PricingCard Component
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`PricingCard` renders a SaaS-style pricing plan card with plan name, price, description, feature list, and call-to-action button. Supports "Most Popular" highlight state with optional badge. Features are highly customizable via CSS token overrides, and the CTA button can be customized via the `#cta` slot for custom variants and themes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Props reference
|
|
10
|
+
|
|
11
|
+
> **Hyphenation rule**: Vue's ESLint config enforces `vue/attribute-hyphenation`. Always write camelCase prop names hyphenated in templates: `:style-class-passthrough`.
|
|
12
|
+
|
|
13
|
+
| Prop (template form) | Type | Default | Notes |
|
|
14
|
+
| -------------------------- | ----------------------------- | ------------ | ------------------------------------------------------------------ |
|
|
15
|
+
| `tag` | `"div" \| "section" \| "article"` | `"article"` | Root element tag for semantic HTML. |
|
|
16
|
+
| `:plan-name` | `string` | (required) | Plan/tier name (e.g., "Basic", "Professional", "Enterprise"). |
|
|
17
|
+
| `:price` | `number` | (required) | Price amount (e.g., `99`, `4.99`). Currency symbol added in template. |
|
|
18
|
+
| `:billing-period` | `string` | `"one-time"` | Billing frequency text (e.g., "monthly", "yearly"). |
|
|
19
|
+
| `:description` | `string` | (optional) | Short plan description. |
|
|
20
|
+
| `:features` | `string[]` | `[]` | Array of feature strings. Rendered with checkmark icons. |
|
|
21
|
+
| `:is-highlighted` | `boolean` | `false` | Highlight state; applies "Most Popular" badge, border, shadow, and scale. |
|
|
22
|
+
| `:cta-text` | `string` | `"Get started"` | Button label text. |
|
|
23
|
+
| `:cta-disabled` | `boolean` | `false` | Disables the CTA button. |
|
|
24
|
+
| `:style-class-passthrough` | `string \| string[]` | `[]` | Extra CSS classes applied to the card root. |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Events
|
|
29
|
+
|
|
30
|
+
| Event | Payload | Notes |
|
|
31
|
+
| ------ | ---------- | ---------------------------------------- |
|
|
32
|
+
| `select` | `planName: string` | Emitted when the CTA button is clicked. |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Slots
|
|
37
|
+
|
|
38
|
+
### Default slot (features)
|
|
39
|
+
|
|
40
|
+
Override the default feature list rendering. Receives no slot scope.
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<PricingCard plan-name="Pro" :price="99">
|
|
44
|
+
<template #features>
|
|
45
|
+
<li>Custom feature HTML here</li>
|
|
46
|
+
</template>
|
|
47
|
+
</PricingCard>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### #cta slot
|
|
51
|
+
|
|
52
|
+
Customize the CTA button. Default renders `InputButtonCore` with theme support.
|
|
53
|
+
|
|
54
|
+
**Slot scope:**
|
|
55
|
+
- `ctaText: string` — button label from `:cta-text` prop
|
|
56
|
+
- `isDisabled: boolean` — button disabled state from `:cta-disabled` prop
|
|
57
|
+
- `planName: string` — plan name for context
|
|
58
|
+
- `onSelect: () => void` — callback to trigger `select` event
|
|
59
|
+
|
|
60
|
+
```vue
|
|
61
|
+
<PricingCard plan-name="Pro" :price="99" @select="handleSelect">
|
|
62
|
+
<template #cta="{ ctaText, isDisabled, onSelect }">
|
|
63
|
+
<InputButtonCore
|
|
64
|
+
:button-text="ctaText"
|
|
65
|
+
variant="secondary"
|
|
66
|
+
theme="custom"
|
|
67
|
+
:readonly="isDisabled"
|
|
68
|
+
@click="onSelect"
|
|
69
|
+
/>
|
|
70
|
+
</template>
|
|
71
|
+
</PricingCard>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## CSS Token Customization
|
|
77
|
+
|
|
78
|
+
All `--pricing-card-*` tokens can be overridden at global, page, or instance scope. See `CONSUMER-STYLING.md` in the component directory for full token documentation and examples.
|
|
79
|
+
|
|
80
|
+
**Common tokens:**
|
|
81
|
+
- `--pricing-card-padding` — card internal spacing
|
|
82
|
+
- `--pricing-card-border-radius` — card corners
|
|
83
|
+
- `--pricing-card-cta-bg` — button background
|
|
84
|
+
- `--pricing-card-highlight-border` — highlighted card border
|
|
85
|
+
- `--pricing-card-name-color` — plan name text color
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Usage Examples
|
|
90
|
+
|
|
91
|
+
### Single card with default button
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<PricingCard
|
|
95
|
+
plan-name="Basic"
|
|
96
|
+
:price="29.99"
|
|
97
|
+
billing-period="monthly"
|
|
98
|
+
description="Perfect for getting started"
|
|
99
|
+
:features="['Feature 1', 'Feature 2', 'Feature 3']"
|
|
100
|
+
cta-text="Get started"
|
|
101
|
+
@select="handleSelect"
|
|
102
|
+
/>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Highlighted "Most Popular" card
|
|
106
|
+
|
|
107
|
+
```vue
|
|
108
|
+
<PricingCard
|
|
109
|
+
plan-name="Professional"
|
|
110
|
+
:price="79.99"
|
|
111
|
+
billing-period="monthly"
|
|
112
|
+
:is-highlighted="true"
|
|
113
|
+
:features="['All Basic features', 'Priority support', 'Advanced analytics']"
|
|
114
|
+
@select="handleSelect"
|
|
115
|
+
/>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Card with custom button variant
|
|
119
|
+
|
|
120
|
+
```vue
|
|
121
|
+
<PricingCard
|
|
122
|
+
plan-name="Enterprise"
|
|
123
|
+
:price="199.99"
|
|
124
|
+
billing-period="monthly"
|
|
125
|
+
description="Everything you need"
|
|
126
|
+
:features="['Unlimited access', 'Dedicated support', 'Custom integrations']"
|
|
127
|
+
@select="handleSelect"
|
|
128
|
+
>
|
|
129
|
+
<template #cta="{ ctaText, isDisabled, onSelect }">
|
|
130
|
+
<InputButtonCore
|
|
131
|
+
:button-text="ctaText"
|
|
132
|
+
variant="primary"
|
|
133
|
+
:readonly="isDisabled"
|
|
134
|
+
@click="onSelect"
|
|
135
|
+
/>
|
|
136
|
+
</template>
|
|
137
|
+
</PricingCard>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Three-column grid with token customization
|
|
141
|
+
|
|
142
|
+
```vue
|
|
143
|
+
<div class="pricing-grid">
|
|
144
|
+
<PricingCard
|
|
145
|
+
v-for="plan in plans"
|
|
146
|
+
:key="plan.id"
|
|
147
|
+
:plan-name="plan.name"
|
|
148
|
+
:price="plan.price"
|
|
149
|
+
:is-highlighted="plan.id === 'pro'"
|
|
150
|
+
:features="plan.features"
|
|
151
|
+
@select="handleSelect"
|
|
152
|
+
style="--pricing-card-padding: 2.5rem; --pricing-card-cta-bg: var(--brand-primary)"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Notes
|
|
160
|
+
|
|
161
|
+
- Feature checkmarks are always rendered with a teal checkmark (`✓`). Customize the color via `--teal-06` or component tokens.
|
|
162
|
+
- The `#cta` slot is preferred over CSS-only button customization when you need variant or theme changes.
|
|
163
|
+
- Highlighted cards apply a 1.05x scale transform — consider z-index when nesting in flex/grid layouts.
|
package/.claude/skills/index.md
CHANGED
|
@@ -48,6 +48,7 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
48
48
|
├── qa-panel.md — collapsible dev-only panel for toggling component props live on a page (demo pages and consuming apps)
|
|
49
49
|
├── release-notes.md — produce release notes as a fenced markdown block from git log
|
|
50
50
|
├── pull-request-description.md — produce a PR description as a fenced markdown block from git diff vs main
|
|
51
|
+
├── using-component-skills.md — discover and use component skills in consumer apps: where skills land, browsing patterns, workflow for deciding build vs. compose
|
|
51
52
|
├── composable-canonical-url.md — useCanonicalUrl: set <link rel="canonical"> from runtimeConfig.public.canonicalHost; layout setup, node types
|
|
52
53
|
├── composable-whatsapp.md — useWhatsApp: open pre-filled wa.me link from form payload; runtime config, security, usage
|
|
53
54
|
├── composable-zod-validation.md — useZodValidation: schema-driven form validation, error binding, submit flow, API error push
|
|
@@ -73,7 +74,8 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
73
74
|
├── expanding-panel.md — ExpandingPanel v-model, forceOpened, slots (summary/icon/content), ARIA wiring
|
|
74
75
|
├── glass-panel.md — GlassPanel props, slots, CSS token API (--glass-panel-bg/border-color/shadow/highlight), theming override
|
|
75
76
|
├── navigation-horizontal.md — NavigationHorizontal props, NavItemData type, CSS token API, import path gotcha
|
|
76
|
-
├──
|
|
77
|
+
├── pricing-card.md — PricingCard: SaaS-style plan card with highlight, feature list, #cta slot for button customization, CSS token API
|
|
78
|
+
├── input-copy.md — InputCopy: readonly copy-to-clipboard input, visual feedback, Clipboard API, accessibility, CSS token API
|
|
77
79
|
├── banner-video.md — BannerVideo: full-width hero video banner, depth tier system, objectFit/objectPosition, playIcon/pauseIcon/toggle-icon slot, reduced-motion fallback, CSS tokens
|
|
78
80
|
├── grid-stack.md — GridStack: CSS Grid z-axis stacking, slot API, z-order rules, sizing, video+overlay and image+text patterns
|
|
79
81
|
├── scroll-reveal-frame.md — ScrollRevealFrame: generic parallax clipping frame, slot API, image grid pattern, CSS tokens, browser support
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
Prevent
|
|
5
|
+
Prevent common "forgot to run after install" problems in a consuming app:
|
|
6
6
|
|
|
7
7
|
1. `nuxt prepare` — generates Nuxt type declarations. Skipping it causes TypeScript errors after install or package updates.
|
|
8
8
|
2. `npm run setup:claude` — copies the latest `srcdev-nuxt-components` skills into `.claude/skills/srcdev-nuxt-components/`. Skipping it leaves Claude working from stale skill docs after a package update.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Additionally, `srcdev-nuxt-components` automatically copies VSCode snippet files (`.code-snippets`) to your `.vscode/` folder during its own postinstall, so components' code snippets are available immediately.
|
|
11
|
+
|
|
12
|
+
A consumer app's `postinstall` script runs both automatically after every `npm install`.
|
|
11
13
|
|
|
12
14
|
## Steps
|
|
13
15
|
|
|
@@ -41,5 +43,6 @@ From this point on, `npm install` and `npm ci` trigger both steps automatically.
|
|
|
41
43
|
## Notes
|
|
42
44
|
|
|
43
45
|
- Skills land in `.claude/skills/srcdev-nuxt-components/` — safe to re-run without overwriting your own project's skills.
|
|
46
|
+
- VSCode snippets are copied to `.vscode/` automatically by the layer's postinstall — no extra step needed on the consumer's side. They're available in VSCode's autocomplete immediately after `npm install`.
|
|
44
47
|
- `postinstall` also fires on `npm ci`, so CI environments get the skills too if they have a `.claude/` directory in the project.
|
|
45
48
|
- If you do not want `postinstall` running in CI, guard it: `"postinstall": "[ \"$CI\" = \"true\" ] || (nuxt prepare && npm run setup:claude)"`.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Using Component Skills
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Component skills are reference documentation files for every reusable component in `srcdev-nuxt-components`. They land in your consumer app after running `npm run setup:claude` and serve two purposes:
|
|
6
|
+
|
|
7
|
+
1. **As human-readable reference** — browse component skills to understand props, slots, CSS tokens, and usage patterns
|
|
8
|
+
2. **As discovery tool** — before building custom components, check if a suitable pattern already exists in the layer
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
### 1. Copy skills from the layer
|
|
15
|
+
|
|
16
|
+
After scaffolding a new Nuxt app that extends `srcdev-nuxt-components`, run:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run setup:claude
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This copies all skills (including component skills) into `.claude/skills/srcdev-nuxt-components/`.
|
|
23
|
+
|
|
24
|
+
### 2. After upgrading the layer
|
|
25
|
+
|
|
26
|
+
When you upgrade `srcdev-nuxt-components` to a new version (which may include new components), re-run the command to refresh:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run setup:claude
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## File structure
|
|
35
|
+
|
|
36
|
+
Component skills land in `.claude/skills/srcdev-nuxt-components/components/`:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
.claude/
|
|
40
|
+
├── skills/
|
|
41
|
+
│ └── srcdev-nuxt-components/
|
|
42
|
+
│ ├── index.md — overview of all skills
|
|
43
|
+
│ ├── new-app-scaffold.md — scaffolding a new app
|
|
44
|
+
│ ├── theming-colour-ramps.md — theme customization
|
|
45
|
+
│ ├── composable-*.md — utility composables
|
|
46
|
+
│ └── components/
|
|
47
|
+
│ ├── pricing-card.md — SaaS pricing plan card
|
|
48
|
+
│ ├── input-copy.md — copy-to-clipboard input
|
|
49
|
+
│ ├── expanding-panel.md — single expand/collapse panel
|
|
50
|
+
│ ├── accordian-core.md — native accordion (grouped panels)
|
|
51
|
+
│ ├── display-avatar.md — circular avatar component
|
|
52
|
+
│ ├── card-core.md — generic card container
|
|
53
|
+
│ ├── auto-grid.md — responsive auto-fit grid
|
|
54
|
+
│ ├── display-dialog.md — native dialog overlay
|
|
55
|
+
│ ├── display-toast.md — toast notifications
|
|
56
|
+
│ └── ... (40+ more components)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Discovering components
|
|
62
|
+
|
|
63
|
+
### Via your file explorer
|
|
64
|
+
|
|
65
|
+
Browse `.claude/skills/srcdev-nuxt-components/components/` to see all available components with one-line descriptions.
|
|
66
|
+
|
|
67
|
+
### Via grep
|
|
68
|
+
|
|
69
|
+
Search for components by pattern:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Find all form-related components
|
|
73
|
+
grep -l "input\|form\|button" .claude/skills/srcdev-nuxt-components/components/*.md
|
|
74
|
+
|
|
75
|
+
# Find components with slot patterns
|
|
76
|
+
grep -l "slot" .claude/skills/srcdev-nuxt-components/components/*.md
|
|
77
|
+
|
|
78
|
+
# Find components with CSS token customization
|
|
79
|
+
grep -l "CSS token" .claude/skills/srcdev-nuxt-components/components/*.md
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Via Claude Code
|
|
83
|
+
|
|
84
|
+
If your `.claude/settings.json` includes the layer's skills in `additionalDirectories`, Claude can reference them:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"permissions": {
|
|
89
|
+
"additionalDirectories": [
|
|
90
|
+
"path/to/srcdev-nuxt-components/.claude/skills/components"
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then ask Claude: *"What button components are available in the layer?"* or *"Show me the expanding-panel skill."*
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Understanding a component skill
|
|
101
|
+
|
|
102
|
+
Each component skill documents:
|
|
103
|
+
|
|
104
|
+
### Props
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Notes |
|
|
107
|
+
| --- | --- | --- | --- |
|
|
108
|
+
| `:prop-name` | type | default | What this prop does |
|
|
109
|
+
|
|
110
|
+
### Slots
|
|
111
|
+
|
|
112
|
+
Explains what content goes inside each slot and provides scoped variables if applicable.
|
|
113
|
+
|
|
114
|
+
### Events / Emits
|
|
115
|
+
|
|
116
|
+
When the component emits events and what data they carry.
|
|
117
|
+
|
|
118
|
+
### CSS Tokens
|
|
119
|
+
|
|
120
|
+
All `--component-*` tokens available for customization via global/page/instance scope.
|
|
121
|
+
|
|
122
|
+
### Usage Examples
|
|
123
|
+
|
|
124
|
+
Practical examples showing:
|
|
125
|
+
- Basic usage (simplest case)
|
|
126
|
+
- Common customizations
|
|
127
|
+
- Advanced patterns (slots, tokens, events)
|
|
128
|
+
|
|
129
|
+
### Accessibility
|
|
130
|
+
|
|
131
|
+
ARIA attributes, keyboard support, screen reader behavior.
|
|
132
|
+
|
|
133
|
+
### Notes
|
|
134
|
+
|
|
135
|
+
Edge cases, browser support, performance considerations, common gotchas.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Workflow: Build a feature
|
|
140
|
+
|
|
141
|
+
### 1. Understand the requirement
|
|
142
|
+
|
|
143
|
+
*"I need a contact form with error handling and field validation."*
|
|
144
|
+
|
|
145
|
+
### 2. Search skills for similar patterns
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
grep -l "form\|input\|validation" .claude/skills/srcdev-nuxt-components/components/*.md
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 3. Read relevant skills
|
|
152
|
+
|
|
153
|
+
Open 2–3 matching skills to see:
|
|
154
|
+
- What components already handle form inputs?
|
|
155
|
+
- What slots and CSS tokens do they expose?
|
|
156
|
+
- What validation patterns are documented?
|
|
157
|
+
|
|
158
|
+
### 4. Decide: build vs. compose
|
|
159
|
+
|
|
160
|
+
**Compose existing components** if the layer has:
|
|
161
|
+
- Input components with validation props
|
|
162
|
+
- Card/container components for layout
|
|
163
|
+
- Button variants for form actions
|
|
164
|
+
|
|
165
|
+
**Build custom component** if you need something specific the layer doesn't provide.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Examples
|
|
170
|
+
|
|
171
|
+
### Pricing page
|
|
172
|
+
|
|
173
|
+
*Task: Build a pricing plan comparison with 3 tiers.*
|
|
174
|
+
|
|
175
|
+
1. Search skills: `grep -l "price\|plan\|tier" components/*.md`
|
|
176
|
+
2. Read `pricing-card.md` — describes PricingCard component, props, #cta slot, CSS tokens
|
|
177
|
+
3. Use PricingCard in a grid layout, customize buttons via slot, override token colors via page-scoped CSS
|
|
178
|
+
|
|
179
|
+
### FAQ section
|
|
180
|
+
|
|
181
|
+
*Task: Add expandable FAQ items.*
|
|
182
|
+
|
|
183
|
+
1. Search skills: `grep -l "expand\|accordion\|collaps" components/*.md`
|
|
184
|
+
2. Read `expanding-panel.md` — describes single panel behavior, v-model, slots
|
|
185
|
+
3. Use ExpandingPanel in a v-for loop for FAQ items (no need to build custom accordion)
|
|
186
|
+
|
|
187
|
+
### License key display
|
|
188
|
+
|
|
189
|
+
*Task: Show a copyable license key on success page.*
|
|
190
|
+
|
|
191
|
+
1. Search skills: `grep -l "copy\|clipboard\|readonly" components/*.md`
|
|
192
|
+
2. Read `input-copy.md` — describes readonly input, copy button, events, CSS tokens
|
|
193
|
+
3. Use InputCopy component, listen to @copied event, show toast via consumer's toast library
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Updating skills
|
|
198
|
+
|
|
199
|
+
When the layer releases a new component or updates an existing one:
|
|
200
|
+
|
|
201
|
+
1. **Upgrade the package**: `npm install srcdev-nuxt-components@latest`
|
|
202
|
+
2. **Refresh skills**: `npm run setup:claude`
|
|
203
|
+
3. **Read the new skill** to understand the new component's API
|
|
204
|
+
|
|
205
|
+
The skills always stay in sync with the layer's released version.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Tips
|
|
210
|
+
|
|
211
|
+
- **Skim the index.md** in `.claude/skills/srcdev-nuxt-components/` to get an overview of all available skills
|
|
212
|
+
- **Reference skills when asking Claude** — "Here's the InputCopy skill: [paste content]. How do I customize the button color?"
|
|
213
|
+
- **CSS tokens are your friend** — most components expose tokens for customization without component changes
|
|
214
|
+
- **Slots > props for complexity** — if you need custom button behavior, use a #cta slot instead of adding button props to the component
|
|
215
|
+
- **Check accessibility notes** — ARIA labels, keyboard support, and screen reader behavior are documented per component
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"SRCDEV PricingCard Highlighted": {
|
|
21
|
-
"description": "PricingCard with 'Most Popular' highlight",
|
|
21
|
+
"description": "PricingCard with 'Most Popular' highlight and custom button",
|
|
22
22
|
"scope": "vue,html",
|
|
23
23
|
"body": [
|
|
24
24
|
"<PricingCard",
|
|
@@ -33,7 +33,16 @@
|
|
|
33
33
|
" '$7Feature 3',",
|
|
34
34
|
" ]\"",
|
|
35
35
|
" @select=\"$8handleSelect\"",
|
|
36
|
-
"
|
|
36
|
+
">",
|
|
37
|
+
" <template #cta=\"{ ctaText, isDisabled, onSelect }\">",
|
|
38
|
+
" <InputButtonCore",
|
|
39
|
+
" :button-text=\"ctaText\"",
|
|
40
|
+
" variant=\"$9primary\"",
|
|
41
|
+
" :readonly=\"isDisabled\"",
|
|
42
|
+
" @click=\"onSelect\"",
|
|
43
|
+
" />",
|
|
44
|
+
" </template>",
|
|
45
|
+
"</PricingCard>"
|
|
37
46
|
]
|
|
38
47
|
},
|
|
39
48
|
"SRCDEV PricingCard Grid": {
|
|
@@ -69,6 +78,30 @@
|
|
|
69
78
|
"</div>"
|
|
70
79
|
]
|
|
71
80
|
},
|
|
81
|
+
"SRCDEV PricingCard Custom Button": {
|
|
82
|
+
"description": "PricingCard with custom button variant via #cta slot",
|
|
83
|
+
"scope": "vue,html",
|
|
84
|
+
"body": [
|
|
85
|
+
"<PricingCard",
|
|
86
|
+
" planName=\"$1Premium\"",
|
|
87
|
+
" :price=\"$2799\"",
|
|
88
|
+
" billingPeriod=\"$3one-time\"",
|
|
89
|
+
" description=\"$4All features included.\"",
|
|
90
|
+
" :features=\"['$5Feature 1', '$6Feature 2']\"",
|
|
91
|
+
" @select=\"$7handleSelect\"",
|
|
92
|
+
">",
|
|
93
|
+
" <template #cta=\"{ ctaText, isDisabled, onSelect }\">",
|
|
94
|
+
" <InputButtonCore",
|
|
95
|
+
" :button-text=\"ctaText\"",
|
|
96
|
+
" variant=\"$8secondary\"",
|
|
97
|
+
" theme=\"$9default\"",
|
|
98
|
+
" :readonly=\"isDisabled\"",
|
|
99
|
+
" @click=\"onSelect\"",
|
|
100
|
+
" />",
|
|
101
|
+
" </template>",
|
|
102
|
+
"</PricingCard>"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
72
105
|
"SRCDEV PricingCard CSS Override": {
|
|
73
106
|
"description": "CSS override scaffold for PricingCard tokens",
|
|
74
107
|
"scope": "css",
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcdev-nuxt-components",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.2.
|
|
4
|
+
"version": "9.2.4",
|
|
5
5
|
"main": "nuxt.config.ts",
|
|
6
6
|
"types": "types.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"scripts": {
|
|
9
|
+
"postinstall": "node scripts/copy-snippets.mjs",
|
|
9
10
|
"clean": "rm -rf .nuxt && rm -rf .output",
|
|
10
11
|
"cleanall": "rm -rf node_modules && rm -rf .nuxt && rm -rf .output && rm package-lock.json",
|
|
11
12
|
"reinstall": "rm -rf node_modules && npm install",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { copyFileSync, mkdirSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
6
|
+
const srcDir = join(__dirname, '../.vscode');
|
|
7
|
+
const destDir = join(process.cwd(), '.vscode');
|
|
8
|
+
|
|
9
|
+
// Only run when installed as a package (not in the layer's own repo)
|
|
10
|
+
if (process.cwd() === join(__dirname, '..')) {
|
|
11
|
+
console.log('⊘ Skipping snippet copy (running in layer repo)');
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
mkdirSync(destDir, { recursive: true });
|
|
16
|
+
|
|
17
|
+
// Find and copy all .code-snippets files
|
|
18
|
+
try {
|
|
19
|
+
const files = readdirSync(srcDir).filter(file => file.endsWith('.code-snippets'));
|
|
20
|
+
|
|
21
|
+
if (files.length === 0) {
|
|
22
|
+
console.log('⊘ No snippet files found');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
files.forEach(file => {
|
|
27
|
+
try {
|
|
28
|
+
copyFileSync(join(srcDir, file), join(destDir, file));
|
|
29
|
+
console.log(`✓ Copied VSCode snippet: ${file}`);
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.warn(`⚠ Failed to copy ${file}:`, err.message);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log(`✓ VSCode snippets installed to .vscode/`);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.warn('⚠ Snippet installation skipped:', err.message);
|
|
38
|
+
}
|