@deriv-com/dds-marketing 0.1.0 → 0.2.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 (460) hide show
  1. package/.llm/components/accordion.md +193 -0
  2. package/.llm/components/accordionitem.md +236 -0
  3. package/.llm/components/aspectratio.md +212 -0
  4. package/.llm/components/badge.md +231 -0
  5. package/.llm/components/bottomsheet.md +404 -0
  6. package/.llm/components/breadcrumb.md +197 -0
  7. package/.llm/components/breadcrumbitem.md +178 -0
  8. package/.llm/components/button.md +271 -0
  9. package/.llm/components/buttonicon.md +313 -0
  10. package/.llm/components/card.md +313 -0
  11. package/.llm/components/cardbody.md +409 -0
  12. package/.llm/components/cardbutton.md +259 -0
  13. package/.llm/components/checkbox.md +285 -0
  14. package/.llm/components/divider.md +230 -0
  15. package/.llm/components/dropdownmenu.md +453 -0
  16. package/.llm/components/filterchip.md +314 -0
  17. package/.llm/components/footer.md +408 -0
  18. package/.llm/components/footergroup.md +241 -0
  19. package/.llm/components/grid.md +659 -0
  20. package/.llm/components/icon.md +194 -0
  21. package/.llm/components/link.md +354 -0
  22. package/.llm/components/menuitem.md +218 -0
  23. package/.llm/components/navbar.md +458 -0
  24. package/.llm/components/navbaritem.md +238 -0
  25. package/.llm/components/navbarmenu.md +319 -0
  26. package/.llm/components/navbarmenugroup.md +184 -0
  27. package/.llm/components/navbarpanel.md +255 -0
  28. package/.llm/components/navbarswitcher.md +343 -0
  29. package/.llm/components/navbarswitcheroption.md +156 -0
  30. package/.llm/components/option.md +268 -0
  31. package/.llm/components/pagination.md +361 -0
  32. package/.llm/components/removablechip.md +247 -0
  33. package/.llm/components/row.md +381 -0
  34. package/.llm/components/scroller.md +470 -0
  35. package/.llm/components/searchinput.md +279 -0
  36. package/.llm/components/segmentedcontrol.md +358 -0
  37. package/.llm/components/segmentedcontrolitem.md +272 -0
  38. package/.llm/components/select.md +509 -0
  39. package/.llm/components/selectchip.md +334 -0
  40. package/.llm/components/spinner.md +222 -0
  41. package/.llm/components/stack.md +334 -0
  42. package/.llm/components/statusdot.md +205 -0
  43. package/.llm/components/subdomainnavbar.md +327 -0
  44. package/.llm/components/tab.md +291 -0
  45. package/.llm/components/table.md +660 -0
  46. package/.llm/components/tablebody.md +258 -0
  47. package/.llm/components/tablecell.md +248 -0
  48. package/.llm/components/tablehead.md +248 -0
  49. package/.llm/components/tableheadercell.md +298 -0
  50. package/.llm/components/tablerow.md +285 -0
  51. package/.llm/components/tablist.md +210 -0
  52. package/.llm/components/textarea.md +362 -0
  53. package/.llm/components/textinput.md +467 -0
  54. package/.llm/components/tooltip.md +267 -0
  55. package/.llm/components/typography.md +245 -0
  56. package/.llm/digest/accordion.md +43 -0
  57. package/.llm/digest/accordionitem.md +47 -0
  58. package/.llm/digest/aspectratio.md +47 -0
  59. package/.llm/digest/badge.md +45 -0
  60. package/.llm/digest/bottomsheet.md +69 -0
  61. package/.llm/digest/breadcrumb.md +38 -0
  62. package/.llm/digest/breadcrumbitem.md +43 -0
  63. package/.llm/digest/button.md +48 -0
  64. package/.llm/digest/buttonicon.md +61 -0
  65. package/.llm/digest/card.md +50 -0
  66. package/.llm/digest/cardbody.md +68 -0
  67. package/.llm/digest/cardbutton.md +49 -0
  68. package/.llm/digest/checkbox.md +63 -0
  69. package/.llm/digest/divider.md +52 -0
  70. package/.llm/digest/dropdownmenu.md +67 -0
  71. package/.llm/digest/filterchip.md +68 -0
  72. package/.llm/digest/footer.md +57 -0
  73. package/.llm/digest/footergroup.md +42 -0
  74. package/.llm/digest/grid.md +62 -0
  75. package/.llm/digest/icon.md +45 -0
  76. package/.llm/digest/link.md +59 -0
  77. package/.llm/digest/menuitem.md +46 -0
  78. package/.llm/digest/navbar.md +60 -0
  79. package/.llm/digest/navbaritem.md +54 -0
  80. package/.llm/digest/navbarmenu.md +54 -0
  81. package/.llm/digest/navbarmenugroup.md +40 -0
  82. package/.llm/digest/navbarpanel.md +53 -0
  83. package/.llm/digest/navbarswitcher.md +51 -0
  84. package/.llm/digest/navbarswitcheroption.md +37 -0
  85. package/.llm/digest/option.md +54 -0
  86. package/.llm/digest/pagination.md +71 -0
  87. package/.llm/digest/removablechip.md +51 -0
  88. package/.llm/digest/row.md +60 -0
  89. package/.llm/digest/scroller.md +59 -0
  90. package/.llm/digest/searchinput.md +56 -0
  91. package/.llm/digest/segmentedcontrol.md +59 -0
  92. package/.llm/digest/segmentedcontrolitem.md +59 -0
  93. package/.llm/digest/select.md +92 -0
  94. package/.llm/digest/selectchip.md +68 -0
  95. package/.llm/digest/spinner.md +52 -0
  96. package/.llm/digest/stack.md +56 -0
  97. package/.llm/digest/statusdot.md +46 -0
  98. package/.llm/digest/subdomainnavbar.md +54 -0
  99. package/.llm/digest/tab.md +60 -0
  100. package/.llm/digest/table.md +71 -0
  101. package/.llm/digest/tablebody.md +49 -0
  102. package/.llm/digest/tablecell.md +55 -0
  103. package/.llm/digest/tablehead.md +50 -0
  104. package/.llm/digest/tableheadercell.md +65 -0
  105. package/.llm/digest/tablerow.md +56 -0
  106. package/.llm/digest/tablist.md +46 -0
  107. package/.llm/digest/textarea.md +78 -0
  108. package/.llm/digest/textinput.md +95 -0
  109. package/.llm/digest/tooltip.md +58 -0
  110. package/.llm/digest/typography.md +59 -0
  111. package/.llm/index.md +86 -0
  112. package/.llm/rules.md +122 -0
  113. package/.llm/tokens.md +341 -0
  114. package/AGENTS.md +164 -0
  115. package/LICENSE +21 -0
  116. package/README.md +63 -15
  117. package/dist/components/core/accordion/Accordion.css +10 -0
  118. package/dist/components/core/accordion/Accordion.d.ts +20 -0
  119. package/dist/components/core/accordion/Accordion.js +69 -0
  120. package/dist/components/core/accordion/Accordion.types.d.ts +20 -0
  121. package/dist/components/core/accordion/Accordion.types.js +7 -0
  122. package/dist/components/core/accordion-item/AccordionItem.css +98 -0
  123. package/dist/components/core/accordion-item/AccordionItem.d.ts +44 -0
  124. package/dist/components/core/accordion-item/AccordionItem.js +63 -0
  125. package/dist/components/core/accordion-item/AccordionItem.types.d.ts +32 -0
  126. package/dist/components/core/accordion-item/AccordionItem.types.js +13 -0
  127. package/dist/components/core/accordion-item/accordion-item-impl.css +107 -0
  128. package/dist/components/core/badge/Badge.css +37 -0
  129. package/dist/components/core/badge/Badge.d.ts +37 -0
  130. package/dist/components/core/badge/Badge.js +40 -0
  131. package/dist/components/core/badge/Badge.types.d.ts +24 -0
  132. package/dist/components/core/badge/Badge.types.js +8 -0
  133. package/dist/components/core/badge/badge-impl.css +32 -0
  134. package/dist/components/core/bottom-sheet/BottomSheet.css +178 -0
  135. package/dist/components/core/bottom-sheet/BottomSheet.d.ts +22 -0
  136. package/dist/components/core/bottom-sheet/BottomSheet.js +359 -0
  137. package/dist/components/core/bottom-sheet/BottomSheet.types.d.ts +67 -0
  138. package/dist/components/core/bottom-sheet/BottomSheet.types.js +18 -0
  139. package/dist/components/core/bottom-sheet/bottom-sheet-impl.css +57 -0
  140. package/dist/components/core/breadcrumb/Breadcrumb.css +19 -0
  141. package/dist/components/core/breadcrumb/Breadcrumb.d.ts +16 -0
  142. package/dist/components/core/breadcrumb/Breadcrumb.js +22 -0
  143. package/dist/components/core/breadcrumb/Breadcrumb.types.d.ts +18 -0
  144. package/dist/components/core/breadcrumb/Breadcrumb.types.js +9 -0
  145. package/dist/components/core/breadcrumb-item/BreadcrumbItem.css +34 -0
  146. package/dist/components/core/breadcrumb-item/BreadcrumbItem.d.ts +12 -0
  147. package/dist/components/core/breadcrumb-item/BreadcrumbItem.js +15 -0
  148. package/dist/components/core/breadcrumb-item/BreadcrumbItem.types.d.ts +22 -0
  149. package/dist/components/core/breadcrumb-item/BreadcrumbItem.types.js +9 -0
  150. package/dist/components/core/button/Button.css +141 -0
  151. package/dist/components/core/button/Button.d.ts +25 -0
  152. package/dist/components/core/button/Button.js +28 -0
  153. package/dist/components/core/button/Button.types.d.ts +40 -0
  154. package/dist/components/core/button/Button.types.js +9 -0
  155. package/dist/components/core/button/button-impl.css +46 -0
  156. package/dist/components/core/button-icon/ButtonIcon.css +135 -0
  157. package/dist/components/core/button-icon/ButtonIcon.d.ts +34 -0
  158. package/dist/components/core/button-icon/ButtonIcon.js +117 -0
  159. package/dist/components/core/button-icon/ButtonIcon.types.d.ts +41 -0
  160. package/dist/components/core/button-icon/ButtonIcon.types.js +8 -0
  161. package/dist/components/core/card/Card.css +65 -0
  162. package/dist/components/core/card/Card.d.ts +24 -0
  163. package/dist/components/core/card/Card.js +26 -0
  164. package/dist/components/core/card/Card.types.d.ts +33 -0
  165. package/dist/components/core/card/Card.types.js +7 -0
  166. package/dist/components/core/card-body/CardBody.css +139 -0
  167. package/dist/components/core/card-body/CardBody.d.ts +62 -0
  168. package/dist/components/core/card-body/CardBody.js +97 -0
  169. package/dist/components/core/card-body/CardBody.types.d.ts +61 -0
  170. package/dist/components/core/card-body/CardBody.types.js +15 -0
  171. package/dist/components/core/card-body/card-body-impl.css +87 -0
  172. package/dist/components/core/card-button/CardButton.css +73 -0
  173. package/dist/components/core/card-button/CardButton.d.ts +27 -0
  174. package/dist/components/core/card-button/CardButton.js +37 -0
  175. package/dist/components/core/card-button/CardButton.types.d.ts +24 -0
  176. package/dist/components/core/card-button/CardButton.types.js +7 -0
  177. package/dist/components/core/card-button/card-button-impl.css +39 -0
  178. package/dist/components/core/checkbox/Checkbox.css +149 -0
  179. package/dist/components/core/checkbox/Checkbox.d.ts +21 -0
  180. package/dist/components/core/checkbox/Checkbox.js +97 -0
  181. package/dist/components/core/checkbox/Checkbox.types.d.ts +46 -0
  182. package/dist/components/core/checkbox/Checkbox.types.js +15 -0
  183. package/dist/components/core/checkbox/checkbox-impl.css +90 -0
  184. package/dist/components/core/divider/Divider.css +41 -0
  185. package/dist/components/core/divider/Divider.d.ts +46 -0
  186. package/dist/components/core/divider/Divider.js +51 -0
  187. package/dist/components/core/divider/Divider.types.d.ts +25 -0
  188. package/dist/components/core/divider/Divider.types.js +9 -0
  189. package/dist/components/core/dropdown-menu/DropdownMenu.css +146 -0
  190. package/dist/components/core/dropdown-menu/DropdownMenu.d.ts +22 -0
  191. package/dist/components/core/dropdown-menu/DropdownMenu.js +309 -0
  192. package/dist/components/core/dropdown-menu/DropdownMenu.types.d.ts +62 -0
  193. package/dist/components/core/dropdown-menu/DropdownMenu.types.js +12 -0
  194. package/dist/components/core/dropdown-menu/dropdown-menu-impl.css +70 -0
  195. package/dist/components/core/filter-chip/FilterChip.css +149 -0
  196. package/dist/components/core/filter-chip/FilterChip.d.ts +23 -0
  197. package/dist/components/core/filter-chip/FilterChip.js +37 -0
  198. package/dist/components/core/filter-chip/FilterChip.types.d.ts +53 -0
  199. package/dist/components/core/filter-chip/FilterChip.types.js +12 -0
  200. package/dist/components/core/footer/Footer.css +104 -0
  201. package/dist/components/core/footer/Footer.d.ts +43 -0
  202. package/dist/components/core/footer/Footer.js +53 -0
  203. package/dist/components/core/footer/Footer.types.d.ts +47 -0
  204. package/dist/components/core/footer/Footer.types.js +18 -0
  205. package/dist/components/core/footer/footer-impl.css +218 -0
  206. package/dist/components/core/footer-group/FooterGroup.css +90 -0
  207. package/dist/components/core/footer-group/FooterGroup.d.ts +32 -0
  208. package/dist/components/core/footer-group/FooterGroup.js +64 -0
  209. package/dist/components/core/footer-group/FooterGroup.types.d.ts +25 -0
  210. package/dist/components/core/footer-group/FooterGroup.types.js +12 -0
  211. package/dist/components/core/footer-group/footer-group-impl.css +78 -0
  212. package/dist/components/core/icon/Icon.css +40 -0
  213. package/dist/components/core/icon/Icon.d.ts +23 -0
  214. package/dist/components/core/icon/Icon.js +79 -0
  215. package/dist/components/core/icon/Icon.types.d.ts +24 -0
  216. package/dist/components/core/icon/Icon.types.js +7 -0
  217. package/dist/components/core/link/Link.css +89 -0
  218. package/dist/components/core/link/Link.d.ts +37 -0
  219. package/dist/components/core/link/Link.js +141 -0
  220. package/dist/components/core/link/Link.types.d.ts +41 -0
  221. package/dist/components/core/link/Link.types.js +9 -0
  222. package/dist/components/core/link/link-impl.css +168 -0
  223. package/dist/components/core/menu-item/MenuItem.css +70 -0
  224. package/dist/components/core/menu-item/MenuItem.d.ts +11 -0
  225. package/dist/components/core/menu-item/MenuItem.js +26 -0
  226. package/dist/components/core/menu-item/MenuItem.types.d.ts +28 -0
  227. package/dist/components/core/menu-item/MenuItem.types.js +9 -0
  228. package/dist/components/core/navbar/Navbar.css +135 -0
  229. package/dist/components/core/navbar/Navbar.d.ts +36 -0
  230. package/dist/components/core/navbar/Navbar.js +371 -0
  231. package/dist/components/core/navbar/Navbar.types.d.ts +49 -0
  232. package/dist/components/core/navbar/Navbar.types.js +17 -0
  233. package/dist/components/core/navbar/navbar-impl.css +590 -0
  234. package/dist/components/core/navbar-item/NavbarItem.css +75 -0
  235. package/dist/components/core/navbar-item/NavbarItem.d.ts +22 -0
  236. package/dist/components/core/navbar-item/NavbarItem.js +40 -0
  237. package/dist/components/core/navbar-item/NavbarItem.types.d.ts +34 -0
  238. package/dist/components/core/navbar-item/NavbarItem.types.js +10 -0
  239. package/dist/components/core/navbar-menu/NavbarMenu.css +128 -0
  240. package/dist/components/core/navbar-menu/NavbarMenu.d.ts +5 -0
  241. package/dist/components/core/navbar-menu/NavbarMenu.js +186 -0
  242. package/dist/components/core/navbar-menu/NavbarMenu.types.d.ts +45 -0
  243. package/dist/components/core/navbar-menu/NavbarMenu.types.js +12 -0
  244. package/dist/components/core/navbar-menu/navbar-menu-impl.css +186 -0
  245. package/dist/components/core/navbar-menu-group/NavbarMenuGroup.css +26 -0
  246. package/dist/components/core/navbar-menu-group/NavbarMenuGroup.d.ts +17 -0
  247. package/dist/components/core/navbar-menu-group/NavbarMenuGroup.js +22 -0
  248. package/dist/components/core/navbar-menu-group/NavbarMenuGroup.types.d.ts +14 -0
  249. package/dist/components/core/navbar-menu-group/NavbarMenuGroup.types.js +7 -0
  250. package/dist/components/core/navbar-panel/NavbarPanel.css +87 -0
  251. package/dist/components/core/navbar-panel/NavbarPanel.d.ts +34 -0
  252. package/dist/components/core/navbar-panel/NavbarPanel.js +46 -0
  253. package/dist/components/core/navbar-panel/NavbarPanel.types.d.ts +29 -0
  254. package/dist/components/core/navbar-panel/NavbarPanel.types.js +12 -0
  255. package/dist/components/core/navbar-panel/navbar-panel-impl.css +104 -0
  256. package/dist/components/core/navbar-panel/use-navbar-panel.d.ts +26 -0
  257. package/dist/components/core/navbar-panel/use-navbar-panel.js +135 -0
  258. package/dist/components/core/navbar-switcher/NavbarSwitcher.css +117 -0
  259. package/dist/components/core/navbar-switcher/NavbarSwitcher.d.ts +20 -0
  260. package/dist/components/core/navbar-switcher/NavbarSwitcher.js +90 -0
  261. package/dist/components/core/navbar-switcher/NavbarSwitcher.types.d.ts +46 -0
  262. package/dist/components/core/navbar-switcher/NavbarSwitcher.types.js +13 -0
  263. package/dist/components/core/navbar-switcher/navbar-switcher-impl.css +179 -0
  264. package/dist/components/core/navbar-switcher-option/NavbarSwitcherOption.css +39 -0
  265. package/dist/components/core/navbar-switcher-option/NavbarSwitcherOption.d.ts +16 -0
  266. package/dist/components/core/navbar-switcher-option/NavbarSwitcherOption.js +19 -0
  267. package/dist/components/core/navbar-switcher-option/NavbarSwitcherOption.types.d.ts +18 -0
  268. package/dist/components/core/navbar-switcher-option/NavbarSwitcherOption.types.js +7 -0
  269. package/dist/components/core/option/Option.css +133 -0
  270. package/dist/components/core/option/Option.d.ts +25 -0
  271. package/dist/components/core/option/Option.js +31 -0
  272. package/dist/components/core/option/Option.types.d.ts +44 -0
  273. package/dist/components/core/option/Option.types.js +14 -0
  274. package/dist/components/core/pagination/Pagination.css +237 -0
  275. package/dist/components/core/pagination/Pagination.d.ts +30 -0
  276. package/dist/components/core/pagination/Pagination.js +406 -0
  277. package/dist/components/core/pagination/Pagination.types.d.ts +59 -0
  278. package/dist/components/core/pagination/Pagination.types.js +14 -0
  279. package/dist/components/core/pagination/pagination-impl.css +17 -0
  280. package/dist/components/core/removable-chip/RemovableChip.css +125 -0
  281. package/dist/components/core/removable-chip/RemovableChip.d.ts +25 -0
  282. package/dist/components/core/removable-chip/RemovableChip.js +36 -0
  283. package/dist/components/core/removable-chip/RemovableChip.types.d.ts +38 -0
  284. package/dist/components/core/removable-chip/RemovableChip.types.js +13 -0
  285. package/dist/components/core/search-input/SearchInput.css +205 -0
  286. package/dist/components/core/search-input/SearchInput.d.ts +5 -0
  287. package/dist/components/core/search-input/SearchInput.js +81 -0
  288. package/dist/components/core/search-input/SearchInput.types.d.ts +43 -0
  289. package/dist/components/core/search-input/SearchInput.types.js +13 -0
  290. package/dist/components/core/search-input/search-input-impl.css +20 -0
  291. package/dist/components/core/segmented-control/SegmentedControl.css +57 -0
  292. package/dist/components/core/segmented-control/SegmentedControl.d.ts +37 -0
  293. package/dist/components/core/segmented-control/SegmentedControl.js +269 -0
  294. package/dist/components/core/segmented-control/SegmentedControl.types.d.ts +38 -0
  295. package/dist/components/core/segmented-control/SegmentedControl.types.js +8 -0
  296. package/dist/components/core/segmented-control/segmented-control-impl.css +57 -0
  297. package/dist/components/core/segmented-control-item/SegmentedControlItem.css +151 -0
  298. package/dist/components/core/segmented-control-item/SegmentedControlItem.d.ts +50 -0
  299. package/dist/components/core/segmented-control-item/SegmentedControlItem.js +101 -0
  300. package/dist/components/core/segmented-control-item/SegmentedControlItem.types.d.ts +37 -0
  301. package/dist/components/core/segmented-control-item/SegmentedControlItem.types.js +10 -0
  302. package/dist/components/core/segmented-control-item/segmented-control-item-impl.css +69 -0
  303. package/dist/components/core/select/Select.css +400 -0
  304. package/dist/components/core/select/Select.d.ts +29 -0
  305. package/dist/components/core/select/Select.js +524 -0
  306. package/dist/components/core/select/Select.types.d.ts +96 -0
  307. package/dist/components/core/select/Select.types.js +26 -0
  308. package/dist/components/core/select/select-impl.css +114 -0
  309. package/dist/components/core/select-chip/SelectChip.css +155 -0
  310. package/dist/components/core/select-chip/SelectChip.d.ts +20 -0
  311. package/dist/components/core/select-chip/SelectChip.js +273 -0
  312. package/dist/components/core/select-chip/SelectChip.types.d.ts +56 -0
  313. package/dist/components/core/select-chip/SelectChip.types.js +14 -0
  314. package/dist/components/core/select-chip/select-chip-impl.css +38 -0
  315. package/dist/components/core/spinner/Spinner.css +76 -0
  316. package/dist/components/core/spinner/Spinner.d.ts +5 -0
  317. package/dist/components/core/spinner/Spinner.js +29 -0
  318. package/dist/components/core/spinner/Spinner.types.d.ts +37 -0
  319. package/dist/components/core/spinner/Spinner.types.js +10 -0
  320. package/dist/components/core/spinner/spinner-impl.css +27 -0
  321. package/dist/components/core/status-dot/StatusDot.css +43 -0
  322. package/dist/components/core/status-dot/StatusDot.d.ts +35 -0
  323. package/dist/components/core/status-dot/StatusDot.js +46 -0
  324. package/dist/components/core/status-dot/StatusDot.types.d.ts +29 -0
  325. package/dist/components/core/status-dot/StatusDot.types.js +7 -0
  326. package/dist/components/core/status-dot/status-dot-impl.css +42 -0
  327. package/dist/components/core/subdomain-navbar/SubdomainNavbar.css +89 -0
  328. package/dist/components/core/subdomain-navbar/SubdomainNavbar.d.ts +21 -0
  329. package/dist/components/core/subdomain-navbar/SubdomainNavbar.js +127 -0
  330. package/dist/components/core/subdomain-navbar/SubdomainNavbar.types.d.ts +43 -0
  331. package/dist/components/core/subdomain-navbar/SubdomainNavbar.types.js +13 -0
  332. package/dist/components/core/subdomain-navbar/subdomain-navbar-impl.css +147 -0
  333. package/dist/components/core/tab/Tab.css +113 -0
  334. package/dist/components/core/tab/Tab.d.ts +36 -0
  335. package/dist/components/core/tab/Tab.js +53 -0
  336. package/dist/components/core/tab/Tab.types.d.ts +42 -0
  337. package/dist/components/core/tab/Tab.types.js +13 -0
  338. package/dist/components/core/tab/tab-impl.css +49 -0
  339. package/dist/components/core/tab-list/TabList.css +40 -0
  340. package/dist/components/core/tab-list/TabList.d.ts +26 -0
  341. package/dist/components/core/tab-list/TabList.js +124 -0
  342. package/dist/components/core/tab-list/TabList.types.d.ts +20 -0
  343. package/dist/components/core/tab-list/TabList.types.js +7 -0
  344. package/dist/components/core/tab-list/tab-list-impl.css +56 -0
  345. package/dist/components/core/table/Table.css +40 -0
  346. package/dist/components/core/table/Table.d.ts +19 -0
  347. package/dist/components/core/table/Table.js +55 -0
  348. package/dist/components/core/table/Table.types.d.ts +59 -0
  349. package/dist/components/core/table/Table.types.js +9 -0
  350. package/dist/components/core/table/table-impl.css +75 -0
  351. package/dist/components/core/table/use-table-sort.d.ts +43 -0
  352. package/dist/components/core/table/use-table-sort.js +51 -0
  353. package/dist/components/core/table-body/TableBody.css +6 -0
  354. package/dist/components/core/table-body/TableBody.d.ts +12 -0
  355. package/dist/components/core/table-body/TableBody.js +26 -0
  356. package/dist/components/core/table-body/TableBody.types.d.ts +36 -0
  357. package/dist/components/core/table-body/TableBody.types.js +7 -0
  358. package/dist/components/core/table-cell/TableCell.css +76 -0
  359. package/dist/components/core/table-cell/TableCell.d.ts +21 -0
  360. package/dist/components/core/table-cell/TableCell.js +31 -0
  361. package/dist/components/core/table-cell/TableCell.types.d.ts +43 -0
  362. package/dist/components/core/table-cell/TableCell.types.js +7 -0
  363. package/dist/components/core/table-head/TableHead.css +6 -0
  364. package/dist/components/core/table-head/TableHead.d.ts +22 -0
  365. package/dist/components/core/table-head/TableHead.js +52 -0
  366. package/dist/components/core/table-head/TableHead.types.d.ts +39 -0
  367. package/dist/components/core/table-head/TableHead.types.js +7 -0
  368. package/dist/components/core/table-head/table-head-impl.css +51 -0
  369. package/dist/components/core/table-header-cell/TableHeaderCell.css +126 -0
  370. package/dist/components/core/table-header-cell/TableHeaderCell.d.ts +21 -0
  371. package/dist/components/core/table-header-cell/TableHeaderCell.js +34 -0
  372. package/dist/components/core/table-header-cell/TableHeaderCell.types.d.ts +56 -0
  373. package/dist/components/core/table-header-cell/TableHeaderCell.types.js +9 -0
  374. package/dist/components/core/table-header-cell/table-header-cell-impl.css +74 -0
  375. package/dist/components/core/table-row/TableRow.css +18 -0
  376. package/dist/components/core/table-row/TableRow.d.ts +17 -0
  377. package/dist/components/core/table-row/TableRow.js +32 -0
  378. package/dist/components/core/table-row/TableRow.types.d.ts +45 -0
  379. package/dist/components/core/table-row/TableRow.types.js +7 -0
  380. package/dist/components/core/table-row/table-row-impl.css +31 -0
  381. package/dist/components/core/text-area/TextArea.css +325 -0
  382. package/dist/components/core/text-area/TextArea.d.ts +13 -0
  383. package/dist/components/core/text-area/TextArea.js +133 -0
  384. package/dist/components/core/text-area/TextArea.types.d.ts +67 -0
  385. package/dist/components/core/text-area/TextArea.types.js +19 -0
  386. package/dist/components/core/text-area/text-area-impl.css +36 -0
  387. package/dist/components/core/text-input/TextInput.css +400 -0
  388. package/dist/components/core/text-input/TextInput.d.ts +5 -0
  389. package/dist/components/core/text-input/TextInput.js +130 -0
  390. package/dist/components/core/text-input/TextInput.types.d.ts +96 -0
  391. package/dist/components/core/text-input/TextInput.types.js +24 -0
  392. package/dist/components/core/text-input/text-input-impl.css +36 -0
  393. package/dist/components/core/tooltip/Tooltip.css +101 -0
  394. package/dist/components/core/tooltip/Tooltip.d.ts +51 -0
  395. package/dist/components/core/tooltip/Tooltip.js +87 -0
  396. package/dist/components/core/tooltip/Tooltip.types.d.ts +38 -0
  397. package/dist/components/core/tooltip/Tooltip.types.js +9 -0
  398. package/dist/components/core/tooltip/TooltipAnchor.d.ts +52 -0
  399. package/dist/components/core/tooltip/TooltipAnchor.js +69 -0
  400. package/dist/components/core/tooltip/tooltip-anchor.css +145 -0
  401. package/dist/components/core/tooltip/tooltip-impl.css +54 -0
  402. package/dist/components/core/tooltip/use-tooltip.d.ts +111 -0
  403. package/dist/components/core/tooltip/use-tooltip.js +189 -0
  404. package/dist/components/core/typography/Typography.css +96 -0
  405. package/dist/components/core/typography/Typography.d.ts +297 -0
  406. package/dist/components/core/typography/Typography.js +28 -0
  407. package/dist/components/core/typography/Typography.types.d.ts +40 -0
  408. package/dist/components/core/typography/Typography.types.js +7 -0
  409. package/dist/components/css-time.d.ts +20 -0
  410. package/dist/components/css-time.js +30 -0
  411. package/dist/components/given.d.ts +7 -0
  412. package/dist/components/given.js +9 -0
  413. package/dist/components/hover-open.d.ts +34 -0
  414. package/dist/components/hover-open.js +90 -0
  415. package/dist/components/index.d.ts +114 -1
  416. package/dist/components/index.js +115 -2
  417. package/dist/components/layout/aspect-ratio/AspectRatio.css +29 -0
  418. package/dist/components/layout/aspect-ratio/AspectRatio.d.ts +24 -0
  419. package/dist/components/layout/aspect-ratio/AspectRatio.js +26 -0
  420. package/dist/components/layout/aspect-ratio/AspectRatio.types.d.ts +22 -0
  421. package/dist/components/layout/aspect-ratio/AspectRatio.types.js +7 -0
  422. package/dist/components/layout/aspect-ratio/aspect-ratio-impl.css +44 -0
  423. package/dist/components/layout/grid/Grid.css +76 -0
  424. package/dist/components/layout/grid/Grid.d.ts +31 -0
  425. package/dist/components/layout/grid/Grid.js +33 -0
  426. package/dist/components/layout/grid/Grid.types.d.ts +48 -0
  427. package/dist/components/layout/grid/Grid.types.js +7 -0
  428. package/dist/components/layout/grid/grid-impl.css +62 -0
  429. package/dist/components/layout/row/Row.css +85 -0
  430. package/dist/components/layout/row/Row.d.ts +32 -0
  431. package/dist/components/layout/row/Row.js +34 -0
  432. package/dist/components/layout/row/Row.types.d.ts +45 -0
  433. package/dist/components/layout/row/Row.types.js +7 -0
  434. package/dist/components/layout/scroller/Scroller.css +59 -0
  435. package/dist/components/layout/scroller/Scroller.d.ts +34 -0
  436. package/dist/components/layout/scroller/Scroller.js +36 -0
  437. package/dist/components/layout/scroller/Scroller.types.d.ts +38 -0
  438. package/dist/components/layout/scroller/Scroller.types.js +7 -0
  439. package/dist/components/layout/scroller/scroller-impl.css +45 -0
  440. package/dist/components/layout/stack/Stack.css +73 -0
  441. package/dist/components/layout/stack/Stack.d.ts +28 -0
  442. package/dist/components/layout/stack/Stack.js +30 -0
  443. package/dist/components/layout/stack/Stack.types.d.ts +41 -0
  444. package/dist/components/layout/stack/Stack.types.js +7 -0
  445. package/dist/components/visually-hidden.d.ts +22 -0
  446. package/dist/components/visually-hidden.js +31 -0
  447. package/dist/fonts/LICENSE-inter.txt +93 -0
  448. package/dist/fonts/LICENSE-red-hat-mono.txt +93 -0
  449. package/dist/fonts/fonts.css +111 -0
  450. package/dist/fonts/inter-cyrillic-ext-wght-normal.woff2 +0 -0
  451. package/dist/fonts/inter-cyrillic-wght-normal.woff2 +0 -0
  452. package/dist/fonts/inter-greek-ext-wght-normal.woff2 +0 -0
  453. package/dist/fonts/inter-greek-wght-normal.woff2 +0 -0
  454. package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
  455. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  456. package/dist/fonts/inter-vietnamese-wght-normal.woff2 +0 -0
  457. package/dist/fonts/red-hat-mono-latin-ext-wght-normal.woff2 +0 -0
  458. package/dist/fonts/red-hat-mono-latin-wght-normal.woff2 +0 -0
  459. package/dist/marketing.css +289 -241
  460. package/package.json +18 -4
