@repobit/dex-system-design 0.25.12 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/README.md +131 -8
  3. package/package.json +95 -13
  4. package/schemas/component-manifest.json +3294 -405
  5. package/schemas/manifest-omissions.json +173 -0
  6. package/src/blog/src/breakpoints.js +6 -6
  7. package/src/blog/src/content/blog-atoms-showcase.js +1 -1
  8. package/src/blog/src/content/blog-page-layout.js +2 -2
  9. package/src/blog/src/lib/article-toc.js +7 -7
  10. package/src/blog/src/shell/blog-shell.js +1 -1
  11. package/src/blog/src/tokens/blog.js +2 -2
  12. package/src/blog/src/tokens/core.js +34 -35
  13. package/src/blog/src/tokens/legacy.js +0 -9
  14. package/src/blog/tokens/blog.js +2 -2
  15. package/src/blog/tokens/core.js +34 -35
  16. package/src/blog/tokens/legacy.js +0 -9
  17. package/src/blog/tokens.css +36 -46
  18. package/src/components/accordion/accordion-bg.css.js +83 -59
  19. package/src/components/accordion/accordion-bg.js +29 -10
  20. package/src/components/accordion/accordion-bg.stories.js +67 -67
  21. package/src/components/accordion/accordion-keyboard.js +80 -0
  22. package/src/components/accordion/accordion.css.js +142 -80
  23. package/src/components/accordion/accordion.js +37 -10
  24. package/src/components/accordion/accordion.stories.js +7 -7
  25. package/src/components/agent-endpoints/agent-endpoints.css.js +36 -39
  26. package/src/components/agent-endpoints/agent-endpoints.js +30 -20
  27. package/src/components/agent-endpoints/agent-endpoints.stories.js +1 -1
  28. package/src/components/ai-guardian-demo.stories.js +91 -91
  29. package/src/components/alert/alert.css.js +198 -0
  30. package/src/components/alert/alert.js +103 -0
  31. package/src/components/alert/alert.stories.js +206 -0
  32. package/src/components/anchor/anchor-nav.css.js +213 -101
  33. package/src/components/anchor/anchor-nav.js +26 -40
  34. package/src/components/anchor/anchor.stories.js +22 -12
  35. package/src/components/annotation/annotation.css.js +58 -0
  36. package/src/components/annotation/annotation.js +67 -0
  37. package/src/components/annotation/annotation.stories.js +63 -0
  38. package/src/components/architecture.stories.js +285 -0
  39. package/src/components/atoms.stories.js +342 -0
  40. package/src/components/avatar/avatar.css.js +67 -0
  41. package/src/components/avatar/avatar.js +42 -0
  42. package/src/components/avatar/avatar.stories.js +95 -0
  43. package/src/components/awards/awards-flex.css.js +209 -23
  44. package/src/components/awards/awards-flex.js +66 -7
  45. package/src/components/awards/awards-flex.stories.js +1 -1
  46. package/src/components/awards/{awards-light.stories.js → awards-summary.stories.js} +54 -64
  47. package/src/components/awards/awards.css.js +116 -58
  48. package/src/components/awards/awards.js +102 -28
  49. package/src/components/awards/awards.stories.js +1 -1
  50. package/src/components/back/back.css.js +51 -22
  51. package/src/components/back/back.js +28 -7
  52. package/src/components/back/back.stories.js +9 -3
  53. package/src/components/badge/badge.css.js +158 -46
  54. package/src/components/badge/badge.js +62 -74
  55. package/src/components/badge/badge.stories.js +176 -248
  56. package/src/components/bento-grid/bento-grid.css.js +21 -16
  57. package/src/components/bento-grid/bento-grid.js +16 -4
  58. package/src/components/bento-grid/bento-grid.stories.js +19 -18
  59. package/src/components/bento-grid/bento-item.css.js +52 -28
  60. package/src/components/bento-grid/bento-item.js +2 -2
  61. package/src/components/blockquote/blockquote.css.js +47 -0
  62. package/src/components/blockquote/blockquote.js +29 -0
  63. package/src/components/blockquote/blockquote.stories.js +62 -0
  64. package/src/components/breadcrumb/breadcrumb.css.js +92 -22
  65. package/src/components/breadcrumb/breadcrumb.js +56 -11
  66. package/src/components/button/button.css.js +531 -161
  67. package/src/components/button/button.js +206 -214
  68. package/src/components/button/button.stories.js +322 -174
  69. package/src/components/caption/caption.css.js +21 -0
  70. package/src/components/caption/caption.js +31 -0
  71. package/src/components/caption/caption.stories.js +62 -0
  72. package/src/components/card-features/card-features.css.js +14 -22
  73. package/src/components/card-features/card-features.js +68 -9
  74. package/src/components/card-features/card-features.stories.js +11 -11
  75. package/src/components/cards/card.css.js +60 -81
  76. package/src/components/cards/card.js +56 -25
  77. package/src/components/cards/card.stories.js +39 -39
  78. package/src/components/carousel/carousel.css.js +93 -99
  79. package/src/components/carousel/carousel.js +32 -4
  80. package/src/components/checkbox/checkbox.css.js +111 -93
  81. package/src/components/checkbox/checkbox.js +64 -90
  82. package/src/components/checkbox/checkbox.stories.js +96 -29
  83. package/src/components/code/code.css.js +70 -0
  84. package/src/components/code/code.js +40 -0
  85. package/src/components/code/code.stories.js +73 -0
  86. package/src/components/compare/compare.css.js +179 -43
  87. package/src/components/compare/compare.js +52 -25
  88. package/src/components/compare/compare.stories.js +61 -4
  89. package/src/components/comparison-panel/comparison-panel.css.js +38 -51
  90. package/src/components/comparison-panel/comparison-panel.js +5 -4
  91. package/src/components/comparison-panel/comparison-panel.stories.js +1 -1
  92. package/src/components/compatibility-table/compatibility-table.css.js +180 -80
  93. package/src/components/compatibility-table/compatibility-table.js +37 -9
  94. package/src/components/control-alignment.stories.js +393 -0
  95. package/src/components/cta-banner/cta-banner.css.js +41 -13
  96. package/src/components/cta-banner/cta-banner.js +6 -5
  97. package/src/components/cta-banner/cta-banner.stories.js +43 -98
  98. package/src/components/display/display.css.js +25 -1
  99. package/src/components/display/display.js +46 -13
  100. package/src/components/display/display.stories.js +270 -40
  101. package/src/components/divider/divider-horizontal.js +21 -5
  102. package/src/components/divider/divider-vertical.js +15 -7
  103. package/src/components/divider/divider.css.js +2 -2
  104. package/src/components/divider/divider.stories.js +38 -34
  105. package/src/components/dropdown/dropdown.css.js +93 -126
  106. package/src/components/dropdown/dropdown.js +114 -34
  107. package/src/components/dropdown/dropdown.stories.js +56 -24
  108. package/src/components/features/features.css.js +24 -11
  109. package/src/components/features/features.js +7 -3
  110. package/src/components/features/features.stories.js +37 -37
  111. package/src/components/features-tab/features-tab.css.js +19 -8
  112. package/src/components/features-tab/features-tab.js +6 -5
  113. package/src/components/features-tab/features-tab.stories.js +23 -23
  114. package/src/components/fieldset/fieldset.css.js +30 -0
  115. package/src/components/fieldset/fieldset.js +28 -0
  116. package/src/components/fieldset/fieldset.stories.js +84 -0
  117. package/src/components/footer/footer-links-group.css.js +10 -14
  118. package/src/components/footer/footer-links-group.js +4 -3
  119. package/src/components/footer/footer-lp.css.js +59 -30
  120. package/src/components/footer/footer-lp.js +17 -12
  121. package/src/components/footer/footer-lp.stories.js +63 -75
  122. package/src/components/footer/footer-nav-menu.css.js +26 -4
  123. package/src/components/footer/footer-nav-menu.js +3 -2
  124. package/src/components/footer/footer.css.js +146 -264
  125. package/src/components/footer/footer.js +176 -106
  126. package/src/components/footer/footer.stories.js +9 -9
  127. package/src/components/footer/localeMap.js +1 -1
  128. package/src/components/grid/grid.css.js +1 -1
  129. package/src/components/grid/grid.js +49 -7
  130. package/src/components/grid/grid.stories.js +111 -0
  131. package/src/components/header/header-code.css.js +61 -51
  132. package/src/components/header/header-code.js +28 -14
  133. package/src/components/header/header-code.stories.js +5 -5
  134. package/src/components/header/header.css.js +655 -176
  135. package/src/components/header/header.js +269 -36
  136. package/src/components/header/header.stories.js +841 -18
  137. package/src/components/heading/heading.css.js +14 -17
  138. package/src/components/heading/heading.js +48 -33
  139. package/src/components/heading/heading.stories.js +27 -25
  140. package/src/components/help-text/help-text.css.js +21 -0
  141. package/src/components/help-text/help-text.js +25 -0
  142. package/src/components/help-text/help-text.stories.js +66 -0
  143. package/src/components/highlight/highlight.css.js +86 -64
  144. package/src/components/highlight/highlight.js +18 -27
  145. package/src/components/highlight/highlight.stories.js +14 -3
  146. package/src/components/icons/analysis-icon.js +6 -16
  147. package/src/components/icons/arrow-down-icon.js +6 -19
  148. package/src/components/icons/arrow-icon.js +44 -0
  149. package/src/components/icons/arrow-right-icon.js +30 -23
  150. package/src/components/icons/arrow-up-icon.js +19 -18
  151. package/src/components/icons/bd-ai-icon.js +1 -1
  152. package/src/components/icons/brand-marks.stories.js +279 -0
  153. package/src/components/icons/brand-signature-icon.js +42 -0
  154. package/src/components/icons/check-icon.js +27 -0
  155. package/src/components/icons/circle-info-icon.js +33 -0
  156. package/src/components/icons/close-icon.js +6 -18
  157. package/src/components/icons/family-icon.js +8 -20
  158. package/src/components/icons/globe-icon.js +6 -18
  159. package/src/components/icons/guide-icon.js +6 -18
  160. package/src/components/icons/icon-element.js +99 -0
  161. package/src/components/icons/icons.js +36 -0
  162. package/src/components/icons/icons.stories.js +184 -52
  163. package/src/components/icons/individual-icon.js +6 -18
  164. package/src/components/icons/laurel-icon.js +9 -19
  165. package/src/components/icons/machine-learning-icon.js +6 -17
  166. package/src/components/icons/marks.js +41 -0
  167. package/src/components/icons/minus-icon.js +7 -21
  168. package/src/components/icons/missing-icon.js +52 -0
  169. package/src/components/icons/plus-icon.js +7 -21
  170. package/src/components/icons/quote-icon.js +19 -19
  171. package/src/components/image/image.css.js +72 -25
  172. package/src/components/image/image.js +11 -6
  173. package/src/components/image/image.stories.js +2 -2
  174. package/src/components/input/custom-form.stories.js +214 -62
  175. package/src/components/input/input-clipboard.css.js +51 -144
  176. package/src/components/input/input-clipboard.js +31 -15
  177. package/src/components/input/input.css.js +8 -146
  178. package/src/components/input/input.js +93 -38
  179. package/src/components/install-options/install-options.css.js +84 -77
  180. package/src/components/install-options/install-options.js +33 -30
  181. package/src/components/install-options/install-options.stories.js +3 -3
  182. package/src/components/label/label.css.js +35 -0
  183. package/src/components/label/label.js +38 -0
  184. package/src/components/label/label.stories.js +82 -0
  185. package/src/components/light-carousel/light-carousel.css.js +111 -63
  186. package/src/components/light-carousel/light-carousel.js +85 -11
  187. package/src/components/light-carousel/light-carousel.stories.js +126 -82
  188. package/src/components/link/link.css.js +150 -99
  189. package/src/components/link/link.js +86 -11
  190. package/src/components/link/link.stories.js +124 -30
  191. package/src/components/list/list-item/list-item.css.js +74 -22
  192. package/src/components/list/list-item/list-item.js +32 -10
  193. package/src/components/list/list-item/list-item.stories.js +23 -23
  194. package/src/components/list/list.css.js +8 -2
  195. package/src/components/list/list.js +3 -2
  196. package/src/components/list/list.stories.js +222 -0
  197. package/src/components/modal/modal.css.js +114 -24
  198. package/src/components/modal/modal.js +83 -7
  199. package/src/components/modal/modal.stories.js +9 -9
  200. package/src/components/overview/overview.css.js +19 -14
  201. package/src/components/overview/overview.js +6 -5
  202. package/src/components/overview/overview.stories.js +10 -10
  203. package/src/components/page-switcher/page-switcher.css.js +97 -40
  204. package/src/components/page-switcher/page-switcher.js +28 -5
  205. package/src/components/page-switcher/page-switcher.stories.js +95 -0
  206. package/src/components/paragraph/paragraph.css.js +15 -10
  207. package/src/components/paragraph/paragraph.js +41 -44
  208. package/src/components/paragraph/paragraph.stories.js +56 -21
  209. package/src/components/picture/picture.css.js +103 -44
  210. package/src/components/picture/picture.js +100 -30
  211. package/src/components/picture/picture.stories.js +1 -1
  212. package/src/components/pricing-cards/new-pricing-card.js +3 -3
  213. package/src/components/pricing-cards/new-pricing.css.js +24 -15
  214. package/src/components/pricing-cards/pricing-card-actions.css.js +1 -1
  215. package/src/components/pricing-cards/pricing-card-actions.js +3 -4
  216. package/src/components/pricing-cards/pricing-card-container.css.js +3 -3
  217. package/src/components/pricing-cards/pricing-card-container.js +3 -2
  218. package/src/components/pricing-cards/pricing-card-header.css.js +10 -8
  219. package/src/components/pricing-cards/pricing-card-header.js +6 -7
  220. package/src/components/pricing-cards/pricing-card-pricing.css.js +16 -9
  221. package/src/components/pricing-cards/pricing-card-pricing.js +8 -6
  222. package/src/components/pricing-cards/pricing-card-show-more.css.js +11 -3
  223. package/src/components/pricing-cards/pricing-card-show-more.js +8 -3
  224. package/src/components/pricing-cards/pricing-card.css.js +244 -190
  225. package/src/components/pricing-cards/pricing-card.js +54 -70
  226. package/src/components/pricing-cards/pricing-feature-item.css.js +25 -8
  227. package/src/components/pricing-cards/pricing-feature-item.js +8 -3
  228. package/src/components/process-steps/process-steps.css.js +26 -14
  229. package/src/components/process-steps/process-steps.js +23 -6
  230. package/src/components/process-steps/process-steps.stories.js +1 -1
  231. package/src/components/progress/progress.css.js +80 -0
  232. package/src/components/progress/progress.js +42 -0
  233. package/src/components/progress/progress.stories.js +79 -0
  234. package/src/components/quick-answer/quick-answer.css.js +75 -13
  235. package/src/components/quick-answer/quick-answer.js +8 -3
  236. package/src/components/radio/radio.css.js +92 -128
  237. package/src/components/radio/radio.js +106 -127
  238. package/src/components/radio/radio.stories.js +54 -19
  239. package/src/components/review/review-grid.css.js +5 -6
  240. package/src/components/review/review-grid.js +14 -11
  241. package/src/components/review/review.css.js +11 -6
  242. package/src/components/review/review.js +3 -8
  243. package/src/components/shared/ensemble.css.js +93 -0
  244. package/src/components/shared/form-field.css.js +206 -0
  245. package/src/components/shared/prop-vocabulary.js +48 -0
  246. package/src/components/shared/token-scales.js +306 -0
  247. package/src/components/skeleton/skeleton.css.js +34 -0
  248. package/src/components/skeleton/skeleton.js +32 -0
  249. package/src/components/skeleton/skeleton.stories.js +70 -0
  250. package/src/components/spinner/spinner.css.js +62 -0
  251. package/src/components/spinner/spinner.js +38 -0
  252. package/src/components/spinner/spinner.stories.js +92 -0
  253. package/src/components/spotlight-section/spotlight-section.css.js +14 -13
  254. package/src/components/spotlight-section/spotlight-section.js +42 -39
  255. package/src/components/switch/switch.css.js +103 -0
  256. package/src/components/switch/switch.js +44 -0
  257. package/src/components/switch/switch.stories.js +86 -0
  258. package/src/components/tab-list/content-switcher.stories.js +189 -0
  259. package/src/components/tab-list/tab-list.css.js +250 -0
  260. package/src/components/tab-list/tab-list.js +184 -0
  261. package/src/components/tab-list/tab-list.stories.js +221 -0
  262. package/src/components/tabs/tabs.css.js +14 -123
  263. package/src/components/tabs/tabs.js +16 -19
  264. package/src/components/tabs/tabs.stories.js +95 -95
  265. package/src/components/tag/tag.css.js +54 -0
  266. package/src/components/tag/tag.js +32 -0
  267. package/src/components/tag/tag.stories.js +126 -0
  268. package/src/components/terminal/terminal-message.css.js +164 -60
  269. package/src/components/terminal/terminal-message.js +5 -5
  270. package/src/components/terminal/terminal-message.stories.js +70 -1
  271. package/src/components/termsOfUse/terms.css.js +25 -20
  272. package/src/components/termsOfUse/terms.js +13 -9
  273. package/src/components/termsOfUse/terms.stories.js +1 -1
  274. package/src/components/textarea/textarea.css.js +51 -0
  275. package/src/components/textarea/textarea.js +136 -0
  276. package/src/components/textarea/textarea.stories.js +191 -0
  277. package/src/components/tooltip/tooltip.css.js +75 -0
  278. package/src/components/tooltip/tooltip.js +156 -0
  279. package/src/components/tooltip/tooltip.stories.js +161 -0
  280. package/src/components/trust-panel/trust-panel.css.js +85 -33
  281. package/src/components/trust-panel/trust-panel.js +18 -10
  282. package/src/components/trust-panel/trust-panel.stories.js +1 -1
  283. package/src/components/ultimate-security-demo.stories.js +431 -0
  284. package/src/components/video/video.css.js +17 -0
  285. package/src/components/video/video.js +40 -0
  286. package/src/components/video/video.stories.js +106 -0
  287. package/src/components/video-player/video-player.css.js +67 -11
  288. package/src/components/video-player/video-player.js +35 -4
  289. package/src/components/video-player/video-player.stories.js +1 -1
  290. package/src/components/visual-alignment.stories.js +411 -0
  291. package/src/components/visually-hidden/visually-hidden.css.js +24 -0
  292. package/src/components/visually-hidden/visually-hidden.js +16 -0
  293. package/src/components/visually-hidden/visually-hidden.stories.js +56 -0
  294. package/src/stories/Button.stories.js +17 -17
  295. package/src/stories/Header.stories.js +10 -10
  296. package/src/stories/Page.js +1 -1
  297. package/src/stories/Page.stories.js +6 -6
  298. package/src/tokens/borders.stories.js +339 -0
  299. package/src/tokens/color-responsibilities.stories.js +227 -0
  300. package/src/tokens/dark-derivation.js +309 -0
  301. package/src/tokens/dark-findings.js +197 -0
  302. package/src/tokens/dark-mode.stories.js +207 -0
  303. package/src/tokens/dimension.stories.js +251 -0
  304. package/src/tokens/elevation.stories.js +143 -0
  305. package/src/tokens/focus.stories.js +276 -0
  306. package/src/tokens/fonts.css +20 -20
  307. package/src/tokens/fonts.stories.js +354 -374
  308. package/src/tokens/layout.css +25 -11
  309. package/src/tokens/motion.stories.js +231 -0
  310. package/src/tokens/shape.stories.js +272 -0
  311. package/src/tokens/spacing-rhythm.stories.js +359 -0
  312. package/src/tokens/spacing.stories.js +208 -703
  313. package/src/tokens/story-chrome.js +503 -0
  314. package/src/tokens/surfaces.stories.js +224 -0
  315. package/src/tokens/text-on-surface.stories.js +164 -0
  316. package/src/tokens/token-map.js +220 -0
  317. package/src/tokens/token-map.stories.js +257 -0
  318. package/src/tokens/token-purposes.js +266 -0
  319. package/src/tokens/tokens-grid.stories.js +388 -900
  320. package/src/tokens/tokens.css +1633 -1045
  321. package/src/tokens/tokens.js +1130 -492
  322. package/src/tokens/tokens.stories.js +137 -47
  323. package/src/tokens/type-ceiling-words.js +299 -0
  324. package/src/tokens/typography.stories.js +1003 -965
  325. package/src/tokens/vertical-rhythm.stories.js +224 -0
  326. package/src/components/awards/awards-icon.js +0 -56
  327. package/src/components/awards/awards-light.js +0 -240
  328. package/src/components/banner/banner.js +0 -60
  329. package/src/components/banner/banner.stories.js +0 -51
  330. package/src/components/button/icons.js +0 -24
  331. package/src/components/input/index.js +0 -0
  332. package/src/components/light-carousel/light-carousel-simple.css.js +0 -183
  333. package/src/components/light-carousel/light-carousel-simple.js +0 -74
  334. package/src/tokens/colors.js +0 -121
  335. package/src/tokens/typography.css.js +0 -207
  336. package/src/tokens/typography.js +0 -0
