srcdev-nuxt-components 9.6.12 → 9.6.14

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 (111) hide show
  1. package/.claude/rules/consumer/ids-from-useid.md +2 -1
  2. package/.claude/rules/consumer/story-register-components.md +1 -1
  3. package/.claude/rules/consumer/vfor-keys-from-data.md +14 -0
  4. package/.claude/skills/component-compliance-checklist.md +24 -1
  5. package/.claude/skills/component-naming.md +1 -0
  6. package/.claude/skills/components/banner-video.md +24 -30
  7. package/.claude/skills/components/content-docs.md +18 -5
  8. package/.claude/skills/components/display-dialog.md +16 -6
  9. package/.claude/skills/components/display-qr-code.md +22 -4
  10. package/.claude/skills/components/{display-tooltip-defined.md → display-tooltip-rich.md} +40 -42
  11. package/.claude/skills/components/display-tooltip.md +17 -6
  12. package/.claude/skills/components/eyebrow-text.md +9 -5
  13. package/.claude/skills/components/header-block.md +13 -5
  14. package/.claude/skills/components/hero-text.md +13 -9
  15. package/.claude/skills/components/link-text.md +8 -5
  16. package/.claude/skills/components/pop-over.md +17 -3
  17. package/.claude/skills/components/skip-links.md +16 -0
  18. package/.claude/skills/composable-tooltips-guide.md +4 -4
  19. package/.claude/skills/index.md +6 -6
  20. package/.claude/skills/storybook-add-story.md +5 -2
  21. package/.claude/skills/testing-add-playwright.md +12 -7
  22. package/.claude/skills/testing-add-unit-test.md +8 -0
  23. package/.vscode/srcdev-component-banner-video.code-snippets +38 -25
  24. package/.vscode/srcdev-component-content-docs.code-snippets +69 -60
  25. package/.vscode/srcdev-component-display-dialog.code-snippets +7 -5
  26. package/.vscode/srcdev-component-display-qr-code.code-snippets +12 -1
  27. package/.vscode/srcdev-component-display-tooltip-rich.code-snippets +50 -0
  28. package/.vscode/srcdev-component-display-tooltip.code-snippets +31 -0
  29. package/.vscode/srcdev-component-hero-text.code-snippets +21 -5
  30. package/.vscode/srcdev-component-link-text.code-snippets +3 -3
  31. package/.vscode/srcdev-component-pop-over.code-snippets +28 -0
  32. package/.vscode/srcdev-component-skip-links.code-snippets +1 -1
  33. package/app/components/01.atoms/banner-video/BannerVideo.vue +51 -19
  34. package/app/components/01.atoms/banner-video/CONSUMER-STYLING.md +59 -40
  35. package/app/components/01.atoms/banner-video/stories/BannerVideo.stories.ts +88 -1
  36. package/app/components/01.atoms/banner-video/tests/BannerVideo.spec.ts +127 -78
  37. package/app/components/01.atoms/banner-video/tests/__snapshots__/BannerVideo.spec.ts.snap +12 -4
  38. package/app/components/01.atoms/content-wrappers/docs-pages/CONSUMER-STYLING.md +42 -14
  39. package/app/components/01.atoms/content-wrappers/docs-pages/ContentDocs.vue +86 -112
  40. package/app/components/01.atoms/content-wrappers/docs-pages/stories/ContentDocs.stories.ts +134 -8
  41. package/app/components/01.atoms/content-wrappers/docs-pages/tests/ContentDocs.spec.ts +131 -65
  42. package/app/components/01.atoms/display-dialog/CONSUMER-STYLING.md +28 -3
  43. package/app/components/01.atoms/display-dialog/DisplayDialog.vue +39 -31
  44. package/app/components/01.atoms/display-dialog/stories/DisplayDialog.stories.ts +92 -1
  45. package/app/components/01.atoms/display-dialog/tests/DisplayDialog.spec.ts +87 -41
  46. package/app/components/01.atoms/display-dialog/tests/__snapshots__/DisplayDialog.spec.ts.snap +5 -1
  47. package/app/components/01.atoms/display-tooltip/CONSUMER-STYLING.md +70 -24
  48. package/app/components/01.atoms/display-tooltip/DisplayTooltip.vue +24 -12
  49. package/app/components/01.atoms/display-tooltip/stories/DisplayTooltip.stories.ts +89 -2
  50. package/app/components/01.atoms/display-tooltip/tests/DisplayTooltip.spec.ts +65 -18
  51. package/app/components/01.atoms/display-tooltip/tests/__snapshots__/DisplayTooltip.spec.ts.snap +3 -3
  52. package/app/components/01.atoms/navigation/skip-links/CONSUMER-STYLING.md +65 -19
  53. package/app/components/01.atoms/navigation/skip-links/SkipLinks.vue +20 -14
  54. package/app/components/01.atoms/navigation/skip-links/stories/SkipLinks.stories.ts +89 -20
  55. package/app/components/01.atoms/navigation/skip-links/tests/SkipLinks.spec.ts +70 -10
  56. package/app/components/01.atoms/pop-over/CONSUMER-STYLING.md +42 -9
  57. package/app/components/01.atoms/pop-over/PopOver.vue +28 -12
  58. package/app/components/01.atoms/pop-over/stories/PopOver.stories.ts +114 -2
  59. package/app/components/01.atoms/pop-over/tests/PopOver.spec.ts +35 -0
  60. package/app/components/01.atoms/pop-over/tests/__snapshots__/PopOver.spec.ts.snap +5 -1
  61. package/app/components/01.atoms/qr-code/CONSUMER-STYLING.md +16 -3
  62. package/app/components/01.atoms/qr-code/DisplayQrCode.vue +31 -0
  63. package/app/components/01.atoms/qr-code/stories/DisplayQrCode.stories.ts +190 -157
  64. package/app/components/01.atoms/qr-code/tests/DisplayQrCode.spec.ts +58 -0
  65. package/app/components/01.atoms/text-blocks/eyebrow-text/CONSUMER-STYLING.md +9 -1
  66. package/app/components/01.atoms/text-blocks/eyebrow-text/EyebrowText.vue +5 -4
  67. package/app/components/01.atoms/text-blocks/eyebrow-text/playwright/eyebrow-text.playwright.ts +1 -1
  68. package/app/components/01.atoms/text-blocks/eyebrow-text/stories/EyebrowText.stories.ts +104 -17
  69. package/app/components/01.atoms/text-blocks/eyebrow-text/tests/EyebrowText.spec.ts +68 -118
  70. package/app/components/01.atoms/text-blocks/eyebrow-text/tests/__snapshots__/EyebrowText.spec.ts.snap +8 -8
  71. package/app/components/01.atoms/text-blocks/header-block/CONSUMER-STYLING.md +49 -24
  72. package/app/components/01.atoms/text-blocks/header-block/HeaderBlock.vue +9 -1
  73. package/app/components/01.atoms/text-blocks/header-block/stories/HeaderBlock.stories.ts +96 -26
  74. package/app/components/01.atoms/text-blocks/header-block/tests/HeaderBlock.spec.ts +64 -48
  75. package/app/components/01.atoms/text-blocks/header-block/tests/__snapshots__/HeaderBlock.spec.ts.snap +2 -2
  76. package/app/components/01.atoms/text-blocks/hero-text/CONSUMER-STYLING.md +22 -6
  77. package/app/components/01.atoms/text-blocks/hero-text/HeroText.vue +32 -23
  78. package/app/components/01.atoms/text-blocks/hero-text/playwright/hero-text.playwright.ts +1 -1
  79. package/app/components/01.atoms/text-blocks/hero-text/stories/HeroText.stories.ts +176 -23
  80. package/app/components/01.atoms/text-blocks/hero-text/tests/HeroText.spec.ts +129 -123
  81. package/app/components/01.atoms/text-blocks/hero-text/tests/__snapshots__/HeroText.spec.ts.snap +2 -2
  82. package/app/components/01.atoms/text-blocks/link-text/CONSUMER-STYLING.md +17 -0
  83. package/app/components/01.atoms/text-blocks/link-text/LinkText.vue +9 -3
  84. package/app/components/01.atoms/text-blocks/link-text/stories/LinkText.stories.ts +96 -99
  85. package/app/components/01.atoms/text-blocks/link-text/tests/LinkText.spec.ts +84 -93
  86. package/app/components/02.molecules/display-tooltip-rich/CONSUMER-STYLING.md +110 -0
  87. package/app/components/02.molecules/display-tooltip-rich/DisplayTooltipRich.vue +148 -0
  88. package/app/components/02.molecules/display-tooltip-rich/stories/DisplayTooltipRich.stories.ts +254 -0
  89. package/app/components/02.molecules/display-tooltip-rich/tests/DisplayTooltipRich.spec.ts +156 -0
  90. package/app/components/02.molecules/display-tooltip-rich/tests/__snapshots__/DisplayTooltipRich.spec.ts.snap +22 -0
  91. package/app/components/02.molecules/navigation/deep-expanding-menu/DeepExpandingMenu.vue +1 -0
  92. package/app/components/02.molecules/navigation/deep-expanding-menu/tests/__snapshots__/DeepExpandingMenu.spec.ts.snap +1 -1
  93. package/app/components/02.molecules/price-list/tests/__snapshots__/PriceList.spec.ts.snap +8 -8
  94. package/app/components/03.organisms/image-galleries/carousel-flip/CarouselFlip.vue +1 -0
  95. package/app/components/03.organisms/services/service-detail/tests/__snapshots__/ServiceDetail.spec.ts.snap +15 -15
  96. package/app/components/03.organisms/services/service-summary/tests/__snapshots__/ServiceSummary.spec.ts.snap +3 -3
  97. package/app/components/03.organisms/services/services-card/tests/ServicesCard.spec.ts +3 -3
  98. package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +3 -3
  99. package/app/components/03.organisms/services/services-grids/tests/ServicesCardGrid.spec.ts +1 -1
  100. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServiceSummaryGrid.spec.ts.snap +9 -9
  101. package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +9 -9
  102. package/app/composables/tests/useTooltips.spec.ts +3 -3
  103. package/app/types/components/index.ts +1 -1
  104. package/package.json +1 -1
  105. package/.vscode/srcdev-component-display-tooltip-defined.code-snippets +0 -29
  106. package/app/components/02.molecules/display-tooltip-defined/CONSUMER-STYLING.md +0 -53
  107. package/app/components/02.molecules/display-tooltip-defined/DisplayTooltipDefined.vue +0 -117
  108. package/app/components/02.molecules/display-tooltip-defined/stories/DisplayTooltipDefined.stories.ts +0 -129
  109. package/app/components/02.molecules/display-tooltip-defined/tests/DisplayTooltipDefined.spec.ts +0 -85
  110. package/app/components/02.molecules/display-tooltip-defined/tests/__snapshots__/DisplayTooltipDefined.spec.ts.snap +0 -17
  111. /package/app/types/components/{display-tooltip-defined.d.ts → display-tooltip-rich.d.ts} +0 -0
