@deriv-com/dds-marketing 0.2.0 → 1.0.1

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 (281) hide show
  1. package/.llm/components/accordion.md +9 -1
  2. package/.llm/components/accordionitem.md +9 -1
  3. package/.llm/components/aspectratio.md +9 -1
  4. package/.llm/components/badge.md +9 -1
  5. package/.llm/components/bottomsheet.md +9 -1
  6. package/.llm/components/breadcrumb.md +9 -1
  7. package/.llm/components/breadcrumbitem.md +9 -1
  8. package/.llm/components/button.md +55 -27
  9. package/.llm/components/buttonicon.md +16 -8
  10. package/.llm/components/card.md +16 -2
  11. package/.llm/components/cardbody.md +29 -3
  12. package/.llm/components/cardbutton.md +16 -2
  13. package/.llm/components/checkbox.md +9 -1
  14. package/.llm/components/ctabanner.md +186 -0
  15. package/.llm/components/ctabannerslider.md +204 -0
  16. package/.llm/components/divider.md +9 -1
  17. package/.llm/components/dropdownmenu.md +15 -7
  18. package/.llm/components/faqaccordion.md +217 -0
  19. package/.llm/components/featurebullets.md +254 -0
  20. package/.llm/components/featurecards.md +313 -0
  21. package/.llm/components/featurescroll.md +284 -0
  22. package/.llm/components/featuresplit.md +249 -0
  23. package/.llm/components/filterchip.md +9 -1
  24. package/.llm/components/footer.md +9 -1
  25. package/.llm/components/footergroup.md +9 -1
  26. package/.llm/components/grid.md +9 -1
  27. package/.llm/components/icon.md +21 -3
  28. package/.llm/components/landinghero.md +254 -0
  29. package/.llm/components/link.md +13 -5
  30. package/.llm/components/logocolumns.md +235 -0
  31. package/.llm/components/logorows.md +212 -0
  32. package/.llm/components/menuitem.md +9 -1
  33. package/.llm/components/navbar.md +80 -16
  34. package/.llm/components/navbaritem.md +10 -2
  35. package/.llm/components/navbarmenu.md +9 -1
  36. package/.llm/components/navbarmenugroup.md +9 -1
  37. package/.llm/components/navbarpanel.md +10 -2
  38. package/.llm/components/navbarswitcher.md +10 -2
  39. package/.llm/components/navbarswitcheroption.md +9 -1
  40. package/.llm/components/option.md +9 -1
  41. package/.llm/components/pageheader.md +231 -0
  42. package/.llm/components/pagination.md +27 -90
  43. package/.llm/components/paginationdots.md +215 -0
  44. package/.llm/components/producthero.md +248 -0
  45. package/.llm/components/removablechip.md +9 -1
  46. package/.llm/components/row.md +9 -1
  47. package/.llm/components/scroller.md +9 -1
  48. package/.llm/components/searchinput.md +9 -1
  49. package/.llm/components/segmentedcontrol.md +9 -1
  50. package/.llm/components/segmentedcontrolitem.md +9 -1
  51. package/.llm/components/select.md +9 -1
  52. package/.llm/components/selectchip.md +57 -16
  53. package/.llm/components/spinner.md +9 -1
  54. package/.llm/components/stack.md +9 -1
  55. package/.llm/components/statusdot.md +9 -1
  56. package/.llm/components/subdomainnavbar.md +9 -1
  57. package/.llm/components/tab.md +11 -3
  58. package/.llm/components/table.md +9 -1
  59. package/.llm/components/tablebody.md +9 -1
  60. package/.llm/components/tablecell.md +9 -1
  61. package/.llm/components/tablehead.md +9 -1
  62. package/.llm/components/tableheadercell.md +9 -1
  63. package/.llm/components/tablerow.md +9 -1
  64. package/.llm/components/tablist.md +9 -1
  65. package/.llm/components/testimonialrow.md +235 -0
  66. package/.llm/components/testimonialslider.md +254 -0
  67. package/.llm/components/textarea.md +9 -1
  68. package/.llm/components/textinput.md +9 -1
  69. package/.llm/components/tooltip.md +9 -1
  70. package/.llm/components/typography.md +9 -1
  71. package/.llm/digest/button.md +11 -6
  72. package/.llm/digest/buttonicon.md +2 -2
  73. package/.llm/digest/card.md +1 -0
  74. package/.llm/digest/cardbody.md +3 -1
  75. package/.llm/digest/cardbutton.md +1 -0
  76. package/.llm/digest/ctabanner.md +44 -0
  77. package/.llm/digest/ctabannerslider.md +45 -0
  78. package/.llm/digest/dropdownmenu.md +1 -1
  79. package/.llm/digest/faqaccordion.md +43 -0
  80. package/.llm/digest/featurebullets.md +52 -0
  81. package/.llm/digest/featurecards.md +49 -0
  82. package/.llm/digest/featurescroll.md +52 -0
  83. package/.llm/digest/featuresplit.md +54 -0
  84. package/.llm/digest/landinghero.md +53 -0
  85. package/.llm/digest/logocolumns.md +55 -0
  86. package/.llm/digest/logorows.md +48 -0
  87. package/.llm/digest/navbar.md +18 -3
  88. package/.llm/digest/pageheader.md +49 -0
  89. package/.llm/digest/pagination.md +6 -17
  90. package/.llm/digest/paginationdots.md +43 -0
  91. package/.llm/digest/producthero.md +53 -0
  92. package/.llm/digest/selectchip.md +6 -1
  93. package/.llm/digest/testimonialrow.md +51 -0
  94. package/.llm/digest/testimonialslider.md +51 -0
  95. package/.llm/index.md +24 -3
  96. package/.llm/rules.md +43 -9
  97. package/.llm/tokens.md +56 -12
  98. package/AGENTS.md +32 -8
  99. package/README.md +41 -21
  100. package/dist/components/banner-data.d.ts +38 -0
  101. package/dist/components/banner-data.js +15 -0
  102. package/dist/components/blocks/block-reveal.css +90 -0
  103. package/dist/components/blocks/cta-banner/CtaBanner.css +66 -0
  104. package/dist/components/blocks/cta-banner/CtaBanner.d.ts +22 -0
  105. package/dist/components/blocks/cta-banner/CtaBanner.js +31 -0
  106. package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +29 -0
  107. package/dist/components/blocks/cta-banner/CtaBanner.types.js +13 -0
  108. package/dist/components/blocks/cta-banner/cta-banner-impl.css +29 -0
  109. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.css +90 -0
  110. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +39 -0
  111. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +60 -0
  112. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +41 -0
  113. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.js +15 -0
  114. package/dist/components/blocks/cta-banner-slider/cta-banner-slider-impl.css +73 -0
  115. package/dist/components/blocks/faq-accordion/FaqAccordion.css +54 -0
  116. package/dist/components/blocks/faq-accordion/FaqAccordion.d.ts +20 -0
  117. package/dist/components/blocks/faq-accordion/FaqAccordion.js +28 -0
  118. package/dist/components/blocks/faq-accordion/FaqAccordion.types.d.ts +25 -0
  119. package/dist/components/blocks/faq-accordion/FaqAccordion.types.js +10 -0
  120. package/dist/components/blocks/faq-accordion/faq-accordion-impl.css +54 -0
  121. package/dist/components/blocks/feature-bullets/FeatureBullets.css +139 -0
  122. package/dist/components/blocks/feature-bullets/FeatureBullets.d.ts +31 -0
  123. package/dist/components/blocks/feature-bullets/FeatureBullets.js +55 -0
  124. package/dist/components/blocks/feature-bullets/FeatureBullets.types.d.ts +39 -0
  125. package/dist/components/blocks/feature-bullets/FeatureBullets.types.js +16 -0
  126. package/dist/components/blocks/feature-bullets/feature-bullets-impl.css +104 -0
  127. package/dist/components/blocks/feature-cards/FeatureCards.css +84 -0
  128. package/dist/components/blocks/feature-cards/FeatureCards.d.ts +24 -0
  129. package/dist/components/blocks/feature-cards/FeatureCards.js +66 -0
  130. package/dist/components/blocks/feature-cards/FeatureCards.types.d.ts +33 -0
  131. package/dist/components/blocks/feature-cards/FeatureCards.types.js +14 -0
  132. package/dist/components/blocks/feature-cards/feature-cards-impl.css +245 -0
  133. package/dist/components/blocks/feature-scroll/FeatureScroll.css +137 -0
  134. package/dist/components/blocks/feature-scroll/FeatureScroll.d.ts +39 -0
  135. package/dist/components/blocks/feature-scroll/FeatureScroll.js +167 -0
  136. package/dist/components/blocks/feature-scroll/FeatureScroll.types.d.ts +40 -0
  137. package/dist/components/blocks/feature-scroll/FeatureScroll.types.js +17 -0
  138. package/dist/components/blocks/feature-scroll/feature-scroll-impl.css +292 -0
  139. package/dist/components/blocks/feature-split/FeatureSplit.css +95 -0
  140. package/dist/components/blocks/feature-split/FeatureSplit.d.ts +30 -0
  141. package/dist/components/blocks/feature-split/FeatureSplit.js +38 -0
  142. package/dist/components/blocks/feature-split/FeatureSplit.types.d.ts +39 -0
  143. package/dist/components/blocks/feature-split/FeatureSplit.types.js +14 -0
  144. package/dist/components/blocks/feature-split/feature-split-impl.css +83 -0
  145. package/dist/components/blocks/landing-hero/LandingHero.css +85 -0
  146. package/dist/components/blocks/landing-hero/LandingHero.d.ts +41 -0
  147. package/dist/components/blocks/landing-hero/LandingHero.js +54 -0
  148. package/dist/components/blocks/landing-hero/LandingHero.types.d.ts +37 -0
  149. package/dist/components/blocks/landing-hero/LandingHero.types.js +15 -0
  150. package/dist/components/blocks/landing-hero/landing-hero-impl.css +355 -0
  151. package/dist/components/blocks/logo-columns/LogoColumns.css +161 -0
  152. package/dist/components/blocks/logo-columns/LogoColumns.d.ts +23 -0
  153. package/dist/components/blocks/logo-columns/LogoColumns.js +46 -0
  154. package/dist/components/blocks/logo-columns/LogoColumns.types.d.ts +46 -0
  155. package/dist/components/blocks/logo-columns/LogoColumns.types.js +18 -0
  156. package/dist/components/blocks/logo-columns/logo-columns-impl.css +188 -0
  157. package/dist/components/blocks/logo-rows/LogoRows.css +122 -0
  158. package/dist/components/blocks/logo-rows/LogoRows.d.ts +20 -0
  159. package/dist/components/blocks/logo-rows/LogoRows.js +43 -0
  160. package/dist/components/blocks/logo-rows/LogoRows.types.d.ts +36 -0
  161. package/dist/components/blocks/logo-rows/LogoRows.types.js +16 -0
  162. package/dist/components/blocks/logo-rows/logo-rows-impl.css +179 -0
  163. package/dist/components/blocks/page-header/PageHeader.css +79 -0
  164. package/dist/components/blocks/page-header/PageHeader.d.ts +34 -0
  165. package/dist/components/blocks/page-header/PageHeader.js +43 -0
  166. package/dist/components/blocks/page-header/PageHeader.types.d.ts +32 -0
  167. package/dist/components/blocks/page-header/PageHeader.types.js +14 -0
  168. package/dist/components/blocks/page-header/page-header-impl.css +176 -0
  169. package/dist/components/blocks/product-hero/ProductHero.css +86 -0
  170. package/dist/components/blocks/product-hero/ProductHero.d.ts +35 -0
  171. package/dist/components/blocks/product-hero/ProductHero.js +48 -0
  172. package/dist/components/blocks/product-hero/ProductHero.types.d.ts +37 -0
  173. package/dist/components/blocks/product-hero/ProductHero.types.js +15 -0
  174. package/dist/components/blocks/product-hero/product-hero-impl.css +354 -0
  175. package/dist/components/blocks/testimonial-row/TestimonialRow.css +245 -0
  176. package/dist/components/blocks/testimonial-row/TestimonialRow.d.ts +20 -0
  177. package/dist/components/blocks/testimonial-row/TestimonialRow.js +128 -0
  178. package/dist/components/blocks/testimonial-row/TestimonialRow.types.d.ts +54 -0
  179. package/dist/components/blocks/testimonial-row/TestimonialRow.types.js +26 -0
  180. package/dist/components/blocks/testimonial-row/testimonial-row-impl.css +195 -0
  181. package/dist/components/blocks/testimonial-slider/TestimonialSlider.css +181 -0
  182. package/dist/components/blocks/testimonial-slider/TestimonialSlider.d.ts +22 -0
  183. package/dist/components/blocks/testimonial-slider/TestimonialSlider.js +36 -0
  184. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.d.ts +49 -0
  185. package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.js +21 -0
  186. package/dist/components/blocks/testimonial-slider/testimonial-slider-impl.css +202 -0
  187. package/dist/components/blocks/use-reveal.d.ts +46 -0
  188. package/dist/components/blocks/use-reveal.js +120 -0
  189. package/dist/components/blocks/use-rotation.d.ts +62 -0
  190. package/dist/components/blocks/use-rotation.js +176 -0
  191. package/dist/components/core/accordion-item/AccordionItem.css +6 -5
  192. package/dist/components/core/bottom-sheet/BottomSheet.css +1 -1
  193. package/dist/components/core/button/Button.css +63 -16
  194. package/dist/components/core/button/Button.d.ts +14 -12
  195. package/dist/components/core/button/Button.js +43 -14
  196. package/dist/components/core/button/Button.types.d.ts +9 -14
  197. package/dist/components/core/button/button-impl.css +25 -0
  198. package/dist/components/core/button-icon/ButtonIcon.css +31 -15
  199. package/dist/components/core/button-icon/ButtonIcon.d.ts +2 -2
  200. package/dist/components/core/button-icon/ButtonIcon.js +2 -2
  201. package/dist/components/core/button-icon/ButtonIcon.types.d.ts +4 -4
  202. package/dist/components/core/card/Card.css +13 -4
  203. package/dist/components/core/card/Card.types.d.ts +2 -1
  204. package/dist/components/core/card-body/CardBody.css +11 -6
  205. package/dist/components/core/card-body/CardBody.d.ts +1 -1
  206. package/dist/components/core/card-body/CardBody.js +2 -2
  207. package/dist/components/core/card-body/CardBody.types.d.ts +4 -1
  208. package/dist/components/core/card-body/CardBody.types.js +1 -0
  209. package/dist/components/core/card-button/CardButton.css +13 -4
  210. package/dist/components/core/card-button/CardButton.types.d.ts +2 -1
  211. package/dist/components/core/checkbox/Checkbox.css +8 -1
  212. package/dist/components/core/dropdown-menu/DropdownMenu.css +13 -12
  213. package/dist/components/core/dropdown-menu/DropdownMenu.d.ts +5 -3
  214. package/dist/components/core/dropdown-menu/DropdownMenu.js +5 -3
  215. package/dist/components/core/filter-chip/FilterChip.css +2 -1
  216. package/dist/components/core/footer/footer-impl.css +1 -1
  217. package/dist/components/core/footer-group/FooterGroup.css +8 -7
  218. package/dist/components/core/icon/Icon.css +1 -1
  219. package/dist/components/core/link/Link.css +1 -1
  220. package/dist/components/core/link/Link.js +5 -5
  221. package/dist/components/core/menu-item/MenuItem.css +1 -1
  222. package/dist/components/core/navbar/Navbar.css +7 -8
  223. package/dist/components/core/navbar/Navbar.d.ts +17 -1
  224. package/dist/components/core/navbar/Navbar.js +139 -12
  225. package/dist/components/core/navbar/Navbar.types.d.ts +23 -4
  226. package/dist/components/core/navbar/Navbar.types.js +1 -1
  227. package/dist/components/core/navbar/navbar-impl.css +10 -3
  228. package/dist/components/core/navbar/navbar-material.css +96 -0
  229. package/dist/components/core/navbar-item/NavbarItem.css +4 -4
  230. package/dist/components/core/navbar-menu/NavbarMenu.css +7 -5
  231. package/dist/components/core/navbar-menu/NavbarMenu.d.ts +1 -0
  232. package/dist/components/core/navbar-menu/NavbarMenu.js +1 -0
  233. package/dist/components/core/navbar-menu/navbar-menu-impl.css +7 -7
  234. package/dist/components/core/navbar-switcher/NavbarSwitcher.css +6 -5
  235. package/dist/components/core/navbar-switcher/NavbarSwitcher.d.ts +1 -0
  236. package/dist/components/core/navbar-switcher/NavbarSwitcher.js +2 -1
  237. package/dist/components/core/option/Option.css +1 -1
  238. package/dist/components/core/pagination/Pagination.css +1 -75
  239. package/dist/components/core/pagination/Pagination.d.ts +4 -4
  240. package/dist/components/core/pagination/Pagination.js +11 -51
  241. package/dist/components/core/pagination/Pagination.types.d.ts +5 -17
  242. package/dist/components/core/pagination/Pagination.types.js +1 -3
  243. package/dist/components/core/pagination-dots/PaginationDots.css +60 -0
  244. package/dist/components/core/pagination-dots/PaginationDots.d.ts +25 -0
  245. package/dist/components/core/pagination-dots/PaginationDots.js +104 -0
  246. package/dist/components/core/pagination-dots/PaginationDots.types.d.ts +38 -0
  247. package/dist/components/core/pagination-dots/PaginationDots.types.js +9 -0
  248. package/dist/components/core/removable-chip/RemovableChip.css +3 -3
  249. package/dist/components/core/search-input/SearchInput.css +1 -1
  250. package/dist/components/core/segmented-control/SegmentedControl.css +1 -1
  251. package/dist/components/core/segmented-control-item/SegmentedControlItem.css +1 -1
  252. package/dist/components/core/select/Select.css +2 -1
  253. package/dist/components/core/select-chip/SelectChip.css +14 -7
  254. package/dist/components/core/select-chip/SelectChip.d.ts +1 -1
  255. package/dist/components/core/select-chip/SelectChip.js +7 -4
  256. package/dist/components/core/select-chip/SelectChip.types.d.ts +7 -1
  257. package/dist/components/core/select-chip/SelectChip.types.js +1 -1
  258. package/dist/components/core/select-chip/select-chip-impl.css +14 -3
  259. package/dist/components/core/status-dot/StatusDot.css +1 -1
  260. package/dist/components/core/subdomain-navbar/SubdomainNavbar.css +3 -3
  261. package/dist/components/core/tab/Tab.css +1 -1
  262. package/dist/components/core/tab/Tab.types.d.ts +1 -1
  263. package/dist/components/core/tab-list/TabList.css +1 -1
  264. package/dist/components/core/table/table-impl.css +36 -14
  265. package/dist/components/core/table-header-cell/TableHeaderCell.css +5 -5
  266. package/dist/components/core/text-area/TextArea.css +1 -1
  267. package/dist/components/core/text-input/TextInput.css +1 -1
  268. package/dist/components/core/tooltip/Tooltip.css +1 -1
  269. package/dist/components/core/typography/Typography.css +5 -5
  270. package/dist/components/index.d.ts +35 -0
  271. package/dist/components/index.js +30 -0
  272. package/dist/components/logo-data.d.ts +24 -0
  273. package/dist/components/logo-data.js +11 -0
  274. package/dist/components/scroll-data.d.ts +64 -0
  275. package/dist/components/scroll-data.js +17 -0
  276. package/dist/components/testimonial-row-data.d.ts +51 -0
  277. package/dist/components/testimonial-row-data.js +11 -0
  278. package/dist/components/testimonial-slider-data.d.ts +26 -0
  279. package/dist/components/testimonial-slider-data.js +10 -0
  280. package/dist/marketing.css +385 -14
  281. package/package.json +1 -6