@@ -1,166 +1,220 @@
1
- import { LitElement, html } from "lit";
2
- import { tokens } from "../../tokens/tokens.js";
3
- import "../icons/arrow-right-icon.js";
1
+ import { LitElement, html, nothing } from "lit";
2
+ // Installs the design tokens into document scope; see src/tokens/tokens.js
3
+ import "../../tokens/tokens.js";
4
4
  import buttonsCSS from "./button.css.js";
5
+ import { fontSize, fontWeight } from "../shared/token-scales.js";
6
+ // the trailing arrow on bd-contact-button; an atom now, not a local registry
7
+ import "../icons/arrow-icon.js";
8
+
9
+ // "white" and "outline-white" are ButtonLink-only presets (no bd-button
10
+ // equivalent) used on a solid-colour banner (e.g. cta-banner) where a
11
+ // surface="dark" primary/tertiary button would still be too close in tone —
12
+ // they render a plain white fill / white 2px outline regardless of surface.
13
+ const VARIANTS = ["primary", "secondary", "tertiary", "ghost", "accent", "white", "outline-white"];
14
+
15
+ /**
16
+ * Pre-ds-button kind values, mapped onto the variant that reproduces them, so
17
+ * existing markup keeps rendering the same thing.
18
+ */
19
+ const LEGACY_KINDS = {
20
+ "outline" : "ghost", // dark 2px outline
21
+ "primary-outline": "tertiary", // blue 2px outline
22
+ // red is this system's conversion colour, not a danger signal, so both of
23
+ // these earlier names described the wrong thing
24
+ "danger" : "accent",
25
+ "destructive" : "accent"
26
+ };
27
+
28
+ const SIZE_CLASS = { sm: "button--small", md: "button--medium", lg: "button--large" };
29
+ const SURFACES = ["light", "dark"];
30
+ const SHAPES = ["default", "rounded"];
31
+
32
+ /**
33
+ * Everything bd-button and bd-button-link share.
34
+ *
35
+ * They differ only in the element they render, and that difference is
36
+ * semantic rather than visual: <button> performs an action in place, <a href>
37
+ * navigates. No CSS substitutes for it — only the link appears in a screen
38
+ * reader's list of links and offers open-in-new-tab, and only the button
39
+ * responds to Space as well as Enter.
40
+ */
41
+ class BdButtonBase extends LitElement {
42
+ static styles = [buttonsCSS];
5
43
 
6
- const arrowIcon = html`
7
- <bd-arrow-right-icon class="button__arrow" width="100%" height="100%" aria-hidden="true"></bd-arrow-right-icon>
8
- `;
9
-
10
- class Button extends LitElement {
11
44
  static properties = {
12
- label : { type: String },
13
- href : { type: String },
14
- size : { type: String },
15
- customClass: { type: String },
16
- kind : { type: String },
17
- fullWidth : { type: Boolean },
18
- strong : { type: Boolean },
19
- fontSize : { type: String, attribute: "font-size" },
20
- fontWeight : { type: String, attribute: "font-weight" },
21
- align : { type: String },
22
- noArrow : { type: Boolean, attribute: "no-arrow" },
23
- shape : { type: String, reflect: true }
45
+ label : { type: String },
46
+ size : { type: String },
47
+ variant : { type: String, reflect: true },
48
+ /** @deprecated pass `variant`. kind meant four different things across the
49
+ atoms; on this one it always meant appearance. */
50
+ kind : { type: String },
51
+ fullWidth : { type: Boolean, attribute: "fullwidth" },
52
+ strong : { type: Boolean },
53
+ disabled : { type: Boolean, reflect: true },
54
+ loading : { type: Boolean, reflect: true },
55
+ fontSize : { type: String, attribute: "font-size" },
56
+ fontWeight: { type: String, attribute: "font-weight" },
57
+ align : { type: String },
58
+ surface : { type: String, reflect: true },
59
+ /** "rounded" takes the pill corner; see .button--rounded. */
60
+ shape : { type: String, reflect: true },
61
+ /** The accent variant reveals a trailing arrow on hover; this opts out. */
62
+ noArrow : { type: Boolean, attribute: "no-arrow" },
63
+ /* A circle holding one icon instead of a label: the homepage's audience
64
+ cards and the mobile menu toggle. A mode, not a variant, because every
65
+ variant, size, surface and state applies to it unchanged -- which is how
66
+ the Button icon sets in the foundation Figma file are drawn. With no
67
+ visible text there is nothing to name the control, so label becomes
68
+ required rather than optional. */
69
+ iconOnly : { type: Boolean, attribute: "icon-only", reflect: true }
24
70
  };
25
71
 
26
72
  constructor() {
27
73
  super();
28
- this.label = "Buy now";
29
- this.href = "";
74
+ // label is only an accessible name, never the visible text: defaulting it
75
+ // to a word like "Buy now" would override whatever the caller slotted
76
+ this.label = "";
30
77
  this.size = "md";
78
+ this.variant = "primary";
79
+ this.kind = "";
80
+ this.fullWidth = false;
81
+ this.strong = false;
82
+ this.disabled = false;
83
+ this.loading = false;
31
84
  this.fontSize = "";
32
85
  this.fontWeight = "";
33
86
  this.align = "left";
34
- this.noArrow = false;
87
+ // "dark" retunes every variant for a hero or panel with a full background
88
+ // colour; see the SURFACE block in button.css.js
89
+ this.surface = "light";
35
90
  this.shape = "default";
91
+ this.noArrow = false;
92
+ this.iconOnly = false;
36
93
  }
37
94
 
38
- _onFocusin(e) {
39
- const el = e.currentTarget;
40
- requestAnimationFrame(() =>
41
- requestAnimationFrame(() => {
42
- if (el.matches(':focus-visible')) {
43
- this.setAttribute('data-bd-button-focus', '');
44
- }
45
- })
46
- );
47
- }
48
-
49
- _onFocusout() {
50
- this.removeAttribute('data-bd-button-focus');
51
- }
52
-
53
- _handleClick() {
54
- this.dispatchEvent(new Event("custom-click"));
95
+ willUpdate(changed) {
96
+ if (changed.has("kind") && this.kind) this.variant = this.kind;
97
+ if (changed.has("variant") || changed.has("kind")) {
98
+ if (LEGACY_KINDS[this.variant]) this.variant = LEGACY_KINDS[this.variant];
99
+ if (!VARIANTS.includes(this.variant)) this.variant = "primary";
100
+ }
101
+ if (changed.has("size") && !SIZE_CLASS[this.size]) this.size = "md";
102
+ if (changed.has("surface") && !SURFACES.includes(this.surface)) this.surface = "light";
103
+ if (changed.has("shape") && !SHAPES.includes(this.shape)) this.shape = "default";
55
104
  }
56
105
 
57
- get sizeClass() {
58
- switch (this.size) {
59
- case "lg":
60
- return "button--large";
61
- case "md":
62
- return "button--medium";
63
- case "sm":
64
- return "button--small";
65
- default:
66
- return "";
67
- }
106
+ get _inert() {
107
+ return this.disabled || this.loading;
68
108
  }
69
109
 
70
- get kindClass() {
71
- switch (this.kind) {
72
- case "primary-outline":
73
- return "button--primary--outline";
74
- case "outline":
75
- return "button--outline";
76
- case "secondary":
77
- return "button--secondary";
78
- case "primary":
79
- return "button--primary";
80
- case "danger":
81
- return "button--danger";
82
- case "white":
83
- return "button--white";
84
- case "outline-white":
85
- return "button--outline-white";
86
- default:
87
- return "";
88
- }
110
+ /* The hover arrow belongs to the conversion CTA -- what "danger" meant before
111
+ it became accent -- and never to a control that cannot be pressed. */
112
+ get _hasArrow() {
113
+ // an icon-only circle has no label to slide aside
114
+ return this.variant === "accent" && !this.noArrow && !this._inert && !this.iconOnly;
89
115
  }
90
116
 
91
- get shapeClass() {
92
- return this.shape === "rounded" ? "button--rounded" : "";
117
+ get _classes() {
118
+ return [
119
+ "button",
120
+ `button--${this.variant}`,
121
+ SIZE_CLASS[this.size],
122
+ this.iconOnly ? "button--icon-only" : "",
123
+ // a circle stretched to the container is a pill, not this control
124
+ this.fullWidth && !this.iconOnly ? "max--width" : "",
125
+ this.strong ? "button--bold" : "",
126
+ // the circle is already the roundest shape there is
127
+ this.shape === "rounded" && !this.iconOnly ? "button--rounded" : "",
128
+ this._hasArrow ? "button--with-arrow" : "",
129
+ this.loading ? "button--loading" : ""
130
+ ].filter(Boolean).join(" ");
93
131
  }
94
132
 
95
- get fullWidthClass() {
96
- return this.fullWidth ? "max--width" : "";
133
+ /**
134
+ * font-size and font-weight are step names resolved to token references by
135
+ * shared/token-scales.js; anything off-scale is dropped.
136
+ */
137
+ get _inlineStyle() {
138
+ return [
139
+ ["font-size", fontSize(this.fontSize)],
140
+ ["font-weight", fontWeight(this.fontWeight)]
141
+ ].filter(([, v]) => v).map(([p, v]) => `${p}: ${v};`)
142
+ .join(" ");
97
143
  }
98
144
 
99
- get strongClass() {
100
- return this.strong ? "button--bold" : "";
145
+ /** The spinner is decorative — aria-busy on the control carries the state. */
146
+ get _content() {
147
+ if (this.iconOnly) return this._iconContent;
148
+ return html`${this.loading
149
+ ? html`<span class="spinner" aria-hidden="true"></span>`
150
+ : nothing}<span class="button__label"><slot>${this.label}</slot></span>${this._hasArrow
151
+ ? html`<bd-arrow-icon class="button__arrow" aria-hidden="true"></bd-arrow-icon>`
152
+ : nothing}`;
101
153
  }
102
154
 
103
- get hasArrow() {
104
- return this.kind === "danger" && !this.noArrow;
155
+ /**
156
+ * One glyph, never a glyph and a spinner side by side: the circle holds a
157
+ * single item, so loading replaces the icon rather than joining it.
158
+ *
159
+ * The icon is a slot so any bd-*-icon can go in; with nothing slotted it is
160
+ * the arrow, the system's only true shafted arrow and the default the Figma
161
+ * sets draw. The button and icon scales share step names -- sm 16, md 20,
162
+ * lg 24 against padding 8, 12 and 16 -- so the size passes straight through
163
+ * and the circle comes out 32, 44 and 56.
164
+ */
165
+ get _iconContent() {
166
+ if (this.loading) return html`<span class="spinner" aria-hidden="true"></span>`;
167
+ return html`<slot><bd-arrow-icon size="${this.size}" aria-hidden="true"></bd-arrow-icon></slot>`;
105
168
  }
106
169
 
107
- get arrowClass() {
108
- return this.hasArrow ? "button--with-arrow" : "";
170
+ _handleClick(e) {
171
+ if (this._inert) {
172
+ e.preventDefault();
173
+ e.stopPropagation();
174
+ return;
175
+ }
176
+ this.dispatchEvent(new Event("custom-click"));
109
177
  }
178
+ }
110
179
 
180
+ /** Performs an action in place. */
181
+ class Button extends BdButtonBase {
111
182
  render() {
112
183
  return html`
113
184
  <button
185
+ type="button"
186
+ class="${this._classes}"
187
+ aria-label="${this.label || nothing}"
188
+ aria-busy="${this.loading ? "true" : nothing}"
189
+ ?disabled="${this.disabled}"
190
+ style="${this._inlineStyle}"
114
191
  @click=${this._handleClick}
115
- @focusin=${this._onFocusin}
116
- @focusout=${this._onFocusout}
117
- class="button ${this.kindClass} ${this.sizeClass} ${this.shapeClass} ${this.fullWidthClass} ${this.strongClass} ${this.arrowClass}"
118
- aria-label="${this.label}"
119
- style="
120
- ${this.fontSize ? `font-size: ${this.fontSize};` : ""}
121
- ${this.fontWeight ? `font-weight: ${this.fontWeight};` : ""}
122
- "
123
- >
124
- <span class="button__label"><slot></slot></span>
125
- ${this.hasArrow ? arrowIcon : ""}
126
- </button>
192
+ >${this._content}</button>
127
193
  `;
128
194
  }
129
195
  }
130
196
 
131
- class ButtonLink extends LitElement {
197
+ /** Navigates. Looks identical; is not the same control. */
198
+ class ButtonLink extends BdButtonBase {
132
199
  static properties = {
133
- label : { type: String },
134
- href : { type: String },
135
- size : { type: String },
136
- customClass: { type: String },
137
- kind : { type: String },
138
- fullWidth : { type: Boolean, attribute: "fullwidth" },
139
- strong : { type: Boolean },
140
- fontSize : { type: String, attribute: "font-size" },
141
- fontWeight : { type: String, attribute: "font-weight" },
142
- buyLink : { type: Boolean },
143
- shadow : { type: Boolean, reflect: true },
144
- align : { type: String },
145
- noArrow : { type: Boolean, attribute: "no-arrow" },
146
- shape : { type: String, reflect: true }
200
+ href : { type: String },
201
+ buyLink: { type: Boolean },
202
+ /* The base class comment calls <a href> the element that navigates and
203
+ "offers open-in-new-tab" -- and until now the atom could not express
204
+ that, so a consumer needing a new tab had to hand-roll the anchor and
205
+ lost the button's focus ring, disabled state and typography with it. */
206
+ target : { type: String },
207
+ rel : { type: String }
147
208
  };
148
209
 
149
210
  constructor() {
150
211
  super();
151
- this.label = "Buy now";
152
212
  this.href = "";
153
- this.size = "md";
154
- this.kind = "primary";
155
- this.fullWidth = false;
156
- this.strong = false;
157
- this.fontSize = "";
158
- this.fontWeight = "";
159
213
  this.buyLink = false;
160
214
  this.shadow = true;
161
215
  this.align = "left";
162
- this.noArrow = false;
163
- this.shape = "default";
216
+ this.target = "";
217
+ this.rel = "";
164
218
  }
165
219
 
166
220
  _onFocusin(e) {
@@ -178,44 +232,6 @@ class ButtonLink extends LitElement {
178
232
  this.removeAttribute('data-bd-button-focus');
179
233
  }
180
234
 
181
- get sizeClass() {
182
- switch (this.size) {
183
- case "lg":
184
- return "button--large";
185
- case "md":
186
- return "button--medium";
187
- case "sm":
188
- return "button--small";
189
- default:
190
- return "";
191
- }
192
- }
193
-
194
- get kindClass() {
195
- switch (this.kind) {
196
- case "primary-outline":
197
- return "button--primary--outline";
198
- case "outline":
199
- return "button--outline";
200
- case "secondary":
201
- return "button--secondary";
202
- case "primary":
203
- return "button--primary";
204
- case "danger":
205
- return "button--danger";
206
- case "white":
207
- return "button--white";
208
- case "outline-white":
209
- return "button--outline-white";
210
- default:
211
- return "";
212
- }
213
- }
214
-
215
- get shapeClass() {
216
- return this.shape === "rounded" ? "button--rounded" : "";
217
- }
218
-
219
235
  get fullWidthClass() {
220
236
  return this.fullWidth ? "max--width" : "";
221
237
  }
@@ -224,42 +240,47 @@ class ButtonLink extends LitElement {
224
240
  return this.strong ? "button--bold" : "";
225
241
  }
226
242
 
227
- get hasArrow() {
228
- return this.kind === "danger" && !this.noArrow;
229
- }
230
-
231
- get arrowClass() {
232
- return this.hasArrow ? "button--with-arrow" : "";
243
+ /* Reverse tabnabbing is the reason rather than convention: a new tab opened
244
+ from here keeps a window.opener handle back unless rel says otherwise. An
245
+ explicit rel always wins, so a consumer can still opt out deliberately. */
246
+ get _rel() {
247
+ if (this.rel) return this.rel;
248
+ return this.target === "_blank" ? "noopener noreferrer" : "";
233
249
  }
234
250
 
235
251
  render() {
252
+ // A link has no native disabled state. Dropping the href stops it being a
253
+ // link at all, so the state is announced with aria-disabled and the role
254
+ // is restored explicitly; tabindex keeps it out of the tab order.
255
+ const href = this._inert || !this.href ? nothing : this.href;
256
+
236
257
  return html`
237
258
  <a
238
- href="${this.href}"
239
- class="button ${this.kindClass} ${this.sizeClass} ${this.shapeClass} ${this.fullWidthClass} ${this.strongClass} ${this.arrowClass}"
240
- aria-label="${this.label}"
259
+ href="${href}"
260
+ target="${this.target || nothing}"
261
+ rel="${this._rel || nothing}"
262
+ role="${this._inert ? "link" : nothing}"
263
+ tabindex="${this._inert ? "-1" : nothing}"
264
+ class="${this._classes}"
265
+ aria-label="${this.label || nothing}"
266
+ aria-busy="${this.loading ? "true" : nothing}"
267
+ aria-disabled="${this.disabled ? "true" : nothing}"
268
+ style="${this._inlineStyle}"
241
269
  @click=${this._handleClick}
242
- @focusin=${this._onFocusin}
243
- @focusout=${this._onFocusout}
244
- style="
245
- ${this.fontSize ? `font-size: ${this.fontSize};` : ""}
246
- ${this.fontWeight ? `font-weight: ${this.fontWeight};` : ""}
247
- "
248
270
  ?data-store-render="${this.buyLink}"
249
271
  ?data-store-buy-link="${this.buyLink}"
250
- >
251
- <span class="button__label"><slot>${this.label}</slot></span>
252
- ${this.hasArrow ? arrowIcon : ""}
253
- </a>
272
+ >${this._content}</a>
254
273
  `;
255
274
  }
256
-
257
- _handleClick() {
258
- this.dispatchEvent(new Event("custom-click"));
259
- }
260
275
  }
261
276
 
277
+ /**
278
+ * A fixed "Contact us" CTA — a preset rather than a variant, with no size or
279
+ * kind axis. Its trailing arrow is bd-arrow-icon, an atom.
280
+ */
262
281
  class ContactButton extends LitElement {
282
+ static styles = [buttonsCSS];
283
+
263
284
  static properties = {
264
285
  label: { type: String },
265
286
  href : { type: String },
@@ -270,59 +291,30 @@ class ContactButton extends LitElement {
270
291
  constructor() {
271
292
  super();
272
293
  this.label = "Contact us";
273
- this.href = "https://www.google.com";
294
+ this.href = "";
274
295
  this.align = "left";
275
296
  this.shape = "default";
276
297
  }
277
298
 
278
- _onFocusin(e) {
279
- const el = e.currentTarget;
280
- requestAnimationFrame(() =>
281
- requestAnimationFrame(() => {
282
- if (el.matches(':focus-visible')) {
283
- this.setAttribute('data-bd-button-focus', '');
284
- }
285
- })
286
- );
287
- }
288
-
289
- _onFocusout() {
290
- this.removeAttribute('data-bd-button-focus');
291
- }
292
-
293
- get shapeClass() {
294
- return this.shape === "rounded" ? "button--rounded" : "";
299
+ _handleClick() {
300
+ this.dispatchEvent(new Event("custom-click"));
295
301
  }
296
302
 
297
303
  render() {
298
304
  return html`
299
- <a
300
- class="button bd-contact-btn ${this.shapeClass}"
301
- href="${this.href}"
302
- aria-label="${this.label}"
305
+ <a
306
+ class="button bd-contact-btn ${this.shape === "rounded" ? "button--rounded" : ""}"
307
+ href="${this.href || nothing}"
308
+ aria-label="${this.label || nothing}"
303
309
  @click=${this._handleClick}
304
- @focusin=${this._onFocusin}
305
- @focusout=${this._onFocusout}
306
- style="
307
- ${this.fontSize ? `font-size: ${this.fontSize};` : ""}
308
- ${this.fontWeight ? `font-weight: ${this.fontWeight};` : ""}
309
- "
310
310
  >
311
311
  ${this.label}
312
- <arrow-icon aria-hidden="true"></arrow-icon>
312
+ <bd-arrow-icon aria-hidden="true"></bd-arrow-icon>
313
313
  </a>
314
314
  `;
315
315
  }
316
-
317
- _handleClick() {
318
- this.dispatchEvent(new Event("custom-click"));
319
- }
320
316
  }
321
317
 
322
- Button.styles = [tokens, buttonsCSS];
323
- ButtonLink.styles = [tokens, buttonsCSS];
324
- ContactButton.styles = [tokens, buttonsCSS];
325
-
326
318
  customElements.define("bd-button", Button);
327
319
  customElements.define("bd-button-link", ButtonLink);
328
320
  customElements.define("bd-contact-button", ContactButton);