@@ -4,49 +4,51 @@
4
4
  "scope": "vue,html",
5
5
  "body": [
6
6
  "<ContentDocs",
7
- " v-model:active-nav-item=\"$1activeNavItem\"",
8
- " v-model:active-page-nav-item=\"$2activePageNavItem\"",
9
- " :docs-nav-items=\"$3docsNavItems\"",
10
- " :docs-page-nav-items=\"$4docsPageNavItems\"",
7
+ " v-model:active-nav-item=\"${1:activeNavItem}\"",
8
+ " v-model:active-page-nav-item=\"${2:activePageNavItem}\"",
9
+ " :docs-nav-items=\"${3:docsNavItems}\"",
10
+ " :docs-page-nav-items=\"${4:docsPageNavItems}\"",
11
11
  ">",
12
12
  " <template #docsContent>",
13
- " <p>$5Page content goes here.</p>",
13
+ " <p>${5:Page content goes here.}</p>",
14
14
  " </template>",
15
15
  "</ContentDocs>"
16
16
  ]
17
17
  },
18
- "SRCDEV ContentDocs Custom Labels" : {
18
+ "SRCDEV ContentDocs Custom Labels": {
19
19
  "description": "ContentDocs with custom docsNav / docsPageNav panel headings",
20
20
  "scope": "vue,html",
21
21
  "body": [
22
22
  "<ContentDocs",
23
- " v-model:active-nav-item=\"$1activeNavItem\"",
24
- " v-model:active-page-nav-item=\"$2activePageNavItem\"",
25
- " :docs-nav-items=\"$3docsNavItems\"",
26
- " :docs-page-nav-items=\"$4docsPageNavItems\"",
27
- " docs-nav-label=\"$5Sections\"",
28
- " docs-page-nav-label=\"$6Contents\"",
23
+ " v-model:active-nav-item=\"${1:activeNavItem}\"",
24
+ " v-model:active-page-nav-item=\"${2:activePageNavItem}\"",
25
+ " :docs-nav-items=\"${3:docsNavItems}\"",
26
+ " :docs-page-nav-items=\"${4:docsPageNavItems}\"",
27
+ " docs-nav-label=\"${5:Sections}\"",
28
+ " docs-page-nav-label=\"${6:Contents}\"",
29
29
  ">",
30
30
  " <template #docsContent>",
31
- " <p>$7Page content goes here.</p>",
31
+ " <p>${7:Page content goes here.}</p>",
32
32
  " </template>",
33
33
  "</ContentDocs>"
34
34
  ]
35
35
  },
36
36
  "SRCDEV ContentDocs Localised Nav Labels": {
37
- "description": "ContentDocs with localised aria-labels on the docsNav/docsPageNav <nav> landmarks",
37
+ "description": "ContentDocs with localised headings and aria-labels on the docsNav/docsPageNav <nav> landmarks",
38
38
  "scope": "vue,html",
39
39
  "body": [
40
40
  "<ContentDocs",
41
- " v-model:active-nav-item=\"$1activeNavItem\"",
42
- " v-model:active-page-nav-item=\"$2activePageNavItem\"",
43
- " :docs-nav-items=\"$3docsNavItems\"",
44
- " :docs-page-nav-items=\"$4docsPageNavItems\"",
45
- " docs-nav-aria-label=\"${5:Docs navigation}\"",
46
- " docs-page-nav-aria-label=\"${6:On this page}\"",
41
+ " v-model:active-nav-item=\"${1:activeNavItem}\"",
42
+ " v-model:active-page-nav-item=\"${2:activePageNavItem}\"",
43
+ " :docs-nav-items=\"${3:docsNavItems}\"",
44
+ " :docs-page-nav-items=\"${4:docsPageNavItems}\"",
45
+ " :docs-nav-label=\"${5:t('docs.navLabel')}\"",
46
+ " :docs-page-nav-label=\"${6:t('docs.pageNavLabel')}\"",
47
+ " :docs-nav-aria-label=\"${7:t('docs.navAriaLabel')}\"",
48
+ " :docs-page-nav-aria-label=\"${8:t('docs.pageNavAriaLabel')}\"",
47
49
  ">",
48
50
  " <template #docsContent>",
49
- " <p>$7Page content goes here.</p>",
51
+ " <p>${9:Page content goes here.}</p>",
50
52
  " </template>",
51
53
  "</ContentDocs>"
52
54
  ]
@@ -56,11 +58,11 @@
56
58
  "scope": "typescript,vue",
57
59
  "body": [
58
60
  "const docsNavItems: DocsNavItem[] = [",
59
- " { label: \"$1Getting started\", to: \"$2/docs\" },",
61
+ " { label: \"${1:Getting started}\", to: \"${2:/docs}\" },",
60
62
  "];",
61
63
  "",
62
64
  "const docsPageNavItems: DocsNavItem[] = [",
63
- " { label: \"$3Overview\", to: \"$2/docs#overview\" },",
65
+ " { label: \"${3:Overview}\", to: \"${2:/docs}#overview\" },",
64
66
  "];",
65
67
  "",
66
68
  "const activeNavItem = ref<string | undefined>(docsNavItems[0]?.to);",
@@ -72,11 +74,11 @@
72
74
  "scope": "typescript,vue",
73
75
  "body": [
74
76
  "const docsNavItems: DocsNavItem[] = [",
75
- " { label: \"$1Getting started\", to: \"$2/docs\", icon: \"$3lucide:rocket\" },",
77
+ " { label: \"${1:Getting started}\", to: \"${2:/docs}\", icon: \"${3:lucide:rocket}\" },",
76
78
  "];",
77
79
  "",
78
80
  "const docsPageNavItems: DocsNavItem[] = [",
79
- " { label: \"$4Overview\", to: \"$2/docs#overview\", icon: \"$5lucide:list\" },",
81
+ " { label: \"${4:Overview}\", to: \"${2:/docs}#overview\", icon: \"${5:lucide:list}\" },",
80
82
  "];",
81
83
  "",
82
84
  "const activeNavItem = ref<string | undefined>(docsNavItems[0]?.to);",
@@ -87,13 +89,11 @@
87
89
  "description": "CSS override scaffold for ContentDocs nav-link icon tokens (gap, size, and start/end order)",
88
90
  "scope": "css",
89
91
  "body": [
90
- ".$1my-content-docs {",
91
- " .content-docs {",
92
- " --content-docs-link-icon-gap: $2;",
93
- " --content-docs-link-icon-size: $3;",
94
- " --content-docs-nav-link-icon-order: $4rtl; /* icon at the end */",
95
- " --content-docs-page-nav-link-icon-order: $5ltr; /* icon at the start (default) */",
96
- " }",
92
+ ".${1:my-content-docs} {",
93
+ " --content-docs-link-icon-gap: $2;",
94
+ " --content-docs-link-icon-size: $3;",
95
+ " --content-docs-nav-link-icon-order: ${4:rtl}; /* icon at the end */",
96
+ " --content-docs-page-nav-link-icon-order: ${5:ltr}; /* icon at the start (default) */",
97
97
  "}"
98
98
  ]
99
99
  },
@@ -101,27 +101,26 @@
101
101
  "description": "CSS override scaffold for ContentDocs heading/panel/link tokens, applied to both docsNav and docsPageNav at once",
102
102
  "scope": "css",
103
103
  "body": [
104
- ".$1my-content-docs {",
105
- " .content-docs {",
106
- " --content-docs-heading-font-size: $2;",
107
- " --content-docs-heading-color: $3;",
108
- " --content-docs-heading-bg: $4;",
109
- " --content-docs-heading-padding-block: $5;",
104
+ ".${1:my-content-docs} {",
105
+ " --content-docs-heading-font-size: $2;",
106
+ " --content-docs-heading-color: $3;",
107
+ " --content-docs-heading-bg: $4;",
108
+ " --content-docs-heading-padding-block: $5;",
110
109
  "",
111
- " --content-docs-panel-bg: $6;",
112
- " --content-docs-panel-padding-block: $7;",
113
- " --content-docs-panel-padding-inline: $8;",
110
+ " --content-docs-panel-bg: $6;",
111
+ " --content-docs-panel-padding-block: $7;",
112
+ " --content-docs-panel-padding-inline: $8;",
114
113
  "",
115
- " --content-docs-link-font-size: $9;",
116
- " --content-docs-link-color: $10;",
117
- " --content-docs-link-hover-bg: $11;",
118
- " --content-docs-link-active-bg: $12;",
119
- " --content-docs-link-active-color: $13;",
114
+ " --content-docs-link-font-size: $9;",
115
+ " --content-docs-link-color: $10;",
116
+ " --content-docs-link-hover-bg: $11;",
117
+ " --content-docs-link-active-bg: $12;",
118
+ " --content-docs-link-active-color: $13;",
119
+ " --content-docs-link-focus-outline-color: $14;",
120
120
  "",
121
- " --content-docs-nav-column-width: $14;",
122
- " --content-docs-page-nav-column-width: $15;",
123
- " --content-docs-page-nav-column-width-tablet: $16;",
124
- " }",
121
+ " --content-docs-nav-column-width: $15;",
122
+ " --content-docs-page-nav-column-width: $16;",
123
+ " --content-docs-page-nav-column-width-tablet: $17;",
125
124
  "}"
126
125
  ]
127
126
  },
@@ -129,17 +128,27 @@
129
128
  "description": "CSS override scaffold for independently theming docsNav vs docsPageNav heading/panel/link tokens",
130
129
  "scope": "css",
131
130
  "body": [
132
- ".$1my-content-docs {",
133
- " .content-docs {",
134
- " /* docsNav (primary) */",
135
- " --content-docs-nav-heading-color: $2;",
136
- " --content-docs-nav-panel-bg: $3;",
137
- " --content-docs-nav-link-active-bg: $4;",
131
+ ".${1:my-content-docs} {",
132
+ " /* docsNav (primary) */",
133
+ " --content-docs-nav-heading-color: $2;",
134
+ " --content-docs-nav-panel-bg: $3;",
135
+ " --content-docs-nav-link-active-bg: $4;",
138
136
  "",
139
- " /* docsPageNav (on-this-page / secondary) */",
140
- " --content-docs-page-nav-heading-color: $5;",
141
- " --content-docs-page-nav-panel-bg: $6;",
142
- " --content-docs-page-nav-link-active-bg: $7;",
137
+ " /* docsPageNav (on-this-page / secondary) */",
138
+ " --content-docs-page-nav-heading-color: $5;",
139
+ " --content-docs-page-nav-panel-bg: $6;",
140
+ " --content-docs-page-nav-link-active-bg: $7;",
141
+ "}"
142
+ ]
143
+ },
144
+ "SRCDEV ContentDocs CSS Override — Active Link": {
145
+ "description": "Style the active ContentDocs link beyond its tokens: select [aria-current=\"page\"], there is no .is-active class",
146
+ "scope": "css",
147
+ "body": [
148
+ ".${1:my-content-docs} {",
149
+ " .content-docs-nav-link[aria-current=\"page\"],",
150
+ " .content-docs-page-nav-link[aria-current=\"page\"] {",
151
+ " $2",
143
152
  " }",
144
153
  "}"
145
154
  ]
@@ -19,7 +19,7 @@
19
19
  ]
20
20
  },
