@deriv-com/dds-marketing 1.0.2 → 1.1.0

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 (315) hide show
  1. package/.llm/components/awards.md +209 -0
  2. package/.llm/components/background.md +89 -40
  3. package/.llm/components/bottomsheet.md +5 -0
  4. package/.llm/components/comparisoncards.md +257 -0
  5. package/.llm/components/comparisontable.md +231 -0
  6. package/.llm/components/ctabanner.md +9 -12
  7. package/.llm/components/ctabannerslider.md +15 -2
  8. package/.llm/components/datatable.md +273 -0
  9. package/.llm/components/downloadbanner.md +283 -0
  10. package/.llm/components/featurecards.md +41 -3
  11. package/.llm/components/featurecardscarousel.md +311 -0
  12. package/.llm/components/featuresplit.md +5 -0
  13. package/.llm/components/featurestack.md +227 -0
  14. package/.llm/components/illustrationcard.md +2 -2
  15. package/.llm/components/illustrationscene.md +6 -10
  16. package/.llm/components/imagebanner.md +278 -0
  17. package/.llm/components/navbar.md +1 -1
  18. package/.llm/components/navbaritem.md +4 -0
  19. package/.llm/components/pageheader.md +4 -3
  20. package/.llm/components/paginationdots.md +6 -1
  21. package/.llm/components/producthero.md +35 -11
  22. package/.llm/components/resourcebanner.md +247 -0
  23. package/.llm/components/scenescroll.md +222 -0
  24. package/.llm/components/select.md +17 -0
  25. package/.llm/components/stats.md +219 -0
  26. package/.llm/components/steps.md +286 -0
  27. package/.llm/components/stickycards.md +251 -0
  28. package/.llm/components/tableheadercell.md +19 -0
  29. package/.llm/components/teamgrid.md +243 -0
  30. package/.llm/components/testimonialrow.md +5 -0
  31. package/.llm/components/testimonialslider.md +14 -1
  32. package/.llm/components/textarea.md +13 -0
  33. package/.llm/components/textinput.md +13 -0
  34. package/.llm/components/textscroll.md +187 -0
  35. package/.llm/components/tiers.md +227 -0
  36. package/.llm/digest/awards.md +45 -0
  37. package/.llm/digest/background.md +11 -10
  38. package/.llm/digest/bottomsheet.md +2 -0
  39. package/.llm/digest/comparisoncards.md +51 -0
  40. package/.llm/digest/comparisontable.md +53 -0
  41. package/.llm/digest/ctabanner.md +4 -4
  42. package/.llm/digest/ctabannerslider.md +4 -0
  43. package/.llm/digest/datatable.md +65 -0
  44. package/.llm/digest/downloadbanner.md +65 -0
  45. package/.llm/digest/featurecards.md +1 -1
  46. package/.llm/digest/featurecardscarousel.md +54 -0
  47. package/.llm/digest/featuresplit.md +1 -0
  48. package/.llm/digest/featurestack.md +48 -0
  49. package/.llm/digest/illustrationscene.md +2 -3
  50. package/.llm/digest/imagebanner.md +64 -0
  51. package/.llm/digest/navbaritem.md +1 -0
  52. package/.llm/digest/pageheader.md +2 -1
  53. package/.llm/digest/paginationdots.md +3 -1
  54. package/.llm/digest/producthero.md +4 -2
  55. package/.llm/digest/resourcebanner.md +53 -0
  56. package/.llm/digest/scenescroll.md +49 -0
  57. package/.llm/digest/select.md +5 -0
  58. package/.llm/digest/stats.md +46 -0
  59. package/.llm/digest/steps.md +55 -0
  60. package/.llm/digest/stickycards.md +52 -0
  61. package/.llm/digest/tableheadercell.md +4 -0
  62. package/.llm/digest/teamgrid.md +53 -0
  63. package/.llm/digest/testimonialrow.md +2 -0
  64. package/.llm/digest/testimonialslider.md +4 -0
  65. package/.llm/digest/textarea.md +4 -0
  66. package/.llm/digest/textinput.md +4 -0
  67. package/.llm/digest/textscroll.md +43 -0
  68. package/.llm/digest/tiers.md +50 -0
  69. package/.llm/index.md +19 -3
  70. package/.llm/tokens.md +26 -1
  71. package/AGENTS.md +17 -1
  72. package/dist/components/awards-data.d.ts +24 -0
  73. package/dist/components/awards-data.js +11 -0
  74. package/dist/components/block-image-data.d.ts +24 -0
  75. package/dist/components/block-image-data.js +9 -0
  76. package/dist/components/blocks/awards/Awards.css +134 -0
  77. package/dist/components/blocks/awards/Awards.d.ts +24 -0
  78. package/dist/components/blocks/awards/Awards.js +114 -0
  79. package/dist/components/blocks/awards/Awards.types.d.ts +31 -0
  80. package/dist/components/blocks/awards/Awards.types.js +16 -0
  81. package/dist/components/blocks/awards/awards-impl.css +222 -0
  82. package/dist/components/blocks/cards-of.d.ts +16 -0
  83. package/dist/components/blocks/cards-of.js +23 -0
  84. package/dist/components/blocks/comparison-cards/ComparisonCards.css +195 -0
  85. package/dist/components/blocks/comparison-cards/ComparisonCards.d.ts +24 -0
  86. package/dist/components/blocks/comparison-cards/ComparisonCards.js +75 -0
  87. package/dist/components/blocks/comparison-cards/ComparisonCards.types.d.ts +49 -0
  88. package/dist/components/blocks/comparison-cards/ComparisonCards.types.js +26 -0
  89. package/dist/components/blocks/comparison-cards/comparison-cards-impl.css +262 -0
  90. package/dist/components/blocks/comparison-table/ComparisonTable.css +111 -0
  91. package/dist/components/blocks/comparison-table/ComparisonTable.d.ts +25 -0
  92. package/dist/components/blocks/comparison-table/ComparisonTable.js +100 -0
  93. package/dist/components/blocks/comparison-table/ComparisonTable.types.d.ts +36 -0
  94. package/dist/components/blocks/comparison-table/ComparisonTable.types.js +15 -0
  95. package/dist/components/blocks/comparison-table/comparison-table-impl.css +143 -0
  96. package/dist/components/blocks/cta-banner/CtaBanner.css +1 -0
  97. package/dist/components/blocks/cta-banner/CtaBanner.js +2 -2
  98. package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +4 -3
  99. package/dist/components/blocks/cta-banner/CtaBanner.types.js +1 -1
  100. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.css +1 -0
  101. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +1 -1
  102. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +6 -2
  103. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +8 -0
  104. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.js +1 -1
  105. package/dist/components/blocks/data-table/DataTable.css +170 -0
  106. package/dist/components/blocks/data-table/DataTable.d.ts +29 -0
  107. package/dist/components/blocks/data-table/DataTable.js +84 -0
  108. package/dist/components/blocks/data-table/DataTable.types.d.ts +69 -0
  109. package/dist/components/blocks/data-table/DataTable.types.js +19 -0
  110. package/dist/components/blocks/data-table/data-table-impl.css +155 -0
  111. package/dist/components/blocks/download-banner/DownloadBanner.css +228 -0
  112. package/dist/components/blocks/download-banner/DownloadBanner.d.ts +28 -0
  113. package/dist/components/blocks/download-banner/DownloadBanner.js +75 -0
  114. package/dist/components/blocks/download-banner/DownloadBanner.types.d.ts +56 -0
  115. package/dist/components/blocks/download-banner/DownloadBanner.types.js +23 -0
  116. package/dist/components/blocks/download-banner/download-banner-impl.css +216 -0
  117. package/dist/components/blocks/faq-accordion/FaqAccordion.css +1 -0
  118. package/dist/components/blocks/faq-accordion/faq-accordion-impl.css +3 -7
  119. package/dist/components/blocks/feature-bullets/FeatureBullets.css +1 -0
  120. package/dist/components/blocks/feature-bullets/feature-bullets-impl.css +14 -1
  121. package/dist/components/blocks/feature-cards/FeatureCards.css +1 -0
  122. package/dist/components/blocks/feature-cards/FeatureCards.d.ts +2 -1
  123. package/dist/components/blocks/feature-cards/FeatureCards.js +4 -21
  124. package/dist/components/blocks/feature-cards/feature-cards-impl.css +10 -9
  125. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.css +115 -0
  126. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.d.ts +30 -0
  127. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.js +160 -0
  128. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.d.ts +52 -0
  129. package/dist/components/blocks/feature-cards-carousel/FeatureCardsCarousel.types.js +17 -0
  130. package/dist/components/blocks/feature-cards-carousel/feature-cards-carousel-impl.css +207 -0
  131. package/dist/components/blocks/feature-scroll/FeatureScroll.css +1 -0
  132. package/dist/components/blocks/feature-split/FeatureSplit.css +1 -0
  133. package/dist/components/blocks/feature-split/feature-split-impl.css +14 -1
  134. package/dist/components/blocks/feature-stack/FeatureStack.css +86 -0
  135. package/dist/components/blocks/feature-stack/FeatureStack.d.ts +19 -0
  136. package/dist/components/blocks/feature-stack/FeatureStack.js +27 -0
  137. package/dist/components/blocks/feature-stack/FeatureStack.types.d.ts +34 -0
  138. package/dist/components/blocks/feature-stack/FeatureStack.types.js +14 -0
  139. package/dist/components/blocks/feature-stack/feature-stack-impl.css +87 -0
  140. package/dist/components/blocks/image-banner/ImageBanner.css +133 -0
  141. package/dist/components/blocks/image-banner/ImageBanner.d.ts +32 -0
  142. package/dist/components/blocks/image-banner/ImageBanner.js +41 -0
  143. package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +54 -0
  144. package/dist/components/blocks/image-banner/ImageBanner.types.js +14 -0
  145. package/dist/components/blocks/image-banner/image-banner-impl.css +281 -0
  146. package/dist/components/blocks/landing-hero/LandingHero.css +1 -0
  147. package/dist/components/blocks/logo-columns/LogoColumns.css +1 -0
  148. package/dist/components/blocks/logo-rows/LogoRows.css +1 -0
  149. package/dist/components/blocks/page-header/PageHeader.css +1 -0
  150. package/dist/components/blocks/page-header/PageHeader.js +2 -2
  151. package/dist/components/blocks/page-header/PageHeader.types.d.ts +4 -2
  152. package/dist/components/blocks/page-header/PageHeader.types.js +1 -1
  153. package/dist/components/blocks/page-header/page-header-impl.css +5 -3
  154. package/dist/components/blocks/product-hero/ProductHero.css +8 -3
  155. package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -1
  156. package/dist/components/blocks/product-hero/ProductHero.js +5 -2
  157. package/dist/components/blocks/product-hero/ProductHero.types.d.ts +5 -1
  158. package/dist/components/blocks/product-hero/ProductHero.types.js +1 -0
  159. package/dist/components/blocks/product-hero/product-hero-impl.css +43 -12
  160. package/dist/components/blocks/resource-banner/ResourceBanner.css +106 -0
  161. package/dist/components/blocks/resource-banner/ResourceBanner.d.ts +32 -0
  162. package/dist/components/blocks/resource-banner/ResourceBanner.js +51 -0
  163. package/dist/components/blocks/resource-banner/ResourceBanner.types.d.ts +44 -0
  164. package/dist/components/blocks/resource-banner/ResourceBanner.types.js +14 -0
  165. package/dist/components/blocks/resource-banner/resource-banner-impl.css +69 -0
  166. package/dist/components/blocks/scene-scroll/SceneScroll.css +93 -0
  167. package/dist/components/blocks/scene-scroll/SceneScroll.d.ts +36 -0
  168. package/dist/components/blocks/scene-scroll/SceneScroll.js +54 -0
  169. package/dist/components/blocks/scene-scroll/SceneScroll.types.d.ts +32 -0
  170. package/dist/components/blocks/scene-scroll/SceneScroll.types.js +15 -0
  171. package/dist/components/blocks/scene-scroll/scene-scroll-impl.css +275 -0
  172. package/dist/components/blocks/stats/Stats.css +139 -0
  173. package/dist/components/blocks/stats/Stats.d.ts +24 -0
  174. package/dist/components/blocks/stats/Stats.js +120 -0
  175. package/dist/components/blocks/stats/Stats.types.d.ts +36 -0
  176. package/dist/components/blocks/stats/Stats.types.js +19 -0
  177. package/dist/components/blocks/stats/stats-count.d.ts +34 -0
  178. package/dist/components/blocks/stats/stats-count.js +61 -0
  179. package/dist/components/blocks/stats/stats-impl.css +284 -0
  180. package/dist/components/blocks/stats/use-wheel.d.ts +37 -0
  181. package/dist/components/blocks/stats/use-wheel.js +126 -0
  182. package/dist/components/blocks/steps/Steps.css +202 -0
  183. package/dist/components/blocks/steps/Steps.d.ts +32 -0
  184. package/dist/components/blocks/steps/Steps.js +65 -0
  185. package/dist/components/blocks/steps/Steps.types.d.ts +48 -0
  186. package/dist/components/blocks/steps/Steps.types.js +21 -0
  187. package/dist/components/blocks/steps/steps-impl.css +216 -0
  188. package/dist/components/blocks/steps/use-turn.d.ts +33 -0
  189. package/dist/components/blocks/steps/use-turn.js +75 -0
  190. package/dist/components/blocks/sticky-cards/StickyCards.css +154 -0
  191. package/dist/components/blocks/sticky-cards/StickyCards.d.ts +35 -0
  192. package/dist/components/blocks/sticky-cards/StickyCards.js +153 -0
  193. package/dist/components/blocks/sticky-cards/StickyCards.types.d.ts +43 -0
  194. package/dist/components/blocks/sticky-cards/StickyCards.types.js +18 -0
  195. package/dist/components/blocks/sticky-cards/sticky-cards-impl.css +393 -0
  196. package/dist/components/blocks/team-grid/TeamGrid.css +159 -0
  197. package/dist/components/blocks/team-grid/TeamGrid.d.ts +28 -0
  198. package/dist/components/blocks/team-grid/TeamGrid.js +74 -0
  199. package/dist/components/blocks/team-grid/TeamGrid.types.d.ts +51 -0
  200. package/dist/components/blocks/team-grid/TeamGrid.types.js +20 -0
  201. package/dist/components/blocks/team-grid/team-grid-impl.css +129 -0
  202. package/dist/components/blocks/testimonial-row/TestimonialRow.css +1 -0
  203. package/dist/components/blocks/testimonial-row/TestimonialRow.d.ts +1 -1
  204. package/dist/components/blocks/testimonial-row/TestimonialRow.js +4 -3
  205. package/dist/components/blocks/testimonial-row/TestimonialRow.types.d.ts +3 -0
  206. package/dist/components/blocks/testimonial-row/TestimonialRow.types.js +1 -1
  207. package/dist/components/blocks/testimonial-row/testimonial-row-impl.css +8 -0
  208. package/dist/components/blocks/testimonial-slider/TestimonialSlider.css +1 -0
  209. package/dist/components/blocks/testimonial-slider/TestimonialSlider.d.ts +1 -1
  210. package/dist/components/blocks/testimonial-slider/TestimonialSlider.js +2 -2
  211. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.d.ts +8 -0
  212. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.js +1 -1
  213. package/dist/components/blocks/text-scroll/TextScroll.css +53 -0
  214. package/dist/components/blocks/text-scroll/TextScroll.d.ts +5 -0
  215. package/dist/components/blocks/text-scroll/TextScroll.js +71 -0
  216. package/dist/components/blocks/text-scroll/TextScroll.types.d.ts +22 -0
  217. package/dist/components/blocks/text-scroll/TextScroll.types.js +10 -0
  218. package/dist/components/blocks/text-scroll/text-scroll-impl.css +115 -0
  219. package/dist/components/blocks/tiers/Tiers.css +233 -0
  220. package/dist/components/blocks/tiers/Tiers.d.ts +32 -0
  221. package/dist/components/blocks/tiers/Tiers.js +68 -0
  222. package/dist/components/blocks/tiers/Tiers.types.d.ts +45 -0
  223. package/dist/components/blocks/tiers/Tiers.types.js +25 -0
  224. package/dist/components/blocks/tiers/tiers-impl.css +185 -0
  225. package/dist/components/blocks/use-rotation.d.ts +12 -2
  226. package/dist/components/blocks/use-rotation.js +6 -28
  227. package/dist/components/comparison-cards-data.d.ts +59 -0
  228. package/dist/components/comparison-cards-data.js +12 -0
  229. package/dist/components/comparison-table-data.d.ts +39 -0
  230. package/dist/components/comparison-table-data.js +11 -0
  231. package/dist/components/core/accordion-item/AccordionItem.css +1 -5
  232. package/dist/components/core/background/Background.css +23 -0
  233. package/dist/components/core/background/Background.d.ts +10 -4
  234. package/dist/components/core/background/Background.js +16 -7
  235. package/dist/components/core/background/Background.types.d.ts +11 -8
  236. package/dist/components/core/background/Background.types.js +3 -1
  237. package/dist/components/core/background/background-impl.css +334 -1
  238. package/dist/components/core/background/halftone.d.ts +11 -0
  239. package/dist/components/core/background/halftone.js +43 -3
  240. package/dist/components/core/background/patterns.d.ts +66 -0
  241. package/dist/components/core/background/patterns.js +244 -0
  242. package/dist/components/core/background/shader.d.ts +44 -0
  243. package/dist/components/core/background/shader.js +307 -0
  244. package/dist/components/core/background/use-shader.d.ts +11 -0
  245. package/dist/components/core/background/use-shader.js +25 -0
  246. package/dist/components/core/background/use-spotlight.d.ts +2 -1
  247. package/dist/components/core/background/use-spotlight.js +9 -1
  248. package/dist/components/core/bottom-sheet/BottomSheet.js +2 -2
  249. package/dist/components/core/bottom-sheet/BottomSheet.types.d.ts +3 -1
  250. package/dist/components/core/breadcrumb/Breadcrumb.d.ts +1 -0
  251. package/dist/components/core/breadcrumb/Breadcrumb.js +1 -0
  252. package/dist/components/core/breadcrumb/breadcrumb-impl.css +11 -0
  253. package/dist/components/core/button/button-impl.css +14 -1
  254. package/dist/components/core/illustration-card/IllustrationCard.js +1 -1
  255. package/dist/components/core/illustration-card/illustration-card-impl.css +20 -0
  256. package/dist/components/core/illustration-scene/IllustrationScene.css +4 -7
  257. package/dist/components/core/illustration-scene/IllustrationScene.types.d.ts +2 -3
  258. package/dist/components/core/illustration-scene/illustration-scene-impl.css +7 -1
  259. package/dist/components/core/link/Link.js +7 -3
  260. package/dist/components/core/link/link-impl.css +29 -2
  261. package/dist/components/core/navbar/Navbar.css +2 -2
  262. package/dist/components/core/navbar/Navbar.types.d.ts +1 -1
  263. package/dist/components/core/navbar-item/NavbarItem.d.ts +2 -1
  264. package/dist/components/core/navbar-item/NavbarItem.js +3 -10
  265. package/dist/components/core/navbar-item/NavbarItem.types.d.ts +4 -1
  266. package/dist/components/core/navbar-item/NavbarItem.types.js +1 -1
  267. package/dist/components/core/navbar-item/navbar-item-impl.css +10 -0
  268. package/dist/components/core/navbar-menu/navbar-menu-impl.css +9 -0
  269. package/dist/components/core/navbar-switcher/navbar-switcher-impl.css +19 -0
  270. package/dist/components/core/pagination/Pagination.js +1 -1
  271. package/dist/components/core/pagination/pagination-impl.css +9 -0
  272. package/dist/components/core/pagination-dots/PaginationDots.d.ts +1 -1
  273. package/dist/components/core/pagination-dots/PaginationDots.js +3 -3
  274. package/dist/components/core/pagination-dots/PaginationDots.types.d.ts +3 -1
  275. package/dist/components/core/segmented-control/SegmentedControl.css +4 -3
  276. package/dist/components/core/segmented-control/SegmentedControl.js +17 -3
  277. package/dist/components/core/segmented-control-item/SegmentedControlItem.css +2 -1
  278. package/dist/components/core/select/Select.js +3 -3
  279. package/dist/components/core/select/Select.types.d.ts +11 -1
  280. package/dist/components/core/select/Select.types.js +1 -1
  281. package/dist/components/core/tab-list/TabList.js +9 -1
  282. package/dist/components/core/table-header-cell/TableHeaderCell.d.ts +1 -1
  283. package/dist/components/core/table-header-cell/TableHeaderCell.js +5 -2
  284. package/dist/components/core/table-header-cell/TableHeaderCell.types.d.ts +7 -1
  285. package/dist/components/core/table-header-cell/TableHeaderCell.types.js +1 -1
  286. package/dist/components/core/text-area/TextArea.d.ts +1 -1
  287. package/dist/components/core/text-area/TextArea.js +2 -2
  288. package/dist/components/core/text-area/TextArea.types.d.ts +9 -1
  289. package/dist/components/core/text-area/TextArea.types.js +1 -1
  290. package/dist/components/core/text-input/TextInput.d.ts +1 -1
  291. package/dist/components/core/text-input/TextInput.js +2 -2
  292. package/dist/components/core/text-input/TextInput.types.d.ts +9 -1
  293. package/dist/components/core/text-input/TextInput.types.js +1 -1
  294. package/dist/components/data-table-data.d.ts +33 -0
  295. package/dist/components/data-table-data.js +12 -0
  296. package/dist/components/download-banner-data.d.ts +52 -0
  297. package/dist/components/download-banner-data.js +11 -0
  298. package/dist/components/index.d.ts +45 -0
  299. package/dist/components/index.js +32 -0
  300. package/dist/components/resource-banner-data.d.ts +34 -0
  301. package/dist/components/resource-banner-data.js +13 -0
  302. package/dist/components/scene-scroll-data.d.ts +24 -0
  303. package/dist/components/scene-scroll-data.js +9 -0
  304. package/dist/components/stats-data.d.ts +28 -0
  305. package/dist/components/stats-data.js +11 -0
  306. package/dist/components/steps-data.d.ts +38 -0
  307. package/dist/components/steps-data.js +13 -0
  308. package/dist/components/sticky-cards-data.d.ts +43 -0
  309. package/dist/components/sticky-cards-data.js +12 -0
  310. package/dist/components/team-grid-data.d.ts +38 -0
  311. package/dist/components/team-grid-data.js +11 -0
  312. package/dist/components/tiers-data.d.ts +47 -0
  313. package/dist/components/tiers-data.js +13 -0
  314. package/dist/marketing.css +77 -28
  315. package/package.json +1 -1
