@micha.bigler/ui-core-micha 2.33.2 → 2.34.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 (78) hide show
  1. package/WORK_ORDERS.md +2 -2
  2. package/dist/auth/ReauthModal.js +3 -1
  3. package/dist/components/AccessCodeManager.js +16 -14
  4. package/dist/components/AccessCodeSingleUseToggle.js +2 -1
  5. package/dist/components/AllowedEmailDomainsManager.js +3 -1
  6. package/dist/components/AuthFactorRequirementCard.js +2 -1
  7. package/dist/components/BulkInviteCsvTab.js +7 -7
  8. package/dist/components/LoginForm.js +2 -1
  9. package/dist/components/MFAComponent.js +11 -5
  10. package/dist/components/MfaLoginComponent.js +4 -1
  11. package/dist/components/PasskeysComponent.js +4 -2
  12. package/dist/components/QrSignupManager.js +2 -1
  13. package/dist/components/QrSignupValidityManager.js +4 -1
  14. package/dist/components/RegistrationMethodsManager.js +3 -1
  15. package/dist/components/SecurityComponent.js +3 -1
  16. package/dist/components/SupportRecoveryRequestsTab.js +4 -1
  17. package/dist/components/UserInviteComponent.js +8 -8
  18. package/dist/components/UserListComponent.js +8 -4
  19. package/dist/components/charts/ChartFrame.js +5 -2
  20. package/dist/components/charts/TimeSeriesChart.js +7 -4
  21. package/dist/index.js +1 -1
  22. package/dist/layout/PageLayout.js +3 -2
  23. package/dist/messaging/AttachmentList.js +6 -1
  24. package/dist/messaging/Composer.js +2 -1
  25. package/dist/messaging/DirectMessageLauncher.js +2 -1
  26. package/dist/messaging/MessageBubble.js +29 -8
  27. package/dist/messaging/PollCard.js +6 -4
  28. package/dist/messaging/Thread.js +2 -1
  29. package/dist/notifications/NotificationSettings.js +17 -3
  30. package/dist/notifications/PopupSurface.js +2 -1
  31. package/dist/onboarding/steps/BrowserPushStep.js +2 -1
  32. package/dist/pages/AccountPage.js +2 -1
  33. package/dist/pages/LoginPage.js +2 -1
  34. package/dist/pages/SignUpPage.js +2 -1
  35. package/dist/pages/SignupConfirmPage.js +2 -1
  36. package/dist/theme/index.js +2 -1
  37. package/dist/theme/kitSxRegistry.js +187 -0
  38. package/dist/theme/themeCompleteness.js +217 -0
  39. package/package.json +1 -1
  40. package/src/auth/ReauthModal.jsx +5 -2
  41. package/src/components/AccessCodeManager.jsx +22 -20
  42. package/src/components/AccessCodeSingleUseToggle.jsx +4 -2
  43. package/src/components/AllowedEmailDomainsManager.jsx +6 -3
  44. package/src/components/AuthFactorRequirementCard.jsx +4 -2
  45. package/src/components/BulkInviteCsvTab.jsx +11 -10
  46. package/src/components/LoginForm.jsx +5 -3
  47. package/src/components/MFAComponent.jsx +14 -7
  48. package/src/components/MfaLoginComponent.jsx +9 -5
  49. package/src/components/PasskeysComponent.jsx +7 -4
  50. package/src/components/QrSignupManager.jsx +6 -4
  51. package/src/components/QrSignupValidityManager.jsx +8 -4
  52. package/src/components/RegistrationMethodsManager.jsx +7 -4
  53. package/src/components/SecurityComponent.jsx +10 -7
  54. package/src/components/SupportRecoveryRequestsTab.jsx +8 -4
  55. package/src/components/UserInviteComponent.jsx +10 -10
  56. package/src/components/UserListComponent.jsx +12 -7
  57. package/src/components/charts/ChartFrame.jsx +6 -2
  58. package/src/components/charts/TimeSeriesChart.jsx +8 -4
  59. package/src/index.js +3 -0
  60. package/src/layout/PageLayout.jsx +4 -2
  61. package/src/messaging/AttachmentList.jsx +9 -3
  62. package/src/messaging/Composer.jsx +3 -1
  63. package/src/messaging/DirectMessageLauncher.jsx +3 -1
  64. package/src/messaging/MessageBubble.jsx +31 -9
  65. package/src/messaging/PollCard.jsx +8 -6
  66. package/src/messaging/Thread.jsx +3 -1
  67. package/src/notifications/NotificationSettings.jsx +24 -9
  68. package/src/notifications/PopupSurface.jsx +3 -1
  69. package/src/onboarding/steps/BrowserPushStep.jsx +3 -1
  70. package/src/pages/AccountPage.jsx +11 -9
  71. package/src/pages/LoginPage.jsx +6 -4
  72. package/src/pages/SignUpPage.jsx +4 -2
  73. package/src/pages/SignupConfirmPage.jsx +4 -2
  74. package/src/theme/index.js +3 -0
  75. package/src/theme/kitSxRegistry.js +246 -0
  76. package/src/theme/themeCompleteness.js +227 -0
  77. package/tests/themeCompleteness.test.js +199 -0
  78. package/work-orders/THEME-4.md +86 -0