21
21
  "SRCDEV DisplayDialog Confirm Template": {
22
- "description": "DisplayDialog confirm variant — content-sized, requires explicit action",
22
+ "description": "DisplayDialog confirm variant — compact (max 48rem), dismissable with Escape or outside-click",
23
23
  "scope": "vue,html",
24
24
  "body": [
25
25
  "<DisplayDialog",
@@ -114,10 +114,12 @@
114
114
  " --display-dialog-backdrop-blur: ;",
115
115
  " --display-dialog-transition-duration: ;",
116
116
  "",
117
- " .inner {",
118
- " .header {}",
119
- " .dialog-content {}",
120
- " .footer {}",
117
+ " --display-dialog-header-accent-color: ;",
118
+ "",
119
+ " .display-dialog-inner {",
120
+ " .display-dialog-header {}",
121
+ " .display-dialog-content {}",
122
+ " .display-dialog-footer {}",
121
123
  " }",
122
124
  " }",
123
125
  "}"
@@ -15,8 +15,19 @@
15
15
  " aria-label=\"$2\"",
16
16
  " black-color=\"${3:currentColor}\"",
17
17
  " :radius=\"${4:0.5}\"",
18
- " :variant=\"{ inner: '${5|default,circle,rounded,dots,classy,classy-rounded|}', marker: '${6|default,circle,rounded,dots,classy,classy-rounded|}', pixel: '${7|default,circle,rounded,dots,classy,classy-rounded|}' }\"",
18
+ " :variant=\"{ inner: '${5|default,circle,rounded,dots,pixelated|}', marker: '${6|default,circle,rounded,dots,pixelated|}', pixel: '${7|default,circle,rounded,dots,pixelated|}' }\"",
19
19
  "/>"
20
20
  ]
21
+ },
22
+ "SRCDEV DisplayQrCode Fallback": {
23
+ "description": "DisplayQrCode with a fallback for values too long to encode",
24
+ "scope": "vue,html",
25
+ "body": [
26
+ "<DisplayQrCode :qr-value=\"${1:url}\" aria-label=\"${2:QR code}\" @error=\"${3:onQrError}\">",
27
+ " <template #fallback>",
28
+ " $0",
29
+ " </template>",
30
+ "</DisplayQrCode>"
31
+ ]
21
32
  }
