@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,74 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId, useState } from 'react';
4
+ import './TeamGrid.css';
5
+ import './team-grid-impl.css';
6
+ import '../block-reveal.css';
7
+ import { given } from '../../given.js';
8
+ import { Button } from '../../core/button/Button.js';
9
+ import { Link } from '../../core/link/Link.js';
10
+ import { AspectRatio } from '../../layout/aspect-ratio/AspectRatio.js';
11
+ import { arriving, useReveal } from '../use-reveal.js';
12
+ import { teamGridDefaults, teamGridParts } from './TeamGrid.types.js';
13
+ /**
14
+ * A profile link's accessible name when the page gives none: the visible word, then whose it is.
15
+ *
16
+ * The word first, because it is what a reader using speech sees and says — "click LinkedIn" has to
17
+ * match — and the name after it, because "LinkedIn" alone is the same on every card and a list of
18
+ * links heard out of context would be one word repeated.
19
+ */
20
+ const profileNameOf = (person) => `${person.profile?.label ?? ''}, ${person.name}`;
21
+ /**
22
+ * Where a person comes in the arrival, capped so a long set does not keep a reader waiting.
23
+ *
24
+ * The heading and the line under it take the first two steps; the people showing follow a step
25
+ * apart up to the fourth, and the button arrives a step after the last of them. FeatureCards' cap,
26
+ * for its reason: block-reveal.css keeps a block's trail inside its own movement.
27
+ */
28
+ const FIRST_PERSON = 2;
29
+ const LAST_STEP = FIRST_PERSON + 3;
30
+ /**
31
+ * A heading over a grid of people — the reference design's Leaders template, AvatarGrid in its
32
+ * source.
33
+ *
34
+ * Each person is a square photograph over their name, their role and, where they have one, a link
35
+ * to a profile of theirs elsewhere. The people are records rather than cards the page composes: a
36
+ * person here is words and a picture, and the grid is what decides how each one is drawn.
37
+ *
38
+ * The first `collapsedCount` show at once and the rest wait behind a button under the grid. One
39
+ * list rather than the reference's two grids, so a screen reader counts the whole set and the
40
+ * order is the same whether it is open or not; the people past the count are in the markup from
41
+ * the first render and `hidden` until asked for, which takes them out of the accessibility tree
42
+ * and the tab order as well as off the page. The reference marked its hidden half `aria-hidden`
43
+ * only, and left their links in the tab order behind a collapsed row.
44
+ *
45
+ * Pressing the button leaves focus on it. The people it shows sit straight before it in reading
46
+ * order, so they are the next thing a reader moving back meets, and the people it hides take any
47
+ * focus they could have held away with them — which they cannot be holding, the button having it.
48
+ *
49
+ * `'use client'` for the button's state, for the arrival, and for `useId`, which names the region
50
+ * by its heading and gives the button the list's id to control.
51
+ */
52
+ export function TeamGrid({ ground = teamGridDefaults.ground, title, description, people, collapsedCount = teamGridDefaults.collapsedCount, expandLabel = teamGridDefaults.expandLabel, collapseLabel = teamGridDefaults.collapseLabel, profileName = profileNameOf, externalLabel = teamGridDefaults.externalLabel, }) {
53
+ const titleId = useId();
54
+ const listId = useId();
55
+ const [band, revealed] = useReveal();
56
+ const [expanded, setExpanded] = useState(false);
57
+ // A count below one would hide everyone behind the button; a fraction is not a number of people.
58
+ const shown = Math.max(1, Math.floor(collapsedCount));
59
+ const longer = people.length > shown;
60
+ const inverse = ground === 'strong';
61
+ return (_jsx("section", { className: teamGridParts.root, "data-ground": ground, "aria-labelledby": titleId, children: _jsxs("div", { className: teamGridParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: teamGridParts.header, children: [_jsx("h2", { className: teamGridParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: teamGridParts.description, ...arriving(1), children: description })) : null] }), _jsxs("div", { className: teamGridParts.people, children: [_jsx("ul", { className: teamGridParts.list, id: listId, children: people.map((person, at) => {
62
+ const extra = at >= shown;
63
+ /* A person showing from the start arrives with the block. One past the count arrives
64
+ when the button shows them, by the stylesheet's own animation, a step behind the
65
+ one before — so it carries its place among the extras rather than the block's. */
66
+ const motion = extra
67
+ ? {
68
+ 'data-extra': '',
69
+ style: { '--dds-teamgrid-extra-at': at - shown },
70
+ }
71
+ : arriving(Math.min(FIRST_PERSON + at, LAST_STEP));
72
+ return (_jsxs("li", { className: teamGridParts.item, hidden: extra && !expanded, ...motion, children: [_jsx("div", { className: teamGridParts.media, children: _jsx(AspectRatio, { ratio: "square", children: _jsx("img", { src: person.image.src, alt: person.image.alt, loading: "lazy", decoding: "async" }) }) }), _jsxs("div", { className: teamGridParts.text, children: [_jsx("h3", { className: teamGridParts.name, children: person.name }), _jsx("p", { className: teamGridParts.role, children: person.role }), person.profile !== undefined ? (_jsx("div", { className: teamGridParts.profile, children: _jsx(Link, { href: person.profile.href, external: true, tone: inverse ? 'inherit' : 'brand', "aria-label": `${profileName(person)} ${externalLabel}`, children: person.profile.label }) })) : null] })] }, `${person.name}-${at}`));
73
+ }) }), longer ? (_jsx("div", { className: teamGridParts.actions, ...arriving(Math.min(FIRST_PERSON + shown, LAST_STEP + 1)), children: _jsx(Button, { variant: "secondary", size: "md", tone: inverse ? 'inverse' : 'default', "aria-expanded": expanded, "aria-controls": listId, onClick: () => setExpanded((open) => !open), children: expanded ? collapseLabel : expandLabel }) })) : null] })] }) }));
74
+ }
@@ -0,0 +1,51 @@
1
+ import type { TeamGridMember } from '../../team-grid-data.js';
2
+ export interface TeamGridOwnProps {
3
+ /**
4
+ * `page` — The default, and the page's own white. Most people sections have nothing to separate themselves from
5
+ * `subtle` — A quiet step off the page — the reference design's slate band — for a section that wants to read as its own. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section; the photographs' placeholder is raised to color.bg.surface-card-raised so it does not vanish into the band
6
+ * `strong` — `color.bg.brand-strong`, the dark band, for a section a reader should arrive at. The heading, the names and the roles turn white, the profile links take the band's ink rather than the brand's, and the button takes Button's inverse tone
7
+ */
8
+ ground?: 'page' | 'subtle' | 'strong';
9
+ /** Always. Who these people are, in the fewest words — "Meet our leadership team". It names the section as a region, so a screen reader can jump to it and past it. */
10
+ title: string;
11
+ /** A line saying who the set is, when the title alone does not — "The people steering Deriv's products, markets and teams". */
12
+ description?: string;
13
+ /** Always. The people, in the order the page wants them read — by seniority or by relevance, the first `collapsedCount` of them showing at once. Each has a name, a role, a picture with its `alt`, and where there is one a `profile` link. The same framing in every photograph: the grid crops each to a square, and a set of faces at different distances reads as a set of different sources. An empty list draws the heading alone. */
14
+ people: TeamGridMember[];
15
+ /** How many people show before the rest are asked for. Four, the default, is one row at full width, so the button sits under a complete row. A list no longer than this has no button at all — there is nothing more to show. */
16
+ collapsedCount?: number;
17
+ /** The button's words while the rest of the list is hidden. Translate it with the page, and keep it parallel to collapseLabel. */
18
+ expandLabel?: string;
19
+ /** The button's words once the whole list is showing, for the reason expandLabel exists. */
20
+ collapseLabel?: string;
21
+ /** The accessible name of a person's profile link, given the person. The visible word is the profile's own — "LinkedIn" — and it is the same on every card, so the link is named for its person too. Without this the name is the profile's label and then the person's, "LinkedIn, Alex Morgan": the visible word first, so a reader using speech can say what they see. Pass it to translate that with the page. */
22
+ profileName?: (person: TeamGridMember) => string;
23
+ /** Said after every profile link's name, because a profile opens in a new tab and a reader should know before they press it — ResourceBanner's words for the same thing. Translate it with the page. */
24
+ externalLabel?: string;
25
+ }
26
+ export type TeamGridProps = TeamGridOwnProps;
27
+ /** The class each part carries. Apply these — do not retype them. */
28
+ export declare const teamGridParts: {
29
+ readonly root: "dds-teamgrid";
30
+ readonly inner: "dds-teamgrid__inner";
31
+ readonly header: "dds-teamgrid__header";
32
+ readonly title: "dds-teamgrid__title";
33
+ readonly description: "dds-teamgrid__description";
34
+ readonly people: "dds-teamgrid__people";
35
+ readonly list: "dds-teamgrid__list";
36
+ readonly item: "dds-teamgrid__item";
37
+ readonly media: "dds-teamgrid__media";
38
+ readonly text: "dds-teamgrid__text";
39
+ readonly name: "dds-teamgrid__name";
40
+ readonly role: "dds-teamgrid__role";
41
+ readonly profile: "dds-teamgrid__profile";
42
+ readonly actions: "dds-teamgrid__actions";
43
+ };
44
+ /** Generated so defaults cannot drift from the contract. */
45
+ export declare const teamGridDefaults: {
46
+ readonly ground: "page";
47
+ readonly collapsedCount: 4;
48
+ readonly expandLabel: "View all";
49
+ readonly collapseLabel: "View less";
50
+ readonly externalLabel: "(opens in a new tab)";
51
+ };
@@ -0,0 +1,20 @@
1
+ // GENERATED from team-grid.contract.json. Do not edit.
2
+ /** The class each part carries. Apply these — do not retype them. */
3
+ export const teamGridParts = {
4
+ root: 'dds-teamgrid',
5
+ inner: 'dds-teamgrid__inner',
6
+ header: 'dds-teamgrid__header',
7
+ title: 'dds-teamgrid__title',
8
+ description: 'dds-teamgrid__description',
9
+ people: 'dds-teamgrid__people',
10
+ list: 'dds-teamgrid__list',
11
+ item: 'dds-teamgrid__item',
12
+ media: 'dds-teamgrid__media',
13
+ text: 'dds-teamgrid__text',
14
+ name: 'dds-teamgrid__name',
15
+ role: 'dds-teamgrid__role',
16
+ profile: 'dds-teamgrid__profile',
17
+ actions: 'dds-teamgrid__actions',
18
+ };
19
+ /** Generated so defaults cannot drift from the contract. */
20
+ export const teamGridDefaults = { ground: 'page', collapsedCount: 4, expandLabel: 'View all', collapseLabel: 'View less', externalLabel: '(opens in a new tab)' };
@@ -0,0 +1,129 @@
1
+ /*
2
+ * Scoped to this package, like the generated sheet beside it.
3
+ */
4
+
5
+ /*
6
+ * The header starts on a phone and is centred once the band is wide enough to hold a line.
7
+ *
8
+ * FeatureCards' rule and its reason: centred text starts every line at a different edge, which
9
+ * costs little across a wide measure and a great deal across a narrow one. The header and the line
10
+ * under it are capped narrower than the column, so `margin-inline: auto` is what centres those
11
+ * boxes rather than their words inside a box left at the start edge.
12
+ *
13
+ * `48rem` longhand because `var()` is not permitted in a container query condition, and it is the
14
+ * width the rest of this package folds at.
15
+ */
16
+ @container (min-width: 48rem) {
17
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__header {
18
+ align-items: center;
19
+ margin-inline: auto;
20
+ text-align: center;
21
+ }
22
+
23
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__description {
24
+ margin-inline: auto;
25
+ }
26
+ }
27
+
28
+ /*
29
+ * One column on a phone, two from the fold, four at full width — the reference design's grid, so
30
+ * the four people showing at first are one row on a desktop. `minmax(0, 1fr)` rather than `1fr`, so
31
+ * a long unbroken name shrinks to its column instead of pushing the grid past the page.
32
+ *
33
+ * Four from 64rem rather than FeatureCards' 62rem: four photographs and four names a row want the
34
+ * extra width, and below it two a row keeps each face large enough to be one.
35
+ */
36
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__list {
37
+ grid-template-columns: minmax(0, 1fr);
38
+ }
39
+
40
+ @container (min-width: 48rem) {
41
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__list {
42
+ grid-template-columns: repeat(2, minmax(0, 1fr));
43
+ }
44
+ }
45
+
46
+ @container (min-width: 64rem) {
47
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__list {
48
+ grid-template-columns: repeat(4, minmax(0, 1fr));
49
+ }
50
+ }
51
+
52
+ /*
53
+ * A person waiting behind the button is not there at all.
54
+ *
55
+ * The item is a flex column by its contract, and an author's `display` beats the browser's own
56
+ * `[hidden] { display: none }` — so without this the attribute would take a person out of the
57
+ * accessibility tree while leaving them drawn on the page.
58
+ */
59
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__item[hidden] {
60
+ display: none;
61
+ }
62
+
63
+ /*
64
+ * And arrives when the button shows them: faded up from opacity.hidden and risen
65
+ * motion.translate.reveal, the arrival every block makes, a stagger step behind the person before.
66
+ *
67
+ * A keyframe rather than a transition because the person was `display: none` a moment earlier and
68
+ * a transition has no painted starting state to move from — the same reason a block's entrance is
69
+ * keyframed. `both`, so a person waiting out their delay waits at the start rather than showing
70
+ * and then jumping back to it. The reference also grew the grid's height open; that needs the
71
+ * extras in a second list, and one list a screen reader counts is worth more than the slide.
72
+ */
73
+ @keyframes dds-teamgrid-arrive {
74
+ from {
75
+ opacity: var(--opacity-hidden);
76
+ translate: var(--motion-translate-reveal);
77
+ }
78
+ }
79
+
80
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__item[data-extra]:not([hidden]) {
81
+ animation: dds-teamgrid-arrive var(--motion-duration-reveal) var(--motion-easing-reveal) both;
82
+ animation-delay: calc(var(--dds-teamgrid-extra-at, 0) * var(--motion-duration-stagger));
83
+ }
84
+
85
+ /*
86
+ * A reader who asked for less movement gets the people without the arrival: they are simply there
87
+ * the moment the button is pressed.
88
+ */
89
+ @media (prefers-reduced-motion: reduce) {
90
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__item[data-extra]:not([hidden]) {
91
+ animation: none;
92
+ }
93
+ }
94
+
95
+ /*
96
+ * The photograph's frame takes the whole of the card's width; AspectRatio inside it holds the
97
+ * square and the frame's clip rounds the corners of whatever the picture is.
98
+ */
99
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__media {
100
+ inline-size: 100%;
101
+ }
102
+
103
+ /*
104
+ * A profile link is a pointer target, so it is at least size.target.min tall. A standalone Link is
105
+ * as tall as its line, which is under that at the label step — the profile part holds the height
106
+ * and the link fills it, so the whole of the row is the target rather than the words alone.
107
+ */
108
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__profile > .dds-link {
109
+ min-block-size: var(--size-target-min);
110
+ }
111
+
112
+ /*
113
+ * The button fills the column on a phone, the rule for a section's actions — the part is a column,
114
+ * which stretches it. From the fold it is its own width, centred under the grid it lengthens.
115
+ */
116
+ @container (min-width: 48rem) {
117
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__actions {
118
+ align-items: center;
119
+ }
120
+ }
121
+
122
+ /*
123
+ * A heading with no line under it closes up: its bound step is the distance to the line, and with
124
+ * nothing there it would only add to the step from the header to the people. `:last-child` is the
125
+ * condition itself, DataTable's way, rather than a prop standing in for it.
126
+ */
127
+ :where([data-dds-context="marketing"]) .dds-teamgrid .dds-teamgrid__title:last-child {
128
+ margin-block-end: 0;
129
+ }
@@ -7,6 +7,7 @@
7
7
  inline-size: 100%;