@@ -0,0 +1,246 @@
1
+ import {
2
+ ACCESS_CODE_ACTION_SX,
3
+ ACCESS_CODE_ALERT_SX,
4
+ ACCESS_CODE_PRIMARY_ACTION_SX,
5
+ ACCESS_CODE_VALUE_FIELD_SX,
6
+ } from '../components/AccessCodeManager';
7
+ import { ACCESS_CODE_SINGLE_USE_ALERT_SX } from '../components/AccessCodeSingleUseToggle';
8
+ import {
9
+ ALLOWED_EMAIL_DOMAINS_ALERT_SX,
10
+ ALLOWED_EMAIL_DOMAINS_BUTTON_SX,
11
+ } from '../components/AllowedEmailDomainsManager';
12
+ import { AUTH_FACTOR_REQUIREMENT_ALERT_SX } from '../components/AuthFactorRequirementCard';
13
+ import { BULK_INVITE_ACTION_SX, BULK_INVITE_ALERT_SX } from '../components/BulkInviteCsvTab';
14
+ import { LOGIN_FORM_DIVIDER_SX } from '../components/LoginForm';
15
+ import {
16
+ MFA_ACTIVE_CARD_SX,
17
+ MFA_ALERT_SX,
18
+ MFA_CARD_SX,
19
+ MFA_DIVIDER_SX,
20
+ MFA_RECOVERY_BUTTON_SX,
21
+ MFA_TEXT_FIELD_SX,
22
+ } from '../components/MFAComponent';
23
+ import {
24
+ MFA_LOGIN_ALERT_SX,
25
+ MFA_LOGIN_DIVIDER_SX,
26
+ MFA_LOGIN_TEXT_FIELD_SX,
27
+ } from '../components/MfaLoginComponent';
28
+ import {
29
+ MOBILE_BOTTOM_NAV_ACTION_SX,
30
+ MOBILE_BOTTOM_NAV_ROOT_SX,
31
+ } from '../components/MobileBottomNav';
32
+ import { PASSKEYS_ALERT_SX, PASSKEYS_DIVIDER_SX } from '../components/PasskeysComponent';
33
+ import { QR_SIGNUP_MANAGER_ALERT_SX } from '../components/QrSignupManager';
34
+ import {
35
+ QR_SIGNUP_VALIDITY_ALERT_SX,
36
+ QR_SIGNUP_VALIDITY_BUTTON_SX,
37
+ QR_SIGNUP_VALIDITY_FIELD_SX,
38
+ } from '../components/QrSignupValidityManager';
39
+ import {
40
+ REGISTRATION_METHODS_ALERT_SX,
41
+ REGISTRATION_METHODS_INFO_SX,
42
+ } from '../components/RegistrationMethodsManager';
43
+ import { SECURITY_ALERT_SX, SECURITY_DIVIDER_SX } from '../components/SecurityComponent';
44
+ import {
45
+ SUPPORT_RECOVERY_AGENT_BUTTON_SX,
46
+ SUPPORT_RECOVERY_AGENT_PAPER_SX,
47
+ SUPPORT_RECOVERY_ALERT_SX,
48
+ } from '../components/SupportRecoveryRequestsTab';
49
+ import { USER_INVITE_ACTION_SX, USER_INVITE_ALERT_SX } from '../components/UserInviteComponent';
50
+ import {
51
+ USER_LIST_ACTION_SX,
52
+ USER_LIST_ALERT_SX,
53
+ USER_LIST_BODY_CELL_SX,
54
+ USER_LIST_EMPTY_CELL_SX,
55
+ USER_LIST_HEADER_CELL_SX,
56
+ } from '../components/UserListComponent';
57
+ import { CHART_FRAME_ALERT_SX, CHART_FRAME_ROOT_SX } from '../components/charts/ChartFrame';
58
+ import { PAGE_LAYOUT_CONTAINER_SX } from '../layout/PageLayout';
59
+ import {
60
+ ATTACHMENT_LIST_ALERT_SX,
61
+ ATTACHMENT_LIST_LIGHTBOX_ACTION_SX,
62
+ } from '../messaging/AttachmentList';
63
+ import { COMPOSER_EMOJI_BUTTON_SX } from '../messaging/Composer';
64
+ import { DIRECT_MESSAGE_LAUNCHER_ALERT_SX } from '../messaging/DirectMessageLauncher';
65
+ import { MESSAGE_BUBBLE_ACTION_SX, MESSAGE_BUBBLE_ROOT_SX } from '../messaging/MessageBubble';
66
+ import {
67
+ POLL_CARD_CHECKBOX_SX,
68
+ POLL_CARD_FORM_CONTROL_SX,
69
+ POLL_CARD_ROOT_SX,
70
+ } from '../messaging/PollCard';
71
+ import { THREAD_DAY_SEPARATOR_SX } from '../messaging/Thread';
72
+ import {
73
+ NOTIFICATION_SETTINGS_ALERT_SX,
74
+ NOTIFICATION_SETTINGS_CATEGORY_CONTROL_SX,
75
+ NOTIFICATION_SETTINGS_FORM_CONTROL_SX,
76
+ NOTIFICATION_SETTINGS_HINT_ALERT_SX,
77
+ NOTIFICATION_SETTINGS_PUSH_CONTROL_SX,
78
+ NOTIFICATION_SETTINGS_REACH_ALERT_SX,
79
+ } from '../notifications/NotificationSettings';
80
+ import { POPUP_SURFACE_DISMISS_BUTTON_SX } from '../notifications/PopupSurface';
81
+ import { BROWSER_PUSH_FORM_CONTROL_LABEL_SX } from '../onboarding/steps/BrowserPushStep';
82
+ import { ACCOUNT_PAGE_SECTION_PAPER_SX } from '../pages/AccountPage';
83
+ import { LOGIN_PAGE_ALERT_SX } from '../pages/LoginPage';
84
+ import { SIGN_UP_PAGE_ALERT_SX } from '../pages/SignUpPage';
85
+ import { SIGNUP_CONFIRM_PAGE_ALERT_SX } from '../pages/SignupConfirmPage';
86
+ import { REAUTH_MODAL_ALERT_SX, REAUTH_MODAL_TEXT_FIELD_SX } from '../auth/ReauthModal';
87
+
88
+ const SX_EXPORTS = {
89
+ ACCESS_CODE_ACTION_SX,
90
+ ACCESS_CODE_ALERT_SX,
91
+ ACCESS_CODE_PRIMARY_ACTION_SX,
92
+ ACCESS_CODE_SINGLE_USE_ALERT_SX,
93
+ ACCESS_CODE_VALUE_FIELD_SX,
94
+ ACCOUNT_PAGE_SECTION_PAPER_SX,
95
+ ALLOWED_EMAIL_DOMAINS_ALERT_SX,
96
+ ALLOWED_EMAIL_DOMAINS_BUTTON_SX,
97
+ ATTACHMENT_LIST_ALERT_SX,
98
+ ATTACHMENT_LIST_LIGHTBOX_ACTION_SX,
99
+ AUTH_FACTOR_REQUIREMENT_ALERT_SX,
100
+ BROWSER_PUSH_FORM_CONTROL_LABEL_SX,
101
+ BULK_INVITE_ACTION_SX,
102
+ BULK_INVITE_ALERT_SX,
103
+ CHART_FRAME_ALERT_SX,
104
+ CHART_FRAME_ROOT_SX,
105
+ COMPOSER_EMOJI_BUTTON_SX,
106
+ DIRECT_MESSAGE_LAUNCHER_ALERT_SX,
107
+ LOGIN_FORM_DIVIDER_SX,
108
+ LOGIN_PAGE_ALERT_SX,
109
+ MESSAGE_BUBBLE_ACTION_SX,
110
+ MESSAGE_BUBBLE_ROOT_SX,
111
+ MFA_ACTIVE_CARD_SX,
112
+ MFA_ALERT_SX,
113
+ MFA_CARD_SX,
114
+ MFA_DIVIDER_SX,
115
+ MFA_LOGIN_ALERT_SX,
116
+ MFA_LOGIN_DIVIDER_SX,
117
+ MFA_LOGIN_TEXT_FIELD_SX,
118
+ MFA_RECOVERY_BUTTON_SX,
119
+ MFA_TEXT_FIELD_SX,
120
+ MOBILE_BOTTOM_NAV_ACTION_SX,
121
+ MOBILE_BOTTOM_NAV_ROOT_SX,
122
+ NOTIFICATION_SETTINGS_ALERT_SX,
123
+ NOTIFICATION_SETTINGS_CATEGORY_CONTROL_SX,
124
+ NOTIFICATION_SETTINGS_FORM_CONTROL_SX,
125
+ NOTIFICATION_SETTINGS_HINT_ALERT_SX,
126
+ NOTIFICATION_SETTINGS_PUSH_CONTROL_SX,
127
+ NOTIFICATION_SETTINGS_REACH_ALERT_SX,
128
+ PAGE_LAYOUT_CONTAINER_SX,
129
+ PASSKEYS_ALERT_SX,
130
+ PASSKEYS_DIVIDER_SX,
131
+ POLL_CARD_CHECKBOX_SX,
132
+ POLL_CARD_FORM_CONTROL_SX,
133
+ POLL_CARD_ROOT_SX,
134
+ POPUP_SURFACE_DISMISS_BUTTON_SX,
135
+ QR_SIGNUP_MANAGER_ALERT_SX,
136
+ QR_SIGNUP_VALIDITY_ALERT_SX,
137
+ QR_SIGNUP_VALIDITY_BUTTON_SX,
138
+ QR_SIGNUP_VALIDITY_FIELD_SX,
139
+ REAUTH_MODAL_ALERT_SX,
140
+ REAUTH_MODAL_TEXT_FIELD_SX,
141
+ REGISTRATION_METHODS_ALERT_SX,
142
+ REGISTRATION_METHODS_INFO_SX,
143
+ SECURITY_ALERT_SX,
144
+ SECURITY_DIVIDER_SX,
145
+ SIGNUP_CONFIRM_PAGE_ALERT_SX,
146
+ SIGN_UP_PAGE_ALERT_SX,
147
+ SUPPORT_RECOVERY_AGENT_BUTTON_SX,
148
+ SUPPORT_RECOVERY_AGENT_PAPER_SX,
149
+ SUPPORT_RECOVERY_ALERT_SX,
150
+ THREAD_DAY_SEPARATOR_SX,
151
+ USER_INVITE_ACTION_SX,
152
+ USER_INVITE_ALERT_SX,
153
+ USER_LIST_ACTION_SX,
154
+ USER_LIST_ALERT_SX,
155
+ USER_LIST_BODY_CELL_SX,
156
+ USER_LIST_EMPTY_CELL_SX,
157
+ USER_LIST_HEADER_CELL_SX,
158
+ };
159
+
160
+ const SX_EXPORT_NAMES = new Map(
161
+ Object.entries(SX_EXPORTS).map(([name, sx]) => [sx, name]),
162
+ );
163
+
164
+ const entry = (component, muiComponent, sx) => ({
165
+ component,
166
+ muiComponent,
167
+ sx,
168
+ exportName: SX_EXPORT_NAMES.get(sx),
169
+ });
170
+
171
+ /**
172
+ * Kit-owned, top-level sx objects paired with the baseline-styled MUI key they
173
+ * target. Conditional and nested sx remain a documented lower bound in the
174
+ * checks rather than being represented here as if they were fully inspected.
175
+ */
176
+ export const KIT_COMPONENT_SX_REGISTRY = [
177
+ entry('AccessCodeManager.action', 'MuiButton', ACCESS_CODE_ACTION_SX),
178
+ entry('AccessCodeManager.primaryAction', 'MuiButton', ACCESS_CODE_PRIMARY_ACTION_SX),
179
+ entry('AccessCodeManager.alert', 'MuiAlert', ACCESS_CODE_ALERT_SX),
180
+ entry('AccessCodeManager.valueField', 'MuiTextField', ACCESS_CODE_VALUE_FIELD_SX),
181
+ entry('AccessCodeSingleUseToggle.alert', 'MuiAlert', ACCESS_CODE_SINGLE_USE_ALERT_SX),
182
+ entry('AllowedEmailDomainsManager.alert', 'MuiAlert', ALLOWED_EMAIL_DOMAINS_ALERT_SX),
183
+ entry('AllowedEmailDomainsManager.action', 'MuiButton', ALLOWED_EMAIL_DOMAINS_BUTTON_SX),
184
+ entry('AuthFactorRequirementCard.alert', 'MuiAlert', AUTH_FACTOR_REQUIREMENT_ALERT_SX),
185
+ entry('BulkInviteCsvTab.action', 'MuiButton', BULK_INVITE_ACTION_SX),
186
+ entry('BulkInviteCsvTab.alert', 'MuiAlert', BULK_INVITE_ALERT_SX),
187
+ entry('LoginForm.divider', 'MuiDivider', LOGIN_FORM_DIVIDER_SX),
188
+ entry('MFAComponent.alert', 'MuiAlert', MFA_ALERT_SX),
189
+ entry('MFAComponent.card', 'MuiCard', MFA_CARD_SX),
190
+ entry('MFAComponent.activeCard', 'MuiCard', MFA_ACTIVE_CARD_SX),
191
+ entry('MFAComponent.field', 'MuiTextField', MFA_TEXT_FIELD_SX),
192
+ entry('MFAComponent.divider', 'MuiDivider', MFA_DIVIDER_SX),
193
+ entry('MFAComponent.recoveryAction', 'MuiButton', MFA_RECOVERY_BUTTON_SX),
194
+ entry('MfaLoginComponent.alert', 'MuiAlert', MFA_LOGIN_ALERT_SX),
195
+ entry('MfaLoginComponent.divider', 'MuiDivider', MFA_LOGIN_DIVIDER_SX),
196
+ entry('MfaLoginComponent.field', 'MuiTextField', MFA_LOGIN_TEXT_FIELD_SX),
197
+ entry('MobileBottomNav.root', 'MuiBottomNavigation', MOBILE_BOTTOM_NAV_ROOT_SX),
198
+ entry('MobileBottomNav.action', 'MuiBottomNavigationAction', MOBILE_BOTTOM_NAV_ACTION_SX),
199
+ entry('PasskeysComponent.alert', 'MuiAlert', PASSKEYS_ALERT_SX),
200
+ entry('PasskeysComponent.divider', 'MuiDivider', PASSKEYS_DIVIDER_SX),
201
+ entry('QrSignupManager.alert', 'MuiAlert', QR_SIGNUP_MANAGER_ALERT_SX),
202
+ entry('QrSignupValidityManager.alert', 'MuiAlert', QR_SIGNUP_VALIDITY_ALERT_SX),
203
+ entry('QrSignupValidityManager.field', 'MuiTextField', QR_SIGNUP_VALIDITY_FIELD_SX),
204
+ entry('QrSignupValidityManager.action', 'MuiButton', QR_SIGNUP_VALIDITY_BUTTON_SX),
205
+ entry('RegistrationMethodsManager.alert', 'MuiAlert', REGISTRATION_METHODS_ALERT_SX),
206
+ entry('RegistrationMethodsManager.info', 'MuiAlert', REGISTRATION_METHODS_INFO_SX),
207
+ entry('SecurityComponent.alert', 'MuiAlert', SECURITY_ALERT_SX),
208
+ entry('SecurityComponent.divider', 'MuiDivider', SECURITY_DIVIDER_SX),
209
+ entry('SupportRecoveryRequestsTab.agentPaper', 'MuiPaper', SUPPORT_RECOVERY_AGENT_PAPER_SX),
210
+ entry('SupportRecoveryRequestsTab.alert', 'MuiAlert', SUPPORT_RECOVERY_ALERT_SX),
211
+ entry('SupportRecoveryRequestsTab.agentAction', 'MuiButton', SUPPORT_RECOVERY_AGENT_BUTTON_SX),
212
+ entry('UserInviteComponent.action', 'MuiButton', USER_INVITE_ACTION_SX),
213
+ entry('UserInviteComponent.alert', 'MuiAlert', USER_INVITE_ALERT_SX),
214
+ entry('UserListComponent.action', 'MuiButton', USER_LIST_ACTION_SX),
215
+ entry('UserListComponent.alert', 'MuiAlert', USER_LIST_ALERT_SX),
216
+ entry('UserListComponent.headerCell', 'MuiTableCell', USER_LIST_HEADER_CELL_SX),
217
+ entry('UserListComponent.emptyCell', 'MuiTableCell', USER_LIST_EMPTY_CELL_SX),
218
+ entry('UserListComponent.bodyCell', 'MuiTableCell', USER_LIST_BODY_CELL_SX),
219
+ entry('ChartFrame.root', 'MuiPaper', CHART_FRAME_ROOT_SX),
220
+ entry('ChartFrame.alert', 'MuiAlert', CHART_FRAME_ALERT_SX),
221
+ entry('PageLayout.container', 'MuiContainer', PAGE_LAYOUT_CONTAINER_SX),
222
+ entry('AttachmentList.lightboxAction', 'MuiIconButton', ATTACHMENT_LIST_LIGHTBOX_ACTION_SX),
223
+ entry('AttachmentList.alert', 'MuiAlert', ATTACHMENT_LIST_ALERT_SX),
224
+ entry('Composer.emojiAction', 'MuiIconButton', COMPOSER_EMOJI_BUTTON_SX),
225
+ entry('DirectMessageLauncher.alert', 'MuiAlert', DIRECT_MESSAGE_LAUNCHER_ALERT_SX),
226
+ entry('MessageBubble.root', 'MuiPaper', MESSAGE_BUBBLE_ROOT_SX),
227
+ entry('MessageBubble.action', 'MuiIconButton', MESSAGE_BUBBLE_ACTION_SX),
228
+ entry('PollCard.root', 'MuiPaper', POLL_CARD_ROOT_SX),
229
+ entry('PollCard.formControl', 'MuiFormControlLabel', POLL_CARD_FORM_CONTROL_SX),
230
+ entry('PollCard.checkbox', 'MuiCheckbox', POLL_CARD_CHECKBOX_SX),
231
+ entry('Thread.daySeparator', 'MuiDivider', THREAD_DAY_SEPARATOR_SX),
232
+ entry('NotificationSettings.alert', 'MuiAlert', NOTIFICATION_SETTINGS_ALERT_SX),
233
+ entry('NotificationSettings.hintAlert', 'MuiAlert', NOTIFICATION_SETTINGS_HINT_ALERT_SX),
234
+ entry('NotificationSettings.reachAlert', 'MuiAlert', NOTIFICATION_SETTINGS_REACH_ALERT_SX),
235
+ entry('NotificationSettings.formControl', 'MuiFormControlLabel', NOTIFICATION_SETTINGS_FORM_CONTROL_SX),
236
+ entry('NotificationSettings.pushControl', 'MuiFormControlLabel', NOTIFICATION_SETTINGS_PUSH_CONTROL_SX),
237
+ entry('NotificationSettings.categoryControl', 'MuiFormControlLabel', NOTIFICATION_SETTINGS_CATEGORY_CONTROL_SX),
238
+ entry('PopupSurface.dismissAction', 'MuiButton', POPUP_SURFACE_DISMISS_BUTTON_SX),
239
+ entry('BrowserPushStep.formControl', 'MuiFormControlLabel', BROWSER_PUSH_FORM_CONTROL_LABEL_SX),
240
+ entry('AccountPage.sectionPaper', 'MuiPaper', ACCOUNT_PAGE_SECTION_PAPER_SX),
241
+ entry('LoginPage.alert', 'MuiAlert', LOGIN_PAGE_ALERT_SX),
242
+ entry('SignUpPage.alert', 'MuiAlert', SIGN_UP_PAGE_ALERT_SX),
243
+ entry('SignupConfirmPage.alert', 'MuiAlert', SIGNUP_CONFIRM_PAGE_ALERT_SX),
244
+ entry('ReauthModal.alert', 'MuiAlert', REAUTH_MODAL_ALERT_SX),
245
+ entry('ReauthModal.field', 'MuiTextField', REAUTH_MODAL_TEXT_FIELD_SX),
246
+ ];
@@ -1,6 +1,7 @@
1
1
  import { createTheme, getContrastRatio as getMuiContrastRatio } from '@mui/material/styles';