@@ -147,7 +147,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
147
147
 
148
148
  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.
149
149
  Guidance only — cannot be checked automatically.
150
- - **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.
150
+ - **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, CardButton and TextInput simultaneously.
151
151
  Guidance only — cannot be checked automatically.
152
152
  - **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.
153
153
 
@@ -199,6 +199,14 @@ The target and the mark are separate decisions, and conflating them is what prod
199
199
 
200
200
  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.
201
201
  Guidance only — cannot be checked automatically.
202
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
203
+
204
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
205
+
206
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
207
+
208
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
209
+ Guidance only — cannot be checked automatically.
202
210
 
203
211
  ## Anti-examples
204
212
 
@@ -128,7 +128,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
128
128
 
129
129
  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.
130
130
  Guidance only — cannot be checked automatically.
131
- - **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.
131
+ - **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, CardButton and TextInput simultaneously.
132
132
  Guidance only — cannot be checked automatically.
133
133
  - **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.
134
134
 
@@ -180,6 +180,14 @@ The target and the mark are separate decisions, and conflating them is what prod
180
180
 
181
181
  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.
182
182
  Guidance only — cannot be checked automatically.
183
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
184
+
185
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
186
+
187
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
188
+
189
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
190
+ Guidance only — cannot be checked automatically.
183
191
 