22
33
  }
@@ -0,0 +1,50 @@
1
+ {
2
+ "SRCDEV DisplayTooltipRich Basic": {
3
+ "description": "DisplayTooltipRich — structured title/body/action tooltip",
4
+ "scope": "vue,html",
5
+ "body": [
6
+ "<DisplayTooltipRich",
7
+ " :content-text=\"{",
8
+ " tooltipTitle: { tag: 'h4', text: '$1' },",
9
+ " tooltipContent: { tag: 'p', text: '$0' },",
10
+ " }\"",
11
+ "/>"
12
+ ]
13
+ },
14
+ "SRCDEV DisplayTooltipRich Localised Copy": {
15
+ "description": "DisplayTooltipRich with localised trigger/close-button copy and a custom trigger icon",
16
+ "scope": "vue,html",
17
+ "body": [
18
+ "<DisplayTooltipRich",
19
+ " :content-text=\"{",
20
+ " tooltipTitle: { tag: 'h4', text: '$1' },",
21
+ " tooltipContent: { tag: 'p', text: '$2' },",
22
+ " }\"",
23
+ " trigger-aria-label=\"${3:Toggle the popover}\"",
24
+ " close-button-text=\"${4:Close}\"",
25
+ " close-button-aria-label=\"${5:Close tool tip}\"",
26
+ " trigger-icon=\"${6:fa7-solid:circle-question}\"",
27
+ "/>"
28
+ ]
29
+ },
30
+ "SRCDEV DisplayTooltipRich CSS Override": {
31
+ "description": "CSS override scaffold for DisplayTooltipRich, scoped to a page or section class",
32
+ "scope": "css",
33
+ "body": [
34
+ ".${1:my-page} {",
35
+ " .display-tooltip-rich {",
36
+ " --display-tooltip-rich-content-gap: ;",
37
+ " --display-tooltip-rich-title-colour: ;",
38
+ " --display-tooltip-rich-title-font-size: ;",
39
+ " --display-tooltip-rich-title-line-clamp: ${2:none};",
40
+ " --display-tooltip-rich-body-font-size: ;",
41
+ " --display-tooltip-rich-body-line-clamp: ${3:none};",
42
+ " --display-tooltip-rich-action-colour: ;",
43
+ " --display-tooltip-rich-action-line-clamp: ${4:none};",
44
+ " --display-tooltip-popover-background-colour: ;",
45
+ " --display-tooltip-popover-text-colour: ;",
46
+ " }",
47
+ "}"
48
+ ]
49
+ }
50
+ }
@@ -34,5 +34,36 @@
34
34
  " </template>",
