@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,206 @@
1
+ import { css } from "lit";
2
+
3
+ /**
4
+ * The shared chrome for a form field: its label, the control itself, its three
5
+ * sizes, its states and its help text.
6
+ *
7
+ * bd-custom-input, bd-custom-dropdown and bd-custom-input-clipboard are one
8
+ * field with three kinds of control in the middle. They used to be three
9
+ * stylesheets of about 160 lines each, roughly 130 of them identical, and they
10
+ * had drifted: the select set its labels and help text at line-height normal
11
+ * where the other two used tight, and its `md` size declared 8px of vertical
12
+ * padding while the height formula on the line below assumed 12. Every fix had
13
+ * to be applied three times and demonstrably had not been.
14
+ *
15
+ * This is the same arrangement radio.css.js already uses across bd-radio and
16
+ * bd-toggle-radio. A component imports this first and then its own sheet:
17
+ *
18
+ * static styles = [formFieldCSS, dropdownCSS];
19
+ *
20
+ * Values come from the --form-input-* and --form-label-* component tokens
21
+ * rather than from the palette, which is what bd-textarea was already doing
22
+ * and what lets a single token change reach all four controls.
23
+ */
24
+ export const formFieldCSS = css`
25
+ /* No width. This used to be 90%, an arbitrary fraction that made every field
26
+ narrower than whatever contained it and left the last tenth unexplained.
27
+ A field is display:block and fills its container; deciding how wide that
28
+ container is belongs to the page. */
29
+ :host {
30
+ display: block;
31
+ }
32
+
33
+ .input-container {
34
+ display: flex;
35
+ flex-direction: column;
36
+ }
37
+
38
+ /* ---- label ------------------------------------------------------------
39
+ Driven by --form-label-*, the same tokens bd-label reads, so the field's
40
+ built-in label and the standalone atom render identically. They did not
41
+ before: these rules hardcoded bold where the token says medium, which is
42
+ 200 units apart on the same line of the same form. */
43
+ .form-label {
44
+ display: block;
45
+ font-family: var(--form-label-default-fontFamily);
46
+ color: var(--form-label-default-color);
47
+ letter-spacing: var(--form-label-default-letterSpacing);
48
+ }
49
+
50
+ .label-sm {
51
+ font-size: var(--form-label-small-fontSize);
52
+ font-weight: var(--form-label-small-fontWeight);
53
+ line-height: var(--form-label-small-lineHeight);
54
+ letter-spacing: var(--form-label-small-letterSpacing);
55
+ margin-bottom: var(--form-label-small-marginBottom);
56
+ }
57
+
58
+ .label-md {
59
+ font-size: var(--form-label-default-fontSize);
60
+ font-weight: var(--form-label-default-fontWeight);
61
+ line-height: var(--form-label-default-lineHeight);
62
+ margin-bottom: var(--form-label-default-marginBottom);
63
+ }
64
+
65
+ .label-lg {
66
+ font-size: var(--form-label-large-fontSize);
67
+ font-weight: var(--form-label-large-fontWeight);
68
+ line-height: var(--form-label-large-lineHeight);
69
+ margin-bottom: var(--form-label-large-marginBottom);
70
+ }
71
+
72
+ .form-label__required {
73
+ color: var(--form-label-required-color);
74
+ margin-left: var(--form-label-required-marginLeft);
75
+ }
76
+
77
+ /* ---- the control ------------------------------------------------------
78
+ line-height is tight here and only here. The height below is derived from
79
+ padding, type step and line-height, and bd-button derives its own from the
80
+ same three, which is what keeps a field and a button of the same size the
81
+ same height. At normal (1.5) the line box also stood taller than the
82
+ content box the fixed height leaves, and the select clipped the descenders
83
+ of its own option text. */
84
+ .form-input-bd {
85
+ width: 100%;
86
+ box-sizing: border-box;
87
+ font-family: var(--form-input-default-fontFamily);
88
+ /* no role: tight is load-bearing here rather than chosen -- the comment
89
+ above this rule has the argument. The height is DERIVED from padding,
90
+ type step and this leading, so a role's leading would change the
91
+ control's height; and an <input> renders one line, so CLAUDE.md's
92
+ "anything that can wrap needs at least tight" floor is met and its
93
+ concern does not arise. Overridden for the textarea, which does wrap:
94
+ see textarea.css.js. */
95
+ line-height: var(--typography-lineHeight-tight);
96
+ border: var(--form-input-default-borderWidth) solid var(--form-input-default-borderColor);
97
+ background-color: var(--form-input-default-backgroundColor);
98
+ color: var(--form-input-default-color);
99
+ outline: none;
100
+ transition: border-color var(--transition-duration-slow) var(--transition-easing-smooth),
101
+ background-color var(--transition-duration-slow) var(--transition-easing-smooth),
102
+ box-shadow var(--transition-duration-slow) var(--transition-easing-smooth);
103
+ }
104
+
105
+ /* :focus-visible, not :focus. On a text field the two agree — a browser
106
+ shows the indicator for a pointer click there too, because typing is
107
+ expected — but the select is a control you click and walk away from, and
108
+ it was drawing the full ring on every mouse press where bd-button drew
109
+ nothing. .focused is the component's own prop, not a user interaction, so
110
+ it stays unconditional. */
111
+ .form-input-bd:focus-visible,
112
+ .form-input-bd.focused {
113
+ border-color: var(--form-input-focus-borderColor);
114
+ box-shadow: var(--focus-outline-primary);
115
+ }
116
+
117
+ /* ---- sizes ------------------------------------------------------------
118
+ Four reads of the size's own token, no arithmetic. The height calc used to
119
+ live here in triplicate; it lives in --form-input-*-height now, where the
120
+ value it produces is also the value the package publishes. */
121
+ .input-sm {
122
+ padding: var(--form-input-small-padding);
123
+ font-size: var(--form-input-small-fontSize);
124
+ height: var(--form-input-small-height);
125
+ border-radius: var(--form-input-small-borderRadius);
126
+ }
127
+
128
+ .input-md {
129
+ padding: var(--form-input-medium-padding);
130
+ font-size: var(--form-input-medium-fontSize);
131
+ height: var(--form-input-medium-height);
132
+ border-radius: var(--form-input-medium-borderRadius);
133
+ }
134
+
135
+ .input-lg {
136
+ padding: var(--form-input-large-padding);
137
+ font-size: var(--form-input-large-fontSize);
138
+ height: var(--form-input-large-height);
139
+ border-radius: var(--form-input-large-borderRadius);
140
+ }
141
+
142
+ ::placeholder {
143
+ color: var(--color-text-subtle);
144
+ opacity: 1;
145
+ }
146
+
147
+ /* On a tinted field the placeholder takes the next step up. text-subtle on
148
+ the error tint measured 4.22:1 and on the success tint 4.43 -- under 4.5,
149
+ and an empty required field in error is exactly when the placeholder is
150
+ showing. text-muted gives 5.95 and 6.25. The plain field keeps
151
+ text-subtle (4.81). */
152
+ .form-input--error::placeholder,
153
+ .form-input--success::placeholder {
154
+ color: var(--color-text-muted);
155
+ }
156
+
157
+ /* ---- states ----------------------------------------------------------- */
158
+ .form-input--error {
159
+ background-color: var(--form-input-error-backgroundColor);
160
+ border-color: var(--form-input-error-borderColor);
161
+ }
162
+
163
+ .form-input--success {
164
+ background-color: var(--form-input-success-backgroundColor);
165
+ border-color: var(--form-input-success-borderColor);
166
+ }
167
+
168
+ .form-input--disabled {
169
+ background-color: var(--form-input-disabled-backgroundColor);
170
+ border-color: var(--form-input-disabled-borderColor);
171
+ color: var(--form-input-disabled-color);
172
+ cursor: not-allowed;
173
+ }
174
+
175
+ /* ---- help text --------------------------------------------------------
176
+ The same roles bd-help-text renders, so a field's built-in message and the
177
+ standalone atom agree on colour and measure. */
178
+ .form-help-text {
179
+ font-family: var(--form-help-text-default-fontFamily);
180
+ font-size: var(--form-help-text-default-fontSize);
181
+ font-weight: var(--form-help-text-default-fontWeight);
182
+ line-height: var(--form-help-text-default-lineHeight);
183
+ letter-spacing: var(--form-help-text-default-letterSpacing);
184
+ margin-top: var(--form-help-text-default-marginTop);
185
+ }
186
+
187
+ .form-help-text--default {
188
+ color: var(--form-help-text-default-color);
189
+ }
190
+
191
+ .form-help-text--error {
192
+ color: var(--form-help-text-error-color);
193
+ }
194
+
195
+ .form-help-text--success {
196
+ color: var(--form-help-text-success-color);
197
+ }
198
+
199
+ @media (prefers-reduced-motion: reduce) {
200
+ .form-input-bd {
201
+ transition: none;
202
+ }
203
+ }
204
+ `;
205
+
206
+ export default formFieldCSS;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The words a component prop is allowed to use, and the ones it used to.
3
+ *
4
+ * Three axes, three names, and each name means one thing everywhere:
5
+ *
6
+ * size how big sm · md · lg
7
+ * variant how it looks primary, bullet, shade — per component
8
+ * tone what it means neutral · brand · success · warning · error · info
9
+ *
10
+ * Before this there were three size vocabularies and `kind` meant four
11
+ * unrelated things: the visual variant on bd-button and bd-link, the size on
12
+ * the three form fields, the type step on bd-p and the marker on bd-li. An
13
+ * author could not carry anything learned on one atom to the next.
14
+ *
15
+ * Every old name still resolves. bd-button already did this for the kind
16
+ * values it retired, and that is the pattern the rest now follow — renaming a
17
+ * prop is not worth breaking a consumer's markup over.
18
+ */
19
+
20
+ /** The one size scale. */
21
+ export const SIZES = ['sm', 'md', 'lg'];
22
+
23
+ /**
24
+ * Size names from before there was one vocabulary. `default` is the
25
+ * interesting one: it named a position rather than a size, so a reader had to
26
+ * know the set to know whether it was smaller or larger than the alternative.
27
+ */
28
+ export const LEGACY_SIZES = {
29
+ small : 'sm',
30
+ medium : 'md',
31
+ default: 'md',
32
+ large : 'lg'
33
+ };
34
+
35
+ /**
36
+ * Resolves a prop value against its closed set, mapping any legacy spelling
37
+ * first and falling back when the value is not in the set at all. Returning
38
+ * the fallback rather than the raw value is what stops a typo rendering an
39
+ * unstyled component.
40
+ */
41
+ export function resolveEnum(value, { allowed, legacy = {}, fallback }) {
42
+ const mapped = Object.hasOwn(legacy, value) ? legacy[value] : value;
43
+ return allowed.includes(mapped) ? mapped : fallback;
44
+ }
45
+
46
+ /** The common case: a size prop, with the old spellings still accepted. */
47
+ export const resolveSize = (value, fallback = 'md') =>
48
+ resolveEnum(value, { allowed: SIZES, legacy: LEGACY_SIZES, fallback });
@@ -0,0 +1,306 @@
1
+ /**
2
+ * The closed sets a component prop may draw from, and the only place that
3
+ * turns a prop value into CSS.
4
+ *
5
+ * See CLAUDE.md, "The foundation rule". Every prop-facing function here
6
+ * returns either a token reference or the empty string, so a literal cannot
7
+ * come out of a component prop no matter what a caller passes. An
8
+ * unrecognised value is dropped and the component keeps its own styling.
9
+ *
10
+ * The breakpoint helpers at the foot of this file are the one exception, and
11
+ * they throw rather than returning empty. The reason is who calls them: every
12
+ * scale above is fed a value a *consumer* passed as an attribute, where
13
+ * dropping a bad value degrades gracefully. A breakpoint name is written by
14
+ * the component author inside a stylesheet, and an empty string there yields
15
+ * `@media { ... }` — a malformed query the browser discards silently, taking
16
+ * the whole block with it. A typo has to fail loudly at the point it is made.
17
+ */
18
+
19
+ export const SPACING_STEPS = [
20
+ '0', '1', '2', '4', '6', '8', '10', '12', '14', '16', '18', '20', '22', '24',
21
+ '28', '32', '36', '40', '44', '48', '52', '56', '64', '80', '96', '112', '128'
22
+ ];
23
+
24
+ export const FONT_SIZE_STEPS = [
25
+ 'xxs', 'xs', 'sm', 'base', 'lg', 'xl',
26
+ '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl'
27
+ ];
28
+
29
+ export const FONT_WEIGHT_STEPS = ['light', 'normal', 'medium', 'semibold', 'bold'];
30
+
31
+ /** The two faces the system ships. There is no third, and no escape hatch. */
32
+ export const FONT_FAMILY_STEPS = ['sans', 'mono'];
33
+
34
+ export const LETTER_SPACING_STEPS = ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest'];
35
+
36
+ /**
37
+ * The t-shirt vocabulary bd-grid's gap and padding props take, mapped onto the
38
+ * live pixel scale. It used to be a second CSS scale, --space-*, which existed
39
+ * mainly so the component could build a token name from a prop — thirteen
40
+ * tokens duplicating --spacing-* under other names. A prop vocabulary belongs
41
+ * here, with the other closed sets, rather than in the token file.
42
+ */
43
+ export const SPACE_STEPS = {
44
+ none : '0', '4xs': '2', '3xs': '4', '2xs': '8', xs : '12', sm : '16',
45
+ md : '24', lg : '32', xl : '40', '2xl': '48', '3xl': '64', '4xl': '96', '5xl': '128'
46
+ };
47
+
48
+ /** Icon sizes. The scale stops at 3xl (64px); there is no step above it. */
49
+ export const ICON_SIZE_STEPS = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
50
+
51
+ export const RADIUS_STEPS = ['none', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 'full'];
52
+
53
+ /**
54
+ * Semantic colour roles a component's `color` prop may name.
55
+ *
56
+ * These resolve through the --color-text-* roles rather than reaching past
57
+ * them into the palette, which is what the semantic colour block in
58
+ * src/tokens/tokens.js says they should do. Spelled as palette steps, this
59
+ * map was the reason a page could retint --color-text-muted and watch every
60
+ * bd-h with color="muted" ignore it: the values matched, so nothing looked
61
+ * wrong until someone tried to change one.
62
+ *
63
+ * The prop vocabulary and the token vocabulary are deliberately not the same
64
+ * words. `strong` and `primary` read better on a component than
65
+ * `text-primary` and `text-brand` do, and renaming the prop would break
66
+ * consumers' markup for no gain. The mapping is the seam, and it lives here.
67
+ *
68
+ * Every role clears 4.5:1 on white, which is what the docstring here always
69
+ * claimed and what `subtle` did not do: it named neutral-400, which measures
70
+ * 3.23:1 and fails 1.4.3 for text. It now resolves to --color-text-subtle
71
+ * (neutral-500, 4.67:1). Nothing in this repository passed color="subtle",
72
+ * so the fix costs nothing today — note that neutral-400 is the step the
73
+ * token layer calls `disabled`, and disabled text is exempt under 1.4.3,
74
+ * which is presumably how the two got crossed.
75
+ */
76
+ export const COLOR_ROLES = {
77
+ primary: '--color-text-brand',
78
+ strong : '--color-text-primary',
79
+ muted : '--color-text-muted',
80
+ subtle : '--color-text-subtle',
81
+ inverse: '--color-text-inverse',
82
+ success: '--color-text-success',
83
+ warning: '--color-text-warning',
84
+ error : '--color-text-error'
85
+ };
86
+
87
+ export const COLOR_ROLE_NAMES = Object.keys(COLOR_ROLES);
88
+
89
+ /**
90
+ * The hairline vocabulary.
91
+ *
92
+ * A divider is a border, and until now the only colour prop it had resolved
93
+ * through COLOR_ROLES above -- the *ink* vocabulary. That is the category
94
+ * error CLAUDE.md names ("Never use a token from the wrong category"), and
95
+ * this file already carried a milder version of it: bd-divider-horizontal's
96
+ * constructor records that its default used to say `subtle`, a text role that
97
+ * matched the right value by coincidence.
98
+ *
99
+ * The cost was not theoretical. bd-quick-answer needs a hairline in
100
+ * --status-brand-border, because its rule sits on a brand tint and a neutral
101
+ * line reads as dirt there. No text role expresses that, so the component drew
102
+ * its own `border-top` instead of composing the atom -- which is exactly the
103
+ * "dividers are not borders" confusion the same document warns about, arrived
104
+ * at from the other direction: the divider could not say what the component
105
+ * meant, so the component stopped using the divider.
106
+ *
107
+ * `brand-subtle` is the one entry that reaches outside the --color-border-*
108
+ * family, and it is here because it has a consumer today. The rest of the
109
+ * status hairlines are deliberately absent until one does.
110
+ *
111
+ * `inverse` is deliberately NOT here. It would resolve to
112
+ * --color-border-container-inverse, which is white at 12%, where the text
113
+ * role it resolves to today is solid white -- and each divider then multiplies
114
+ * that by its own `opacity` prop (0.25 by default on the horizontal, 1.00 on
115
+ * the vertical). The dividers that pass color="inverse" would drop from solid
116
+ * white to 12%, or from 25% to 3%, and effectively vanish. It keeps falling
117
+ * through to the legacy text role below, and that is recorded rather than
118
+ * fixed: whether a hairline on a dark surface should be a 12% border role at
119
+ * full opacity, or a solid one dimmed by the prop, is a real design question
120
+ * and not this change's to answer.
121
+ */
122
+ export const BORDER_ROLES = {
123
+ default : '--color-border-default',
124
+ subtle : '--color-border-subtle',
125
+ strong : '--color-border-strong',
126
+ container : '--color-border-container',
127
+ brand : '--color-border-brand',
128
+ disabled : '--color-border-disabled',
129
+ 'brand-subtle': '--status-brand-border'
130
+ };
131
+
132
+ export const BORDER_ROLE_NAMES = Object.keys(BORDER_ROLES);
133
+
134
+ const onScale = (steps, prefix) => value =>
135
+ steps.includes(String(value)) ? `var(${prefix}${value})` : '';
136
+
137
+ export const spacing = onScale(SPACING_STEPS, '--spacing-');
138
+ export const fontSize = onScale(FONT_SIZE_STEPS, '--typography-fontSize-');
139
+ export const fontWeight = onScale(FONT_WEIGHT_STEPS, '--typography-fontWeight-');
140
+ export const fontFamily = onScale(FONT_FAMILY_STEPS, '--typography-fontFamily-');
141
+ export const letterSpacing = onScale(LETTER_SPACING_STEPS, '--typography-letterSpacing-');
142
+ export const radius = onScale(RADIUS_STEPS, '--radius-');
143
+
144
+ export const space = name =>
145
+ (Object.hasOwn(SPACE_STEPS, name) ? `var(--spacing-${SPACE_STEPS[name]})` : '');
146
+
147
+ /** --icon-*-size wraps its step, so it needs a suffix onScale cannot express. */
148
+ export const iconSize = value =>
149
+ (ICON_SIZE_STEPS.includes(String(value)) ? `var(--icon-${value}-size)` : '');
150
+
151
+ export const color = role =>
152
+ Object.hasOwn(COLOR_ROLES, role) ? `var(${COLOR_ROLES[role]})` : '';
153
+
154
+ /**
155
+ * A hairline's colour: border roles first, then the ink vocabulary as legacy.
156
+ *
157
+ * Border-first is the point -- `subtle` on a divider means
158
+ * --color-border-subtle, not --color-text-subtle. The fallback exists so the
159
+ * names already in use keep working (`inverse`, and `strong`, which resolves
160
+ * to neutral-900 either way), and it is a migration path, not a second
161
+ * vocabulary: a new divider should be spelled with a border role.
162
+ */
163
+ export const borderColor = role =>
164
+ (Object.hasOwn(BORDER_ROLES, role)
165
+ ? `var(${BORDER_ROLES[role]})`
166
+ : color(role));
167
+
168
+ /**
169
+ * A one-to-four-value spacing shorthand written as steps, e.g. "10 0" gives
170
+ * `var(--spacing-10) var(--spacing-0)`. All parts must be on the scale, or
171
+ * the whole value is dropped — a half-valid shorthand is worse than none.
172
+ */
173
+ export const spacingShorthand = value => {
174
+ const parts = String(value ?? '').trim()
175
+ .split(/\s+/)
176
+ .filter(Boolean);
177
+ if (parts.length < 1 || parts.length > 4) return '';
178
+ const refs = parts.map(spacing);
179
+ return refs.every(Boolean) ? refs.join(' ') : '';
180
+ };
181
+
182
+ /* ================================================================
183
+ BREAKPOINTS
184
+ ================================================================
185
+ The six viewport widths, and the only place a media condition is built
186
+ from them.
187
+
188
+ This lives in JS rather than in the token file because a media condition
189
+ cannot read a custom property — `@media (max-width: var(--breakpoint-md))`
190
+ is not valid CSS — so the six values have to be interpolated into the
191
+ stylesheet at author time. `--breakpoint-*` in src/tokens/tokens.js stays
192
+ the published record of the scale; this is how a component consumes it.
193
+
194
+ `above` and `below` return a media *condition*, not a whole `@media` rule,
195
+ so they compose:
196
+
197
+ @media ${below('md')} { ... }
198
+ @media ${above('sm')} and ${below('lg')} { ... }
199
+
200
+ ---
201
+
202
+ WHY THE HELPER OWNS THE COMPLEMENT.
203
+
204
+ A breakpoint is one number, but it has two spellings, and writing the
205
+ second one by hand is how the boundary drifts. `min-width: 768px` and
206
+ `max-width: 768px` are both true at exactly 768px, so a component that
207
+ spells its mobile rule the obvious way applies both its mobile and its
208
+ desktop styling at that one width. pricing-card.css.js has the collision
209
+ today — `@media (max-width: 768px)` at one rule and
210
+ `@media (min-width: 768px) and (max-width: 1024px)` at another — and
211
+ thirteen more files use the same `max-width: 768px` form.
212
+
213
+ The complement is derived here so that number is never typed. Do not write
214
+ 767px, 991px or 1199px anywhere; write `below('md')`, `below('lg')`,
215
+ `below('xl')`.
216
+
217
+ ---
218
+
219
+ WHY 0.02 AND NOT 1.
220
+
221
+ A viewport is not always a whole number of pixels: browser zoom, some
222
+ Android densities and fractional device-pixel-ratios all produce widths
223
+ like 767.5px. Measured in Chrome at that width, `min-width: 768px` is
224
+ false and `max-width: 767px` is *also* false — an element between the two
225
+ rules falls back to its unqueried state, briefly neither mobile nor
226
+ desktop. Subtracting 0.02 rather than 1 removes that band: across
227
+ 575.98, 576, 767, 767.5, 767.98, 767.99, 768 and 768.5 every width matches
228
+ exactly one of `above('md')` and `below('md')`, and none matches both.
229
+
230
+ 0.02 rather than 0.01 is the long-standing convention, from Safari having
231
+ rounded a hundredth away.
232
+
233
+ The five subtractions are exact in IEEE 754 — 575.98, 767.98, 991.98,
234
+ 1199.98 and 1399.98 all round-trip — so no rounding is needed here. If a
235
+ value is ever added to the scale whose complement does not, this is where
236
+ the rounding belongs. */
237
+
238
+ export const BREAKPOINTS = {
239
+ xs : 0,
240
+ sm : 576,
241
+ md : 768,
242
+ lg : 992,
243
+ xl : 1200,
244
+ xxl: 1400
245
+ };
246
+
247
+ export const BREAKPOINT_NAMES = Object.keys(BREAKPOINTS);
248
+
249
+ /** Throws rather than returning empty — see the note in the file header. */
250
+ const step = name => {
251
+ if (!Object.hasOwn(BREAKPOINTS, name)) {
252
+ throw new Error(`Unknown breakpoint: ${name}. Expected one of ${BREAKPOINT_NAMES.join(', ')}.`);
253
+ }
254
+ return BREAKPOINTS[name];
255
+ };
256
+
257
+ /**
258
+ * "This breakpoint and wider."
259
+ *
260
+ * above('md') → (min-width: 768px)
261
+ *
262
+ * `above('xs')` is `(min-width: 0px)`, which is always true. That is a
263
+ * harmless no-op rather than an error, because it is the honest answer for
264
+ * the bottom of the scale — but a rule that needs no condition should have
265
+ * no media query.
266
+ */
267
+ export const above = name => `(min-width: ${step(name)}px)`;
268
+
269
+ /**
270
+ * "Narrower than this breakpoint" — the exact complement of `above`.
271
+ *
272
+ * below('md') → (max-width: 767.98px)
273
+ *
274
+ * `below('xs')` throws: xs is 0, and no viewport is narrower than that, so
275
+ * the query could only ever be dead CSS.
276
+ */
277
+ export const below = name => {
278
+ const value = step(name);
279
+ if (value === 0) {
280
+ throw new Error(`below('${name}') has no meaning: no viewport is narrower than ${value}px. Did you mean above('${name}')?`);
281
+ }
282
+ return `(max-width: ${value - 0.02}px)`;
283
+ };
284
+
285
+ /**
286
+ * The JS side of `below`, for the viewport logic CSS genuinely cannot express
287
+ * — choosing which markup to render rather than how to style it.
288
+ *
289
+ * matchesBelow('md') → true when the viewport is narrower than md
290
+ *
291
+ * It reads the same scale as the stylesheet, so the two cannot disagree. That
292
+ * is the failure this replaces: bd-footer switched at 767 in CSS and at 767
293
+ * in JS while bd-footer-lp, sharing the same stylesheet, switched at 768.
294
+ *
295
+ * Prefer CSS. Reach for this only when the component's *behaviour* changes,
296
+ * and re-read it on `resize` — the value is a snapshot, not a subscription.
297
+ *
298
+ * Outside a browser (SSR, a bare Node import) there is no viewport to be
299
+ * narrower than, so this reports false. The name is still validated first, so
300
+ * a typo throws in every environment rather than only in the browser.
301
+ */
302
+ export const matchesBelow = name => {
303
+ const condition = below(name);
304
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;
305
+ return window.matchMedia(condition).matches;
306
+ };
@@ -0,0 +1,34 @@
1
+ import { css } from 'lit';
2
+
3
+ /** Styled entirely from design tokens — see npm run lint:tokens. */
4
+ export const skeletonCSS = css`
5
+ :host { display: block; }
6
+
7
+ .bone {
8
+ background: var(--color-surface-muted);
9
+ border-radius: var(--radius-sm);
10
+ height: var(--spacing-16);
11
+ animation: bd-shimmer var(--transition-duration-slower) var(--transition-easing-smooth) infinite alternate;
12
+ }
13
+
14
+ .bone + .bone { margin-top: var(--spacing-8); }
15
+
16
+ :host([shape='circle']) .bone {
17
+ width: var(--dimension-40);
18
+ height: var(--dimension-40);
19
+ border-radius: var(--radius-full);
20
+ }
21
+
22
+ :host([shape='title']) .bone { height: var(--spacing-24); }
23
+
24
+ /* --opacity-pulse, not 0.45. The dim end of a "this is busy" pulse is the
25
+ same intent here, in bd-button and in bd-spinner; it was spelled three
26
+ times and two of the three said 0.4. The role now says it once. */
27
+ @keyframes bd-shimmer { to { opacity: var(--opacity-pulse); } }
28
+
29
+ @media (prefers-reduced-motion: reduce) {
30
+ .bone { animation: none; }
31
+ }
32
+ `;
33
+
34
+ export default skeletonCSS;
@@ -0,0 +1,32 @@
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 { skeletonCSS } from './skeleton.css.js';
5
+
6
+ export class BdSkeleton extends LitElement {
7
+ static styles = [skeletonCSS];
8
+
9
+ static properties = {
10
+ lines: { type: Number },
11
+ shape: { type: String, reflect: true }
12
+ };
13
+
14
+ constructor() {
15
+ super();
16
+ this.lines = 1;
17
+ this.shape = 'line';
18
+ }
19
+
20
+ render() {
21
+ return html`
22
+ <span aria-hidden="true">
23
+ ${Array.from({ length: this.lines }, (_unused, i) => html`<div
24
+ class="bone"
25
+ style=${i === this.lines - 1 && this.lines > 1 ? 'width: 60%' : nothing}
26
+ ></div>`)}
27
+ </span>
28
+ `;
29
+ }
30
+ }
31
+
32
+ customElements.define('bd-skeleton', BdSkeleton);