@schukai/monster 4.148.9 → 4.149.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 (205) hide show
  1. package/package.json +1 -1
  2. package/source/components/accessibility/stylesheet/locale-picker.mjs +61 -1
  3. package/source/components/content/image-editor.mjs +79 -11
  4. package/source/components/content/style/copy.pcss +1 -1
  5. package/source/components/content/stylesheet/camera-capture.mjs +1 -1
  6. package/source/components/content/stylesheet/copy.mjs +1 -1
  7. package/source/components/content/stylesheet/fetch-box.mjs +1 -1
  8. package/source/components/content/stylesheet/viewer.mjs +61 -1
  9. package/source/components/data/kpi-tile.mjs +5 -3
  10. package/source/components/data/metric-graph.mjs +22 -20
  11. package/source/components/data/metric.mjs +20 -18
  12. package/source/components/data/style/kpi-tile.pcss +65 -43
  13. package/source/components/data/style/metric-graph.pcss +35 -18
  14. package/source/components/data/style/metric.pcss +35 -28
  15. package/source/components/data/stylesheet/kpi-tile.mjs +61 -1
  16. package/source/components/data/stylesheet/metric-graph.mjs +61 -1
  17. package/source/components/data/stylesheet/metric.mjs +61 -1
  18. package/source/components/datatable/columnbar.mjs +4 -2
  19. package/source/components/datatable/datatable.mjs +73 -15
  20. package/source/components/datatable/filter/date-range.mjs +61 -61
  21. package/source/components/datatable/filter/date-time.mjs +5 -1
  22. package/source/components/datatable/filter/range.mjs +3 -3
  23. package/source/components/datatable/filter/select-operator.mjs +7 -1
  24. package/source/components/datatable/filter/text-operator.mjs +11 -3
  25. package/source/components/datatable/filter.mjs +16 -0
  26. package/source/components/datatable/pagination.mjs +2 -2
  27. package/source/components/datatable/style/column-bar.pcss +115 -23
  28. package/source/components/datatable/style/datatable.pcss +257 -29
  29. package/source/components/datatable/style/embedded-pagination.pcss +63 -34
  30. package/source/components/datatable/style/filter-button.pcss +1 -1
  31. package/source/components/datatable/style/filter-controls-defaults.pcss +49 -3
  32. package/source/components/datatable/style/filter-select.pcss +4 -1
  33. package/source/components/datatable/style/filter.pcss +18 -1
  34. package/source/components/datatable/stylesheet/change-button.mjs +61 -1
  35. package/source/components/datatable/stylesheet/column-bar.mjs +69 -1
  36. package/source/components/datatable/stylesheet/dataset.mjs +61 -1
  37. package/source/components/datatable/stylesheet/datatable.mjs +71 -1
  38. package/source/components/datatable/stylesheet/embedded-pagination.mjs +1 -1
  39. package/source/components/datatable/stylesheet/filter-button.mjs +1 -1
  40. package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +1 -1
  41. package/source/components/datatable/stylesheet/filter-select.mjs +61 -1
  42. package/source/components/datatable/stylesheet/filter.mjs +61 -1
  43. package/source/components/datatable/stylesheet/pagination.mjs +67 -1
  44. package/source/components/datatable/stylesheet/save-button.mjs +61 -1
  45. package/source/components/datatable/stylesheet/status.mjs +61 -1
  46. package/source/components/form/action-button.mjs +2 -1
  47. package/source/components/form/button-bar.mjs +7 -2
  48. package/source/components/form/context-error.mjs +4 -3
  49. package/source/components/form/context-help.mjs +10 -4
  50. package/source/components/form/context-hint.mjs +1 -1
  51. package/source/components/form/context-info.mjs +1 -1
  52. package/source/components/form/context-note.mjs +1 -1
  53. package/source/components/form/context-success.mjs +1 -1
  54. package/source/components/form/context-warning.mjs +1 -1
  55. package/source/components/form/control-bar.mjs +300 -14
  56. package/source/components/form/credential-button.mjs +4 -2
  57. package/source/components/form/digits.mjs +28 -1
  58. package/source/components/form/field-set.mjs +30 -0
  59. package/source/components/form/login.mjs +13 -10
  60. package/source/components/form/password.mjs +6 -0
  61. package/source/components/form/popper-button.mjs +1 -1
  62. package/source/components/form/quantity.mjs +14 -6
  63. package/source/components/form/register-wizard.mjs +46 -26
  64. package/source/components/form/select.mjs +17 -10
  65. package/source/components/form/sheet.mjs +13 -2
  66. package/source/components/form/style/action-button.pcss +131 -5
  67. package/source/components/form/style/button-bar.pcss +204 -3
  68. package/source/components/form/style/control-bar.pcss +254 -26
  69. package/source/components/form/style/digits.pcss +7 -6
  70. package/source/components/form/style/input-group.pcss +65 -1
  71. package/source/components/form/style/login.pcss +1 -1
  72. package/source/components/form/style/mixin/field-set-layout.pcss +8 -0
  73. package/source/components/form/style/password.pcss +25 -1
  74. package/source/components/form/style/popper-button.pcss +1 -0
  75. package/source/components/form/style/quantity.pcss +11 -20
  76. package/source/components/form/style/select.pcss +52 -7
  77. package/source/components/form/style/sheet.pcss +70 -1
  78. package/source/components/form/style/state-button.pcss +5 -2
  79. package/source/components/form/style/toggle-switch.pcss +7 -4
  80. package/source/components/form/stylesheet/action-button.mjs +63 -1
  81. package/source/components/form/stylesheet/button-bar.mjs +153 -1
  82. package/source/components/form/stylesheet/button.mjs +61 -1
  83. package/source/components/form/stylesheet/control-bar.mjs +163 -1
  84. package/source/components/form/stylesheet/digits.mjs +1 -1
  85. package/source/components/form/stylesheet/field-layout.mjs +1 -1
  86. package/source/components/form/stylesheet/field-set.mjs +1 -1
  87. package/source/components/form/stylesheet/form.mjs +1 -1
  88. package/source/components/form/stylesheet/input-group.mjs +15 -1
  89. package/source/components/form/stylesheet/login.mjs +1 -1
  90. package/source/components/form/stylesheet/password.mjs +7 -1
  91. package/source/components/form/stylesheet/popper-button.mjs +61 -1
  92. package/source/components/form/stylesheet/quantity.mjs +1 -1
  93. package/source/components/form/stylesheet/register-wizard.mjs +1 -1
  94. package/source/components/form/stylesheet/select.mjs +3 -1
  95. package/source/components/form/stylesheet/sheet.mjs +1 -1
  96. package/source/components/form/stylesheet/state-button.mjs +3 -1
  97. package/source/components/form/stylesheet/toggle-switch.mjs +1 -1
  98. package/source/components/form/stylesheet/wizard.mjs +1 -1
  99. package/source/components/form/util/floating-ui.mjs +35 -7
  100. package/source/components/host/call-button.mjs +3 -1
  101. package/source/components/host/stylesheet/call-button.mjs +61 -1
  102. package/source/components/host/stylesheet/host.mjs +1 -1
  103. package/source/components/host/stylesheet/toggle-button.mjs +61 -1
  104. package/source/components/layout/full-screen.mjs +31 -23
  105. package/source/components/layout/iframe.mjs +5 -1
  106. package/source/components/layout/popper.mjs +34 -4
  107. package/source/components/layout/style/full-screen.pcss +94 -23
  108. package/source/components/layout/style/split-panel.pcss +57 -5
  109. package/source/components/layout/style/tabs.pcss +277 -47
  110. package/source/components/layout/style/vertical-tabs.pcss +147 -0
  111. package/source/components/layout/stylesheet/collapse.mjs +61 -1
  112. package/source/components/layout/stylesheet/details.mjs +61 -1
  113. package/source/components/layout/stylesheet/full-screen.mjs +23 -1
  114. package/source/components/layout/stylesheet/overlay.mjs +1 -1
  115. package/source/components/layout/stylesheet/panel.mjs +1 -1
  116. package/source/components/layout/stylesheet/slider.mjs +1 -1
  117. package/source/components/layout/stylesheet/split-panel.mjs +17 -1
  118. package/source/components/layout/stylesheet/tabs.mjs +97 -1
  119. package/source/components/layout/stylesheet/vertical-tabs.mjs +33 -95
  120. package/source/components/layout/tabs.mjs +63 -0
  121. package/source/components/layout/vertical-tabs.mjs +63 -0
  122. package/source/components/navigation/stylesheet/site-navigation.mjs +1 -1
  123. package/source/components/navigation/stylesheet/table-of-content.mjs +1 -1
  124. package/source/components/navigation/stylesheet/wizard-navigation.mjs +1 -1
  125. package/source/components/navigation/table-of-content.mjs +13 -0
  126. package/source/components/notify/style/notify.pcss +1 -2
  127. package/source/components/notify/stylesheet/message.mjs +1 -1
  128. package/source/components/notify/stylesheet/notify.mjs +1 -1
  129. package/source/components/state/style/log.pcss +1 -0
  130. package/source/components/state/style/state.pcss +5 -1
  131. package/source/components/state/stylesheet/log.mjs +1 -1
  132. package/source/components/state/stylesheet/state.mjs +7 -1
  133. package/source/components/style/button.css +61 -205
  134. package/source/components/style/button.pcss +78 -34
  135. package/source/components/style/common.css +1 -170
  136. package/source/components/style/common.pcss +5 -9
  137. package/source/components/style/form.css +1 -49
  138. package/source/components/style/form.pcss +24 -6
  139. package/source/components/style/link.css +1 -39
  140. package/source/components/style/link.pcss +8 -16
  141. package/source/components/style/mixin/button.pcss +19 -10
  142. package/source/components/style/mixin/property.pcss +8 -0
  143. package/source/components/style/mixin/skeleton.pcss +11 -5
  144. package/source/components/style/normalize.css +1 -144
  145. package/source/components/style/normalize.pcss +5 -5
  146. package/source/components/style/property.css +1 -387
  147. package/source/components/style/skeleton.css +1 -198
  148. package/source/components/style/skeleton.pcss +8 -2
  149. package/source/components/style/theme-modern.css +74 -0
  150. package/source/components/style/theme-modern.pcss +498 -0
  151. package/source/components/stylesheet/button.mjs +61 -1
  152. package/source/components/stylesheet/common.mjs +1 -1
  153. package/source/components/stylesheet/form.mjs +1 -1
  154. package/source/components/stylesheet/link.mjs +1 -1
  155. package/source/components/stylesheet/normalize.mjs +1 -1
  156. package/source/components/stylesheet/property.mjs +1 -1
  157. package/source/components/stylesheet/skeleton.mjs +1 -1
  158. package/source/components/stylesheet/theme-modern.mjs +110 -0
  159. package/source/components/time/stylesheet/day.mjs +61 -1
  160. package/source/components/time/stylesheet/month-calendar.mjs +61 -1
  161. package/source/components/time/timeline/stylesheet/appointment.mjs +61 -1
  162. package/source/components/time/timeline/stylesheet/segment.mjs +61 -1
  163. package/source/components/tree-menu/style/tree-menu.pcss +26 -37
  164. package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
  165. package/test/cases/components/content/image-editor.mjs +19 -0
  166. package/test/cases/components/datatable/accessibility-defaults.mjs +112 -0
  167. package/test/cases/components/datatable/filter-availability.mjs +37 -0
  168. package/test/cases/components/datatable/filter-date-range-layout.mjs +40 -0
  169. package/test/cases/components/datatable/filter-select-operator.mjs +118 -0
  170. package/test/cases/components/datatable/mobile-layout.mjs +203 -0
  171. package/test/cases/components/datatable/responsive-breakpoint.mjs +41 -0
  172. package/test/cases/components/datatable/selection.mjs +149 -0
  173. package/test/cases/components/form/action-button-style.mjs +85 -0
  174. package/test/cases/components/form/button-bar.mjs +222 -3
  175. package/test/cases/components/form/context-theme-colors.mjs +59 -0
  176. package/test/cases/components/form/control-bar.mjs +228 -3
  177. package/test/cases/components/form/control-heights.mjs +103 -0
  178. package/test/cases/components/form/digits-accessibility.mjs +65 -0
  179. package/test/cases/components/form/field-set.mjs +54 -0
  180. package/test/cases/components/form/floating-ui.mjs +74 -0
  181. package/test/cases/components/form/input-group.mjs +61 -28
  182. package/test/cases/components/form/login.mjs +47 -0
  183. package/test/cases/components/form/password.mjs +63 -0
  184. package/test/cases/components/form/popper-button.mjs +30 -15
  185. package/test/cases/components/form/quantity-accessibility.mjs +46 -0
  186. package/test/cases/components/form/register-wizard.mjs +79 -0
  187. package/test/cases/components/form/select.mjs +94 -2
  188. package/test/cases/components/form/sheet.mjs +56 -2
  189. package/test/cases/components/host/toggle-button.mjs +50 -0
  190. package/test/cases/components/layout/full-screen.mjs +60 -0
  191. package/test/cases/components/layout/iframe-accessibility.mjs +44 -0
  192. package/test/cases/components/layout/popper.mjs +128 -0
  193. package/test/cases/components/layout/slit-panel.mjs +30 -1
  194. package/test/cases/components/layout/tabs.mjs +93 -0
  195. package/test/cases/components/navigation/table-of-content.mjs +18 -0
  196. package/test/cases/components/state/state-style.mjs +31 -0
  197. package/test/cases/components/style/action-menu.mjs +74 -0
  198. package/test/cases/components/style/form-controls.mjs +37 -0
  199. package/test/cases/components/style/kpi-tile.mjs +60 -0
  200. package/test/cases/components/style/legacy-theme-compatibility.mjs +137 -0
  201. package/test/cases/components/style/metric-layout.mjs +94 -0
  202. package/test/cases/components/style/skeleton.mjs +69 -0
  203. package/test/cases/components/style/theme-component-catalog.mjs +853 -0
  204. package/test/cases/components/style/theme-modern.mjs +439 -0
  205. package/test/cases/components/style/theme-review-regressions.mjs +41 -0