8
8
  justify-content: center;
9
9
  overflow: clip;
10
+ overflow-wrap: anywhere;
10
11
  }
11
12
 
12
13
  :where([data-dds-context="marketing"]) .dds-testimonialrow .dds-testimonialrow__inner {
@@ -17,4 +17,4 @@ export type { TestimonialRowProps };
17
17
  * `'use client'` for the row's scroll position, the arrival, and `useId`, which names the region
18
18
  * by its heading.
19
19
  */
20
- export declare function TestimonialRow({ ground, title, reviews, summary, ratingLabel, label, previousLabel, nextLabel, }: TestimonialRowProps): import("react").JSX.Element | null;
20
+ export declare function TestimonialRow({ ground, title, reviews, summary, ratingLabel, label, externalLabel, previousLabel, nextLabel, }: TestimonialRowProps): import("react").JSX.Element | null;
@@ -8,6 +8,7 @@ import "../block-reveal.css";
8
8
  import { given } from "../../given.js";
9
9
  import { ButtonIcon } from "../../core/button-icon/ButtonIcon.js";
10
10
  import { Link } from "../../core/link/Link.js";
11
+ import { visuallyHidden } from "../../visually-hidden.js";
11
12
  import { arriving, useReveal } from "../use-reveal.js";
12
13
  import { testimonialRowDefaults, testimonialRowParts, } from "./TestimonialRow.types.js";
13
14
  /**
@@ -45,7 +46,7 @@ const AT_END = 2;
45
46
  * `'use client'` for the row's scroll position, the arrival, and `useId`, which names the region
46
47
  * by its heading.
47
48
  */
48
- export function TestimonialRow({ ground = testimonialRowDefaults.ground, title, reviews, summary, ratingLabel, label = testimonialRowDefaults.label, previousLabel = testimonialRowDefaults.previousLabel, nextLabel = testimonialRowDefaults.nextLabel, }) {
49
+ export function TestimonialRow({ ground = testimonialRowDefaults.ground, title, reviews, summary, ratingLabel, label = testimonialRowDefaults.label, externalLabel = testimonialRowDefaults.externalLabel, previousLabel = testimonialRowDefaults.previousLabel, nextLabel = testimonialRowDefaults.nextLabel, }) {
49
50
  const titleId = useId();
50
51
  const [band, revealed] = useReveal();
51
52
  const row = useRef(null);
@@ -116,13 +117,13 @@ export function TestimonialRow({ ground = testimonialRowDefaults.ground, title,
116
117
  return (_jsx("section", { className: testimonialRowParts.root, "data-ground": ground, "aria-labelledby": titleId, children: _jsxs("div", { className: testimonialRowParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: testimonialRowParts.header, children: [_jsx("h2", { className: testimonialRowParts.title, id: titleId, ...arriving(0), children: title }), summary === undefined ? null : (_jsx("p", { className: testimonialRowParts.summary, ...arriving(1), children: summary.href !== undefined && given(summary.href) ? (
117
118
  // A link on its own line, so the standalone one: brand ink and a mark. On the dark
118
119
  // band it takes the band's ink, where coral is the wrong colour for words.
119
- _jsx(Link, { variant: "standalone", tone: ground === "strong" ? "inherit" : "brand", external: true, href: summary.href, children: summary.label })) : (_jsx("span", { children: summary.label })) }))] }), _jsxs("div", { className: testimonialRowParts.carousel, children: [_jsx("div", { ref: row, className: testimonialRowParts.track, role: "group", "aria-label": label, tabIndex: 0, onScroll: read, ...arriving(2), children: reviews.map((each, at) => {
120
+ _jsxs(Link, { variant: "standalone", tone: ground === "strong" ? "inherit" : "brand", external: true, href: summary.href, children: [summary.label, _jsx("span", { style: visuallyHidden, children: ` ${externalLabel}` })] })) : (_jsx("span", { children: summary.label })) }))] }), _jsxs("div", { className: testimonialRowParts.carousel, children: [_jsx("div", { ref: row, className: testimonialRowParts.track, role: "group", "aria-label": label, tabIndex: 0, onScroll: read, ...arriving(2), children: reviews.map((each, at) => {
120
121
  // A review's position is its identity: the set is a list, and nothing reorders it.
121
122
  const card = (_jsxs("figure", { className: testimonialRowParts.item, children: [each.rating === undefined ? null : (
122
123
  // Named by the score it is drawn at, so a 4.3 is heard as the 4.5 a reader
123
124
  // sees rather than as a number the stars do not show.
124
125
  _jsx(Rating, { score: each.rating, label: ratingLabel?.(toHalves(each.rating)) ?? `Rated ${toHalves(each.rating)} out of 5` })), _jsx("blockquote", { className: testimonialRowParts.quote, children: each.quote }), _jsx("figcaption", { className: testimonialRowParts.author, children: _jsxs("span", { children: [_jsx("span", { className: testimonialRowParts.name, children: each.name }), given(each.detail) ? (_jsx("span", { className: testimonialRowParts.detail, children: each.detail })) : null] }) })] }, at));
125
126
  // The review is on a service the page does not own, so it opens beside the page.
126
- return given(each.href) ? (_jsx("a", { className: testimonialRowParts["review-link"], href: each.href, target: "_blank", rel: "noopener noreferrer", children: card }, at)) : (card);
127
+ return given(each.href) ? (_jsxs("a", { className: testimonialRowParts["review-link"], href: each.href, target: "_blank", rel: "noopener noreferrer", children: [card, _jsx("span", { style: visuallyHidden, children: ` ${externalLabel}` })] }, at)) : (card);
127
128
  }) }), reviews.length > 1 ? (_jsx("div", { className: testimonialRowParts.footer, ...arriving(3), children: _jsxs("div", { className: testimonialRowParts.nav, hidden: !scrolls, children: [_jsx("span", { ref: back, className: testimonialRowParts["nav-slot"], "data-available": ends.back, inert: !ends.back, children: _jsx(ButtonIcon, { icon: _jsx(ChevronLeftRegularIcon, {}), label: previousLabel, ...arrow, onClick: () => step(-1) }) }), _jsx("span", { ref: on, className: testimonialRowParts["nav-slot"], "data-available": ends.on, inert: !ends.on, children: _jsx(ButtonIcon, { icon: _jsx(ChevronRightRegularIcon, {}), label: nextLabel, ...arrow, onClick: () => step(1) }) })] }) })) : null] })] }) }));
128
129
  }
@@ -20,6 +20,8 @@ export interface TestimonialRowOwnProps {
20
20
  previousLabel?: string;
21
21
  /** The accessible name of the arrow stepping the row on, for the reason previousLabel exists. */
22
22
  nextLabel?: string;
23
+ /** Said after the summary link and every linked review, because both open on a service the page does not own, in a new tab, and a reader should know before they press — TeamGrid's and ResourceBanner's words for the same thing. Translate it with the page. */
24
+ externalLabel?: string;
23
25
  }
24
26
  export type TestimonialRowProps = TestimonialRowOwnProps;
25
27
  /** The class each part carries. Apply these — do not retype them. */
@@ -51,4 +53,5 @@ export declare const testimonialRowDefaults: {
51
53
  readonly label: "Reviews";
52
54
  readonly previousLabel: "Previous reviews";
53
55
  readonly nextLabel: "Next reviews";
56
+ readonly externalLabel: "(opens in a new tab)";
54
57
  };
@@ -23,4 +23,4 @@ export const testimonialRowParts = {
23
23
  'nav-slot': 'dds-testimonialrow__nav-slot',
24
24
  };
25
25
  /** Generated so defaults cannot drift from the contract. */
26
- export const testimonialRowDefaults = { ground: 'page', label: 'Reviews', previousLabel: 'Previous reviews', nextLabel: 'Next reviews' };
26
+ export const testimonialRowDefaults = { ground: 'page', label: 'Reviews', previousLabel: 'Previous reviews', nextLabel: 'Next reviews', externalLabel: '(opens in a new tab)' };
@@ -193,3 +193,11 @@
193
193
  :where([data-dds-context="marketing"]) .dds-testimonialrow .dds-testimonialrow__nav-slot[data-available='false'] {
194
194
  opacity: var(--opacity-disabled);
195
195
  }
196
+
197
+ /*
198
+ * Previous and next point along the row. A right-to-left row puts previous on the right, so the
199
+ * chevrons are mirrored to point the way each one moves; as they ship, both pointed backwards.
200
+ */
201
+ :where([data-dds-context="marketing"]) .dds-testimonialrow .dds-testimonialrow__nav-slot svg:dir(rtl) {
202
+ scale: -1 1;
203
+ }
@@ -7,6 +7,7 @@
7
7
  inline-size: 100%;
8
8
  justify-content: center;
9
9
  overflow: clip;
10
+ overflow-wrap: anywhere;
10
11
  }
