@micha.bigler/ui-core-micha 3.14.3 → 4.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 (367) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +7 -0
  3. package/dist/auth/UserMenu.js +1 -1
  4. package/dist/auth/apiClient.js +7 -6
  5. package/dist/chat/ChatList.js +1 -1
  6. package/dist/chat/ChatThread.js +2 -1
  7. package/dist/chat/ChatWorkspace.js +1 -1
  8. package/dist/chat/ModelPicker.js +2 -5
  9. package/dist/chat/kitSxRegistry.js +9 -0
  10. package/dist/chat/useChatSocket.js +1 -1
  11. package/dist/components/LoginForm.js +1 -1
  12. package/dist/components/SecurityComponent.js +1 -1
  13. package/dist/components/UserListComponent.js +2 -8
  14. package/dist/components/charts/ChartFrame.js +1 -1
  15. package/dist/components/charts/ScatterChart.js +2 -1
  16. package/dist/components/charts/kitSxRegistry.js +6 -0
  17. package/dist/entries/charts.js +6 -0
  18. package/dist/entries/chat.js +8 -0
  19. package/dist/entries/markdown.js +1 -0
  20. package/dist/index.js +0 -14
  21. package/dist/layout/SectionNav.js +1 -1
  22. package/dist/messaging/AttachmentList.js +2 -2
  23. package/dist/messaging/Composer.js +2 -1
  24. package/dist/messaging/ConversationLaunchers.js +1 -4
  25. package/dist/messaging/ConversationList.js +1 -1
  26. package/dist/messaging/DirectMessageLauncher.js +1 -1
  27. package/dist/messaging/MessagingProvider.js +0 -0
  28. package/dist/messaging/PollCard.js +7 -5
  29. package/dist/messaging/ReadTicks.js +2 -2
  30. package/dist/messaging/Thread.js +5 -3
  31. package/dist/notifications/NotificationBell.js +4 -7
  32. package/dist/notifications/NotificationSettings.js +1 -1
  33. package/dist/onboarding/OnboardingProvider.js +3 -6
  34. package/dist/pages/AccountPage.js +1 -2
  35. package/dist/pages/LoginPage.js +2 -3
  36. package/dist/pages/PasswordChangePage.js +2 -3
  37. package/dist/pages/PasswordInvitePage.js +2 -3
  38. package/dist/pages/PasswordResetRequestPage.js +2 -3
  39. package/dist/pages/SignUpPage.js +2 -3
  40. package/dist/pages/SignupConfirmPage.js +2 -3
  41. package/dist/theme/createAppTheme.js +5 -4
  42. package/dist/theme/kitSxEntry.js +11 -0
  43. package/dist/theme/kitSxRegistry.js +2 -15
  44. package/dist/theme/themeCompleteness.js +3 -12
  45. package/dist/utils/auth-errors.js +4 -3
  46. package/package.json +41 -16
  47. package/.github/workflows/publish.yml +0 -126
  48. package/DESIGN.md +0 -60
  49. package/WORK_ORDERS.md +0 -148
  50. package/dev/entries.jsx +0 -721
  51. package/dev/fixtures.js +0 -26
  52. package/dev/main.jsx +0 -75
  53. package/dev/mockTransport.jsx +0 -128
  54. package/docs/CHART-LAYOUT.md +0 -157
  55. package/docs/DARK-TOKENS.md +0 -140
  56. package/docs/TYPE-SCALE.md +0 -111
  57. package/docs/messaging-deviations.md +0 -118
  58. package/index.html +0 -12
  59. package/renovate.json +0 -10
  60. package/scripts/chart-api-census.mjs +0 -415
  61. package/src/auth/AuthContext.jsx +0 -158
  62. package/src/auth/ReauthModal.jsx +0 -82
  63. package/src/auth/UserMenu.jsx +0 -121
  64. package/src/auth/apiClient.jsx +0 -232
  65. package/src/auth/authApi.jsx +0 -539
  66. package/src/auth/authConfig.jsx +0 -34
  67. package/src/auth/reauth.js +0 -47
  68. package/src/chat/ChatComposer.jsx +0 -36
  69. package/src/chat/ChatList.jsx +0 -87
  70. package/src/chat/ChatThread.jsx +0 -128
  71. package/src/chat/ChatWorkspace.jsx +0 -244
  72. package/src/chat/MarkdownReply.jsx +0 -22
  73. package/src/chat/ModelPicker.jsx +0 -85
  74. package/src/chat/api.js +0 -18
  75. package/src/chat/useChatSocket.js +0 -32
  76. package/src/chat/wire.js +0 -7
  77. package/src/components/AccessCodeManager.jsx +0 -288
  78. package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
  79. package/src/components/AllowedEmailDomainsManager.jsx +0 -92
  80. package/src/components/AuthFactorRequirementCard.jsx +0 -95
  81. package/src/components/BulkInviteCsvTab.jsx +0 -198
  82. package/src/components/EmptyState.jsx +0 -38
  83. package/src/components/LoginForm.jsx +0 -159
  84. package/src/components/MFAComponent.jsx +0 -350
  85. package/src/components/MfaLoginComponent.jsx +0 -252
  86. package/src/components/MobileBottomNav.jsx +0 -82
  87. package/src/components/PasskeysComponent.jsx +0 -252
  88. package/src/components/PasswordChangeForm.jsx +0 -87
  89. package/src/components/PasswordResetRequestForm.jsx +0 -43
  90. package/src/components/PasswordSetForm.jsx +0 -97
  91. package/src/components/ProfileComponent.jsx +0 -348
  92. package/src/components/QrSignupManager.jsx +0 -404
  93. package/src/components/QrSignupValidityManager.jsx +0 -106
  94. package/src/components/RegistrationMethodsManager.jsx +0 -150
  95. package/src/components/SecurityComponent.jsx +0 -141
  96. package/src/components/SocialLoginButtons.jsx +0 -83
  97. package/src/components/SoftChip.jsx +0 -93
  98. package/src/components/StatTile.jsx +0 -39
  99. package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
  100. package/src/components/TurnstileWidget.jsx +0 -99
  101. package/src/components/UserInviteComponent.jsx +0 -76
  102. package/src/components/UserListComponent.jsx +0 -618
  103. package/src/components/charts/BarChart.jsx +0 -102
  104. package/src/components/charts/ChartFrame.jsx +0 -214
  105. package/src/components/charts/LineChart.jsx +0 -126
  106. package/src/components/charts/ScatterChart.jsx +0 -299
  107. package/src/components/charts/TimeSeriesChart.jsx +0 -300
  108. package/src/components/charts/chartDefaults.js +0 -644
  109. package/src/components/charts/chartLabels.js +0 -36
  110. package/src/components/charts/exportChart.js +0 -258
  111. package/src/components/charts/formatters.js +0 -39
  112. package/src/components/charts/palette.js +0 -33
  113. package/src/components/charts/yearTickInterval.js +0 -7
  114. package/src/i18n/authTranslations.ts +0 -2651
  115. package/src/i18n/chartsTranslations.ts +0 -91
  116. package/src/i18n/chatTranslations.ts +0 -55
  117. package/src/i18n/messagingTranslations.ts +0 -120
  118. package/src/i18n/notificationsTranslations.ts +0 -32
  119. package/src/i18n/onboardingTranslations.ts +0 -29
  120. package/src/i18n/sectionNavTranslations.ts +0 -20
  121. package/src/i18n/uiCoreTranslations.js +0 -29
  122. package/src/i18n/userMenuTranslations.ts +0 -5
  123. package/src/index.js +0 -158
  124. package/src/layout/PageLayout.jsx +0 -36
  125. package/src/layout/SectionNav.jsx +0 -320
  126. package/src/messaging/AttachmentList.jsx +0 -145
  127. package/src/messaging/Composer.jsx +0 -197
  128. package/src/messaging/ConversationLaunchers.jsx +0 -51
  129. package/src/messaging/ConversationList.jsx +0 -109
  130. package/src/messaging/DirectMessageLauncher.jsx +0 -112
  131. package/src/messaging/MessageBubble.jsx +0 -183
  132. package/src/messaging/MessagingProvider.jsx +0 -0
  133. package/src/messaging/MessagingScopeConfig.jsx +0 -24
  134. package/src/messaging/PollCard.jsx +0 -163
  135. package/src/messaging/ReactionBar.jsx +0 -43
  136. package/src/messaging/ReadTicks.jsx +0 -82
  137. package/src/messaging/Thread.jsx +0 -195
  138. package/src/messaging/api.js +0 -48
  139. package/src/messaging/composerImageCompression.js +0 -35
  140. package/src/messaging/linkifyText.jsx +0 -37
  141. package/src/notifications/NotificationBell.jsx +0 -84
  142. package/src/notifications/NotificationSettings.jsx +0 -330
  143. package/src/notifications/NotificationsProvider.jsx +0 -224
  144. package/src/notifications/PopupSurface.jsx +0 -105
  145. package/src/notifications/api.js +0 -55
  146. package/src/notifications/feedApi.js +0 -18
  147. package/src/notifications/realtime.jsx +0 -138
  148. package/src/notifications/serviceWorker/sw.js +0 -50
  149. package/src/onboarding/OnboardingProvider.jsx +0 -326
  150. package/src/onboarding/OnboardingWizard.jsx +0 -61
  151. package/src/onboarding/WizardDialogShell.jsx +0 -51
  152. package/src/onboarding/api.js +0 -13
  153. package/src/onboarding/stepSelection.js +0 -16
  154. package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
  155. package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
  156. package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
  157. package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
  158. package/src/pages/AccountPage.jsx +0 -394
  159. package/src/pages/LoginPage.jsx +0 -343
  160. package/src/pages/PasswordChangePage.jsx +0 -62
  161. package/src/pages/PasswordInvitePage.jsx +0 -177
  162. package/src/pages/PasswordResetRequestPage.jsx +0 -68
  163. package/src/pages/SignUpPage.jsx +0 -268
  164. package/src/pages/SignupConfirmPage.jsx +0 -155
  165. package/src/testing/checkKitIntegration.js +0 -233
  166. package/src/theme/createAppTheme.js +0 -423
  167. package/src/theme/fonts.js +0 -3
  168. package/src/theme/index.js +0 -21
  169. package/src/theme/kitSxRegistry.js +0 -285
  170. package/src/theme/themeCompleteness.js +0 -1045
  171. package/src/theme/tokens.js +0 -478
  172. package/src/utils/auth-errors.js +0 -76
  173. package/src/utils/authService.js +0 -223
  174. package/src/utils/resolveErrorText.js +0 -28
  175. package/src/utils/webauthn.js +0 -51
  176. package/tests/AccountPage.profileStatements.test.jsx +0 -162
  177. package/tests/AccountPage.test.jsx +0 -200
  178. package/tests/AuthContext.test.jsx +0 -129
  179. package/tests/BarChart.test.jsx +0 -112
  180. package/tests/BrowserPushStep.test.jsx +0 -110
  181. package/tests/ChartFrame.test.jsx +0 -336
  182. package/tests/ConversationList.test.jsx +0 -144
  183. package/tests/CookieConsentStep.test.jsx +0 -69
  184. package/tests/EmptyState.test.jsx +0 -62
  185. package/tests/LineChart.test.jsx +0 -80
  186. package/tests/MessagingProvider.test.jsx +0 -50
  187. package/tests/MobileBottomNav.test.jsx +0 -319
  188. package/tests/NotificationBell.test.jsx +0 -60
  189. package/tests/NotificationSettings.test.jsx +0 -276
  190. package/tests/NotificationsProvider.test.jsx +0 -240
  191. package/tests/OnboardingProvider.test.jsx +0 -294
  192. package/tests/OnboardingWizard.test.jsx +0 -143
  193. package/tests/PasswordChangeForm.test.jsx +0 -70
  194. package/tests/PasswordInvitePage.test.jsx +0 -142
  195. package/tests/PasswordSetForm.test.jsx +0 -70
  196. package/tests/PopupSurface.test.jsx +0 -142
  197. package/tests/ProfileComponent.refresh.test.jsx +0 -194
  198. package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
  199. package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
  200. package/tests/ScatterChart.test.jsx +0 -270
  201. package/tests/SectionNav.test.jsx +0 -363
  202. package/tests/SignUpPage.test.jsx +0 -208
  203. package/tests/SoftChip.test.jsx +0 -105
  204. package/tests/StatTile.test.jsx +0 -57
  205. package/tests/Thread.test.jsx +0 -151
  206. package/tests/TimeSeriesChart.test.jsx +0 -367
  207. package/tests/UserListComponent.test.jsx +0 -192
  208. package/tests/UserMenu.test.jsx +0 -171
  209. package/tests/apiClientLanguage.test.js +0 -33
  210. package/tests/apiClientPublicPaths.test.js +0 -178
  211. package/tests/authApiLogin.test.js +0 -42
  212. package/tests/authApiRegistration.test.js +0 -31
  213. package/tests/authErrorCodeRendering.test.jsx +0 -77
  214. package/tests/authErrors.test.js +0 -74
  215. package/tests/authTranslations.test.js +0 -74
  216. package/tests/chart-api-census.test.js +0 -366
  217. package/tests/chartAxisTickBand.test.jsx +0 -95
  218. package/tests/chartDefaults.test.js +0 -902
  219. package/tests/chartLabels.test.js +0 -50
  220. package/tests/chartsPalette.test.js +0 -99
  221. package/tests/chatApi.test.js +0 -48
  222. package/tests/chatDriverNames.test.jsx +0 -71
  223. package/tests/chatExports.test.js +0 -3
  224. package/tests/chatList.test.jsx +0 -84
  225. package/tests/chatPicker.test.jsx +0 -82
  226. package/tests/chatSocket.test.jsx +0 -26
  227. package/tests/chatThread.test.jsx +0 -116
  228. package/tests/chatTranslations.test.js +0 -47
  229. package/tests/chatWorkspace.test.jsx +0 -234
  230. package/tests/checkKitIntegration.test.jsx +0 -219
  231. package/tests/createAppTheme.test.js +0 -132
  232. package/tests/createAppThemeDark.test.js +0 -127
  233. package/tests/createAppThemeLightGuard.test.js +0 -40
  234. package/tests/createAppThemePageContrast.test.js +0 -108
  235. package/tests/createAppThemeTypography.test.js +0 -53
  236. package/tests/errorCallSiteGuard.test.js +0 -32
  237. package/tests/exportChart.test.js +0 -313
  238. package/tests/feedApi.test.js +0 -46
  239. package/tests/fixtures/light-theme-baseline.json +0 -2234
  240. package/tests/i18nAggregate.test.js +0 -120
  241. package/tests/i18nGermanVariant.test.js +0 -65
  242. package/tests/loginPageErrorResolution.test.jsx +0 -102
  243. package/tests/messagingApi.test.js +0 -32
  244. package/tests/messagingChunk6.test.jsx +0 -116
  245. package/tests/messagingCompactBubble.test.jsx +0 -64
  246. package/tests/messagingComposer.test.jsx +0 -136
  247. package/tests/messagingComposerChunk5.test.jsx +0 -113
  248. package/tests/messagingContractConformance.test.js +0 -69
  249. package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
  250. package/tests/messagingExports.test.js +0 -21
  251. package/tests/messagingInteractions.test.jsx +0 -136
  252. package/tests/messagingLastMessagePreview.test.jsx +0 -93
  253. package/tests/messagingLinkify.test.jsx +0 -51
  254. package/tests/messagingMessageActions.test.jsx +0 -163
  255. package/tests/messagingMsg6.test.jsx +0 -105
  256. package/tests/messagingMsg6f.test.jsx +0 -155
  257. package/tests/messagingMsg6g.test.jsx +0 -166
  258. package/tests/messagingMsg6h.test.jsx +0 -136
  259. package/tests/messagingMsg6i.test.jsx +0 -129
  260. package/tests/messagingPollRendering.test.jsx +0 -142
  261. package/tests/messagingReactionMerge.test.jsx +0 -77
  262. package/tests/messagingReadTicksTooltip.test.jsx +0 -36
  263. package/tests/messagingThreadReceipts.test.jsx +0 -178
  264. package/tests/messagingThreadingQuoting.test.jsx +0 -87
  265. package/tests/messagingUnreadLifecycle.test.jsx +0 -78
  266. package/tests/notificationsApi.test.js +0 -41
  267. package/tests/notificationsExports.test.js +0 -34
  268. package/tests/onboardingDescriptors.test.js +0 -132
  269. package/tests/packageTreeShaking.test.js +0 -126
  270. package/tests/realtime.test.jsx +0 -219
  271. package/tests/resolveErrorText.test.js +0 -72
  272. package/tests/stepSelection.test.js +0 -53
  273. package/tests/themeCompleteness.test.js +0 -626
  274. package/tests/timeSeriesLegend.test.js +0 -99
  275. package/tests/userScreenTranslations.test.jsx +0 -218
  276. package/tsconfig.build.json +0 -18
  277. package/vite.config.mjs +0 -6
  278. package/work-orders/AUTH-1.md +0 -74
  279. package/work-orders/AUTH-2.md +0 -74
  280. package/work-orders/AUTH-4.md +0 -183
  281. package/work-orders/AUTH-5.md +0 -154
  282. package/work-orders/AUTH-7.md +0 -181
  283. package/work-orders/AUTH-8.md +0 -184
  284. package/work-orders/CHART-1.md +0 -285
  285. package/work-orders/CHART-2.md +0 -222
  286. package/work-orders/CHART-3.md +0 -163
  287. package/work-orders/CHART-4.md +0 -203
  288. package/work-orders/CHART-5.md +0 -226
  289. package/work-orders/CHART-6.md +0 -428
  290. package/work-orders/CHART-7.md +0 -175
  291. package/work-orders/DX-1.md +0 -169
  292. package/work-orders/DX-2.md +0 -188
  293. package/work-orders/DX-3.md +0 -195
  294. package/work-orders/I18N-1.md +0 -254
  295. package/work-orders/I18N-3.md +0 -207
  296. package/work-orders/MSG-3.md +0 -375
  297. package/work-orders/MSG-3b.md +0 -1002
  298. package/work-orders/MSG-3c.md +0 -378
  299. package/work-orders/MSG-3d.md +0 -268
  300. package/work-orders/MSG-3e.md +0 -164
  301. package/work-orders/MSG-3f.md +0 -391
  302. package/work-orders/MSG-6e.md +0 -308
  303. package/work-orders/MSG-6f.md +0 -214
  304. package/work-orders/MSG-6g.md +0 -244
  305. package/work-orders/MSG-6h.md +0 -238
  306. package/work-orders/MSG-6i.md +0 -271
  307. package/work-orders/MSG-6j.md +0 -117
  308. package/work-orders/NOTIF-12.md +0 -214
  309. package/work-orders/NOTIF-13.md +0 -142
  310. package/work-orders/NOTIF-14.md +0 -151
  311. package/work-orders/PRIM-1.md +0 -177
  312. package/work-orders/SHELL-1.md +0 -255
  313. package/work-orders/SHELL-2.md +0 -197
  314. package/work-orders/SHELL-3.md +0 -359
  315. package/work-orders/SHELL-4.md +0 -351
  316. package/work-orders/SHELL-5.md +0 -345
  317. package/work-orders/SHELL-6.md +0 -210
  318. package/work-orders/THEME-1.md +0 -587
  319. package/work-orders/THEME-10.md +0 -303
  320. package/work-orders/THEME-11.md +0 -208
  321. package/work-orders/THEME-2.md +0 -339
  322. package/work-orders/THEME-3.md +0 -210
  323. package/work-orders/THEME-4.md +0 -271
  324. package/work-orders/THEME-5.md +0 -341
  325. package/work-orders/THEME-6.md +0 -247
  326. package/work-orders/THEME-7.md +0 -82
  327. package/work-orders/THEME-8.md +0 -211
  328. package/work-orders/THEME-9.md +0 -194
  329. package/work-orders/UCM-AUTH-10.md +0 -490
  330. package/work-orders/UCM-AUTH-11.md +0 -185
  331. package/work-orders/UCM-AUTH-12.md +0 -98
  332. package/work-orders/UCM-AUTH-13.md +0 -47
  333. package/work-orders/UCM-AUTH-9.md +0 -207
  334. package/work-orders/UCM-CHART-10.md +0 -150
  335. package/work-orders/UCM-CHART-11.md +0 -132
  336. package/work-orders/UCM-CHART-12.md +0 -348
  337. package/work-orders/UCM-CHART-13.md +0 -122
  338. package/work-orders/UCM-CHART-14.md +0 -198
  339. package/work-orders/UCM-CHART-15.md +0 -187
  340. package/work-orders/UCM-CHART-16.md +0 -169
  341. package/work-orders/UCM-CHART-17.md +0 -195
  342. package/work-orders/UCM-CHART-18.md +0 -200
  343. package/work-orders/UCM-CHART-8.md +0 -147
  344. package/work-orders/UCM-CHART-9.md +0 -265
  345. package/work-orders/UCM-CHAT-1.md +0 -190
  346. package/work-orders/UCM-CHAT-2.md +0 -75
  347. package/work-orders/UCM-DEP-1.md +0 -63
  348. package/work-orders/UCM-I18N-4.md +0 -226
  349. package/work-orders/UCM-I18N-5.md +0 -211
  350. package/work-orders/UCM-MSG-20.md +0 -192
  351. package/work-orders/UCM-PRIM-2.md +0 -114
  352. package/work-orders/UCM-SHELL-7.md +0 -134
  353. package/work-orders/UCM-SHELL-8.md +0 -125
  354. package/work-orders/UCM-TEST-1.md +0 -304
  355. package/work-orders/UCM-THEME-13.md +0 -69
  356. package/work-orders/UCM-THEME-14.md +0 -312
  357. package/work-orders/UCM-THEME-15.md +0 -356
  358. package/work-orders/UCM-THEME-16.md +0 -80
  359. package/work-orders/UCM-THEME-17.md +0 -111
  360. package/work-orders/UCM-THEME-18.md +0 -184
  361. package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
  362. package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
  363. package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
  364. package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
  365. package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
  366. package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
  367. package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
