@deriv-com/dds-marketing 1.1.1 → 1.1.3

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 (256) hide show
  1. package/.llm/components/accordion.md +4 -0
  2. package/.llm/components/accordionitem.md +4 -0
  3. package/.llm/components/aspectratio.md +4 -0
  4. package/.llm/components/awards.md +4 -0
  5. package/.llm/components/background.md +4 -0
  6. package/.llm/components/badge.md +7 -3
  7. package/.llm/components/bottomsheet.md +7 -3
  8. package/.llm/components/breadcrumb.md +4 -0
  9. package/.llm/components/breadcrumbitem.md +4 -0
  10. package/.llm/components/button.md +4 -0
  11. package/.llm/components/buttonicon.md +10 -6
  12. package/.llm/components/card.md +5 -1
  13. package/.llm/components/cardbody.md +5 -1
  14. package/.llm/components/cardbutton.md +4 -0
  15. package/.llm/components/checkbox.md +1 -1
  16. package/.llm/components/comparisoncards.md +4 -0
  17. package/.llm/components/comparisontable.md +4 -0
  18. package/.llm/components/ctabanner.md +4 -0
  19. package/.llm/components/ctabannerslider.md +4 -0
  20. package/.llm/components/datatable.md +4 -0
  21. package/.llm/components/divider.md +4 -0
  22. package/.llm/components/downloadbanner.md +4 -0
  23. package/.llm/components/dropdownmenu.md +9 -5
  24. package/.llm/components/estimator.md +336 -0
  25. package/.llm/components/faqaccordion.md +4 -0
  26. package/.llm/components/featurebullets.md +4 -0
  27. package/.llm/components/featurecards.md +5 -1
  28. package/.llm/components/featurecardscarousel.md +4 -0
  29. package/.llm/components/featurescroll.md +5 -1
  30. package/.llm/components/featuresplit.md +4 -0
  31. package/.llm/components/featurestack.md +4 -0
  32. package/.llm/components/filterchip.md +5 -1
  33. package/.llm/components/footer.md +4 -0
  34. package/.llm/components/footergroup.md +4 -0
  35. package/.llm/components/grid.md +4 -0
  36. package/.llm/components/icon.md +9 -5
  37. package/.llm/components/illustrationcard.md +4 -0
  38. package/.llm/components/illustrationscene.md +4 -0
  39. package/.llm/components/imagebanner.md +6 -2
  40. package/.llm/components/landinghero.md +4 -0
  41. package/.llm/components/link.md +5 -1
  42. package/.llm/components/logocolumns.md +4 -0
  43. package/.llm/components/logorows.md +4 -0
  44. package/.llm/components/menuitem.md +6 -2
  45. package/.llm/components/navbar.md +5 -1
  46. package/.llm/components/navbaritem.md +4 -0
  47. package/.llm/components/navbarmenu.md +4 -0
  48. package/.llm/components/navbarmenugroup.md +4 -0
  49. package/.llm/components/navbarpanel.md +4 -0
  50. package/.llm/components/navbarswitcher.md +10 -6
  51. package/.llm/components/navbarswitcheroption.md +4 -0
  52. package/.llm/components/option.md +5 -1
  53. package/.llm/components/pageheader.md +4 -0
  54. package/.llm/components/pagination.md +4 -0
  55. package/.llm/components/paginationdots.md +4 -0
  56. package/.llm/components/producthero.md +4 -0
  57. package/.llm/components/removablechip.md +5 -1
  58. package/.llm/components/resourcebanner.md +4 -0
  59. package/.llm/components/row.md +4 -0
  60. package/.llm/components/scenescroll.md +4 -0
  61. package/.llm/components/scroller.md +4 -0
  62. package/.llm/components/searchinput.md +4 -0
  63. package/.llm/components/segmentedcontrol.md +5 -1
  64. package/.llm/components/segmentedcontrolitem.md +6 -2
  65. package/.llm/components/select.md +4 -0
  66. package/.llm/components/selectchip.md +4 -0
  67. package/.llm/components/spinner.md +4 -0
  68. package/.llm/components/stack.md +4 -0
  69. package/.llm/components/stats.md +4 -0
  70. package/.llm/components/statusdot.md +4 -0
  71. package/.llm/components/steps.md +18 -6
  72. package/.llm/components/stickycards.md +4 -0
  73. package/.llm/components/subdomainnavbar.md +5 -1
  74. package/.llm/components/tab.md +6 -2
  75. package/.llm/components/table.md +4 -0
  76. package/.llm/components/tablebody.md +4 -0
  77. package/.llm/components/tablecell.md +4 -0
  78. package/.llm/components/tablehead.md +4 -0
  79. package/.llm/components/tableheadercell.md +4 -0
  80. package/.llm/components/tablerow.md +4 -0
  81. package/.llm/components/tablist.md +4 -0
  82. package/.llm/components/teamgrid.md +4 -0
  83. package/.llm/components/testimonialrow.md +4 -0
  84. package/.llm/components/testimonialslider.md +4 -0
  85. package/.llm/components/textarea.md +4 -0
  86. package/.llm/components/textinput.md +5 -1
  87. package/.llm/components/textscroll.md +4 -0
  88. package/.llm/components/tiers.md +4 -0
  89. package/.llm/components/typography.md +4 -0
  90. package/.llm/digest/accordion.md +1 -1
  91. package/.llm/digest/accordionitem.md +1 -1
  92. package/.llm/digest/aspectratio.md +1 -1
  93. package/.llm/digest/awards.md +1 -1
  94. package/.llm/digest/background.md +1 -1
  95. package/.llm/digest/badge.md +1 -1
  96. package/.llm/digest/bottomsheet.md +1 -1
  97. package/.llm/digest/breadcrumb.md +1 -1
  98. package/.llm/digest/breadcrumbitem.md +1 -1
  99. package/.llm/digest/button.md +1 -1
  100. package/.llm/digest/buttonicon.md +1 -1
  101. package/.llm/digest/card.md +1 -1
  102. package/.llm/digest/cardbody.md +1 -1
  103. package/.llm/digest/cardbutton.md +1 -1
  104. package/.llm/digest/checkbox.md +1 -1
  105. package/.llm/digest/comparisoncards.md +1 -1
  106. package/.llm/digest/comparisontable.md +1 -1
  107. package/.llm/digest/ctabanner.md +1 -1
  108. package/.llm/digest/ctabannerslider.md +1 -1
  109. package/.llm/digest/datatable.md +1 -1
  110. package/.llm/digest/divider.md +1 -1
  111. package/.llm/digest/downloadbanner.md +1 -1
  112. package/.llm/digest/dropdownmenu.md +1 -1
  113. package/.llm/digest/estimator.md +66 -0
  114. package/.llm/digest/faqaccordion.md +1 -1
  115. package/.llm/digest/featurebullets.md +1 -1
  116. package/.llm/digest/featurecards.md +1 -1
  117. package/.llm/digest/featurecardscarousel.md +1 -1
  118. package/.llm/digest/featurescroll.md +1 -1
  119. package/.llm/digest/featuresplit.md +1 -1
  120. package/.llm/digest/featurestack.md +1 -1
  121. package/.llm/digest/filterchip.md +1 -1
  122. package/.llm/digest/footer.md +1 -1
  123. package/.llm/digest/footergroup.md +1 -1
  124. package/.llm/digest/grid.md +1 -1
  125. package/.llm/digest/icon.md +1 -1
  126. package/.llm/digest/illustrationcard.md +1 -1
  127. package/.llm/digest/illustrationscene.md +1 -1
  128. package/.llm/digest/imagebanner.md +2 -2
  129. package/.llm/digest/landinghero.md +1 -1
  130. package/.llm/digest/link.md +1 -1
  131. package/.llm/digest/logocolumns.md +1 -1
  132. package/.llm/digest/logorows.md +1 -1
  133. package/.llm/digest/menuitem.md +1 -1
  134. package/.llm/digest/navbar.md +1 -1
  135. package/.llm/digest/navbaritem.md +1 -1
  136. package/.llm/digest/navbarmenu.md +1 -1
  137. package/.llm/digest/navbarmenugroup.md +1 -1
  138. package/.llm/digest/navbarpanel.md +1 -1
  139. package/.llm/digest/navbarswitcher.md +1 -1
  140. package/.llm/digest/navbarswitcheroption.md +1 -1
  141. package/.llm/digest/option.md +1 -1
  142. package/.llm/digest/pageheader.md +1 -1
  143. package/.llm/digest/pagination.md +1 -1
  144. package/.llm/digest/paginationdots.md +1 -1
  145. package/.llm/digest/producthero.md +1 -1
  146. package/.llm/digest/removablechip.md +1 -1
  147. package/.llm/digest/resourcebanner.md +1 -1
  148. package/.llm/digest/row.md +1 -1
  149. package/.llm/digest/scenescroll.md +1 -1
  150. package/.llm/digest/scroller.md +1 -1
  151. package/.llm/digest/searchinput.md +1 -1
  152. package/.llm/digest/segmentedcontrol.md +1 -1
  153. package/.llm/digest/segmentedcontrolitem.md +2 -2
  154. package/.llm/digest/select.md +1 -1
  155. package/.llm/digest/selectchip.md +1 -1
  156. package/.llm/digest/spinner.md +1 -1
  157. package/.llm/digest/stack.md +1 -1
  158. package/.llm/digest/stats.md +1 -1
  159. package/.llm/digest/statusdot.md +1 -1
  160. package/.llm/digest/steps.md +3 -3
  161. package/.llm/digest/stickycards.md +1 -1
  162. package/.llm/digest/subdomainnavbar.md +1 -1
  163. package/.llm/digest/tab.md +1 -1
  164. package/.llm/digest/table.md +1 -1
  165. package/.llm/digest/tablebody.md +1 -1
  166. package/.llm/digest/tablecell.md +1 -1
  167. package/.llm/digest/tablehead.md +1 -1
  168. package/.llm/digest/tableheadercell.md +1 -1
  169. package/.llm/digest/tablerow.md +1 -1
  170. package/.llm/digest/tablist.md +1 -1
  171. package/.llm/digest/teamgrid.md +1 -1
  172. package/.llm/digest/testimonialrow.md +1 -1
  173. package/.llm/digest/testimonialslider.md +1 -1
  174. package/.llm/digest/textarea.md +1 -1
  175. package/.llm/digest/textinput.md +1 -1
  176. package/.llm/digest/textscroll.md +1 -1
  177. package/.llm/digest/tiers.md +1 -1
  178. package/.llm/digest/typography.md +1 -1
  179. package/.llm/index.md +2 -1
  180. package/.llm/tokens.md +5 -0
  181. package/AGENTS.md +1 -0
  182. package/README.md +28 -3
  183. package/dist/components/blocks/block-parallax.css +62 -0
  184. package/dist/components/blocks/comparison-cards/comparison-cards-impl.css +0 -12
  185. package/dist/components/blocks/download-banner/DownloadBanner.d.ts +1 -0
  186. package/dist/components/blocks/download-banner/DownloadBanner.js +2 -1
  187. package/dist/components/blocks/estimator/Estimator.css +206 -0
  188. package/dist/components/blocks/estimator/Estimator.d.ts +27 -0
  189. package/dist/components/blocks/estimator/Estimator.js +61 -0
  190. package/dist/components/blocks/estimator/Estimator.types.d.ts +63 -0
  191. package/dist/components/blocks/estimator/Estimator.types.js +25 -0
  192. package/dist/components/blocks/estimator/estimator-impl.css +159 -0
  193. package/dist/components/blocks/feature-bullets/FeatureBullets.d.ts +1 -0
  194. package/dist/components/blocks/feature-bullets/FeatureBullets.js +2 -1
  195. package/dist/components/blocks/feature-cards/FeatureCards.d.ts +1 -0
  196. package/dist/components/blocks/feature-cards/FeatureCards.js +1 -0
  197. package/dist/components/blocks/feature-scroll/FeatureScroll.js +9 -3
  198. package/dist/components/blocks/feature-scroll/feature-scroll-impl.css +63 -40
  199. package/dist/components/blocks/feature-split/FeatureSplit.d.ts +1 -0
  200. package/dist/components/blocks/feature-split/FeatureSplit.js +2 -1
  201. package/dist/components/blocks/feature-stack/FeatureStack.d.ts +1 -0
  202. package/dist/components/blocks/feature-stack/FeatureStack.js +2 -1
  203. package/dist/components/blocks/image-banner/ImageBanner.d.ts +1 -0
  204. package/dist/components/blocks/image-banner/ImageBanner.js +2 -1
  205. package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +2 -2
  206. package/dist/components/blocks/image-banner/ImageBanner.types.js +1 -1
  207. package/dist/components/blocks/image-banner/image-banner-impl.css +7 -51
  208. package/dist/components/blocks/landing-hero/LandingHero.css +1 -1
  209. package/dist/components/blocks/landing-hero/LandingHero.d.ts +1 -0
  210. package/dist/components/blocks/landing-hero/LandingHero.js +2 -1
  211. package/dist/components/blocks/product-hero/ProductHero.css +1 -1
  212. package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -0
  213. package/dist/components/blocks/product-hero/ProductHero.js +2 -1
  214. package/dist/components/blocks/steps/Steps.css +2 -2
  215. package/dist/components/blocks/steps/Steps.d.ts +1 -0
  216. package/dist/components/blocks/steps/Steps.js +3 -2
  217. package/dist/components/blocks/steps/Steps.types.d.ts +1 -1
  218. package/dist/components/blocks/steps/steps-impl.css +76 -4
  219. package/dist/components/blocks/steps/use-turn.d.ts +8 -8
  220. package/dist/components/blocks/steps/use-turn.js +19 -16
  221. package/dist/components/blocks/team-grid/TeamGrid.d.ts +1 -0
  222. package/dist/components/blocks/team-grid/TeamGrid.js +2 -1
  223. package/dist/components/core/card/Card.css +1 -1
  224. package/dist/components/core/segmented-control-item/SegmentedControlItem.types.d.ts +1 -1
  225. package/dist/components/core/select/Select.css +2 -0
  226. package/dist/components/core/select/select-impl.css +58 -0
  227. package/dist/components/core/text-area/TextArea.css +2 -0
  228. package/dist/components/core/text-area/text-area-impl.css +54 -0
  229. package/dist/components/core/text-input/TextInput.css +2 -0
  230. package/dist/components/core/text-input/text-input-impl.css +63 -0
  231. package/dist/components/estimator-data.d.ts +29 -0
  232. package/dist/components/estimator-data.js +12 -0
  233. package/dist/components/index.d.ts +3 -0
  234. package/dist/components/index.js +2 -0
  235. package/dist/icons/accounts.d.ts +1 -0
  236. package/dist/icons/accounts.js +1 -0
  237. package/dist/icons/app-stores.d.ts +1 -0
  238. package/dist/icons/app-stores.js +1 -0
  239. package/dist/icons/currency.d.ts +1 -0
  240. package/dist/icons/currency.js +1 -0
  241. package/dist/icons/flag.d.ts +1 -0
  242. package/dist/icons/flag.js +1 -0
  243. package/dist/icons/illustrative.d.ts +1 -0
  244. package/dist/icons/illustrative.js +1 -0
  245. package/dist/icons/index.d.ts +1 -0
  246. package/dist/icons/index.js +4 -0
  247. package/dist/icons/markets.d.ts +1 -0
  248. package/dist/icons/markets.js +1 -0
  249. package/dist/icons/social.d.ts +1 -0
  250. package/dist/icons/social.js +1 -0
  251. package/dist/icons/system.d.ts +1 -0
  252. package/dist/icons/system.js +1 -0
  253. package/dist/icons/trade.d.ts +1 -0
  254. package/dist/icons/trade.js +1 -0
  255. package/dist/marketing.css +10 -0
  256. package/package.json +44 -2
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Typography
4
4
 
