@repobit/dex-system-design 0.25.12 → 1.0.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 +204 -0
  2. package/README.md +131 -8
  3. package/package.json +96 -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,400 +1,380 @@
1
1
  import { html } from 'lit';
2
+ import {
3
+ copyToken,
4
+ discoverConsumers,
5
+ discoverTokens,
6
+ docsPage,
7
+ readAllCssText,
8
+ refSection,
9
+ resolve,
10
+ usageCell
11
+ } from './story-chrome.js';
2
12
 
3
13
  export default {
4
- title: 'Design Tokens/Fonts',
5
- tags : ['autodocs'],
6
-
7
- argTypes: {
8
- sampleText: {
9
- control : 'text',
10
- description: 'Text sample to display',
11
- table : {
12
- type : { summary: 'string' },
13
- defaultValue: { summary: 'The quick brown fox jumps over the lazy dog.' }
14
- }
15
- },
16
- fontSize: {
17
- control : { type: 'select' },
18
- options : ['small', 'medium', 'large', 'xlarge'],
19
- description: 'Font size demonstration',
20
- table : {
21
- type : { summary: 'string' },
22
- defaultValue: { summary: 'medium' }
23
- }
24
- }
25
- },
26
-
27
- parameters: {
28
- docs: {
29
- description: {
30
- component: `
31
- # Font Tokens & Typography System
32
-
33
- Design tokens and typography system using IBM Plex font family.
34
-
35
- ## Overview
36
-
37
- This system uses IBM Plex as the primary font family with CSS custom properties for consistent typography across the application.
38
-
39
- ## Font Families
40
-
41
- ### IBM Plex Sans
42
- Primary font for UI text, headings, and body content.
43
-
44
- **CSS Custom Property:** \`--font-family-sans\`
45
-
46
- ### IBM Plex Mono
47
- Monospaced font for code, technical content, and data display.
48
-
49
- **CSS Custom Property:** \`--font-family-mono\`
50
-
51
- ## Usage
52
-
53
- ### In CSS
54
- \`\`\`css
55
- /* Using CSS custom properties */
56
- .element {
57
- font-family: var(--font-family-sans);
58
- font-weight: 600;
59
- }
60
-
61
- .code-block {
62
- font-family: var(--font-family-mono);
63
- font-size: 0.875rem;
64
- }
65
- \`\`\`
66
-
67
- ### In HTML with Classes
68
- \`\`\`html
69
- <div class="sans-normal">Regular text</div>
70
- <div class="sans-semibold">Semi-bold text</div>
71
- <div class="mono-regular">Code example</div>
72
- \`\`\`
73
-
74
- ## Available Weights & Styles
75
-
76
- ### IBM Plex Sans
77
- | Class | Weight | Style | Use Case |
78
- |-------|--------|-------|----------|
79
- | \`.sans-normal\` | 400 | Normal | Body text, paragraphs |
80
- | \`.sans-semibold\` | 600 | Normal | Subheadings, emphasis |
81
- | \`.sans-bold\` | 700 | Normal | Headings, titles |
82
- | \`.sans-italic\` | 400 | Italic | Quotes, highlights |
83
-
84
- ### IBM Plex Mono
85
- | Class | Weight | Style | Use Case |
86
- |-------|--------|-------|----------|
87
- | \`.mono-regular\` | 400 | Normal | Code snippets, technical text |
88
- | \`.mono-bold\` | 700 | Normal | Important code, errors |
89
- | \`.mono-italic\` | 400 | Italic | Comments, annotations |
90
-
91
- ## CSS Custom Properties
92
-
93
- \`\`\`css
94
- :root {
95
- /* Font families */
96
- --font-family-sans: 'IBM Plex Sans', Arial, sans-serif;
97
- --font-family-mono: 'IBM Plex Mono', monospace;
98
-
99
- /* Font sizes */
100
- --font-size-xs: 0.75rem; /* 12px */
101
- --font-size-sm: 0.875rem; /* 14px */
102
- --font-size-md: 1rem; /* 16px */
103
- --font-size-lg: 1.125rem; /* 18px */
104
- --font-size-xl: 1.25rem; /* 20px */
105
- --font-size-2xl: 1.5rem; /* 24px */
106
- --font-size-3xl: 1.875rem; /* 30px */
107
- --font-size-4xl: 2.25rem; /* 36px */
108
-
109
- /* Line heights */
110
- --line-height-tight: 1.25;
111
- --line-height-normal: 1.5;
112
- --line-height-relaxed: 1.75;
113
- }
114
- \`\`\`
115
-
116
- ## Best Practices
117
-
118
- 1. **Use semantic HTML**: Always use proper heading tags (h1-h6) for structure
119
- 2. **Respect hierarchy**: Maintain consistent font size hierarchy
120
- 3. **Contrast ratio**: Ensure minimum 4.5:1 contrast for normal text
121
- 4. **Line length**: Keep line length between 50-75 characters for readability
122
- 5. **Responsive**: Use relative units (rem, em) for font sizes
123
-
124
- ## Browser Support
125
-
126
- IBM Plex is supported in all modern browsers:
127
- - Chrome 60+
128
- - Firefox 55+
129
- - Safari 11+
130
- - Edge 79+
131
-
132
- ## Loading Fonts
133
-
134
- Fonts are loaded via \`@font-face\` declarations in the global styles:
135
-
136
- \`\`\`css
137
- @font-face {
138
- font-family: 'IBM Plex Sans';
139
- font-style: normal;
140
- font-weight: 400;
141
- src: url('/fonts/ibm-plex-sans-regular.woff2') format('woff2');
142
- }
143
-
144
- @font-face {
145
- font-family: 'IBM Plex Sans';
146
- font-style: italic;
147
- font-weight: 400;
148
- src: url('/fonts/ibm-plex-sans-italic.woff2') format('woff2');
149
- }
150
-
151
- @font-face {
152
- font-family: 'IBM Plex Sans';
153
- font-style: normal;
154
- font-weight: 600;
155
- src: url('/fonts/ibm-plex-sans-semibold.woff2') format('woff2');
156
- }
157
-
158
- @font-face {
159
- font-family: 'IBM Plex Sans';
160
- font-style: normal;
161
- font-weight: 700;
162
- src: url('/fonts/ibm-plex-sans-bold.woff2') format('woff2');
163
- }
164
-
165
- @font-face {
166
- font-family: 'IBM Plex Mono';
167
- font-style: normal;
168
- font-weight: 400;
169
- src: url('/fonts/ibm-plex-mono-regular.woff2') format('woff2');
170
- }
171
-
172
- @font-face {
173
- font-family: 'IBM Plex Mono';
174
- font-style: italic;
175
- font-weight: 400;
176
- src: url('/fonts/ibm-plex-mono-italic.woff2') format('woff2');
177
- }
178
-
179
- @font-face {
180
- font-family: 'IBM Plex Mono';
181
- font-style: normal;
182
- font-weight: 700;
183
- src: url('/fonts/ibm-plex-mono-bold.woff2') format('woff2');
184
- }
185
- \`\`\`
186
- `
187
- }
188
- }
189
- }
14
+ title: 'Foundations/Fonts',
15
+ tags : ['autodocs']
190
16
  };