@@ -1,303 +0,0 @@
1
- # THEME-10 — A scatter preset, drawn from three real consumers
2
-
3
- ## Part A — Envelope
4
-
5
- **Goal.** Add a `ScatterChart` preset alongside `BarChart` / `LineChart` / `TimeSeriesChart`,
6
- with the same axis handling, tick formatting, legend and frame behaviour, so a consuming app
7
- can plot a point cloud without building its own chart from raw SVG.
8
-
9
- **Why.** hram draws **three** scatters entirely by hand — its own axis lines, gridlines, ticks,
10
- tick labels and rotated axis titles, on hard-coded canvases. Only `ChartFrame` comes from this
11
- package. That is not negligence: **this package has no scatter preset**, so there was nothing
12
- to reach for. Every fix this kit has made since (axis sizing, tick formatting, label placement,
13
- the role palette) passes all three by, and will keep doing so until a preset exists.
14
-
15
- | Consumer | Canvas | Composition |
16
- |---|---|---|
17
- | `AllocationPerformancePanel` | `WIDTH 720`, `HEIGHT 420`, `MARGIN.left 92` | cloud + computed envelope curve + three individually marked points; continuous per-point colouring by one of three equity measures |
18
- | `AccessGapScatterPanel` | `CHART_H 714`, responsive width, `M.left 58` | bubble cloud (radius by population, z-ordered) + labelled y=x reference diagonal; **categorical** per-point colouring (division or settlement) with a discrete legend; both axes pinned to 0–1, ticks fixed at 0/25/50/75/100 % |
19
- | `OptimizationResultsPanel` | `WIDTH 720`, `HEIGHT 420`, `MARGIN.left 72` | cloud at `r=6` + dashed reference line; the status-quo point drawn hollow (`fill: none`) against filled candidates |
20
-
21
- The operator chose this route deliberately over letting each app reach for MUI X directly
22
- (hram `FIX-15`, fork option b), so that scatters are built the same way as every other chart in
23
- the estate rather than becoming a second, parallel charting style.
24
-
25
- **On the single-consumer risk this WO originally carried.** The first draft was written when
26
- only the allocation panel was known, and warned that an abstraction drawn from one example is
27
- how the wrong shape gets frozen. **The operator then named the access panel, and a sweep found
28
- a third.** That risk is largely spent: three independent consumers, written by different hands
29
- at different times, are enough to tell a real axis of variation from a guess. What replaces it
30
- is a narrower discipline:
31
-
32
- - **Every option in the API must be traceable to one of the three panels above.** They are the
33
- requirements document. An option no listed consumer needs does not go in — adding one later
34
- is cheap, removing one from a published package is not.
35
- - **Two of the variation axes were discovered, not designed**, and that is the point: neither
36
- categorical-colour-plus-discrete-legend nor data-driven mark radius appears in the allocation
37
- panel at all. Had this been built from that panel alone, both would have been missed and the
38
- access panel would have had to bypass the preset.
39
- - The surface is no longer "provisional pending a second consumer" — but the docs should still
40
- state which consumers it was drawn from, so a fourth with different needs knows what it is
41
- extending.
42
-
43
- **Scope.**
44
-
45
- 1. **`ScatterChart` preset** with the same contract shape as the existing presets: `series`,
46
- `xAxis`, `yAxis`, `xAxisLabel`, `yAxisLabel`, `palette`, `grid`, `minHeight`, `aspect`,
47
- `hideLegend`, `legendPosition`, `slotProps`, and remaining props forwarded to MUI.
48
- 2. **Reuse the THEME-9 machinery rather than reimplementing it** — `withAxisDefaults`,
49
- `sizeYAxisForContent`, `spaceForRotatedTicks`, `defaultNumericTickFormatter`,
50
- `withGridDefaults`, `withChartSlotDefaults`. A preset that sizes its own axes differently
51
- from `BarChart` would re-open exactly what THEME-9 just closed.
52
- 3. **A neutral cloud by default.** Unlike a bar chart, where each series takes a categorical
53
- colour, an *undifferentiated* scatter cloud is one neutral mass — the role register's
54
- neutral, not a series hue. **The distinction matters, because one consumer colours
55
- categorically and is right to:** the access panel encodes division or settlement class, a
56
- real data dimension, and already draws those from the frozen family register. The allocation
57
- panel's defect is different in kind — it paints a cloud that encodes *nothing* in
58
- `dataViz.cost.main`, a KPI identity colour. Neutral is the default for "no dimension
59
- encoded", not a prohibition on colour.
60
- 4. **Three colouring modes, all three in use today:** neutral (default), **categorical** with a
61
- discrete legend (access — division, settlement), and **continuous** with a scale legend
62
- (allocation — three equity measures). Categorical draws from the family register's
63
- positional slots; the consumer must not have to hand-index a palette array to get them.
64
- 5. **Mark size: small fixed default, optionally data-driven.** The default is sized for hundreds
65
- of overlapping points (hram plots ~300; it must not fuse into a solid area). The access panel
66
- additionally needs a **radius bound to a value** (`max(3, 10·√(pop/maxPop))`) with **explicit
67
- z-ordering by that value**, so large bubbles do not hide small ones. Bubble sizing without a
68
- defined draw order is a silently wrong chart.
69
- 6. **Support the compositions the consumers actually need** — a point cloud **plus** overlaid
70
- reference geometry **plus** individually marked points:
71
- - reference geometry appears in all three: a computed curve (allocation's envelope), a
72
- straight identity line carrying a rotated inline label (access's y=x "no gap"), and a
73
- dashed reference line (optimization);
74
- - individually marked points appear in two, including a **hollow** state — optimization
75
- draws the status quo as `fill: none` with a coloured stroke against filled candidates.
76
-
77
- Whether that is one component with layers or a documented composition of MUI's
78
- `ChartContainer` + plots is an implementation decision — but a consumer must be able to do it
79
- *through* this package. If it cannot, the app bypasses the preset and we are back where we
80
- started.
81
- 7. **Axis furniture from theme tokens, not hard-coded greys.** This is the concrete regression
82
- the preset exists to stop: the access panel hard-codes `#F0F0F0` (grid), `#D0D0D0` (axis),
83
- `#999` (ticks), `#666` (tick labels), `#555` (axis titles), `#B0B0B0` (reference line) —
84
- six greys that no theme change can reach. The other two are better but still partial.
85
- 8. **Support a pinned tick set on a fixed domain.** The access panel's axes are both 0–1 with
86
- ticks fixed at 0/25/50/75/100 % — a deliberate choice (the y=x diagonal is only readable on
87
- equal, fixed axes), not something auto-ticking should override.
88
- 9. Documentation in `DESIGN.md` alongside the other chart guidance, including the axis-title
89
- rule THEME-9 introduced, and naming the three consumers the surface was drawn from.
90
-
91
- **Non-goals / do-not-touch.** No changes to the existing presets beyond what sharing helpers
92
- requires. No new colour tokens — everything comes from the existing role register and palette.
93
- No zoom, brush, selection-rectangle or animation features: none of them has a consumer, and
94
- each would be a guess. Do not change `ChartFrame`.
95
-
96
- **Explicitly NOT in scope — the row-based dot plots.** The same sweep found two more
97
- hand-drawn hram panels that also plot circles, and they are **a different chart, not a wide
98
- scatter**: `CalibrationPanel` is a forest plot (`MARGIN.left = 150`, one `ROW_HEIGHT` band per
99
- parameter, point estimate `r=5` plus a dashed interval) and `SensitivityAnalysisPanel` is a
100
- tornado (`MARGIN.left = 220`, `<rect>` bars per row). `OptimizationResultsPanel` carries a third
101
- of the same family in its divergence sub-view (`DIVERGENCE_MARGIN.left = 150`). Their y-axis is
102
- categorical, their left margin is 150–220 px of row labels, and their layout problem is row
103
- density — none of which a scatter preset should try to absorb. **Stretching this preset to cover
104
- them is exactly the over-generalisation this WO is guarding against.** They are a separate
105
- preset and a separate WO; note them, do not serve them.
106
-
107
- **Tier 3** — shared-core surface, new public API. `reviewer` mandatory; `ui_reviewer` too,
108
- since the deliverable is visual.
109
-
110
- **Tests to write.** The mark count — a scatter that silently drops points is the failure mode
111
- nobody sees, and it is trivial to assert. That the default cloud colour is the neutral role, not
112
- a categorical series colour. That bubble mode draws large marks **before** small ones (the
113
- z-order is the whole correctness of a bubble chart, and it is one assertion on draw order). Axis
114
- behaviour is already covered by THEME-9's tests if the helpers are reused — if a test has to be
115
- duplicated here, the helpers were not reused.
116
-
117
- **Risks.**
118
- - **Serving three consumers is not the same as serving all scatters.** The single-consumer risk
119
- is spent, but the opposite one is now live: with three shapes in hand it is tempting to
120
- generalise to a fourth that does not exist. Every option traces to a listed panel, or it does
121
- not go in. If the API starts growing during implementation, stop and report.
122
- - **The row-plot family is the specific over-reach to watch** (see non-goals). Two more panels
123
- draw circles and will look like near-misses. They are not.
124
- - **Performance at a few hundred marks** is fine; at tens of thousands it is not. State the
125
- tested magnitude in the docs rather than implying it scales indefinitely.
126
- - The overlay composition (scope 6) is where this can quietly fail its purpose. Verify against
127
- the real panel shapes — cloud + envelope + three special points, and bubble cloud + labelled
128
- diagonal — not against a synthetic example.
129
-
130
- **This WO is commissioned before the requirement set is closed — deliberately.** The operator
131
- dispatched it on 2026-08-14 knowing that the access and optimization panels will each get their
132
- own prototype, and that **those prototypes are expected to carry additional requirements**. The
133
- three shapes listed above are what is known now, not the final set. Two consequences the
134
- implementer must honour:
135
-
136
- - **Keep the surface additively extensible.** A later requirement must be satisfiable by adding
137
- a prop, not by changing the meaning of an existing one or reshaping the contract. Prefer an
138
- explicit option over an inferred behaviour wherever the two cost the same.
139
- - **Do not read this as licence to pre-build for the unknown.** The rule stands: every option
140
- ships because a listed panel needs it. "A prototype might later want X" is not a consumer.
141
- The insurance against the unknown is that the API can grow, not that it already did.
142
-
143
- **One forthcoming shape is already named — do NOT build it, but do not foreclose it.** The
144
- optimization panel will want its MILP solution and its simulated solution shown as a **paired
145
- point** (two marks joined by a connector, with the difference between them displayed). The
146
- plumbing is largely there: that panel already puts both on shared scales
147
- (`buildScale([...cloud.map(p => p.cost), ...milp.map(p => p.x)])`) and already joins the MILP
148
- points with a `polyline`. The part that is **not** additive is the data unit: every contract in
149
- this WO is *one mark per datum*, whereas a pair is two marks plus a link that must travel
150
- together — shared tooltip, shared colour, one hit target, and a delta label anchored between
151
- them. Retrofitting that means reshaping `series`, not adding a prop.
152
-
153
- So: **design the point contract so a "pair" variant can be added later without changing what a
154
- single point means** — most simply, by not assuming a 1:1 datum→mark mapping anywhere in the
155
- internals. Ship nothing for it now. It has no prototype yet, and two of its questions are
156
- app-level rather than preset-level: which key joins a MILP point to its simulated counterpart
157
- (the scatter's points come from `planningPoints`, the deltas from a separate `getDivergence`
158
- call keyed by regime), and whether the difference is rendered as a per-pair numeric label —
159
- which would be genuine mark labelling, absent from this preset — or carried by the connector
160
- length plus the tooltip, which costs nothing.
161
-
162
- **Delivery is not done at publish.** hram `FIX-15` (allocation) is the first consumer and is
163
- blocked on this package; the pin bump and that panel's rebuild are what prove the preset works.
164
- The access and optimization panels are known consumers with **no WO yet** — they are what this
165
- surface was designed against, so they are not optional evidence, but converting them is separate
166
- work to be scheduled, not folded in here. THEME-8 and THEME-9 are both sitting published with
167
- consumer pins still open, so bundle the pin bump rather than adding a third pending hop.
168
-
169
- ## Part B — Implementation map — ADDRESSED TO THE IMPLEMENTER
170
-
171
- Codex was not dispatched: `.claude/codex-status.md` already carried a same-day (2026-08-14)
172
- `unavailable` line (THEME-8/9/FIX-11/FIX-13, same day), so per the known-unavailable shortcut
173
- the Orchestrator implemented directly, which flips authorship — `reviewer` + `ui_reviewer`
174
- became mandatory (already required at Tier 3).
175
-
176
- ### What landed
177
-
178
- `src/components/charts/ScatterChart.jsx` (new), exported from `src/index.js` alongside
179
- `BarChart`/`LineChart`/`TimeSeriesChart`:
180
-
181
- - **Contract shape (scope 1).** `series`/`xAxis`/`yAxis`/`xAxisLabel`/`yAxisLabel`/`palette`/
182
- `grid`/`minHeight`/`aspect`/`hideLegend`/`legendPosition`/`slotProps`, remaining props
183
- forwarded to MUI's own `ScatterChart` — same shape as `BarChart`/`LineChart`.
184
- - **THEME-9 reuse (scope 2).** `withAxisDefaults` + `sizeYAxisForContent` +
185
- `defaultNumericTickFormatter` for the Y axis (both axes on a scatter are numeric by
186
- construction, so no band/point default or `spaceForRotatedTicks` reuse was needed — X gets
187
- MUI's own default sizing, which THEME-9 already established is adequate for an unrotated
188
- numeric axis).
189
- - **Neutral default + colouring modes (scope 3/4).** `palette.js` gained a `neutral` key
190
- (`theme.palette.text.secondary`) — a single series with no explicit `color` renders in that
191
- tone; multiple series render categorically (MUI's own multi-series legend, unchanged
192
- mechanism already used by `BarChart`); continuous colouring is the caller's own resolved
193
- colour applied via `getPointStyle` (this package does not ship a colour-scale/legend
194
- component — out of scope, see "Not built" below).
195
- - **Bubble sizing + explicit z-order (scope 5).** `scaleBubbleRadius(value, maxValue)` —
196
- `max(3, 10·√(value/maxValue))`, exported. `sizeAccessor` on `ScatterChart` turns on bubble
197
- mode: every series is sorted largest-first before being handed to MUI (SVG paints later
198
- elements on top, so small bubbles are never hidden), and the max is **one chart-wide value
199
- across every series** (matches the access panel's own `Math.max(1, ...points.map(p =>
200
- p.population))` over ALL points regardless of division/settlement — a per-series max was an
201
- implementation bug caught during the rendered verification below, fixed before commit).
202
- - **Individually marked points (scope 6, part).** `getPointStyle(point, {seriesId, dataIndex})
203
- => {hollow?, color?, shape?, radius?, strokeWidth?}` — a status-quo point drawn hollow
204
- (`fill:none`), a per-point colour override, `'circle'|'square'|'diamond'`. Every shape option
205
- traces to the allocation panel specifically (`AllocationPerformancePanel.jsx:500-554`, its
206
- `ShapeGlyph` legend): `isSelected` → diamond, `isTargeted` → **square**, plain candidate →
207
- filled circle, status quo → **hollow** circle — the SAME panel that also motivated the
208
- hollow state (allocation's own status-quo point is hollow too, not only optimization's — the
209
- Envelope's Part A table names optimization's hollow explicitly but allocation's own point
210
- rendering carries the identical `fill: isStatusQuo ? 'none' : color` pattern). `ui_reviewer`
211
- finding U1 flagged `'square'` as apparently untraceable; it traces to `isTargeted`, cited here
212
- because the original Part A table didn't spell out allocation's shape variety. Implemented via
213
- a custom MUI marker slot (`slots.marker`) — confirmed against `@mui/x-charts/ScatterChart/
214
- Scatter.js` that MUI's own marker size is fixed PER SERIES (`series.markerSize`), never
215
- derived from a point's own `z`, so per-point radius/colour/shape genuinely requires replacing
216
- the marker, not a prop MUI already exposes.
217
- - **Reference geometry (scope 6, part).** `ScatterReferenceCurve`/`ScatterReferenceLine`,
218
- exported, rendered as `ScatterChart` `children` — read the chart's own live `useXScale`/
219
- `useYScale` (confirmed exported from `@mui/x-charts/hooks`), never a second hand-rolled pixel
220
- mapping. Covers all three named shapes: allocation's envelope (curve), access's labelled y=x
221
- diagonal (line + inline rotated label), optimization's dashed threshold (line, dashed).
222
- - **Theme-token furniture (scope 7).** Inherited for free — grid/axis/tick colours already come
223
- from the theme via `withGridDefaults`/MUI's own axis styling (the same mechanism `BarChart`/
224
- `LineChart` already use), never a hard-coded grey.
225
- - **Pinned tick set on a fixed domain (scope 8).** No new mechanism needed — MUI's own
226
- `tickInterval: any[]` (confirmed in `@mui/x-charts/hooks/useTicks.d.ts`) already accepts an
227
- explicit tick array via plain `xAxis`/`yAxis` passthrough (`tickInterval: [0,0.25,0.5,0.75,1]`
228
- reproduces the access panel's fixed 0/25/50/75/100% ticks exactly).
229
- - **DESIGN.md (scope 9).** New principle 8b: neutral-by-default, the three colouring modes, the
230
- bubble-z-order rule, and the explicit "not this preset" pointer to the row-based dot-plot
231
- family (forest plot, tornado — a different WO).
232
-
233
- ### Forward-compatibility requirement (added to the Envelope after initial implementation)
234
-
235
- The WO was updated mid-session to add: "commissioned before the requirement set is closed,"
236
- additive-only extension going forward, and an explicit instruction to **not assume a 1:1
237
- datum→mark mapping anywhere in the internals**, so a later "paired point" shape (optimization's
238
- MILP-vs-simulated pair) can be added without reshaping the contract. Reviewed against the
239
- landed code: nothing here assumes a fixed mark count per datum — a point object may carry
240
- arbitrary extra fields beyond `{x,y,z,id}` (already exercised by `getPointStyle`'s status-
241
- quo/bubble usage), `getPointStyle`/`sizeAccessor` key off the datum itself, and reference
242
- geometry composes via `children` rather than being baked into the per-point contract. A pair
243
- variant can layer on later (extra series entries + a connector drawn through `children`)
244
- without touching `series`'s shape or either callback's signature. Documented in the component's
245
- own docstring so this reasoning survives independent of this WO file.
246
-
247
- ### Not built (named, not silently dropped)
248
-
249
- - **A continuous colour-scale/legend component.** The WO's scope items describe "continuous
250
- with a scale legend" as a supported MODE, not a component this package must ship — the
251
- allocation panel already owns a working gradient-legend implementation
252
- (`AllocationPerformancePanel.jsx:594-646`) built from its own `getColorScale` utility;
253
- `getPointStyle` is the seam that lets it apply that resolved colour per point through this
254
- preset. Building a second, generic version was not named as a required deliverable in scope
255
- 1-9 and risked exactly the over-generalisation the WO warns against (no listed consumer needs
256
- a DIFFERENT continuous-legend implementation from the one it already has).
257
- - **The paired-point shape.** Explicitly deferred per the Envelope update above — no prototype,
258
- no WO, ship nothing.
259
- - ~~A `dev/entries.jsx` fixture for optimization's exact shape was not added as a third harness
260
- entry~~ — **reversed per `ui_reviewer` finding U2.** It was added (`OptimizationShapeScatterEntry`)
261
- and rendered live: this is exactly the combination (no `yAxis` min/max, no explicit `palette`)
262
- that `reviewer` finding R1 caught a real bug on (`sizeYAxisForContent` never received number
263
- candidates from scatter's `{x,y}` point objects) — the other two fixtures each avoided the gap
264
- incidentally (access sets explicit `yAxis` min/max; allocation's Y magnitude happened to fit
265
- MUI's flat default), so U2's point that live rendering catches what the unit suite doesn't was
266
- correct and specific, not general caution.
267
-
268
- ### Target repo
269
-
270
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`
271
-
272
- ## Part C — Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
273
-
274
- > **If you are the implementer reading this work order as your own specification: STOP at this
275
- > line.**
276
-
277
- - **Execution.** Codex known-unavailable today (see Part B) — implemented directly in Claude.
278
- - **Review routing.** `reviewer` + `ui_reviewer`, concurrent, one background batch.
279
- - **Verification.** Package suite (`ScatterChart.test.jsx` — 15 tests: mark count, neutral
280
- default, categorical/explicit palette override, bubble z-order, chart-wide bubble max,
281
- hollow/shape marker rendering, reference curve/line geometry against the live scale;
282
- `chartsPalette.test.js` gained the `neutral` key pin; full affected set incl.
283
- `BarChart.test.jsx`/`LineChart.test.jsx`/`chartDefaults.test.js` — 51 tests, green).
284
- **Rendered check against both real consumer shapes, done live** in `ui-core-micha`'s own dev
285
- harness (`dev/entries.jsx`, two new entries: "ScatterChart (allocation shape)" and "(access
286
- shape)"), on the browser tab already carrying real viewport dimensions (cross-origin
287
- navigation from the previously-displayed hram tab, working around the earlier THEME-9 session's
288
- 0×0-viewport limitation on freshly-opened tabs).
289
- - **Allocation shape:** 6/6 points rendered (mark count matches supplied), hollow status-quo
290
- (`fill:"none"`), continuous per-point colour gradient (hsl hue 188→164→132→104.8 tracking
291
- the fixture's equity values), diamond shape for the selected point, envelope curve drawn
292
- through the correct live-scale pixel coordinates (`M85,295 L377.9,196 L729.3,86 L905,64`,
293
- matching the four supplied envelope points).
294
- - **Access shape:** 7/7 points rendered across 3 categorical series (colours `#0F62FE`/
295
- `#24A148`/`#FF832B`, correct per-series), fixed 0/25/50/75/100% ticks on both axes, labelled
296
- dashed y=x reference line present. **First render caught a real bug**: bubble radii were
297
- computed per-series instead of chart-wide, so a small series' own largest point rendered at
298
- full radius regardless of its actual size relative to other series — fixed (`chartMax`,
299
- single value across all series' data) and re-verified live: radii now correctly proportional
300
- across all three series against one shared max (7.75/6.32/3.16 · 5.48/4.08 · 10/3).
301
- - **Register & commit.** Advance the THEME-10 row with the reviewer verdicts and the rendered
302
- mark counts above. hram `FIX-15` (the first actual consumer) does not exist yet — no pin bump
303
- to bundle in this WO; that happens when `FIX-15` is authored.
@@ -1,208 +0,0 @@
1
- # THEME-11 — The kit has no opinion on chart margins, so every chart in the estate inherits MUI's
2
-
3
- ## Part A — Envelope
4
-
5
- **Goal.** Give the chart presets a margin default of their own, so charts stop falling back to
6
- MUI X's `DEFAULT_MARGINS` of 20 px on all four sides, and trim the one comparable block in
7
- `ChartFrame`. The whitespace around plots shrinks everywhere at once, without each consuming
8
- panel having to hand-tune a `margin` prop.
9
-
10
- **Why — measured, not estimated.** hram's four KPI cards were taken apart pixel by pixel. The
11
- chart is 270 px tall and its plot area is 202 px; the remaining 167 px is furniture, and the
12
- budget accounts for the card's observed ~370 px exactly:
13
-
14
- | Block | px | Owner |
15
- |---|---|---|
16
- | `ChartFrame` root padding (`p: 2`) top | 16 | this package |
17
- | Title text | ~24 | this package |
18
- | Title box `mb: 2` | 16 | this package |
19
- | `margin.top` | 20 | **MUI default** |
20
- | **Plot area** | **202** | — |
21
- | x-axis height (`DEFAULT_AXIS_SIZE_HEIGHT`) | 25 | MUI default |
22
- | `margin.bottom` | 20 | **MUI default** |
23
- | Forced blank (`minHeight 300` over `height 270`) | 30 | hram — its own `FIX-16` |
24
-
25
- The forced blank is **centred, not stacked at the bottom**: `ChartFrame`'s content box is
26
- `display: flex` with `alignItems: center`, so a `minHeight` above the chart's own height splits
27
- evenly — 15 px above the chart and 15 px below. It is listed once here for the total.
28
- | `ChartFrame` root padding bottom | 16 | this package |
29
-
30
- **The MUI defaults apply because this package never forms an opinion.** `spaceForRotatedTicks`
31
- returns the caller's `margin` untouched unless the x-ticks are rotated *and* the caller left
32
- `bottom` unset — so for the ordinary case it hands `margin: undefined` straight to
33
- `MuiBarChart`, and `DEFAULT_MARGINS = { top: 20, bottom: 20, left: 20, right: 20 }` takes over.
34
- THEME-9 gave the kit axis *sizing*; margins were never in its scope. Every chart in every
35
- consuming app has been paying those 20 px since.
36
-
37
- **What each side is actually for**, which is what makes the trim safe rather than a guess:
38
- - `margin.left` sits **outside** the y-axis width — this package's own comment records
39
- `drawingArea.left = margin.left + axisSizeLeft`. The tick labels live in `axisSizeLeft`, so
40
- `margin.left` is pure padding.
41
- - `margin.bottom` sits **below** the x-axis block; `DEFAULT_AXIS_SIZE_HEIGHT` already contains
42
- the tick label.
43
- - `margin.top` is the only one carrying real content: the topmost y tick label is centred on its
44
- gridline and overhangs the plot by roughly half a caption line-height (~9 px). This is why the
45
- top cannot simply be trimmed to match the others.
46
- - `margin.right` is the risky one — see the scope note.
47
-
48
- **Scope.**
49
-
50
- 1. **A margin default in the shared chart path**, applied when the caller passes no margin and
51
- merged per-side (not wholesale) when the caller passes a partial one, so an explicit
52
- `{ left: 60 }` does not silently reinstate 20 px on the other three sides:
53
- - `top: 10` — leaves the ~9 px overhang of the topmost tick label its room. **Not 8**: at 8
54
- that label sits on the edge.
55
- - `bottom: 8`
56
- - `left: 8`
57
- - `right: 16` — **deliberately a smaller trim than the other sides.** On a band x-axis the
58
- tick labels are centred and 8 would be fine, but on a **linear** x-axis the last tick label
59
- sits *at* the edge and overhangs by half its width (~13 px for a four-character label at
60
- caption size). 16 is the value that survives both. Do **not** make this conditional on
61
- scale type: that is inferred behaviour serving no named consumer, and a chart that genuinely
62
- needs more can pass `margin` explicitly.
63
- 2. **`ChartFrame`'s title box `mb: 2` → `mb: 1`** (16 → 8 px).
64
- 3. Apply consistently across the presets that share the path (`BarChart`, `LineChart`,
65
- `TimeSeriesChart`), so consumers do not have to know which chart type trims what.
66
-
67
- **Non-goals / do-not-touch.**
68
- - **`CHART_FRAME_ROOT_SX = { p: 2 }` stays.** Trimming it would help the left and right edges
69
- too, but `ChartFrame` wraps non-chart content as well, and its padding is what makes a panel
70
- read as a card. That is a separate proposal with a different blast radius, not a rider here.
71
- - No change to axis sizing, tick formatting or label placement — THEME-9 owns those and they
72
- are working.
73
- - No new props. This is a default, not a feature.
74
-
75
- **Tier 3** — shared-core. `reviewer` mandatory; `ui_reviewer` too, the deliverable being visual.
76
-
77
- **Two traps this WO must not walk into.**
78
-
79
- 1. **`spaceForRotatedTicks` decides "did the caller set bottom?" by `margin?.bottom != null`.**
80
- If a package-level default margin object is injected *before* that check, every rotated-tick
81
- chart sees it as caller-set and loses its extra bottom space — hram's `RatioMetricsPanel`
82
- runs x-labels at −45° and −90° and would clip them. The default must be applied so that
83
- `spaceForRotatedTicks` still distinguishes a genuine caller margin from the package default,
84
- or the rotated path must be computed first and the default merged under it.
85
- 2. **`MUI_CHART_MARGIN_BOTTOM = 20` in `chartDefaults.js` is a hard-coded mirror of MUI's
86
- default**, used as the base when the rotated path computes extra bottom space. It must move
87
- with the new default, or rotated charts get their extra height added to the wrong base.
88
-
89
- **Tests to write.** Narrow, but these three are the ones that catch a real break:
90
- - A rotated-tick chart still receives its enlarged bottom margin (trap 1 — the regression this
91
- change is most likely to cause).
92
- - A caller-supplied partial margin keeps its own value on the side it set and gets the new
93
- default on the others (the merge semantics in scope 1).
94
- - A chart with a linear x-axis does not clip its last tick label at the new right margin.
95
-
96
- **Risks.**
97
- - **The blast radius is every chart in every consuming app**, and it lands the moment each app
98
- bumps its pin — not at publish. A single wrong side value is therefore an estate-wide visual
99
- regression rather than a local one. That is also the point of doing it here rather than in
100
- each panel, but it raises the bar on the rendered check.
101
- - **Panels that already hand-tune `margin`** (hram's `RatioMetricsPanel` passes
102
- `{ left: 60, right: 8, top: 20, bottom: … }`) must be unaffected on the sides they set. This
103
- is what scope 1's per-side merge is for; whole-object replacement would break them.
104
- - The numbers above are derived from a caption-sized tick label. A consuming app with a
105
- markedly larger chart font would need more top margin. Do not build for that — no consumer
106
- has it — but state the assumption in the docs.
107
-
108
- **Delivery is not done at publish.** hram is the consumer that reported this, and its pins for
109
- THEME-8, THEME-9 and THEME-10 are all already open. Take this one in the same bump rather than
110
- adding a fourth pending hop.
111
-
112
- ## Part B — Implementation map — ADDRESSED TO THE IMPLEMENTER
113
-
114
- Codex not dispatched: `.claude/codex-status.md` already carried a same-day `unavailable` line —
115
- implemented directly in Claude, authorship flip, `reviewer` + `ui_reviewer` mandatory (already
116
- required at Tier 3).
117
-
118
- ### What landed
119
-
120
- - `chartDefaults.js`: `PACKAGE_DEFAULT_MARGIN = { top: 10, bottom: 8, left: 8, right: 16 }`
121
- (exported) and `withMarginDefaults(margin)` (per-side merge, number-shorthand expansion).
122
- `MUI_CHART_MARGIN_BOTTOM` moved `20 → 8` to match the new baseline (trap 2).
123
- - `BarChart.jsx`/`LineChart.jsx`: `spaceForRotatedTicks` still runs FIRST, on the caller's
124
- UNMODIFIED `margin` — its own `callerSetBottom` check (`margin?.bottom != null`) therefore
125
- still tells a genuine caller margin from "nothing set" correctly (trap 1 avoided by ordering,
126
- not by threading a flag through the shared helper). `withMarginDefaults` is applied AFTER,
127
- wrapping `rotatedTickSpace.margin` on the way into `MuiBarChart`/`MuiLineChart` — so a
128
- rotation-computed `bottom` (already present on that object) is preserved by the merge, and
129
- only genuinely-unset sides get the package default.
130
- - `TimeSeriesChart.jsx`: no change needed — it never sets its own `margin`, so it inherits
131
- `BarChart`'s new default automatically through composition.
132
- - `ChartFrame.jsx`: title row `mb: 2 → mb: 1`. Controls row `mb: 2` left untouched (not named).
133
- **Deliberately unconditional** (`ui_reviewer` U3 asked whether this should be scoped to only
134
- chart-preset consumers, matching the margin trim's scope): `ChartFrame` wraps non-chart
135
- content too, and scope item 2 names the title box specifically, with no "only when a chart
136
- preset is the child" qualifier anywhere in Part A — a scatter-backed or bespoke-SVG panel
137
- gets the tighter title spacing but keeps whatever margin its own content uses, which is
138
- correct: the title row and the plot margin are two independent pieces of furniture, not one
139
- setting that should move together.
140
-
141
- ### Scope gap surfaced, not silently folded in
142
-
143
- `ScatterChart` (THEME-10, same day, not yet published) passes its `margin` prop straight
144
- through with no default handling at all — it would keep MUI's flat 20px default while
145
- `BarChart`/`LineChart`/`TimeSeriesChart` get 8/10/16. THEME-11's own scope item 3 names exactly
146
- three presets, not four, and `ScatterChart` didn't exist when this WO's Part A was authored.
147
- Flagging this explicitly rather than extending scope silently: `ScatterChart` should likely get
148
- `withMarginDefaults` too, in a follow-up (or when `ScatterChart`'s own version bumps). **This is
149
- NOT the same seam as the right-margin note below** — `ScatterChart`'s X axis is always linear by
150
- construction (THEME-10 scope), so if/when it does adopt `withMarginDefaults`, its own rendered
151
- check needs to re-examine the right-margin question specifically, not inherit this WO's
152
- "no consumer has it" conclusion by assumption.
153
-
154
- ### Verification detail beyond the WO's own risk note — corrected after review
155
-
156
- The rendered check found that a wide, locale-grouped tick value (`"1,000"`, 6 characters)
157
- leaves only ~1px of clearance at the new `right: 16` before MUI's own internal `shortenLabels`
158
- truncates it to `"1,0…"` — never actual overflow-clipping past the SVG boundary in either the
159
- narrow or wide case. Two corrections to the original note, from `reviewer`/`ui_reviewer`:
160
-
161
- - **The grouped formatting is MUI's OWN default tick rendering, not THEME-9's
162
- `defaultNumericTickFormatter`** (`reviewer`/`ui_reviewer` finding, independently confirmed:
163
- neither `BarChart.jsx` nor `LineChart.jsx` wires a `valueFormatter` onto the x-axis — only the
164
- y-axis gets THEME-9's default — and the dev fixture that produced `"1,000"` set none either).
165
- This means the tight case is not a THEME-9-introduced interaction; it is how MUI itself has
166
- always formatted a linear axis's ticks, now simply more VISIBLE because `right` shrank from
167
- 20 to 16.
168
- - **No real consumer hits this today** (`reviewer`, verified by grepping every hram panel that
169
- actually consumes `BarChart`/`LineChart`/`TimeSeriesChart`: `CostMetricsPanel`,
170
- `RatioMetricsPanel`, `AccessibilityPanel`, `TotalMetricsPanel`, `ReferralMetricsPanel`,
171
- `GroupMetricsPanel`, `GenericAnalysisPlot` — every one uses `scaleType: 'band'`, none
172
- `'linear'`. `AllocationPerformancePanel`, the panel this note originally worried about,
173
- imports only `ChartFrame` from this package and draws its own bespoke SVG axes — it does not
174
- go through `BarChart`/`LineChart` at all, so it is unaffected by this WO regardless.) The WO's
175
- own explicit "do not build for a case no consumer has; state the assumption" therefore applies
176
- cleanly, not just plausibly. The residual risk (`ui_reviewer`) is real but one hop removed: the
177
- first FUTURE consumer that renders a linear x-axis through `BarChart`/`LineChart` with wide
178
- values should re-check this margin before shipping, and there is no code-level guard that would
179
- catch it automatically — recorded here so that check doesn't get skipped.
180
-
181
- ### Target repo
182
-
183
- `C:\Users\biglmi\Documents\webapps\ui-core-micha`
184
-
185
- ## Part C — Orchestrator only — NOT ADDRESSED TO THE IMPLEMENTER
186
-
187
- > **If you are the implementer reading this work order as your own specification: STOP at this
188
- > line.**
189
-
190
- - **Execution.** Codex known-unavailable today — implemented directly in Claude.
191
- - **Review routing.** `reviewer` + `ui_reviewer`, concurrent, one background batch.
192
- - **Verification.** Package suite: `chartDefaults.test.js` gained a dedicated
193
- `withMarginDefaults` describe block (pure-function contract) plus integration tests against
194
- `BarChart`/`LineChart` (package default applied, rotated-tick chart still gets its enlarged
195
- bottom margin — not the flat package default, partial-margin per-side merge, full-margin
196
- passthrough, right > left/bottom by construction); full affected set (`BarChart.test.jsx`,
197
- `LineChart.test.jsx`, `TimeSeriesChart.test.jsx`, `timeSeriesLegend.test.js`,
198
- `ScatterChart.test.jsx`, `chartsPalette.test.js`) — 91 tests, green.
199
- **Rendered check, done live** in the package's own dev harness: (1) the existing "Caller
200
- overrides" `LineChart` fixture (−25° rotated ticks, no explicit margin — exactly trap 1's
201
- shape) measured with NO overlap between the rotated tick labels and the axis title (15px
202
- clear gap) — trap 1 confirmed avoided; (2) a new `LineChart (linear x-axis)` fixture added
203
- and measured at two magnitudes — narrow (`"100"`) with 5px clearance from the SVG edge, wide
204
- locale-grouped (`"1,000"`) with MUI's own truncation kicking in at ~1px clearance but never
205
- actual clipping past the boundary (see the "beyond the WO's risk note" section above).
206
- - **Register & commit.** Advance the THEME-11 row with the reviewer verdicts. Coordinate the
207
- consumer pin bump with THEME-8, THEME-9 and THEME-10 — all still open at publish time,
208
- pending this WO's own reviews and a bundled version bump.