5
- Copy, set in one of the page's type roles. …
5
+ Sets text in one of the type roles
6
6
 
7
7
  `import { Typography } from '@deriv-com/dds-marketing'`
8
8
 
package/.llm/index.md CHANGED
@@ -9,7 +9,7 @@ need — loading all of them dilutes the relevant one.
9
9
  Paths here are from the package root, so they resolve whether this file is read in place
10
10
  or its content handed over on its own.
11
11
 
12
- Sections: Core (54) · Layout (5) · Blocks (30)
12
+ Sections: Core (54) · Layout (5) · Blocks (31)
13
13
 
14
14
 
15
15
  ## Core
@@ -105,6 +105,7 @@ The sections a page is built from, and the rhythm between them.
105
105
  | CtaBannerSlider | Rotating between two or three genuinely different promotions without a hero's weight — open an account, try a tool, download the app | One promotion → `CtaBanner` · The band that opens the page → `LandingHero` · A message with more than a headline, a line and a button or two → `FeatureSplit` |
106
106
  | DataTable | Trading instruments and their specifications — contract size, minimum size, volume limits — on a market page · Trading conditions a reader looks up rather than reads through: spreads, leverage, margin, swaps · An MT5 or Deriv X specification page, where the set runs to dozens of rows and a reader is hunting for the one they trade | Three or four plans or accounts compared feature by feature, where every row is read rather than searched → `ComparisonTable` · A handful of rows with no need to search or page — a fee schedule of four lines → `Table` |