184
192
  ## Anti-examples
185
193
 
@@ -268,7 +268,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
268
268
 
269
269
  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.
270
270
  Guidance only — cannot be checked automatically.
271
- - **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.
271
+ - **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, CardButton and TextInput simultaneously.
272
272
  Guidance only — cannot be checked automatically.
273
273
  - **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.
274
274
 
@@ -320,6 +320,14 @@ The target and the mark are separate decisions, and conflating them is what prod
320
320
 
321
321
  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.
322
322
  Guidance only — cannot be checked automatically.
323
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
324
+
325
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
326
+
327
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
328
+
329
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
330
+ Guidance only — cannot be checked automatically.
323
331
 
324
332
  ## Anti-examples
325
333
 
@@ -9,6 +9,7 @@ A chip that opens a list of answers and keeps the one chosen, so a filter row sh
9
9
  - A row narrows an index by one value out of a set, and has to keep showing which value that is
10
10
  - The set is short enough to scan rather than search — a region, a language, a sort order
11
11
  - The row has no space for a form field: nothing above the control, nothing beneath it, only the answer
12
+ - A view setting that always has an answer — a sort order, a language — where every answer is a way of looking rather than a way of leaving out
12
13
 
13
14
  ## Do not use
14
15
 
@@ -24,9 +25,9 @@ A chip that opens a list of answers and keeps the one chosen, so a filter row sh
24
25
  SelectChip renders 6 elements. A token binding names the part it applies to.
