@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,18 +1,44 @@
1
- import { css } from "lit";
1
+ import { css, unsafeCSS } from "lit";
2
+ import { below } from "../shared/token-scales.js";
2
3
 
3
4
  export default css`
4
5
 
5
6
  :host(bd-compare-section) {
6
7
  display: block;
7
8
 
8
- --cs-bar-primary-fg: var(--color-neutral-0);
9
- --cs-bar-primary-bg: var(--color-blue-500);
9
+ /* The brand fill and the ink that belongs to it. theme-green and theme-red
10
+ adopted --status-*-fill / --status-*-on-fill below with the argument that a
11
+ saturated fill carrying a label is exactly that pair's responsibility; the
12
+ default bar IS that shape and the migration skipped it, so it kept the
13
+ primitives. Value-identical -- --status-brand-fill is blue-500 and
14
+ --status-brand-on-fill is neutral-0 -- and the label measures 4.53:1 here,
15
+ which passes 4.5:1 with almost nothing spare. Worth knowing before anyone
16
+ retunes blue-500: green-500 and red-500 both failed this same test. */
17
+ --cs-bar-primary-fg: var(--status-brand-on-fill);
18
+ --cs-bar-primary-bg: var(--status-brand-fill);
19
+ /* no role: the competitor bar is a resting fill. The three roles at
20
+ neutral-100 are a state (surface-muted), a disabled control
21
+ (surface-disabled) and a surface in the neutral status family, whose FILL
22
+ is neutral-900 -- so the family that matches on job does not match on
23
+ value, and the ones that match on value do not match on job. */
10
24
  --cs-bar-secondary-bg: var(--color-neutral-100);
11
25
  --cs-bar-track-bg: transparent;
12
- --cs-bar-track-border: var(--color-neutral-200);
13
- --cs-bar-height: var(--dimension-48px);
26
+ --cs-bar-track-border: var(--color-border-subtle);
27
+ --cs-bar-height: var(--dimension-48);
14
28
  --cs-gap: var(--spacing-32);
15
- --cs-grid-max: 1228px;
29
+
30
+ /* The section's own text, which is the only text that sits on the page's
31
+ ground rather than on a card. .theme-dark recoloured the bars and the icon
32
+ and stopped there, so on a dark ground the section title kept
33
+ --color-text-primary and rendered at 1.05:1 -- a documented story whose
34
+ heading was invisible.
35
+
36
+ Deliberately section-scoped. .card sets its own --color-surface-container
37
+ and stays light under .theme-dark, so card titles, descriptions, footnotes
38
+ and bar labels must keep their light-surface roles; inverting those turned
39
+ white text onto a light card at 1.04:1 when it was tried. This is a
40
+ component-context fix, not a theme architecture. */
41
+ --cs-section-text: var(--color-text-primary);
16
42
  }
17
43
 
18
44
  :host(compare-card) {
@@ -39,30 +65,30 @@ export default css`
39
65
 
40
66
  .cs-section {
41
67
  background: transparent;
42
- padding: var(--spacing-0) var(--spacing-24);
68
+ padding-block: var(--spacing-0);
43
69
  box-sizing: border-box;
44
70
  }
45
71
 
46
72
  .cs-heading {
47
73
  text-align: center;
74
+ /* not the column: a centred heading measure, one grid step narrower than the page */
48
75
  max-width: var(--grid-container-max-md);
49
76
  margin: 0 auto var(--spacing-40);
50
77
  }
51
78
 
52
- .cs-title { margin: 0 0 var(--spacing-8); }
53
- .cs-subtitle { margin: 0; }
79
+ .cs-title { margin: 0 0 var(--spacing-8); color: var(--cs-section-text); }
80
+ .cs-subtitle { margin: 0; color: var(--cs-section-text); }
54
81
 
55
82
  .cs-grid {
56
83
  display: grid;
57
84
  grid-template-columns: repeat(var(--_cols, 2), minmax(0, 598px));
58
85
  gap: var(--cs-gap);
59
- max-width: var(--cs-grid-max);
60
86
  margin: 0 auto;
61
87
  justify-content: center;
62
88
  align-items: stretch;
63
89
  }
64
90
 