191
17
 
192
- const Template = (args) => html`
18
+ const fontStyles = html`
193
19
  <style>
194
- section {
195
- margin-bottom: 24px;
196
- padding: 16px;
197
- background: white;
198
- border-radius: 8px;
199
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
200
- }
201
- h3 {
202
- font-family: var(--font-family-sans, 'IBM Plex Sans', Arial, sans-serif);
203
- font-weight: 600;
204
- font-size: 1.2rem;
205
- margin-bottom: 12px;
206
- color: #333;
207
- border-bottom: 2px solid #f0f0f0;
208
- padding-bottom: 8px;
209
- }
210
- .sample {
211
- margin-bottom: 12px;
212
- font-size: 1.1rem;
213
- line-height: 1.5;
214
- padding: 8px;
215
- background: #fafafa;
216
- border-radius: 4px;
217
- }
218
- .sans-normal {
219
- font-family: var(--font-family-sans, 'IBM Plex Sans', Arial, sans-serif);
220
- font-weight: 400;
221
- font-style: normal;
222
- }
223
- .sans-semibold {
224
- font-family: var(--font-family-sans, 'IBM Plex Sans', Arial, sans-serif);
225
- font-weight: 600;
226
- font-style: normal;
20
+ .face-sample {
21
+ font-size: var(--typography-fontSize-lg);
22
+ color: var(--color-neutral-900);
23
+ white-space: nowrap;
227
24
  }
228
- .sans-bold {
229
- font-family: var(--font-family-sans, 'IBM Plex Sans', Arial, sans-serif);
230
- font-weight: 700;
231
- font-style: normal;
232
- }
233
- .sans-italic {
234
- font-family: var(--font-family-sans, 'IBM Plex Sans', Arial, sans-serif);
235
- font-weight: 400;
236
- font-style: italic;
25
+
26
+ .file {
27
+ font-family: var(--typography-fontFamily-mono);
28
+ font-size: var(--typography-fontSize-xxs);
29
+ color: var(--color-neutral-500);
30
+ white-space: nowrap;
237
31
  }
238
- .mono-regular {
239
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
240
- font-weight: 400;
241
- font-style: normal;
242
- background: #f5f5f5;
243
- border-left: 3px solid #ddd;
32
+
33
+ .status {
34
+ font-family: var(--typography-fontFamily-sans);
35
+ font-size: var(--typography-fontSize-xxs);
36
+ font-weight: var(--typography-fontWeight-semibold);
37
+ padding: 1px var(--spacing-6);
38
+ border-radius: var(--radius-sm);
244
39
  }
245
- .mono-bold {
246
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
247
- font-weight: 700;
248
- font-style: normal;
249
- background: #f5f5f5;
250
- border-left: 3px solid #ddd;
40
+
41
+ .status[data-ok='true'] {
42
+ color: var(--color-green-800);
43
+ background: var(--color-green-50);
251
44
  }
252
- .mono-italic {
253
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
254
- font-weight: 400;
255
- font-style: italic;
256
- background: #f5f5f5;
257
- border-left: 3px solid #ddd;
45
+
46
+ .status[data-ok='false'] {
47
+ color: var(--color-red-800);
48
+ background: var(--color-red-50);
258
49
  }
259
- .font-info {
260
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
261
- font-size: 0.85rem;
262
- color: #666;
263
- margin-top: 4px;
264
- padding-left: 12px;
50
+
51
+ .status[data-ok='pending'] {
52
+ color: var(--color-neutral-600);
53
+ background: var(--color-neutral-50);
265
54
  }
266
- .token-display {
267
- background: #2d2d2d;
268
- color: #fff;
269
- padding: 12px;
270
- border-radius: 4px;
271
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
272
- font-size: 0.9rem;
273
- margin-top: 16px;
55
+
56
+ .stack-list {
57
+ margin: 0;
58
+ padding-left: var(--spacing-16);
59
+ font-family: var(--typography-fontFamily-mono);
60
+ font-size: var(--typography-fontSize-xxs);
61
+ color: var(--color-neutral-600);
62
+ line-height: var(--typography-lineHeight-normal);
274
63
  }
275
- .token-property {
276
- color: #9cdcfe;
64
+
65
+ .pangram {
66
+ margin: 0 0 var(--spacing-12);
67
+ color: var(--color-neutral-900);
277
68
  }
278
- .token-value {
279
- color: #ce9178;
69
+
70
+ .pangram:last-child {
71
+ margin-bottom: 0;
280
72
  }
281
73
  </style>
74
+ `;
282
75
 
283
- <section>
284
- <h3>IBM Plex Sans - Weights & Styles</h3>
285
- <div class="sample sans-normal">
286
- ${args.sampleText}
287
- <div class="font-info">font-family: var(--font-family-sans); font-weight: 400;</div>
288
- </div>
289
- <div class="sample sans-semibold">
290
- ${args.sampleText}
291
- <div class="font-info">font-family: var(--font-family-sans); font-weight: 600;</div>
292
- </div>
293
- <div class="sample sans-bold">
294
- ${args.sampleText}
295
- <div class="font-info">font-family: var(--font-family-sans); font-weight: 700;</div>
296
- </div>
297
- <div class="sample sans-italic">
298
- ${args.sampleText}
299
- <div class="font-info">font-family: var(--font-family-sans); font-weight: 400; font-style: italic;</div>
300
- </div>
301
- </section>
76
+ /**
77
+ * Reads the @font-face rules actually registered in the document. These only
78
+ * exist because fonts.css is loaded in document scope — @font-face inside a
79
+ * shadow root never reaches the document font set.
80
+ */
81
+ function readFontFaces(ownFamilies) {
82
+ const faces = [];
83
+ for (const sheet of Array.from(document.styleSheets)) {
84
+ let rules;
85
+ try {
86
+ rules = sheet.cssRules;
87
+ } catch {
88
+ continue;
89
+ }
90
+ for (const rule of Array.from(rules)) {
91
+ if (rule.constructor.name !== 'CSSFontFaceRule' && !(rule.style && rule.type === 5)) continue;
92
+ const family = (rule.style.getPropertyValue('font-family') || '').replace(/['"]/g, '').trim();
93
+ // Storybook registers its own UI faces in this document; only report the
94
+ // families our own --typography-fontFamily-* tokens actually ask for.
95
+ if (!family || !ownFamilies.has(family)) continue;
96
+ faces.push({
97
+ family,
98
+ weight : (rule.style.getPropertyValue('font-weight') || '400').trim(),
99
+ style : (rule.style.getPropertyValue('font-style') || 'normal').trim(),
100
+ display: (rule.style.getPropertyValue('font-display') || '—').trim(),
101
+ file : ((rule.style.getPropertyValue('src') || '').match(/([^/'")]+\.woff2?)/) || [null, '—'])[1]
102
+ });
103
+ }
104
+ }
105
+ return faces;
106
+ }
302
107
 
303
- <section>
304
- <h3>IBM Plex Mono - Weights & Styles</h3>
305
- <div class="sample mono-regular">
306
- ${args.sampleText}
307
- <div class="font-info">font-family: var(--font-family-mono); font-weight: 400;</div>
308
- </div>
309
- <div class="sample mono-bold">
310
- ${args.sampleText}
311
- <div class="font-info">font-family: var(--font-family-mono); font-weight: 700;</div>
312
- </div>
313
- <div class="sample mono-italic">
314
- ${args.sampleText}
315
- <div class="font-info">font-family: var(--font-family-mono); font-weight: 400; font-style: italic;</div>
108
+ function faceTable(family, faces) {
109
+ const sorted = [...faces].sort(
110
+ (a, b) => Number(a.weight) - Number(b.weight) || a.style.localeCompare(b.style)
111
+ );
112
+
113
+ return html`
114
+ <div class="table-scroll">
115
+ <table>
116
+ <caption>${family} faces</caption>
117
+ <thead>
118
+ <tr>
119
+ <th scope="col">Weight</th>
120
+ <th scope="col">Style</th>
121
+ <th scope="col">Sample</th>
122
+ <th scope="col">File</th>
123
+ <th scope="col">font-display</th>
124
+ <th scope="col">Loaded</th>
125
+ </tr>
126
+ </thead>
127
+ <tbody>
128
+ ${sorted.map(face => {
129
+ const spec = `${face.style} ${face.weight} 16px "${family}"`;
130
+ const id = `${family}|${face.weight}|${face.style}`;
131
+ // Faces are fetched lazily, so check() alone only reports what the
132
+ // page happens to have used. Ask for the face, then report whether
133
+ // the file actually resolved.
134
+ queueMicrotask(async () => {
135
+ let ok;
136
+ try {
137
+ const loadedFaces = await document.fonts.load(spec);
138
+ ok = loadedFaces.length > 0;
139
+ } catch {
140
+ ok = false;
141
+ }
142
+ for (const node of document.querySelectorAll(`[data-face="${id}"]`)) {
143
+ node.dataset.ok = String(ok);
144
+ node.textContent = ok ? 'yes' : 'failed';
145
+ }
146
+ });
147
+ return html`
148
+ <tr>
149
+ <td class="num">${face.weight}</td>
150
+ <td class="mono muted">${face.style}</td>
151
+ <td
152
+ class="face-sample"
153
+ style="font-family: '${family}'; font-weight: ${face.weight}; font-style: ${face.style}"
154
+ >
155
+ Handgloves 123
156
+ </td>
157
+ <td class="file">${face.file}</td>
158
+ <td class="mono muted">${face.display}</td>
159
+ <td>
160
+ <span class="status" data-face=${id} data-ok="pending">checking</span>
161
+ </td>
162
+ </tr>
163
+ `;
164
+ })}
165
+ </tbody>
166
+ </table>
316
167
  </div>
317
- </section>
168
+ `;
169
+ }
318
170
 
319
- <section>
320
- <h3>CSS Custom Properties</h3>
321
- <div class="token-display">
322
- <span class="token-property">--font-family-sans</span>: <span class="token-value">'IBM Plex Sans', Arial, sans-serif</span>;<br>
323
- <span class="token-property">--font-family-mono</span>: <span class="token-value">'IBM Plex Mono', monospace</span>;<br><br>
324
- <span class="token-property">.sans-normal</span> {<br>
325
- &nbsp;&nbsp;<span class="token-property">font-family</span>: <span class="token-value">var(--font-family-sans)</span>;<br>
326
- &nbsp;&nbsp;<span class="token-property">font-weight</span>: <span class="token-value">400</span>;<br>
327
- }<br><br>
328
- <span class="token-property">.mono-regular</span> {<br>
329
- &nbsp;&nbsp;<span class="token-property">font-family</span>: <span class="token-value">var(--font-family-mono)</span>;<br>
330
- &nbsp;&nbsp;<span class="token-property">font-weight</span>: <span class="token-value">400</span>;<br>
331
- }
332
- </div>
333
- </section>
334
- `;
171
+ export const Default = () => {
172
+ const cssText = readAllCssText();
173
+ const consumers = discoverConsumers(cssText);
174
+
175
+ const familyTokenNames = discoverTokens(
176
+ cssText,
177
+ /(--typography-fontFamily-[a-zA-Z0-9-]+)\s*:/g
178
+ ).map(match => match[1]);
179
+
180
+ // the first entry of each stack is the face we actually ship
181
+ const ownFamilies = new Set(
182
+ familyTokenNames
183
+ .map(token => resolve(token).split(',')[0].replace(/['"]/g, '').trim())
184
+ .filter(Boolean)
185
+ );
186
+ const faces = readFontFaces(ownFamilies);
187
+
188
+ const byFamily = new Map();
189
+ for (const face of faces) {
190
+ if (!byFamily.has(face.family)) byFamily.set(face.family, []);
191
+ byFamily.get(face.family).push(face);
192
+ }
335
193
 
336
- export const Default = Template.bind({});
337
- Default.args = {
338
- sampleText: 'The quick brown fox jumps over the lazy dog.',
339
- fontSize : 'medium'
194
+ const familyTokens = familyTokenNames;
195
+
196
+ return html`
197
+ ${fontStyles}
198
+ ${docsPage({
199
+ title: 'Fonts',
200
+ lede : html`The families the design system ships, and the individual faces registered in the
201
+ document. Both tables are read from the loaded CSS at render time.`,
202
+ note: html`Faces are registered by <code>src/tokens/fonts.css</code>, which must be loaded
203
+ in <b>document scope</b>. An <code>@font-face</code> rule inside a component's shadow root
204
+ never reaches <code>document.fonts</code>, so it silently does nothing.
205
+ <b>Loaded</b> asks the browser to fetch each face and reports whether the file actually
206
+ resolved, so a broken <code>url()</code> shows up here as <b>failed</b>.`,
207
+ children: html`
208
+ ${refSection({
209
+ name : 'Family tokens',
210
+ meta : `${familyTokens.length} tokens · --typography-fontFamily-*`,
211
+ children: html`
212
+ <div class="table-scroll">
213
+ <table>
214
+ <caption>Font family tokens</caption>
215
+ <thead>
216
+ <tr>
217
+ <th scope="col">Token</th>
218
+ <th scope="col">Sample</th>
219
+ <th scope="col">Stack</th>
220
+ <th scope="col">Used by</th>
221
+ </tr>
222
+ </thead>
223
+ <tbody>
224
+ ${familyTokens.map(token => {
225
+ const stack = resolve(token)
226
+ .split(',')
227
+ .map(part => part.trim())
228
+ .filter(Boolean);
229
+ return html`
230
+ <tr>
231
+ <td>${copyToken(token)}</td>
232
+ <td class="face-sample" style="font-family: var(${token})">
233
+ Handgloves 123
234
+ </td>
235
+ <td>
236
+ <ol class="stack-list">
237
+ ${stack.map(part => html`<li>${part}</li>`)}
238
+ </ol>
239
+ </td>
240
+ <td>${usageCell(consumers.get(token))}</td>
241
+ </tr>
242
+ `;
243
+ })}
244
+ </tbody>
245
+ </table>
246
+ </div>
247
+ `
248
+ })}
249
+ ${[...byFamily].map(([family, list]) =>
250
+ refSection({
251
+ name : family,
252
+ meta : `${list.length} faces registered`,
253
+ children: faceTable(family, list)
254
+ })
255
+ )}
256
+ ${byFamily.size
257
+ ? ''
258
+ : html`<div class="docs-note">
259
+ No <code>@font-face</code> rules found in the document. Check that
260
+ <code>src/tokens/fonts.css</code> is imported in
261
+ <code>.storybook/preview.js</code>.
262
+ </div>`}
263
+ `
264
+ })}
265
+ `;
340
266
  };
341
267
 
342
- export const CustomText = Template.bind({});
343
- CustomText.args = {
344
- sampleText: 'Typography is the craft of arranging type to make language visible.',
345
- fontSize : 'medium'
268
+ export const CustomText = {
269
+ args: {
270
+ text: 'The quick brown fox jumps over the lazy dog'
271
+ },
272
+ argTypes: {
273
+ text: { control: 'text', name: 'Specimen text' }
274
+ },
275
+ render: ({ text }) => {
276
+ const weights = ['300', '400', '500', '600', '700'];
277
+
278
+ return html`
279
+ ${fontStyles}
280
+ ${docsPage({
281
+ title : 'Specimen',
282
+ lede : 'Set your own text to judge the families at every weight and in italic.',
283
+ children: ['--typography-fontFamily-sans', '--typography-fontFamily-mono'].map(token =>
284
+ refSection({
285
+ name : token.replace('--typography-fontFamily-', ''),
286
+ meta : token,
287
+ children: html`
288
+ <div class="specimen">
289
+ ${weights.map(
290
+ weight => html`
291
+ <p
292
+ class="pangram"
293
+ style="font-family: var(${token}); font-weight: ${weight}; font-size: var(--typography-fontSize-lg)"
294
+ >
295
+ <span class="file">${weight}</span> ${text}
296
+ </p>
297
+ <p
298
+ class="pangram"
299
+ style="font-family: var(${token}); font-weight: ${weight}; font-style: italic; font-size: var(--typography-fontSize-lg)"
300
+ >
301
+ <span class="file">${weight} italic</span> ${text}
302
+ </p>
303
+ `
304
+ )}
305
+ </div>
306
+ `
307
+ })
308
+ )
309
+ })}
310
+ `;
311
+ }
346
312
  };
347
313
 
348
314
  export const TechnicalExample = () => html`
349
- <style>
350
- .code-example {
351
- background: #1e1e1e;
352
- color: #d4d4d4;
353
- padding: 20px;
354
- border-radius: 8px;
355
- font-family: var(--font-family-mono, 'IBM Plex Mono', monospace);
356
- font-size: 0.9rem;
357
- line-height: 1.6;
358
- margin: 16px 0;
359
- }
360
- .code-comment { color: #6a9955; }
361
- .code-keyword { color: #569cd6; }
362
- .code-string { color: #ce9178; }
363
- .code-function { color: #dcdcaa; }
364
- .code-number { color: #b5cea8; }
365
- </style>
366
-
367
- <section style="background: white; padding: 20px; border-radius: 8px;">
368
- <h3 class="sans-semibold">Real-world Usage Example</h3>
369
-
370
- <div class="sans-normal" style="margin-bottom: 16px;">
371
- This is regular body text using IBM Plex Sans. It's used for most UI text and content.
372
- </div>
373
-
374
- <div class="sans-bold" style="margin-bottom: 16px;">
375
- This is a bold heading using IBM Plex Sans. Perfect for section titles.
376
- </div>
377
-
378
- <h4 class="sans-semibold" style="margin: 16px 0 8px 0;">Code Example:</h4>
379
- <div class="code-example">
380
- <span class="code-comment">// JavaScript function example</span><br>
381
- <span class="code-keyword">function</span> <span class="code-function">calculateTotal</span>(<span class="code-keyword">items</span>) {<br>
382
- &nbsp;&nbsp;<span class="code-keyword">let</span> total = <span class="code-number">0</span>;<br>
383
- &nbsp;&nbsp;<span class="code-keyword">for</span> (<span class="code-keyword">let</span> item <span class="code-keyword">of</span> items) {<br>
384
- &nbsp;&nbsp;&nbsp;&nbsp;total += item.price * item.quantity;<br>
385
- &nbsp;&nbsp;}<br>
386
- &nbsp;&nbsp;<span class="code-keyword">return</span> total.<span class="code-function">toFixed</span>(<span class="code-number">2</span>);<br>
387
- }<br><br>
388
- <span class="code-comment">// Configuration object</span><br>
389
- <span class="code-keyword">const</span> config = {<br>
390
- &nbsp;&nbsp;apiUrl: <span class="code-string">'https://api.example.com/v1'</span>,<br>
391
- &nbsp;&nbsp;timeout: <span class="code-number">5000</span>,<br>
392
- &nbsp;&nbsp;retries: <span class="code-number">3</span><br>
393
- };
394
- </div>
395
-
396
- <div class="sans-italic" style="margin-top: 16px; color: #666;">
397
- Note: Code blocks use IBM Plex Mono for better readability of technical content.
398
- </div>
399
- </section>
400
- `;
315
+ ${fontStyles}
316
+ ${docsPage({
317
+ title: 'Monospace in context',
318
+ lede : html`The mono family carries code and any tabular figures. It is applied through
319
+ <code>--typography-code-inline-*</code> and <code>--typography-code-block-*</code> rather
320
+ than by naming the family directly.`,
321
+ children: html`
322
+ ${refSection({
323
+ name : 'Inline code',
324
+ meta : '--typography-code-inline-*',
325
+ children: html`
326
+ <div class="specimen">
327
+ <p style="font: var(--typography-body-regular-fontWeight) var(--typography-body-regular-fontSize)/var(--typography-body-regular-lineHeight) var(--typography-fontFamily-sans); margin: 0">
328
+ Apply a token with
329
+ <code
330
+ style="font-family: var(--typography-code-inline-fontFamily); font-size: var(--typography-code-inline-fontSize); background: var(--color-neutral-50); padding: 0 var(--spacing-4); border-radius: var(--radius-sm)"
331
+ >var(--color-blue-500)</code
332
+ >
333
+ rather than a raw hex value.
334
+ </p>
335
+ </div>
336
+ `
337
+ })}
338
+ ${refSection({
339
+ name : 'Code block',
340
+ meta : '--typography-code-block-*',
341
+ children: html`
342
+ <div class="specimen">
343
+ <pre
344
+ style="font-family: var(--typography-code-block-fontFamily); font-size: var(--typography-code-block-fontSize); line-height: var(--typography-code-block-lineHeight); margin: 0; overflow-x: auto"
345
+ >
346
+ .button {
347
+ padding: var(--spacing-12) var(--spacing-20);
348
+ font-size: var(--typography-fontSize-base);
349
+ border-radius: var(--radius-md);
350
+ }</pre
351
+ >
352
+ </div>
353
+ `
354
+ })}
355
+ ${refSection({
356
+ name : 'Tabular figures',
357
+ meta : 'font-variant-numeric: tabular-nums',
358
+ children: html`
359
+ <div class="specimen">
360
+ <table>
361
+ <caption>Tabular figures</caption>
362
+ <thead>
363
+ <tr>
364
+ <th scope="col">Plan</th>
365
+ <th scope="col">Devices</th>
366
+ <th scope="col">Price</th>
367
+ </tr>
368
+ </thead>
369
+ <tbody>
370
+ <tr><td>Antivirus Plus</td><td class="num">3</td><td class="num">29.99</td></tr>
371
+ <tr><td>Internet Security</td><td class="num">10</td><td class="num">104.99</td></tr>
372
+ <tr><td>Ultimate Security</td><td class="num">25</td><td class="num">1109.99</td></tr>
373
+ </tbody>
374
+ </table>
375
+ </div>
376
+ `
377
+ })}
378
+ `
379
+ })}
380
+ `;