25
26
 
26
27
  - `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` — `<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 and the chip is highlight, the same dark ground a chosen FilterChip takes — the two say the same thing and should not say it two ways.
28
29
  - `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
+ - `trigger-label` — `<span>`, class `dds-selectchip__trigger-label`. What is being narrowed by, and what is chosen where anything is. A quiet chip shows both, as "Sort: Newest"; a highlight chip shows the answer alone, because its fill already says a narrowing is on. 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
31
  - `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
32
  - `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
 
@@ -60,9 +61,17 @@ Never for: Marking one chip in a row as more important. They are a set of equals
60
61
  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
62
  Never for: Nudging the list into place a few pixels. It names an edge to line up, not an offset
62
63
 
64
+ ### emphasis — `highlight` | `quiet` (default `highlight`)
65
+
66
+ - `highlight` — A filter. Choosing an answer hides part of the list, like a category, a topic or a product line. The chip fills to show a filter is on
67
+ - `quiet` — A setting that only changes how the page is shown, like a sort order, a language or a layout. Nothing is hidden, so the chip stays plain and reads "Label: Value". Always give it a starting value with value or defaultValue
68
+
69
+ Use when: Whether the chip fills once an answer is chosen. Ask one question: does choosing an answer hide anything? If it does, it is a filter and the chip is highlight. If it only changes how the page is shown, it is a setting and the chip is quiet.
70
+ Never for: Making one chip in a row look more important than the others. Decide by what the chip does to the page, not by how much it matters. Having a starting value does not make a chip quiet either: a status filter that starts on Open still hides the closed ones, so it stays highlight
71
+
63
72
  ### label — `string` **Required.**
