srcdev-nuxt-components 9.4.8 → 9.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.claude/commands/migrate-component.md +20 -6
  2. package/.claude/component-ledger/audit.json +1 -1
  3. package/.claude/component-ledger/build.mjs +26 -0
  4. package/.claude/component-ledger/output.html +8 -3
  5. package/.claude/component-ledger/template.html +7 -2
  6. package/.claude/skills/component-dynamic-slots.md +1 -1
  7. package/.claude/skills/components/alert-mask-core.md +84 -0
  8. package/.claude/skills/components/canvas-switcher.md +1 -1
  9. package/.claude/skills/components/carousel-flip.md +112 -168
  10. package/.claude/skills/components/container-glow.md +123 -0
  11. package/.claude/skills/components/dashboard-quad-grid.md +98 -0
  12. package/.claude/skills/components/dashboard-stats-grid.md +103 -0
  13. package/.claude/skills/components/display-theme-switch.md +60 -0
  14. package/.claude/skills/components/glowing-border.md +66 -0
  15. package/.claude/skills/components/input-range-core.md +142 -0
  16. package/.claude/skills/components/section-parallax.md +32 -10
  17. package/.claude/skills/components/ui-block-decorated.md +56 -0
  18. package/.claude/skills/components/wipe-away-vertical.md +98 -0
  19. package/.claude/skills/icon-sets.md +1 -1
  20. package/.claude/skills/index.md +10 -1
  21. package/.vscode/css-custom-data.json +38 -0
  22. package/.vscode/settings.json +3 -0
  23. package/.vscode/srcdev-component-alert-mask-core.code-snippets +31 -0
  24. package/.vscode/srcdev-component-carousel-flip.code-snippets +41 -0
  25. package/.vscode/srcdev-component-container-glow.code-snippets +33 -0
  26. package/.vscode/srcdev-component-dashboard-quad-grid.code-snippets +36 -0
  27. package/.vscode/srcdev-component-dashboard-stats-grid.code-snippets +35 -0
  28. package/.vscode/srcdev-component-display-theme-switch.code-snippets +27 -0
  29. package/.vscode/srcdev-component-glowing-border.code-snippets +23 -0
  30. package/.vscode/srcdev-component-input-range.code-snippets +85 -0
  31. package/.vscode/srcdev-component-section-parallax.code-snippets +27 -0
  32. package/.vscode/srcdev-component-ui-block-decorated.code-snippets +24 -0
  33. package/.vscode/srcdev-component-wipe-away-vertical.code-snippets +25 -0
  34. package/README.md +18 -37
  35. package/app/components/{alert-mask → 01.atoms/alert-mask}/AlertMaskCore.vue +16 -17
  36. package/app/components/01.atoms/alert-mask/CONSUMER-STYLING.md +46 -0
  37. package/app/components/01.atoms/alert-mask/stories/AlertMaskCore.stories.ts +79 -0
  38. package/app/components/{alert-mask → 01.atoms/alert-mask}/tests/AlertMaskCore.spec.ts +10 -10
  39. package/app/components/01.atoms/animations/container-glow/CONSUMER-STYLING.md +86 -0
  40. package/app/components/01.atoms/animations/container-glow/ContainerGlow.vue +299 -0
  41. package/app/components/01.atoms/animations/container-glow/stories/ContainerGlow.stories.ts +175 -0
  42. package/app/components/01.atoms/animations/container-glow/tests/ContainerGlow.spec.ts +131 -0
  43. package/app/components/01.atoms/animations/container-glow/tests/__snapshots__/ContainerGlow.spec.ts.snap +14 -0
  44. package/app/components/01.atoms/animations/glowing-border/CONSUMER-STYLING.md +61 -0
  45. package/app/components/01.atoms/animations/glowing-border/GlowingBorder.vue +132 -0
  46. package/app/components/01.atoms/animations/glowing-border/stories/GlowingBorder.stories.ts +165 -0
  47. package/app/components/01.atoms/animations/glowing-border/tests/GlowingBorder.spec.ts +94 -0
  48. package/app/components/01.atoms/animations/glowing-border/tests/__snapshots__/GlowingBorder.spec.ts.snap +3 -0
  49. package/app/components/01.atoms/animations/section-parallax/CONSUMER-STYLING.md +44 -0
  50. package/app/components/{parallax → 01.atoms/animations/section-parallax}/SectionParallax.vue +11 -10
  51. package/app/components/01.atoms/animations/section-parallax/stories/SectionParallax.stories.ts +98 -0
  52. package/app/components/01.atoms/animations/section-parallax/tests/SectionParallax.spec.ts +102 -0
  53. package/app/components/01.atoms/animations/section-parallax/tests/__snapshots__/SectionParallax.spec.ts.snap +9 -0
  54. package/app/components/01.atoms/animations/view-timeline/CONSUMER-STYLING.md +70 -0
  55. package/app/components/01.atoms/animations/view-timeline/WipeAwayVertical.vue +181 -0
  56. package/app/components/01.atoms/animations/view-timeline/stories/WipeAwayVertical.stories.ts +269 -0
  57. package/app/components/01.atoms/animations/view-timeline/tests/WipeAwayVertical.spec.ts +127 -0
  58. package/app/components/01.atoms/canvas-switcher/CONSUMER-STYLING.md +1 -1
  59. package/app/components/01.atoms/canvas-switcher/stories/CanvasSwitcher.stories.ts +1 -1
  60. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/CONSUMER-STYLING.md +42 -0
  61. package/app/components/{layout-grids/LayoutGridA.vue → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/DashboardQuadGrid.vue} +23 -13
  62. package/app/components/{layout-grids/stories/LayoutGridA.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/stories/DashboardQuadGrid.stories.ts} +151 -23
  63. package/app/components/{layout-grids/tests/LayoutGridA.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-quad-grid/tests/DashboardQuadGrid.spec.ts} +25 -25
  64. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/CONSUMER-STYLING.md +41 -0
  65. package/app/components/01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/DashboardStatsGrid.vue +133 -0
  66. package/app/components/{layout-grids/stories/LayoutGridB.stories.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/stories/DashboardStatsGrid.stories.ts} +24 -24
  67. package/app/components/{layout-grids/tests/LayoutGridB.spec.ts → 01.atoms/content-wrappers/layout-grid/dashboard-stats-grid/tests/DashboardStatsGrid.spec.ts} +23 -23
  68. package/app/components/01.atoms/navigation/skip-links/SkipLinks.vue +7 -1
  69. package/app/components/01.atoms/ui-block-decorated/CONSUMER-STYLING.md +39 -0
  70. package/app/components/01.atoms/ui-block-decorated/UiBlockDecorated.vue +141 -0
  71. package/app/components/01.atoms/ui-block-decorated/stories/UiBlockDecorated.stories.ts +148 -0
  72. package/app/components/01.atoms/ui-block-decorated/tests/UiBlockDecorated.spec.ts +80 -0
  73. package/app/components/01.atoms/ui-block-decorated/tests/__snapshots__/UiBlockDecorated.spec.ts.snap +3 -0
  74. package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +6 -6
  75. package/app/components/02.molecules/display-theme-switch/CONSUMER-STYLING.md +34 -0
  76. package/app/components/02.molecules/display-theme-switch/DisplayThemeSwitch.vue +102 -0
  77. package/app/components/02.molecules/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +115 -0
  78. package/app/components/02.molecules/display-theme-switch/tests/DisplayThemeSwitch.spec.ts +86 -0
  79. package/app/components/03.organisms/image-galleries/carousel-flip/CONSUMER-STYLING.md +46 -0
  80. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/CarouselFlip.vue +63 -42
  81. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/stories/CarouselFlip.stories.ts +96 -11
  82. package/app/components/{carousels → 03.organisms/image-galleries/carousel-flip}/tests/CarouselFlip.spec.ts +80 -0
  83. package/app/components/05.forms/form-field/FormField.vue +1 -1
  84. package/app/components/05.forms/form-wrapper/FormWrapper.vue +1 -1
  85. package/app/components/05.forms/input-range/CONSUMER-STYLING.md +45 -0
  86. package/app/components/05.forms/input-range/InputRangeCore.vue +79 -143
  87. package/app/components/05.forms/input-range/stories/InputRangeCore.stories.ts +126 -0
  88. package/app/components/05.forms/input-range/tests/InputRangeCore.spec.ts +124 -0
  89. package/app/components/05.forms/input-range/variants/InputRangeDefault.vue +9 -32
  90. package/app/components/05.forms/input-range/variants/stories/InputRangeDefault.stories.ts +108 -0
  91. package/app/components/05.forms/input-range/variants/tests/InputRangeDefault.spec.ts +140 -0
  92. package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +135 -0
  93. package/app/components/05.forms/triple-toggle-switch/CONSUMER-STYLING.md +16 -2
  94. package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +6 -4
  95. package/app/types/components/container-glow.d.ts +8 -0
  96. package/app/types/components/index.ts +1 -1
  97. package/package.json +3 -2
  98. package/app/components/05.forms/input-range-fancy/InputRangeFancyCore.vue +0 -426
  99. package/app/components/05.forms/input-range-fancy/InputRangeFancyWithLabel.vue +0 -94
  100. package/app/components/carousels/CarouselBasic.vue +0 -317
  101. package/app/components/carousels/CarouselInfinite.vue +0 -358
  102. package/app/components/container-glow/ContainerGlowCore.vue +0 -296
  103. package/app/components/container-glow/stories/ContainerGlowCore.stories.ts +0 -337
  104. package/app/components/display-theme-switch/DisplayThemeSwitch.vue +0 -189
  105. package/app/components/display-theme-switch/stories/DisplayThemeSwitch.stories.ts +0 -154
  106. package/app/components/glowing-border/GlowingBorder.vue +0 -142
  107. package/app/components/glowing-border/stories/GlowingBorder.stories.ts +0 -141
  108. package/app/components/layout-grids/LayoutGridB.vue +0 -134
  109. package/app/components/test-storybook/TestStorybook.vue +0 -49
  110. package/app/components/test-storybook/stories/TestStorybook.stories.ts +0 -28
  111. package/app/components/ui-block-decorated/UiBlockDecorated.vue +0 -167
  112. package/app/components/view-timeline/WipeAwayVertical.vue +0 -180
  113. package/app/types/components/carousel-basic.d.ts +0 -19