2
2
 
3
3
  import { STATUS_KEYS, TYPOGRAPHY_VARIANTS } from './tokens';
4
+ import { KIT_COMPONENT_SX_REGISTRY } from './kitSxRegistry';
4
5
 
5
6
  const MUI_DEFAULT_THEME = createTheme();
6
7
 
@@ -366,6 +367,232 @@ export function assertThemeComplete(theme, { exemptions = [] } = {}) {
366
367
  return { findings };
367
368
  }
368
369
 
370
+ const SAME_ROOT_STYLE_OVERRIDE_SLOTS = {
371
+ MuiAlert: ['standardSuccess', 'standardWarning', 'standardError', 'standardInfo'],
372
+ MuiButton: ['root', 'contained', 'outlined'],
373
+ MuiChip: ['root', 'outlined'],
374
+ };
375
+
376
+ // MUI's sx prop accepts CSS spacing shorthands (`py`, `px`, `pt`, ...) that
377
+ // styleOverrides' plain CSS-in-JS objects do not -- a baseline `padding`
378
+ // default and a component's own `sx={{ py: 1 }}` collide on the vertical
379
+ // axis even though the two key strings never match literally. Each key is
380
+ // expanded to the canonical longhand properties it can affect so the
381
+ // disjointness check compares axes, not raw key spellings; a property
382
+ // outside this table expands to itself.
383
+ const SPACING_SHORTHAND_LONGHANDS = {
384
+ p: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
385
+ padding: ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'],
386
+ px: ['paddingLeft', 'paddingRight'],
387
+ py: ['paddingTop', 'paddingBottom'],
388
+ pt: ['paddingTop'],
389
+ pr: ['paddingRight'],
390
+ pb: ['paddingBottom'],
391
+ pl: ['paddingLeft'],
392
+ m: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
393
+ margin: ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'],
394
+ mx: ['marginLeft', 'marginRight'],
395
+ my: ['marginTop', 'marginBottom'],
396
+ mt: ['marginTop'],
397
+ mr: ['marginRight'],
398
+ mb: ['marginBottom'],
399
+ ml: ['marginLeft'],
400
+ };
401
+
402
+ function expandShorthandProperty(property) {
403
+ return SPACING_SHORTHAND_LONGHANDS[property] || [property];
404
+ }
405
+
406
+ function styleOverrideKeys(componentConfig, slots = ['root']) {
407
+ const keys = new Set();
408
+ for (const slot of slots) {
409
+ const slotStyles = componentConfig?.styleOverrides?.[slot];
410
+ if (!slotStyles || typeof slotStyles !== 'object' || Array.isArray(slotStyles)) continue;
411
+ Object.keys(slotStyles).forEach((key) => expandShorthandProperty(key).forEach((longhand) => keys.add(longhand)));
412
+ }
413
+ return keys;
414
+ }
415
+
416
+ /**
417
+ * Reports top-level property collisions between registered kit sx objects and
418
+ * the baseline MUI component they target. Spacing shorthands (`py`/`px`/...)
419
+ * are compared by the longhand axis they affect, not by literal key name --
420
+ * see `SPACING_SHORTHAND_LONGHANDS`.
421
+ *
422
+ * This is deliberately a lower bound on shadowing: conditional sx, nested sx,
423
+ * callback results, and nested selector properties are not inspected. A clean
424
+ * result therefore proves only that the registered top-level key sets are
425
+ * disjoint; it is not proof that no component can shadow the baseline.
426
+ */
427
+ export function assertKitSxDisjoint(
428
+ theme,
429
+ { registry = KIT_COMPONENT_SX_REGISTRY, exemptions = [] } = {},
430
+ ) {
431
+ const declaredExemptions = [
432
+ ...(theme?.themeCompleteness?.exemptions || []),
433
+ ...exemptions,
434
+ ];
435
+ const validExemptions = new Set();
436
+ const findings = [];
437
+
438
+ for (const exemption of declaredExemptions) {
439
+ if (!exemption?.surface || !exemption?.reason?.trim()) {
440
+ findings.push({
441
+ surface: `exemption.${exemption?.surface || 'unknown'}`,
442
+ reason: 'A completeness exemption must name a surface and include a reason.',
443
+ });
444
+ continue;
445
+ }
446
+ validExemptions.add(exemption.surface);
447
+ }
448
+
449
+ for (const entry of registry) {
450
+ const baselineKeys = styleOverrideKeys(
451
+ theme?.components?.[entry.muiComponent],
452
+ entry.slots || SAME_ROOT_STYLE_OVERRIDE_SLOTS[entry.muiComponent],
453
+ );
454
+ for (const property of Object.keys(entry.sx || {})) {
455
+ const surface = `${entry.component}.${entry.muiComponent}.${property}`;
456
+ const collides = expandShorthandProperty(property).some((longhand) => baselineKeys.has(longhand));
457
+ if (collides && !validExemptions.has(surface)) {
458
+ findings.push({
459
+ surface,
460
+ reason: `${entry.component} sx shadows ${entry.muiComponent} styleOverrides property "${property}".`,
461
+ });
462
+ }
463
+ }
464
+ }
465
+
466
+ return { findings };
467
+ }
468
+
469
+ const BASELINE_STYLED_MUI_COMPONENTS = [
470
+ 'Alert',
471
+ 'BottomNavigation',
472
+ 'BottomNavigationAction',
473
+ 'Button',
474
+ 'Card',
475
+ 'Checkbox',
476
+ 'Chip',
477
+ 'Container',
478
+ 'CssBaseline',
479
+ 'Dialog',
480
+ 'Divider',
481
+ 'Drawer',
482
+ 'FilledInput',
483
+ 'FormControlLabel',
484
+ 'IconButton',
485
+ 'Input',
486
+ 'Menu',
487
+ 'OutlinedInput',
488
+ 'Paper',
489
+ 'Select',
490
+ 'TableCell',
491
+ 'TextField',
492
+ 'Tooltip',
493
+ ];
494
+
495
+ function jsxOpeningTag(source, start) {
496
+ let braces = 0;
497
+ let quote = null;
498
+ for (let index = start; index < source.length; index += 1) {
499
+ const character = source[index];
500
+ if (quote) {
501
+ if (character === quote && source[index - 1] !== '\\') quote = null;
502
+ continue;
503
+ }
504
+ if (character === '"' || character === "'" || character === '`') {
505
+ quote = character;
506
+ } else if (character === '{') {
507
+ braces += 1;
508
+ } else if (character === '}') {
509
+ braces = Math.max(0, braces - 1);
510
+ } else if (character === '>' && braces === 0) {
511
+ return source.slice(start, index + 1);
512
+ }
513
+ }
514
+ return source.slice(start);
515
+ }
516
+
517
+ function topLevelSxValue(tag) {
518
+ let braces = 0;
519
+ let quote = null;
520
+ for (let index = 0; index < tag.length; index += 1) {
521
+ const character = tag[index];
522
+ if (quote) {
523
+ if (character === quote && tag[index - 1] !== '\\') quote = null;
524
+ continue;
525
+ }
526
+ if (character === '"' || character === "'" || character === '`') {
527
+ quote = character;
528
+ continue;
529
+ }
530
+ if (braces === 0 && (index === 0 || /\s/.test(tag[index - 1]))) {
531
+ const attribute = tag.slice(index).match(/^sx\b\s*=\s*/)?.[0];
532
+ if (attribute) return tag.slice(index + attribute.length);
533
+ }
534
+ if (character === '{') braces += 1;
535
+ if (character === '}') braces = Math.max(0, braces - 1);
536
+ }
537
+ return '';
538
+ }
539
+
540
+ /**
541
+ * Reports baseline-styled MUI tags that bypass the exported-object convention.
542
+ * Like assertKitSxDisjoint, this is a documented lower bound: conditional,
543
+ * array, callback, and otherwise nested sx expressions are not inspected.
544
+ */
545
+ export function reportKitSxBypasses(sources = [], { registry } = {}) {
546
+ const normalized = sources.map((entry, index) =>
547
+ typeof entry === 'string'
548
+ ? { path: `source-${index + 1}`, source: entry }
549
+ : { path: entry.path || `source-${index + 1}`, source: entry.source || '' },
550
+ );
551
+ const findings = [];
552
+ const registeredTargets = registry
553
+ ? registry.reduce((targets, { exportName, muiComponent }) => {
554
+ if (!exportName || !muiComponent) return targets;
555
+ if (!targets.has(exportName)) targets.set(exportName, new Set());
556
+ targets.get(exportName).add(muiComponent);
557
+ return targets;
558
+ }, new Map())
559
+ : null;
560
+ const componentPattern = BASELINE_STYLED_MUI_COMPONENTS.join('|');
561
+ const tagPattern = new RegExp(`<(${componentPattern})\\b`, 'g');
562
+
563
+ for (const { path, source } of normalized) {
564
+ const exportedObjects = new Set(
565
+ [...source.matchAll(/\bexport\s+const\s+([A-Za-z_$][\w$]*)\s*=\s*\{/g)]
566
+ .map((match) => match[1]),
567
+ );
568
+
569
+ for (const match of source.matchAll(tagPattern)) {
570
+ const tag = jsxOpeningTag(source, match.index);
571
+ const sxValue = topLevelSxValue(tag);
572
+ const inline = /^\{\s*\{/.test(sxValue);
573
+ const identifier = sxValue.match(/^\{\s*([A-Za-z_$][\w$]*)\s*\}/)?.[1];
574
+ const exported = identifier && exportedObjects.has(identifier);
575
+ const muiComponent = `Mui${match[1]}`;
576
+ const unregistered = exported
577
+ && registeredTargets
578
+ && !registeredTargets.get(identifier)?.has(muiComponent);
579
+ if (!inline && (!identifier || (exported && !unregistered))) continue;
580
+
581
+ const line = source.slice(0, match.index).split(/\r?\n/).length;
582
+ findings.push({
583
+ surface: `${path}:${line}.Mui${match[1]}.sx`,
584
+ reason: inline
585
+ ? `${match[1]} uses an inline sx object instead of a top-level exported object.`
586
+ : unregistered
587
+ ? `${match[1]} uses exported sx object "${identifier}" without a ${muiComponent} registry entry.`
588
+ : `${match[1]} uses non-exported sx object "${identifier}".`,
589
+ });
590
+ }
591
+ }
592
+
593
+ return { findings };
594
+ }
595
+
369
596
  /** Reports adoption signals as numbers; it never changes completeness. */
370
597
  export function reportThemeAdoption(sources = []) {
371
598
  const normalized = sources.map((entry, index) =>