64
73
 
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
74
+ 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, and what a quiet chip goes on showing ahead of the answer. Short enough to read at a glance in a row of other chips
66
75
  Never for: Describing what pressing it does. The chip names the narrowing, and what happens is that a list opens
67
76
 
68
77
  ### value — `string`
@@ -144,7 +153,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
144
153
 
145
154
  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
155
  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.
156
+ - **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, CardButton and TextInput simultaneously.
148
157
  Guidance only — cannot be checked automatically.
149
158
  - **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
159
 
@@ -196,6 +205,14 @@ The target and the mark are separate decisions, and conflating them is what prod
196
205
 
197
206
  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
207
  Guidance only — cannot be checked automatically.
208
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
209
+
210
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
211
+
212
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
213
+
214
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
215
+ Guidance only — cannot be checked automatically.
199
216
 
200
217
  ## Anti-examples
201
218
 
@@ -219,7 +236,7 @@ Declared rather than emergent. Padding plus contents does clear 24px in several
219
236
  ```tsx
220
237
  import { SelectChip, Option } from '@deriv-com/dds-marketing'
221
238
 
222
- <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" defaultValue="newest">
239
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" emphasis="quiet" defaultValue="newest">
223
240
  <Option value="newest">Newest first</Option>
224
241
  <Option value="oldest">Oldest first</Option>
225
242
  <Option value="popular">Most read</Option>
@@ -232,7 +249,7 @@ import { SelectChip, Option } from '@deriv-com/dds-marketing'
232
249
 
233
250
  // Controlled: SelectChip reports the value, not the event.
234
251
  const [value, setValue] = useState('')
235
- <SelectChip label="Sort" value={value} onChange={setValue} onOpenChange={(open) => { /* your handler */ }}>
252
+ <SelectChip label="Sort" emphasis="quiet" value={value} onChange={setValue} onOpenChange={(open) => { /* your handler */ }}>
236
253
  <Option value="newest">Newest first</Option>
237
254
  <Option value="oldest">Oldest first</Option>
238
255
  <Option value="popular">Most read</Option>
@@ -241,26 +258,50 @@ const [value, setValue] = useState('')
241
258
 
242
259
  ## In use
243
260
 
261
+ Most rows below hold the same set, written once here:
262
+
263
+ ```tsx
264
+ import { Option } from '@deriv-com/dds-marketing'
265
+
266
+ const options = (
267
+ <>
268
+ <Option value="newest">Newest first</Option>
269
+ <Option value="oldest">Oldest first</Option>
270
+ <Option value="popular">Most read</Option>
271
+ </>
272
+ )
273
+ ```
274
+
244
275
  ### Before and after an answer
245
276
 
246
277
  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
278
 
248
279
  ```tsx