@@ -0,0 +1,160 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useId, useRef, useState } from 'react';
4
+ import { ChevronLeftRegularIcon, ChevronRightRegularIcon } from '@deriv-com/quill-icons-web';
5
+ import './FeatureCardsCarousel.css';
6
+ import './feature-cards-carousel-impl.css';
7
+ import '../block-reveal.css';
8
+ import { given } from '../../given.js';
9
+ import { ButtonIcon } from '../../core/button-icon/ButtonIcon.js';
10
+ import { PaginationDots } from '../../core/pagination-dots/PaginationDots.js';
11
+ import { cardsOf } from '../cards-of.js';
12
+ import { arriving, useReveal } from '../use-reveal.js';
13
+ import { featureCardsCarouselDefaults, featureCardsCarouselParts, } from './FeatureCardsCarousel.types.js';
14
+ /** Close enough to an end to call it the end: a snapped row can rest a pixel short of it. */
15
+ const AT_END = 2;
16
+ const OPENING = { stops: 1, current: 0, back: false, on: true };
17
+ /**
18
+ * The row's own measures, read rather than assumed: one card and the gap after it, and how far the
19
+ * row can scroll at all.
20
+ */
21
+ function measure(row) {
22
+ const card = row.firstElementChild;
23
+ const gap = Number.parseFloat(getComputedStyle(row).columnGap) || 0;
24
+ return {
25
+ step: (card?.offsetWidth ?? row.clientWidth) + gap,
26
+ furthest: Math.max(0, row.scrollWidth - row.clientWidth),
27
+ };
28
+ }
29
+ /**
30
+ * A heading over a row of the page's own cards, moved along a stop at a time — the reference site's
31
+ * FeatureCardsCarousel.
32
+ *
33
+ * The cards are Card or CardButton with a CardBody inside, as in FeatureCards: a card's fill, its
34
+ * picture and where it leads are the card's, so the reference's coral, dark and light slides are
35
+ * Card's accent, accent-strong and surface-light, and its cover picture is CardBody's overMedia.
36
+ * What this decides is the heading, the row and the way along it.
37
+ *
38
+ * The browser does the scrolling and the snapping, TestimonialRow's way; this reads where the row
39
+ * is. A stop is a card's start, until the last few cards are all in view — those share the last
40
+ * stop, the row's end, so the row never scrolls into empty space and there are fewer dots than
41
+ * cards at full width. The dots say which stop the row is at and move to any one pressed; the
42
+ * arrows step one either way.
43
+ *
44
+ * Autoplay is off unless asked for. On, PaginationDots holds the clock, as it does for every block
45
+ * that rotates: each motion.duration.dwell it reports the next stop, wrapping from the last to the
46
+ * first, and this scrolls there. It holds while the pointer is over the band or focus is inside it,
47
+ * never runs for a reader who asked for less motion, and stops for good once the reader presses
48
+ * anything in the band or swipes the row.
49
+ *
50
+ * `'use client'` for the row's position, the clock, the arrival, and `useId`, which names the
51
+ * region by its heading.
52
+ */
53
+ export function FeatureCardsCarousel({ ground = featureCardsCarouselDefaults.ground, title, description, autoplay = featureCardsCarouselDefaults.autoplay, label = featureCardsCarouselDefaults.label, previousLabel = featureCardsCarouselDefaults.previousLabel, nextLabel = featureCardsCarouselDefaults.nextLabel, controlsLabel = featureCardsCarouselDefaults.controlsLabel, stopLabel = featureCardsCarouselDefaults.stopLabel, children, }) {
54
+ const titleId = useId();
55
+ const [band, revealed] = useReveal();
56
+ const row = useRef(null);
57
+ const back = useRef(null);
58
+ const on = useRef(null);
59
+ const [place, setPlace] = useState(OPENING);
60
+ // The same answer, read outside a render: the focus hand-off below compares against it, and a
61
+ // state updater is no place for a side effect — React may call one twice.
62
+ const was = useRef(place);
63
+ /*
64
+ A pointer over the band or focus inside it holds the clock for as long as it lasts; a press or a
65
+ swipe stops it for good — CtaBannerSlider's rules, the WAI carousel pattern's. PaginationDots
66
+ keeps its own stop for a dot pressed; these cover the rest of the band.
67
+ */
68
+ const [held, setHeld] = useState(false);
69
+ const [stopped, setStopped] = useState(false);
70
+ const cards = cardsOf(children);
71
+ const read = useCallback(() => {
72
+ const element = row.current;
73
+ if (element === null)
74
+ return;
75
+ const { step, furthest } = measure(element);
76
+ // `abs`, because a right-to-left row counts its scroll position down from zero.
77
+ const at = Math.abs(element.scrollLeft);
78
+ const stops = furthest <= AT_END || step <= 0 ? 1 : Math.ceil((furthest - AT_END) / step) + 1;
79
+ const current = at >= furthest - AT_END ? stops - 1 : Math.min(stops - 1, Math.round(at / step));
80
+ const next = { stops, current, back: at > AT_END, on: at < furthest - AT_END };
81
+ const before = was.current;
82
+ if (before.stops === next.stops &&
83
+ before.current === next.current &&
84
+ before.back === next.back &&
85
+ before.on === next.on) {
86
+ return;
87
+ }
88
+ // An arrow pressed to the end of the row is about to go inert with focus on it, which drops
89
+ // focus to the page. Hand it to the other arrow first, which has somewhere to go.
90
+ const focused = document.activeElement;
91
+ if (before.on && !next.on && on.current?.contains(focused))
92
+ back.current?.querySelector('button')?.focus();
93
+ if (before.back && !next.back && back.current?.contains(focused))
94
+ on.current?.querySelector('button')?.focus();
95
+ was.current = next;
96
+ setPlace(next);
97
+ }, []);
98
+ useEffect(() => {
99
+ const element = row.current;
100
+ if (element === null)
101
+ return;
102
+ read();
103
+ // Asked defensively: a renderer with no ResizeObserver measures once, on mount.
104
+ if (typeof ResizeObserver === 'undefined')
105
+ return;
106
+ const watching = new ResizeObserver(read);
107
+ watching.observe(element);
108
+ return () => watching.disconnect();
109
+ // The count too: a page that swaps the set at the same width leaves the row's own box the size
110
+ // it was, so nothing above would fire, and the dots would describe the old set.
111
+ }, [read, cards.length]);
112
+ // Nothing to show is nothing drawn: a named region with no card in it leads nowhere.
113
+ if (cards.length === 0)
114
+ return null;
115
+ /** Scroll the row to a stop: a card's start, or the row's end for the last. */
116
+ const go = (to) => {
117
+ const element = row.current;
118
+ if (element === null)
119
+ return;
120
+ const { step, furthest } = measure(element);
121
+ const stop = Math.min(Math.max(0, to), place.stops - 1);
122
+ const left = stop >= place.stops - 1 ? furthest : stop * step;
123
+ const style = getComputedStyle(element);
124
+ const still = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
125
+ element.scrollTo({
126
+ left: left * (style.direction === 'rtl' ? -1 : 1),
127
+ behavior: still ? 'auto' : 'smooth',
128
+ });
129
+ };
130
+ const scrolls = place.back || place.on;
131
+ // Button's secondary, as a circle; on the dark band its inverse tone turns the tint white.
132
+ const arrow = { variant: 'secondary', tone: ground === 'strong' ? 'inverse' : 'default' };
133
+ // Any primary press in the band stops the clock; the press itself still does what it does.
134
+ const press = (event) => {
135
+ if (event.isPrimary && (event.pointerType !== 'mouse' || event.button === 0))
136
+ setStopped(true);
137
+ };
138
+ return (_jsx("section", { className: featureCardsCarouselParts.root, "data-ground": ground, "aria-labelledby": titleId, onPointerEnter: (event) => {
139
+ if (event.pointerType === 'mouse')
140
+ setHeld(true);
141
+ }, onPointerLeave: (event) => {
142
+ if (event.pointerType === 'mouse')
143
+ setHeld(false);
144
+ }, onPointerDownCapture: press,
145
+ // A keyboard press on an arrow is a press too; a pointer's has already stopped it.
146
+ onClickCapture: () => setStopped(true), onFocus: () => setHeld(true), onBlur: (event) => {
147
+ // Held while focus is anywhere inside, released only when it leaves the band altogether.
148
+ if (!event.currentTarget.contains(event.relatedTarget))
149
+ setHeld(false);
150
+ }, children: _jsxs("div", { className: featureCardsCarouselParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: featureCardsCarouselParts.header, children: [_jsx("h2", { className: featureCardsCarouselParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: featureCardsCarouselParts.description, ...arriving(1), children: description })) : null] }), _jsxs("div", { className: featureCardsCarouselParts.carousel, children: [_jsx("div", { ref: row, className: featureCardsCarouselParts.track, role: "group", "aria-label": label, tabIndex: 0, onScroll: read, ...arriving(2), children: cards.map(({ key, card }) => (
151
+ /* Each card in a place of its own, so the card is the page's element and keeps its
152
+ own transitions; the place is what takes the row's measure. */
153
+ _jsx("div", { className: featureCardsCarouselParts.item, children: card }, key))) }), cards.length > 1 ? (_jsxs("div", { className: featureCardsCarouselParts.footer, hidden: !scrolls, ...arriving(3), children: [_jsx(PaginationDots
154
+ // The coral marks on the page's own white, and the band's ink on a painted one —
155
+ // PaginationDots' own ruling on where its accent belongs.
156
+ , {
157
+ // The coral marks on the page's own white, and the band's ink on a painted one —
158
+ // PaginationDots' own ruling on where its accent belongs.
159
+ variant: ground === 'page' ? 'accent' : 'default', page: place.current + 1, totalPages: place.stops, onPageChange: (page) => go(page - 1), autoplay: autoplay, paused: held || stopped, label: controlsLabel, itemLabel: stopLabel }), _jsxs("div", { className: featureCardsCarouselParts.nav, children: [_jsx("span", { ref: back, className: featureCardsCarouselParts['nav-slot'], "data-available": place.back, inert: !place.back, children: _jsx(ButtonIcon, { icon: _jsx(ChevronLeftRegularIcon, {}), label: previousLabel, ...arrow, onClick: () => go(place.current - 1) }) }), _jsx("span", { ref: on, className: featureCardsCarouselParts['nav-slot'], "data-available": place.on, inert: !place.on, children: _jsx(ButtonIcon, { icon: _jsx(ChevronRightRegularIcon, {}), label: nextLabel, ...arrow, onClick: () => go(place.current + 1) }) })] })] })) : null] })] }) }));
160
+ }
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface FeatureCardsCarouselOwnProps {
3
+ /**
4
+ * `page` — The default, and the page's own white. A section that has nothing to separate itself from what is above it — most of them — and the ground a reader is already on
5
+ * `subtle` — A quiet step off the page, for a band that wants to read as its own section without changing the page's voice. The one to reach for first when two sections need telling apart. A light card — surface-light — turns white on it, color.bg.surface-card-light-raised, because its own gray.50 is the band's and would vanish into it. A dark card — accent-strong — is lifted to color.bg.surface-card-strong-raised, a step lighter in dark mode, where the band is within a shade of its gray.900. A plain surface card is raised off it the same way, to color.bg.surface-card-raised, because the recessed fill is the band's gray too. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section — in dark mode the tint alone took on whatever it was set against, and on a light surface it was light ink on white.
6
+ * `strong` — `color.bg.brand-strong`, the dark band. It stops the page rather than dividing it, so it is for a section a reader should arrive at — and one per page is usually one more than enough. The coloured cards keep their own grounds on it; a neutral card — surface or surface-raised — turns to Card's inverse fill and white ink, and a plain card takes the white ink alone, because their own would be dark ink on near-black; an eyebrow inside any of them takes the band's quieter ink
7
+ */
8
+ ground?: 'page' | 'subtle' | 'strong';
9
+ /** Always. What the cards are, in the fewest words that name them — "What you get", "Built for every trader". */
10
+ title: string;
11
+ /** A line saying what the set has in common, when the title alone does not. */
12
+ description?: string;
13
+ /** Whether the row moves on to the next stop on its own, holding each for motion.duration.dwell and returning to the start after the last. Off by default, so a row nobody configured stays still. On, it holds while the pointer is over the band or focus is inside it, never moves for a reader who asked for less motion, and stops for good once the reader presses anything in it — a card, an arrow, a dot — or swipes the row: the WAI carousel pattern's rule that a reader who has touched it is reading, not watching. That gives a reader a way to stop it, which WCAG 2.2.2 asks for, but not a visible one, as in CtaBannerSlider: a page that needs 2.2.2 met plainly should leave it off. */
14
+ autoplay?: boolean;
15
+ /** The name the row is announced by. The row is a scrolling region a keyboard has to be able to reach, so it carries a tab stop and a name of its own — Scroller's rule, as in TestimonialRow. Its own name rather than the section's, so a screen reader moving into it hears what it is. Translate it with the page. */
16
+ label?: string;
17
+ /** The accessible name of the arrow stepping the row back. A glyph announces nothing, so this is the whole of the control's name. Translate it with the page, and keep it parallel to nextLabel. */
18
+ previousLabel?: string;
19
+ /** The accessible name of the arrow stepping the row on, for the reason previousLabel exists. */
20
+ nextLabel?: string;
21
+ /** The name the dots are announced by, as a group inside the band. Its own name rather than the row's, so a screen reader moving into the dots hears what they are. Translate it with the page, like label. */
22
+ controlsLabel?: string;
23
+ /** The word each dot is announced by, before its number — "Position 2". A dot is a place the row stops, not a card: at full width the last few cards share the last stop, so there are fewer dots than cards. Translate it with the page, as controlsLabel is. */
24
+ stopLabel?: string;
25
+ /** Allowed: Card, CardButton */
26
+ children: ReactNode;
27
+ }
28
+ export type FeatureCardsCarouselProps = FeatureCardsCarouselOwnProps;
29
+ /** The class each part carries. Apply these — do not retype them. */
30
+ export declare const featureCardsCarouselParts: {
31
+ readonly root: "dds-featurecardscarousel";
32
+ readonly inner: "dds-featurecardscarousel__inner";
33
+ readonly header: "dds-featurecardscarousel__header";
34
+ readonly title: "dds-featurecardscarousel__title";
35
+ readonly description: "dds-featurecardscarousel__description";
36
+ readonly carousel: "dds-featurecardscarousel__carousel";
37
+ readonly track: "dds-featurecardscarousel__track";
38
+ readonly item: "dds-featurecardscarousel__item";
39
+ readonly footer: "dds-featurecardscarousel__footer";
40
+ readonly nav: "dds-featurecardscarousel__nav";
41
+ readonly 'nav-slot': "dds-featurecardscarousel__nav-slot";
42
+ };
43
+ /** Generated so defaults cannot drift from the contract. */
44
+ export declare const featureCardsCarouselDefaults: {
45
+ readonly ground: "page";
46
+ readonly autoplay: false;
47
+ readonly label: "Features";
48
+ readonly previousLabel: "Previous cards";
49
+ readonly nextLabel: "Next cards";
50
+ readonly controlsLabel: "Positions";
51
+ readonly stopLabel: "Position";
52
+ };
@@ -0,0 +1,17 @@
1
+ // GENERATED from feature-cards-carousel.contract.json. Do not edit.
2
+ /** The class each part carries. Apply these — do not retype them. */
3
+ export const featureCardsCarouselParts = {
4
+ root: 'dds-featurecardscarousel',
5
+ inner: 'dds-featurecardscarousel__inner',
6
+ header: 'dds-featurecardscarousel__header',
7
+ title: 'dds-featurecardscarousel__title',
8
+ description: 'dds-featurecardscarousel__description',
9
+ carousel: 'dds-featurecardscarousel__carousel',
10
+ track: 'dds-featurecardscarousel__track',
11
+ item: 'dds-featurecardscarousel__item',
12
+ footer: 'dds-featurecardscarousel__footer',
13
+ nav: 'dds-featurecardscarousel__nav',
14
+ 'nav-slot': 'dds-featurecardscarousel__nav-slot',
15
+ };
16
+ /** Generated so defaults cannot drift from the contract. */
17
+ export const featureCardsCarouselDefaults = { ground: 'page', autoplay: false, label: 'Features', previousLabel: 'Previous cards', nextLabel: 'Next cards', controlsLabel: 'Positions', stopLabel: 'Position' };
@@ -0,0 +1,207 @@
1
+ /*
2
+ * Scoped to this package, like the generated sheet beside it.
3
+ */
4
+
5
+ /*
6
+ * A heading with no line under it closes up. Its bound step is the distance to the line, and with
7
+ * nothing there it would only add to the step from the header to the row.
8
+ */
9
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__title:last-child {
10
+ margin-block-end: 0;
11
+ }
12
+
13
+ /*
14
+ * The header starts on a phone and is centred from the fold — FeatureCards' arrangement and its
15
+ * reason. Both boxes are capped narrower than the column, so `margin-inline: auto` is what centres
16
+ * them rather than their words inside a box left at the start edge.
17
+ *
18
+ * `48rem` longhand because `var()` is not permitted in a container query condition, and it is the
19
+ * width the rest of this package folds at.
20
+ */
21
+ @container (min-width: 48rem) {
22
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__header {
23
+ align-items: center;
24
+ margin-inline: auto;
25
+ text-align: center;
26
+ }
27
+
28
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__description {
29
+ margin-inline: auto;
30
+ }
31
+ }
32
+
33
+ /*
34
+ * Two measures the row is built from, both read off the band rather than the row — TestimonialRow's.
35
+ *
36
+ * The root declares `container-type`, so `cqi` here is the band's width. `--column` is the width of
37
+ * the column the heading sits in; `--bleed` is how far that column stands in from the band's edge.
38
+ */
39
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel {
40
+ --dds-featurecardscarousel-column: calc(min(100cqi, var(--size-content-max)) - 2 * var(--space-inset-page));
41
+ --dds-featurecardscarousel-bleed: calc((100cqi - min(100cqi, var(--size-content-max))) / 2 + var(--space-inset-page));
42
+ }
43
+
44
+ /*
45
+ * The row runs out to the band's edges and its first card stands where the heading does — the
46
+ * reference design's breakout, built as TestimonialRow builds it.
47
+ *
48
+ * A negative margin takes the row out of the column to the band's edges; a leading padding of the
49
+ * same measure brings the first card back into line with the heading. The padding scrolls away with
50
+ * the cards, so a card is then cut off by the band rather than by the column.
51
+ * `scroll-padding-inline` matches it, or a browser reads the resting position as short of the first
52
+ * card's snap point and scrolls the gutter away on load.
53
+ *
54
+ * The block padding is room for a CardButton's lift and its focus ring, which a scroll container
55
+ * would otherwise clip; the negative margin gives it back.
56
+ *
57
+ * No scrollbar: the dots and the arrows under the row are the standing sign that it moves.
58
+ */
59
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__track {
60
+ margin-block: calc(-1 * var(--space-gap-sm));
61
+ margin-inline: calc(-1 * var(--dds-featurecardscarousel-bleed));
62
+ overflow-x: auto;
63
+ overflow-y: hidden;
64
+ overscroll-behavior-x: contain;
65
+ padding-block: var(--space-gap-sm);
66
+ padding-inline-start: var(--dds-featurecardscarousel-bleed);
67
+ scroll-padding-inline: var(--dds-featurecardscarousel-bleed);
68
+ scroll-snap-type: x proximity;
69
+ scrollbar-width: none;
70
+ }
71
+
72
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__track::-webkit-scrollbar {
73
+ display: none;
74
+ }
75
+
76
+ /*
77
+ * The row's focus ring is drawn inside it. The row runs out to the band's edges and the band hides
78
+ * its overflow, so a ring drawn outside would lose its two sides to the band.
79
+ */
80
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__track:focus-visible {
81
+ outline-offset: calc(-1 * var(--border-width-focus));
82
+ }
83
+
84
+ /*
85
+ * The trailing gutter, as a spacer rather than a padding.
86
+ *
87
+ * A scroll container leaves its end padding out of what it scrolls to once its content overflows,
88
+ * so the last card would come to rest against the band's edge. A generated box is a flex item like
89
+ * a card, and its width counts. The negative margin takes back the gap the row puts before it.
90
+ */
91
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__track::after {
92
+ content: '';
93
+ flex: none;
94
+ inline-size: var(--dds-featurecardscarousel-bleed);
95
+ margin-inline-start: calc(-1 * var(--space-grid-gutter));
96
+ }
97
+
98
+ /*
99
+ * A card's width: most of the column on a phone — 85%, TestimonialRow's share — so the next card's
100
+ * edge shows past it, which is what says the row moves; size.feature-cards-carousel.card from the
101
+ * fold, held to the column, so at full width the row shows the next card's edge the same way. The
102
+ * reference set a phone's card to the whole column, and a row whose next card starts exactly at
103
+ * the screen's edge reads as a single card.
104
+ *
105
+ * It does not shrink: a flex item shrinks to fit before it overflows, and a row of squeezed cards
106
+ * is the one thing this is not. Every card is as tall as the row, which is the tallest of them.
107
+ */
108
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__item {
109
+ flex: none;
110
+ inline-size: calc(var(--dds-featurecardscarousel-column) * 0.85);
111
+ scroll-snap-align: start;
112
+ }
113
+
114
+ @container (min-width: 48rem) {
115
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__item {
116
+ inline-size: min(var(--size-feature-cards-carousel-card), var(--dds-featurecardscarousel-column));
117
+ }
118
+ }
119
+
120
+ /* `hidden` has to win over the part's own `display`, which outranks the browser's rule for it. */
121
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__footer[hidden] {
122
+ display: none;
123
+ }
124
+
125
+ /*
126
+ * The arrows stand at the end of the footer, after the dots — and at its end on their own too,
127
+ * when the row has fewer stops than PaginationDots draws a row for.
128
+ */
129
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__nav {
130
+ margin-inline-start: auto;
131
+ }
132
+
133
+ /*
134
+ * An arrow with nowhere to go stays in its place, receded to opacity.disabled, and its slot is
135
+ * inert — TestimonialRow's rule and its reason: ButtonIcon has no disabled state of its own.
136
+ */
137
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__nav-slot[data-available='false'] {
138
+ opacity: var(--opacity-disabled);
139
+ }
140
+
141
+ /*
142
+ * The cards on a painted ground — FeatureCards' rules, for the same cards on the same grounds.
143
+ *
144
+ * A light card turns white on the subtle band, where its own gray.50 is the band's; a dark card is
145
+ * lifted a step in dark mode; a surface card is raised off it. On the dark band a neutral card
146
+ * turns round to the inverse fill and white ink, a plain card takes the white ink alone, and an
147
+ * eyebrow inside any of them takes the band's quieter ink. The coloured cards keep their own.
148
+ */
149
+ :where([data-dds-context="marketing"])
150
+ .dds-featurecardscarousel[data-ground="subtle"]
151
+ .dds-featurecardscarousel__item
152
+ > :is(.dds-card, .dds-cardbutton)[data-variant="surface-light"] {
153
+ background-color: var(--color-bg-surface-card-light-raised);
154
+ }
155
+
156
+ :where([data-dds-context="marketing"])
157
+ .dds-featurecardscarousel[data-ground="subtle"]
158
+ .dds-featurecardscarousel__item
159
+ > :is(.dds-card, .dds-cardbutton)[data-variant="accent-strong"] {
160
+ background-color: var(--color-bg-surface-card-strong-raised);
161
+ }
162
+
163
+ :where([data-dds-context="marketing"])
164
+ .dds-featurecardscarousel[data-ground="subtle"]
165
+ .dds-featurecardscarousel__item
166
+ > :is(.dds-card, .dds-cardbutton)[data-variant="surface"] {
167
+ background-color: var(--color-bg-surface-card-raised);
168
+ }
169
+
170
+ :where([data-dds-context="marketing"])
171
+ .dds-featurecardscarousel[data-ground="strong"]
172
+ .dds-featurecardscarousel__item
173
+ > :is(.dds-card, .dds-cardbutton)[data-variant="surface"] {
174
+ background-color: var(--color-bg-surface-inverse);
175
+ color: var(--color-fg-on-brand-strong);
176
+ }
177
+
178
+ :where([data-dds-context="marketing"])
179
+ .dds-featurecardscarousel[data-ground="strong"]
180
+ .dds-featurecardscarousel__item
181
+ > :is(.dds-card, .dds-cardbutton)[data-variant="surface-raised"] {
182
+ background-color: var(--color-bg-surface-raised-on-brand-strong);
183
+ color: var(--color-fg-on-brand-strong);
184
+ }
185
+
186
+ :where([data-dds-context="marketing"])
187
+ .dds-featurecardscarousel[data-ground="strong"]
188
+ .dds-featurecardscarousel__item
189
+ > :is(.dds-card, .dds-cardbutton)[data-variant="plain"] {
190
+ color: var(--color-fg-on-brand-strong);
191
+ }
192
+
193
+ :where([data-dds-context="marketing"])
194
+ .dds-featurecardscarousel[data-ground="strong"]
195
+ .dds-featurecardscarousel__item
196
+ > :is(.dds-card, .dds-cardbutton):is([data-variant="surface"], [data-variant="surface-raised"], [data-variant="plain"])
197
+ .dds-cardbody__eyebrow {
198
+ color: var(--color-fg-muted-inverse);
199
+ }
200
+
201
+ /*
202
+ * Previous and next point along the row. A right-to-left row puts previous on the right, so the
203
+ * chevrons are mirrored to point the way each one moves; as they ship, both pointed backwards.
204
+ */
205
+ :where([data-dds-context="marketing"]) .dds-featurecardscarousel .dds-featurecardscarousel__nav-slot svg:dir(rtl) {
206
+ scale: -1 1;
207
+ }
@@ -6,6 +6,7 @@
6
6
  display: flex;
