@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
@@ -0,0 +1,51 @@
1
+ import { css } from 'lit';
2
+
3
+ /**
4
+ * What a textarea adds to the shared field chrome.
5
+ *
6
+ * Everything else — label, sizes, padding, radius, states, help text, focus
7
+ * ring — comes from shared/form-field.css.js, the same sheet bd-custom-input,
8
+ * bd-custom-dropdown and bd-custom-input-clipboard read.
9
+ *
10
+ * Only two things genuinely differ for a control that holds more than one
11
+ * line, and both are here rather than in the shared sheet so the other three
12
+ * are unaffected.
13
+ */
14
+ export const textareaCSS = css`
15
+ /* 1. Height comes from the rows attribute, not from the size.
16
+
17
+ The shared sheet pins a height per size — that is what makes an input the
18
+ same height as a button of the same size — and applying it here would
19
+ collapse a four-row textarea to one line. The size still sets padding,
20
+ type step and radius, so the control still matches its siblings on every
21
+ axis where matching means anything.
22
+
23
+ Do not expect a rows="1" textarea to equal a one-line input: the two
24
+ compute height by different formulas and land about 6px apart at md.
25
+ Roughly 4px of that is the taller line box below, and the other 2px is
26
+ the borders — the shared sheet's fixed height is border-box, so its 1px
27
+ borders sit inside the number, while a height derived from rows adds them
28
+ on top. Closing the gap would mean forcing tight on a box of running
29
+ text, which is the one thing this component should not do. */
30
+ textarea.form-input-bd {
31
+ height: auto;
32
+ resize: vertical;
33
+ }
34
+
35
+ /* 2. line-height normal, not tight.
36
+
37
+ The shared sheet uses tight because a single-line field's height is
38
+ derived from it and because the select was clipping its own descenders.
39
+ Neither applies to a box of running text, where 1.25 is cramped to read
40
+ and 1.5 is the body-copy value the rest of the system sets. */
41
+ textarea.form-input-bd {
42
+ /* no role: the comment above this rule has the argument, and it is a
43
+ leading decision rather than a type role -- the size comes from the
44
+ field's own size step. 1.25 does not clip here (measured: 104/104 on
45
+ wrapped text), so this is a legibility call for a box of running text,
46
+ not a fix for a clipped box. */
47
+ line-height: var(--typography-lineHeight-normal);
48
+ }
49
+ `;
50
+
51
+ export default textareaCSS;
@@ -0,0 +1,136 @@
1
+ import { LitElement, html, nothing } from 'lit';
2
+ // Installs the design tokens into document scope; see src/tokens/tokens.js
3
+ import '../../tokens/tokens.js';
4
+ import { formFieldCSS } from '../shared/form-field.css.js';
5
+ import { textareaCSS } from './textarea.css.js';
6
+ import { resolveSize } from '../shared/prop-vocabulary.js';
7
+
8
+ // ids are scoped to the shadow root, so fixed ones are safe here
9
+ const TEXTAREA_ID = 'custom-textarea';
10
+ const HELP_ID = 'custom-textarea-help';
11
+
12
+ /**
13
+ * The fourth form control, and the one the shared-stylesheet work missed.
14
+ *
15
+ * bd-custom-input, bd-custom-dropdown and bd-custom-input-clipboard were
16
+ * collapsed onto shared/form-field.css.js; this one kept a stylesheet of its
17
+ * own and, with it, no size axis, no label, no help text and no validation
18
+ * states. It pinned the `md` radius and a fixed 16px type, so the Control
19
+ * Sizes story — which renders it in all three size rows under a caption that
20
+ * names it — showed it disagreeing with the other three at `sm` and `lg`.
21
+ *
22
+ * It now composes the same sheet and takes the same props, so a textarea and
23
+ * an input of the same size are the same control with a different number of
24
+ * lines.
25
+ */
26
+ export class BdTextarea extends LitElement {
27
+ static styles = [formFieldCSS, textareaCSS];
28
+
29
+ static properties = {
30
+ label : { type: String },
31
+ size : { type: String, reflect: true },
32
+ value : { type: String },
33
+ placeholder: { type: String },
34
+ rows : { type: Number },
35
+ required : { type: Boolean },
36
+ tooltip : { type: String },
37
+ invalid : { type: Boolean },
38
+ validated : { type: Boolean, reflect: true },
39
+ disabled : { type: Boolean, reflect: true },
40
+ name : { type: String }
41
+ };
42
+
43
+ constructor() {
44
+ super();
45
+ this.label = '';
46
+ this.size = 'md';
47
+ this.value = '';
48
+ this.placeholder = '';
49
+ this.rows = 4;
50
+ this.required = false;
51
+ this.tooltip = '';
52
+ this.invalid = false;
53
+ this.validated = false;
54
+ this.disabled = false;
55
+ this.name = '';
56
+ }
57
+
58
+ willUpdate(changed) {
59
+ if (changed.has('size')) this.size = resolveSize(this.size);
60
+ }
61
+
62
+ /* The native event retargets to the host, which has no value property of its
63
+ own, so the host mirrors it — the same arrangement bd-custom-input uses.
64
+ `bd-input` keeps its original detail shape (the raw string) because
65
+ consumers already read it that way. */
66
+ _onInput(e) {
67
+ this.value = e.target.value;
68
+ this.dispatchEvent(new CustomEvent('bd-input', {
69
+ detail : e.target.value,
70
+ bubbles : true,
71
+ composed: true
72
+ }));
73
+ }
74
+
75
+ _onChange(e) {
76
+ this.value = e.target.value;
77
+ this.dispatchEvent(new CustomEvent('bd-change', {
78
+ detail : { value: this.value, name: this.name },
79
+ bubbles : true,
80
+ composed: true
81
+ }));
82
+ }
83
+
84
+ render() {
85
+ const inputClass = { sm: 'input-sm', md: 'input-md', lg: 'input-lg' }[this.size];
86
+ const labelClass = { sm: 'label-sm', md: 'label-md', lg: 'label-lg' }[this.size];
87
+
88
+ const stateClass = this.disabled
89
+ ? 'form-input--disabled'
90
+ : this.invalid
91
+ ? 'form-input--error'
92
+ : this.validated
93
+ ? 'form-input--success'
94
+ : '';
95
+
96
+ const helpTextClass = this.invalid
97
+ ? 'form-help-text--error'
98
+ : this.validated
99
+ ? 'form-help-text--success'
100
+ : 'form-help-text--default';
101
+
102
+ return html`
103
+ <div class="input-container">
104
+ ${this.label
105
+ ? html`<label for="${TEXTAREA_ID}" class="form-label ${labelClass}">
106
+ ${this.label}
107
+ ${this.required
108
+ ? html`<span class="form-label__required" aria-hidden="true">*</span>`
109
+ : ''}
110
+ </label>`
111
+ : nothing}
112
+ <textarea
113
+ id="${TEXTAREA_ID}"
114
+ class="form-input-bd ${inputClass} ${stateClass}"
115
+ rows=${this.rows}
116
+ name="${this.name || nothing}"
117
+ placeholder=${this.placeholder || nothing}
118
+ ?required=${this.required}
119
+ ?disabled=${this.disabled}
120
+ aria-invalid="${this.invalid ? 'true' : nothing}"
121
+ aria-describedby="${this.tooltip ? HELP_ID : nothing}"
122
+ .value=${this.value}
123
+ @input=${this._onInput}
124
+ @change=${this._onChange}
125
+ ></textarea>
126
+ ${this.tooltip
127
+ ? html`<div id="${HELP_ID}" class="form-help-text ${helpTextClass}">
128
+ ${this.tooltip}
129
+ </div>`
130
+ : nothing}
131
+ </div>
132
+ `;
133
+ }
134
+ }
135
+
136
+ customElements.define('bd-textarea', BdTextarea);
@@ -0,0 +1,191 @@
1
+ import { html } from 'lit';
2
+ import './textarea.js';
3
+ import '../label/label.js';
4
+
5
+ export default {
6
+ title : 'Atoms/Textarea',
7
+ tags : ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: `
12
+ **BdTextarea** is a multi-line text field. It is the fourth control in the form
13
+ family, alongside bd-custom-input, bd-custom-dropdown and
14
+ bd-custom-input-clipboard, and it reads the same stylesheet as all three — so a
15
+ textarea and an input of the same size are the same control with a different
16
+ number of lines.
17
+
18
+ ### Usage
19
+ \`\`\`html
20
+ <bd-textarea label="What happened?" placeholder="Tell us more"></bd-textarea>
21
+ <bd-textarea label="Notes" size="lg" rows="8"></bd-textarea>
22
+ <bd-textarea label="Reason" required invalid tooltip="This field is required."></bd-textarea>
23
+ <bd-textarea label="Summary" disabled value="Read only"></bd-textarea>
24
+
25
+ <bd-textarea @bd-input=\${e => console.log(e.detail)}></bd-textarea>
26
+ \`\`\`
27
+
28
+ ### Size
29
+ \`size\` sets padding, type step and corner radius from the same
30
+ \`--form-input-*\` aliases the other three controls read, so a textarea sits
31
+ correctly in a row beside a button or an input of the same size.
32
+
33
+ **Height is the exception.** The other three derive a fixed height from padding,
34
+ type step and line-height — that is what keeps a field and a button the same
35
+ height. A textarea's height comes from \`rows\` instead, because a fixed height
36
+ would collapse it to one line. It also sets \`line-height: normal\` rather than
37
+ \`tight\`: 1.25 is right for a single line whose height is derived from it, and
38
+ cramped for a box of running text.
39
+
40
+ ### Label
41
+ The field renders its own label when you pass one, with the same typography and
42
+ the same 4px gap as bd-label — pick either, not both. Left empty, no label
43
+ element is rendered at all, and the control is then **unlabelled**: a screen
44
+ reader announces only "edit, multiline". Pair it with a label if you do not use
45
+ the prop:
46
+
47
+ \`\`\`html
48
+ <bd-label id="msg-label">What happened?</bd-label>
49
+ <bd-textarea aria-labelledby="msg-label"></bd-textarea>
50
+ \`\`\`
51
+
52
+ A \`placeholder\` is not a substitute. It disappears as soon as the user types,
53
+ it is not reliably announced, and its contrast is set by the browser.
54
+
55
+ ### Not form-associated
56
+ The \`<textarea>\` lives in this element's shadow root, so it does not
57
+ participate in an outer \`<form>\`. \`name\` is carried on the events for your own
58
+ submit handling; read the value from \`bd-input\`, \`bd-change\` or the \`value\`
59
+ property.
60
+
61
+ ### Events
62
+ \`bd-input\` — every keystroke, \`detail\` is the current value (a string).
63
+ \`bd-change\` — on commit, \`detail\` is \`{ value, name }\`.
64
+
65
+ The vertical resize handle is left enabled, so a user can grow the field beyond
66
+ \`rows\`.
67
+ `
68
+ }
69
+ }
70
+ },
71
+ argTypes: {
72
+ label: {
73
+ control : 'text',
74
+ description: 'The field\'s own label. Empty renders no label element.',
75
+ table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Content' }
76
+ },
77
+ size: {
78
+ control : { type: 'inline-radio' },
79
+ options : ['sm', 'md', 'lg'],
80
+ description: 'Padding, type step and radius. Height comes from `rows`.',
81
+ table : { type: { summary: "'sm'|'md'|'lg'" }, defaultValue: { summary: "'md'" }, category: 'Appearance' }
82
+ },
83
+ value: {
84
+ control : 'text',
85
+ description: 'The text content.',
86
+ table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Data' }
87
+ },
88
+ placeholder: {
89
+ control : 'text',
90
+ description: 'Hint shown while empty. Not a replacement for a label.',
91
+ table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Content' }
92
+ },
93
+ rows: {
94
+ control : 'number',
95
+ description: 'Visible rows before scrolling. The user can still resize vertically.',
96
+ table : { type: { summary: 'number' }, defaultValue: { summary: '4' }, category: 'Appearance' }
97
+ },
98
+ tooltip: {
99
+ control : 'text',
100
+ description: 'Help text below the control, and the field\'s accessible description.',
101
+ table : { type: { summary: 'string' }, defaultValue: { summary: "''" }, category: 'Content' }
102
+ },
103
+ required: {
104
+ control : 'boolean',
105
+ description: 'Marks the field required and shows the asterisk on the label.',
106
+ table : { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'State' }
107
+ },
108
+ invalid: {
109
+ control : 'boolean',
110
+ description: 'Error tint and border, and `aria-invalid` on the control.',
111
+ table : { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'State' }
112
+ },
113
+ validated: {
114
+ control : 'boolean',
115
+ description: 'Success tint and border.',
116
+ table : { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'State' }
117
+ },
118
+ disabled: {
119
+ control : 'boolean',
120
+ description: 'Blocks editing.',
121
+ table : { type: { summary: 'boolean' }, defaultValue: { summary: 'false' }, category: 'State' }
122
+ }
123
+ }
124
+ };
125
+
126
+ export const Default = () => html`
127
+ <bd-textarea label="What happened?" placeholder="Tell us what happened"></bd-textarea>
128
+ `;
129
+
130
+ export const Sizes = {
131
+ name : 'All Sizes',
132
+ render: () => html`
133
+ <div style="display: grid; gap: var(--spacing-24); max-width: 24rem">
134
+ <bd-textarea size="sm" label="Small" rows="2" placeholder="sm"></bd-textarea>
135
+ <bd-textarea size="md" label="Medium" rows="2" placeholder="md"></bd-textarea>
136
+ <bd-textarea size="lg" label="Large" rows="2" placeholder="lg"></bd-textarea>
137
+ </div>
138
+ `,
139
+ parameters: {
140
+ docs: {
141
+ description: {
142
+ story: `Padding, type step and radius step with the size — 4px / 6px / 8px corners,
143
+ matching a button or an input of the same size. Height stays with \`rows\`.`
144
+ }
145
+ }
146
+ }
147
+ };
148
+
149
+ export const States = {
150
+ name : 'All Validation States',
151
+ render: () => html`
152
+ <div style="display: grid; gap: var(--spacing-24); max-width: 24rem">
153
+ <bd-textarea label="Default" rows="2" tooltip="A neutral hint."></bd-textarea>
154
+ <bd-textarea label="Required" rows="2" required tooltip="Tell us what happened."></bd-textarea>
155
+ <bd-textarea label="Invalid" rows="2" invalid tooltip="This field is required."></bd-textarea>
156
+ <bd-textarea label="Validated" rows="2" validated value="Looks good" tooltip="Thanks."></bd-textarea>
157
+ <bd-textarea label="Disabled" rows="2" disabled value="Cannot edit this"></bd-textarea>
158
+ </div>
159
+ `,
160
+ parameters: {
161
+ docs: {
162
+ description: {
163
+ story: `The same four state tints the other form controls use, from the same
164
+ \`--form-input-*\` tokens. The help text takes the matching tone.`
165
+ }
166
+ }
167
+ }
168
+ };
169
+
170
+ export const Disabled = () => html`
171
+ <bd-textarea label="Summary" disabled value="Cannot edit this"></bd-textarea>
172
+ `;
173
+
174
+ export const NoLabel = {
175
+ name : 'No label (pair it yourself)',
176
+ render: () => html`
177
+ <div style="max-width: 24rem">
178
+ <bd-label id="msg-label">What happened?</bd-label>
179
+ <bd-textarea aria-labelledby="msg-label" rows="3"></bd-textarea>
180
+ </div>
181
+ `,
182
+ parameters: {
183
+ docs: {
184
+ description: {
185
+ story: `With no \`label\`, no label element is rendered — so the control is
186
+ unlabelled unless you connect one. This is the composed alternative to the
187
+ built-in label, and it should sit at the same rhythm as the built-in version.`
188
+ }
189
+ }
190
+ }
191
+ };
@@ -0,0 +1,75 @@
1
+ import { css } from 'lit';
2
+
3
+ /** Styled entirely from design tokens — see npm run lint:tokens. */
4
+ export const tooltipCSS = css`
5
+ :host { display: inline-block; position: relative; }
6
+
7
+ .bubble {
8
+ position: absolute;
9
+ bottom: calc(100% + var(--spacing-8));
10
+ left: 50%;
11
+ /* --bd-tooltip-shift is set by the component when the bubble would paint
12
+ outside the viewport, and is 0 the rest of the time. Centring alone
13
+ cannot stay on screen: the bubble is centred on its trigger and is wider
14
+ than it, so a trigger near an edge puts part of the bubble at negative x
15
+ whatever its width. Bounding the width in P6B reduced that overhang and
16
+ did not remove it -- the Default story measured 45px outside before and
17
+ after. There is no CSS that can read where the trigger sits. */
18
+ transform: translateX(calc(-50% + var(--bd-tooltip-shift, 0px)));
19
+ /* --z-tooltip is the top rung, and this is a real change: the bubble used
20
+ to sit on 1, which lost to anything raised above it in the same stacking
21
+ context. A tooltip opened from inside a dialog has to clear the dialog,
22
+ which is the reason the ladder puts it above --z-modal. */
23
+ z-index: var(--z-tooltip);
24
+ /* The bubble used to be white-space: nowrap with no width bound, so its
25
+ text became one line as long as the string: measured at 606px for the
26
+ LongText story, identical at 320 and at 1440, because a single line does
27
+ not care how much room it has. Anything past the container edge was
28
+ unreachable -- there is nothing to scroll to a bubble that is painted
29
+ outside the page box.
30
+
31
+ Bounded the way the anchor-nav dropdown panel bounds its own height:
32
+ min() of a viewport-relative value and a content cap, both from tokens.
33
+ --content-width-readable is the measure this package already uses for
34
+ "text somebody has to read". */
35
+ white-space: normal;
36
+ /* max-width has to mean the whole bubble, because what has to fit inside
37
+ the viewport is the box the reader sees. Without this the cap applied to
38
+ the content and the 4/8 padding was added outside it, so the border box
39
+ came out 16px wider than the number the min() had just worked out:
40
+ measured at 320, a cap of 288 produced a 304px bubble, and at 375 a cap
41
+ of 343 produced 359. The placement code then had 16px less room than the
42
+ stylesheet thought it had given it. */
43
+ box-sizing: border-box;
44
+ /* A single unbreakable token longer than the cap would otherwise paint
45
+ outside the bubble rather than wrap inside it. break-word only breaks
46
+ when there is no break opportunity, so ordinary prose is unaffected. */
47
+ overflow-wrap: break-word;
48
+ /* width: max-content is load-bearing. The bubble's containing block is the
49
+ host, which is only as wide as the trigger, so an absolutely positioned
50
+ bubble with left: 50% has half a trigger to lay out in: removing nowrap
51
+ without this collapsed the LongText bubble to 88px and eleven lines. */
52
+ width: max-content;
53
+ max-width: min(calc(100vw - var(--spacing-32)), var(--content-width-readable));
54
+ font-family: var(--typography-fontFamily-sans);
55
+ font-size: var(--typography-caption-default-fontSize);
56
+ line-height: var(--typography-caption-default-lineHeight);
57
+ color: var(--color-text-inverse);
58
+ background: var(--color-surface-inverse);
59
+ padding: var(--spacing-4) var(--spacing-8);
60
+ border-radius: var(--radius-sm);
61
+ box-shadow: var(--shadow-md);
62
+ opacity: 0;
63
+ pointer-events: none;
64
+ transition: opacity var(--transition-duration-fast) var(--transition-easing-smooth);
65
+ }
66
+
67
+ :host(:hover) .bubble,
68
+ :host(:focus-within) .bubble { opacity: 1; }
69
+
70
+ @media (prefers-reduced-motion: reduce) {
71
+ .bubble { transition: none; }
72
+ }
73
+ `;
74
+
75
+ export default tooltipCSS;
@@ -0,0 +1,156 @@
1
+ import { LitElement, html } from 'lit';
2
+ // Installs the design tokens into document scope; see src/tokens/tokens.js
3
+ import '../../tokens/tokens.js';
4
+ import { tooltipCSS } from './tooltip.css.js';
5
+
6
+ export class BdTooltip extends LitElement {
7
+ static styles = [tooltipCSS];
8
+
9
+ static properties = {
10
+ text: { type: String }
11
+ };
12
+
13
+ constructor() {
14
+ super();
15
+ this.text = '';
16
+ }
17
+
18
+ /* The bubble is centred on its trigger and is wider than it, so near a
19
+ viewport edge part of it lands outside the page. No CSS can fix that --
20
+ the browser cannot be asked "where is my trigger" from a stylesheet -- so
21
+ the shift is measured here and handed back to CSS as a custom property.
22
+
23
+ Deliberately additive: --bd-tooltip-shift defaults to 0 in the stylesheet,
24
+ so a tooltip in a document where this never runs renders exactly as it did
25
+ before. getBoundingClientRect and ResizeObserver are both already used by
26
+ five components in this package; this is the same pattern, not a new one. */
27
+ /**
28
+ * The margin the bubble keeps from each viewport edge, read from the same
29
+ * token the stylesheet subtracts. The cap there is `100vw - --spacing-32`,
30
+ * which is that margin on each side, and the two numbers have to agree: the
31
+ * previous version clamped to a hard-coded 8 while the stylesheet reserved
32
+ * 16, so placement aimed at a boundary the width contract had not left room
33
+ * for.
34
+ */
35
+ _margin() {
36
+ const raw = getComputedStyle(this).getPropertyValue('--spacing-16');
37
+ const px = parseFloat(raw);
38
+ /* Tokens are installed in document scope and always resolve; the fallback
39
+ is for a disconnected element, where getComputedStyle returns nothing. */
40
+ return Number.isFinite(px) ? (raw.includes('rem') ? px * 16 : px) : 16;
41
+ }
42
+
43
+ /* The bubble is centred on its trigger and is wider than it, so near a
44
+ viewport edge part of it lands outside the page. No CSS can fix that --
45
+ the browser cannot be asked "where is my trigger" from a stylesheet -- so
46
+ the shift is measured here and handed back to CSS as a custom property.
47
+
48
+ Deliberately additive: --bd-tooltip-shift defaults to 0 in the stylesheet,
49
+ so a tooltip in a document where this never runs renders exactly as it did
50
+ before.
51
+
52
+ Both edges, not one. The previous version corrected whichever edge it
53
+ found outside first and stopped -- an `if` on the left and an `else if` on
54
+ the right -- which is only sound while the bubble is narrower than the
55
+ space between the margins. It was not: the border box ran 16px past its
56
+ own cap, so pushing the left edge in pushed the right edge out and the
57
+ `else if` never ran to notice.
58
+
59
+ Now the two constraints are solved together. For a shift s the bubble
60
+ lands at [left + s, right + s], and staying inside the margins means
61
+
62
+ s >= M - left and s <= V - M - right
63
+
64
+ so the centred position s = 0 is clamped into [minShift, maxShift]. With
65
+ box-sizing: border-box the bubble is at most V - 2M wide, which is exactly
66
+ the condition for that interval to be non-empty -- the width contract is
67
+ what makes the placement solvable, which is why the box model had to be
68
+ fixed first. */
69
+ _place() {
70
+ const bubble = this.renderRoot?.querySelector('.bubble');
71
+ if (!bubble) return;
72
+ bubble.style.setProperty('--bd-tooltip-shift', '0px');
73
+ const box = bubble.getBoundingClientRect();
74
+ if (box.width === 0) return;
75
+
76
+ const margin = this._margin();
77
+ const minShift = margin - box.left;
78
+ const maxShift = window.innerWidth - margin - box.right;
79
+
80
+ /* minShift > maxShift means the bubble is wider than the space between the
81
+ margins, which the width cap should prevent. If a consuming page has
82
+ overridden that cap, the left edge wins: the start of the text stays
83
+ readable rather than the end of it. */
84
+ const shift = minShift > maxShift
85
+ ? minShift
86
+ : Math.min(Math.max(0, minShift), maxShift);
87
+
88
+ bubble.style.setProperty('--bd-tooltip-shift', `${Math.round(shift)}px`);
89
+ }
90
+
91
+ /* Placement runs once the bubble has a box, not only when it is revealed.
92
+ The bubble is laid out at all times -- opacity: 0 hides it and
93
+ pointer-events: none takes it out of the way, but neither takes it out of
94
+ flow -- so an unplaced bubble on a trigger near the right edge widened the
95
+ document: measured at 1440, the right-edge bubble in Atoms/Tooltip "At the
96
+ viewport edges" ended at x=1622.5 and the page scrollWidth was 1623, a
97
+ 183px horizontal scrollbar produced by something invisible.
98
+
99
+ This is not the placement-on-first-update that P7F tried and reverted.
100
+ That call ran the single-edge algorithm above on a content-box bubble, and
101
+ it moved the overflow rather than removing it -- 183px at the right edge
102
+ became 61px on Long Text at 320. Running it here is only correct because
103
+ the width contract and the clamp were fixed first; the ordering is the
104
+ fix, not the call site.
105
+
106
+ One pass here is not enough on its own, and the observer below is why.
107
+ Measured: this call alone left Long Text at 320 with shift 80px and 53px
108
+ of overflow, where placing again after everything had settled produced
109
+ shift 11px and none. The arithmetic was right both times -- the geometry
110
+ was not final the first time. */
111
+ firstUpdated() {
112
+ this._place();
113
+ /* The trigger is slotted light DOM and is usually a custom element of its
114
+ own, so it upgrades and takes its real width AFTER this tooltip has
115
+ rendered: the bubble is centred on a reference that has not finished
116
+ existing. Observing both boxes covers that, and the font swap and any
117
+ later content change with it, rather than guessing a delay.
118
+
119
+ Both boxes, because they move for different reasons: the host changes
120
+ when the trigger resizes, the bubble when its own text reflows.
121
+ ResizeObserver fires once on observe, which is the settled first pass. */
122
+ if (typeof ResizeObserver === 'undefined') return;
123
+ this._ro = new ResizeObserver(() => this._place());
124
+ this._ro.observe(this);
125
+ const bubble = this.renderRoot?.querySelector('.bubble');
126
+ if (bubble) this._ro.observe(bubble);
127
+ }
128
+
129
+ connectedCallback() {
130
+ super.connectedCallback();
131
+ this._onShow = () => this._place();
132
+ /* Both reveals the stylesheet already honours, and resize because the
133
+ measurement is only true for the width it was taken at. */
134
+ this.addEventListener('mouseenter', this._onShow);
135
+ this.addEventListener('focusin', this._onShow);
136
+ window.addEventListener('resize', this._onShow);
137
+ }
138
+
139
+ disconnectedCallback() {
140
+ this.removeEventListener('mouseenter', this._onShow);
141
+ this.removeEventListener('focusin', this._onShow);
142
+ window.removeEventListener('resize', this._onShow);
143
+ this._ro?.disconnect();
144
+ this._ro = undefined;
145
+ super.disconnectedCallback();
146
+ }
147
+
148
+ render() {
149
+ return html`
150
+ <slot></slot>
151
+ <span class="bubble" role="tooltip">${this.text}</span>
152
+ `;
153
+ }
154
+ }
155
+
156
+ customElements.define('bd-tooltip', BdTooltip);