35
35
  "</DisplayTooltip>"
36
36
  ]
37
+ },
38
+ "SRCDEV DisplayTooltip Custom Icon": {
39
+ "description": "DisplayTooltip with a custom trigger icon and localised label",
40
+ "scope": "vue,html",
41
+ "body": [
42
+ "<DisplayTooltip trigger-icon=\"${1:lucide:info}\" trigger-aria-label=\"${2:More information}\">",
43
+ " <template #triggerContent>",
44
+ " <span>$3</span>",
45
+ " </template>",
46
+ " <template #tooltipContent>",
47
+ " <p>$0</p>",
48
+ " </template>",
49
+ "</DisplayTooltip>"
50
+ ]
51
+ },
52
+ "SRCDEV DisplayTooltip CSS Override": {
53
+ "description": "CSS override scaffold for DisplayTooltip, scoped to a page or section class",
54
+ "scope": "css",
55
+ "body": [
56
+ ".${1:my-page} {",
57
+ " .display-tooltip {",
58
+ " --display-tooltip-trigger-icon-box-size: ;",
59
+ " --display-tooltip-trigger-icon-colour: ;",
60
+ " --display-tooltip-popover-width: ;",
61
+ " --display-tooltip-popover-background-colour: ;",
62
+ " --display-tooltip-popover-text-colour: ;",
63
+ " --display-tooltip-popover-outline-colour: ;",
64
+ " --display-tooltip-popover-border-radius: ;",
65
+ " }",
66
+ "}"
67
+ ]
37
68
  }