package/README.md CHANGED
@@ -123,7 +123,7 @@ A formula declared on every potential theme host (`html`, `[data-theme]`, `[data
123
123
  computes the scale from the two params:
124
124
 
125
125
  ```css
126
- --colour-theme-6: oklch(56% calc(var(--theme-chroma) * 1.00) var(--theme-hue)); /* peak chroma */
126
+ --colour-theme-6: oklch(56% calc(var(--theme-chroma) * 1) var(--theme-hue)); /* peak chroma */
127
127
  --colour-theme-0: oklch(98% calc(var(--theme-chroma) * 0.045) var(--theme-hue)); /* near-white */
128
128
  --colour-theme-10: oklch(25% calc(var(--theme-chroma) * 0.64) var(--theme-hue)); /* near-black */
129
129
  ```
@@ -138,7 +138,7 @@ re-evaluates locally so each themed element gets its own full palette without af
138
138
  ### Built-in named palettes
139
139
 
140
140
  | Name | Hue | Used for |
141
- |--------|-----|---------------------------------------------------|
141
+ | ------ | --- | ------------------------------------------------- |
142
142
  | blue | 255 | Default (page-level) |
143
143
  | red | 30 | Error / `data-theme="error"` |
144
144
  | green | 157 | Success / `data-theme="success"` |
@@ -163,7 +163,7 @@ the cascade automatically:
163
163
  // ramps.config.mjs
164
164
  export const ramps = {
165
165
  // New palette — adds --gold-00..10 and --palette-gold-* vars
166
- gold: { hue: 85, chroma: 0.20 },
166
+ gold: { hue: 85, chroma: 0.2 },
167
167
 
168
168
  // Override a built-in — replaces the layer's --blue-00..10 with your values
169
169
  // blue: { hue: 240, chroma: 0.18 },
@@ -218,11 +218,11 @@ In your `app/assets/styles/setup/02.colours/index.css` (create if it doesn't exi
218
218
  ```css
219
219
  /* app/assets/styles/setup/03.theming/_default.css */
220
220
  :where(html) {
221
- --theme-hue: var(--palette-gold-hue);
221
+ --theme-hue: var(--palette-gold-hue);
222
222
  --theme-chroma: var(--palette-gold-chroma);
223
223
 
224
224
  /* Page-level tokens — readable named steps, not raw oklch */
225
- --colour-text-accent: light-dark(var(--gold-09), var(--gold-04));
225
+ --colour-text-accent: light-dark(var(--gold-09), var(--gold-04));
226
226
  --colour-text-eyebrow: light-dark(var(--gold-09), var(--gold-04));
227
227
  }
228
228
  ```
@@ -244,7 +244,7 @@ And in `app/assets/styles/main.css`:
244
244
  ### Hue quick reference
245
245
 
246
246
  | Range | Colour |
247
- |---------|----------------|
247
+ | ------- | -------------- |
248
248
  | 0–30 | Red / pink |
249
249
  | 30–70 | Orange / amber |
250
250
  | 70–100 | Yellow / gold |
@@ -265,12 +265,12 @@ npm run check:ramps # CI: fail if generated CSS is out of date
265
265
 
266
266
  ### Further reading
267
267
 
268
- | Guide | Location |
269
- |-------|----------|
270
- | Full ramp architecture, formula details, hue drift | `.claude/skills/theming-colour-ramps.md` |
271
- | Full palette swap for a consumer app | `.claude/skills/theming-override-default.md` |
268
+ | Guide | Location |
269
+ | ------------------------------------------------------- | -------------------------------------------- |
270
+ | Full ramp architecture, formula details, hue drift | `.claude/skills/theming-colour-ramps.md` |
271
+ | Full palette swap for a consumer app | `.claude/skills/theming-override-default.md` |
272
272
  | Partial token override (palette shift, buttons, inputs) | `.claude/skills/theming-partial-override.md` |
273
- | Disable light/dark mode support | `.claude/skills/colour-scheme-disable.md` |
273
+ | Disable light/dark mode support | `.claude/skills/colour-scheme-disable.md` |
274
274
 
275
275
  Skills are available in your project after running `npm run setup:claude`.
276
276
 
@@ -330,15 +330,19 @@ The `.vscode` directory contains Visual Studio Code configuration files to ensur
330
330
  - **CSS Development**: `willofindie.vscode-cssvar` - CSS custom property IntelliSense
331
331
  - **Testing**: `vitest.explorer` - Vitest test runner integration
332
332
  - **Markdown**: `davidanson.vscode-markdownlint` - Markdown linting
333
- - **Productivity**: `jkjustjoshing.vscode-text-pastry`, `formulahendry.auto-rename-tag`
333
+ - **Productivity**: `jkjustjoshing.vscode-text-pastry`, `formulahendry.auto-rename-tag`, `nixon.env-cmd-file-syntax`
334
+ - **AI-assisted development**: `anthropic.claude-code`
334
335
 
335
336
  ### Code Snippets
336
337
 
337
- The `.vscode` directory includes comprehensive code snippets for rapid component development:
338
+ Every component ships a matching `.vscode/srcdev-component-{name}.code-snippets` file — one per
339
+ component, kept up to date as part of this project's component migration workflow (see
340
+ `CLAUDE.md`). Consumer apps get all of them copied into their own `.vscode/` folder automatically
341
+ via the `postinstall` setup described above; there's nothing to configure manually.
338
342
 
339
343
  ## Contact Form — Resend Setup
340
344
 
341
- The contact form at `/ui/contact-section` sends enquiries via [Resend](https://resend.com).
345
+ The `ContactSection` component (see it in Storybook under **Molecules/ContactSection**) sends enquiries via [Resend](https://resend.com).
342
346
  No extra packages are required — the server route calls the Resend REST API directly.
343
347
 
344
348
  ### 1. Create a Resend account
@@ -490,26 +494,3 @@ Fonts are served instead from local files in `.storybook/public/_fonts/`, declar
490
494
  | Playfair Display | woff2 | `.storybook/public/_fonts/playfair-display/` |
491
495
 
492
496
  To add a new font, see [.claude/skills/storybook-add-font.md](.claude/skills/storybook-add-font.md).
493
-
494
- #### Core Components
495
-
496
- - **`srcdev-nuxt3-component-boilerplate.code-snippets`** - Base component template with prop validation
497
- - **`srcdev-nuxt3-page-layout.code-snippets`** - Page layout scaffolding
498
- - **`srcdev-nuxt3-pinia-store-setup.code-snippets`** - Pinia store configuration
499
-
500
- #### UI Components
501
-
502
- - **`srcdev-nuxt3-accordian-component.code-snippets`** - Accordion component templates
503
- - **`srcdev-nuxt3-carousel-basic-component.code-snippets`** - Basic carousel implementation
504
- - **`srcdev-nuxt3-carousel-flip-component.code-snippets`** - Flip carousel variant
505
- - **`srcdev-nuxt3-container-glow-component.code-snippets`** - Container with glow effects
506
- - **`srcdev-nuxt3-dialog-component.code-snippets`** - Modal dialog templates
507
- - **`srcdev-nuxt3-display-banner-component.code-snippets`** - Display banner component
508
- - **`srcdev-nuxt3-display-details-component.code-snippets`** - Collapsible details component
509
- - **`srcdev-nuxt3-display-prompt-component.code-snippets`** - Alert/notification prompts
510
- - **`srcdev-nuxt3-expanding-panel-component.code-snippets`** - Expandable panel component
511
- - **`srcdev-nuxt3-layout-row.code-snippets`** - Layout row component
512
- - **`srcdev-nuxt3-responsive-header-component.code-snippets`** - Responsive navigation header
513
- - **`srcdev-nuxt3-tabs-component.code-snippets`** - Tab component system
514
-
515
- Each snippet provides both HTML template usage examples and TypeScript setup code, enabling developers to quickly implement components with proper typing and configuration.
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="alert-mask-core" :class="[elementClasses]">
3
- <svg class="alert-mask-decorator" :style="{ '--alertHeight': svgHeight + 'px' }" xmlns="http://www.w3.org/2000/svg">
3
+ <svg class="alert-mask-decorator" :style="{ '--_height': svgHeight + 'px' }" xmlns="http://www.w3.org/2000/svg">
4
4
  <defs>
5
5
  <mask id="borderMask" maskUnits="userSpaceOnUse">
6
6
  <path :d="outerPath" fill="white" />
@@ -15,10 +15,10 @@
15
15
  <div
16
16
  class="alert-mask-content"
17
17
  :style="{
18
- '--insetInlineStart': (props.config?.borderLeft ?? 0) + 'px',
19
- '--insetInlineEnd': (props.config?.borderRight ?? 0) + 'px',
20
- '--insetBlockStart': (props.config?.borderTop ?? 0) + 'px',
21
- '--insetBlockEnd': (props.config?.borderBottom ?? 0) + 'px',
18
+ '--_inset-inline-start': (props.config?.borderLeft ?? 0) + 'px',
19
+ '--_inset-inline-end': (props.config?.borderRight ?? 0) + 'px',
20
+ '--_inset-block-start': (props.config?.borderTop ?? 0) + 'px',
21
+ '--_inset-block-end': (props.config?.borderBottom ?? 0) + 'px',
22
22
  }"
23
23
  >
24
24
  <div ref="alertContentRef" class="alert-mask-content-slot">
@@ -31,15 +31,14 @@
31
31
  <script setup lang="ts">
32
32
  import type { AlertMaskConfig } from "~/types/components";
33
33
 
34
- const props = defineProps({
35
- config: {
36
- type: Object as PropType<AlertMaskConfig>,
37
- default: () => ({}),
38
- },
39
- styleClassPassthrough: {
40
- type: [String, Array] as PropType<string | string[]>,
41
- default: () => [],
42
- },
34
+ interface Props {
35
+ config?: AlertMaskConfig;
36
+ styleClassPassthrough?: string | string[];
37
+ }
38
+
39
+ const props = withDefaults(defineProps<Props>(), {
40
+ config: () => ({}),
41
+ styleClassPassthrough: () => [],
43
42
  });
44
43
 
45
44
  const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
@@ -136,13 +135,13 @@ const innerPath = computed(() => {
136
135
  .alert-mask-decorator {
137
136
  grid-area: mask;
138
137
  width: 100%;
139
- height: var(--alertHeight);
138
+ height: var(--_height);
140
139
  }
141
140
 
142
141
  .alert-mask-content {
143
142
  grid-area: mask;
144
- margin-block: var(--insetBlockStart) var(--insetBlockEnd);
145
- margin-inline: var(--insetInlineStart) var(--insetInlineEnd);
143
+ margin-block: var(--_inset-block-start) var(--_inset-block-end);
144
+ margin-inline: var(--_inset-inline-start) var(--_inset-inline-end);
146
145
  }
147
146
  }
148
147
  }
@@ -0,0 +1,46 @@
1
+ # AlertMaskCore — Consumer Styling Guide
2
+
3
+ ## No public CSS token API
4
+
5
+ `AlertMaskCore` draws its border/background via an SVG `<mask>` whose path geometry is computed
6
+ from measured content dimensions, so it can't expose colour/radius/thickness as CSS custom
7
+ properties the way most components do — a `var()` can't be interpolated into an SVG path or
8
+ resized reactively the way a `fill` attribute can. Instead, the override surface is the `config`
9
+ prop (`AlertMaskConfig`):
10
+
11
+ ```vue
12
+ <AlertMaskCore
13
+ :config="{
14
+ backgroundColour: '#1a1a1a',
15
+ borderColour: 'var(--theme-accent)',
16
+ radiusLeft: 8,
17
+ radiusRight: 4,
18
+ borderLeft: 6,
19
+ borderTop: 1,
20
+ borderRight: 1,
21
+ borderBottom: 1,
22
+ }"
23
+ >
24
+ <p>Alert content</p>
25
+ </AlertMaskCore>
26
+ ```
27
+
28
+ All fields are optional and fall back to `backgroundColour: "rgba(0,0,0,0.25)"`,
29
+ `borderColour: "var(--orange-08)"`, `radiusLeft`/`radiusRight: 12`, `borderLeft`/`borderTop`/
30
+ `borderRight`/`borderBottom: 8`.
31
+
32
+ ## Class passthrough
33
+
34
+ Use `styleClassPassthrough` to add classes to the root `.alert-mask-core` element for layout
35
+ purposes (positioning, sizing) — it doesn't affect the mask's own colours or geometry, which stay
36
+ governed by `config`.
37
+
38
+ ```vue
39
+ <AlertMaskCore style-class-passthrough="my-alert-mask">...</AlertMaskCore>
40
+ ```
41
+
42
+ ## Internal (non-overridable) custom properties
43
+
44
+ `--_height`, `--_inset-inline-start`, `--_inset-inline-end`, `--_inset-block-start`, and
45
+ `--_inset-block-end` are set inline from measured content size and `config` border widths. They're
46
+ private implementation plumbing, not a consumer override surface — use `config` instead.
@@ -0,0 +1,79 @@
1
+ import AlertMaskCore from "../AlertMaskCore.vue";
2
+ import type { Meta, StoryObj } from "@nuxtjs/storybook";
3
+
4
+ const meta: Meta<typeof AlertMaskCore> = {
5
+ title: "Atoms/AlertMaskCore",
6
+ component: AlertMaskCore,
7
+ argTypes: {
8
+ config: {
9
+ control: "object",
10
+ description:
11
+ "Border/background colour and geometry: backgroundColour, borderColour, radiusLeft, radiusRight, borderLeft, borderTop, borderRight, borderBottom",
12
+ table: { category: "Basic" },
13
+ },
14
+ styleClassPassthrough: {
15
+ table: { disable: true },
16
+ },
17
+ },
18
+ parameters: {
19
+ docs: {
20
+ description: {
21
+ component:
22
+ "Draws a border/background mask around slotted content via an SVG path, sized to the measured content. Colour and geometry are set via the `config` prop, not CSS tokens — see CONSUMER-STYLING.md.",
23
+ },
24
+ },
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+ type Story = StoryObj<typeof AlertMaskCore>;
30
+
31
+ export const Default: Story = {
32
+ args: {
33
+ config: {},
34
+ },
35
+ render: (args) => ({
36
+ components: { AlertMaskCore },
37
+ setup() {
38
+ return { args };
39
+ },
40
+ template: `
41
+ <AlertMaskCore v-bind="args">
42
+ <p style="margin: 0; padding: 1.6rem; color: white;">Default alert mask content</p>
43
+ </AlertMaskCore>
44
+ `,
45
+ }),
46
+ };
47
+
48
+ export const CustomConfig: Story = {
49
+ args: {
50
+ config: {
51
+ backgroundColour: "rgba(0, 0, 0, 0.3)",
52
+ borderColour: "var(--theme-accent)",
53
+ radiusLeft: 8,
54
+ radiusRight: 4,
55
+ borderLeft: 6,
56
+ borderTop: 1,
57
+ borderRight: 1,
58
+ borderBottom: 1,
59
+ },
60
+ },
61
+ render: (args) => ({
62
+ components: { AlertMaskCore },
63
+ setup() {
64
+ return { args };
65
+ },
66
+ template: `
67
+ <AlertMaskCore v-bind="args">
68
+ <p style="margin: 0; padding: 1.6rem; color: white;">Custom accent-bar config, matching AlertMaskedContent's default shape</p>
69
+ </AlertMaskCore>
70
+ `,
71
+ }),
72
+ parameters: {
73
+ docs: {
74
+ description: {
75
+ story: "The accent-bar shape used by AlertMaskedContent: thick left radius/border, thin elsewhere.",
76
+ },
77
+ },
78
+ },
79
+ };
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import { mountSuspended } from "@nuxt/test-utils/runtime";
3
3
  import AlertMaskCore from "../AlertMaskCore.vue";
4
- import type { AlertMaskConfig } from "../../../types/components/alert-mask-core.d";
4
+ import type { AlertMaskConfig } from "../../../../types/components/alert-mask-core.d";
5
5
 
6
6
  // Type interface for AlertMaskCore component instance
7
7
  interface AlertMaskCoreInstance {
@@ -193,10 +193,10 @@ describe("AlertMaskCore", () => {
193
193
  const contentEl = wrapper.find(".alert-mask-content");
194
194
  const style = (contentEl.element as HTMLElement).style;
195
195
 
196
- expect(style.getPropertyValue("--insetInlineStart")).toBe("15px");
197
- expect(style.getPropertyValue("--insetBlockStart")).toBe("20px");
198
- expect(style.getPropertyValue("--insetInlineEnd")).toBe("25px");
199
- expect(style.getPropertyValue("--insetBlockEnd")).toBe("30px");
196
+ expect(style.getPropertyValue("--_inset-inline-start")).toBe("15px");
197
+ expect(style.getPropertyValue("--_inset-block-start")).toBe("20px");
198
+ expect(style.getPropertyValue("--_inset-inline-end")).toBe("25px");
199
+ expect(style.getPropertyValue("--_inset-block-end")).toBe("30px");
200
200
  });
201
201
 
202
202
  it("handles zero border values correctly", async () => {
@@ -214,10 +214,10 @@ describe("AlertMaskCore", () => {
214
214
  const contentEl = wrapper.find(".alert-mask-content");
215
215
  const style = (contentEl.element as HTMLElement).style;
216
216
 
217
- expect(style.getPropertyValue("--insetInlineStart")).toBe("0px");
218
- expect(style.getPropertyValue("--insetBlockStart")).toBe("0px");
219
- expect(style.getPropertyValue("--insetInlineEnd")).toBe("0px");
220
- expect(style.getPropertyValue("--insetBlockEnd")).toBe("0px");
217
+ expect(style.getPropertyValue("--_inset-inline-start")).toBe("0px");
218
+ expect(style.getPropertyValue("--_inset-block-start")).toBe("0px");
219
+ expect(style.getPropertyValue("--_inset-inline-end")).toBe("0px");
220
+ expect(style.getPropertyValue("--_inset-block-end")).toBe("0px");
221
221
  });
222
222
 
223
223
  it("sets up ResizeObserver on mount", async () => {
@@ -271,7 +271,7 @@ describe("AlertMaskCore", () => {
271
271
 
272
272
  const svg = wrapper.find(".alert-mask-decorator");
273
273
  const style = (svg.element as HTMLElement).style;
274
- expect(style.getPropertyValue("--alertHeight")).toBe("150px");
274
+ expect(style.getPropertyValue("--_height")).toBe("150px");
275
275
  });
276
276
 
277
277
  it("handles empty configuration object", async () => {
@@ -0,0 +1,86 @@
1
+ # ContainerGlow — Consumer Styling Guide
2
+
3
+ ## Public CSS custom properties
4
+
5
+ These are static, per-card visual tokens — override them with a `style` attribute or from a
6
+ consuming page's own stylesheet:
7
+
8
+ | Token | Default | Description |
9
+ |---|---|---|
10
+ | `--container-glow-background` | `light-dark(white, hsl(246 44% 7%))` | Card background colour. |
11
+ | `--container-glow-padding` | `2rem` | Card inner padding. |
12
+ | `--container-glow-aspect-ratio` | `330 / 400` | Card aspect ratio. |
13
+ | `--container-glow-border-radius` | `12px` | Card and glow-effect corner radius (shared by the card and every glow layer). |
14
+ | `--container-glow-min-width` | `280px` | Card minimum width. |
15
+ | `--container-glow-max-width` | `280px` | Card maximum width. |
16
+ | `--container-glow-content-gap` | `0.25rem` | Gap between a card's own slotted children. |
17
+ | `--container-glow-highlight-colour` | `hsl(280 10% 50% / 1)` | Tint of the thin proximity-tracking ring drawn under the coloured glow. |
18
+ | `--container-glow-brightness` | `1.5` | Brightness multiplier applied to the coloured glow layer. |
19
+ | `--container-glow-transition-duration` | `1s` | Fade duration when the glow's opacity changes (also disabled under `prefers-reduced-motion: reduce`). |
20
+ | `--container-glow-gradient` | 5-stop conic rainbow gradient | The glow's colour gradient. Override with any valid `background` value (a `conic-gradient(...)`, a flat colour, etc). |
21
+
22
+ ```vue
23
+ <ContainerGlow
24
+ style="--container-glow-background: #111; --container-glow-border-radius: 4px; --container-glow-gradient: conic-gradient(from 180deg, #22d3ee, #a855f7, #22d3ee);"
25
+ >
26
+ <template #cta>
27
+ <p>Custom-coloured glow card</p>
28
+ </template>
29
+ </ContainerGlow>
30
+ ```
31
+
32
+ ## The `config` prop (layout and interaction behaviour)
33
+
34
+ Proximity distance, glow spread/blur, wrapper gap, wrapper direction, and the inactive-state
35
+ opacity are **not** CSS tokens — they're computed per-pointer-move in JavaScript and applied as
36
+ private custom properties, so they're driven entirely by the `config` prop instead
37
+ (`ContainerGlowConfig`, importable from `srcdev-nuxt-components`):
38
+
39
+ | Field | Type | Default | Description |
40
+ |---|---|---|---|
41
+ | `proximity` | `number` | `40` | Pointer distance (px) at which a card starts glowing. |
42
+ | `spread` | `number` | `80` | Angular spread of the glow (degrees). |
43
+ | `blur` | `number` | `20` | Blur applied to the glow layer (px). |
44
+ | `gap` | `number` | `32` | Gap between cards in the wrapper (px). |
45
+ | `vertical` | `boolean` | `false` | Stack cards vertically instead of horizontally. |
46
+ | `inactiveOpacity` | `number` | `0` | Glow opacity when the pointer isn't nearby. |
47
+
48
+ All fields are optional — pass only the ones you want to change:
49
+
50
+ ```vue
51
+ <ContainerGlow :config="{ blur: 40, vertical: true }">
52
+ <template #one><p>Card one</p></template>
53
+ <template #two><p>Card two</p></template>
54
+ </ContainerGlow>
55
+ ```
56
+
57
+ ## Named dynamic slots
58
+
59
+ Each named slot renders as one glow card — the consumer controls the slot names, there's no
60
+ `itemCount`-style prop:
61
+
62
+ ```vue
63
+ <ContainerGlow>
64
+ <template #pricing><p>Starter</p></template>
65
+ <template #featured><p>Pro</p></template>
66
+ </ContainerGlow>
67
+ ```
68
+
69
+ ## Motion
70
+
71
+ The glow's opacity fade (`--container-glow-transition-duration`) is disabled under
72
+ `prefers-reduced-motion: reduce`, so proximity changes are instant rather than animated.
73
+
74
+ ## Class passthrough
75
+
76
+ Use `styleClassPassthrough` to add classes to the root `.container-glow-wrapper` element.
77
+
78
+ ```vue
79
+ <ContainerGlow style-class-passthrough="my-container-glow">...</ContainerGlow>
80
+ ```
81
+
82
+ ## Internal (non-overridable) custom properties
83
+
84
+ `--_start`, `--_opacity-active`, `--_gap`, `--_blur`, `--_spread`, `--_direction`, and `--_gradient`
85
+ are internal plumbing driven by pointer events and the `config` prop — use `config` (for
86
+ gap/blur/spread/direction/opacity) or the public tokens above (for colour/geometry) instead.