@@ -0,0 +1,439 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { expect } from "chai";
3
+
4
+ const themeSource = readFileSync(
5
+ new URL(
6
+ "../../../../source/components/style/theme-modern.pcss",
7
+ import.meta.url,
8
+ ),
9
+ "utf8",
10
+ );
11
+ const commonSource = readFileSync(
12
+ new URL("../../../../source/components/style/common.pcss", import.meta.url),
13
+ "utf8",
14
+ );
15
+ const normalizeSource = readFileSync(
16
+ new URL(
17
+ "../../../../source/components/style/normalize.pcss",
18
+ import.meta.url,
19
+ ),
20
+ "utf8",
21
+ );
22
+ const generatedCss = readFileSync(
23
+ new URL(
24
+ "../../../../source/components/style/theme-modern.css",
25
+ import.meta.url,
26
+ ),
27
+ "utf8",
28
+ );
29
+ const generatedStyleSheet = readFileSync(
30
+ new URL(
31
+ "../../../../source/components/stylesheet/theme-modern.mjs",
32
+ import.meta.url,
33
+ ),
34
+ "utf8",
35
+ );
36
+
37
+ const requiredCompatibilityTokens = [
38
+ "--monster-color-primary-1",
39
+ "--monster-bg-color-primary-1",
40
+ "--monster-color-secondary-1",
41
+ "--monster-bg-color-secondary-4",
42
+ "--monster-color-tertiary-1",
43
+ "--monster-bg-color-tertiary-4",
44
+ "--monster-color-success-1",
45
+ "--monster-bg-color-success-1",
46
+ "--monster-color-warning-1",
47
+ "--monster-bg-color-warning-1",
48
+ "--monster-color-error-1",
49
+ "--monster-bg-color-error-1",
50
+ "--monster-color-gradient-1",
51
+ "--monster-color-gradient-2",
52
+ "--monster-color-gradient-3",
53
+ "--monster-theme-control-bg-color",
54
+ "--monster-theme-control-color",
55
+ "--monster-theme-control-border-color",
56
+ "--monster-button-padding",
57
+ "--monster-tree-menu-font-size-level-3",
58
+ "--monster-tree-menu-font-size-deep",
59
+ "--monster-tree-menu-button-min-block-size",
60
+ "--monster-input-group-background-color",
61
+ "--monster-input-group-hover-background-color",
62
+ "--monster-input-group-field-hover-background-color",
63
+ "--monster-action-menu-padding",
64
+ "--monster-action-menu-border-color",
65
+ "--monster-action-menu-item-padding",
66
+ "--monster-action-menu-item-border-width",
67
+ "--monster-action-menu-item-hover-background-color",
68
+ "--monster-action-button-popper-border-color",
69
+ "--monster-action-button-item-background-color",
70
+ "--monster-action-button-item-hover-background-color",
71
+ "--monster-full-screen-control-size",
72
+ "--monster-full-screen-control-background-color",
73
+ "--monster-full-screen-control-active-background-color",
74
+ "--monster-split-panel-dragger-background-color",
75
+ "--monster-split-panel-dragger-hover-background-color",
76
+ "--monster-split-panel-handle-background-color",
77
+ "--monster-split-panel-handle-hover-background-color",
78
+ "--monster-split-panel-transition-duration",
79
+ "--monster-tabs-nav-background-color",
80
+ "--monster-tabs-button-active-border-color",
81
+ "--monster-tabs-indicator-background-color",
82
+ "--monster-vertical-tabs-control-min-block-size",
83
+ "--monster-vertical-tabs-nav-min-block-size",
84
+ "--monster-vertical-tabs-label-writing-mode",
85
+ ];
86
+
87
+ const contrastPairs = [
88
+ ["theme-text", "theme-surface-canvas", 4.5],
89
+ ["theme-text", "theme-surface", 4.5],
90
+ ["theme-text-secondary", "theme-surface", 4.5],
91
+ ["theme-text-muted", "theme-surface", 4.5],
92
+ ["theme-border", "theme-surface", 3],
93
+ ["theme-text", "theme-surface-subtle", 4.5],
94
+ ["theme-text-secondary", "theme-surface-subtle", 4.5],
95
+ ["theme-secondary", "theme-surface-subtle", 3],
96
+ ["theme-secondary", "theme-secondary-subtle", 4.5],
97
+ ["theme-tertiary", "theme-surface-subtle", 3],
98
+ ["theme-success", "theme-surface-subtle", 3],
99
+ ["theme-warning", "theme-surface-subtle", 3],
100
+ ["theme-error", "theme-surface-subtle", 3],
101
+ ["theme-on-accent", "theme-accent", 4.5],
102
+ ["theme-on-accent", "theme-accent-hover", 4.5],
103
+ ["theme-on-accent", "theme-secondary", 4.5],
104
+ ["theme-on-accent", "theme-secondary-strong", 4.5],
105
+ ["theme-on-accent", "theme-tertiary", 4.5],
106
+ ["theme-on-accent", "theme-success", 4.5],
107
+ ["theme-on-accent", "theme-warning", 4.5],
108
+ ["theme-on-accent", "theme-error", 4.5],
109
+ ["theme-on-warning", "theme-warning-emphasis", 4.5],
110
+ ["theme-text", "theme-success-subtle", 4.5],
111
+ ["theme-text", "theme-warning-subtle", 4.5],
112
+ ["theme-text", "theme-error-subtle", 4.5],
113
+ ["theme-text", "theme-info-subtle", 4.5],
114
+ ["theme-text", "theme-selection", 4.5],
115
+ ["theme-focus-ring", "theme-surface", 3],
116
+ ["theme-focus-ring", "theme-surface-canvas", 3],
117
+ ];
118
+
119
+ const darkSemanticTokens = [
120
+ "theme-surface-canvas",
121
+ "theme-surface",
122
+ "theme-surface-subtle",
123
+ "theme-surface-strong",
124
+ "theme-text",
125
+ "theme-text-secondary",
126
+ "theme-text-muted",
127
+ "theme-border",
128
+ "theme-border-strong",
129
+ "theme-accent",
130
+ "theme-accent-hover",
131
+ "theme-accent-subtle",
132
+ "theme-secondary",
133
+ "theme-secondary-subtle",
134
+ "theme-secondary-strong",
135
+ "theme-tertiary",
136
+ "theme-tertiary-subtle",
137
+ "theme-on-accent",
138
+ "theme-focus-ring",
139
+ "theme-selection",
140
+ "theme-success",
141
+ "theme-success-subtle",
142
+ "theme-warning",
143
+ "theme-warning-subtle",
144
+ "theme-warning-emphasis",
145
+ "theme-on-warning",
146
+ "theme-error",
147
+ "theme-error-subtle",
148
+ "theme-info",
149
+ "theme-info-subtle",
150
+ "theme-disabled",
151
+ "theme-disabled-surface",
152
+ ];
153
+
154
+ const darkContrastPairs = [
155
+ ...contrastPairs,
156
+ ["theme-disabled", "theme-disabled-surface", 4.5],
157
+ ];
158
+
159
+ describe("Modern theme stylesheet", () => {
160
+ it("scopes the opt-in theme to the existing theme-name contract", () => {
161
+ expect(themeSource).to.include(':root[data-monster-theme-name="modern"]');
162
+ expect(themeSource).to.include("color-scheme: light dark");
163
+ });
164
+
165
+ it("maps the established Monster theme tokens", () => {
166
+ for (const token of requiredCompatibilityTokens) {
167
+ expect(themeSource, token).to.include(token);
168
+ }
169
+ });
170
+
171
+ it("keeps the generated theme artifacts in sync", () => {
172
+ expect(generatedCss).to.include(":root[data-monster-theme-name=modern]");
173
+ expect(generatedCss).to.include("--monster-theme-surface-canvas:#efeee9");
174
+ expect(generatedCss).to.include("--monster-theme-focus-ring:#236b5b");
175
+ expect(generatedCss).to.include("--monster-theme-warning-emphasis:#f2c94c");
176
+ expect(generatedCss).to.include(
177
+ "--monster-color-warning-1:var(--monster-theme-on-warning)",
178
+ );
179
+ expect(generatedCss).to.include(
180
+ "--monster-bg-color-warning-4:var(--monster-theme-warning-emphasis)",
181
+ );
182
+ expect(generatedCss).to.include("--monster-border-radius:0.25rem");
183
+ expect(generatedCss).to.include(
184
+ "--monster-theme-control-border-radius:0.25rem",
185
+ );
186
+ expect(generatedCss).to.include(
187
+ "--monster-control-min-block-size:max(2.75rem,44px)",
188
+ );
189
+ expect(generatedCss).to.include(
190
+ "--monster-button-padding:0.5rem 0.875rem",
191
+ );
192
+ expect(generatedCss).to.include(
193
+ "--monster-tree-menu-font-size-level-2:0.9375rem",
194
+ );
195
+ expect(generatedCss).to.include(
196
+ "--monster-tree-menu-font-size-level-3:0.875rem",
197
+ );
198
+ expect(generatedCss).to.include(
199
+ "--monster-tree-menu-font-size-deep:0.875rem",
200
+ );
201
+ expect(generatedCss).to.include(
202
+ "--monster-tree-menu-button-min-block-size:2rem",
203
+ );
204
+ expect(generatedCss).to.include(
205
+ "--monster-input-group-background-color:var(--monster-theme-surface)",
206
+ );
207
+ expect(generatedCss).to.include(
208
+ "--monster-input-group-field-hover-background-color:transparent",
209
+ );
210
+ expect(generatedCss).to.include(
211
+ "--monster-action-menu-item-min-block-size:max(2.75rem,44px)",
212
+ );
213
+ expect(generatedCss).to.include(
214
+ "--monster-action-menu-item-border-width:0",
215
+ );
216
+ expect(generatedCss).to.include(
217
+ "--monster-action-menu-item-padding:0.625rem 0.75rem",
218
+ );
219
+ expect(generatedCss).to.include(
220
+ "--monster-box-shadow-2:0 2px 0 rgba(36,35,31,.18)",
221
+ );
222
+ expect(generatedCss).to.include(
223
+ "--monster-tabs-nav-border-width:0 0 1px",
224
+ );
225
+ expect(generatedCss).to.include("--monster-dragger-width:0.75rem");
226
+ expect(generatedCss).to.include(
227
+ "--monster-split-panel-handle-border-radius:999px",
228
+ );
229
+ expect(generatedCss).to.include(
230
+ "--monster-tabs-button-active-border-color:var(--monster-theme-tertiary)",
231
+ );
232
+ expect(generatedCss).to.include(
233
+ "--monster-tabs-indicator-active-opacity:0",
234
+ );
235
+ expect(generatedCss).to.include(
236
+ "--monster-vertical-tabs-control-min-block-size:9rem",
237
+ );
238
+ expect(generatedCss).to.include(
239
+ "--monster-vertical-tabs-nav-min-block-size:9rem",
240
+ );
241
+ expect(generatedCss).to.include(
242
+ "--monster-vertical-tabs-label-writing-mode:horizontal-tb",
243
+ );
244
+ expect(generatedCss).to.include("--monster-button-bar-gap:0");
245
+ expect(generatedCss).to.include("--monster-button-bar-item-spacing:0");
246
+ expect(generatedCss).to.include(
247
+ "--monster-action-menu-min-inline-size:11rem",
248
+ );
249
+ expect(generatedCss).to.include(
250
+ "--monster-action-button-item-background-color:var(",
251
+ );
252
+ expect(generatedCss).to.include(
253
+ "--monster-full-screen-control-size:var(--monster-control-min-block-size)",
254
+ );
255
+ expect(generatedCss).to.match(
256
+ /--monster-full-screen-control-active-background-color:var\(\s*--monster-theme-accent\s*\)/u,
257
+ );
258
+ expect(generatedCss).to.match(
259
+ /--monster-action-button-item-hover-background-color:var\(\s*--monster-action-menu-item-hover-background-color\s*\)/u,
260
+ );
261
+ expect(generatedStyleSheet).to.include("export { ThemeModernStyleSheet }");
262
+ expect(generatedStyleSheet).to.include(
263
+ ":root[data-monster-theme-name=modern]",
264
+ );
265
+ });
266
+
267
+ it("uses a neutral warm foundation without blue-tinted gray surfaces", () => {
268
+ for (const token of [
269
+ "theme-surface-canvas",
270
+ "theme-surface",
271
+ "theme-surface-subtle",
272
+ "theme-surface-strong",
273
+ "theme-text",
274
+ "theme-text-secondary",
275
+ "theme-text-muted",
276
+ "theme-border",
277
+ "theme-border-strong",
278
+ ]) {
279
+ const channels = getHexToken(themeSource, token)
280
+ .slice(1)
281
+ .match(/.{2}/gu)
282
+ .map((channel) => Number.parseInt(channel, 16));
283
+
284
+ expect(channels[0], token).to.be.at.least(channels[1]);
285
+ expect(channels[1], token).to.be.at.least(channels[2]);
286
+ }
287
+ });
288
+
289
+ it("uses neutral secondary content with warm subtle surfaces", () => {
290
+ expect(themeSource).to.include("--monster-theme-accent-subtle: #ece7df");
291
+ expect(themeSource).to.include("--monster-theme-secondary: #4d4b45");
292
+ expect(themeSource).to.include("--monster-theme-secondary-subtle: #f2e4d5");
293
+ expect(themeSource).to.include("--monster-theme-secondary-strong: #3b3935");
294
+ expect(themeSource).to.include("--monster-theme-focus-ring: #236b5b");
295
+ expect(themeSource).to.include("--monster-theme-selection: #dcefe6");
296
+ for (const level of [2, 3]) {
297
+ expect(themeSource).to.include(
298
+ `--monster-bg-color-secondary-${level}: var(--monster-theme-secondary-subtle)`,
299
+ );
300
+ }
301
+ expect(themeSource).to.include(
302
+ "--monster-bg-color-secondary-4: var(--monster-theme-secondary-strong)",
303
+ );
304
+ for (const violet of ["#674fd1", "#e7e0f8", "#ddd4f5"]) {
305
+ expect(themeSource).to.not.include(violet);
306
+ }
307
+ });
308
+
309
+ it("preserves the standard gradient colors in light and dark mode", () => {
310
+ for (const declaration of [
311
+ "--monster-color-gradient-1: #833ab4",
312
+ "--monster-color-gradient-2: #fd1d1d",
313
+ "--monster-color-gradient-3: #fcb045",
314
+ "--monster-color-gradient-1: #ffe0b2",
315
+ "--monster-color-gradient-2: #ad8275",
316
+ "--monster-color-gradient-3: #771ba3",
317
+ ]) {
318
+ expect(themeSource, declaration).to.include(declaration);
319
+ }
320
+
321
+ expect(themeSource).to.include("@media (prefers-color-scheme: dark)");
322
+ expect(generatedCss).to.include("--monster-color-gradient-1:#833ab4");
323
+ expect(generatedCss).to.include("--monster-color-gradient-2:#fd1d1d");
324
+ expect(generatedCss).to.include("--monster-color-gradient-3:#fcb045");
325
+ expect(generatedCss).to.include("--monster-color-gradient-1:#ffe0b2");
326
+ expect(generatedCss).to.include("--monster-color-gradient-2:#ad8275");
327
+ expect(generatedCss).to.include("--monster-color-gradient-3:#771ba3");
328
+ });
329
+
330
+ it("defines a complete automatic dark semantic palette", () => {
331
+ const darkThemeSource = getDarkThemeSource(themeSource);
332
+
333
+ for (const token of darkSemanticTokens) {
334
+ expect(getHexToken(darkThemeSource, token), token).to.match(
335
+ /^#[0-9a-f]{6}$/iu,
336
+ );
337
+ }
338
+
339
+ expect(generatedCss).to.include(
340
+ "@media (prefers-color-scheme:dark){:root[data-monster-theme-name=modern]",
341
+ );
342
+ expect(generatedCss).to.include(
343
+ "--monster-theme-surface-canvas:#161512",
344
+ );
345
+ expect(generatedCss).to.include("--monster-theme-text:#f4f0e8");
346
+ expect(generatedCss).to.include("--monster-theme-secondary:#d2ccc0");
347
+ expect(generatedCss).to.include("--monster-theme-secondary-subtle:#302d29");
348
+ expect(generatedCss).to.include("--monster-theme-focus-ring:#72cbb0");
349
+ expect(generatedCss).to.include("--monster-theme-selection:#24483e");
350
+ expect(generatedStyleSheet).to.include(
351
+ "--monster-theme-disabled-surface:#37342f",
352
+ );
353
+ });
354
+
355
+ for (const [foreground, background, minimum] of contrastPairs) {
356
+ it(`${foreground} on ${background} has at least ${minimum}:1 contrast`, () => {
357
+ const ratio = getContrastRatio(
358
+ getHexToken(themeSource, foreground),
359
+ getHexToken(themeSource, background),
360
+ );
361
+ expect(ratio).to.be.at.least(minimum);
362
+ });
363
+ }
364
+
365
+ for (const [foreground, background, minimum] of darkContrastPairs) {
366
+ it(`dark ${foreground} on ${background} has at least ${minimum}:1 contrast`, () => {
367
+ const darkThemeSource = getDarkThemeSource(themeSource);
368
+ const ratio = getContrastRatio(
369
+ getHexToken(darkThemeSource, foreground),
370
+ getHexToken(darkThemeSource, background),
371
+ );
372
+ expect(ratio).to.be.at.least(minimum);
373
+ });
374
+ }
375
+
376
+ it("uses the shared focus-ring tokens for document controls", () => {
377
+ for (const source of [commonSource, normalizeSource]) {
378
+ expect(source).to.include(":focus-visible");
379
+ expect(source).to.include("var(--monster-focus-ring-width");
380
+ expect(source).to.include("var(--monster-focus-ring-color");
381
+ expect(source).to.not.match(/:focus-visible\s*\{\s*outline:\s*none;/u);
382
+ }
383
+ });
384
+ });
385
+
386
+ function getHexToken(source, name) {
387
+ const match = source.match(
388
+ new RegExp(`--monster-${name}:\\s*(#[0-9a-f]{6})`, "iu"),
389
+ );
390
+ expect(match, `--monster-${name}`).to.not.equal(null);
391
+ return match[1];
392
+ }
393
+
394
+ function getDarkThemeSource(source) {
395
+ const mediaStart = source.indexOf("@media (prefers-color-scheme: dark)");
396
+ expect(mediaStart, "dark media query").to.be.at.least(0);
397
+
398
+ const selectorStart = source.indexOf(
399
+ ':root[data-monster-theme-name="modern"]',
400
+ mediaStart,
401
+ );
402
+ expect(selectorStart, "modern dark selector").to.be.at.least(0);
403
+
404
+ const blockStart = source.indexOf("{", selectorStart);
405
+ let depth = 1;
406
+ let cursor = blockStart + 1;
407
+ while (cursor < source.length && depth > 0) {
408
+ if (source[cursor] === "{") {
409
+ depth += 1;
410
+ } else if (source[cursor] === "}") {
411
+ depth -= 1;
412
+ }
413
+ cursor += 1;
414
+ }
415
+
416
+ expect(depth, "balanced dark theme block").to.equal(0);
417
+ return source.slice(blockStart + 1, cursor - 1);
418
+ }
419
+
420
+ function getContrastRatio(foreground, background) {
421
+ const foregroundLuminance = getRelativeLuminance(foreground);
422
+ const backgroundLuminance = getRelativeLuminance(background);
423
+ return (
424
+ (Math.max(foregroundLuminance, backgroundLuminance) + 0.05) /
425
+ (Math.min(foregroundLuminance, backgroundLuminance) + 0.05)
426
+ );
427
+ }
428
+
429
+ function getRelativeLuminance(hex) {
430
+ const channels = hex
431
+ .slice(1)
432
+ .match(/.{2}/gu)
433
+ .map((channel) => Number.parseInt(channel, 16) / 255)
434
+ .map((channel) =>
435
+ channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4,
436
+ );
437
+
438
+ return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
439
+ }
@@ -0,0 +1,41 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+
5
+ const projectRoot = resolve(import.meta.dirname, "../../../..");
6
+
7
+ describe("Theme review component regressions", () => {
8
+ it("keeps reviewed responsive styles in generated component sheets", async () => {
9
+ const checks = [
10
+ ["content", "copy", "cursor: pointer", "cursor:pointer"],
11
+ ["form", "sheet", '[part="context-menu"]', "[part=context-menu]"],
12
+ ["notify", "notify", "width: min(25rem, 100%)", "width:min(25rem,100%)"],
13
+ ["state", "log", "box-sizing: border-box", "box-sizing:border-box"],
14
+ ];
15
+
16
+ for (const [family, name, sourceToken, generatedToken] of checks) {
17
+ const [source, generated] = await Promise.all([
18
+ readFile(
19
+ resolve(
20
+ projectRoot,
21
+ `source/components/${family}/style/${name}.pcss`,
22
+ ),
23
+ "utf8",
24
+ ),
25
+ readFile(
26
+ resolve(
27
+ projectRoot,
28
+ `source/components/${family}/stylesheet/${name}.mjs`,
29
+ ),
30
+ "utf8",
31
+ ),
32
+ ]);
33
+
34
+ assert.ok(source.includes(sourceToken), `${family}/${name} source`);
35
+ assert.ok(
36
+ generated.includes(generatedToken),
37
+ `${family}/${name} generated`,
38
+ );
39
+ }
40
+ });
41
+ });