38
69
  }
@@ -4,11 +4,27 @@
4
4
  "scope": "vue,html",
5
5
  "body": [
6
6
  "<HeroText",
7
- " tag=\"$1h1\"",
8
- " fontSize=\"$2title\"",
9
- " :textContent=\"[",
10
- " { text: '$3Designing', styleClass: 'normal' },",
11
- " { text: '$4Artistry', styleClass: 'accent' },",
7
+ " tag=\"${1|h1,h2,h3,h4,h5,h6|}\"",
8
+ " font-size=\"${2|title,display,heading,subheading,label|}\"",
9
+ " :text-content=\"[",
10
+ " { text: '${3:Designing}', styleClass: 'normal' },",
11
+ " { text: '${4:Artistry}', styleClass: 'accent' },",
12
+ " ]\"",
13
+ "/>"
14
+ ]
15
+ },
16
+ "SRCDEV HeroText Vertical With Icon": {
17
+ "description": "HeroText stacked vertically with a decorative icon",
18
+ "scope": "vue,html",
19
+ "body": [
20
+ "<HeroText",
21
+ " tag=\"${1|h2,h1,h3,h4,h5,h6|}\"",
22
+ " axis=\"vertical\"",
23
+ " icon=\"${2:lucide:sparkles}\"",
24
+ " font-size=\"${3|display,title,heading,subheading,label|}\"",
25
+ " :text-content=\"[",
26
+ " { text: '${4:Modern}', styleClass: 'normal' },",
27
+ " { text: '${5:Living}', styleClass: 'accent' },",
12
28
  " ]\"",
13
29
  "/>"
14
30
  ]
@@ -19,11 +19,11 @@
19
19
  "description": "CSS override scaffold for LinkText tokens",
20
20
  "scope": "css",
21
21
  "body": [
22
- ".$1my-page {",
22
+ ".${1:my-page} {",
23
23
  " --link-text-colour: $2;",
24
24
  " --link-text-colour-hover: $3;",
25
- " --link-text-decoration: $4underline;",
26
- " --link-text-decoration-hover: $5none;",
25
+ " --link-text-decoration: ${4:underline};",
26
+ " --link-text-decoration-hover: ${5:none};",
27
27
  "}"
28
28
  ]
29
29
  }