11
12
 
12
13
  :where([data-dds-context="marketing"]) .dds-testimonialslider .dds-testimonialslider__inner {
@@ -19,4 +19,4 @@ export type { TestimonialSliderProps };
19
19
  * comes into view. `'use client'` for that, for the rotation, and for `useId`, which names the
20
20
  * region by its heading.
21
21
  */
22
- export declare function TestimonialSlider({ ground, title, quotes, imageStart, imageEnd, autoplay, controlsLabel, slideLabel, }: TestimonialSliderProps): import("react").JSX.Element | null;
22
+ export declare function TestimonialSlider({ ground, title, quotes, imageStart, imageEnd, autoplay, controlsLabel, slideLabel, roleDescription, slideRoleDescription, positionLabel, }: TestimonialSliderProps): import("react").JSX.Element | null;
@@ -25,10 +25,10 @@ import { testimonialSliderDefaults, testimonialSliderParts, } from "./Testimonia
25
25
  * comes into view. `'use client'` for that, for the rotation, and for `useId`, which names the
26
26
  * region by its heading.
27
27
  */
28
- export function TestimonialSlider({ ground = testimonialSliderDefaults.ground, title, quotes, imageStart, imageEnd, autoplay = testimonialSliderDefaults.autoplay, controlsLabel = testimonialSliderDefaults.controlsLabel, slideLabel = testimonialSliderDefaults.slideLabel, }) {
28
+ export function TestimonialSlider({ ground = testimonialSliderDefaults.ground, title, quotes, imageStart, imageEnd, autoplay = testimonialSliderDefaults.autoplay, controlsLabel = testimonialSliderDefaults.controlsLabel, slideLabel = testimonialSliderDefaults.slideLabel, roleDescription = testimonialSliderDefaults.roleDescription, slideRoleDescription = testimonialSliderDefaults.slideRoleDescription, positionLabel, }) {
29
29
  const titleId = useId();
30
30
  const [band, revealed] = useReveal();
31
- const { rotating, root, region, track, item, away, dots } = useRotation(quotes.length, autoplay);
31
+ const { rotating, root, region, track, item, away, dots } = useRotation(quotes.length, autoplay, { roleDescription, slideRoleDescription, positionLabel });
32
32
  // Nothing to show is nothing drawn: a named region with no quote in it leads nowhere.
33
33
  if (quotes.length === 0)
34
34
  return null;
@@ -20,6 +20,12 @@ export interface TestimonialSliderOwnProps {
20
20
  controlsLabel?: string;
21
21
  /** The word each dot is announced by, before its number — 'Slide 2'. Translate it with the page, as controlsLabel is; a page that translates only the group's name still has every dot announced in English. */
22
22
  slideLabel?: string;
23
+ /** What a screen reader calls the set while it rotates — announced in place of a region, as the word for this kind of thing. Translate it with the page: it is read out as given, so an untranslated one announces an English word on every page in every language. */
24
+ roleDescription?: string;
25
+ /** What a screen reader calls each item while the set rotates, for the reason roleDescription exists. Translate it with the page. */
26
+ slideRoleDescription?: string;
27
+ /** The accessible name of each item while the set rotates, given its place in the set counting from one — so a reader hears where they are. Without it each is named "2 of 5"; pass it to translate that with the page, in that language's own order and digits. */
28
+ positionLabel?: (position: number, count: number) => string;
23
29
  }
24
30
  export type TestimonialSliderProps = TestimonialSliderOwnProps;
25
31
  /** The class each part carries. Apply these — do not retype them. */
@@ -46,4 +52,6 @@ export declare const testimonialSliderDefaults: {
46
52
  readonly autoplay: false;
47
53
  readonly controlsLabel: "Testimonials";
48
54
  readonly slideLabel: "Slide";
55
+ readonly roleDescription: "carousel";
56
+ readonly slideRoleDescription: "slide";
49
57
  };
@@ -18,4 +18,4 @@ export const testimonialSliderParts = {
18
18
  controls: 'dds-testimonialslider__controls',
19
19
  };
20
20
  /** Generated so defaults cannot drift from the contract. */
21
- export const testimonialSliderDefaults = { ground: 'page', autoplay: false, controlsLabel: 'Testimonials', slideLabel: 'Slide' };
21
+ export const testimonialSliderDefaults = { ground: 'page', autoplay: false, controlsLabel: 'Testimonials', slideLabel: 'Slide', roleDescription: 'carousel', slideRoleDescription: 'slide' };
@@ -0,0 +1,53 @@
1
+ /* GENERATED from textscroll.contract.json. Do not edit. */
2
+
3
+ :where([data-dds-context="marketing"]) .dds-textscroll {
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-textscroll .dds-textscroll__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
+ }
22
+
23
+ :where([data-dds-context="marketing"]) .dds-textscroll .dds-textscroll__text {
24
+ margin-block: 0;
25
+ text-wrap: pretty;
26
+ font-family: var(--font-h4-family);
27
+ font-size: var(--font-h4-size);
28
+ font-weight: var(--font-h4-weight);
29
+ letter-spacing: var(--font-h4-letter-spacing);
30
+ line-height: var(--font-h4-line-height);
31
+ max-inline-size: var(--size-measure-hero-heading);
32
+ }
33
+
34
+ :where([data-dds-context="marketing"]) .dds-textscroll .dds-textscroll__word {
35
+ display: inline;
36
+ }
37
+
38
+ :where([data-dds-context="marketing"]) .dds-textscroll[data-ground="page"] {
39
+ background-color: var(--color-bg-page);
40
+ color: var(--color-fg-default);
41
+ }
42
+
43
+ :where([data-dds-context="marketing"]) .dds-textscroll[data-ground="strong"] {
44
+ background-color: var(--color-bg-brand-strong);
45
+ color: var(--color-fg-on-brand-strong);
46
+ }
47
+
48
+ :where([data-dds-context="marketing"]) .dds-textscroll[data-ground="subtle"] {
49
+ background-color: var(--color-bg-page);
50
+ background-image: linear-gradient(var(--color-bg-band-subtle), var(--color-bg-band-subtle));
51
+ color: var(--color-fg-default);
52
+ }
53
+
@@ -0,0 +1,5 @@
1
+ import './TextScroll.css';
2
+ import './text-scroll-impl.css';
3
+ import { type TextScrollProps } from './TextScroll.types.js';
4
+ export type { TextScrollProps };
5
+ export declare function TextScroll({ ground, text }: TextScrollProps): import("react").JSX.Element | null;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Fragment } from 'react';
3
+ import './TextScroll.css';
4
+ import './text-scroll-impl.css';
5
+ import { textScrollDefaults, textScrollParts } from './TextScroll.types.js';
6
+ const SPACE = /^\s+$/u;
7
+ /**
8
+ * The sentence in words and the spaces between them, every character kept.
9
+ *
10
+ * By the language's own word boundaries rather than by spaces. Chinese, Japanese and Thai write
11
+ * none between words, so a split on whitespace handed the whole paragraph back as one word and it
12
+ * brightened all at once. `Intl.Segmenter` knows where their words end, in the browser and on the
13
+ * server alike.
14
+ *
15
+ * The spaces are kept as written rather than rebuilt as one ordinary space: French sets a narrow
16
+ * no-break space before `?` and `%`, and turning it into a breaking one let the line wrap before
17
+ * the punctuation it exists to hold on to. Punctuation rides with the word beside it, so a full
18
+ * stop brightens with its word instead of standing lit on its own.
19
+ */
20
+ function piecesOf(text) {
21
+ const runs = typeof Intl !== 'undefined' && 'Segmenter' in Intl
22
+ ? Array.from(new Intl.Segmenter(undefined, { granularity: 'word' }).segment(text), (each) => ({
23
+ text: each.segment,
24
+ wordLike: each.isWordLike === true,
25
+ }))
26
+ : text.split(/(\s+)/u).map((run) => ({ text: run, wordLike: !SPACE.test(run) }));
27
+ const pieces = [];
28
+ // The word being built, and whether it holds a word yet or only punctuation leading into one.
29
+ let open = null;
30
+ const close = () => {
31
+ if (open !== null)
32
+ pieces.push({ word: true, text: open.text });
33
+ open = null;
34
+ };
35
+ for (const run of runs) {
36
+ if (run.text === '')
37
+ continue;
38
+ if (SPACE.test(run.text)) {
39
+ close();
40
+ pieces.push({ word: false, text: run.text });
41
+ }
42
+ else if (run.wordLike && open?.holdsWord) {
43
+ // Two words with nothing between them — the ordinary case in a script that writes no spaces.
44
+ close();
45
+ open = { text: run.text, holdsWord: true };
46
+ }
47
+ else if (open === null) {
48
+ open = { text: run.text, holdsWord: run.wordLike };
49
+ }
50
+ else {
51
+ open = { text: open.text + run.text, holdsWord: open.holdsWord || run.wordLike };
52
+ }
53
+ }
54
+ close();
55
+ return pieces;
56
+ }
57
+ export function TextScroll({ ground = textScrollDefaults.ground, text }) {
58
+ const pieces = piecesOf(text.trim());
59
+ const count = pieces.filter((piece) => piece.word).length;
60
+ // Nothing to say is nothing drawn: an empty band between two sections is a gap, not a pause.
61
+ if (count === 0)
62
+ return null;
63
+ let at = -1;
64
+ return (_jsx("section", { className: textScrollParts.root, "data-ground": ground, children: _jsx("div", { className: textScrollParts.inner, children: _jsx("p", { className: textScrollParts.text, style: { '--dds-textscroll-words': count }, children: pieces.map((piece, index) => {
65
+ // A piece's place is its identity: the sentence is the page's, and nothing reorders it.
66
+ if (!piece.word)
67
+ return _jsx(Fragment, { children: piece.text }, index);
68
+ at += 1;
69
+ return (_jsx("span", { className: textScrollParts.word, style: { '--dds-textscroll-at': at }, children: piece.text }, index));
70
+ }) }) }) }));
71
+ }
@@ -0,0 +1,22 @@
1
+ export interface TextScrollOwnProps {
2
+ /**
3
+ * `page` — The default, and the page's own white. A statement between two sections that need nothing to separate them
4
+ * `subtle` — The grey band, for a statement that should read as a section of its own without changing the page's voice. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section
5
+ * `strong` — `color.bg.brand-strong`, the dark band, for the statement a page is built around — the mission on an About page. The words turn white and dim toward the band
6
+ */
7
+ ground?: 'page' | 'subtle' | 'strong';
8
+ /** Always. The statement, as plain text — one sentence or two short ones, under about three hundred characters. Split into words on its whitespace, so a run of spaces or a line break is one space in what is drawn. Written so it reads well a word at a time, because that is how the reveal delivers it: "We believe trading should be accessible to everyone, everywhere, with the tools and the trust to do it well." */
9
+ text: string;
10
+ }
11
+ export type TextScrollProps = TextScrollOwnProps;
12
+ /** The class each part carries. Apply these — do not retype them. */
13
+ export declare const textScrollParts: {
14
+ readonly root: "dds-textscroll";
15
+ readonly inner: "dds-textscroll__inner";
16
+ readonly text: "dds-textscroll__text";
17
+ readonly word: "dds-textscroll__word";
18
+ };
19
+ /** Generated so defaults cannot drift from the contract. */
20
+ export declare const textScrollDefaults: {
21
+ readonly ground: "page";
22
+ };
@@ -0,0 +1,10 @@
1
+ // GENERATED from text-scroll.contract.json. Do not edit.
2
+ /** The class each part carries. Apply these — do not retype them. */
3
+ export const textScrollParts = {
4
+ root: 'dds-textscroll',
5
+ inner: 'dds-textscroll__inner',
6
+ text: 'dds-textscroll__text',
7
+ word: 'dds-textscroll__word',
8
+ };
9
+ /** Generated so defaults cannot drift from the contract. */
10
+ export const textScrollDefaults = { ground: 'page' };