@@ -0,0 +1,334 @@
1
+ <!-- GENERATED from select-chip.contract.json. Do not edit. -->
2
+
3
+ # SelectChip
4
+
5
+ A chip that opens a list of answers and keeps the one chosen, so a filter row shows the current narrowing rather than the name of a filter. On a marketing site that is the control above an index whose set is too long to spell out as a row of toggles — a language, a region, a sort order, a market.
6
+
7
+ ## Use when
8
+
9
+ - A row narrows an index by one value out of a set, and has to keep showing which value that is
10
+ - The set is short enough to scan rather than search — a region, a language, a sort order
11
+ - The row has no space for a form field: nothing above the control, nothing beneath it, only the answer
12
+
13
+ ## Do not use
14
+
15
+ - Pressing an item does something rather than answering something → `DropdownMenu`
16
+ - The chip answers on its own, or the set is short enough for every answer to sit in the row as a chip of its own → `FilterChip`
17
+ - Each answer is a page with its own address → `Link`
18
+ - Running commands. Nothing here is performed — the list holds answers, and a reader who reopens it finds the one they gave still marked
19
+ - A form field. There is no label row, no helper and no status, so a control that must be explained or validated has nowhere to say it
20
+ - More than one answer at once. One chip carries one value; a set of narrowings that stack is a row of FilterChips
21
+
22
+ ## Anatomy
23
+
24
+ SelectChip renders 6 elements. A token binding names the part it applies to.
25
+
26
+ - `root` — `<div>`, class `dds-selectchip`. Holds the chip and the list, and nothing else. `position: relative`, which is what the list is absolute against — so it is pinned to the chip that opened it and travels with it rather than with the page. Inline, because a chip sits in a row of other chips: a block root would take the width of whatever contains it and put the chip alone on a line.
27
+ - `trigger` — `<button>`, class `dds-selectchip__trigger`. The chip itself, and the only thing pressed. A button inside the root rather than the root being one, because the list is a sibling that has to be positioned against something — and a button containing its own popup is a control inside a control. It says a list opens and whether it is open, which a chevron says to the eye and to nobody else. Whether anything has been chosen is this control's own to know rather than a prop it is handed, so it travels as a state on this element instead of an axis on the root: the answer comes from the value, and a prop beside it could disagree with it. At rest it wears the same white ground and hairline edge a FilterChip does, and once an answer is in it, the same dark ground a chosen FilterChip takes — the two say the same thing and should not say it two ways.
28
+ - `trigger-icon` — `<span>`, class `dds-selectchip__trigger-icon`. A glyph before the words, naming the kind of narrowing. Its own part because a Quill glyph ships width and height attributes on the svg and only a rule on that element overrides them. Rendered only when `leadingIcon` is set.
29
+ - `trigger-label` — `<span>`, class `dds-selectchip__trigger-label`. What is being narrowed by, and what is chosen where anything is. The value belongs here rather than in a second slot: a token reading "Sort: Newest" is one thing to read, and a name beside a separate value is two. It truncates rather than wrapping, because a chip is one line in a row of them. Its figures are tabular: a chip carrying a number — a year, a count — is one whose words change while the row around it stays put, and proportional digits are not the same width, so a 1 becoming a 2 would resize the chip and shift every chip after it.
30
+ - `chevron` — `<svg>`, class `dds-selectchip__chevron`. The chevron that says a list is behind this. Always drawn, and that is the whole promise the component makes: a chip without one is a toggle, and one that showed a chevron and opened nothing would teach the reader not to trust the row. It turns while the list is open, which is the same fact aria-expanded carries for a reader who cannot see it. Drawn through Icon rather than sized here — a Quill glyph ships width and height attributes, and only a rule on that element beats them. The step it takes is the chip's own, the same one its leading glyph takes, so the two ends of a chip are one size.
31
+ - `listbox` — `<ul>`, class `dds-selectchip__listbox`. The answers on offer. One element carrying the fill, the corner, the shadow, the inset and the ceiling before it scrolls. It takes the same surface a DropdownMenu takes in this package — same corner, same inset, same shadow — because two floating lists that differ by a few pixels read as a mistake rather than as a distinction. Announced as a listbox, which is what tells a reader these are things to choose between rather than things to do, and what makes the arrow keys mean what they mean. Absent when shut rather than hidden, so nothing beneath it is covered by something nobody can see and no answer is reachable by a reader running past a shut chip. As wide as its longest answer with the chip's width as a floor: this list is a different object from the chip that opened it, and an answer reading "Most recent first" cannot be cut to fit a chip reading "Sort". One edge is pinned and the other let go, and which one is the alignment axis. It always opens downwards — a chip near the foot of the screen should open upwards instead, and nothing here does that yet, the same gap DropdownMenu has. It is not a prop: which side a list opens on is a fact about the room left below the chip at that moment, which a page cannot assert and cannot keep true through a scroll. Rendered only when `open` is set.
32
+
33
+ Apply these classes from the generated `selectChipParts` constant rather than writing the strings by hand.
34
+
35
+ ## Composition
36
+
37
+ - Can contain: `Option`
38
+ - Never contains: `Button`, `Link`, `TextInput`, `MenuItem`, `FilterChip`, `SelectChip` — in children or in any prop that takes a node
39
+
40
+ ## Props
41
+
42
+ ### disabled — `boolean` (default `false`)
43
+
44
+ Use when: The narrowing cannot be made just now — the set is empty, or the index it governs has not loaded. It dims and leaves the tab order
45
+ Never for: The reader could act on it if they knew what to change. This says the choice is closed and nothing about why, so a reason belongs in the prose around the row
46
+
47
+ ### size — `sm` | `md` (default `sm`)
48
+
49
+ - `sm` — The default, and the same measure a FilterChip takes at its own default, so a row holding both sits level
50
+ - `md` — A touch-first layout, or a filter row in a hero band where the chips carry more weight
51
+
52
+ Use when: How dense the row is, matched to whatever it sits beside. The chip takes the size and the list it opens follows, so a reader never meets rows from one size under a chip from another
53
+ Never for: Marking one chip in a row as more important. They are a set of equals and a row of mismatched heights reads as a mistake before it reads as emphasis
54
+
55
+ ### alignment — `start` | `end` (default `start`)
56
+
57
+ - `start` — The list's leading edge lines up with the chip's. The default, because a reader reads from that edge and the answers begin where the chip's own words began
58
+ - `end` — Its trailing edge lines up instead, for a chip near the end of a row where a list opening the other way would run off the screen
59
+
60
+ Use when: Which edge the list is pinned to. The other edge is let go, so the list is as wide as its longest answer with the chip's width as a floor.
61
+ Never for: Nudging the list into place a few pixels. It names an edge to line up, not an offset
62
+
63
+ ### label — `string` **Required.**
64
+
65
+ Use when: Always, and required on that account — it names what is being chosen, and it is what the chip shows until something has been. Short enough to read at a glance in a row of other chips
66
+ Never for: Describing what pressing it does. The chip names the narrowing, and what happens is that a list opens
67
+
68
+ ### value — `string`
69
+
70
+ Use when: The page owns the answer — it arrives from a URL, is shared with something else on screen, or has to survive the chip unmounting. Passed, this is the answer, and onChange is the only way it moves
71
+ Never for: Seeding a first answer and then leaving it alone. That is defaultValue, and passing this instead gives a chip that cannot be changed
72
+
73
+ ### defaultValue — `string`
74
+
75
+ Use when: An answer the chip arrives carrying, which the reader is then free to change. The uncontrolled half of the pair, and what almost every use wants
76
+ Never for: Keeping the answer in step with something else on the page. Once set it is the chip's own; a page that has to steer it wants value
77
+
78
+ ### onChange — `callback` **Required.**
79
+
80
+ Use when: Always, and it is required for that reason. It hands back the value of the answer taken, each time one is taken. It is also the only way the answer reaches the page: this renders a div, a button and a list, so there is no form field carrying the choice and no name to submit it under — and a narrowing the page never learns about narrows nothing. The chip goes on showing it either way, which is what makes the omission quiet
81
+ Never for: Nothing — there is no case for leaving it out
82
+
83
+ ### open — `boolean`
84
+
85
+ Use when: The page owns whether the list is showing — closing it when something else opens, or holding it open across a re-render. Left out, the chip opens and shuts itself, which is what almost every use wants
86
+ Never for: Opening the list once on arrival. That is defaultOpen; passing this pins it open until the page says otherwise
87
+
88
+ ### defaultOpen — `boolean`
89
+
90
+ Use when: The list is showing when the chip arrives, and the chip owns it from there
91
+ Never for: A list that should stay open. Anything the reader can shut and the page must reopen wants open
92
+
93
+ ### onOpenChange — `callback`
94
+
95
+ Use when: Something outside the chip has to know the list opened or shut — closing another one beside it, or reporting the interaction
96
+ Never for: Reading the answer. That is onChange, and shutting is not answering
97
+
98
+ ### leadingIcon — `node`
99
+
100
+ Accepts: QuillSystemIcon
101
+ Use when: A glyph makes the kind of narrowing faster to recognise than its name alone. It sits before the words and never replaces them. Pass the glyph itself rather than one already wrapped in an Icon: the chip wraps it at icon.size.xs at sm and icon.size.sm at md, the same step its chevron takes
102
+ Never for: Decoration, or a second copy of what the word says. A row where every chip carries a mark that adds nothing reads more slowly than plain words would
103
+
104
+ ## Choosing a variant
105
+
106
+ Placement in, props out. When one of these describes what you are building, take
107
+ its props — do not re-derive them from the variant descriptions above.
108
+
109
+ - **A sort order above an article index** — `label="Sort"`
110
+ The set is four or five answers and the row has space for one control rather than five. The chip shows the answer in place of its own name once something is chosen, so the row reports the query rather than naming the filter
111
+ - **A region or language picker in a filter row** — `alignment="end"`
112
+ Near the end of a row, a list pinned to its leading edge runs off the screen. The trailing edge is pinned instead and the other let go
113
+
114
+ ## Accessibility
115
+
116
+ Guaranteed by the component. Write none of it by hand: the ids these attributes point at are generated per instance, so an attribute set from outside either says the same thing twice or points at nothing.
117
+
118
+ - Announced as: `listbox`
119
+ - `Tab` — move focus. One stop, on the chip — the list never takes focus, so a reader passes an open chip in a single press just as they pass a shut one
120
+ - `Enter` — open the list, or take the answer the arrow keys are resting on
121
+ - `Space` — the same as Enter. A chip that opens on one and not the other is broken
122
+ - `ArrowDown` — open the list, or move down it
123
+ - `ArrowUp` — open the list at its end, or move up it
124
+ - `Home` — the first answer that can be taken
125
+ - `End` — the last answer that can be taken
126
+ - `Escape` — shut the list, leaving the answer as it was
127
+ - an accessible name drawn from label, which names what is being chosen rather than what pressing does
128
+ - the chip says a list opens and whether it is open, which a chevron says to the eye and to nobody else. Both come from the chip itself, which is why this renders one rather than taking any trigger
129
+ - focus stays on the chip while the list is open, and the answer the arrow keys are resting on is named from there. A list of answers is not a set of tab stops — the chip holds the answer, so the chip holds the focus, which is what separates this from a DropdownMenu
130
+ - the answer already given is marked in the list rather than only shown on the chip, so a reader who reopens it hears which one is theirs without leaving the list
131
+ - Escape returns nothing but the list. The answer stays as it was, because shutting is not answering — a reader who opens a list and changes their mind has changed nothing
132
+
133
+ ## Rules
134
+
135
+ - **On a phone a section's actions stack and each fills the width, including when there is only one** — On a phone a page's actions stack, and each one fills the width. Not most of it, and not the width its label happens to need — the whole column.
136
+
137
+ Two side by side is what this replaces. On the narrowest screen each gets half a column minus the gap, which is rarely enough for a real label: the words wrap inside the pills, the two buttons come out different heights, and the pair reads as a broken row rather than as a choice. Stacked, each has the whole width and neither has to shorten its words to fit.
138
+
139
+ A single action fills too, and that is the part worth saying, because it is the one that looks unnecessary. A lone button has the room to sit at its natural width and still should not: a full-width control is the phone's convention for the thing the screen is asking you to do, and a pill hugging its label reads as one option among others that are not there. It also makes the target predictable — the whole width, wherever the thumb lands — which a centred pill does not.
140
+
141
+ Filling is the row's job rather than the button's. A button has no width of its own and is not given one; the row it sits in becomes a column and stretches its children, so nothing about the control changes and the same button is full width here and its own size on a desktop.
142
+
143
+ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that decides whether a heading is centred. Those are different questions and they break at different widths: a heading stops being centred when the measure is the screen, while a button stops being full width much sooner than that — stretched across a tablet it is a long way from its own label, and a label centred in a metre of pill is harder to hit than one in a pill the size of the words.
144
+
145
+ What this is not about is every row of controls. A set of filter chips, a pagination strip, a toolbar, a link sitting after a sentence — none of these is the page asking for something, and stacking them to full width would turn a strip the eye skims into a list it has to read. The test is whether the row is the call to action the section was written for.
146
+ Guidance only — cannot be checked automatically.
147
+ - **Never nest one interactive element inside another** — Nested interactive elements produce ambiguous hit targets and an unreachable inner control for keyboard and screen-reader users. This is why the rule cannot live on any single component — it constrains Button, Link, Input and Switch simultaneously.
148
+ Guidance only — cannot be checked automatically.
149
+ - **A section that paints a ground sets the ink to go with it** — A page sits on color.bg.page, and a section that separates itself from it takes one of three grounds. color.bg.subtle is the light one and needs nothing further — the page's own ink still reads on it. The other two are dark and do not: color.bg.brand-strong takes color.fg.on-brand-strong, and color.bg.brand takes color.fg.on-solid. Both are white, and both are written as the role rather than the white they resolve to. Set one of those backgrounds without its ink and the text inherits whatever the page was already using, which is near-black on a dark band — legible to nobody, and invisible to every check here, because each pair passes contrast on its own and nothing can tell whether a section used both.
150
+
151
+ color.bg.brand carries a second limit that is not about forgetting. Its ink measures 3.39:1, which clears the 3:1 a heading needs and misses the 4.5:1 running copy needs. A coral band may carry a headline and a call to action; a paragraph on one is text the reader has to work through at a ratio the waiver on that role does not cover — the waiver says so itself. A section with copy in it takes color.bg.brand-strong, which is near-black and carries white at full contrast.
152
+
153
+ color.bg.strong is not a section ground. It is the inverted fill for an action that has to outrank what surrounds it, and the components that paint it — Button and ButtonIcon in their inverse form — already set both halves in one rule, which is the pattern a hand-painted section has to copy.
154
+ Guidance only — cannot be checked automatically.
155
+ - **Every gap inside a section names the relationship it expresses, and the same relationship keeps its measure when it turns on its side** — A section's spacing is named by the relationship it expresses, not chosen by size. Every gap in a section has a role token, and reaching for a step off the generic scale because it happens to be the right number is how two sections written by two people stop lining up: the numbers agree today and drift the first time one of them is tuned.
156
+
157
+ The heading group is three steps, not one gap. An eyebrow sits nearer its title than the title sits to its description, and the actions sit further from the description than either — space.heading.eyebrow-title, space.heading.title-description, space.heading.description-actions, in that order. A single `gap` on the group cannot say this; it sets all three the same and the block reads as evenly spaced lines rather than as a mark, a name and a paragraph. The eyebrow has to identify itself for this to work, because a group that opens with a title must not give its description the eyebrow's tighter step.
158
+
159
+ The step between a title and its description follows the size of the title, which is why there are two of it. What a reader sees is not the gap but the gap plus the leading above it, and leading scales with the type: under a display title the leading is most of the space, under a card's title it is half as deep. One value for both makes a card look loose at the size that made a section look right. So space.heading.title-description is the display step and space.heading.title-description-compact is the step at a card's size, and the choice is made by how big the title is rather than by what kind of block it sits in.
160
+
161
+ Every one of those steps is smaller than the gap below the group, and that ordering is what holds the group together as one thing. space.heading.content is the step between a heading group and the content it introduces, and it is the widest measure inside a section: the one gap the eye reads as a change of subject rather than as a continuation.
162
+
163
+ That step is the same whether the content is under the heading or beside it. A split is the same relationship turned on its side — a heading group and the content it introduces — so the space between the two halves is space.heading.content, not a gutter. When the split collapses to one column on a phone the gap becomes vertical again, which is where it started, and nothing has to change for that to be right.
164
+
165
+ space.grid.gutter is between two things of the same kind. Two cards in a row are the same kind; the two halves of a split are not, which is why the gutter is the wrong measure there even when the number would have looked acceptable. The gutter runs on both axes, so a grid reads as an even field rather than as rows with a seam down them.
166
+
167
+ space.inset.card is the card's, not the page's. A row of cards padded differently is a row of different cards, and the fill is the only edge they have. A page composing a row does not get to choose it.
168
+
169
+ Column count is not a breakpoint decision. A row holds as many columns as fit before a column would be narrower than size.grid.column-min, and drops one when they would not. Written as a fixed count that collapses at a breakpoint, a four-up row keeps four columns at every width above the phone and sets its copy two or three words to a line through the whole of the tablet range — and on a narrow desktop, which no breakpoint sits on at all. The threshold is a property of the row rather than of a card: a card fills whatever cell it is given.
170
+
171
+ What this does not cover is the space above and below a section, which is space.section.default and space.section.compact, and the page's own side margin, which is space.inset.page. Those are the section's outside; everything here is its inside.
172
+ Guidance only — cannot be checked automatically.
173
+ - **A section's title is centred over stacked content, starts beside content in columns, and starts on a phone** — Two arrangements, and the title's alignment is decided by which one it is introducing rather than chosen per section.
174
+
175
+ Content stacked under the title — a row of cards, an accordion, a set of steps — takes a centred title. The heading group and the block below it share one axis, so a centred title is symmetric with the thing it names, and the eye drops from the middle of the words to the middle of the set. A title starting at the left over a centred or full-width block reads as belonging to the first column rather than to all of them.
176
+
177
+ Content beside the title — a split, a picture in the other half, anything laid out in columns — takes a title that starts where its content starts. The two share a left edge, and that edge is what says they are one thing read across rather than two stacked. Centring the title there breaks the only alignment the reader has to hold the halves together, and it drifts further from its content the wider the page gets.
178
+
179
+ The heading group is what carries this, not the heading alone. An eyebrow, a title and a description are read as one block and align together; aligning the title without them leaves a centred heading over start-aligned copy, which reads as a mistake rather than as a choice.
180
+
181
+ Centred means all of it, not just the words. A badge, a row of actions, a picture — these are flex children, and a flex child is placed by align-items rather than by text-align, so a block that only sets the text leaves everything that is not text at the start. A hero is the case that shows it: its title and copy centred while its badge and its buttons did not.
182
+
183
+ A capped block centres itself as well as its words. A heading group takes size.heading-group.max, and a cap that is not centred leaves the measure at the start of the column with the words centred inside it — composed, and a long way left of the middle it appears to be in.
184
+
185
+ Both arrangements go to the start on a phone. A centred block reverts below the breakpoint, and the reason is the measure: on a phone the measure is the screen, so a centred title wraps to three or four ragged lines and the eye loses the left edge it was tracking — the same symmetry that reads as composure on a desktop is what makes it hard to scan on a phone. Centring there has to be asked for twice, and a section title is not one of the things that asks: it is centred because there is room for it, and on a phone there is not. Going back to the start means all of it too, and a capped block is the one that hides a leftover: its words go to the start while its measure stays in the middle of the column, which reads as a title still centred over a badge and buttons that are not — the box in the middle and the line inside it at the edge.
186
+
187
+ A hero is a section like any other: its badge, title, copy and actions are one stacked block, so it is centred on a desktop and starts on a phone with everything else.
188
+
189
+ What this is not about is a page whose body is a form or a document. A title over a sign-up form or a guide is not introducing a set of content — it is the first line of the thing itself, and it keeps the alignment the body has, which for running text is the start. The distinction is whether the block below is content the title names or content the title is part of.
190
+ Guidance only — cannot be checked automatically.
191
+ - **Anything a pointer has to hit is at least 24px in both directions** — A control sized by its contents is as big as its contents, and a glyph is chosen for how it reads rather than for how easily it is hit. An icon-only button with no padding is a 16px target — smaller than the pad of a finger, and smaller than the tremor of a hand using a mouse.
192
+
193
+ WCAG 2.2 asks for 24 by 24 CSS pixels at AA. The exceptions are narrower than they look: inline targets inside a sentence are exempt, and so is a small target with 24px of clear space around it — but a clear button sits against the field it empties, and a field is itself a target, so the spacing exemption does not apply to the case that keeps arising.
194
+
195
+ The target and the mark are separate decisions, and conflating them is what produces both faults. Growing the glyph to 24px makes it shout; leaving the button at 16px makes it hard to hit. So the glyph keeps the size that reads correctly beside its text, and the box around it declares a minimum of its own — which is what size.target.min is for.
196
+
197
+ Declared rather than emergent. Padding plus contents does clear 24px in several components today, and it is the weaker statement: it is a size nobody wrote down, so it moves when the type scale or the glyph does, and nothing says it was meant to be a target at all.
198
+ Guidance only — cannot be checked automatically.
199
+
200
+ ## Anti-examples
201
+
202
+ ❌ SelectChip for: Pressing an item does something rather than answering something
203
+ A menu performs and a list of answers answers. This holds a value, shows it when shut and marks it when reopened; a menu keeps its own name whatever was pressed, and the difference reaches a screen reader as two different roles
204
+ → use `DropdownMenu` instead.
205
+
206
+ ❌ SelectChip for: The chip answers on its own, or the set is short enough for every answer to sit in the row as a chip of its own
207
+ A chevron promises a list, and a chip that only turns on and off and shows one anyway is promising something it does not have. The same holds from the other end: a chip each shows every narrowing at once and answers in one press, where this costs a press to open before anything can be read. Folding is worth it only when the row cannot hold the set
208
+ → use `FilterChip` instead.
209
+
210
+ ❌ SelectChip for: Each answer is a page with its own address
211
+ A narrowing that has a URL is a place, and a place should be an anchor: crawlable, openable in a new tab, returned to by the back button. A list of answers is none of those
212
+ → use `Link` instead.
213
+
214
+ ❌ disabled=true + open=true
215
+ A chip that cannot be used has no list to show. Opening one asks for a set of answers on a control that can take none of them, and the component declines it — this is the same refusal a compiler can make first.
216
+
217
+ ## Example
218
+
219
+ ```tsx
220
+ import { SelectChip, Option } from '@deriv-com/dds-marketing'
221
+
222
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" defaultValue="newest">
223
+ <Option value="newest">Newest first</Option>
224
+ <Option value="oldest">Oldest first</Option>
225
+ <Option value="popular">Most read</Option>
226
+ </SelectChip>
227
+ ```
228
+
229
+ ```tsx
230
+ import { useState } from 'react'
231
+ import { SelectChip, Option } from '@deriv-com/dds-marketing'
232
+
233
+ // Controlled: SelectChip reports the value, not the event.
234
+ const [value, setValue] = useState('')
235
+ <SelectChip label="Sort" value={value} onChange={setValue} onOpenChange={(open) => { /* your handler */ }}>
236
+ <Option value="newest">Newest first</Option>
237
+ <Option value="oldest">Oldest first</Option>
238
+ <Option value="popular">Most read</Option>
239
+ </SelectChip>
240
+ ```
241
+
242
+ ## In use
243
+
244
+ ### Before and after an answer
245
+
246
+ The chip names the narrowing until one is chosen, and carries the answer afterwards. A row of these reads as decisions made rather than filters available.
247
+
248
+ ```tsx
249
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
250
+
251
+ // Nothing chosen — the chip is the name of the filter
252
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort">
253
+ <Option value="newest">Newest first</Option>
254
+ <Option value="oldest">Oldest first</Option>
255
+ <Option value="popular">Most read</Option>
256
+ </SelectChip>
257
+
258
+ // Answered — the chip carries the answer
259
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" defaultValue="popular">
260
+ <Option value="newest">Newest first</Option>
261
+ <Option value="oldest">Oldest first</Option>
262
+ <Option value="popular">Most read</Option>
263
+ </SelectChip>
264
+ ```
265
+
266
+ ### Which edge the list is pinned to
267
+
268
+ The other edge is let go, so the list is as wide as its longest answer with the chip's width as a floor. End is for a chip near the end of a row.
269
+
270
+ ```tsx
271
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
272
+
273
+ // start — the default, answers begin at that edge
274
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Region" alignment="start" defaultOpen>
275
+ <Option value="eu">Europe</Option>
276
+ <Option value="apac">Asia Pacific</Option>
277
+ <Option value="latam">Latin America</Option>
278
+ </SelectChip>
279
+
280
+ // end — for a chip that would otherwise open off the screen
281
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Region" alignment="end" defaultOpen>
282
+ <Option value="eu">Europe</Option>
283
+ <Option value="apac">Asia Pacific</Option>
284
+ <Option value="latam">Latin America</Option>
285
+ </SelectChip>
286
+ ```
287
+
288
+ ### An answer that cannot be taken
289
+
290
+ It stays in the list and stays readable: a set that hides what it will not accept leaves the reader hunting for something that was there last week. The arrow keys step over it.
291
+
292
+ ```tsx
293
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
294
+
295
+ // One market not open to this account
296
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Market" defaultOpen>
297
+ <Option value="forex">Forex</Option>
298
+ <Option value="stocks">Stocks</Option>
299
+ <Option value="crypto" disabled>Crypto</Option>
300
+ </SelectChip>
301
+ ```
302
+
303
+ ### Sizes
304
+
305
+ The chip takes the size and the list it opens follows, so a reader never meets rows from one size under a chip from another.
306
+
307
+ ```tsx
308
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
309
+
310
+ // sm — the default, level with a FilterChip
311
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" size="sm">
312
+ <Option value="newest">Newest first</Option>
313
+ <Option value="oldest">Oldest first</Option>
314
+ </SelectChip>
315
+
316
+ // md — a touch-first layout
317
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" size="md">
318
+ <Option value="newest">Newest first</Option>
319
+ <Option value="oldest">Oldest first</Option>
320
+ </SelectChip>
321
+ ```
322
+
323
+ ### Unavailable
324
+
325
+ The narrowing cannot be made just now — the set is empty, or the index it governs has not loaded. It dims and leaves the tab order.
326
+
327
+ ```tsx
328
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
329
+
330
+ // Nothing to choose between yet
331
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Market" disabled>
332
+ <Option value="forex">Forex</Option>
333
+ </SelectChip>
334
+ ```
@@ -0,0 +1,222 @@
1
+ <!-- GENERATED from spinner.contract.json. Do not edit. -->
2
+
3
+ # Spinner
4
+
5
+ A turning ring that says a wait is real and the page has not stalled. A marketing page has very little to wait for — it is copy and pictures, and they arrive with the page — so a spinner here is the exception rather than the furniture: a form that has just been submitted, an embed or a demo still loading, a band of live figures being fetched.
6
+
7
+ That rarity is the brief. In an app a spinner is ordinary and a reader waits because they have an account and a reason to; on a landing page it is shown to someone with no stake in the outcome and nothing keeping them there. So it appears where a wait has already started — after the reader did something, or where a section has visibly held space for something that has not come — and nowhere else.
8
+
9
+ ## Use when
10
+
11
+ - The reader has just submitted something — a signup, a contact request — and the page owes them a sign that it is working
12
+ - A section is holding space for content that arrives separately: an embed, a demo, a band of live figures
13
+ - Something the reader asked for is on its way and nobody can say how long it will take
14
+
15
+ ## Do not use
16
+
17
+ - The wait is the page's own first load → nothing here does this
18
+ - How much is done is known, or could be → nothing here does this
19
+ - A wait short enough that the ring is a flash. A spinner that appears and goes inside a few hundred milliseconds reads as a glitch in the page rather than as work being done, and the reader is left with the flicker rather than the result
20
+ - More than one in a view. Three rings turning in three cards is a page that looks broken rather than busy — one spinner for the thing being waited on, at the level the reader is looking
21
+ - Decoration. A ring turning in a hero because the section looked static is motion with nothing behind it, and it teaches a reader that a spinner on this site does not mean a wait
22
+ - Standing in for content whose shape is already known. A ring in the middle of a section says nothing about what is coming and holds no room for it, so the layout jumps when it arrives — space the content will fill is better held by the space itself
23
+
24
+ ## Anatomy
25
+
26
+ Spinner renders 4 elements. A token binding names the part it applies to.
27
+
28
+ - `root` — `<div>`, class `dds-spinner`. The container, and the thing that carries the accessible name when there is one. `inline-flex` in a column with the items centred: the circle, and the label beneath it when there is a label to draw.
29
+
30
+ A column rather than a row, and there is no orientation to choose. The label is off by default and the cases that show one are standalone — a section or a form waiting — where the words sit under the ring and the pair reads as one centred block. A spinner beside a line of text is the other case, and it is better served by omitting the label entirely and letting the sentence already on the page say what is loading.
31
+ - `circle` — `<svg>`, class `dds-spinner__circle`. The turning ring. Its shapes carry `vector-effect: non-scaling-stroke`, which is what makes the stroke-width tokens mean rendered pixels rather than viewBox units — without it one token would draw a different thickness at every size, right at one of them and wrong at the rest.
32
+
33
+ It takes the colour and the size — a fill role rather than an ink one, because the ring is a drawn shape and not text, and the fg roles are darkened to carry contrast as words; the rotation is in `spinner-impl.css`, because a keyframed loop is not something a contract can hold. It reads `--motion-duration-spinner` and `--motion-easing-spinner` straight from the built stylesheet: the token build emits every semantic role whether a contract binds it or not, so binding them here bought two `transition-*` declarations on an element that never transitions. That file also stops it under `prefers-reduced-motion`, where the ring stands still and the arc alone still marks the component as present.
34
+ - `track` — `<circle>`, class `dds-spinner__track`. The faint full ring the arc travels over. Drawn in the same colour as the arc, so the only thing separating them is its alpha — which is why it is a part rather than an attribute: `stroke-opacity` inherits, so bound on the svg it would fade the arc along with it and leave a ring with nothing moving on it.
35
+ - `label` — `<span>`, class `dds-spinner__label`. What is loading, in words. Rendered whenever a label is given, and hidden visually rather than dropped when `showLabel` is false — `role="status"` is a live region and a live region with no name is one that fires with nothing to say.
36
+
37
+ Absent with no label, rather than present and empty: the span exists to carry the name, and with no name to carry it is a flex item holding a gap open under the ring.
38
+
39
+ Its family is bound rather than inherited. A component that does not bind one takes whatever the page sets, and a marketing page sets a great many things — the ratified stack governs nothing it does not already happen to get. Rendered only when `label` is set.
40
+
41
+ Apply these classes from the generated `spinnerParts` constant rather than writing the strings by hand.
42
+
43
+ ## Composition
44
+
45
+ - Takes no children. Everything it renders comes from props
46
+
47
+ ## Props
48
+
49
+ ### size — `sm` | `md` | `lg` (default `md`)
50
+
51
+ - `sm` — In a line of body copy, or inside a control the reader has just pressed. Sixteen, the size of the words beside it, so it sits on the line rather than in front of it
52
+ - `md` — The default. A form that has been submitted, a card waiting on the figure it is going to show
53
+ - `lg` — A section holding space for something that arrives separately — an embed, a demo, a band of live figures. Sized to be found from where the reader is looking, which is the whole section rather than one control in it
54
+
55
+ ### variant — `accent` | `neutral` | `inherit` (default `accent`)
56
+
57
+ - `accent` — The default, and what a page nearly always wants. The wait is the one thing happening, and the brand colour is what says the page is doing it rather than failing to
58
+ - `neutral` — The wait is incidental to what the reader came for — a figure refreshing inside a card that is otherwise readable. Brand colour on a wait nobody is watching spends attention the page needs further down
59
+ - `inherit` — Inside something that has already set a colour: a filled button, a dark band, a coloured panel. `currentColor` reaches the ring and the label together.
60
+
61
+ This is also how a spinner on a dark section or a photograph gets its colour. There is no fixed-light or fixed-dark role in this package, and adding one would be asking a second time a question the section has already answered by setting its text colour
62
+ Never for: Marking one wait as more important than another. Two spinners in a view is the thing to fix; recolouring one of them is not
63
+
64
+ ### label — `string` **Required when showLabel=true.**
65
+
66
+ Use when: Naming what is loading, for a spinner that stands on its own — a section, a form, a button whose own words have been replaced by it. Without it the spinner is decorative and says so properly: no role, no name, hidden from a screen reader outright
67
+ Never for: A second voice for something the page has already said. A ring beside a line reading “Sending your details” is not a separate thing loading, and naming it makes a screen reader announce the wait twice. Omitting it is what that case is for — an empty string is not, because an unnamed live region is still a live region and it still fires
68
+
69
+ ### showLabel — `boolean` (default `false`)
70
+
71
+ Use when: The words should be drawn as well as announced — beneath the ring, for a section or a form where the reader can see the wait but not what it is for
72
+
73
+ Also accepts everything from `HTMLAttributes<HTMLDivElement>`.
74
+
75
+ ## Accessibility
76
+
77
+ Guaranteed by the component. Write none of it by hand: the ids these attributes point at are generated per instance, so an attribute set from outside either says the same thing twice or points at nothing.
78
+
79
+ - Announced as: `status`
80
+ - accessible name from label
81
+ - A spinner with no label carries no role either. Decorative is a real mode here rather than an oversight — aria-hidden, no live region, nothing announced — and it is the right one wherever the copy beside the ring already names the wait.
82
+
83
+ ## Rules
84
+
85
+ - **On a phone a section's actions stack and each fills the width, including when there is only one** — On a phone a page's actions stack, and each one fills the width. Not most of it, and not the width its label happens to need — the whole column.
86
+
87
+ Two side by side is what this replaces. On the narrowest screen each gets half a column minus the gap, which is rarely enough for a real label: the words wrap inside the pills, the two buttons come out different heights, and the pair reads as a broken row rather than as a choice. Stacked, each has the whole width and neither has to shorten its words to fit.
88
+
89
+ A single action fills too, and that is the part worth saying, because it is the one that looks unnecessary. A lone button has the room to sit at its natural width and still should not: a full-width control is the phone's convention for the thing the screen is asking you to do, and a pill hugging its label reads as one option among others that are not there. It also makes the target predictable — the whole width, wherever the thumb lands — which a centred pill does not.
90
+
91
+ Filling is the row's job rather than the button's. A button has no width of its own and is not given one; the row it sits in becomes a column and stretches its children, so nothing about the control changes and the same button is full width here and its own size on a desktop.
92
+
93
+ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that decides whether a heading is centred. Those are different questions and they break at different widths: a heading stops being centred when the measure is the screen, while a button stops being full width much sooner than that — stretched across a tablet it is a long way from its own label, and a label centred in a metre of pill is harder to hit than one in a pill the size of the words.
94
+
95
+ What this is not about is every row of controls. A set of filter chips, a pagination strip, a toolbar, a link sitting after a sentence — none of these is the page asking for something, and stacking them to full width would turn a strip the eye skims into a list it has to read. The test is whether the row is the call to action the section was written for.
96
+ Guidance only — cannot be checked automatically.
97
+ - **Never nest one interactive element inside another** — Nested interactive elements produce ambiguous hit targets and an unreachable inner control for keyboard and screen-reader users. This is why the rule cannot live on any single component — it constrains Button, Link, Input and Switch simultaneously.
98
+ Guidance only — cannot be checked automatically.
99
+ - **A section that paints a ground sets the ink to go with it** — A page sits on color.bg.page, and a section that separates itself from it takes one of three grounds. color.bg.subtle is the light one and needs nothing further — the page's own ink still reads on it. The other two are dark and do not: color.bg.brand-strong takes color.fg.on-brand-strong, and color.bg.brand takes color.fg.on-solid. Both are white, and both are written as the role rather than the white they resolve to. Set one of those backgrounds without its ink and the text inherits whatever the page was already using, which is near-black on a dark band — legible to nobody, and invisible to every check here, because each pair passes contrast on its own and nothing can tell whether a section used both.
100
+
101
+ color.bg.brand carries a second limit that is not about forgetting. Its ink measures 3.39:1, which clears the 3:1 a heading needs and misses the 4.5:1 running copy needs. A coral band may carry a headline and a call to action; a paragraph on one is text the reader has to work through at a ratio the waiver on that role does not cover — the waiver says so itself. A section with copy in it takes color.bg.brand-strong, which is near-black and carries white at full contrast.
102
+
103
+ color.bg.strong is not a section ground. It is the inverted fill for an action that has to outrank what surrounds it, and the components that paint it — Button and ButtonIcon in their inverse form — already set both halves in one rule, which is the pattern a hand-painted section has to copy.
104
+ Guidance only — cannot be checked automatically.
105
+ - **Every gap inside a section names the relationship it expresses, and the same relationship keeps its measure when it turns on its side** — A section's spacing is named by the relationship it expresses, not chosen by size. Every gap in a section has a role token, and reaching for a step off the generic scale because it happens to be the right number is how two sections written by two people stop lining up: the numbers agree today and drift the first time one of them is tuned.
106
+
107
+ The heading group is three steps, not one gap. An eyebrow sits nearer its title than the title sits to its description, and the actions sit further from the description than either — space.heading.eyebrow-title, space.heading.title-description, space.heading.description-actions, in that order. A single `gap` on the group cannot say this; it sets all three the same and the block reads as evenly spaced lines rather than as a mark, a name and a paragraph. The eyebrow has to identify itself for this to work, because a group that opens with a title must not give its description the eyebrow's tighter step.
108
+
109
+ The step between a title and its description follows the size of the title, which is why there are two of it. What a reader sees is not the gap but the gap plus the leading above it, and leading scales with the type: under a display title the leading is most of the space, under a card's title it is half as deep. One value for both makes a card look loose at the size that made a section look right. So space.heading.title-description is the display step and space.heading.title-description-compact is the step at a card's size, and the choice is made by how big the title is rather than by what kind of block it sits in.
110
+
111
+ Every one of those steps is smaller than the gap below the group, and that ordering is what holds the group together as one thing. space.heading.content is the step between a heading group and the content it introduces, and it is the widest measure inside a section: the one gap the eye reads as a change of subject rather than as a continuation.
112
+
113
+ That step is the same whether the content is under the heading or beside it. A split is the same relationship turned on its side — a heading group and the content it introduces — so the space between the two halves is space.heading.content, not a gutter. When the split collapses to one column on a phone the gap becomes vertical again, which is where it started, and nothing has to change for that to be right.
114
+
115
+ space.grid.gutter is between two things of the same kind. Two cards in a row are the same kind; the two halves of a split are not, which is why the gutter is the wrong measure there even when the number would have looked acceptable. The gutter runs on both axes, so a grid reads as an even field rather than as rows with a seam down them.
116
+
117
+ space.inset.card is the card's, not the page's. A row of cards padded differently is a row of different cards, and the fill is the only edge they have. A page composing a row does not get to choose it.
118
+
119
+ Column count is not a breakpoint decision. A row holds as many columns as fit before a column would be narrower than size.grid.column-min, and drops one when they would not. Written as a fixed count that collapses at a breakpoint, a four-up row keeps four columns at every width above the phone and sets its copy two or three words to a line through the whole of the tablet range — and on a narrow desktop, which no breakpoint sits on at all. The threshold is a property of the row rather than of a card: a card fills whatever cell it is given.
120
+
121
+ What this does not cover is the space above and below a section, which is space.section.default and space.section.compact, and the page's own side margin, which is space.inset.page. Those are the section's outside; everything here is its inside.
122
+ Guidance only — cannot be checked automatically.
123
+ - **A section's title is centred over stacked content, starts beside content in columns, and starts on a phone** — Two arrangements, and the title's alignment is decided by which one it is introducing rather than chosen per section.
124
+
125
+ Content stacked under the title — a row of cards, an accordion, a set of steps — takes a centred title. The heading group and the block below it share one axis, so a centred title is symmetric with the thing it names, and the eye drops from the middle of the words to the middle of the set. A title starting at the left over a centred or full-width block reads as belonging to the first column rather than to all of them.
126
+
127
+ Content beside the title — a split, a picture in the other half, anything laid out in columns — takes a title that starts where its content starts. The two share a left edge, and that edge is what says they are one thing read across rather than two stacked. Centring the title there breaks the only alignment the reader has to hold the halves together, and it drifts further from its content the wider the page gets.
128
+
129
+ The heading group is what carries this, not the heading alone. An eyebrow, a title and a description are read as one block and align together; aligning the title without them leaves a centred heading over start-aligned copy, which reads as a mistake rather than as a choice.
130
+
131
+ Centred means all of it, not just the words. A badge, a row of actions, a picture — these are flex children, and a flex child is placed by align-items rather than by text-align, so a block that only sets the text leaves everything that is not text at the start. A hero is the case that shows it: its title and copy centred while its badge and its buttons did not.
132
+
133
+ A capped block centres itself as well as its words. A heading group takes size.heading-group.max, and a cap that is not centred leaves the measure at the start of the column with the words centred inside it — composed, and a long way left of the middle it appears to be in.
134
+
135
+ Both arrangements go to the start on a phone. A centred block reverts below the breakpoint, and the reason is the measure: on a phone the measure is the screen, so a centred title wraps to three or four ragged lines and the eye loses the left edge it was tracking — the same symmetry that reads as composure on a desktop is what makes it hard to scan on a phone. Centring there has to be asked for twice, and a section title is not one of the things that asks: it is centred because there is room for it, and on a phone there is not. Going back to the start means all of it too, and a capped block is the one that hides a leftover: its words go to the start while its measure stays in the middle of the column, which reads as a title still centred over a badge and buttons that are not — the box in the middle and the line inside it at the edge.
136
+
137
+ A hero is a section like any other: its badge, title, copy and actions are one stacked block, so it is centred on a desktop and starts on a phone with everything else.
138
+
139
+ What this is not about is a page whose body is a form or a document. A title over a sign-up form or a guide is not introducing a set of content — it is the first line of the thing itself, and it keeps the alignment the body has, which for running text is the start. The distinction is whether the block below is content the title names or content the title is part of.
140
+ Guidance only — cannot be checked automatically.
141
+ - **Anything a pointer has to hit is at least 24px in both directions** — A control sized by its contents is as big as its contents, and a glyph is chosen for how it reads rather than for how easily it is hit. An icon-only button with no padding is a 16px target — smaller than the pad of a finger, and smaller than the tremor of a hand using a mouse.
142
+
143
+ WCAG 2.2 asks for 24 by 24 CSS pixels at AA. The exceptions are narrower than they look: inline targets inside a sentence are exempt, and so is a small target with 24px of clear space around it — but a clear button sits against the field it empties, and a field is itself a target, so the spacing exemption does not apply to the case that keeps arising.
144
+
145
+ The target and the mark are separate decisions, and conflating them is what produces both faults. Growing the glyph to 24px makes it shout; leaving the button at 16px makes it hard to hit. So the glyph keeps the size that reads correctly beside its text, and the box around it declares a minimum of its own — which is what size.target.min is for.
146
+
147
+ Declared rather than emergent. Padding plus contents does clear 24px in several components today, and it is the weaker statement: it is a size nobody wrote down, so it moves when the type scale or the glyph does, and nothing says it was meant to be a target at all.
148
+ Guidance only — cannot be checked automatically.
149
+
150
+ ## Anti-examples
151
+
152
+ ❌ Spinner for: The wait is the page's own first load
153
+ Nothing here does this, and the useful thing is knowing why. A landing page that opens on a spinner has replaced its first impression with a placeholder — the reader has not asked for anything yet, so there is no wait they are party to, only a page that is not ready. That is a build and delivery problem, and a ring turning in the middle of it makes it look deliberate.
154
+ → nothing in this system does this. Not a gap waiting to be filled — the case belongs outside it.
155
+
156
+ ❌ Spinner for: How much is done is known, or could be
157
+ Nothing in this package shows progress yet, and a spinner is not a stand-in for it: a ring turning says only that something is happening, which is the right answer when it is the true one and a worse one than a bar whenever a share of the work can be named.
158
+ → nothing in this system does this. Not a gap waiting to be filled — the case belongs outside it.
159
+
160
+ ❌ showLabel=true + label missing or empty
161
+ A label shown on screen with no text in it is a gap held open under the ring, and a centred block that is no longer centred on anything
162
+
163
+ ## Example
164
+
165
+ ```tsx
166
+ import { Spinner } from '@deriv-com/dds-marketing'
167
+
168
+ <Spinner label="Sending your details" showLabel />
169
+ ```
170
+
171
+ ## In use
172
+
173
+ ### Three sizes, three places
174
+
175
+ The size follows what the reader is looking at when the wait starts — a line of copy, a form, or a whole section holding space.
176
+
177
+ ```tsx
178
+ import { Spinner } from '@deriv-com/dds-marketing'
179
+
180
+ // sm — in a line of body copy, or in a control just pressed
181
+ <Spinner size="sm" label="Checking that address" />
182
+
183
+ // md — the default: a form that has just been submitted
184
+ <Spinner size="md" label="Sending your details" />
185
+
186
+ // lg — a section holding space for an embed or live figures
187
+ <Spinner size="lg" label="Loading live spreads" />
188
+ ```
189
+
190
+ ### Three colours, and the one that borrows
191
+
192
+ Brand says the page is working on what the reader asked for. Neutral is a wait nobody is watching. Inherit takes currentColor, which is how a ring gets its colour on a filled button or a dark band.
193
+
194
+ ```tsx
195
+ import { Spinner } from '@deriv-com/dds-marketing'
196
+
197
+ // accent — the default, and the wait the page owes the reader
198
+ <Spinner variant="accent" label="Sending your details" />
199
+
200
+ // neutral — a figure refreshing in a readable card
201
+ <Spinner variant="neutral" label="Refreshing prices" />
202
+
203
+ // inherit — inside something that has already set a colour
204
+ <Spinner variant="inherit" label="Sending your details" />
205
+ ```
206
+
207
+ ### Named, drawn, or neither
208
+
209
+ A standalone spinner has to name itself, and showLabel decides only whether the words are drawn. Omitting the label is the third mode: no role, nothing announced.
210
+
211
+ ```tsx
212
+ import { Spinner } from '@deriv-com/dds-marketing'
213
+
214
+ // Named and announced, not drawn — the default
215
+ <Spinner label="Sending your details" />
216
+
217
+ // Drawn as well — the wait is visible, its reason is not
218
+ <Spinner label="Loading live spreads" showLabel size="lg" />
219
+
220
+ // Neither — decorative, beside copy that names the wait
221
+ <Spinner />
222
+ ```