@@ -40,5 +40,33 @@
40
40
  " </template>",
41
41
  "</PopOver>"
42
42
  ]
43
+ },
44
+ "SRCDEV PopOver Localised Close": {
45
+ "description": "PopOver with a localised close label and a custom close icon",
46
+ "scope": "vue,html",
47
+ "body": [
48
+ "<PopOver close-button-aria-label=\"${1:Close}\" close-icon=\"${2:lucide:x}\">",
49
+ " <template #trigger>",
50
+ " <span>$3</span>",
51
+ " </template>",
52
+ " <template #content>",
53
+ " <p>$0</p>",
54
+ " </template>",
55
+ "</PopOver>"
56
+ ]
57
+ },
58
+ "SRCDEV PopOver CSS Overrides": {
59
+ "description": "Unlayered CSS override scaffold for PopOver public tokens",
60
+ "scope": "css,vue",
61
+ "body": [
62
+ ".${1:my-section} {",
63
+ " --pop-over-width: ${2:30rem};",
64
+ " --pop-over-gap: ${3:1rem};",
65
+ " --pop-over-background-colour: ${4:var(--slate-00)};",
66
+ " --pop-over-text-colour: ${5:var(--slate-09)};",
67
+ " --pop-over-border-colour: ${6:var(--slate-02)};",
68
+ " --pop-over-content-padding: ${7:1.6rem};$0",
69
+ "}"
70
+ ]
43
71
  }
44
72
  }
@@ -34,7 +34,7 @@
34
34
  "description": "CSS override scaffold for SkipLinks — scope to a page or section class",
35
35
  "scope": "css",
36
36
  "body": [
37
- ".$1my-page {",
37
+ ".${1:my-page} {",
38
38
  " .skip-links {",
39
39
  " --skip-links-z-index: ;",
40
40
  " --skip-links-gap: ;",
@@ -5,7 +5,7 @@
5
5
  :class="elementClasses"
6
6
  :data-depth="depth"
7
7
  :style="{
8
- '--_aspect-ratio': aspectRatio,
8
+ '--_aspect-ratio': safeAspectRatio,
9
9
  '--_align-self': verticalPosition,
10
10
  '--_justify-self': horizontalPosition,
11
11
  }"
@@ -13,7 +13,7 @@
13
13
  <video
14
14
  :key="src"
15
15
  ref="videoEl"
16
- class="video"
16
+ class="banner-video-media"
17
17
  autoplay
18
18
  muted
19
19
  loop
@@ -30,7 +30,7 @@
30
30
  <source :src="src" type="video/mp4" />
31
31
  </video>
32
32
  <NuxtImg
33
- class="fallback"
33
+ class="banner-video-fallback"
34
34
  :src="poster"
35
35
  :alt="alt"
36
36
  :width="imgWidth"
@@ -40,16 +40,19 @@
40
40
  decoding="async"
41
41
  :style="{ objectFit: props.objectFit, objectPosition: imgObjectPosition }"
42
42
  />
43
- <button
43
+ <InputButton
44
44
  type="button"
45
- class="banner-video__toggle"
46
- :aria-label="isPlaying ? 'Pause background video' : 'Play background video'"
45
+ variant="inline"
46
+ :button-text="isPlaying ? pauseLabel : playLabel"
47
+ style-class-passthrough="banner-video-toggle"
47
48
  @click="togglePlayback"
48
49
  >
49
- <slot name="toggle-icon" :is-playing="isPlaying">
50
- <Icon :name="isPlaying ? pauseIcon : playIcon" aria-hidden="true" />
51
- </slot>
52
- </button>
50
+ <template #iconOnly>
51
+ <slot name="toggle-icon" :is-playing="isPlaying">
52
+ <Icon :name="isPlaying ? pauseIcon : playIcon" aria-hidden="true" />
53
+ </slot>
54
+ </template>
55
+ </InputButton>
53
56
  </component>
54
57
  </template>
55
58
 