249
- import { Option, SelectChip } from '@deriv-com/dds-marketing'
280
+ import { SelectChip } from '@deriv-com/dds-marketing'
250
281
 
251
282
  // 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>
283
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" emphasis="quiet">{options}</SelectChip>
257
284
 
258
285
  // 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>
286
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" emphasis="quiet" defaultValue="popular">{options}</SelectChip>
287
+ ```
288
+
289
+ ### A filter, or a setting
290
+
291
+ Highlight is for a filter. Choosing hides some items, so the chip fills with colour. Quiet is for a setting that only changes the view, like sorting. The chip stays plain and reads "Sort: Most read".
292
+
293
+ ```tsx
294
+ import { Option, SelectChip } from '@deriv-com/dds-marketing'
295
+
296
+ // Highlight — a filter: shows one topic, hides the rest
297
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Topic" emphasis="highlight" defaultValue="trading">
298
+ <Option value="trading">Trading</Option>
299
+ <Option value="markets">Markets</Option>
300
+ <Option value="company">Company news</Option>
263
301
  </SelectChip>
302
+
303
+ // Quiet — a setting: changes the order, nothing is hidden
304
+ <SelectChip onChange={(value) => { /* your handler */ }} label="Sort" emphasis="quiet" defaultValue="popular">{options}</SelectChip>
264
305
  ```
265
306
 
266
307
  ### Which edge the list is pinned to
@@ -94,7 +94,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
94
94
 
95
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
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.
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, CardButton and TextInput simultaneously.
98
98
  Guidance only — cannot be checked automatically.
99
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
100
 
@@ -146,6 +146,14 @@ The target and the mark are separate decisions, and conflating them is what prod
146
146
 
147
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
148
  Guidance only — cannot be checked automatically.
149
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
150
+
151
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
152
+
153
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
154
+
155
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
156
+ Guidance only — cannot be checked automatically.
149
157
 
150
158
  ## Anti-examples
151
159
 
@@ -117,7 +117,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
117
117
 
118
118
  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.
119
119
  Guidance only — cannot be checked automatically.
120
- - **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.
120
+ - **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, CardButton and TextInput simultaneously.
121
121
  Guidance only — cannot be checked automatically.
122
122
  - **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.
123
123
 
@@ -169,6 +169,14 @@ The target and the mark are separate decisions, and conflating them is what prod
169
169
 
170
170
  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.
171
171
  Guidance only — cannot be checked automatically.
172
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
173
+
174
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
175
+
176
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
177
+
178
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
179
+ Guidance only — cannot be checked automatically.
172
180
 
173
181
  ## Anti-examples
174
182
 
@@ -81,7 +81,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
81
81
 
82
82
  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.
83
83
  Guidance only — cannot be checked automatically.
84
- - **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.
84
+ - **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, CardButton and TextInput simultaneously.
85
85
  Guidance only — cannot be checked automatically.
86
86
  - **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.
87
87
 
@@ -133,6 +133,14 @@ The target and the mark are separate decisions, and conflating them is what prod
133
133
 
134
134
  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.
135
135
  Guidance only — cannot be checked automatically.
136
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
137
+
138
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
139
+
140
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
141
+
142
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
143
+ Guidance only — cannot be checked automatically.
136
144
 
137
145
  ## Anti-examples
138
146
 
@@ -169,7 +169,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
169
169
 
170
170
  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.
171
171
  Guidance only — cannot be checked automatically.
172
- - **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.
172
+ - **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, CardButton and TextInput simultaneously.
173
173
  Guidance only — cannot be checked automatically.
174
174
  - **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.
175
175
 
@@ -221,6 +221,14 @@ The target and the mark are separate decisions, and conflating them is what prod
221
221
 
222
222
  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.
223
223
  Guidance only — cannot be checked automatically.
224
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
225
+
226
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
227
+
228
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
229
+
230
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
231
+ Guidance only — cannot be checked automatically.
224
232
 
225
233
  ## Anti-examples
226
234
 
@@ -39,7 +39,7 @@ Tab renders 7 elements. A token binding names the part it applies to.
39
39
 
40
40
  It takes the same ink as the label and changes with it, so a current tab is current in both. The size is Icon's: the tab hands it `sm` and an Icon arriving already sized is unwrapped rather than obeyed.
41
41
 
42
- Solid when current and regular otherwise, which this component cannot do for you — a Quill glyph's weight is fixed at the import. That is the package's `icon-solid-marks-selection` rule, and it is a rule rather than an axis for exactly that reason. Rendered only when `icon` is set.
42
+ Solid when current and regular otherwise, which this component cannot do for you — a Quill glyph's weight is fixed at the import. That is the package's `icon-weight-within-a-set` rule — the tabs are the set — and it is a rule rather than an axis for exactly that reason. Rendered only when `icon` is set.
43
43
  - `label` — `<span>`, class `dds-tab__label`. The words, and where selection is actually carried. The ink steps from `fg.muted` to `fg.default` and the weight from `font.body.md` to `font.label.md` — 400 to 600.
44
44
 
45
45
  Product steps 400 to 500, because Product's type set has a medium. This one does not: Marketing has one weight per role and `label` is 600 throughout, so the step here is the larger of the two. It is the same decision — the current tab is heavier — landing on the only weight the package owns.
@@ -94,7 +94,7 @@ Never for: A sentence. The strip is scanned across, and a tab as wide as a claus
94
94
  Accepts: QuillSystemIcon
95
95
  Use when: The tabs are things a mark tells apart faster than a word does — a platform, a market class. Pass the glyph; the tab draws it at its own step.
96
96
 
97
- Solid when the tab is current and regular otherwise, which is the package's `icon-solid-marks-selection` rule. Nothing here can enforce it: a Quill glyph's weight is chosen at the import, so a page that wants the swap passes a different glyph per state
97
+ Solid when the tab is current and regular otherwise, which is the package's `icon-weight-within-a-set` rule. Nothing here can enforce it: a Quill glyph's weight is chosen at the import, so a page that wants the swap passes a different glyph per state
98
98
  Never for: Carrying the label. A tab whose meaning is a shape is a tab a screen reader reads as nothing, and the words are what the strip is scanned by
99
99
 
100
100
  ### badge — `node` **Required when content=badge.**
@@ -150,7 +150,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
150
150
 
151
151
  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.
152
152
  Guidance only — cannot be checked automatically.
153
- - **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.
153
+ - **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, CardButton and TextInput simultaneously.
154
154
  Guidance only — cannot be checked automatically.
155
155
  - **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.
156
156
 
@@ -202,6 +202,14 @@ The target and the mark are separate decisions, and conflating them is what prod
202
202
 
203
203
  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.
204
204
  Guidance only — cannot be checked automatically.
205
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
206
+
207
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
208
+
209
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
210
+
211
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
212
+ Guidance only — cannot be checked automatically.
205
213
 
206
214
  ## Anti-examples
207
215
 
@@ -162,7 +162,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
162
162
 
163
163
  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.
164
164
  Guidance only — cannot be checked automatically.
165
- - **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.
165
+ - **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, CardButton and TextInput simultaneously.
166
166
  Guidance only — cannot be checked automatically.
167
167
  - **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.
168
168
 
@@ -214,6 +214,14 @@ The target and the mark are separate decisions, and conflating them is what prod
214
214
 
215
215
  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.
216
216
  Guidance only — cannot be checked automatically.
217
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
218
+
219
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
220
+
221
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
222
+
223
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
224
+ Guidance only — cannot be checked automatically.
217
225
 
218
226
  ## Anti-examples
219
227
 
@@ -73,7 +73,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
73
73
 
74
74
  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.
75
75
  Guidance only — cannot be checked automatically.
76
- - **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.
76
+ - **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, CardButton and TextInput simultaneously.
77
77
  Guidance only — cannot be checked automatically.
78
78
  - **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.
79
79
 
@@ -125,6 +125,14 @@ The target and the mark are separate decisions, and conflating them is what prod
125
125
 
126
126
  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.
127
127
  Guidance only — cannot be checked automatically.
128
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
129
+
130
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
131
+
132
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
133
+
134
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
135
+ Guidance only — cannot be checked automatically.
128
136
 
129
137
  ## Anti-examples
130
138
 
@@ -99,7 +99,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
99
99
 
100
100
  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.
101
101
  Guidance only — cannot be checked automatically.
102
- - **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.
102
+ - **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, CardButton and TextInput simultaneously.
103
103
  Guidance only — cannot be checked automatically.
104
104
  - **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.
105
105
 
@@ -151,6 +151,14 @@ The target and the mark are separate decisions, and conflating them is what prod
151
151
 
152
152
  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.
153
153
  Guidance only — cannot be checked automatically.
154
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
155
+
156
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
157
+
158
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
159
+
160
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
161
+ Guidance only — cannot be checked automatically.
154
162
 
155
163
  ## Anti-examples
156
164
 
@@ -83,7 +83,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
83
83
 
84
84
  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.
85
85
  Guidance only — cannot be checked automatically.
86
- - **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.
86
+ - **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, CardButton and TextInput simultaneously.
87
87
  Guidance only — cannot be checked automatically.
88
88
  - **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.
89
89
 
@@ -135,6 +135,14 @@ The target and the mark are separate decisions, and conflating them is what prod
135
135
 
136
136
  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.
137
137
  Guidance only — cannot be checked automatically.
138
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
139
+
140
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
141
+
142
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
143
+
144
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
145
+ Guidance only — cannot be checked automatically.
138
146
 
139
147
  ## Anti-examples
140
148
 
@@ -143,7 +143,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
143
143
 
144
144
  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.
145
145
  Guidance only — cannot be checked automatically.
146
- - **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.
146
+ - **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, CardButton and TextInput simultaneously.
147
147
  Guidance only — cannot be checked automatically.
148
148
  - **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.
149
149
 
@@ -195,6 +195,14 @@ The target and the mark are separate decisions, and conflating them is what prod
195
195
 
196
196
  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.
197
197
  Guidance only — cannot be checked automatically.
198
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
199
+
200
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
201
+
202
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
203
+
204
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
205
+ Guidance only — cannot be checked automatically.
198
206
 
199
207
  ## Anti-examples
200
208
 
@@ -99,7 +99,7 @@ This is the phone's breakpoint, size.breakpoint.md, and not the wider one that d
99
99
 
100
100
  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.
101
101
  Guidance only — cannot be checked automatically.
102
- - **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.
102
+ - **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, CardButton and TextInput simultaneously.
103
103
  Guidance only — cannot be checked automatically.
104
104
  - **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.
105
105
 
@@ -151,6 +151,14 @@ The target and the mark are separate decisions, and conflating them is what prod
151
151
 
152
152
  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.
153
153
  Guidance only — cannot be checked automatically.
154
+ - **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
155
+
156
+ The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
157
+
158
+ The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
159
+
160
+ Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
161
+ Guidance only — cannot be checked automatically.
154
162
 
155
163
  ## Anti-examples
156
164