65
- @media (max-width: 768px) {
91
+ @media ${unsafeCSS(below('md'))} {
66
92
  .cs-grid {
67
93
  grid-template-columns: 1fr !important;
68
94
  }
@@ -73,9 +99,16 @@ export default css`
73
99
  }
74
100
 
75
101
  .card {
76
- background: var(--color-neutral-25);
77
- border-radius: var(--radius-3xl);
78
- padding: var(--spacing-32);
102
+ background: var(--color-surface-container);
103
+ /* --radius-2xl, the established card-family corner. This card sat at
104
+ --radius-3xl and was otherwise identical to the family: the same
105
+ --color-surface-container, the same 32px inset, no resting elevation and
106
+ no card-level interaction. P5G could find no responsibility, affordance or
107
+ recorded decision behind the extra 8px -- unlike the carousel item, whose
108
+ 24px corner goes with a 2px border and real drag/modal behaviour and is a
109
+ recorded open question. */
110
+ border-radius: var(--card-radius);
111
+ padding: var(--card-padding);
79
112
  display: flex;
80
113
  flex-direction: column;
81
114
  gap: var(--spacing-24);
@@ -94,8 +127,8 @@ export default css`
94
127
 
95
128
  .card-icon-wrap {
96
129
  flex-shrink: 0;
97
- width: var(--icon-2xl-size, 48px);
98
- height: var(--icon-2xl-size, 48px);
130
+ width: var(--icon-2xl-size);
131
+ height: var(--icon-2xl-size);
99
132
  }
100
133
 
101
134
  .card-icon-wrap bd-img {
@@ -124,7 +157,7 @@ export default css`
124
157
 
125
158
  .card-footnote {
126
159
  margin: 0;
127
- color: var(--color-neutral-900);
160
+ color: var(--color-text-primary);
128
161
  flex: 0 0 auto;
129
162
  }
130
163
 
@@ -158,8 +191,8 @@ slot[name="header"] { display: contents; }
158
191
  }
159
192
 
160
193
  .card--horizontal .card-icon-wrap {
161
- width: var(--dimension-40px);
162
- height: var(--dimension-40px);
194
+ width: var(--dimension-40);
195
+ height: var(--dimension-40);
163
196
  }
164
197
 
165
198
  .card--horizontal .card-description { margin: 0; }
@@ -173,7 +206,7 @@ slot[name="header"] { display: contents; }
173
206
  justify-content: center;
174
207
  }
175
208
 
176
- @media (max-width: 768px) {
209
+ @media ${unsafeCSS(below('md'))} {
177
210
  .card--horizontal {
178
211
  grid-template-columns: 1fr;
179
212
  }
@@ -181,13 +214,27 @@ slot[name="header"] { display: contents; }
181
214
  .card--horizontal .card-footnote { margin-top: 0; }
182
215
  }
183
216
 
217
+ /* Every --cs-bar-* read here carries a fallback, because all of them are
218
+ declared on :host(bd-compare-section) and none on :host(compare-bar). A
219
+ var() with no fallback is invalid at computed-value time, so a bar used
220
+ outside a section lost its height entirely: measured on CompareSection
221
+ "compare-bar (isolated)", which puts four bars in a plain div, the track
222
+ was 0px tall with no border and no fill, and the overflow: hidden below
223
+ clipped the label away with it. Four invisible bars in a story whose whole
224
+ job is to show the bar on its own -- and render-health cannot see it,
225
+ because other things on the page do occupy space.
226
+
227
+ The fallbacks are the same values the section declares, so a bar inside a
228
+ section is unchanged, and a themed section still wins: a custom property
229
+ inheriting from an ancestor beats a fallback, where a declaration on
230
+ :host(compare-bar) would have beaten the theme and pinned every bar. */
184
231
  .bar-track {
185
232
  position: relative;
186
- height: var(--cs-bar-height);
233
+ height: var(--cs-bar-height, var(--dimension-48));
187
234
  border-radius: var(--radius-xl);
188
235
  overflow: hidden;
189
236
  background: var(--cs-bar-track-bg, transparent);
190
- border: var(--border-width-1) solid var(--cs-bar-track-border);
237
+ border: var(--border-width-1) solid var(--cs-bar-track-border, var(--color-border-subtle));
191
238
  box-sizing: border-box;
192
239
  }
193
240
 
@@ -234,52 +281,141 @@ slot[name="header"] { display: contents; }
234
281
 
235
282
  .bar-score { font-variant-numeric: tabular-nums; }
236
283
 
284
+ /* The fallbacks matter: --cs-bar-primary-bg and -fg are declared on
285
+ :host(bd-compare-section) only, so a compare-bar used on its own has neither.
286
+ var() with no fallback is invalid at computed-value time, which made the fill
287
+ transparent and dropped the label back to inherited ink -- measured on
288
+ CompareSection "compare-bar (isolated)", a primary bar with
289
+ background rgba(0, 0, 0, 0) and a dark label, indistinguishable from a
290
+ secondary bar apart from its weight.
291
+
292
+ Fallbacks rather than a declaration on :host(compare-bar): a value set on the
293
+ bar's own host would override the one inheriting from a themed section and
294
+ pin every bar blue. */
237
295
  :host(compare-bar[variant="primary"]) .bar-fill {
238
- background: var(--cs-bar-primary-bg);
296
+ background: var(--cs-bar-primary-bg, var(--status-brand-fill));
239
297
  }
240
298
  :host(compare-bar[variant="primary"]) .bar-label,
241
299
  :host(compare-bar[variant="primary"]) .bar-score {
242
- color: var(--cs-bar-primary-fg);
300
+ color: var(--cs-bar-primary-fg, var(--status-brand-on-fill));
301
+ /* no role: this weight belongs to the primary VARIANT, not to the text --
302
+ the same bar reads 400 in every other variant. A variant is a UI state,
303
+ the category CLAUDE.md keeps apart from type when it says a focus ring is
304
+ never expressed by swapping a border colour. See button.css.js's
305
+ .button--bold for the argument. */
243
306
  font-weight: var(--typography-fontWeight-semibold);
244
307
  }
245
308
 
246
309
  :host(compare-bar[variant="secondary"]) .bar-fill,
247
310
  :host(compare-bar:not([variant])) .bar-fill {
248
- background: var(--cs-bar-secondary-bg);
311
+ /* no role: the same decision as --cs-bar-secondary-bg, which this repeats as
312
+ a fallback for a compare-bar used outside a bd-compare-section. */
313
+ background: var(--cs-bar-secondary-bg, var(--color-neutral-100));
249
314
  }
250
315
  :host(compare-bar[variant="secondary"]) .bar-label,
251
316
  :host(compare-bar:not([variant])) .bar-label,
252
317
  :host(compare-bar[variant="secondary"]) .bar-score,
253
318
  :host(compare-bar:not([variant])) .bar-score {
254
- color: var(--color-neutral-900);
319
+ color: var(--color-text-primary);
255
320
  font-weight: var(--typography-body-regular-fontWeight);
256
321
  }
257
322
 
323
+ /* theme-green and theme-red take --status-*-fill and --status-*-on-fill: a
324
+ saturated fill carrying a label on it is exactly the responsibility those
325
+ roles name, and bd-badge variant="fill" already draws it.
326
+
327
+ This is adopting a decision the system recorded rather than making a new one.
328
+ badge.css.js: "green-700 with a white label, rather than the design's
329
+ green-500 with a dark one. green-700 is the lightest step in the family that
330
+ carries white text at 4.5:1 -- it measures 4.75, where green-600 is only
331
+ 3.20." So green-500-with-dark-ink was considered there and rejected.
332
+
333
+ Measured on the built stories, white label on the primary fill:
334
+ theme-green green-500 #15c14f 2.39:1 FAIL -> green-700 #0e8532 4.75:1
335
+ theme-red red-500 #ff0000 4.00:1 FAIL -> red-600 #d60000 5.44:1
336
+ Both are 16px/600, which is not WCAG large text, so the bar is 4.5:1.
337
+
338
+ Each theme also used to set --cs-icon-color and --cs-icon-filter, eight
339
+ declarations in all, and nothing read either one: the card icon is a <bd-img>
340
+ sized by .card-icon-wrap and given no colour treatment at all. git log -S on
341
+ the reading form finds no commit in the history where a consumer existed, so
342
+ they were written and never wired up rather than orphaned by a later change.
343
+ Removed. They were carrying a paragraph of contrast reasoning about the -500
344
+ steps that read as a live decision, and one of the two occurrences of the raw
345
+ #7c3aed literal -- the fill below is the only one left.
346
+
347
+ Worth recording so it is not re-derived: green-500 is 2.39:1 on white, which
348
+ is below the 3:1 non-text minimum, so tinting that icon green-500 would have
349
+ been a 1.4.11 question if it had ever happened. It would not have failed even
350
+ then -- compare.js renders the icon aria-hidden with alt="", which is the
351
+ exemption -- but the margin is why nothing should reach for -500 here later
352
+ on the strength of the deleted comment. */
258
353
  :host(bd-compare-section.theme-green) {
259
- --cs-bar-primary-bg: var(--color-green-500);
260
- --cs-bar-primary-fg: var(--color-neutral-0);
261
- --cs-icon-color: var(--color-green-500);
262
- --cs-icon-filter: invert(35%) sepia(72%) saturate(500%) hue-rotate(100deg) brightness(95%) contrast(95%);
354
+ --cs-bar-primary-bg: var(--status-success-fill);
355
+ --cs-bar-primary-fg: var(--status-success-on-fill);
263
356
  }
264
357
  :host(bd-compare-section.theme-red) {
265
- --cs-bar-primary-bg: var(--color-red-500);
266
- --cs-bar-primary-fg: var(--color-neutral-0);
267
- --cs-icon-color: var(--color-red-500);
268
- --cs-icon-filter: invert(22%) sepia(90%) saturate(700%) hue-rotate(345deg) brightness(95%) contrast(95%);
269
- }
358
+ --cs-bar-primary-bg: var(--status-error-fill);
359
+ --cs-bar-primary-fg: var(--status-error-on-fill);
360
+ }
361
+ /* theme-dark and theme-purple have no --status-*-fill pair to adopt -- "dark"
362
+ and "purple" are themes, not statuses -- so the migration above does not
363
+ reach them. They are settled separately, and only one of them is settled.
364
+
365
+ theme-dark is D1, approved. The label measured 3.35:1 on the blue-400 fill
366
+ and failed. Two directions had precedent here and they look different:
367
+ darken the fill to blue-500 and keep the white label (4.53:1), or keep
368
+ blue-400 and flip the ink dark (5.37:1). The approved direction is the
369
+ second, because blue-400 is deliberately the brighter step so the fill still
370
+ reads on a dark ground -- darkening it to blue-500 would have spent the one
371
+ thing this theme exists for in order to fix the ink.
372
+
373
+ The ink is --color-text-primary, and that is a responsibility rather than a
374
+ value. Two tokens resolve to neutral-900 today and only one of them means
375
+ this. --status-warning-on-fill is the ink belonging to the *warning* fill,
376
+ and a compare bar is not a warning; taking it for the value would also tie
377
+ this theme to yellow-500's future, so a warning fill later dark enough to
378
+ carry white would turn this label white on light blue. --color-text-primary
379
+ is the default ink for copy on a light ground, which is what a label on a
380
+ light blue fill is, and it is already what the secondary bar's label uses on
381
+ --cs-bar-secondary-bg two rules above. Same job in the same file, same role.
382
+
383
+ Measured on the built stories at 16px/600, which is not WCAG large text, so
384
+ the bar is 4.5:1:
385
+ theme-dark blue-400 #338bff white #ffffff 3.35:1 FAIL
386
+ -> ink #151719 5.37:1 PASS
387
+
388
+ theme-purple measures 5.70:1 and passes, so there is no accessibility
389
+ pressure; #7c3aed is a raw hex for a colour the palette does not contain.
390
+ Giving it a token or retiring the theme is a product decision, still open.
391
+
392
+ This used to add "and lint:tokens does not report it", which reads as a gate
393
+ weakness and is not one. lint:tokens carries the literal in ALLOW.hex with
394
+ the reason on the line above it -- no purple in the palette, nearest token
395
+ 34.6 deltaE away -- so the silence is a registered exception, not a miss.
396
+ Checked by probe: an unlisted off-scale hex reports hex-off-scale and fails
397
+ the build, a palette value reports hex-for-token, and this one stays quiet.
398
+ The check:color-coverage side needs its own record, which is what the
399
+ no token: marker below is; the two gates do not share an exemption. */
270
400
  :host(bd-compare-section.theme-purple) {
401
+ /* no token: the open product decision recorded above -- purple is not in the
402
+ palette, so neither the fill nor the ink that belongs to it can resolve.
403
+ green and red take --status-*-fill / --status-*-on-fill because they ARE
404
+ statuses; there is no such pair to adopt here, and giving white ink a role
405
+ while its ground stays a literal would record half a decision. */
271
406
  --cs-bar-primary-bg: #7c3aed;
272
- --cs-bar-primary-fg: var(--color-neutral-0);
273
- --cs-icon-color: #7c3aed;
274
- --cs-icon-filter: invert(25%) sepia(80%) saturate(800%) hue-rotate(255deg) brightness(90%) contrast(95%);
407
+ --cs-bar-primary-fg: var(--color-neutral-0); /* no token: as above */
275
408
  }
276
409
  :host(bd-compare-section.theme-dark) {
410
+ /* no role: nothing in the semantic set resolves to blue-400. That is the
411
+ point of the D1 decision above -- blue-400 is deliberately the brighter
412
+ step so the fill still reads on a dark ground -- so the absence is the
413
+ theme working, not a gap to close. */
277
414
  --cs-bar-primary-bg: var(--color-blue-400);
278
- --cs-bar-primary-fg: var(--color-neutral-0);
279
- --cs-icon-color: var(--color-blue-400);
280
- --cs-icon-filter: invert(65%) sepia(50%) saturate(500%) hue-rotate(195deg) brightness(105%) contrast(95%);
415
+ --cs-bar-primary-fg: var(--color-text-primary);
416
+ --cs-section-text: var(--color-text-inverse);
281
417
  }
282
418
 
283
- :host(bd-compare-section.bars-tall) { --cs-bar-height: var(--dimension-55px); }
284
- :host(bd-compare-section.bars-compact) { --cs-bar-height: var(--dimension-40px); --cs-gap: var(--spacing-24); }
419
+ :host(bd-compare-section.bars-tall) { --cs-bar-height: var(--dimension-56); }
420
+ :host(bd-compare-section.bars-compact) { --cs-bar-height: var(--dimension-40); --cs-gap: var(--spacing-24); }
285
421
  `;
@@ -1,6 +1,13 @@
1
1
  import { LitElement, html, nothing } 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";
4
+ import "../heading/heading.js";
5
+ import "../image/image.js";
6
+ import "../link/link.js";
7
+ import "../paragraph/paragraph.js";
3
8
  import compareCSS from "./compare.css.js";
9
+ import { ensembleCSS } from "../shared/ensemble.css.js";
10
+ import { matchesBelow } from "../shared/token-scales.js";
4
11
 
5
12
  function readStretchVars(el) {
6
13
  const styles = getComputedStyle(el);
@@ -49,7 +56,7 @@ class CompareBar extends LitElement {
49
56
  _animatedScore: { state: true }
50
57
  };
51
58
 
52
- static styles = [tokens, compareCSS];
59
+ static styles = [compareCSS];
53
60
 
54
61
  constructor() {
55
62
  super();
@@ -230,7 +237,7 @@ class CompareCard extends LitElement {
230
237
  chartScale : { type: Number, attribute: "chart-scale" }
231
238
  };
232
239
 
233
- static styles = [tokens, compareCSS];
240
+ static styles = [compareCSS];
234
241
 
235
242
  constructor() {
236
243
  super();
@@ -341,7 +348,7 @@ class CompareCard extends LitElement {
341
348
  if (!this.footnote) return "";
342
349
 
343
350
  if (!this.footnoteHref) {
344
- return html`<bd-p kind="xsmall" class="card-footnote">${this.footnote}</bd-p>`;
351
+ return html`<bd-p variant="xsmall" class="card-footnote">${this.footnote}</bd-p>`;
345
352
  }
346
353
 
347
354
  const splitOn = "Source ";
@@ -349,8 +356,8 @@ class CompareCard extends LitElement {
349
356
 
350
357
  if (idx === -1) {
351
358
  return html`
352
- <bd-p kind="xsmall" class="card-footnote">
353
- <bd-link href="${this.footnoteHref}" target="_blank" font-size="12px" color="var(--color-neutral-900)" underline>
359
+ <bd-p variant="xsmall" class="card-footnote">
360
+ <bd-link href="${this.footnoteHref}" target="_blank" font-size="xs" color="strong" underline>
354
361
  ${this.footnote}
355
362
  </bd-link>
356
363
  </bd-p>
@@ -361,12 +368,28 @@ class CompareCard extends LitElement {
361
368
  const linkedText = this.footnote.slice(idx + splitOn.length);
362
369
 
363
370
  return html`
364
- <bd-p kind="xsmall" class="card-footnote">
365
- ${before}<bd-link href="${this.footnoteHref}" target="_blank" font-size="12px" color="var(--color-neutral-900)" underline>${linkedText}</bd-link>
371
+ <bd-p variant="xsmall" class="card-footnote">
372
+ ${before}<bd-link href="${this.footnoteHref}" target="_blank" font-size="xs" color="strong" underline>${linkedText}</bd-link>
366
373
  </bd-p>
367
374
  `;
368
375
  }
369
376
 
377
+ /* aria-labelledby resolves against the root it is written in, and a
378
+ plain id on the <bd-h> HOST is what makes that possible: the host
379
+ sits in this shadow root beside the element referring to it, while
380
+ the <h3> bd-h renders lives in bd-h's own shadow root where an IDREF
381
+ from here cannot reach it. Name computation walks the flattened
382
+ tree, so the host carries the slotted title text.
383
+ This used to be spelled heading-id, which bd-h has never had a
384
+ property for and has never read, so nothing carried the id at all
385
+ and all 43 references across 16 Compare stories pointed at nothing.
386
+ The <section> was not merely unnamed -- a section with no accessible
387
+ name gets no region role, so the landmark did not exist.
388
+ id, not a new bd-h attribute, because modal.js already does exactly
389
+ this two components over and it works: its role=dialog resolves
390
+ aria-labelledby="modal-title" to a <bd-h id="modal-title"> and
391
+ reports the accessible name "VPN Protection". The manifest never
392
+ claimed a headingId property either. */
370
393
  _renderHorizontal() {
371
394
  return html`
372
395
  <div
@@ -382,7 +405,8 @@ class CompareCard extends LitElement {
382
405
  <div class="card-left">
383
406
  <slot name="header">
384
407
  <div class="card-left-top">
385
- ${this.iconSrc ? html`
408
+ ${this.iconSrc
409
+ ? html`
386
410
  <div class="card-icon-wrap" aria-hidden="true">
387
411
  <bd-img
388
412
  src="${this.iconSrc}"
@@ -395,12 +419,15 @@ class CompareCard extends LitElement {
395
419
  loading="eager"
396
420
  ></bd-img>
397
421
  </div>
398
- ` : ""}
399
- <bd-h as="h3" class="card-title" heading-id="${this._headingId}">${this.title}</bd-h>
422
+ `
423
+ : ""}
424
+ <bd-h as="h3" class="card-title" id="${this._headingId}">${this.title}</bd-h>
400
425
  </div>
401
- ${this.description ? html`
402
- <bd-p kind="regular" class="card-description">${this.description}</bd-p>
403
- ` : ""}
426
+ ${this.description
427
+ ? html`
428
+ <bd-p variant="regular" class="card-description">${this.description}</bd-p>
429
+ `
430
+ : ""}
404
431
  ${this._renderFootnote()}
405
432
  </slot>
406
433
  </div>
@@ -417,7 +444,7 @@ class CompareCard extends LitElement {
417
444
  render() {
418
445
  if (this.layout === "horizontal") return this._renderHorizontal();
419
446
 
420
- const isMobile = window.matchMedia("(max-width: 767px)").matches;
447
+ const isMobile = matchesBelow('md');
421
448
 
422
449
  return html`
423
450
  <div
@@ -449,11 +476,11 @@ class CompareCard extends LitElement {
449
476
  : ""}
450
477
  <div class="card-text-wrap">
451
478
  ${isMobile
452
- ? html`<bd-h as="h5" class="card-title" heading-id="${this._headingId}">${this.title}</bd-h>`
453
- : html`<bd-h as="h4" class="card-title" heading-id="${this._headingId}">${this.title}</bd-h>`}
479
+ ? html`<bd-h as="h5" class="card-title" id="${this._headingId}">${this.title}</bd-h>`
480
+ : html`<bd-h as="h4" class="card-title" id="${this._headingId}">${this.title}</bd-h>`}
454
481
  ${this.description
455
482
  ? html`
456
- <bd-p kind="${isMobile ? "small" : "regular"}" class="card-description">
483
+ <bd-p variant="${isMobile ? "small" : "regular"}" class="card-description">
457
484
  ${this.description}
458
485
  </bd-p>
459
486
  `
@@ -484,7 +511,7 @@ class CompareSection extends LitElement {
484
511
  gap : { type: String }
485
512
  };
486
513
 
487
- static styles = [tokens, compareCSS];
514
+ static styles = [ensembleCSS, compareCSS];
488
515
 
489
516
  constructor() {
490
517
  super();
@@ -527,7 +554,7 @@ class CompareSection extends LitElement {
527
554
  this._syncRaf = requestAnimationFrame(() => {
528
555
  this._syncRaf = 0;
529
556
 
530
- const isMobile = window.matchMedia("(max-width: 767px)").matches;
557
+ const isMobile = matchesBelow('md');
531
558
 
532
559
  // Get all slotted compare-card elements
533
560
  const slot = this.shadowRoot?.querySelector("slot");
@@ -560,14 +587,14 @@ class CompareSection extends LitElement {
560
587
  }
561
588
 
562
589
  render() {
563
- const isMobile = window.matchMedia("(max-width: 767px)").matches;
590
+ const isMobile = matchesBelow('md');
564
591
  const gapStyle = this.gap
565
592
  ? `--_cols: ${this.columns}; --cs-gap: ${this.gap}`
566
593
  : `--_cols: ${this.columns}`;
567
594
 
568
595
  return html`
569
596
  <section
570
- class="cs-section"
597
+ class="bd-ensemble cs-section"
571
598
  aria-labelledby="${this.title ? this._sectionTitleId : nothing}"
572
599
  >
573
600
  ${this.title || this.subtitle
@@ -575,12 +602,12 @@ class CompareSection extends LitElement {
575
602
  <div class="cs-heading">
576
603
  ${this.title
577
604
  ? isMobile
578
- ? html`<bd-h as="h4" class="cs-title" heading-id="${this._sectionTitleId}">${this.title}</bd-h>`
579
- : html`<bd-h as="h3" class="cs-title" heading-id="${this._sectionTitleId}">${this.title}</bd-h>`
605
+ ? html`<bd-h as="h4" class="cs-title" id="${this._sectionTitleId}">${this.title}</bd-h>`
606
+ : html`<bd-h as="h3" class="cs-title" id="${this._sectionTitleId}">${this.title}</bd-h>`
580
607
  : ""}
581
608
  ${this.subtitle
582
609
  ? html`
583
- <bd-p kind="${isMobile ? "small" : "regular"}" class="cs-subtitle">
610
+ <bd-p variant="${isMobile ? "small" : "regular"}" class="cs-subtitle">
584
611
  ${this.subtitle}
585
612
  </bd-p>
586
613
  `
@@ -80,7 +80,7 @@ Three Lit web components for building customizable comparison sections with anim
80
80
  | \`--cs-bar-primary-fg\` | \`var(--color-neutral-0)\` | Primary bar text colour |
81
81
  | \`--cs-bar-secondary-bg\` | \`var(--color-neutral-100)\` | Secondary bar fill colour |
82
82
  | \`--cs-bar-track-border\` | \`var(--color-neutral-200)\` | Bar track border colour |
83
- | \`--cs-bar-height\` | \`var(--dimension-48px)\` | Height of every bar |
83
+ | \`--cs-bar-height\` | \`var(--dimension-48)\` | Height of every bar |
84
84
  | \`--cs-gap\` | \`var(--spacing-32)\` | Gap between cards |
85
85
  | \`--cs-grid-max\` | \`1228px\` | Max width of the card grid |
86
86
  `
@@ -552,6 +552,46 @@ export const ThemeGreen = {
552
552
  `
553
553
  };
554
554
 
555
+ export const ThemeRed = {
556
+ name : "Theme — Red",
557
+ render: () => html`
558
+ <bd-compare-section
559
+ title="Red Theme"
560
+ gap="32px"
561
+ class="theme-red"
562
+ >
563
+ <compare-card
564
+ title="Best Protection"
565
+ description="Scale of 0–6. Higher is better."
566
+ footnote="Overall Score. July 2011 – February 2025. Source AV TEST.org"
567
+ footnote-href="${AV_TEST_URL}"
568
+ icon-src="${SHIELD_ICON}"
569
+ bar-stretch="false"
570
+ >
571
+ <compare-bar label="Bitdefender" score="5.95" max-score="6" variant="primary"></compare-bar>
572
+ <compare-bar label="Norton" score="5.59" max-score="6" variant="secondary" scale="0.93"></compare-bar>
573
+ <compare-bar label="McAfee" score="5.45" max-score="6" variant="secondary" scale="0.47"></compare-bar>
574
+ </compare-card>
575
+ </bd-compare-section>
576
+ `,
577
+ parameters: {
578
+ docs: {
579
+ description: {
580
+ story: `\`theme-red\` is a shipped variant of bd-compare-section and had no story, so
581
+ nothing rendered it and no gate opened it — which is how its primary bar label
582
+ sat at 4.00:1 unnoticed. P6C.5 measured that by applying the class at runtime,
583
+ because there was no story to measure.
584
+
585
+ It now takes \`--status-error-fill\` and \`--status-error-on-fill\`, the pair
586
+ the design system already defines for a saturated fill carrying a label, so
587
+ the label measures 5.44:1 against the 4.00:1 of the red-500 it replaced. The
588
+ other four themes are in the sibling stories; theme-dark's label was the one
589
+ remaining failure in this family and D1 has since fixed it.`
590
+ }
591
+ }
592
+ }
593
+ };
594
+
555
595
  export const ThemePurple = {
556
596
  name : "Theme — Purple",
557
597
  render: () => html`
@@ -625,7 +665,24 @@ export const ThemeDark = {
625
665
  </compare-card>
626
666
  </bd-compare-section>
627
667
  </div>
628
- `
668
+ `,
669
+ parameters: {
670
+ docs: {
671
+ description: {
672
+ story: `The primary bar label used to be white on the blue-400 fill, which measured
673
+ 3.35:1 and failed. D1 kept the fill and changed the ink: blue-400 is
674
+ deliberately the brighter blue so the fill still reads on a dark ground, and
675
+ darkening it to blue-500 to carry white would have spent the one thing this
676
+ theme exists for.
677
+
678
+ The ink is \`--color-text-primary\`, the default ink for copy on a light
679
+ ground — which is what a label on a light blue fill is, and already what the
680
+ secondary bars beside it use. It measures 5.37:1 at 16px/600, against a 4.5:1
681
+ bar. The card stays light under this theme on purpose, so only the section
682
+ title inverts.`
683
+ }
684
+ }
685
+ }
629
686
  };
630
687
 
631
688
 
@@ -666,8 +723,8 @@ export const HorizontalLayoutCustomSlot = {
666
723
  <bd-img src="${SHIELD_ICON}" alt="" width="40" height="40" fit="contain" radius="none" shadow="none" loading="eager"></bd-img>
667
724
  <bd-h as="h3">Award-Winning Protection</bd-h>
668
725
  </div>
669
- <bd-p kind="regular">Scale of 0–6. Higher is better. Independently tested since 2011.</bd-p>
670
- <bd-p kind="xsmall" style="margin-top:auto">Overall Score. July 2011 – February 2025. Source AV TEST.org</bd-p>
726
+ <bd-p variant="regular">Scale of 0–6. Higher is better. Independently tested since 2011.</bd-p>
727
+ <bd-p variant="xsmall" style="margin-top:auto">Overall Score. July 2011 – February 2025. Source AV TEST.org</bd-p>
671
728
  </div>
672
729
  <compare-bar label="Bitdefender" score="5.95" max-score="6" variant="primary"></compare-bar>
673
730
  <compare-bar label="Norton" score="5.89" max-score="6" variant="secondary" scale="0.93"></compare-bar>