107
107
  | DownloadBanner | Promoting an app across the stores and platforms it ships on — App Store, Google Play, AppGallery, a desktop download, the web · A trading platform's page, near its end, once the product has been explained and the next thing a reader wants is to get it | One download, or the web alone → `Button` · A promotion that has to argue for the download in a headline and a sentence → `CtaBanner` |
108
+ | Estimator | A page lets a reader work something out before they act — a margin, a pip value, a swap charge, a conversion, an income estimate · The figures change as the reader types or picks, and should be announced as they do · The same tool appears on several pages in several forms — the margin, pip and swap calculators are one layout with different fields and figures | The figures are fixed facts rather than the result of anything the reader enters → `Stats` · Options are compared side by side rather than worked out from an input → `ComparisonCards` · The tool is a form that sends something — a sign-up, a contact request → `FeatureSplit` |
108
109
  | FaqAccordion | The questions a reader asks before acting — fees, limits, how long a withdrawal takes — near the end of a landing page or on a help page · Objections a page has to answer without making every reader read every answer | The answers are short enough to read all at once → `FeatureBullets` · Each item is a feature, not a question → `FeatureCards` |
109
110
  | FeatureBullets | A page has to list what a product or a plan includes, and the list is the argument · Several benefits are each one line long and are read by scanning rather than reading · A claim needs something beside it, and a picture is the only thing there is to put there | The items are two or three words and belong on one line → `Badge` · Each item needs a heading and a paragraph of its own → `Card` · The section opens the page → `ProductHero` |
110
111
  | FeatureCards | Three or four things a product does, each a card with a heading, a line and a picture under them, read side by side and compared at a glance — the first choice for a set of three. The picture is CardBody's `src`, drawn below the words by default · A set of destinations — markets, platforms, tools — each a card a reader presses to go there · One feature that deserves the whole row in a card of its own — the words beside a large picture, carded on the section's ground | Each item is one line, and the list is the argument → `FeatureBullets` · One claim beside a picture, with no card around it → `FeatureSplit` · The items are read in order, one leading to the next → `FeatureScroll` |
package/.llm/tokens.md CHANGED
@@ -378,6 +378,7 @@ one value at every width.
378
378
  ## motion
379
379
 
380
380
  - `--motion-distance-drift` — How far decoration travels against the scroll while its section passes through the view, one way or the other. Far enough to be seen moving as the reader scrolls past, so it reads as depth.