7
7
  inline-size: 100%;
8
8
  justify-content: center;
9
+ overflow-wrap: anywhere;
9
10
  }
10
11
 
11
12
  :where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__inner {
@@ -6,6 +6,7 @@
6
6
  display: flex;
7
7
  inline-size: 100%;
8
8
  justify-content: center;
9
+ overflow-wrap: anywhere;
9
10
  }
10
11
 
11
12
  :where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__inner {
@@ -77,7 +77,20 @@
77
77
  :where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media > *,
78
78
  :where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media img {
79
79
  block-size: 100%;
80
- display: block;
81
80
  inline-size: 100%;
82
81
  object-fit: cover;
83
82
  }
83
+
84
+ /*
85
+ * Bare media is a block; a component keeps its own display.
86
+ *
87
+ * `display: block` is for what arrives as a raw element — an image, a video, an inline SVG — which
88
+ * sits on a text baseline otherwise and leaves a gap under it. Written at no weight at all, so a
89
+ * component the page placed here — an IllustrationScene, whose grid lays its pane over its photo —
90
+ * keeps the display its own sheet gives it. In the rule above, a class deeper than the component's
91
+ * root, it turned the scene's grid into a block, and the pane fell below the photo and was clipped.
92
+ */
93
+ :where([data-dds-context="marketing"] .dds-featuresplit .dds-featuresplit__media > *, [data-dds-context="marketing"] .dds-featuresplit .dds-featuresplit__media img) {
94
+ display: block;
95
+ }
96
+
@@ -0,0 +1,86 @@
1
+ /* GENERATED from featurestack.contract.json. Do not edit. */
2
+
3
+ :where([data-dds-context="marketing"]) .dds-featurestack {
4
+ box-sizing: border-box;
5
+ container-type: inline-size;
6
+ display: flex;
7
+ inline-size: 100%;
8
+ justify-content: center;
9
+ overflow-wrap: anywhere;
10
+ }
11
+
12
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__inner {
13
+ box-sizing: border-box;
14
+ display: flex;
15
+ flex-direction: column;
16
+ inline-size: 100%;
17
+ min-inline-size: 0;
18
+ max-inline-size: var(--size-content-max);
19
+ padding-block: var(--space-section-default);
20
+ padding-inline: var(--space-inset-page);
21
+ row-gap: var(--space-heading-content);
22
+ }
23
+
24
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__header {
25
+ display: flex;
26
+ flex-direction: column;
27
+ min-inline-size: 0;
28
+ max-inline-size: var(--size-measure-heading);
29
+ row-gap: var(--space-gap-none);
30
+ }
31
+
32
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__title {
33
+ margin-block: 0;
34
+ text-wrap: balance;
35
+ font-family: var(--font-h2-family);
36
+ font-size: var(--font-h2-size);
37
+ font-weight: var(--font-h2-weight);
38
+ letter-spacing: var(--font-h2-letter-spacing);
39
+ line-height: var(--font-h2-line-height);
40
+ margin-block-end: var(--space-heading-title-description);
41
+ }
42
+
43
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__description {
44
+ margin-block: 0;
45
+ font-family: var(--font-body-md-family);
46
+ font-size: var(--font-body-md-size);
47
+ font-weight: var(--font-body-md-weight);
48
+ letter-spacing: var(--font-body-md-letter-spacing);
49
+ line-height: var(--font-body-md-line-height);
50
+ max-inline-size: var(--size-measure-description);
51
+ }
52
+
53
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__link {
54
+ display: flex;
55
+ margin-block-start: var(--space-heading-description-actions);
56
+ }
57
+
58
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__media {
59
+ align-self: center;
60
+ min-inline-size: 0;
61
+ overflow: clip;
62
+ border-start-end-radius: var(--radius-container);
63
+ border-start-start-radius: var(--radius-container);
64
+ max-inline-size: var(--size-feature-stack-media);
65
+ }
66
+
67
+ :where([data-dds-context="marketing"]) .dds-featurestack .dds-featurestack__image {
68
+ display: block;
69
+ }
70
+
71
+ :where([data-dds-context="marketing"]) .dds-featurestack[data-ground="page"] {
72
+ background-color: var(--color-bg-page);
73
+ color: var(--color-fg-default);
74
+ }
75
+
76
+ :where([data-dds-context="marketing"]) .dds-featurestack[data-ground="strong"] {
77
+ background-color: var(--color-bg-brand-strong);
78
+ color: var(--color-fg-on-brand-strong);
79
+ }
80
+
81
+ :where([data-dds-context="marketing"]) .dds-featurestack[data-ground="subtle"] {
82
+ background-color: var(--color-bg-page);
83
+ background-image: linear-gradient(var(--color-bg-band-subtle), var(--color-bg-band-subtle));
84
+ color: var(--color-fg-default);
85
+ }
86
+
@@ -0,0 +1,19 @@
1
+ import './FeatureStack.css';
2
+ import './feature-stack-impl.css';
3
+ import '../block-reveal.css';
4
+ import { type FeatureStackProps } from './FeatureStack.types.js';
5
+ export type { FeatureStackProps };
6
+ /**
7
+ * A heading, a line, a way on, and an illustration the band ends on — FeatureSplit stacked, the
8
+ * words over the picture rather than beside it. The reference design's PartnersPayout layout, taken
9
+ * down to its frame.
10
+ *
11
+ * The reference drew figures between the heading and the picture. Those were the page's content,
12
+ * not the layout's: a page that wants figures puts them in a section of its own or in its copy, and
13
+ * this keeps only what every use of the layout shares — the words centred over a picture that
14
+ * stands on the band's foot.
15
+ *
16
+ * It arrives the way every block does: the heading, the line, the link, then the picture.
17
+ * `'use client'` for that and for `useId`, which names the region by its heading.
18
+ */
19
+ export declare function FeatureStack({ ground, title, description, link, image, }: FeatureStackProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import './FeatureStack.css';
5
+ import './feature-stack-impl.css';
6
+ import '../block-reveal.css';
7
+ import { given } from '../../given.js';
8
+ import { arriving, useReveal } from '../use-reveal.js';
9
+ import { featureStackDefaults, featureStackParts, } from './FeatureStack.types.js';
10
+ /**
11
+ * A heading, a line, a way on, and an illustration the band ends on — FeatureSplit stacked, the
12
+ * words over the picture rather than beside it. The reference design's PartnersPayout layout, taken
13
+ * down to its frame.
14
+ *
15
+ * The reference drew figures between the heading and the picture. Those were the page's content,
16
+ * not the layout's: a page that wants figures puts them in a section of its own or in its copy, and
17
+ * this keeps only what every use of the layout shares — the words centred over a picture that
18
+ * stands on the band's foot.
19
+ *
20
+ * It arrives the way every block does: the heading, the line, the link, then the picture.
21
+ * `'use client'` for that and for `useId`, which names the region by its heading.
22
+ */
23
+ export function FeatureStack({ ground = featureStackDefaults.ground, title, description, link, image, }) {
24
+ const titleId = useId();
25
+ const [band, revealed] = useReveal();
26
+ return (_jsx("section", { className: featureStackParts.root, "data-ground": ground, "aria-labelledby": titleId, children: _jsxs("div", { className: featureStackParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: featureStackParts.header, children: [_jsx("h2", { className: featureStackParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: featureStackParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureStackParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { className: featureStackParts.media, ...arriving(3), children: image ? (_jsx("img", { className: featureStackParts.image, src: image.src, alt: image.alt, width: image.width, height: image.height, loading: "lazy", decoding: "async" })) : null })] }) }));
27
+ }
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { BlockImage } from '../../block-image-data.js';
3
+ export interface FeatureStackOwnProps {
4
+ /**
5
+ * `page` — The default, and the page's own white
6
+ * `subtle` — The grey band, for a section that needs setting apart from the one above it. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section
7
+ * `strong` — `color.bg.brand-strong`, the dark band, and the reference design's ground for this layout. The heading and the line under it turn white; an illustration made for a dark ground sits best here
8
+ */
9
+ ground?: 'page' | 'subtle' | 'strong';
10
+ /** Always. The statement the section makes, in a few words — "Trusted in 190+ countries", "One account, every device". */
11
+ title: string;
12
+ /** A short line under the heading that says what the statement means for the reader, or what the picture shows. */
13
+ description?: string;
14
+ /** A way on for the reader who wants more — "Learn more", "See where we operate". A standalone Link, under the line, the way FeatureCards takes one. */
15
+ link?: ReactNode;
16
+ /** Always. The picture the section ends on — a globe, a map, a product shot — which the block draws at its own proportions: a square picture stays square, a tall one stays tall, held to the screen's limit. Use one at least as wide as the column, twice that for sharp screens, and give its width and height so the block reserves its shape before it loads. `alt` empty when the heading already says what it shows; otherwise describe it. */
17
+ image: BlockImage;
18
+ }
19
+ export type FeatureStackProps = FeatureStackOwnProps;
20
+ /** The class each part carries. Apply these — do not retype them. */
21
+ export declare const featureStackParts: {
22
+ readonly root: "dds-featurestack";
23
+ readonly inner: "dds-featurestack__inner";
24
+ readonly header: "dds-featurestack__header";
25
+ readonly title: "dds-featurestack__title";
26
+ readonly description: "dds-featurestack__description";
27
+ readonly link: "dds-featurestack__link";
28
+ readonly media: "dds-featurestack__media";
29
+ readonly image: "dds-featurestack__image";
30
+ };
31
+ /** Generated so defaults cannot drift from the contract. */
32
+ export declare const featureStackDefaults: {
33
+ readonly ground: "page";
34
+ };