@repobit/dex-system-design 0.25.11 → 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 +195 -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,6 +1,8 @@
1
1
  import { LitElement, html } from "lit";
2
- import { tokens } from "../../tokens/tokens.js";
2
+ // Installs the design tokens into document scope; see src/tokens/tokens.js
3
+ import "../../tokens/tokens.js";
3
4
  import carouselCSS from "../carousel/carousel.css.js";
5
+ import "../heading/heading.js";
4
6
  import "../modal/modal.js";
5
7
  export class CarouselItem extends LitElement {
6
8
  static properties = {
@@ -52,12 +54,13 @@ export class CarouselItem extends LitElement {
52
54
  `;
53
55
  }
54
56
 
55
- static styles = [tokens, carouselCSS];
57
+ static styles = [carouselCSS];
56
58
  }
57
59
 
58
60
  class CustomCarousel extends LitElement {
59
61
  static properties = {
60
62
  title : { type: String },
63
+ headingLevel: { type: Number, attribute: "heading-level" },
61
64
  selectedItem: { type: Object }
62
65
  };
63
66
 
@@ -65,6 +68,31 @@ class CustomCarousel extends LitElement {
65
68
  super();
66
69
  this.selectedItem = null;
67
70
  this.title = "";
71
+ this.headingLevel = 2;
72
+ }
73
+
74
+ /**
75
+ * The heading element this section renders. Level is the document-outline
76
+ * question and belongs to whoever composes the page; the type scale is this
77
+ * component's own and does not move with it. Defaults to h2, because a
78
+ * reusable section cannot know it is the only thing on the page, and two of
79
+ * these used to put two h1s on one.
80
+ */
81
+ get _headingTag() {
82
+ const n = Number(this.headingLevel);
83
+ return Number.isInteger(n) && n >= 1 && n <= 6 ? `h${n}` : "h2";
84
+ }
85
+
86
+ /* bd-h already builds the dynamic tag, and it keeps the two questions apart
87
+ the way this component needs: as= is the outline level the page composes,
88
+ visual-level= is the type role, which is ours. Building the element here
89
+ meant re-implementing bd-h and then hand-writing the type on top of it --
90
+ and the hand-written type carried no line-height and no margin, so the
91
+ title rendered with the browser's own h2 margins. */
92
+ _renderTitle(className) {
93
+ return html`<bd-h as=${this._headingTag} visual-level="h3" class=${className}
94
+ >${this.title}</bd-h
95
+ >`;
68
96
  }
69
97
 
70
98
  firstUpdated() {
@@ -224,7 +252,7 @@ class CustomCarousel extends LitElement {
224
252
  <div class="bd-carousel">
225
253
  ${this.title
226
254
  ? html`<div class="bd-header-carousel">
227
- <h1 class="bd-section-title">${this.title}</h1>
255
+ ${this._renderTitle("bd-section-title")}
228
256
  </div>`
229
257
  : ""}
230
258
 
@@ -265,7 +293,7 @@ class CustomCarousel extends LitElement {
265
293
  `;
266
294
  }
267
295
 
268
- static styles = [tokens, carouselCSS];
296
+ static styles = [carouselCSS];
269
297
  }
270
298
 
271
299
  customElements.define("bd-carousel-item", CarouselItem);
@@ -1,132 +1,150 @@
1
1
  import { css } from "lit";
2
2
 
3
+ /**
4
+ * Styled entirely from src/tokens/tokens.js — see CLAUDE.md, "The foundation
5
+ * rule". Measurements were matched to the ds-checkbox design by finding the
6
+ * token that already carries each value.
7
+ *
8
+ * The whole row is the control, not just the box. That makes clicking the
9
+ * label work (it did nothing before) and gives the target the 24px minimum
10
+ * WCAG 2.5.8 asks for, which a 20px box alone cannot reach.
11
+ */
3
12
  export default css`
4
13
  :host {
5
14
  display: inline-block;
6
15
  }
7
16
 
8
- .bd-checkbox,
9
- .bd-radio {
17
+ .bd-checkbox {
10
18
  display: inline-flex;
11
19
  align-items: center;
12
- cursor: pointer;
13
20
  gap: var(--spacing-8);
14
- font-family: sans-serif;
15
- font-size: 14px;
21
+ /* padding rather than a min-height: a min-height adds its slack only
22
+ above and below, which left the focus ring 2px from the content at the
23
+ sides and 4px at the top and bottom. Padding insets all four edges
24
+ equally, and still carries the row past the 24px WCAG 2.5.8 asks for. */
25
+ padding: var(--spacing-2);
26
+ cursor: pointer;
27
+ font-family: var(--typography-fontFamily-sans);
28
+ /* body-small: sm / normal / 1.5, which is what this already rendered on all
29
+ three axes. Only the axes this rule declares are adopted -- the role also
30
+ carries a family and a tracking, and importing those would change text
31
+ the rule never spoke for. */
32
+ font-size: var(--typography-body-small-fontSize);
33
+ line-height: var(--typography-body-small-lineHeight);
34
+ color: var(--color-text-primary);
16
35
  vertical-align: middle;
36
+ outline: none;
17
37
  }
18
38
 
19
- .bd-checkbox.disabled,
20
- .bd-radio.disabled {
21
- cursor: not-allowed;
22
- opacity: 0.6;
23
- }
24
-
25
- .checkbox-label,
26
- .radio-label {
27
- font-size: 14px;
28
- color: black;
39
+ .bd-checkbox:focus-visible {
40
+ /* the one focus ring — see Foundations/Focus. A two-colour ring whose
41
+ white inner band keeps it visible on a coloured or dark surface, which a
42
+ single-colour outline cannot do. */
43
+ outline: none;
44
+ box-shadow: var(--focus-outline-primary);
45
+ border-radius: var(--focus-outline-radius);
29
46
  }
30
47
 
31
- /* Checkbox */
32
- .bd-checkbox .bd-outer {
33
- width: var(--spacing-22);
34
- height: var(--spacing-22);
35
- border: var(--spacing-2) solid var(--color-blue-500);
36
- border-radius: 4px;
37
- display: flex;
48
+ /* ---- the box ---------------------------------------------------------- */
49
+ .box {
50
+ flex: none;
51
+ width: var(--dimension-20);
52
+ height: var(--dimension-20);
53
+ box-sizing: border-box;
54
+ border: var(--border-width-2) solid var(--color-action-primary);
55
+ border-radius: var(--radius-sm);
56
+ background: var(--color-surface-default);
57
+ display: inline-flex;
38
58
  align-items: center;
39
59
  justify-content: center;
40
- box-sizing: border-box;
41
- transition: border-color 0.2s ease;
42
- outline: none;
60
+ /* the mark is drawn in currentColor, so one declaration moves both. It is
61
+ ink on an action fill, not "the page colour" -- the two are both white
62
+ here, and only one of them stays white in a dark theme */
63
+ color: var(--color-action-on-fill);
64
+ transition: background-color var(--transition-duration-normal) var(--transition-easing-smooth),
65
+ border-color var(--transition-duration-normal) var(--transition-easing-smooth);
43
66
  }
44
67
 
45
- .bd-checkbox .bd-inner {
46
- width: var(--spacing-12);
47
- height: var(--spacing-12);
48
- background-color: var(--color-blue-500);
49
- border-radius: var(--spacing-2);
50
- transform: scale(0);
51
- transition: transform 0.2s ease, background-color 0.2s ease;
68
+ .bd-checkbox:hover .box {
69
+ border-color: var(--color-action-primary-hover);
52
70
  }
53
71
 
54
- .bd-checkbox .bd-inner.visible {
55
- transform: scale(1);
72
+ /* ---- checked and indeterminate: the box fills, the mark is white ------ */
73
+ :host([checked]) .box,
74
+ :host([indeterminate]) .box {
75
+ background: var(--color-action-primary);
76
+ border-color: var(--color-action-primary);
56
77
  }
57
78
 
58
- .bd-radio .bd-outer {
59
- width: var(--spacing-22);
60
- height: var(--spacing-22);
61
- border: var(--spacing-2) solid var(--color-blue-500);
62
- border-radius: 50%;
63
- display: flex;
64
- align-items: center;
65
- justify-content: center;
66
- box-sizing: border-box;
67
- transition: border-color 0.2s ease;
68
- outline: none;
79
+ :host([checked]) .bd-checkbox:hover .box,
80
+ :host([indeterminate]) .bd-checkbox:hover .box {
81
+ background: var(--color-action-primary-hover);
82
+ border-color: var(--color-action-primary-hover);
83
+ }
84
+
85
+ .mark {
86
+ width: var(--dimension-12);
87
+ height: var(--dimension-12);
88
+ display: block;
69
89
  }
70
90
 
71
- .bd-radio .bd-inner {
72
- width: var(--spacing-12);
73
- height: var(--spacing-12);
74
- background-color: var(--color-blue-500);
75
- border-radius: 50%;
76
- transform: scale(0);
77
- transition: transform 0.2s ease, background-color 0.2s ease;
91
+ .mark path {
92
+ fill: none;
93
+ stroke: currentColor;
94
+ stroke-width: var(--border-width-2);
95
+ /* flat ends and a mitred vertex: the tick and the dash read as sharp,
96
+ matching the square corners of the box rather than softening them */
97
+ stroke-linecap: butt;
98
+ stroke-linejoin: miter;
78
99
  }
79
100
 
80
- .bd-radio .bd-inner.visible {
81
- transform: scale(1);
101
+ /* ---- disabled --------------------------------------------------------
102
+ Grey throughout, and the label greys too rather than the whole row
103
+ dropping to an opacity, which is what the design specifies. Exempt from
104
+ contrast under WCAG 1.4.3.
105
+
106
+ The disabled edge is --color-border-disabled, the role named for the
107
+ state it expresses. It was --color-border-subtle, a hierarchy role, and
108
+ the family carried two answers: five controls drew neutral-200 for a
109
+ disabled edge and four drew neutral-300, so a disabled checkbox and a
110
+ disabled text field did not agree on what one looks like. The state role
111
+ wins because the state is what the colour means, and because neutral-300
112
+ measures 2.27:1 against the page where neutral-200 measures 1.66. A
113
+ disabled control is exempt under 1.4.11, so this is perceptibility rather
114
+ than conformance -- but it moves the right way, and it stays subordinate
115
+ to every enabled edge.
116
+
117
+ The fills below still draw neutral-200. A fill is not an edge, and whether
118
+ a disabled checked box should darken along with its border is a separate
119
+ question that has not been put yet. */
120
+ :host([disabled]) .bd-checkbox {
121
+ cursor: not-allowed;
122
+ color: var(--color-text-subtle);
82
123
  }
83
124
 
84
- .bd-checkbox .bd-outer:hover,
85
- .bd-radio .bd-outer:hover {
86
- border-color: var(--color-blue-700);
125
+ :host([disabled]) .box {
126
+ border-color: var(--color-border-disabled);
127
+ background: var(--color-surface-default);
87
128
  }
88
129
 
89
- .bd-checkbox .bd-inner:hover,
90
- .bd-radio .bd-inner:hover {
91
- background-color: var(--color-blue-700);
130
+ :host([disabled][checked]) .box,
131
+ :host([disabled][indeterminate]) .box {
132
+ background: var(--color-border-subtle);
133
+ border-color: var(--color-border-disabled);
92
134
  }
93
135
 
94
- .bd-checkbox.disabled .bd-outer.checked,
95
- .bd-radio.disabled .bd-outer.checked {
96
- border-color: #b0b0b0;
136
+ :host([disabled]) .bd-checkbox:hover .box {
137
+ border-color: var(--color-border-disabled);
97
138
  }
98
139
 
99
- .bd-checkbox.disabled .bd-outer,
100
- .bd-radio.disabled .bd-outer {
101
- border-color: #b0b0b0;
140
+ :host([disabled][checked]) .bd-checkbox:hover .box,
141
+ :host([disabled][indeterminate]) .bd-checkbox:hover .box {
142
+ background: var(--color-border-subtle);
102
143
  }
103
144
 
104
- .bd-checkbox.disabled .bd-inner,
105
- .bd-radio.disabled .bd-inner {
106
- background-color: #b0b0b0;
145
+ @media (prefers-reduced-motion: reduce) {
146
+ .box {
147
+ transition: none;
148
+ }
107
149
  }
108
-
109
-
110
- .bd-checkbox .bd-outer:focus {
111
- outline: none;
112
- }
113
-
114
- .bd-checkbox .bd-outer:focus-visible {
115
- outline: var(--spacing-2) solid var(--color-blue-500);
116
- outline-offset: var(--spacing-2);
117
- }
118
- .bd-checkbox .bd-outer.checked:focus-visible {
119
- border-color: var(--color-blue-700);
120
- }
121
-
122
- .bd-checkbox .bd-outer.checked:focus-visible .bd-inner.visible {
123
- background-color: var(--color-blue-700);
124
- }
125
- .bd-checkbox .bd-outer.checked.kbd-activate {
126
- border-color: var(--color-blue-700);
127
- }
128
-
129
- .bd-checkbox .bd-outer.checked.kbd-activate .bd-inner.visible {
130
- background-color: var(--color-blue-700);
131
- }
132
150
  `;
@@ -1,129 +1,103 @@
1
1
  import { LitElement, html } from "lit";
2
- import { tokens } from "../../tokens/tokens.js";
3
- import checkboxCSS from "./checkbox.css";
2
+ // Installs the design tokens into document scope; see src/tokens/tokens.js
3
+ import "../../tokens/tokens.js";
4
+ // the extension matters: "./checkbox.css" only resolved because Vite appends
5
+ // .js for us, and failed outright under native ESM
6
+ import checkboxCSS from "./checkbox.css.js";
4
7
 
5
8
  export class BdCheckbox extends LitElement {
6
- static styles = [tokens, checkboxCSS];
9
+ static styles = [checkboxCSS];
7
10
 
8
11
  static properties = {
9
- name : { type: String },
10
- value : { type: String },
11
- checked : { type: Boolean, reflect: true },
12
- disabled: { type: Boolean, reflect: true },
13
- label : { type: String }
12
+ name : { type: String },
13
+ value : { type: String },
14
+ checked : { type: Boolean, reflect: true },
15
+ indeterminate: { type: Boolean, reflect: true },
16
+ disabled : { type: Boolean, reflect: true },
17
+ label : { type: String }
14
18
  };
15
19
 
16
20
  constructor() {
17
21
  super();
18
22
  this.name = "";
19
23
  this.value = "";
24
+ // checked was left undefined, which rendered aria-checked="" — an invalid
25
+ // value, so an unchecked box announced no state at all
26
+ this.checked = false;
27
+ this.indeterminate = false;
20
28
  this.disabled = false;
21
29
  this.label = "";
22
- this._hadKeyboardFocus = false;
23
-
24
- this._handleDocumentKeydown = (e) => {
25
- if (e.key === "Tab") {
26
- this._hadKeyboardFocus = true;
27
- }
28
- };
29
-
30
- this._handleDocumentMousedown = this._handleDocumentMousedown.bind(this);
31
- }
32
-
33
- _handleDocumentMousedown(e) {
34
- const path = e.composedPath();
35
- const target = path.find(
36
- (el) =>
37
- el instanceof HTMLElement &&
38
- el.classList &&
39
- el.classList.contains("bd-outer")
40
- );
41
- if (target) {
42
- target.classList.remove("focus");
43
- }
44
- setTimeout(() => {
45
- this._hadKeyboardFocus = false;
46
- }, 0);
47
30
  }
48
31
 
49
- connectedCallback() {
50
- super.connectedCallback();
51
- document.addEventListener("keydown", this._handleDocumentKeydown);
52
- document.addEventListener("mousedown", this._handleDocumentMousedown);
32
+ /** true | false | mixed — mixed wins, as it does natively. */
33
+ get _ariaChecked() {
34
+ if (this.indeterminate) return "mixed";
35
+ return this.checked ? "true" : "false";
53
36
  }
54
37
 
55
- disconnectedCallback() {
56
- super.disconnectedCallback();
57
- document.removeEventListener("keydown", this._handleDocumentKeydown);
58
- document.removeEventListener("mousedown", this._handleDocumentMousedown);
59
- }
60
-
61
- handleClick() {
38
+ _toggle() {
62
39
  if (this.disabled) return;
63
- this.checked = !this.checked;
40
+
41
+ // clicking an indeterminate box resolves it to checked, as it does natively
42
+ if (this.indeterminate) {
43
+ this.indeterminate = false;
44
+ this.checked = true;
45
+ } else {
46
+ this.checked = !this.checked;
47
+ }
64
48
 
65
49
  this.dispatchEvent(
66
50
  new CustomEvent("change", {
67
- detail : { value: this.checked ? this.value : null },
51
+ detail : { checked: this.checked, value: this.checked ? this.value : null },
68
52
  bubbles : true,
69
53
  composed: true
70
54
  })
71
55
  );
72
56
  }
73
57
 
74
- handleFocus(e) {
75
- if (this._hadKeyboardFocus) {
76
- e.target.classList.add("focus");
77
- }
78
- }
79
-
80
- handleKeydown(e) {
81
- if (this.disabled) return;
82
-
58
+ _handleKeydown(e) {
59
+ // Space is the native activation key. Enter is kept as a convenience: this
60
+ // control is not form-associated, so Enter has nothing else to do here.
83
61
  if (e.key === " " || e.key === "Enter") {
84
62
  e.preventDefault();
85
- this.handleClick();
86
-
87
- const outer = e.target;
88
-
89
- if (this._hadKeyboardFocus) {
90
- outer.classList.add("focus", "kbd-activate");
91
-
92
- setTimeout(() => {
93
- outer.classList.remove("kbd-activate");
94
- }, 150);
95
- }
63
+ this._toggle();
96
64
  }
97
65
  }
98
66
 
99
-
100
- handleBlur(e) {
101
- setTimeout(() => {
102
- const active = this.shadowRoot.activeElement || document.activeElement;
103
- if (active !== e.target) {
104
- e.target.classList.remove("focus");
105
- }
106
- }, 0);
67
+ _renderMark() {
68
+ if (this.indeterminate) {
69
+ return html`<svg class="mark" viewBox="0 0 12 12" aria-hidden="true" focusable="false">
70
+ <path d="M2.5 6H9.5" />
71
+ </svg>`;
72
+ }
73
+ if (this.checked) {
74
+ return html`<svg class="mark" viewBox="0 0 12 12" aria-hidden="true" focusable="false">
75
+ <path d="M2.5 6.5L4.75 8.75L9.5 3.5" />
76
+ </svg>`;
77
+ }
78
+ return "";
107
79
  }
108
80
 
109
81
  render() {
82
+ // The row carries the role, the focus and the click target, so the label is
83
+ // part of the control rather than decoration beside it. The previous markup
84
+ // put role and tabindex on the box alone, which meant clicking the label
85
+ // did nothing and the only target was 20px — under the 24px WCAG 2.5.8 asks
86
+ // for. There is no native input to associate a <label> with, so <div> is
87
+ // honest about what this is.
110
88
  return html`
111
- <label class="bd-checkbox ${this.disabled ? "disabled" : ""}">
112
- <span
113
- class="bd-outer ${this.checked && !this.disabled ? "checked" : ""}"
114
- role="checkbox"
115
- aria-checked="${this.checked}"
116
- aria-disabled="${this.disabled}"
117
- tabindex="${this.disabled ? -1 : 0}"
118
- @click=${this.handleClick}
119
- @keydown=${this.handleKeydown}
120
- @focus=${this.handleFocus}
121
- @blur=${this.handleBlur}
122
- >
123
- <span class="bd-inner ${this.checked ? "visible" : ""}"></span>
124
- </span>
125
- <span class="checkbox-label">${this.label}</span>
126
- </label>
89
+ <div
90
+ class="bd-checkbox"
91
+ role="checkbox"
92
+ aria-checked="${this._ariaChecked}"
93
+ aria-disabled="${this.disabled ? "true" : "false"}"
94
+ tabindex="${this.disabled ? -1 : 0}"
95
+ @click=${this._toggle}
96
+ @keydown=${this._handleKeydown}
97
+ >
98
+ <span class="box" part="box">${this._renderMark()}</span>
99
+ <span class="label" part="label"><slot>${this.label}</slot></span>
100
+ </div>
127
101
  `;
128
102
  }
129
103
  }