381
+ - `--motion-distance-shake` — How far a field swings each way when it shakes to say a value was refused. Small on purpose: the field has to stay readable mid-swing, and a reader who has just been told no does not need to be shoved as well.
381
382
  - `--motion-duration-dramatic` — Editorial hero entrances for visual impact, e.g. hero headline revealing, landing page title appearing. Not for scroll reveals — use motion.duration.reveal.
382
383
  - `--motion-duration-drift` — One way of a slow drift that runs while something is on screen — an aurora wandering along a band's foot and back. Ten seconds, so the movement is felt rather than watched.
383
384
  - `--motion-duration-dwell` — How long a rotating message holds before the next one moves in, e.g. a CTA banner's slides. Long enough to read a headline and a line under it; the reference site's 2000 moved before a reader reached the button.
@@ -392,6 +393,7 @@ one value at every width.
392
393
  - `--motion-duration-slow` — Large UI transitions, e.g. accordions expanding, page transitions, interactive demos.
393
394
  - `--motion-duration-spinner` — One full rotation of a spinner. Fast enough to read as working, slow enough that the stroke does not blur into a solid ring.
394
395
  - `--motion-duration-stagger` — The step between one part of a section arriving and the next. A gap rather than a length: five parts at this step trail the first by less than half a second, so a block still arrives as one thing rather than assembling itself while a reader waits.
396
+ - `--motion-duration-step` — How long one step holds before the next takes its turn, in a Steps list under autoplay. Longer than dwell: a step is a title, a sentence and a picture to take in, not a headline to glance at, and at dwell's pace the list moved on before a reader had finished the line.
395
397
  - `--motion-duration-ticker` — How long a running row takes to carry one item past — Awards' row on a phone, multiplied by the number of awards for one whole loop. The reference site's pace: thirty-two seconds for eight awards, slow enough to read an award as it goes by.
396
398
  - `--motion-duration-trail` — How long something takes to catch up with a pointer it trails — a background's glow following the pointer across a band. Half a second, so the light drifts after the hand rather than sticking to it: a trail is felt, a cursor is tracked.
397
399
  - `--motion-duration-turn` — One turn of a wheel of figures: the one in place rising away as the next rises into it. The reference site's 700 — long enough that the eye follows the number in rather than seeing it swap.
@@ -406,9 +408,12 @@ one value at every width.
406
408
  - `--motion-scale-enter` — What a component scales from when something reveals it — a tooltip appearing. Needs a transform origin on the edge nearest its anchor, or it grows out of its own middle. The counterpart to scale.hover, which is a response rather than an entrance.
407
409
  - `--motion-scale-hover` — How much a control grows under the cursor on hover. Deliberately small — a control that grows past the pointer fires its own mouse-out.
408
410
  - `--motion-scale-media-hover` — How much art grows inside a frame that holds still — an editorial card's picture under the cursor. Twice motion.scale.hover, which stays smaller because a control can outrun its own pointer and a framed picture cannot.
411
+ - `--motion-scale-parallax` — How much larger a block's picture is drawn inside its frame, so it can drift with the scroll without its edge showing. A tenth: room for the parallax travel either way with a margin to spare, and too little to feel like a crop.
409
412
  - `--motion-scale-pile-covered` — How far a card in a StickyCards pile draws back once the next card has slid over it, from its top edge, so the pile reads as depth. Not motion.scale.enter, which is for something arriving.
410
413
  - `--motion-translate-enter` — Where a surface waits before something reveals it: level on the inline axis, its own full height down the block one. A share of the element rather than of the page, so a sheet travels exactly its own height whatever that turns out to be.
411
414
  - `--motion-translate-hover` — How far a pressable surface rises under the cursor. A fixed distance rather than a share of the element, so a tall card and a short one lift by the same amount and a row of them stays level. Small enough that nothing it sits beside looks disturbed.
415
+ - `--motion-translate-parallax-from` — Where a block's picture stands in its frame as the frame enters the view from below: a little above centre, as a share of its own height. It runs to parallax-to as the frame crosses the view, so the picture moves slower than the page and reads as further away.
416
+ - `--motion-translate-parallax-to` — Where a block's picture stands as its frame leaves the view at the top: a little below centre. The pair stays inside the room motion.scale.parallax gives, so no edge ever shows.
412
417
  - `--motion-translate-reveal` — How far a section waits below where it lands, before the scroll brings it up. A fixed distance rather than a share of the block, so a hero a screen tall and a split half that arrive by the same movement. Pairs with motion.easing.reveal.
413
418
 
414
419
  ## opacity
package/AGENTS.md CHANGED
@@ -150,6 +150,7 @@ screen is built in is not something to reach for while building a control.
150
150
  - **CtaBannerSlider** — Rotating between two or three genuinely different promotions without a hero's weight
151
151
  - **DataTable** — Trading instruments and their specifications
152
152
  - **DownloadBanner** — Promoting an app across the stores and platforms it ships on
153
+ - **Estimator** — A page lets a reader work something out before they act
153
154
  - **FaqAccordion** — The questions a reader asks before acting
154
155
  - **FeatureBullets** — A page has to list what a product or a plan includes, and the list is the argument
155
156
  - **FeatureCards** — Three or four things a product does, each a card with a heading, a line and a picture under them, read side by side and compared at a glance
package/README.md CHANGED
@@ -19,13 +19,15 @@ on when to reach for it. Product apps want
19
19
  npm install @deriv-com/dds-marketing
20
20
  ```
21
21
 
22
- React 19 and the Quill icon set are peer dependencies, so install them alongside it if
23
- your app does not already have them:
22
+ React 19 is a peer dependency, so install it alongside if your app does not already have
23
+ it:
24
24
 
25
25
  ```bash