@@ -68,6 +71,10 @@ interface Props {
68
71
  horizontalPosition?: "start" | "center" | "end";
69
72
  playIcon?: string;
70
73
  pauseIcon?: string;
74
+ /** Toggle button label while paused — override for localisation. */
75
+ playLabel?: string;
76
+ /** Toggle button label while playing — override for localisation. */
77
+ pauseLabel?: string;
71
78
  styleClassPassthrough?: string | string[];
72
79
  }
73
80
 
@@ -83,9 +90,22 @@ const props = withDefaults(defineProps<Props>(), {
83
90
  horizontalPosition: "center",
84
91
  playIcon: "mdi:play",
85
92
  pauseIcon: "mdi:pause",
93
+ playLabel: "Play background video",
94
+ pauseLabel: "Pause background video",
86
95
  styleClassPassthrough: () => [],
87
96
  });
88
97
 
98
+ const DEFAULT_ASPECT_RATIO = "21/9";
99
+ const ratioPattern = /^\s*(\d*\.?\d+)\s*(?:\/\s*(\d*\.?\d+)\s*)?$/;
100
+
101
+ const safeAspectRatio = computed(() => {
102
+ const match = ratioPattern.exec(props.aspectRatio ?? "");
103
+ if (!match) return DEFAULT_ASPECT_RATIO;
104
+ const width = Number(match[1]);
105
+ const height = match[2] === undefined ? 1 : Number(match[2]);
106
+ return width > 0 && height > 0 ? props.aspectRatio.trim() : DEFAULT_ASPECT_RATIO;
107
+ });
108
+
89
109
  const positionKeywordMap = {
90
110
  vertical: { start: "top", center: "center", end: "bottom" },
91
111
  horizontal: { start: "left", center: "center", end: "right" },
@@ -123,7 +143,7 @@ const tryPlay = async () => {
123
143
  v.muted = true;
124
144
  await v.play();
125
145
  } catch {
126
- // Autoplay blocked or interrupted — fallback image will remain visible
146
+ // Autoplay blocked or interrupted: the video's poster stays visible
127
147
  }
128
148
  };
129
149
 
@@ -162,7 +182,7 @@ onActivated(() => {
162
182
 
163
183
  display: grid;
164
184
  grid-template-areas: "media";
165
- aspect-ratio: var(--_aspect-ratio, 16/9);
185
+ aspect-ratio: var(--_aspect-ratio, 21/9);
166
186
  max-height: var(--_max-height);
167
187
  width: 100%;
168
188
  overflow: hidden;
@@ -183,7 +203,7 @@ onActivated(() => {
183
203
  --_max-height: var(--banner-video-max-height-xl, clamp(52rem, 65vw, 90rem));
184
204
  }
185
205
 
186
- .video {
206
+ .banner-video-media {
187
207
  grid-area: media;
188
208
  display: block;
189
209
  width: 100%;
@@ -193,24 +213,26 @@ onActivated(() => {
193
213
  justify-self: var(--_justify-self, center);
194
214
  }
195
215
 
196
- .fallback {
216
+ .banner-video-fallback {
197
217
  grid-area: media;
198
218
  display: none;
199
219
  width: 100%;
200
220
  height: 100%;
201
221
  }
202
222
 
203
- .banner-video__toggle {
223
+ .banner-video-toggle.input-button {
204
224
  grid-area: media;
205
225
  align-self: end;
206
226
  justify-self: end;
207
227
  margin: var(--banner-video-toggle-offset, 1.2rem);
208
228
  z-index: 1;
209
229
 
230
+ box-sizing: border-box;
210
231
  display: grid;
211
232
  place-items: center;
212
233
  width: var(--banner-video-toggle-size, 3.2rem);
213
234
  height: var(--banner-video-toggle-size, 3.2rem);
235
+ min-height: auto;
214
236
  padding: 0;
215
237
 
216
238
  background-color: var(--banner-video-toggle-background, oklch(0% 0 0 / 0.4));
@@ -220,20 +242,30 @@ onActivated(() => {
220
242
  cursor: pointer;
221
243
  transition: background-color 0.2s ease;
222
244
 
223
- &:hover,
245
+ & .input-button-icon {
246
+ margin: 0;
247
+ }
248
+
249
+ &:hover {
250
+ background-color: var(--banner-video-toggle-background-hover, oklch(0% 0 0 / 0.6));
251
+ }
252
+
224
253
  &:focus-visible {
225
254
  background-color: var(--banner-video-toggle-background-hover, oklch(0% 0 0 / 0.6));
255
+ outline: var(--banner-video-toggle-focus-outline-width, 2px) solid
256
+ var(--banner-video-toggle-focus-outline-color, currentColor);
257
+ outline-offset: var(--banner-video-toggle-focus-outline-offset, 2px);
226
258
  }
227
259
  }
228
260
 
229
261
  @media (prefers-reduced-motion: reduce) {
230
- .video {
262
+ .banner-video-media {
231
263
  display: none;
232
264
  }
233
- .fallback {
265
+ .banner-video-fallback {
234
266
  display: block;
235
267
  }
236
- .banner-video__toggle {
268
+ .banner-video-toggle.input-button {
237
269
  display: none;
238
270
  }
239
271
  }