srcdev-nuxt-components 9.6.18 → 9.6.20
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/css-relative-colour-calc.md +15 -0
- package/.claude/rules/consumer/ids-from-useid.md +3 -2
- package/.claude/rules/consumer/vue-dynamic-arg-no-spaces.md +16 -0
- package/.claude/skills/component-compliance-checklist.md +11 -1
- package/.claude/skills/component-dynamic-slots.md +2 -2
- package/.claude/skills/component-naming.md +1 -1
- package/.claude/skills/components/accordion-chat.md +99 -0
- package/.claude/skills/components/accordion-group.md +98 -0
- package/.claude/skills/components/action-menu.md +11 -5
- package/.claude/skills/components/auto-grid.md +3 -1
- package/.claude/skills/components/capture-qr-code.md +9 -5
- package/.claude/skills/components/carousel-flip.md +41 -30
- package/.claude/skills/components/column-flow-grid.md +2 -0
- package/.claude/skills/components/dashboard-quad-grid.md +7 -2
- package/.claude/skills/components/dashboard-stats-grid.md +4 -0
- package/.claude/skills/components/decode-qr-code.md +7 -2
- package/.claude/skills/components/deep-expanding-menu-classic.md +31 -30
- package/.claude/skills/components/deep-expanding-menu.md +22 -21
- package/.claude/skills/components/expanding-panel-classic.md +9 -7
- package/.claude/skills/components/expanding-panel.md +9 -8
- package/.claude/skills/components/grid-stack.md +2 -0
- package/.claude/skills/components/layout-grid-by-cols.md +2 -0
- package/.claude/skills/components/masonry-grid.md +9 -1
- package/.claude/skills/components/page-row.md +3 -0
- package/.claude/skills/components/samaritan-prompt.md +24 -8
- package/.claude/skills/components/site-header.md +16 -2
- package/.claude/skills/components/sparkle-effect.md +79 -0
- package/.claude/skills/index.md +16 -14
- package/.claude/skills/storybook-add-story.md +33 -0
- package/.claude/skills/testing-add-unit-test.md +26 -0
- package/.claude/skills/using-component-skills.md +1 -1
- package/.vscode/srcdev-component-accordion-chat.code-snippets +42 -0
- package/.vscode/srcdev-component-accordion-group.code-snippets +38 -0
- package/.vscode/srcdev-component-action-menu.code-snippets +3 -3
- package/.vscode/srcdev-component-capture-decode-qr-code.code-snippets +11 -2
- package/.vscode/srcdev-component-carousel-flip.code-snippets +22 -4
- package/.vscode/srcdev-component-deep-expanding-menu-classic.code-snippets +32 -2
- package/.vscode/srcdev-component-deep-expanding-menu.code-snippets +31 -1
- package/.vscode/srcdev-component-samaritan-prompt.code-snippets +5 -4
- package/.vscode/srcdev-component-site-header.code-snippets +13 -1
- package/.vscode/srcdev-component-sparkle-effect.code-snippets +33 -0
- package/app/components/01.atoms/animations/sparkle-effect/CONSUMER-STYLING.md +75 -0
- package/app/components/01.atoms/animations/sparkle-effect/SparkleEffect.vue +213 -0
- package/app/components/01.atoms/animations/sparkle-effect/stories/SparkleEffect.stories.ts +303 -0
- package/app/components/01.atoms/animations/sparkle-effect/tests/SparkleEffect.spec.ts +150 -0
- package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +1 -1
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +12 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue +19 -17
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts +42 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts +31 -44
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +13 -1
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +40 -31
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts +47 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts +51 -53
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/LayoutGridByCols.vue +4 -0
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/stories/LayoutGridByCols.stories.ts +47 -2
- package/app/components/01.atoms/content-wrappers/layout-grid/layout-grid-by-cols/tests/LayoutGridByCols.spec.ts +36 -23
- package/app/components/01.atoms/grids/auto-grid/AutoGrid.vue +7 -3
- package/app/components/01.atoms/grids/auto-grid/CONSUMER-STYLING.md +1 -1
- package/app/components/01.atoms/grids/auto-grid/stories/AutoGrid.stories.ts +46 -1
- package/app/components/01.atoms/grids/auto-grid/tests/AutoGrid.spec.ts +40 -27
- package/app/components/01.atoms/grids/auto-grid/tests/__snapshots__/AutoGrid.spec.ts.snap +1 -1
- package/app/components/01.atoms/grids/column-flow-grid/CONSUMER-STYLING.md +9 -0
- package/app/components/01.atoms/grids/column-flow-grid/ColumnFlowGrid.vue +3 -2
- package/app/components/01.atoms/grids/column-flow-grid/stories/ColumnFlowGrid.stories.ts +46 -0
- package/app/components/01.atoms/grids/column-flow-grid/tests/ColumnFlowGrid.spec.ts +24 -11
- package/app/components/01.atoms/grids/grid-stack/CONSUMER-STYLING.md +3 -0
- package/app/components/01.atoms/grids/grid-stack/GridStack.vue +2 -0
- package/app/components/01.atoms/grids/grid-stack/stories/GridStack.stories.ts +41 -0
- package/app/components/01.atoms/grids/grid-stack/tests/GridStack.spec.ts +26 -13
- package/app/components/01.atoms/grids/masonry-grid/CONSUMER-STYLING.md +15 -0
- package/app/components/01.atoms/grids/masonry-grid/MasonryGrid.vue +51 -31
- package/app/components/01.atoms/grids/masonry-grid/stories/MasonryGrid.stories.ts +48 -0
- package/app/components/01.atoms/grids/masonry-grid/tests/MasonryGrid.spec.ts +84 -29
- package/app/components/01.atoms/page-row/CONSUMER-STYLING.md +6 -1
- package/app/components/01.atoms/page-row/PageRow.vue +16 -15
- package/app/components/01.atoms/page-row/stories/PageRow.stories.ts +23 -0
- package/app/components/01.atoms/page-row/tests/PageRow.spec.ts +6 -5
- package/app/components/02.molecules/action-menu/ActionMenu.vue +6 -2
- package/app/components/02.molecules/action-menu/ActionMenuItem.vue +6 -3
- package/app/components/02.molecules/action-menu/CONSUMER-STYLING.md +11 -4
- package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +130 -18
- package/app/components/02.molecules/expandable/accordion-chat/AccordionChat.vue +227 -0
- package/app/components/02.molecules/expandable/accordion-chat/CONSUMER-STYLING.md +150 -0
- package/app/components/02.molecules/expandable/accordion-chat/stories/AccordionChat.stories.ts +347 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/AccordionChat.spec.ts +205 -0
- package/app/components/02.molecules/expandable/accordion-chat/tests/__snapshots__/AccordionChat.spec.ts.snap +40 -0
- package/app/components/02.molecules/expandable/{accordian/AccordianCore.vue → accordion-group/AccordionGroup.vue} +32 -20
- package/app/components/02.molecules/expandable/accordion-group/CONSUMER-STYLING.md +66 -0
- package/app/components/02.molecules/expandable/accordion-group/stories/AccordionGroup.stories.ts +238 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/AccordionGroup.spec.ts +172 -0
- package/app/components/02.molecules/expandable/accordion-group/tests/__snapshots__/AccordionGroup.spec.ts.snap +45 -0
- package/app/components/02.molecules/expandable/expanding-panel/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel/ExpandingPanel.vue +11 -6
- package/app/components/02.molecules/expandable/expanding-panel/stories/ExpandingPanel.stories.ts +82 -1
- package/app/components/02.molecules/expandable/expanding-panel/tests/ExpandingPanel.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel/tests/__snapshots__/ExpandingPanel.spec.ts.snap +8 -8
- package/app/components/02.molecules/expandable/expanding-panel-classic/CONSUMER-STYLING.md +1 -0
- package/app/components/02.molecules/expandable/expanding-panel-classic/ExpandingPanelClassic.vue +10 -5
- package/app/components/02.molecules/expandable/expanding-panel-classic/stories/ExpandingPanelClassic.stories.ts +83 -1
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/ExpandingPanelClassic.spec.ts +24 -30
- package/app/components/02.molecules/expandable/expanding-panel-classic/tests/__snapshots__/ExpandingPanelClassic.spec.ts.snap +8 -8
- package/app/components/02.molecules/navigation/deep-expanding-menu/CONSUMER-STYLING.md +63 -44
- package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +74 -37
- package/app/components/02.molecules/navigation/deep-expanding-menu/stories/DeepExpandingMenu.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/DeepExpandingMenu.spec.ts +90 -27
- package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +8 -8
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/CONSUMER-STYLING.md +58 -26
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/DeepExpandingMenuClassic.vue +113 -57
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/stories/DeepExpandingMenuClassic.stories.ts +139 -19
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/DeepExpandingMenuClassic.spec.ts +140 -22
- package/app/components/02.molecules/navigation/deep-expanding-menu-classic/tests/__snapshots__/DeepExpandingMenuClassic.spec.ts.snap +9 -9
- package/app/components/02.molecules/qr-code/CONSUMER-STYLING.md +22 -7
- package/app/components/02.molecules/qr-code/CaptureQrCode.vue +63 -71
- package/app/components/02.molecules/qr-code/DecodeQrCode.vue +47 -10
- package/app/components/02.molecules/qr-code/stories/CaptureQrCode.stories.ts +114 -26
- package/app/components/02.molecules/qr-code/stories/DecodeQrCode.stories.ts +115 -24
- package/app/components/02.molecules/qr-code/tests/CaptureQrCode.spec.ts +124 -51
- package/app/components/02.molecules/qr-code/tests/DecodeQrCode.spec.ts +74 -25
- package/app/components/02.molecules/samaritan-prompt/CONSUMER-STYLING.md +22 -10
- package/app/components/02.molecules/samaritan-prompt/SamaritanPrompt.vue +30 -12
- package/app/components/02.molecules/samaritan-prompt/SamaritanPromptMixed.vue +34 -10
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPrompt.stories.ts +55 -4
- package/app/components/02.molecules/samaritan-prompt/stories/SamaritanPromptMixed.stories.ts +67 -7
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPrompt.spec.ts +113 -29
- package/app/components/02.molecules/samaritan-prompt/tests/SamaritanPromptMixed.spec.ts +92 -45
- package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +82 -29
- package/app/components/03.organisms/image-galleries/carousel-flip/CarouselFlip.vue +176 -113
- package/app/components/03.organisms/image-galleries/carousel-flip/stories/CarouselFlip.stories.ts +153 -23
- package/app/components/03.organisms/image-galleries/carousel-flip/tests/CarouselFlip.spec.ts +119 -63
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/NavigationItems.spec.ts.snap +2 -2
- package/app/components/03.organisms/responsive-header/tests/__snapshots__/ResponsiveHeader.spec.ts.snap +2 -2
- package/app/components/03.organisms/services/service-detail/tests/ServiceDetail.spec.ts +8 -8
- package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +4 -4
- package/app/components/03.organisms/site-header/CONSUMER-STYLING.md +41 -23
- package/app/components/03.organisms/site-header/SiteHeader.vue +16 -21
- package/app/components/03.organisms/site-header/stories/SiteHeader.stories.ts +138 -0
- package/app/components/03.organisms/site-header/tests/SiteHeader.spec.ts +53 -26
- package/app/components/03.organisms/site-header/tests/__snapshots__/SiteHeader.spec.ts.snap +2 -2
- package/app/types/components/qr-code.d.ts +12 -0
- package/app/utils/graphemes.ts +8 -0
- package/package.json +1 -1
- package/.claude/skills/components/accordian-core.md +0 -180
- package/.vscode/srcdev-component-accordian-core.code-snippets +0 -74
- package/app/components/01.atoms/animations/entry/.nuxt/app.config.mjs +0 -21
- package/app/components/01.atoms/animations/entry/.nuxt/components.d.ts +0 -66
- package/app/components/01.atoms/animations/entry/.nuxt/imports.d.ts +0 -30
- package/app/components/01.atoms/animations/entry/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/01.atoms/animations/entry/.nuxt/types/build.d.ts +0 -23
- package/app/components/01.atoms/animations/entry/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/01.atoms/animations/entry/.nuxt/types/components.d.ts +0 -71
- package/app/components/01.atoms/animations/entry/.nuxt/types/imports.d.ts +0 -386
- package/app/components/01.atoms/animations/entry/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/01.atoms/animations/entry/.nuxt/types/modules.d.ts +0 -79
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/01.atoms/animations/entry/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/01.atoms/animations/entry/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/01.atoms/animations/entry/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/01.atoms/animations/entry/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/01.atoms/animations/entry/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/01.atoms/animations/entry/.nuxtrc +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
- package/app/components/01.atoms/animations/entry/node_modules/.cache/vite/client/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/app/components/02.molecules/expandable/accordian/CONSUMER-STYLING.md +0 -100
- package/app/components/02.molecules/expandable/accordian/stories/AccordianCore.stories.ts +0 -272
- package/app/components/02.molecules/expandable/accordian/tests/AccordianCore.spec.ts +0 -257
- package/app/components/02.molecules/expandable/accordian/tests/__snapshots__/AccordianCore.spec.ts.snap +0 -95
- package/app/components/03.organisms/responsive-header/tests/.nuxt/app.config.mjs +0 -21
- package/app/components/03.organisms/responsive-header/tests/.nuxt/components.d.ts +0 -66
- package/app/components/03.organisms/responsive-header/tests/.nuxt/imports.d.ts +0 -30
- package/app/components/03.organisms/responsive-header/tests/.nuxt/manifest/meta/test.json +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/app.config.d.ts +0 -35
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/build.d.ts +0 -23
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/builder-env.d.ts +0 -1
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/components.d.ts +0 -71
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/imports.d.ts +0 -386
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/middleware.d.ts +0 -11
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/modules.d.ts +0 -79
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-layouts.d.ts +0 -17
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/nitro-nuxt.d.ts +0 -64
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/plugins.d.ts +0 -29
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/runtime-config.d.ts +0 -32
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/shared-imports.d.ts +0 -10
- package/app/components/03.organisms/responsive-header/tests/.nuxt/types/vue-shim.d.ts +0 -0
- package/app/components/03.organisms/responsive-header/tests/node_modules/.cache/nuxt/chrome-workspace.json +0 -1
|
@@ -313,6 +313,39 @@ prop and try to pass it. Make it unmistakable:
|
|
|
313
313
|
|
|
314
314
|
`PriceList.stories.ts` and `GoogleReviews.stories.ts` (line-clamp controls) follow this.
|
|
315
315
|
|
|
316
|
+
## Where shared story helpers can live
|
|
317
|
+
|
|
318
|
+
Nowhere shared, so far. Stories ship in the package (`app/` is in `package.json` `files`) but
|
|
319
|
+
`.storybook/` doesn't, so a story importing from `.storybook/` breaks in a consumer's type-check;
|
|
320
|
+
and any `.ts` under `app/components/` is registered as a component (`pathPrefix: false` scan).
|
|
321
|
+
Repeat small fixtures per story instead (the grid `StressTest` stories, 2026-10-21, each carry their
|
|
322
|
+
own oversized-content block).
|
|
323
|
+
|
|
324
|
+
## Simulating hardware or library events in a stress story
|
|
325
|
+
|
|
326
|
+
When a component's data only arrives from hardware or a third-party child (a camera, a file
|
|
327
|
+
decoder), a story can't feed it worst-case data through props. Add story-only buttons that call the
|
|
328
|
+
component's own handlers through its internal instance, and say so in the story text:
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const cmpRef = ref<ComponentPublicInstance | null>(null);
|
|
332
|
+
const internals = () =>
|
|
333
|
+
(cmpRef.value?.$ as unknown as { setupState: { onDetect: (codes: { rawValue: string }[]) => void } } | undefined)
|
|
334
|
+
?.setupState;
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`setupState` is internal (not on the public type, hence the cast) but present in every build.
|
|
338
|
+
Don't `defineExpose` handlers just for a story. `CaptureQrCode` and `DecodeQrCode` (2026-10-21)
|
|
339
|
+
follow this.
|
|
340
|
+
|
|
341
|
+
## Dynamic slot names in template strings
|
|
342
|
+
|
|
343
|
+
A dynamic slot argument can't contain spaces: `` #[`item-${i + 6}-summary`] `` is cut at the first space
|
|
344
|
+
(HTML attribute names end there) and the story fails with "Missing } in template expression". It
|
|
345
|
+
only shows when Storybook renders the story, since the template is a string. Write the expression
|
|
346
|
+
without spaces (`` #[`item-${i}-summary`] ``), or build the names in `setup()` and bind them:
|
|
347
|
+
`#[item.summarySlot]` (AccordionChat's StressTest, 2026-10-20).
|
|
348
|
+
|
|
316
349
|
## Docs page
|
|
317
350
|
|
|
318
351
|
`.storybook/preview.ts` sets `tags: ["autodocs"]` globally (2026-10-14), so every story file gets a
|
|
@@ -346,6 +346,32 @@ be resolved from the ends and never reaches it. Mount with two items sharing the
|
|
|
346
346
|
then `setProps` with the first and last items swapped, and assert the rendered labels in order
|
|
347
347
|
(`SkipLinks.spec.ts`). With an `href`-only key that test rendered five links instead of four.
|
|
348
348
|
|
|
349
|
+
## Stub third-party children that hold shared state
|
|
350
|
+
|
|
351
|
+
When a component renders a library child whose behaviour the spec doesn't need (the spec drives
|
|
352
|
+
the parent's own handlers), stub the child in the mount helper. Unmounting doesn't isolate a
|
|
353
|
+
library that keeps module-level state: `QrcodeStream` queues every camera start/stop on one shared
|
|
354
|
+
promise chain, so a start left from an earlier test failed mid-test and emitted `error` into the
|
|
355
|
+
next `CaptureQrCode`, but only under full-suite load and only with `SRCDEV_STANDALONE=true`
|
|
356
|
+
(2026-10-21).
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
const qrcodeStreamStub = { name: "QrcodeStream", emits: ["detect", "error"], template: `<div class="qrcode-stream-stub"></div>` };
|
|
360
|
+
|
|
361
|
+
const mountCapture = async (options: CaptureMountOptions = {}) => {
|
|
362
|
+
const wrapper = await mountSuspended(CaptureQrCode, {
|
|
363
|
+
...options,
|
|
364
|
+
global: { ...options?.global, stubs: { QrcodeStream: qrcodeStreamStub, ...options?.global?.stubs } },
|
|
365
|
+
});
|
|
366
|
+
mounted.push(wrapper);
|
|
367
|
+
return wrapper;
|
|
368
|
+
};
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Assert against the stub's class, not the component's tag name (see
|
|
372
|
+
`.claude/rules/test-no-vacuous-assertions.md`). Run the spec with `npm run test:run` so
|
|
373
|
+
standalone-only modules load.
|
|
374
|
+
|
|
349
375
|
## Mocking browser APIs
|
|
350
376
|
|
|
351
377
|
### Global constructors (ResizeObserver, IntersectionObserver, etc.)
|
|
@@ -47,7 +47,7 @@ Component skills land in `.claude/skills/srcdev-nuxt-components/components/`:
|
|
|
47
47
|
│ ├── pricing-card.md — SaaS pricing plan card
|
|
48
48
|
│ ├── input-copy.md — copy-to-clipboard input
|
|
49
49
|
│ ├── expanding-panel.md — single expand/collapse panel
|
|
50
|
-
│ ├──
|
|
50
|
+
│ ├── accordion-group.md — native accordion (grouped panels)
|
|
51
51
|
│ ├── display-avatar.md — circular avatar component
|
|
52
52
|
│ ├── card-core.md — generic card container
|
|
53
53
|
│ ├── auto-grid.md — responsive auto-fit grid
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"SRCDEV AccordionChat": {
|
|
3
|
+
"description": "AccordionChat: questions as right-hand bubbles, answers as left-hand bubbles with an avatar",
|
|
4
|
+
"scope": "vue,html",
|
|
5
|
+
"body": [
|
|
6
|
+
"<AccordionChat name=\"${1:faq}\" avatar-label=\"${2:AM}\">",
|
|
7
|
+
" <template #item-0-summary>${3:Question one?}</template>",
|
|
8
|
+
" <template #item-0-content><p>${4:Answer one.}</p></template>",
|
|
9
|
+
"",
|
|
10
|
+
" <template #item-1-summary>${5:Question two?}</template>",
|
|
11
|
+
" <template #item-1-content><p>${6:Answer two.}</p></template>",
|
|
12
|
+
"</AccordionChat>"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"SRCDEV AccordionChat Looped": {
|
|
16
|
+
"description": "AccordionChat driven by an array via dynamic item-{n} slot names",
|
|
17
|
+
"scope": "vue,html",
|
|
18
|
+
"body": [
|
|
19
|
+
"<AccordionChat name=\"${1:faq}\" avatar-label=\"${2:AM}\">",
|
|
20
|
+
" <template v-for=\"(item, i) in ${3:items}\" :key=\"`s\\${i}`\" #[`item-\\${i}-summary`]>{{ item.${4:question} }}</template>",
|
|
21
|
+
" <template v-for=\"(item, i) in ${3:items}\" :key=\"`c\\${i}`\" #[`item-\\${i}-content`]><p>{{ item.${5:answer} }}</p></template>",
|
|
22
|
+
"</AccordionChat>"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"SRCDEV AccordionChat CSS Override": {
|
|
26
|
+
"description": "AccordionChat colour and type tokens scoped to a page or section class",
|
|
27
|
+
"scope": "css",
|
|
28
|
+
"body": [
|
|
29
|
+
".${1:faq-section} {",
|
|
30
|
+
" --accordion-chat-question-surface: ${2:#ececec};",
|
|
31
|
+
" --accordion-chat-question-colour: ${3:#1d1d1f};",
|
|
32
|
+
" --accordion-chat-question-surface-open: ${4:#1d1d1f};",
|
|
33
|
+
" --accordion-chat-question-colour-open: ${5:#ffffff};",
|
|
34
|
+
" --accordion-chat-answer-surface: ${6:#ffffff};",
|
|
35
|
+
" --accordion-chat-answer-border: ${7:#e6e4e1};",
|
|
36
|
+
" --accordion-chat-answer-colour: ${8:#2a2a2c};",
|
|
37
|
+
" --accordion-chat-avatar-surface: ${9:#1d1d1f};",
|
|
38
|
+
" --accordion-chat-avatar-colour: ${10:#ffffff};",
|
|
39
|
+
"}"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"SRCDEV AccordionGroup": {
|
|
3
|
+
"description": "AccordionGroup with item-{n}-summary/content slots; a shared name makes it exclusive-open",
|
|
4
|
+
"scope": "vue,html",
|
|
5
|
+
"body": [
|
|
6
|
+
"<AccordionGroup name=\"${1:faq}\">",
|
|
7
|
+
" <template #item-0-summary>${2:Question one?}</template>",
|
|
8
|
+
" <template #item-0-content><p>${3:Answer one.}</p></template>",
|
|
9
|
+
"",
|
|
10
|
+
" <template #item-1-summary>${4:Question two?}</template>",
|
|
11
|
+
" <template #item-1-content><p>${5:Answer two.}</p></template>",
|
|
12
|
+
"</AccordionGroup>"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"SRCDEV AccordionGroup Looped": {
|
|
16
|
+
"description": "AccordionGroup driven by an array via dynamic item-{n} slot names",
|
|
17
|
+
"scope": "vue,html",
|
|
18
|
+
"body": [
|
|
19
|
+
"<AccordionGroup name=\"${1:faq}\">",
|
|
20
|
+
" <template v-for=\"(item, i) in ${2:items}\" :key=\"`s\\${i}`\" #[`item-\\${i}-summary`]>{{ item.${3:question} }}</template>",
|
|
21
|
+
" <template v-for=\"(item, i) in ${2:items}\" :key=\"`c\\${i}`\" #[`item-\\${i}-content`]><p>{{ item.${4:answer} }}</p></template>",
|
|
22
|
+
"</AccordionGroup>"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"SRCDEV AccordionGroup CSS Override": {
|
|
26
|
+
"description": "AccordionGroup width and panel divider overrides scoped to a page or section class",
|
|
27
|
+
"scope": "css",
|
|
28
|
+
"body": [
|
|
29
|
+
".${1:faq-section} {",
|
|
30
|
+
" --accordion-group-max-width: ${2:72rem};",
|
|
31
|
+
"",
|
|
32
|
+
" .accordion-group-item:not(:last-child) {",
|
|
33
|
+
" border-block-end: 0.1rem solid ${3:var(--theme-border)};",
|
|
34
|
+
" }",
|
|
35
|
+
"}"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"scope": "vue,html",
|
|
21
21
|
"body": [
|
|
22
22
|
"<ActionMenu>",
|
|
23
|
-
" <template v-for=\"(
|
|
24
|
-
" <ActionMenuItem :label=\"
|
|
23
|
+
" <template v-for=\"(action, i) in ${1:actions}\" :key=\"`\\${i}-\\${action.label}`\" #[`item-\\${i}`]>",
|
|
24
|
+
" <ActionMenuItem :label=\"action.label\" :href=\"action.href\" @click=\"action.onClick\" />",
|
|
25
25
|
" </template>",
|
|
26
26
|
"</ActionMenu>"
|
|
27
27
|
]
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"description": "CSS override scaffold for ActionMenu tokens",
|
|
31
31
|
"scope": "css",
|
|
32
32
|
"body": [
|
|
33
|
-
".$
|
|
33
|
+
".${1:my-page} {",
|
|
34
34
|
" --action-menu-trigger-icon-color: $2;",
|
|
35
35
|
" --action-menu-popover-background: $3;",
|
|
36
36
|
" --action-menu-popover-border: $4;",
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
"description": "CaptureQrCode live camera scanner with localisable copy",
|
|
4
4
|
"scope": "vue,html",
|
|
5
5
|
"body": [
|
|
6
|
-
"<CaptureQrCode
|
|
6
|
+
"<CaptureQrCode",
|
|
7
|
+
" camera-stopped-label=\"${1:Camera stopped}\"",
|
|
8
|
+
" reset-camera-label=\"${2:Reset camera}\"",
|
|
9
|
+
" :error-messages=\"{ NotAllowedError: '${3:Camera access was blocked.}', default: '${4:The camera could not be started.}' }\"",
|
|
10
|
+
"/>"
|
|
7
11
|
]
|
|
8
12
|
},
|
|
9
13
|
"SRCDEV CaptureQrCode Custom Error": {
|
|
@@ -22,7 +26,12 @@
|
|
|
22
26
|
"description": "DecodeQrCode image upload / drop decoder",
|
|
23
27
|
"scope": "vue,html",
|
|
24
28
|
"body": [
|
|
25
|
-
"<DecodeQrCode
|
|
29
|
+
"<DecodeQrCode",
|
|
30
|
+
" upload-label=\"${1:Upload a QR code image}\"",
|
|
31
|
+
" drop-label=\"${2:Or drop a QR code image here}\"",
|
|
32
|
+
" no-result-label=\"${3:No QR code was found in that image.}\"",
|
|
33
|
+
" read-error-label=\"${4:That file couldn't be read as an image.}\"",
|
|
34
|
+
"/>"
|
|
26
35
|
]
|
|
27
36
|
}
|
|
28
37
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "CarouselFlip with looped item slots",
|
|
4
4
|
"scope": "vue,html",
|
|
5
5
|
"body": [
|
|
6
|
-
"<CarouselFlip :carousel-data-ids=\"$1items.map((item) => item.id)\">",
|
|
6
|
+
"<CarouselFlip :carousel-data-ids=\"$1items.map((item) => item.id)\" aria-label=\"${4:Image carousel}\">",
|
|
7
7
|
" <template v-for=\"$2item in $1items\" :key=\"$2item.id\" #[$2item.id]>",
|
|
8
8
|
" $3",
|
|
9
9
|
" </template>",
|
|
@@ -25,15 +25,33 @@
|
|
|
25
25
|
"</CarouselFlip>"
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
|
+
"SRCDEV CarouselFlip Track Edges": {
|
|
29
|
+
"description": "CarouselFlip with prev/next overlaid on the ends of the visible track",
|
|
30
|
+
"scope": "vue,html",
|
|
31
|
+
"body": [
|
|
32
|
+
"<CarouselFlip",
|
|
33
|
+
" :carousel-data-ids=\"$1items.map((item) => item.id)\"",
|
|
34
|
+
" button-layout=\"track-edges\"",
|
|
35
|
+
" :allow-carousel-overflow=\"true\"",
|
|
36
|
+
">",
|
|
37
|
+
" <template v-for=\"$2item in $1items\" :key=\"$2item.id\" #[$2item.id]>",
|
|
38
|
+
" $3",
|
|
39
|
+
" </template>",
|
|
40
|
+
"</CarouselFlip>"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
28
43
|
"SRCDEV CarouselFlip CSS Override": {
|
|
29
44
|
"description": "CSS override scaffold for CarouselFlip tokens",
|
|
30
45
|
"scope": "css",
|
|
31
46
|
"body": [
|
|
32
47
|
".$1my-page {",
|
|
33
48
|
" .carousel-flip {",
|
|
34
|
-
" --carousel-flip-gap: $2;",
|
|
35
|
-
" --carousel-flip-marker-active-colour: $3;",
|
|
36
|
-
" --carousel-flip-button-background-colour: $4;",
|
|
49
|
+
" --carousel-flip-gap: ${2:1rem};",
|
|
50
|
+
" --carousel-flip-marker-active-colour: ${3:var(--theme-surface)};",
|
|
51
|
+
" --carousel-flip-button-background-colour: ${4:var(--theme-surface-subtle)};",
|
|
52
|
+
" --carousel-flip-button-icon-colour: ${5:var(--theme-text)};",
|
|
53
|
+
" --carousel-flip-button-align: ${6:center};",
|
|
54
|
+
" --carousel-flip-button-inline-offset: ${7:0px};",
|
|
37
55
|
" }",
|
|
38
56
|
"}"
|
|
39
57
|
]
|
|
@@ -1,9 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"SRCDEV DeepExpandingMenuClassic Basic": {
|
|
3
|
-
"description": "DeepExpandingMenuClassic — <details>-based
|
|
3
|
+
"description": "DeepExpandingMenuClassic — <details>-based nav",
|
|
4
4
|
"scope": "vue,html",
|
|
5
|
+
"prefix": "srcdev-deep-expanding-menu-classic",
|
|
5
6
|
"body": [
|
|
6
|
-
"<DeepExpandingMenuClassic :nav-links=\"$
|
|
7
|
+
"<DeepExpandingMenuClassic :nav-links=\"${1:navLinks}\" aria-label=\"${2:Main}\" />"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"SRCDEV DeepExpandingMenuClassic Nav Links": {
|
|
11
|
+
"description": "DeepExpandingMenuClassic — navLinks data (ResponsiveHeaderNavItem[])",
|
|
12
|
+
"scope": "typescript,vue",
|
|
13
|
+
"prefix": "srcdev-deep-expanding-menu-classic-links",
|
|
14
|
+
"body": [
|
|
15
|
+
"const ${1:navLinks}: ResponsiveHeaderNavItem[] = [",
|
|
16
|
+
" { name: \"${2:Home}\", path: \"${3:/}\" },",
|
|
17
|
+
" {",
|
|
18
|
+
" name: \"${4:Services}\",",
|
|
19
|
+
" childLinksTitle: \"${5:Our services}\",",
|
|
20
|
+
" childLinks: [",
|
|
21
|
+
" { name: \"${6:Haircuts}\", path: \"${7:/services/haircuts}\" },",
|
|
22
|
+
" ],",
|
|
23
|
+
" },",
|
|
24
|
+
"];"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"SRCDEV DeepExpandingMenuClassic Style Override": {
|
|
28
|
+
"description": "DeepExpandingMenuClassic — unlayered token override scaffold",
|
|
29
|
+
"scope": "css,vue",
|
|
30
|
+
"prefix": "srcdev-deep-expanding-menu-classic-css",
|
|
31
|
+
"body": [
|
|
32
|
+
".${1:site-nav} {",
|
|
33
|
+
" --deep-expanding-menu-classic-item-gap: ${2:2.4rem};",
|
|
34
|
+
" --deep-expanding-menu-classic-panel-background-colour: ${3:var(--theme-surface-subtle)};",
|
|
35
|
+
" --deep-expanding-menu-classic-panel-heading-line-clamp: ${4:none};",
|
|
36
|
+
"}"
|
|
7
37
|
]
|
|
8
38
|
}
|
|
9
39
|
}
|
|
@@ -2,8 +2,38 @@
|
|
|
2
2
|
"SRCDEV DeepExpandingMenu Basic": {
|
|
3
3
|
"description": "DeepExpandingMenu — anchor-positioned popover nav panels",
|
|
4
4
|
"scope": "vue,html",
|
|
5
|
+
"prefix": "srcdev-deep-expanding-menu",
|
|
5
6
|
"body": [
|
|
6
|
-
"<DeepExpandingMenu :nav-links=\"$
|
|
7
|
+
"<DeepExpandingMenu :nav-links=\"${1:navLinks}\" aria-label=\"${2:Main}\" />"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"SRCDEV DeepExpandingMenu Nav Links": {
|
|
11
|
+
"description": "DeepExpandingMenu — navLinks data (ResponsiveHeaderNavItem[])",
|
|
12
|
+
"scope": "typescript,vue",
|
|
13
|
+
"prefix": "srcdev-deep-expanding-menu-links",
|
|
14
|
+
"body": [
|
|
15
|
+
"const ${1:navLinks}: ResponsiveHeaderNavItem[] = [",
|
|
16
|
+
" { name: \"${2:Home}\", path: \"${3:/}\" },",
|
|
17
|
+
" {",
|
|
18
|
+
" name: \"${4:Services}\",",
|
|
19
|
+
" childLinksTitle: \"${5:Our services}\",",
|
|
20
|
+
" childLinks: [",
|
|
21
|
+
" { name: \"${6:Haircuts}\", path: \"${7:/services/haircuts}\" },",
|
|
22
|
+
" ],",
|
|
23
|
+
" },",
|
|
24
|
+
"];"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"SRCDEV DeepExpandingMenu Style Override": {
|
|
28
|
+
"description": "DeepExpandingMenu — unlayered token override scaffold",
|
|
29
|
+
"scope": "css,vue",
|
|
30
|
+
"prefix": "srcdev-deep-expanding-menu-css",
|
|
31
|
+
"body": [
|
|
32
|
+
".${1:site-nav} {",
|
|
33
|
+
" --deep-expanding-menu-gap: ${2:2.4rem};",
|
|
34
|
+
" --deep-expanding-menu-panel-background-colour: ${3:var(--theme-surface-subtle)};",
|
|
35
|
+
" --deep-expanding-menu-panel-heading-line-clamp: ${4:none};",
|
|
36
|
+
"}"
|
|
7
37
|
]
|
|
8
38
|
}
|
|
9
39
|
}
|
|
@@ -48,13 +48,14 @@
|
|
|
48
48
|
]
|
|
49
49
|
},
|
|
50
50
|
"SRCDEV SamaritanPrompt Tokens": {
|
|
51
|
-
"description": "SamaritanPrompt / SamaritanPromptMixed CSS token overrides",
|
|
51
|
+
"description": "SamaritanPrompt / SamaritanPromptMixed CSS token overrides (classic white-on-black look)",
|
|
52
52
|
"scope": "css,vue",
|
|
53
53
|
"body": [
|
|
54
54
|
"--samaritan-prompt-font-size: ${1:2rem};",
|
|
55
|
-
"--samaritan-prompt-text-colour: ${2
|
|
56
|
-
"--samaritan-prompt-underline-colour: ${3
|
|
57
|
-
"--samaritan-prompt-cursor-colour: ${4:#cc0000};"
|
|
55
|
+
"--samaritan-prompt-text-colour: ${2:#fff};",
|
|
56
|
+
"--samaritan-prompt-underline-colour: ${3:#fff};",
|
|
57
|
+
"--samaritan-prompt-cursor-colour: ${4:#cc0000};",
|
|
58
|
+
"--samaritan-prompt-text-line-clamp: ${5:none};"
|
|
58
59
|
]
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
"</SiteHeader>"
|
|
15
15
|
]
|
|
16
16
|
},
|
|
17
|
+
"SRCDEV SiteHeader Sticky Tokens": {
|
|
18
|
+
"description": "SiteHeader sticky + layout token overrides (set on the styleClassPassthrough class)",
|
|
19
|
+
"scope": "css",
|
|
20
|
+
"body": [
|
|
21
|
+
".${1:header} {",
|
|
22
|
+
" --site-header-position: ${2:sticky};",
|
|
23
|
+
" --site-header-sticky-offset: ${3:0px};",
|
|
24
|
+
" --site-header-bg: ${4:var(--theme-surface-subtle)};",
|
|
25
|
+
" --site-header-branding-max-width: ${5:50%};",
|
|
26
|
+
"}"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
17
29
|
"SRCDEV SiteHeader With Secondary Navigation": {
|
|
18
30
|
"description": "SiteHeader with the secondaryNavigation slot (e.g. a settings icon link)",
|
|
19
31
|
"scope": "vue,html",
|
|
@@ -36,7 +48,7 @@
|
|
|
36
48
|
"body": [
|
|
37
49
|
"<SiteHeader",
|
|
38
50
|
" :responsive-nav-links=\"$1responsiveNavLinks\"",
|
|
39
|
-
" home-nav-aria-label=\"${2:Home
|
|
51
|
+
" home-nav-aria-label=\"${2:Home navigation}\"",
|
|
40
52
|
" main-nav-aria-label=\"${3:Main navigation}\"",
|
|
41
53
|
" secondary-nav-aria-label=\"${4:Secondary navigation}\"",
|
|
42
54
|
" overflow-menu-aria-label=\"${5:Overflow navigation menu}\"",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"SRCDEV SparkleEffect Button": {
|
|
3
|
+
"description": "SparkleEffect around a pill button (sparkles along its border on hover/focus)",
|
|
4
|
+
"scope": "vue,html",
|
|
5
|
+
"body": [
|
|
6
|
+
"<SparkleEffect :count=\"${1:5}\"${4| continuous,|}>",
|
|
7
|
+
" <InputButton button-text=\"${2:Button text}\" variant=\"${3|primary,secondary,tertiary|}\" is-pill />",
|
|
8
|
+
"</SparkleEffect>"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"SRCDEV SparkleEffect Card": {
|
|
12
|
+
"description": "SparkleEffect around block content with its own corner radius",
|
|
13
|
+
"scope": "vue,html",
|
|
14
|
+
"body": [
|
|
15
|
+
"<SparkleEffect tag=\"div\" radius=\"${1:1.2rem}\" :count=\"${2:7}\">",
|
|
16
|
+
" $0",
|
|
17
|
+
"</SparkleEffect>"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"SRCDEV SparkleEffect CSS Override": {
|
|
21
|
+
"description": "SparkleEffect token overrides scoped to a page or section class",
|
|
22
|
+
"scope": "css,vue",
|
|
23
|
+
"body": [
|
|
24
|
+
".${1:page-name} {",
|
|
25
|
+
" --sparkle-effect-colour: ${2:hsl(260 80% 70%)};",
|
|
26
|
+
" --sparkle-effect-size: ${3:0.7rem};",
|
|
27
|
+
" --sparkle-effect-blur: ${6:1px};",
|
|
28
|
+
" --sparkle-effect-duration: ${4:1.5s};",
|
|
29
|
+
" --sparkle-effect-glow-opacity: ${5:0.5};",
|
|
30
|
+
"}"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# SparkleEffect — Consumer Styling Guide
|
|
2
|
+
|
|
3
|
+
## Public token API
|
|
4
|
+
|
|
5
|
+
| Token | Default | Description |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `--sparkle-effect-colour` | `var(--theme-border)` | Glow colour, and the base hue the sparkles shift from. With relative colour support each sparkle is a lightened, hue-shifted version of it; without, they use it as-is. |
|
|
8
|
+
| `--sparkle-effect-size` | `0.7rem` | Width of one sparkle arm (each arm is 1:3, and each sparkle scales this by its preset). |
|
|
9
|
+
| `--sparkle-effect-blur` | `1px` | Blur on each sparkle. `0` gives crisp sparkles; the original pen used `3px`. |
|
|
10
|
+
| `--sparkle-effect-duration` | `1.5s` | Base travel time; each sparkle multiplies it by its own speed. |
|
|
11
|
+
| `--sparkle-effect-radius` | `100vw` | Corner shape of the path the sparkles follow and of the glow. Match the slotted element's radius. Also set by the `radius` prop. |
|
|
12
|
+
| `--sparkle-effect-glow-blur` | `1.2rem` | Blur of the glow behind the slot. |
|
|
13
|
+
| `--sparkle-effect-glow-opacity` | `0.5` | Glow opacity while active. |
|
|
14
|
+
|
|
15
|
+
Private tokens, not public API: `--_sparkle-effect-colour` (the resolved colour) and the per-sparkle
|
|
16
|
+
`--_sparkle-effect-spark-*` values (colour, scale, translate, offset, speed, timing), which
|
|
17
|
+
the component writes inline on each sparkle from its preset table.
|
|
18
|
+
|
|
19
|
+
## State hooks
|
|
20
|
+
|
|
21
|
+
| Selector | Meaning |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `.sparkle-effect` | Root element (`span` or `div`, per `tag`). |
|
|
24
|
+
| `.sparkle-effect::before` | The glow, behind the slot. |
|
|
25
|
+
| `.sparkle-effect-sparks` | `aria-hidden` layer holding the sparkles, over the slot. |
|
|
26
|
+
| `.sparkle-effect-spark` | One sparkle. |
|
|
27
|
+
| `.sparkle-effect[data-continuous]` | Set by the `continuous` prop: sparkles loop while active. |
|
|
28
|
+
|
|
29
|
+
The effect runs on `.sparkle-effect:hover` and when anything inside has `:focus-visible`. It's
|
|
30
|
+
suppressed while the slotted element (direct child) is `:disabled` or `[aria-disabled="true"]`.
|
|
31
|
+
|
|
32
|
+
## Motion and browser support
|
|
33
|
+
|
|
34
|
+
- Sparkles travel along the wrapper's border box with `offset-path: border-box`. They render only
|
|
35
|
+
where that's supported (Chrome 116+, Safari 17.2+, Firefox 122+). Elsewhere, including Safari
|
|
36
|
+
16, the glow still works.
|
|
37
|
+
- Under `prefers-reduced-motion: reduce` the sparkles are hidden and only the glow fades in.
|
|
38
|
+
- Sparkles start at the top-left of the path and snap back when the pointer leaves, so each hover
|
|
39
|
+
replays the effect. With `continuous`, each sparkle loops instead (`sparkle-effect-travel` keyframes:
|
|
40
|
+
travel, drift and fade in one), at its own speed, until the hover or focus ends.
|
|
41
|
+
|
|
42
|
+
## Shape
|
|
43
|
+
|
|
44
|
+
The wrapper shrinks to its slot (`inline-grid` for `span`, `grid` for `div`) and can't read the
|
|
45
|
+
slotted element's own `border-radius`, so set `--sparkle-effect-radius` (or `radius`) to match it:
|
|
46
|
+
the default `100vw` suits pill buttons, a card might need `1.2rem`, a round icon button `50%`.
|
|
47
|
+
|
|
48
|
+
## Local overrides
|
|
49
|
+
|
|
50
|
+
Set the tokens above on an element you own (a page or section class, or a class added with
|
|
51
|
+
`:style-class-passthrough`): they inherit down into the component. Keep the block **unlayered** (no
|
|
52
|
+
`@layer` wrapper) so it beats the library's `@layer components`. If your own file uses
|
|
53
|
+
`<style scoped>`, tokens set on your element still work, but selectors that reach inside the component need
|
|
54
|
+
`:deep()`. Patterns and examples: `.claude/skills/component-local-style-override.md`.
|
|
55
|
+
|
|
56
|
+
**Caveat:** the `radius` prop writes `--sparkle-effect-radius` inline on the root, so when it's set
|
|
57
|
+
an ancestor's value for that token never lands.
|
|
58
|
+
|
|
59
|
+
## Class passthrough
|
|
60
|
+
|
|
61
|
+
`styleClassPassthrough` adds classes to the root, for sizing or placing the wrapper (e.g.
|
|
62
|
+
`inline-size: 100%` around a full-width card) or for setting the tokens above on one instance.
|
|
63
|
+
|
|
64
|
+
```vue
|
|
65
|
+
<SparkleEffect style-class-passthrough="promo-sparkle">
|
|
66
|
+
<InputButton button-text="Join the waitlist" is-pill />
|
|
67
|
+
</SparkleEffect>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
.promo-sparkle {
|
|
72
|
+
--sparkle-effect-colour: hsl(260 80% 70%);
|
|
73
|
+
--sparkle-effect-duration: 2s;
|
|
74
|
+
}
|
|
75
|
+
```
|