26
- npm install react react-dom @deriv-com/quill-icons-web
26
+ npm install react react-dom
27
27
  ```
28
28
 
29
+ The Quill icon set comes with the package — there is nothing else to install.
30
+
29
31
  ## Use
30
32
 
31
33
  Import the stylesheet once, at the root of your app. It defines every token this package's
@@ -60,6 +62,29 @@ export function Home() {
60
62
 
61
63
  Each component brings its own stylesheet with it, so nothing else needs importing.
62
64
 
65
+ ## Icons
66
+
67
+ Every [Quill](https://github.com/deriv-com/product-design-quill-icons) icon is re-exported
68
+ from `@deriv-com/dds-marketing/icons`, under the name Quill gives it:
69
+
70
+ ```tsx
71
+ import { Icon } from '@deriv-com/dds-marketing'
72
+ import { CircleCheckSolidIcon } from '@deriv-com/dds-marketing/icons'
73
+ import { UsdIcon } from '@deriv-com/dds-marketing/icons/currency'
74
+
75
+ <Icon label="Verified"><CircleCheckSolidIcon /></Icon>
76
+ ```
77
+
78
+ Each Quill group has a subpath of its own — `accounts`, `app-stores`, `currency`, `flag`,
79
+ `illustrative`, `markets`, `social`, `system` and `trade`. Production builds tree-shake either
80
+ form to the icons you use; a dev server does not, so the group subpaths keep it from loading
81
+ all ~1,850 at once. Raw SVG files are not re-exported; for those, install
82
+ `@deriv-com/quill-icons-web` and use its `svg/` folder.
83
+
84
+ An app that already imports from `@deriv-com/quill-icons-web` keeps working. It is the same
85
+ module, so the two can be mixed while you migrate — but drop the direct dependency when you
86
+ are done, or pin it to the range this package uses, so the app does not bundle two copies.
87
+
63
88
  ## Fonts
64
89
 
65
90
  The system is designed for [Inter](https://rsms.me/inter/) (all text) and
@@ -0,0 +1,62 @@
1
+ /*
2
+ * The drift every block's picture shares, defined once.
3
+ *
4
+ * As a block passes through the view its picture moves a little slower than the page, so it reads
5
+ * as further away than the words beside it — the reference design's parallax, on every block that
6
+ * holds a picture rather than as an option one banner had. A copy per block is how ten sections
7
+ * come to disagree about how far a picture moves, so the movement is stated here and each block
8
+ * only marks its frame: `data-dds-parallax` on the box that holds the picture and clips it.
9
+ *
10
+ * Imported by each block's component, which is what puts it in the bundle once however many blocks
11
+ * a page uses.
12
+ *
13
+ * Driven by the scroll and nothing else. The frame names a view timeline and the picture runs down
14
+ * it over the frame's whole pass across the view (`cover`), from motion.translate.parallax-from to
15
+ * motion.translate.parallax-to. No script measures anything and no listener runs on scroll: the
16
+ * browser advances the timeline itself, and the only properties it animates are `translate` and
17
+ * `scale`, which it composites without laying the page out or painting it again — so a page full
18
+ * of pictures scrolls as smoothly as a page with none. Linear, because the scroll is the easing.
19
+ *
20
+ * The picture is drawn at motion.scale.parallax inside a frame that clips it, which leaves room
21
+ * above and below for the travel, so no edge of the picture is ever seen. The frame keeps its size,
22
+ * so nothing around it moves.
23
+ *
24
+ * The frame clips with `overflow: clip`, not `hidden`. `hidden` makes a box a scroll container, and
25
+ * a view timeline is measured against its nearest one: under a box that never scrolls it stands
26
+ * still at its first frame. The same holds for every box above the frame, which is why the blocks
27
+ * that carry one clip their roots rather than hiding their overflow.
28
+ *
29
+ * Every picture inside the frame drifts, except one inside an IllustrationCard: the card is a
30
+ * picture of the product laid over the photograph, and the photograph moving behind it is the
31
+ * effect; the product's own icons sliding inside the card would be the card coming apart.
32
+ *
33
+ * Only where the browser can drive it from the scroll, and only for a reader who has not asked for
34
+ * less motion. Everywhere else the picture stands still, centred and at its own size.
35
+ */
36
+ /* FeatureCards' pictures are drawn by CardBody, a core component that marks nothing, so its frame is
37
+ named here for the one block that wants it moving. */
38
+ :where([data-dds-context="marketing"]) :is([data-dds-parallax], .dds-featurecards .dds-cardbody__media-frame) {
39
+ overflow: clip;
40
+ view-timeline-axis: block;
41
+ view-timeline-name: --dds-parallax;
42
+ }
43
+
44
+ @supports (animation-timeline: view()) {
45
+ @media (prefers-reduced-motion: no-preference) {
46
+ :where([data-dds-context="marketing"]) :is([data-dds-parallax], .dds-featurecards .dds-cardbody__media-frame) img:not(.dds-illustrationcard img) {
47
+ animation: dds-parallax linear both;
48
+ animation-range: cover;
49
+ animation-timeline: --dds-parallax;
50
+ scale: var(--motion-scale-parallax);
51
+ }
52
+ }
53
+ }
54
+
55
+ @keyframes dds-parallax {
56
+ from {
57
+ translate: var(--motion-translate-parallax-from);
58
+ }
59
+ to {
60
+ translate: var(--motion-translate-parallax-to);
61
+ }
62
+ }
@@ -248,15 +248,3 @@
248
248
  :where([data-dds-context="marketing"]) .dds-comparisoncards[data-variant='highlighted'] .dds-comparisoncards__item[data-recommended]:has(.dds-comparisoncards__highlight) .dds-comparisoncards__card-head {
249
249
  padding-block-start: var(--space-inset-md);
250
250
  }
251
-
252
- /*
253
- * A pair holds to two thirds of the row from full width, centred — FeatureCards' rule and its
254
- * reason: two cards across the whole width put each label half a page from its value.
255
- */
256
- @container (min-width: 64rem) {
257
- :where([data-dds-context="marketing"]) .dds-comparisoncards .dds-comparisoncards__grid:has(> :nth-child(2):last-child) {
258
- inline-size: 100%;
259
- margin-inline: auto;
260
- max-inline-size: calc((100% - 2 * var(--space-grid-gutter)) * 2 / 3 + var(--space-grid-gutter));
261
- }
262
- }
@@ -1,6 +1,7 @@
1
1
  import './DownloadBanner.css';
2
2
  import './download-banner-impl.css';
3
3
  import '../block-reveal.css';
4
+ import '../block-parallax.css';
4
5
  import { type DownloadBannerProps } from './DownloadBanner.types.js';
5
6
  export type { DownloadBannerProps };
6
7
  /**
@@ -5,6 +5,7 @@ import { AndroidApkFillColoredIcon, AppleAppStoreFillColoredIcon, AppleDmgFillCo
5
5
  import './DownloadBanner.css';
6
6
  import './download-banner-impl.css';
7
7
  import '../block-reveal.css';
8
+ import '../block-parallax.css';
8
9
  import { Background } from '../../core/background/Background.js';
9
10
  import { given } from '../../given.js';
10
11
  import { arriving, useReveal } from '../use-reveal.js';
@@ -71,5 +72,5 @@ export function DownloadBanner({ ground = downloadBannerDefaults.ground, variant
71
72
  builds of the same desktop app — and a key repeated is a badge React confuses
72
73
  with the other. */
73
74
  _jsx("li", { className: downloadBannerParts.badge, children: _jsx("a", { className: downloadBannerParts['badge-link'], href: href, "aria-label": given(label) ? label : name, children: _jsx(Mark, { className: downloadBannerParts.mark, width: undefined, height: undefined, "aria-hidden": "true", focusable: "false" }) }) }, `${platform}-${at}`));
74
- }) })) : null, variant === 'featured' && media !== undefined && media !== null ? (_jsx("div", { className: downloadBannerParts.media, ...arriving(3), children: media })) : null] }) }) }));
75
+ }) })) : null, variant === 'featured' && media !== undefined && media !== null ? (_jsx("div", { "data-dds-parallax": "", className: downloadBannerParts.media, ...arriving(3), children: media })) : null] }) }) }));
75
76
  }
@@ -0,0 +1,206 @@
1
+ /* GENERATED from estimator.contract.json. Do not edit. */
2
+
3
+ :where([data-dds-context="marketing"]) .dds-estimator {
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-estimator .dds-estimator__inner {
13
+ box-sizing: border-box;
14
+ display: flex;
15
+ flex-direction: column;
16
+ inline-size: 100%;
17
+ min-inline-size: 0;
18
+ column-gap: var(--space-gap-3xl);
19
+ max-inline-size: var(--size-content-max);
20
+ padding-block: var(--space-section-default);
21
+ padding-inline: var(--space-inset-page);
22
+ row-gap: var(--space-heading-content);
23
+ }
24
+
25
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__header {
26
+ display: flex;
27
+ flex-direction: column;
28
+ min-inline-size: 0;
29
+ row-gap: var(--space-gap-none);
30
+ }
31
+
32
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__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-estimator .dds-estimator__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-estimator .dds-estimator__tool {
54
+ display: flex;
55
+ flex-direction: column;
56
+ min-inline-size: 0;
57
+ row-gap: var(--space-gap-none);
58
+ }
59
+
60
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__panels {
61
+ box-sizing: border-box;
62
+ display: flex;
63
+ flex-direction: column;
64
+ min-inline-size: 0;
65
+ column-gap: var(--space-gap-xl);
66
+ padding-block: var(--space-inset-card);
67
+ padding-inline: var(--space-inset-card);
68
+ row-gap: var(--space-gap-2xl);
69
+ }
70
+
71
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inputs {
72
+ display: flex;
73
+ flex-direction: column;
74
+ min-inline-size: 0;
75
+ row-gap: var(--space-gap-lg);
76
+ }
77
+
78
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inputs-label {
79
+ margin-block: 0;
80
+ font-family: var(--font-h5-family);
81
+ font-size: var(--font-h5-size);
82
+ font-weight: var(--font-h5-weight);
83
+ letter-spacing: var(--font-h5-letter-spacing);
84
+ line-height: var(--font-h5-line-height);
85
+ }
86
+
87
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__results {
88
+ display: flex;
89
+ flex-direction: column;
90
+ min-inline-size: 0;
91
+ background-color: var(--color-bg-surface-card-raised);
92
+ border-radius: var(--radius-surface-compact);
93
+ box-shadow: var(--elevation-surface-floating);
94
+ padding-block: var(--space-inset-card);
95
+ padding-inline: var(--space-inset-card);
96
+ row-gap: var(--space-gap-lg);
97
+ }
98
+
99
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__results-label {
100
+ margin-block: 0;
101
+ font-family: var(--font-h5-family);
102
+ font-size: var(--font-h5-size);
103
+ font-weight: var(--font-h5-weight);
104
+ letter-spacing: var(--font-h5-letter-spacing);
105
+ line-height: var(--font-h5-line-height);
106
+ }
107
+
108
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__list {
109
+ display: flex;
110
+ flex-direction: column;
111
+ margin-block: 0;
112
+ margin-inline: 0;
113
+ padding-block: 0;
114
+ padding-inline: 0;
115
+ column-gap: var(--space-gap-2xl);
116
+ row-gap: var(--space-gap-none);
117
+ }
118
+
119
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result {
120
+ border-block-end-style: solid;
121
+ display: flex;
122
+ flex-direction: column;
123
+ min-inline-size: 0;
124
+ border-block-end-width: var(--border-width-default);
125
+ border-color: var(--color-border-subtle);
126
+ padding-block: var(--space-inset-md);
127
+ row-gap: var(--space-gap-xs);
128
+ }
129
+
130
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-value {
131
+ margin-inline: 0;
132
+ font-family: var(--font-h4-family);
133
+ font-size: var(--font-h4-size);
134
+ font-weight: var(--font-h4-weight);
135
+ letter-spacing: var(--font-h4-letter-spacing);
136
+ line-height: var(--font-h4-line-height);
137
+ }
138
+
139
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-note {
140
+ margin-inline: 0;
141
+ color: var(--color-fg-muted);
142
+ font-family: var(--font-body-sm-family);
143
+ font-size: var(--font-body-sm-size);
144
+ font-weight: var(--font-body-sm-weight);
145
+ letter-spacing: var(--font-body-sm-letter-spacing);
146
+ line-height: var(--font-body-sm-line-height);
147
+ }
148
+
149
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__empty {
150
+ margin-block: 0;
151
+ color: var(--color-fg-muted);
152
+ font-family: var(--font-body-md-family);
153
+ font-size: var(--font-body-md-size);
154
+ font-weight: var(--font-body-md-weight);
155
+ letter-spacing: var(--font-body-md-letter-spacing);
156
+ line-height: var(--font-body-md-line-height);
157
+ }
158
+
159
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__action {
160
+ display: flex;
161
+ margin-block-start: var(--space-gap-sm);
162
+ }
163
+
164
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__input-actions {
165
+ display: flex;
166
+ flex-wrap: wrap;
167
+ column-gap: var(--space-actions-gap);
168
+ margin-block-start: var(--space-gap-sm);
169
+ row-gap: var(--space-actions-gap);
170
+ }
171
+
172
+ :where([data-dds-context="marketing"]) .dds-estimator[data-layout="split"] .dds-estimator__header {
173
+ align-items: flex-start;
174
+ text-align: start;
175
+ }
176
+
177
+ :where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] .dds-estimator__header {
178
+ align-items: center;
179
+ text-align: center;
180
+ }
181
+
182
+ :where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-label {
183
+ color: var(--color-fg-muted);
184
+ font-family: var(--font-body-sm-family);
185
+ font-size: var(--font-body-sm-size);
186
+ font-weight: var(--font-body-sm-weight);
187
+ letter-spacing: var(--font-body-sm-letter-spacing);
188
+ line-height: var(--font-body-sm-line-height);
189
+ }
190
+
191
+ :where([data-dds-context="marketing"]) .dds-estimator[data-ground="page"] {
192
+ background-color: var(--color-bg-page);
193
+ color: var(--color-fg-default);
194
+ }
195
+
196
+ :where([data-dds-context="marketing"]) .dds-estimator[data-ground="strong"] {
197
+ background-color: var(--color-bg-brand-strong);
198
+ color: var(--color-fg-on-brand-strong);
199
+ }
200
+
201
+ :where([data-dds-context="marketing"]) .dds-estimator[data-ground="subtle"] {
202
+ background-color: var(--color-bg-page);
203
+ background-image: linear-gradient(var(--color-bg-band-subtle), var(--color-bg-band-subtle));
204
+ color: var(--color-fg-default);
205
+ }
206
+
@@ -0,0 +1,27 @@
1
+ import './Estimator.css';
2
+ import './estimator-impl.css';
3
+ import '../block-reveal.css';
4
+ import { type EstimatorProps } from './Estimator.types.js';
5
+ export type { EstimatorProps };
6
+ /**
7
+ * A section that turns a reader's inputs into figures — the trading calculators, the crypto
8
+ * converter and the affiliate income estimate of the reference design, as one block.
9
+ *
10
+ * It lays the tool out and works nothing out. The page passes its own fields as `inputs` and the
11
+ * figures it has computed from them as `results`; a live feed, a formula, a currency conversion are
12
+ * all the page's, so the block is the same on every page that uses it and in every test, and none
13
+ * of it reaches the network. What the block owns is the part every such tool gets wrong once:
14
+ *
15
+ * - The figures are a description list, so a value is announced with the label it answers.
16
+ * - The results panel is a polite live region from the first paint, so a reader using a screen
17
+ * reader hears each new set of figures once, after they stop typing, without being interrupted —
18
+ * and it is there before the first change, which is the only way a live region is listened to.
19
+ * - Each value is isolated (`<bdi>`), so '-12.40 USD' or '0.0214 BTC' keeps its order in a
20
+ * right-to-left line, where a sign or a currency is neutral and moves to the far end.
21
+ * - Before there are figures the panel says what it is waiting for, rather than showing a row of
22
+ * dashes or a zero that reads as an answer.
23
+ *
24
+ * The tool is a Card at its `surface` variant, the same object as every other card on the page; the
25
+ * ground it stands on decides its fill, in the stylesheet, as ComparisonCards' cards do.
26
+ */
27
+ export declare function Estimator({ ground, layout, title, description, inputsLabel, inputs, inputActions, onCalculate, resultsLabel, results, emptyMessage, action, }: EstimatorProps): import("react").JSX.Element;
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from 'react';
4
+ import './Estimator.css';
5
+ import './estimator-impl.css';
6
+ import '../block-reveal.css';
7
+ import { arriving, useReveal } from '../use-reveal.js';
8
+ import { given } from '../../given.js';
9
+ import { Card } from '../../core/card/Card.js';
10
+ import { estimatorDefaults, estimatorParts } from './Estimator.types.js';
11
+ /**
12
+ * A section that turns a reader's inputs into figures — the trading calculators, the crypto
13
+ * converter and the affiliate income estimate of the reference design, as one block.
14
+ *
15
+ * It lays the tool out and works nothing out. The page passes its own fields as `inputs` and the
16
+ * figures it has computed from them as `results`; a live feed, a formula, a currency conversion are
17
+ * all the page's, so the block is the same on every page that uses it and in every test, and none
18
+ * of it reaches the network. What the block owns is the part every such tool gets wrong once:
19
+ *
20
+ * - The figures are a description list, so a value is announced with the label it answers.
21
+ * - The results panel is a polite live region from the first paint, so a reader using a screen
22
+ * reader hears each new set of figures once, after they stop typing, without being interrupted —
23
+ * and it is there before the first change, which is the only way a live region is listened to.
24
+ * - Each value is isolated (`<bdi>`), so '-12.40 USD' or '0.0214 BTC' keeps its order in a
25
+ * right-to-left line, where a sign or a currency is neutral and moves to the far end.
26
+ * - Before there are figures the panel says what it is waiting for, rather than showing a row of
27
+ * dashes or a zero that reads as an answer.
28
+ *
29
+ * The tool is a Card at its `surface` variant, the same object as every other card on the page; the
30
+ * ground it stands on decides its fill, in the stylesheet, as ComparisonCards' cards do.
31
+ */
32
+ export function Estimator({ ground = estimatorDefaults.ground, layout = estimatorDefaults.layout, title, description, inputsLabel, inputs, inputActions, onCalculate, resultsLabel, results, emptyMessage, action, }) {
33
+ const titleId = useId();
34
+ const [band, revealed] = useReveal();
35
+ const figures = Array.isArray(results) ? results : [];
36
+ /*
37
+ Enter in a field asks for the figures, which is what a reader expects of a calculator — the
38
+ page's Calculate button calls the same `onCalculate` from its own press. Only a plain field:
39
+ Enter in a picker's search chooses an answer, and in a text area it starts a line, and both
40
+ are theirs to keep.
41
+ */
42
+ const calculateOnEnter = (event) => {
43
+ if (event.key !== 'Enter' || onCalculate === undefined)
44
+ return;
45
+ const field = event.target;
46
+ if (!(field instanceof HTMLInputElement))
47
+ return;
48
+ if (/^(checkbox|radio|button|submit|reset|file)$/.test(field.type))
49
+ return;
50
+ if (field.closest('[role="listbox"], [role="dialog"], [aria-haspopup], [aria-autocomplete]') !== null)
51
+ return;
52
+ event.preventDefault();
53
+ onCalculate();
54
+ };
55
+ // The fields are a form, and its submit never leaves the page. A form with one field submits on
56
+ // Enter by itself, and without this a reader pressing it would reload the page from under the tool.
57
+ const stay = (event) => event.preventDefault();
58
+ return (_jsx("section", { className: estimatorParts.root, "data-ground": ground, "data-layout": layout, "aria-labelledby": titleId, children: _jsxs("div", { className: estimatorParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: estimatorParts.header, children: [_jsx("h2", { className: estimatorParts.title, ...arriving(0), id: titleId, children: title }), given(description) ? (_jsx("p", { className: estimatorParts.description, ...arriving(1), children: description })) : null] }), _jsx("div", { className: estimatorParts.tool, "data-theme": ground === 'strong' ? 'dark' : undefined, ...arriving(2), children: _jsx(Card, { variant: "surface", children: _jsxs("div", { className: estimatorParts.panels, children: [_jsxs("form", { className: estimatorParts.inputs, onSubmit: stay, onKeyDown: calculateOnEnter, children: [given(inputsLabel) ? _jsx("h3", { className: estimatorParts['inputs-label'], children: inputsLabel }) : null, inputs, inputActions ? _jsx("div", { className: estimatorParts['input-actions'], children: inputActions }) : null] }), _jsxs("div", { className: estimatorParts.results, "aria-live": "polite", children: [given(resultsLabel) ? _jsx("h3", { className: estimatorParts['results-label'], children: resultsLabel }) : null, figures.length > 0 ? (_jsx("dl", { className: estimatorParts.list, children: figures.map((figure) => (
59
+ // A figure's label is its identity: a tool never shows the same figure twice.
60
+ _jsxs("div", { className: estimatorParts.result, children: [_jsx("dt", { className: estimatorParts['result-label'], children: figure.label }), _jsx("dd", { className: estimatorParts['result-value'], children: _jsx("bdi", { children: figure.value }) }), given(figure.note) ? (_jsx("dd", { className: estimatorParts['result-note'], children: figure.note })) : null] }, figure.label))) })) : given(emptyMessage) ? (_jsx("p", { className: estimatorParts.empty, children: emptyMessage })) : null, action ? _jsx("div", { className: estimatorParts.action, children: action }) : null] })] }) }) })] }) }));
61
+ }
@@ -0,0 +1,63 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { EstimatorResult } from '../../estimator-data.js';
3
+ export interface EstimatorOwnProps {
4
+ /**
5
+ * `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
6
+ * `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. 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.
7
+ * `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
8
+ */
9
+ ground?: 'page' | 'subtle' | 'strong';
10
+ /**
11
+ * `stacked` — The default. The heading centred above a wide card, the inputs beside the results — a calculator with several fields and several figures, where the two panels each need room
12
+ * `split` — The heading and a few lines beside the card, the inputs above the results inside it — a compact tool a page introduces in its own words, a converter or a single estimate
13
+ */
14
+ layout?: 'stacked' | 'split';
15
+ /** Always. What the tool works out, in a few words — 'Trading calculator', 'Buy crypto in seconds', 'Estimate your earnings'. */
16
+ title: string;
17
+ /** A line or two under the title saying what the figures are for or where they come from — 'Live prices, updated every few seconds'. */
18
+ description?: string;
19
+ /** A heading over the inputs panel, where the two panels need naming — 'Calculator' over the fields and 'Results' over the figures. Leave both out for a tool whose fields name themselves. */
20
+ inputsLabel?: string;
21
+ /** Always. The fields the reader fills in — Select, TextInput, SearchInput, SegmentedControl, a slider — in the order they are filled. The page owns their values and works the results out from them. */
22
+ inputs: ReactNode;
23
+ /** A tool that works its figures out when asked rather than as the reader types — a calculation too slow or too costly to run on every keystroke, or one where half-filled fields would show figures that mislead. 'Calculate', whose press calls the page's own handler, and 'Reset' beside it to clear the fields. They share the row; on a phone they stack and each fills the width. */
24
+ inputActions?: ReactNode;
25
+ /** Whenever inputActions holds a Calculate button: Enter in any of the fields calls it, as the button's own press does — wire the button's onClick to the same function. The page works the figures out and passes them as `results`; the results panel announces them. */
26
+ onCalculate?: () => void;
27
+ /** A heading over the results panel, with inputsLabel — 'Results', 'You receive'. */
28
+ resultsLabel?: string;
29
+ /** The figures the inputs produce, in reading order — a `label`, a `value` written exactly as it is to be read, and an optional `note`. Pass an empty list until the inputs are complete, and the panel shows `emptyMessage` instead. Each change is announced politely, so a reader using a screen reader hears the new figures without being interrupted. */
30
+ results: EstimatorResult[];
31
+ /** What the results panel says before there are figures to show — 'Choose an instrument to see the results'. Translate it with the page. */
32
+ emptyMessage?: string;
33
+ /** The one thing a reader does once they have their figures — 'Open an account', 'Trade now', 'Become a partner'. Under the results, where the reader's eye already is. */
34
+ action?: ReactNode;
35
+ }
36
+ export type EstimatorProps = EstimatorOwnProps;
37
+ /** The class each part carries. Apply these — do not retype them. */
38
+ export declare const estimatorParts: {
39
+ readonly root: "dds-estimator";
40
+ readonly inner: "dds-estimator__inner";
41
+ readonly header: "dds-estimator__header";
42
+ readonly title: "dds-estimator__title";
43
+ readonly description: "dds-estimator__description";
44
+ readonly tool: "dds-estimator__tool";
45
+ readonly panels: "dds-estimator__panels";
46
+ readonly inputs: "dds-estimator__inputs";
47
+ readonly 'inputs-label': "dds-estimator__inputs-label";
48
+ readonly results: "dds-estimator__results";
49
+ readonly 'results-label': "dds-estimator__results-label";
50
+ readonly list: "dds-estimator__list";
51
+ readonly result: "dds-estimator__result";
52
+ readonly 'result-label': "dds-estimator__result-label";
53
+ readonly 'result-value': "dds-estimator__result-value";
54
+ readonly 'result-note': "dds-estimator__result-note";
55
+ readonly empty: "dds-estimator__empty";
56
+ readonly action: "dds-estimator__action";
57
+ readonly 'input-actions': "dds-estimator__input-actions";
58
+ };
59
+ /** Generated so defaults cannot drift from the contract. */
60
+ export declare const estimatorDefaults: {
61
+ readonly ground: "page";
62
+ readonly layout: "stacked";
63
+ };
@@ -0,0 +1,25 @@
1
+ // GENERATED from estimator.contract.json. Do not edit.
2
+ /** The class each part carries. Apply these — do not retype them. */
3
+ export const estimatorParts = {
4
+ root: 'dds-estimator',
5
+ inner: 'dds-estimator__inner',
6
+ header: 'dds-estimator__header',
7
+ title: 'dds-estimator__title',
8
+ description: 'dds-estimator__description',
9
+ tool: 'dds-estimator__tool',
10
+ panels: 'dds-estimator__panels',
11
+ inputs: 'dds-estimator__inputs',
12
+ 'inputs-label': 'dds-estimator__inputs-label',
13
+ results: 'dds-estimator__results',
14
+ 'results-label': 'dds-estimator__results-label',
15
+ list: 'dds-estimator__list',
16
+ result: 'dds-estimator__result',
17
+ 'result-label': 'dds-estimator__result-label',
18
+ 'result-value': 'dds-estimator__result-value',
19
+ 'result-note': 'dds-estimator__result-note',
20
+ empty: 'dds-estimator__empty',
21
+ action: 'dds-estimator__action',
22
+ 'input-actions': 'dds-estimator__input-actions',
23
+ };
24
+ /** Generated so defaults cannot drift from the contract. */
25
+ export const estimatorDefaults = { ground: 'page', layout: 'stacked' };