@livenetworks/ashlar 1.6.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +202 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -1,413 +1,410 @@
1
- # ln-progress
2
-
3
- > A passive linear-progress renderer. Author writes
4
- > `data-ln-progress="42"` on a bar element; the component picks up the
5
- > change via MutationObserver and writes the new `width` as a
6
- > percentage. Around 85 lines of JS.
7
-
8
- ## Integration
9
-
10
- ### In-Bundle (Standard Integration)
11
- To load `ln-progress` as part of the unified `ln-ashlar` bundle, include the main script:
12
- ```html
13
- <script src="dist/ln-ashlar.iife.js" defer></script>
14
- ```
15
-
16
- ### Standalone (Zero-Dependency IIFE)
17
- If you only need the progress bar component, load the compiled zero-dependency IIFE directly:
18
- ```html
19
- <script src="js/ln-progress/ln-progress.js" defer></script>
20
- ```
21
-
22
- ### Source Files & Development
23
- - **Active Development Source**: [js/ln-progress/src/ln-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-progress/src/ln-progress.js) — The source of truth for component logic.
24
- - **Compiled Standalone**: [js/ln-progress/ln-progress.js](file:///c:/laragon/www/ln-ashlar/js/ln-progress/ln-progress.js) — The compiled, ready-to-use standalone bundle.
25
-
26
- ## Philosophy
27
-
28
- A linear progress indicator is mechanically a track `<div>` wrapping
29
- one or more child bar `<div>`s whose `width` is a percentage of the
30
- track. The maths is `width = (value / max) * 100%`. What `ln-progress`
31
- adds is the reactive plumbing: when the width gets recomputed, what
32
- triggers a re-render, where the maximum comes from when multiple bars
33
- share a denominator, and what survives a `destroy()` call.
34
-
35
- The contract is simple: **the attribute IS the state**. There is no
36
- `element.setValue(42)` API and no imperative entry point. You set
37
- `data-ln-progress="42"` on a bar; the MutationObserver sees the
38
- change; `_render` runs; the bar redraws. State is visible in DOM
39
- Inspector at all times, can be set declaratively from server templates,
40
- and survives `outerHTML` round-trips.
41
-
42
- The component supports stacked bars sharing one denominator: declare
43
- `data-ln-progress-max` on the parent track and every child bar reads
44
- that as its max. This is what makes "4 done + 2 in progress + 1
45
- pending = 7 total" render as three bars filling exactly one track.
46
-
47
- ## Quick start
48
-
49
- ```html
50
- <div class="progress">
51
- <div data-ln-progress="75" class="success"></div>
52
- </div>
53
- ```
54
-
55
- A green bar at 75% of the track, no JS on the consumer side. The
56
- `class="progress"` on the wrapper marks it as a track (pure CSS hook,
57
- no JS instance is created). The `data-ln-progress="75"` on the child
58
- marks it as a bar (instance is created, `style.width = "75%"` is
59
- written).
60
-
61
- ## Attributes
62
-
63
- | Attribute | On | Description |
64
- |---|---|---|
65
- | `data-ln-progress="N"` | bar element | Current value of the bar. Number; can be negative or above max — the rendered percentage clamps to 0–100. Empty / non-numeric values render as 0%. |
66
- | `data-ln-progress-max="N"` | bar element | Maximum value for this bar. Default `100`. Used as the denominator. Setting `max="0"` collapses to 0%. |
67
- | `data-ln-progress-max="N"` | track element | Shared max for all bar children. Wins over each bar's own max. The track itself uses `class="progress"` for styling — no JS instance is created on it. |
68
- | `class="success"` / `.warning` / `.error` | bar element | Colour variant. Plain CSS, no JS involvement. |
69
-
70
- The track is marked with `class="progress"` (pure CSS hook). The bar
71
- is marked with `data-ln-progress="N"` (JS instance).
72
-
73
- The component writes `role="progressbar"`, `aria-valuemin="0"`,
74
- `aria-valuemax`, and `aria-valuenow` on **each bar element** on every
75
- render. `aria-valuenow` is clamped to `[0, max]` to match the rendered
76
- bar (so an overshoot value of `150` against `max=100` reports as
77
- `100`). Add `aria-label` on the bar yourself for screen-reader context
78
- — the component does not invent a label.
79
-
80
- ## Events
81
-
82
- One event, bubbles, not cancelable.
83
-
84
- | Event | `detail` | Dispatched on | Dispatched when |
85
- |---|---|---|---|
86
- | `ln-progress:change` | `{ target: HTMLElement, value: number, max: number, percentage: number }` | the bar element | every `_render` call: at construction, on every value or max attribute change (bar's own value, bar's own max, or parent track's max) |
87
-
88
- `detail.value` is the raw `parseFloat` of the value attribute —
89
- unclamped. `detail.max` is the resolved max (parent's first, then
90
- bar's own, then 100). `detail.percentage` is the clamped 0–100
91
- percentage written to `style.width`.
92
-
93
- The event fires once per attribute write. Writing
94
- `data-ln-progress="50"` then `"75"` synchronously fires the event
95
- twice. The CSS transition smooths the visual to a single sweep, but
96
- the event count is per-write.
97
-
98
- There is no `:initialized` and no `:destroyed` event. The first
99
- `ln-progress:change` fires inside `_render` during construction and serves as
100
- the init signal for any consumer listening at the document level.
101
-
102
- ## API
103
-
104
- `window.lnProgress(root)` re-runs the init scan over `root`. The
105
- document-level observer managed by `registerComponent` covers AJAX inserts and
106
- `data-ln-progress` attribute additions; call this manually only when
107
- you inject markup into a Shadow DOM root or another document context
108
- the observer cannot see.
109
-
110
- `el.lnProgress` on a bar exposes:
111
-
112
- | Property | Type | Description |
113
- |---|---|---|
114
- | `dom` | `HTMLElement` | back-reference to the bar element |
115
- | `_attrObserver` | `MutationObserver` | watches the bar's own `data-ln-progress` and `data-ln-progress-max` |
116
- | `_parentObserver` | `MutationObserver \| null` | watches the parent track's `data-ln-progress-max` |
117
- | `destroy()` | method | disconnects both observers, deletes `el.lnProgress`. Does NOT remove `data-ln-progress` itself, the colour class, or the inline `style.width`. |
118
-
119
- There is no `setValue(n)`, `setMax(n)`, `update()`, or `redraw()`
120
- method. The attribute is the API. A programmatic update is
121
- `el.setAttribute('data-ln-progress', n)`, exactly the same as a
122
- server-side render that writes the attribute into markup. To force a
123
- re-render without changing the value, write the same value back —
124
- the MutationObserver fires on any write, including identical ones.
125
-
126
- ## Max priority resolution
127
-
128
- `_render` resolves the maximum in this order:
129
-
130
- | Priority | Source | Truthy check |
131
- |---|---|---|
132
- | 1 | parent track's `data-ln-progress-max` parsed as float | parses to a finite number > 0 |
133
- | 2 | bar's own `data-ln-progress-max` parsed as float | parses to a finite number > 0 |
134
- | 3 | `100` (default) | always |
135
-
136
- The chain is `parentMax || ownMax || 100`. Two consequences:
137
-
138
- 1. A parent declaring `data-ln-progress-max="0"` falls through to the
139
- bar's own max, because `0` is falsy in JS. Same for non-numeric
140
- values like `data-ln-progress-max="abc"` — `NaN` is falsy.
141
- 2. An own-max declaration is silently ignored when the parent declares
142
- one. If a bar carries `data-ln-progress-max="50"` and the parent
143
- carries `data-ln-progress-max="100"`, the parent wins — the bar
144
- measures against 100. If you genuinely need per-bar maxes inside a
145
- shared-max parent, do not declare the parent max; set each bar's
146
- own max individually and accept that the bars no longer fill the
147
- track in proportional fashion.
148
-
149
- ## Examples
150
-
151
- ### Minimal — fixed value
152
-
153
- ```html
154
- <div class="progress">
155
- <div data-ln-progress="75" class="success"></div>
156
- </div>
157
- ```
158
-
159
- The simplest case. Green bar at 75%, no JS on the consumer side.
160
-
161
- ### Reactive — change value at runtime
162
-
163
- ```html
164
- <div id="upload-track" class="progress">
165
- <div id="upload-bar" data-ln-progress="0" class="success"
166
- aria-label="Upload progress"></div>
167
- </div>
168
- <button id="advance">Advance</button>
169
- ```
170
-
171
- ```js
172
- let pct = 0;
173
- document.getElementById('advance').addEventListener('click', function () {
174
- pct = Math.min(100, pct + 10);
175
- document.getElementById('upload-bar').setAttribute('data-ln-progress', pct);
176
- });
177
- ```
178
-
179
- Each attribute write triggers a re-render and a `:change` event. The
180
- CSS transition smoothly animates between widths. The component writes
181
- `aria-valuenow` on the bar automatically — no manual sync needed.
182
-
183
- ### Custom max — non-percentage scale (per-bar)
184
-
185
- ```html
186
- <!-- 7.5 GB of 10 GB — bar fills 75% of the track -->
187
- <div class="progress">
188
- <div data-ln-progress="7.5" data-ln-progress-max="10" class="warning"></div>
189
- </div>
190
- ```
191
-
192
- `max="10"` shifts the denominator so 7.5 maps to 75%. There is no
193
- text label slot inside the bar; place descriptive text as a sibling
194
- element if you need it.
195
-
196
- ### Stacked — multiple bars, one track, default max=100
197
-
198
- ```html
199
- <!-- CPU 40% / RAM 30% / Disk 30% — sums to 100% of the track -->
200
- <div class="progress">
201
- <div data-ln-progress="40" class="success" aria-label="CPU usage"></div>
202
- <div data-ln-progress="30" class="warning" aria-label="RAM usage"></div>
203
- <div data-ln-progress="30" class="error" aria-label="Disk usage"></div>
204
- </div>
205
- ```
206
-
207
- Each bar uses the default max of 100. Three bars at 40 + 30 + 30 fill
208
- the track exactly. If the values sum to less than 100, the remainder
209
- of the track stays empty (the track's recessed background shows
210
- through). If more than 100, each bar still renders at its clamped
211
- 0–100 width and the visuals overflow the track horizontally — the
212
- SCSS `overflow: hidden` clips the overflow so the visual stays clean,
213
- but the maths is wrong.
214
-
215
- ### Stacked — shared max on the parent track
216
-
217
- ```html
218
- <!-- 4 done + 2 in progress + 1 pending = 7 total — fills the track exactly -->
219
- <div class="progress" data-ln-progress-max="7">
220
- <div data-ln-progress="4" class="success" aria-label="Done"></div>
221
- <div data-ln-progress="2" class="warning" aria-label="In progress"></div>
222
- <div data-ln-progress="1" class="error" aria-label="Pending"></div>
223
- </div>
224
- ```
225
-
226
- The track carries `data-ln-progress-max="7"`. Each child bar's
227
- `_render` reads the parent's max first and uses `7` as the
228
- denominator. Bar widths are 4/7 ≈ 57.1%, 2/7 ≈ 28.6%, 1/7 ≈ 14.3%,
229
- summing to 100%.
230
-
231
- Changing the parent's max at runtime
232
- (`track.setAttribute('data-ln-progress-max', '10')`) re-renders each
233
- child via the per-bar `_parentObserver`.
234
-
235
- ### Upload progress — driving from XHR
236
-
237
- ```html
238
- <form id="upload-form">
239
- <input type="file" name="file">
240
- <div id="upload-track" class="progress">
241
- <div id="upload-bar" data-ln-progress="0" class="success"
242
- aria-label="Upload progress"></div>
243
- </div>
244
- <button type="submit">Upload</button>
245
- </form>
246
- ```
247
-
248
- ```js
249
- document.getElementById('upload-form').addEventListener('submit', function (e) {
250
- e.preventDefault();
251
- const bar = document.getElementById('upload-bar');
252
- const file = e.target.querySelector('input[type=file]').files[0];
253
- if (!file) return;
254
-
255
- const xhr = new XMLHttpRequest();
256
- const fd = new FormData(e.target);
257
-
258
- xhr.upload.addEventListener('progress', function (ev) {
259
- if (!ev.lengthComputable) return;
260
- const pct = Math.round((ev.loaded / ev.total) * 100);
261
- bar.setAttribute('data-ln-progress', pct);
262
- });
263
-
264
- xhr.upload.addEventListener('load', function () {
265
- bar.setAttribute('data-ln-progress', 100);
266
- });
267
-
268
- xhr.open('POST', '/upload');
269
- xhr.send(fd);
270
- });
271
- ```
272
-
273
- ARIA on the bar is managed automatically by `ln-progress` —
274
- `aria-valuenow` updates on every attribute write. The `progress`
275
- event fires often during a large upload — every 16ms or so on a fast
276
- connection. Each call rewrites the attribute, the MutationObserver
277
- fires, `_render` runs, and the `:change` event dispatches. If
278
- profiling shows it as a hot spot, throttle the JS-side write to
279
- every 100ms before calling `setAttribute`.
280
-
281
- ### Threshold-based colour — consumer code
282
-
283
- The component has no automatic threshold colours. `class="success"`
284
- / `.warning` / `.error` are static — JS does not flip them based on
285
- value. If you want red below 30% and green above 70%, that is
286
- consumer code:
287
-
288
- ```js
289
- function setProgressWithThreshold(bar, value) {
290
- bar.setAttribute('data-ln-progress', value);
291
- bar.classList.remove('success', 'warning', 'error');
292
- if (value < 30) bar.classList.add('error');
293
- else if (value < 70) bar.classList.add('warning');
294
- else bar.classList.add('success');
295
- }
296
- ```
297
-
298
- The colour-class swap is a `classList` write — no MutationObserver
299
- fires for it (the component watches `data-ln-progress` and
300
- `data-ln-progress-max`, not `class`). The new colour applies via plain
301
- CSS rules in `scss/components/_progress.scss`.
302
-
303
- ## What `ln-progress` does NOT do
304
-
305
- - **Does NOT clamp the input attribute.** It clamps the *computed
306
- percentage* between 0 and 100 before writing the width. The
307
- attribute can hold any number; reading
308
- `el.getAttribute('data-ln-progress')` after writing `'150'` returns
309
- `'150'`, not `'100'`.
310
- - **Does NOT have an indeterminate / striped / pulsing mode.**
311
- No animated-stripes overlay, no shimmer, no pulse. For an
312
- indeterminate indicator on an unbounded operation, use
313
- `@mixin loader` (`scss/config/mixins/_loader.scss`) — different
314
- visual idiom, separate primitive.
315
- - **Does NOT debounce or throttle rapid-fire value changes.** Every
316
- attribute write triggers a synchronous `_render` call. For
317
- high-frequency flows (60+ writes/sec), the consumer is responsible
318
- for throttling.
319
-
320
- ## Common mistakes
321
-
322
- ### 1. Setting `style.width` manually inside the markup
323
-
324
- ```html
325
- <!-- WRONG — JS overwrites style.width on construction -->
326
- <div class="progress">
327
- <div data-ln-progress="75" class="success" style="width: 50%"></div>
328
- </div>
329
- ```
330
-
331
- The constructor's first action is `_render`, which writes
332
- `this.dom.style.width = percentage + '%'`. Whatever inline width you
333
- set in markup is overwritten on the first paint. If you want the bar
334
- to render at a specific width, set the value via `data-ln-progress`,
335
- not `style.width`.
336
-
337
- ### 2. Mixing percentage values with absolute max
338
-
339
- ```html
340
- <!-- WRONG — value is "50" interpreted as a count, not 50% -->
341
- <div class="progress">
342
- <div data-ln-progress="50" data-ln-progress-max="500" class="success"></div>
343
- </div>
344
- ```
345
-
346
- This renders 50/500 = 10% of the track, probably not what the author
347
- meant. The component does not interpret the value as a percentage; it
348
- treats it as a count against the max. Either write the absolute count
349
- (`250` for "50% of 500") or drop the max and use the default of 100
350
- with the percentage as the value.
351
-
352
- ### 3. Multiple bars sharing a parent without declaring parent-max
353
-
354
- ```html
355
- <!-- WRONG — three bars at 4 / 2 / 1, default max=100 each → 7% of track filled -->
356
- <div class="progress">
357
- <div data-ln-progress="4" class="success"></div>
358
- <div data-ln-progress="2" class="warning"></div>
359
- <div data-ln-progress="1" class="error"></div>
360
- </div>
361
- ```
362
-
363
- If the intent was "4 + 2 + 1 = 7 items, all visible proportionally,"
364
- declare the parent's max:
365
-
366
- ```html
367
- <!-- RIGHT -->
368
- <div class="progress" data-ln-progress-max="7">
369
- <div data-ln-progress="4" class="success"></div>
370
- <div data-ln-progress="2" class="warning"></div>
371
- <div data-ln-progress="1" class="error"></div>
372
- </div>
373
- ```
374
-
375
- Each bar reads parent max (`7`) as the denominator, and 4/7 + 2/7 +
376
- 1/7 fills the track exactly.
377
-
378
- ### 4. Forgetting `class="progress"` on the track wrapper
379
-
380
- Without the class, the wrapper has no styling — no recessed
381
- background, no rounded edges, no overflow clip. The bar still
382
- renders correctly but visually escapes its track.
383
-
384
- ## 🔧 Internals
385
-
386
- Source: `js/ln-progress/src/ln-progress.js` (~85 lines). Passive renderer — `_render` re-reads all three attributes fresh on every call; there is no cached value or max on the instance.
387
-
388
- ### Three observers, three concerns
389
-
390
- 1. `registerComponent`'s shared document-level observer instantiates new bars.
391
- 2. `_attrObserver` (per bar) watches the bar's own `data-ln-progress`/`-max`.
392
- 3. `_parentObserver` (per bar, always registered when a parent exists) watches the parent track's `data-ln-progress-max` — this is what makes a runtime change to the shared max re-render every child bar without any bar-side wiring.
393
-
394
- ### Construction order
395
-
396
- `_render` runs once immediately (dispatching the first `:change` before either observer is even wired), so a document-level listener attached before the bundle loads sees an event for every bar during the initial `DOMContentLoaded` scan; the two observers are attached after.
397
-
398
- ### Render writes
399
-
400
- Every `_render` call writes `style.width` and all four ARIA attributes (`role`, `aria-valuemin`, `aria-valuemax`, `aria-valuenow`) together, in the same pass that dispatches `:change` — there is no separate ARIA-sync step to keep in sync.
401
-
402
- ---
403
-
404
- ## Related
405
-
406
- - **`@mixin progress`** (`scss/config/mixins/_progress.scss`) — the
407
- recipe. Defines the track height, the recessed background, the
408
- rounded full radius, the overflow clip, and the inner-bar rules
409
- (initial `width: 0`, `transition: width var(--transition-base)`
410
- gated through `motion-safe`, rounded right edge on the last child).
411
- - **`scss/components/_progress.scss`** — applies the base mixin to
412
- `.progress` and applies the `.success` / `.warning` /
413
- `.error` colour variants.
1
+ # ln-progress
2
+
3
+ > Applied to a child bar element via `data-ln-progress="value"` inside a track container. An attribute observer detects
4
+ > changes to `data-ln-progress` or `data-ln-progress-max`: it calculates `percentage = (value / max) * 100`, sets inline
5
+ > `style.width = percentage + '%'`, and synchronizes `aria-valuenow` on the element with zero internal state.
6
+
7
+ ## Integration
8
+
9
+ ### In-Bundle (Standard Integration)
10
+ To load `ln-progress` as part of the unified `ln-ashlar` bundle, include the main script:
11
+ ```html
12
+ <script src="dist/ln-ashlar.iife.js" defer></script>
13
+ ```
14
+
15
+ ### Standalone (Zero-Dependency IIFE)
16
+ If you only need the progress bar component, load the compiled zero-dependency IIFE directly:
17
+ ```html
18
+ <script src="components/ln-progress/ln-progress.js" defer></script>
19
+ ```
20
+
21
+ ### Source Files & Development
22
+ - **Active Development Source**: [components/ln-progress/src/ln-progress.js](file:///c:/laragon/www/ln-ashlar/components/ln-progress/src/ln-progress.js) — The source of truth for component logic.
23
+ - **Compiled Standalone**: [components/ln-progress/ln-progress.js](file:///c:/laragon/www/ln-ashlar/components/ln-progress/ln-progress.js) — The compiled, ready-to-use standalone bundle.
24
+
25
+ ## Philosophy
26
+
27
+ A linear progress indicator is mechanically a track `<div>` wrapping
28
+ one or more child bar `<div>`s whose `width` is a percentage of the
29
+ track. The maths is `width = (value / max) * 100%`. What `ln-progress`
30
+ adds is the reactive plumbing: when the width gets recomputed, what
31
+ triggers a re-render, where the maximum comes from when multiple bars
32
+ share a denominator, and what survives a `destroy()` call.
33
+
34
+ The contract is simple: **the attribute IS the state**. There is no
35
+ `element.setValue(42)` API and no imperative entry point. You set
36
+ `data-ln-progress="42"` on a bar; the MutationObserver sees the
37
+ change; `_render` runs; the bar redraws. State is visible in DOM
38
+ Inspector at all times, can be set declaratively from server templates,
39
+ and survives `outerHTML` round-trips.
40
+
41
+ The component supports stacked bars sharing one denominator: declare
42
+ `data-ln-progress-max` on the parent track and every child bar reads
43
+ that as its max. This is what makes "4 done + 2 in progress + 1
44
+ pending = 7 total" render as three bars filling exactly one track.
45
+
46
+ ## Quick start
47
+
48
+ ```html
49
+ <div class="progress">
50
+ <div data-ln-progress="75" class="success"></div>
51
+ </div>
52
+ ```
53
+
54
+ A green bar at 75% of the track, no JS on the consumer side. The
55
+ `class="progress"` on the wrapper marks it as a track (pure CSS hook,
56
+ no JS instance is created). The `data-ln-progress="75"` on the child
57
+ marks it as a bar (instance is created, `style.width = "75%"` is
58
+ written).
59
+
60
+ ## Attributes
61
+
62
+ | Attribute | On | Description |
63
+ |---|---|---|
64
+ | `data-ln-progress="N"` | bar element | Current value of the bar. Number; can be negative or above max — the rendered percentage clamps to 0–100. Empty / non-numeric values render as 0%. |
65
+ | `data-ln-progress-max="N"` | bar element | Maximum value for this bar. Default `100`. Used as the denominator. Setting `max="0"` collapses to 0%. |
66
+ | `data-ln-progress-max="N"` | track element | Shared max for all bar children. Wins over each bar's own max. The track itself uses `class="progress"` for styling — no JS instance is created on it. |
67
+ | `class="success"` / `.warning` / `.error` | bar element | Colour variant. Plain CSS, no JS involvement. |
68
+
69
+ The track is marked with `class="progress"` (pure CSS hook). The bar
70
+ is marked with `data-ln-progress="N"` (JS instance).
71
+
72
+ The component writes `role="progressbar"`, `aria-valuemin="0"`,
73
+ `aria-valuemax`, and `aria-valuenow` on **each bar element** on every
74
+ render. `aria-valuenow` is clamped to `[0, max]` to match the rendered
75
+ bar (so an overshoot value of `150` against `max=100` reports as
76
+ `100`). Add `aria-label` on the bar yourself for screen-reader context
77
+ — the component does not invent a label.
78
+
79
+ ## Events
80
+
81
+ One event, bubbles, not cancelable.
82
+
83
+ | Event | `detail` | Dispatched on | Dispatched when |
84
+ |---|---|---|---|
85
+ | `ln-progress:change` | `{ target: HTMLElement, value: number, max: number, percentage: number }` | the bar element | every `_render` call: at construction, on every value or max attribute change (bar's own value, bar's own max, or parent track's max) |
86
+
87
+ `detail.value` is the raw `parseFloat` of the value attribute —
88
+ unclamped. `detail.max` is the resolved max (parent's first, then
89
+ bar's own, then 100). `detail.percentage` is the clamped 0–100
90
+ percentage written to `style.width`.
91
+
92
+ The event fires once per attribute write. Writing
93
+ `data-ln-progress="50"` then `"75"` synchronously fires the event
94
+ twice. The CSS transition smooths the visual to a single sweep, but
95
+ the event count is per-write.
96
+
97
+ There is no `:initialized` and no `:destroyed` event. The first
98
+ `ln-progress:change` fires inside `_render` during construction and serves as
99
+ the init signal for any consumer listening at the document level.
100
+
101
+ ## API
102
+
103
+ `window.lnProgress(root)` re-runs the init scan over `root`. The
104
+ document-level observer managed by `registerComponent` covers AJAX inserts and
105
+ `data-ln-progress` attribute additions; call this manually only when
106
+ you inject markup into a Shadow DOM root or another document context
107
+ the observer cannot see.
108
+
109
+ `el.lnProgress` on a bar exposes:
110
+
111
+ | Property | Type | Description |
112
+ |---|---|---|
113
+ | `dom` | `HTMLElement` | back-reference to the bar element |
114
+ | `_parentObserver` | `MutationObserver \| null` | watches the parent track's `data-ln-progress-max` |
115
+ | `destroy()` | method | disconnects the parent observer, deletes `el.lnProgress`. Does NOT remove `data-ln-progress` itself, the colour class, or the inline `style.width`. |
116
+
117
+ There is no `setValue(n)`, `setMax(n)`, `update()`, or `redraw()`
118
+ method. The attribute is the API. A programmatic update is
119
+ `el.setAttribute('data-ln-progress', n)`, exactly the same as a
120
+ server-side render that writes the attribute into markup. To force a
121
+ re-render without changing the value, write the same value back —
122
+ the MutationObserver fires on any write, including identical ones.
123
+
124
+ ## Max priority resolution
125
+
126
+ `_render` resolves the maximum in this order:
127
+
128
+ | Priority | Source | Truthy check |
129
+ |---|---|---|
130
+ | 1 | parent track's `data-ln-progress-max` parsed as float | parses to a finite number > 0 |
131
+ | 2 | bar's own `data-ln-progress-max` parsed as float | parses to a finite number > 0 |
132
+ | 3 | `100` (default) | always |
133
+
134
+ The chain is `parentMax || ownMax || 100`. Two consequences:
135
+
136
+ 1. A parent declaring `data-ln-progress-max="0"` falls through to the
137
+ bar's own max, because `0` is falsy in JS. Same for non-numeric
138
+ values like `data-ln-progress-max="abc"` — `NaN` is falsy.
139
+ 2. An own-max declaration is silently ignored when the parent declares
140
+ one. If a bar carries `data-ln-progress-max="50"` and the parent
141
+ carries `data-ln-progress-max="100"`, the parent wins — the bar
142
+ measures against 100. If you genuinely need per-bar maxes inside a
143
+ shared-max parent, do not declare the parent max; set each bar's
144
+ own max individually and accept that the bars no longer fill the
145
+ track in proportional fashion.
146
+
147
+ ## Examples
148
+
149
+ ### Minimal — fixed value
150
+
151
+ ```html
152
+ <div class="progress">
153
+ <div data-ln-progress="75" class="success"></div>
154
+ </div>
155
+ ```
156
+
157
+ The simplest case. Green bar at 75%, no JS on the consumer side.
158
+
159
+ ### Reactive — change value at runtime
160
+
161
+ ```html
162
+ <div id="upload-track" class="progress">
163
+ <div id="upload-bar" data-ln-progress="0" class="success"
164
+ aria-label="Upload progress"></div>
165
+ </div>
166
+ <button id="advance">Advance</button>
167
+ ```
168
+
169
+ ```js
170
+ let pct = 0;
171
+ document.getElementById('advance').addEventListener('click', function () {
172
+ pct = Math.min(100, pct + 10);
173
+ document.getElementById('upload-bar').setAttribute('data-ln-progress', pct);
174
+ });
175
+ ```
176
+
177
+ Each attribute write triggers a re-render and a `:change` event. The
178
+ CSS transition smoothly animates between widths. The component writes
179
+ `aria-valuenow` on the bar automatically — no manual sync needed.
180
+
181
+ ### Custom max — non-percentage scale (per-bar)
182
+
183
+ ```html
184
+ <!-- 7.5 GB of 10 GB — bar fills 75% of the track -->
185
+ <div class="progress">
186
+ <div data-ln-progress="7.5" data-ln-progress-max="10" class="warning"></div>
187
+ </div>
188
+ ```
189
+
190
+ `max="10"` shifts the denominator so 7.5 maps to 75%. There is no
191
+ text label slot inside the bar; place descriptive text as a sibling
192
+ element if you need it.
193
+
194
+ ### Stacked — multiple bars, one track, default max=100
195
+
196
+ ```html
197
+ <!-- CPU 40% / RAM 30% / Disk 30% — sums to 100% of the track -->
198
+ <div class="progress">
199
+ <div data-ln-progress="40" class="success" aria-label="CPU usage"></div>
200
+ <div data-ln-progress="30" class="warning" aria-label="RAM usage"></div>
201
+ <div data-ln-progress="30" class="error" aria-label="Disk usage"></div>
202
+ </div>
203
+ ```
204
+
205
+ Each bar uses the default max of 100. Three bars at 40 + 30 + 30 fill
206
+ the track exactly. If the values sum to less than 100, the remainder
207
+ of the track stays empty (the track's recessed background shows
208
+ through). If more than 100, each bar still renders at its clamped
209
+ 0–100 width and the visuals overflow the track horizontally — the
210
+ SCSS `overflow: hidden` clips the overflow so the visual stays clean,
211
+ but the maths is wrong.
212
+
213
+ ### Stacked — shared max on the parent track
214
+
215
+ ```html
216
+ <!-- 4 done + 2 in progress + 1 pending = 7 total — fills the track exactly -->
217
+ <div class="progress" data-ln-progress-max="7">
218
+ <div data-ln-progress="4" class="success" aria-label="Done"></div>
219
+ <div data-ln-progress="2" class="warning" aria-label="In progress"></div>
220
+ <div data-ln-progress="1" class="error" aria-label="Pending"></div>
221
+ </div>
222
+ ```
223
+
224
+ The track carries `data-ln-progress-max="7"`. Each child bar's
225
+ `_render` reads the parent's max first and uses `7` as the
226
+ denominator. Bar widths are 4/7 ≈ 57.1%, 2/7 ≈ 28.6%, 1/7 ≈ 14.3%,
227
+ summing to 100%.
228
+
229
+ Changing the parent's max at runtime
230
+ (`track.setAttribute('data-ln-progress-max', '10')`) re-renders each
231
+ child via the per-bar `_parentObserver`.
232
+
233
+ ### Upload progress — driving from XHR
234
+
235
+ ```html
236
+ <form id="upload-form">
237
+ <input type="file" name="file">
238
+ <div id="upload-track" class="progress">
239
+ <div id="upload-bar" data-ln-progress="0" class="success"
240
+ aria-label="Upload progress"></div>
241
+ </div>
242
+ <button type="submit">Upload</button>
243
+ </form>
244
+ ```
245
+
246
+ ```js
247
+ document.getElementById('upload-form').addEventListener('submit', function (e) {
248
+ e.preventDefault();
249
+ const bar = document.getElementById('upload-bar');
250
+ const file = e.target.querySelector('input[type=file]').files[0];
251
+ if (!file) return;
252
+
253
+ const xhr = new XMLHttpRequest();
254
+ const fd = new FormData(e.target);
255
+
256
+ xhr.upload.addEventListener('progress', function (ev) {
257
+ if (!ev.lengthComputable) return;
258
+ const pct = Math.round((ev.loaded / ev.total) * 100);
259
+ bar.setAttribute('data-ln-progress', pct);
260
+ });
261
+
262
+ xhr.upload.addEventListener('load', function () {
263
+ bar.setAttribute('data-ln-progress', 100);
264
+ });
265
+
266
+ xhr.open('POST', '/upload');
267
+ xhr.send(fd);
268
+ });
269
+ ```
270
+
271
+ ARIA on the bar is managed automatically by `ln-progress` —
272
+ `aria-valuenow` updates on every attribute write. The `progress`
273
+ event fires often during a large upload — every 16ms or so on a fast
274
+ connection. Each call rewrites the attribute, the MutationObserver
275
+ fires, `_render` runs, and the `:change` event dispatches. If
276
+ profiling shows it as a hot spot, throttle the JS-side write to
277
+ every 100ms before calling `setAttribute`.
278
+
279
+ ### Threshold-based colour — consumer code
280
+
281
+ The component has no automatic threshold colours. `class="success"`
282
+ / `.warning` / `.error` are static — JS does not flip them based on
283
+ value. If you want red below 30% and green above 70%, that is
284
+ consumer code:
285
+
286
+ ```js
287
+ function setProgressWithThreshold(bar, value) {
288
+ bar.setAttribute('data-ln-progress', value);
289
+ bar.classList.remove('success', 'warning', 'error');
290
+ if (value < 30) bar.classList.add('error');
291
+ else if (value < 70) bar.classList.add('warning');
292
+ else bar.classList.add('success');
293
+ }
294
+ ```
295
+
296
+ The colour-class swap is a `classList` write — no MutationObserver
297
+ fires for it (the component watches `data-ln-progress` and
298
+ `data-ln-progress-max`, not `class`). The new colour applies via plain
299
+ CSS rules in `theme/components/_progress.scss`.
300
+
301
+ ## What `ln-progress` does NOT do
302
+
303
+ - **Does NOT clamp the input attribute.** It clamps the *computed
304
+ percentage* between 0 and 100 before writing the width. The
305
+ attribute can hold any number; reading
306
+ `el.getAttribute('data-ln-progress')` after writing `'150'` returns
307
+ `'150'`, not `'100'`.
308
+ - **Does NOT have an indeterminate / striped / pulsing mode.**
309
+ No animated-stripes overlay, no shimmer, no pulse. For an
310
+ indeterminate indicator on an unbounded operation, use
311
+ `@mixin loader` (`theme/config/mixins/_loader.scss`) — different
312
+ visual idiom, separate primitive.
313
+ - **Does NOT debounce or throttle rapid-fire value changes.** Every
314
+ attribute write triggers a synchronous `_render` call. For
315
+ high-frequency flows (60+ writes/sec), the consumer is responsible
316
+ for throttling.
317
+
318
+ ## Common mistakes
319
+
320
+ ### 1. Setting `style.width` manually inside the markup
321
+
322
+ ```html
323
+ <!-- WRONG — JS overwrites style.width on construction -->
324
+ <div class="progress">
325
+ <div data-ln-progress="75" class="success" style="width: 50%"></div>
326
+ </div>
327
+ ```
328
+
329
+ The constructor's first action is `_render`, which writes
330
+ `this.dom.style.width = percentage + '%'`. Whatever inline width you
331
+ set in markup is overwritten on the first paint. If you want the bar
332
+ to render at a specific width, set the value via `data-ln-progress`,
333
+ not `style.width`.
334
+
335
+ ### 2. Mixing percentage values with absolute max
336
+
337
+ ```html
338
+ <!-- WRONG — value is "50" interpreted as a count, not 50% -->
339
+ <div class="progress">
340
+ <div data-ln-progress="50" data-ln-progress-max="500" class="success"></div>
341
+ </div>
342
+ ```
343
+
344
+ This renders 50/500 = 10% of the track, probably not what the author
345
+ meant. The component does not interpret the value as a percentage; it
346
+ treats it as a count against the max. Either write the absolute count
347
+ (`250` for "50% of 500") or drop the max and use the default of 100
348
+ with the percentage as the value.
349
+
350
+ ### 3. Multiple bars sharing a parent without declaring parent-max
351
+
352
+ ```html
353
+ <!-- WRONG — three bars at 4 / 2 / 1, default max=100 each → 7% of track filled -->
354
+ <div class="progress">
355
+ <div data-ln-progress="4" class="success"></div>
356
+ <div data-ln-progress="2" class="warning"></div>
357
+ <div data-ln-progress="1" class="error"></div>
358
+ </div>
359
+ ```
360
+
361
+ If the intent was "4 + 2 + 1 = 7 items, all visible proportionally,"
362
+ declare the parent's max:
363
+
364
+ ```html
365
+ <!-- RIGHT -->
366
+ <div class="progress" data-ln-progress-max="7">
367
+ <div data-ln-progress="4" class="success"></div>
368
+ <div data-ln-progress="2" class="warning"></div>
369
+ <div data-ln-progress="1" class="error"></div>
370
+ </div>
371
+ ```
372
+
373
+ Each bar reads parent max (`7`) as the denominator, and 4/7 + 2/7 +
374
+ 1/7 fills the track exactly.
375
+
376
+ ### 4. Forgetting `class="progress"` on the track wrapper
377
+
378
+ Without the class, the wrapper has no styling — no recessed
379
+ background, no rounded edges, no overflow clip. The bar still
380
+ renders correctly but visually escapes its track.
381
+
382
+ ## 🔧 Internals
383
+
384
+ Source: `components/ln-progress/src/ln-progress.js` (~85 lines). Passive renderer — `_render` re-reads all three attributes fresh on every call; there is no cached value or max on the instance.
385
+
386
+ ### Two observers, two concerns
387
+
388
+ 1. `registerComponent`'s shared document-level observer instantiates new bars and handles own attribute mutations via `onAttributeChange` (`data-ln-progress`, `data-ln-progress-max`).
389
+ 2. `_parentObserver` (per bar, always registered when a parent exists) watches the parent track's `data-ln-progress-max` — this is what makes a runtime change to the shared max re-render every child bar without any bar-side wiring.
390
+
391
+ ### Construction order
392
+
393
+ `_render` runs once immediately (dispatching the first `:change` before the parent observer is wired), so a document-level listener attached before the bundle loads sees an event for every bar during the initial `DOMContentLoaded` scan; the parent observer is attached after.
394
+
395
+ ### Render writes
396
+
397
+ Every `_render` call writes `style.width` and all four ARIA attributes (`role`, `aria-valuemin`, `aria-valuemax`, `aria-valuenow`) together, in the same pass that dispatches `:change` — there is no separate ARIA-sync step to keep in sync.
398
+
399
+ ---
400
+
401
+ ## Related
402
+
403
+ - **`@mixin progress`** (`theme/config/mixins/_progress.scss`) — the
404
+ recipe. Defines the track height, the recessed background, the
405
+ rounded full radius, the overflow clip, and the inner-bar rules
406
+ (initial `width: 0`, `transition: width var(--transition-base)`
407
+ gated through `motion-safe`, rounded right edge on the last child).
408
+ - **`theme/components/_progress.scss`** — applies the base mixin to
409
+ `.progress` and applies the `.success` / `.warning` /
410
+ `.error` colour variants.