@actuate-media/cms-admin 0.63.2 → 0.65.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 (245) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/design-system/README.md +53 -0
  3. package/design-system/tokens.css +114 -0
  4. package/design-system/tokens.json +149 -0
  5. package/design-system/tokens.scss +108 -0
  6. package/design-system/tokens.tailwind.cjs +80 -0
  7. package/dist/__tests__/components/ui-primitives.test.d.ts +2 -0
  8. package/dist/__tests__/components/ui-primitives.test.d.ts.map +1 -0
  9. package/dist/__tests__/components/ui-primitives.test.js +45 -0
  10. package/dist/__tests__/components/ui-primitives.test.js.map +1 -0
  11. package/dist/__tests__/layout/primitives.test.js +9 -2
  12. package/dist/__tests__/layout/primitives.test.js.map +1 -1
  13. package/dist/__tests__/views/updates-tab.render.test.js +1 -1
  14. package/dist/__tests__/views/updates-tab.render.test.js.map +1 -1
  15. package/dist/actuate-admin.css +1 -1
  16. package/dist/components/EnabledSwitch.d.ts +2 -5
  17. package/dist/components/EnabledSwitch.d.ts.map +1 -1
  18. package/dist/components/EnabledSwitch.js +4 -7
  19. package/dist/components/EnabledSwitch.js.map +1 -1
  20. package/dist/components/SEOPanel.d.ts.map +1 -1
  21. package/dist/components/SEOPanel.js +2 -1
  22. package/dist/components/SEOPanel.js.map +1 -1
  23. package/dist/components/ThemeProvider.d.ts.map +1 -1
  24. package/dist/components/ThemeProvider.js +5 -3
  25. package/dist/components/ThemeProvider.js.map +1 -1
  26. package/dist/components/forms/FieldsPanel.js +2 -2
  27. package/dist/components/forms/FieldsPanel.js.map +1 -1
  28. package/dist/components/forms/NotificationsPanel.js +2 -2
  29. package/dist/components/forms/NotificationsPanel.js.map +1 -1
  30. package/dist/components/forms/WebhooksPanel.d.ts.map +1 -1
  31. package/dist/components/forms/WebhooksPanel.js +2 -2
  32. package/dist/components/forms/WebhooksPanel.js.map +1 -1
  33. package/dist/components/forms/primitives.d.ts.map +1 -1
  34. package/dist/components/forms/primitives.js +5 -8
  35. package/dist/components/forms/primitives.js.map +1 -1
  36. package/dist/components/posts/badges.d.ts.map +1 -1
  37. package/dist/components/posts/badges.js +7 -13
  38. package/dist/components/posts/badges.js.map +1 -1
  39. package/dist/components/seo/RedirectEditorDrawer.d.ts.map +1 -1
  40. package/dist/components/seo/RedirectEditorDrawer.js +2 -1
  41. package/dist/components/seo/RedirectEditorDrawer.js.map +1 -1
  42. package/dist/components/seo/SeoEditorDrawer.d.ts.map +1 -1
  43. package/dist/components/seo/SeoEditorDrawer.js +3 -2
  44. package/dist/components/seo/SeoEditorDrawer.js.map +1 -1
  45. package/dist/components/seo/primitives.d.ts.map +1 -1
  46. package/dist/components/seo/primitives.js +11 -15
  47. package/dist/components/seo/primitives.js.map +1 -1
  48. package/dist/components/ui/Button.d.ts +1 -1
  49. package/dist/components/ui/Button.js +1 -1
  50. package/dist/components/ui/Button.js.map +1 -1
  51. package/dist/components/ui/Card.d.ts +6 -5
  52. package/dist/components/ui/Card.d.ts.map +1 -1
  53. package/dist/components/ui/Card.js +20 -18
  54. package/dist/components/ui/Card.js.map +1 -1
  55. package/dist/components/ui/FilterPill.d.ts +14 -0
  56. package/dist/components/ui/FilterPill.d.ts.map +1 -0
  57. package/dist/components/ui/FilterPill.js +8 -0
  58. package/dist/components/ui/FilterPill.js.map +1 -0
  59. package/dist/components/ui/Input.d.ts +2 -1
  60. package/dist/components/ui/Input.d.ts.map +1 -1
  61. package/dist/components/ui/Input.js +9 -8
  62. package/dist/components/ui/Input.js.map +1 -1
  63. package/dist/components/ui/SearchInput.d.ts.map +1 -1
  64. package/dist/components/ui/SearchInput.js +2 -1
  65. package/dist/components/ui/SearchInput.js.map +1 -1
  66. package/dist/components/ui/Select.d.ts.map +1 -1
  67. package/dist/components/ui/Select.js +9 -10
  68. package/dist/components/ui/Select.js.map +1 -1
  69. package/dist/components/ui/StatPill.d.ts +18 -0
  70. package/dist/components/ui/StatPill.d.ts.map +1 -0
  71. package/dist/components/ui/StatPill.js +5 -0
  72. package/dist/components/ui/StatPill.js.map +1 -0
  73. package/dist/components/ui/StatusBadge.d.ts +25 -0
  74. package/dist/components/ui/StatusBadge.d.ts.map +1 -0
  75. package/dist/components/ui/StatusBadge.js +41 -0
  76. package/dist/components/ui/StatusBadge.js.map +1 -0
  77. package/dist/components/ui/Toggle.d.ts +31 -0
  78. package/dist/components/ui/Toggle.d.ts.map +1 -0
  79. package/dist/components/ui/Toggle.js +41 -0
  80. package/dist/components/ui/Toggle.js.map +1 -0
  81. package/dist/components/ui/form-control.d.ts +11 -0
  82. package/dist/components/ui/form-control.d.ts.map +1 -0
  83. package/dist/components/ui/form-control.js +11 -0
  84. package/dist/components/ui/form-control.js.map +1 -0
  85. package/dist/components/ui/index.d.ts +9 -0
  86. package/dist/components/ui/index.d.ts.map +1 -1
  87. package/dist/components/ui/index.js +5 -0
  88. package/dist/components/ui/index.js.map +1 -1
  89. package/dist/fields/PropInput.d.ts.map +1 -1
  90. package/dist/fields/PropInput.js +2 -1
  91. package/dist/fields/PropInput.js.map +1 -1
  92. package/dist/fields/ToggleField.d.ts.map +1 -1
  93. package/dist/fields/ToggleField.js +2 -1
  94. package/dist/fields/ToggleField.js.map +1 -1
  95. package/dist/layout/Header.d.ts.map +1 -1
  96. package/dist/layout/Header.js +5 -2
  97. package/dist/layout/Header.js.map +1 -1
  98. package/dist/layout/Sidebar.js +1 -1
  99. package/dist/layout/Sidebar.js.map +1 -1
  100. package/dist/layout/primitives/PageContainer.d.ts.map +1 -1
  101. package/dist/layout/primitives/PageContainer.js +1 -1
  102. package/dist/layout/primitives/PageContainer.js.map +1 -1
  103. package/dist/layout/primitives/tokens.d.ts +10 -4
  104. package/dist/layout/primitives/tokens.d.ts.map +1 -1
  105. package/dist/layout/primitives/tokens.js +10 -4
  106. package/dist/layout/primitives/tokens.js.map +1 -1
  107. package/dist/views/Dashboard.js +1 -1
  108. package/dist/views/Dashboard.js.map +1 -1
  109. package/dist/views/FormEditor.d.ts.map +1 -1
  110. package/dist/views/FormEditor.js +2 -1
  111. package/dist/views/FormEditor.js.map +1 -1
  112. package/dist/views/FormEntries.js +1 -1
  113. package/dist/views/FormEntries.js.map +1 -1
  114. package/dist/views/FormSubmissions.js +1 -1
  115. package/dist/views/FormSubmissions.js.map +1 -1
  116. package/dist/views/Forms.js +3 -3
  117. package/dist/views/Forms.js.map +1 -1
  118. package/dist/views/MediaBrowser.d.ts.map +1 -1
  119. package/dist/views/MediaBrowser.js +3 -2
  120. package/dist/views/MediaBrowser.js.map +1 -1
  121. package/dist/views/Pages/PageTagManager.d.ts.map +1 -1
  122. package/dist/views/Pages/PageTagManager.js +1 -1
  123. package/dist/views/Pages/PageTagManager.js.map +1 -1
  124. package/dist/views/Pages/PagesListView.js +1 -1
  125. package/dist/views/Pages/PagesListView.js.map +1 -1
  126. package/dist/views/Pages.js +1 -1
  127. package/dist/views/Pages.js.map +1 -1
  128. package/dist/views/Posts/NewPostPicker.d.ts.map +1 -1
  129. package/dist/views/Posts/NewPostPicker.js +1 -1
  130. package/dist/views/Posts/NewPostPicker.js.map +1 -1
  131. package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -1
  132. package/dist/views/Posts/NewPostTypeInstructions.js +1 -1
  133. package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -1
  134. package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
  135. package/dist/views/Posts/PostTypesView.js +1 -1
  136. package/dist/views/Posts/PostTypesView.js.map +1 -1
  137. package/dist/views/Posts/PostsListView.js +1 -1
  138. package/dist/views/Posts/PostsListView.js.map +1 -1
  139. package/dist/views/Profile.js +1 -1
  140. package/dist/views/Profile.js.map +1 -1
  141. package/dist/views/Redirects.js +1 -1
  142. package/dist/views/Redirects.js.map +1 -1
  143. package/dist/views/SEO.js +1 -1
  144. package/dist/views/SEO.js.map +1 -1
  145. package/dist/views/ScriptTagEditor.d.ts.map +1 -1
  146. package/dist/views/ScriptTagEditor.js +1 -1
  147. package/dist/views/ScriptTagEditor.js.map +1 -1
  148. package/dist/views/ScriptTags.d.ts.map +1 -1
  149. package/dist/views/ScriptTags.js +2 -1
  150. package/dist/views/ScriptTags.js.map +1 -1
  151. package/dist/views/Settings.js +1 -1
  152. package/dist/views/Settings.js.map +1 -1
  153. package/dist/views/Users.d.ts.map +1 -1
  154. package/dist/views/Users.js +2 -6
  155. package/dist/views/Users.js.map +1 -1
  156. package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
  157. package/dist/views/page-builder/BlockEditor.js +2 -2
  158. package/dist/views/page-builder/BlockEditor.js.map +1 -1
  159. package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
  160. package/dist/views/page-builder/BottomBar.js +2 -2
  161. package/dist/views/page-builder/BottomBar.js.map +1 -1
  162. package/dist/views/page-builder/NodeSettings.js +3 -3
  163. package/dist/views/page-builder/NodeSettings.js.map +1 -1
  164. package/dist/views/page-builder/PageTemplates.js +1 -1
  165. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  166. package/dist/views/page-builder/SavedSections.js +2 -2
  167. package/dist/views/page-builder/SavedSections.js.map +1 -1
  168. package/dist/views/page-builder/Widgets.js +1 -1
  169. package/dist/views/page-builder/Widgets.js.map +1 -1
  170. package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
  171. package/dist/views/page-editor/SectionInspector.js +2 -1
  172. package/dist/views/page-editor/SectionInspector.js.map +1 -1
  173. package/dist/views/post-editor/PostHeaderPanel.d.ts.map +1 -1
  174. package/dist/views/post-editor/PostHeaderPanel.js +2 -1
  175. package/dist/views/post-editor/PostHeaderPanel.js.map +1 -1
  176. package/dist/views/seo/TechnicalTab.d.ts.map +1 -1
  177. package/dist/views/seo/TechnicalTab.js +2 -2
  178. package/dist/views/seo/TechnicalTab.js.map +1 -1
  179. package/dist/views/settings/components.d.ts.map +1 -1
  180. package/dist/views/settings/components.js +4 -2
  181. package/dist/views/settings/components.js.map +1 -1
  182. package/package.json +9 -4
  183. package/src/__tests__/components/ui-primitives.test.tsx +58 -0
  184. package/src/__tests__/layout/primitives.test.ts +10 -2
  185. package/src/__tests__/views/updates-tab.render.test.tsx +2 -1
  186. package/src/components/EnabledSwitch.tsx +4 -22
  187. package/src/components/SEOPanel.tsx +2 -11
  188. package/src/components/ThemeProvider.tsx +5 -3
  189. package/src/components/forms/FieldsPanel.tsx +4 -6
  190. package/src/components/forms/NotificationsPanel.tsx +2 -9
  191. package/src/components/forms/WebhooksPanel.tsx +4 -6
  192. package/src/components/forms/primitives.tsx +6 -16
  193. package/src/components/posts/badges.tsx +8 -21
  194. package/src/components/seo/RedirectEditorDrawer.tsx +12 -16
  195. package/src/components/seo/SeoEditorDrawer.tsx +16 -24
  196. package/src/components/seo/primitives.tsx +18 -34
  197. package/src/components/ui/Button.tsx +1 -1
  198. package/src/components/ui/Card.tsx +35 -18
  199. package/src/components/ui/FilterPill.tsx +43 -0
  200. package/src/components/ui/Input.tsx +15 -17
  201. package/src/components/ui/SearchInput.tsx +9 -4
  202. package/src/components/ui/Select.tsx +15 -18
  203. package/src/components/ui/StatPill.tsx +37 -0
  204. package/src/components/ui/StatusBadge.tsx +66 -0
  205. package/src/components/ui/Toggle.tsx +85 -0
  206. package/src/components/ui/form-control.ts +12 -0
  207. package/src/components/ui/index.ts +9 -0
  208. package/src/fields/PropInput.tsx +6 -15
  209. package/src/fields/ToggleField.tsx +3 -15
  210. package/src/layout/Header.tsx +6 -2
  211. package/src/layout/Sidebar.tsx +3 -3
  212. package/src/layout/primitives/PageContainer.tsx +1 -5
  213. package/src/layout/primitives/tokens.ts +11 -4
  214. package/src/styles/actuate-tokens.css +97 -0
  215. package/src/styles/theme.css +88 -156
  216. package/src/views/Dashboard.tsx +1 -1
  217. package/src/views/FormEditor.tsx +6 -9
  218. package/src/views/FormEntries.tsx +1 -1
  219. package/src/views/FormSubmissions.tsx +1 -1
  220. package/src/views/Forms.tsx +5 -7
  221. package/src/views/MediaBrowser.tsx +4 -7
  222. package/src/views/Pages/PageTagManager.tsx +1 -3
  223. package/src/views/Pages/PagesListView.tsx +1 -1
  224. package/src/views/Pages.tsx +1 -1
  225. package/src/views/Posts/NewPostPicker.tsx +1 -3
  226. package/src/views/Posts/NewPostTypeInstructions.tsx +1 -3
  227. package/src/views/Posts/PostTypesView.tsx +1 -3
  228. package/src/views/Posts/PostsListView.tsx +1 -1
  229. package/src/views/Profile.tsx +1 -1
  230. package/src/views/Redirects.tsx +1 -1
  231. package/src/views/SEO.tsx +1 -1
  232. package/src/views/ScriptTagEditor.tsx +1 -3
  233. package/src/views/ScriptTags.tsx +17 -1
  234. package/src/views/Settings.tsx +1 -1
  235. package/src/views/Users.tsx +6 -18
  236. package/src/views/page-builder/BlockEditor.tsx +3 -6
  237. package/src/views/page-builder/BottomBar.tsx +3 -6
  238. package/src/views/page-builder/NodeSettings.tsx +7 -16
  239. package/src/views/page-builder/PageTemplates.tsx +1 -1
  240. package/src/views/page-builder/SavedSections.tsx +2 -2
  241. package/src/views/page-builder/Widgets.tsx +1 -1
  242. package/src/views/page-editor/SectionInspector.tsx +9 -21
  243. package/src/views/post-editor/PostHeaderPanel.tsx +10 -20
  244. package/src/views/seo/TechnicalTab.tsx +3 -6
  245. package/src/views/settings/components.tsx +19 -26
@@ -1,113 +1,74 @@
1
1
  @custom-variant dark (&:is(.dark *));
2
2
 
3
+ @import './actuate-tokens.css';
4
+
5
+ /*
6
+ * Semantic Tailwind tokens mapped from the ActuateCMS design system.
7
+ * Raw tokens live in actuate-tokens.css; Appearance accent overrides
8
+ * mutate --acc, --primary, --sidebar-primary, etc. at runtime.
9
+ */
3
10
  .actuate-admin {
4
- --font-size: 16px;
5
- --background: #ffffff;
6
- --foreground: oklch(0.145 0 0);
7
- --card: #ffffff;
8
- --card-foreground: oklch(0.145 0 0);
9
- --popover: oklch(1 0 0);
10
- --popover-foreground: oklch(0.145 0 0);
11
- --primary: #030213;
12
- --primary-foreground: oklch(1 0 0);
13
- --secondary: oklch(0.95 0.0058 264.53);
14
- --secondary-foreground: #030213;
15
- --muted: #ececf0;
16
- --muted-foreground: #717182;
17
- --accent: #e9ebef;
18
- --accent-foreground: #030213;
19
- --destructive: #d4183d;
11
+ --font-size: var(--fs-body);
12
+ --background: var(--bg);
13
+ --foreground: var(--txt);
14
+ /* --card surface token is defined in actuate-tokens.css */
15
+ --card-foreground: var(--txt);
16
+ --popover: var(--card);
17
+ --popover-foreground: var(--txt);
18
+ --primary: var(--acc);
19
+ --primary-foreground: #ffffff;
20
+ --secondary: var(--acc-l);
21
+ --secondary-foreground: var(--txt);
22
+ --muted: color-mix(in srgb, var(--bdr) 55%, var(--card));
23
+ --muted-foreground: var(--sub);
24
+ --accent: var(--acc-l);
25
+ --accent-foreground: var(--acc);
26
+ --destructive: var(--err);
20
27
  --destructive-foreground: #ffffff;
21
- /*
22
- * Semantic status + brand tokens.
23
- * ----------------------------------------------------------------
24
- * `destructive` already covers the "error/danger" state; these add
25
- * the remaining semantic states the admin needs (success / warning /
26
- * info) plus the violet brand accent used for links and primary
27
- * affordances. Values are tuned to read as TEXT on both the card
28
- * surface and their own `/10` tint (badge backgrounds), and as a
29
- * solid fill (dots, progress bars). The `*-foreground` variants are
30
- * for text placed on a SOLID fill. Light values match the previous
31
- * emerald/amber/blue/violet-600 palette so tokenizing is a no-op
32
- * visually; dark values track the *-300/*-400 tints used before.
33
- */
34
- --success: oklch(0.5 0.13 155);
28
+ --success: var(--suc);
35
29
  --success-foreground: #ffffff;
36
- --warning: oklch(0.52 0.13 73);
37
- --warning-foreground: oklch(0.205 0 0);
38
- --info: oklch(0.5 0.17 254);
30
+ --warning: var(--warn);
31
+ --warning-foreground: var(--txt);
32
+ --info: var(--info);
39
33
  --info-foreground: #ffffff;
40
- --brand: oklch(0.54 0.23 293);
34
+ --brand: var(--acc);
41
35
  --brand-foreground: #ffffff;
42
- --border: rgba(0, 0, 0, 0.1);
36
+ --border: var(--bdr);
43
37
  --input: transparent;
44
- --input-background: #f3f3f5;
45
- --switch-background: #cbced4;
38
+ --input-background: var(--card);
39
+ --switch-background: var(--bdr);
46
40
  --font-weight-medium: 500;
47
41
  --font-weight-normal: 400;
48
- --ring: oklch(0.708 0 0);
49
- --chart-1: oklch(0.646 0.222 41.116);
50
- --chart-2: oklch(0.6 0.118 184.704);
51
- --chart-3: oklch(0.398 0.07 227.392);
52
- --chart-4: oklch(0.828 0.189 84.429);
53
- --chart-5: oklch(0.769 0.188 70.08);
54
- --radius: 0.625rem;
55
- --sidebar: oklch(0.985 0 0);
56
- --sidebar-foreground: oklch(0.145 0 0);
57
- --sidebar-primary: #030213;
58
- --sidebar-primary-foreground: oklch(0.985 0 0);
59
- --sidebar-accent: oklch(0.97 0 0);
60
- --sidebar-accent-foreground: oklch(0.205 0 0);
61
- --sidebar-border: oklch(0.922 0 0);
62
- --sidebar-ring: oklch(0.708 0 0);
42
+ --ring: var(--acc-m);
43
+ --chart-1: var(--acc);
44
+ --chart-2: var(--info);
45
+ --chart-3: var(--suc);
46
+ --chart-4: var(--warn);
47
+ --chart-5: var(--err);
48
+ --radius: var(--r);
49
+ --sidebar: var(--sb);
50
+ --sidebar-foreground: var(--txt);
51
+ --sidebar-primary: var(--acc);
52
+ --sidebar-primary-foreground: #ffffff;
53
+ --sidebar-accent: var(--acc-l);
54
+ --sidebar-accent-foreground: var(--acc);
55
+ --sidebar-border: var(--bdr);
56
+ --sidebar-ring: var(--acc-m);
57
+ --shadow-card: var(--shd);
58
+ --shadow-elevated: var(--shd2);
63
59
  }
64
60
 
65
61
  .dark .actuate-admin,
66
62
  .actuate-admin.dark {
67
- --background: oklch(0.145 0 0);
68
- --foreground: oklch(0.985 0 0);
69
- --card: oklch(0.185 0 0);
70
- --card-foreground: oklch(0.985 0 0);
71
- --popover: oklch(0.185 0 0);
72
- --popover-foreground: oklch(0.985 0 0);
73
- --primary: oklch(0.985 0 0);
74
- --primary-foreground: oklch(0.205 0 0);
75
- --secondary: oklch(0.269 0 0);
76
- --secondary-foreground: oklch(0.985 0 0);
77
- --muted: oklch(0.269 0 0);
78
- --muted-foreground: oklch(0.708 0 0);
79
- --accent: oklch(0.269 0 0);
80
- --accent-foreground: oklch(0.985 0 0);
81
- --destructive: oklch(0.396 0.141 25.723);
82
- --destructive-foreground: oklch(0.637 0.237 25.331);
83
- --success: oklch(0.72 0.15 155);
84
- --success-foreground: oklch(0.205 0 0);
85
- --warning: oklch(0.8 0.13 80);
86
- --warning-foreground: oklch(0.205 0 0);
87
- --info: oklch(0.72 0.14 252);
88
- --info-foreground: oklch(0.205 0 0);
89
- --brand: oklch(0.72 0.18 293);
90
- --brand-foreground: oklch(0.205 0 0);
91
- --border: oklch(0.269 0 0);
92
- --input: oklch(0.269 0 0);
93
- --input-background: oklch(0.205 0 0);
94
- --switch-background: oklch(0.35 0 0);
95
- --ring: oklch(0.439 0 0);
96
- --font-weight-medium: 500;
97
- --font-weight-normal: 400;
98
- --chart-1: oklch(0.488 0.243 264.376);
99
- --chart-2: oklch(0.696 0.17 162.48);
100
- --chart-3: oklch(0.769 0.188 70.08);
101
- --chart-4: oklch(0.627 0.265 303.9);
102
- --chart-5: oklch(0.645 0.246 16.439);
103
- --sidebar: oklch(0.205 0 0);
104
- --sidebar-foreground: oklch(0.985 0 0);
105
- --sidebar-primary: oklch(0.488 0.243 264.376);
106
- --sidebar-primary-foreground: oklch(0.985 0 0);
107
- --sidebar-accent: oklch(0.269 0 0);
108
- --sidebar-accent-foreground: oklch(0.985 0 0);
109
- --sidebar-border: oklch(0.269 0 0);
110
- --sidebar-ring: oklch(0.439 0 0);
63
+ --primary-foreground: #0d0c18;
64
+ --secondary-foreground: var(--txt);
65
+ --warning-foreground: var(--txt);
66
+ --brand-foreground: #0d0c18;
67
+ --success-foreground: #0d0c18;
68
+ --info-foreground: #0d0c18;
69
+ --destructive-foreground: #0d0c18;
70
+ --sidebar-primary-foreground: #0d0c18;
71
+ --muted: color-mix(in srgb, var(--bdr) 40%, var(--card));
111
72
  }
112
73
 
113
74
  @theme inline {
@@ -145,10 +106,10 @@
145
106
  --color-chart-3: var(--chart-3);
146
107
  --color-chart-4: var(--chart-4);
147
108
  --color-chart-5: var(--chart-5);
148
- --radius-sm: calc(var(--radius) - 4px);
149
- --radius-md: calc(var(--radius) - 2px);
150
- --radius-lg: var(--radius);
151
- --radius-xl: calc(var(--radius) + 4px);
109
+ --radius-sm: var(--r-sm);
110
+ --radius-md: var(--r-control);
111
+ --radius-lg: var(--r);
112
+ --radius-xl: calc(var(--r) + 2px);
152
113
  --color-sidebar: var(--sidebar);
153
114
  --color-sidebar-foreground: var(--sidebar-foreground);
154
115
  --color-sidebar-primary: var(--sidebar-primary);
@@ -157,6 +118,12 @@
157
118
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
158
119
  --color-sidebar-border: var(--sidebar-border);
159
120
  --color-sidebar-ring: var(--sidebar-ring);
121
+ --text-2xl: var(--fs-page);
122
+ --text-xl: calc(var(--fs-card) + 4px);
123
+ --text-lg: var(--fs-card);
124
+ --text-base: var(--fs-body);
125
+ --text-sm: var(--fs-sub);
126
+ --text-xs: var(--fs-hint);
160
127
  }
161
128
 
162
129
  @layer base {
@@ -166,102 +133,67 @@
166
133
 
167
134
  .actuate-admin {
168
135
  @apply bg-background text-foreground;
169
- /*
170
- * Content font is var-driven so the Appearance tab can switch it at
171
- * runtime, but defaults to the system-ui stack. We do NOT load web fonts
172
- * here (no Google Fonts / @font-face) — the chosen family is applied as a
173
- * family string and falls back to system-ui when not installed, keeping
174
- * the admin dependency-free per the design system. This is the single
175
- * permitted `!important` (it must beat consumer global resets).
176
- */
177
- font-family: var(
178
- --actuate-admin-font,
179
- system-ui,
180
- -apple-system,
181
- BlinkMacSystemFont,
182
- 'Segoe UI',
183
- Roboto,
184
- sans-serif
185
- ) !important;
136
+ font-family: var(--actuate-admin-font, var(--font-sans)) !important;
186
137
  font-size: var(--font-size);
187
138
  line-height: 1.5;
188
139
  isolation: isolate;
189
- /*
190
- * Use `contain: style` only — NOT `contain: layout`.
191
- *
192
- * `contain: layout` would make every `.actuate-admin` element a
193
- * *containing block* for `position: fixed` descendants. That
194
- * silently breaks <AdminRoot>'s own fixed overlay (`position: fixed;
195
- * inset: 0`) whenever a consumer wraps AdminRoot in their own
196
- * `<div className="actuate-admin">` for tailwind isolation — the
197
- * inner fixed div resolves relative to the wrapper instead of the
198
- * viewport, and if the wrapper is 0-height (because all its
199
- * children are out-of-flow), every admin element renders offscreen
200
- * while still reporting non-zero computed dimensions. The visible
201
- * symptom is `<body> intercepts pointer events` on every click in
202
- * Playwright; the user-visible symptom is a blank-looking admin.
203
- *
204
- * `contain: style` keeps the style-scoping benefit (e.g. counters
205
- * don't leak out) without the positioning footgun.
206
- */
207
140
  contain: style;
208
141
  -webkit-font-smoothing: antialiased;
209
142
  -moz-osx-font-smoothing: grayscale;
210
143
  }
211
144
 
212
145
  .actuate-admin h1 {
213
- font-size: var(--text-2xl);
214
- font-weight: var(--font-weight-medium);
146
+ font-size: var(--fs-page);
147
+ font-weight: 700;
148
+ letter-spacing: -0.5px;
215
149
  line-height: 1.5;
216
150
  }
217
151
 
218
152
  .actuate-admin h2 {
219
- font-size: var(--text-xl);
220
- font-weight: var(--font-weight-medium);
153
+ font-size: var(--fs-card);
154
+ font-weight: 600;
221
155
  line-height: 1.5;
222
156
  }
223
157
 
224
158
  .actuate-admin h3 {
225
- font-size: var(--text-lg);
226
- font-weight: var(--font-weight-medium);
159
+ font-size: var(--fs-card);
160
+ font-weight: 600;
227
161
  line-height: 1.5;
228
162
  }
229
163
 
230
164
  .actuate-admin h4 {
231
- font-size: var(--text-base);
232
- font-weight: var(--font-weight-medium);
165
+ font-size: var(--fs-body);
166
+ font-weight: 600;
233
167
  line-height: 1.5;
234
168
  }
235
169
 
236
170
  .actuate-admin label {
237
- font-size: var(--text-base);
171
+ font-size: var(--fs-body);
238
172
  font-weight: var(--font-weight-medium);
239
173
  line-height: 1.5;
240
174
  }
241
175
 
242
176
  .actuate-admin button {
243
- font-size: var(--text-base);
177
+ font-size: var(--fs-body);
244
178
  font-weight: var(--font-weight-medium);
245
179
  line-height: 1.5;
246
180
  }
247
181
 
248
182
  .actuate-admin input {
249
- font-size: var(--text-base);
183
+ font-size: var(--fs-body);
250
184
  font-weight: var(--font-weight-normal);
251
185
  line-height: 1.5;
252
186
  }
187
+
188
+ .actuate-admin .actuate-section-label {
189
+ font-size: var(--fs-label);
190
+ font-weight: 600;
191
+ letter-spacing: 0.08em;
192
+ text-transform: uppercase;
193
+ color: var(--sub);
194
+ }
253
195
  }
254
196
 
255
- /*
256
- * Reduce-motion (admin-level Appearance toggle).
257
- *
258
- * Intentionally UNLAYERED: unlayered rules beat Tailwind's `@layer utilities`
259
- * transition/animation classes without needing `!important`, so the toggle can
260
- * neutralize utility-driven motion. We shorten (not delete) durations and never
261
- * touch outlines/focus rings, so state changes and focus visibility remain
262
- * intact. The OS `prefers-reduced-motion` is still respected app-wide via
263
- * `motion-safe:`/`motion-reduce:`; this class lets users opt in regardless.
264
- */
265
197
  .actuate-admin.reduce-motion,
266
198
  .actuate-admin.reduce-motion *,
267
199
  .actuate-admin.reduce-motion *::before,
@@ -567,7 +567,7 @@ export function Dashboard({ config, session, onNavigate }: DashboardProps) {
567
567
  consistent across navigation. The on-page secondary action row
568
568
  below covers the same creation paths plus authoring shortcuts. */}
569
569
  <div className="flex flex-col gap-1 sm:gap-0.5">
570
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">
570
+ <h1 className="text-foreground">
571
571
  {greeting}, {userName} <span aria-hidden>👋</span>
572
572
  </h1>
573
573
  <p className="text-muted-foreground text-sm">
@@ -16,6 +16,7 @@ import {
16
16
  } from 'lucide-react'
17
17
  import { useApiData } from '../lib/useApiData.js'
18
18
  import { cmsApi } from '../lib/api.js'
19
+ import { Toggle } from '../components/ui/Toggle.js'
19
20
 
20
21
  interface FormField {
21
22
  id: string
@@ -463,15 +464,11 @@ export function FormEditor({ formId, onNavigate }: FormEditorProps) {
463
464
  {expandedSection === 'analytics' && (
464
465
  <div className="border-border border-t p-4">
465
466
  <div className="mb-4 flex items-center gap-3">
466
- <label className="relative inline-flex cursor-pointer items-center">
467
- <input
468
- type="checkbox"
469
- checked={analytics.enabled}
470
- onChange={(e) => setAnalytics((a) => ({ ...a, enabled: e.target.checked }))}
471
- className="peer sr-only"
472
- />
473
- <div className="bg-muted peer peer-checked:bg-primary peer-focus:ring-ring/50 after:border-border after:bg-card h-5 w-9 rounded-full peer-focus:ring-2 peer-focus:outline-none after:absolute after:top-[2px] after:left-[2px] after:h-4 after:w-4 after:rounded-full after:border after:transition-all after:content-[''] peer-checked:after:translate-x-full peer-checked:after:border-white" />
474
- </label>
467
+ <Toggle
468
+ checked={analytics.enabled}
469
+ onCheckedChange={(v) => setAnalytics((a) => ({ ...a, enabled: v }))}
470
+ aria-label="Push events to Google Analytics 4"
471
+ />
475
472
  <span className="text-foreground text-sm font-medium">
476
473
  Push events to Google Analytics 4
477
474
  </span>
@@ -234,7 +234,7 @@ export function FormEntries({ onNavigate }: FormEntriesProps) {
234
234
  <div className="space-y-5 p-6">
235
235
  <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
236
236
  <div>
237
- <h1 className="text-foreground text-2xl font-medium">All Entries</h1>
237
+ <h1 className="text-foreground">All Entries</h1>
238
238
  <p className="text-muted-foreground mt-1 text-sm">
239
239
  {total} {total === 1 ? 'submission' : 'submissions'} across your forms
240
240
  </p>
@@ -254,7 +254,7 @@ export function FormSubmissions({ formId, onNavigate }: FormSubmissionsProps) {
254
254
  </button>
255
255
  <div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
256
256
  <div>
257
- <h1 className="text-foreground text-2xl font-medium">
257
+ <h1 className="text-foreground">
258
258
  {formName ? `${formName} — Submissions` : 'Submissions'}
259
259
  </h1>
260
260
  <p className="text-muted-foreground mt-1 text-sm">
@@ -8,7 +8,7 @@
8
8
  * opens the right-side `FormSchemaDrawer`.
9
9
  */
10
10
  import { ChevronRight, ClipboardList, Plus, Search, Settings2 } from 'lucide-react'
11
- import * as Switch from '@radix-ui/react-switch'
11
+ import { Toggle } from '../components/ui/Toggle.js'
12
12
  import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
13
13
  import { toast } from 'sonner'
14
14
  import { sortByRelevance } from '../lib/search.js'
@@ -158,7 +158,7 @@ export function Forms({ onNavigate }: FormsProps) {
158
158
  <div className="space-y-5 p-6">
159
159
  <header className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
160
160
  <div>
161
- <h1 className="text-foreground text-2xl font-medium">Forms</h1>
161
+ <h1 className="text-foreground">Forms</h1>
162
162
  <p className="text-muted-foreground mt-1 text-sm">
163
163
  Manage forms and view incoming submissions
164
164
  </p>
@@ -382,14 +382,12 @@ function FormsTable({
382
382
  {formatRelative(form.lastEntryAt)}
383
383
  </td>
384
384
  <td className="px-4 py-3 text-center">
385
- <Switch.Root
385
+ <Toggle
386
386
  checked={form.notifyEnabled ?? false}
387
387
  onCheckedChange={(v) => onNotifyToggle(form, v)}
388
388
  aria-label={`Notifications for ${form.name}`}
389
- className="bg-destructive data-[state=checked]:bg-success focus-visible:ring-ring relative inline-flex h-[18px] w-8 shrink-0 rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none"
390
- >
391
- <Switch.Thumb className="bg-background block h-3.5 w-3.5 translate-x-0.5 rounded-full shadow-sm transition-transform data-[state=checked]:translate-x-[14px]" />
392
- </Switch.Root>
389
+ variant="enabled"
390
+ />
393
391
  </td>
394
392
  <td className="px-4 py-3 text-right">
395
393
  <button
@@ -30,6 +30,7 @@ import { sortByRelevance, type SortConfig, toggleSort } from '../lib/search.js'
30
30
  import { useApiData } from '../lib/useApiData.js'
31
31
  import { cmsApi } from '../lib/api.js'
32
32
  import { FolderTree, type FolderSelection } from '../components/FolderTree.js'
33
+ import { StatusBadge } from '../components/ui/StatusBadge.js'
33
34
 
34
35
  interface MediaItem {
35
36
  id: number | string
@@ -411,7 +412,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
411
412
  <FolderInput className="text-muted-foreground h-5 w-5" />
412
413
  </button>
413
414
  <div>
414
- <h1 className="text-foreground mb-1 text-xl font-medium sm:text-2xl">Media Library</h1>
415
+ <h1 className="text-foreground mb-1">Media Library</h1>
415
416
  <p className="text-muted-foreground text-sm">{filteredAndSorted.length} files</p>
416
417
  </div>
417
418
  </div>
@@ -722,13 +723,9 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
722
723
  </td>
723
724
  <td className="px-3 py-2">
724
725
  {hasIssues ? (
725
- <span className="bg-warning/15 text-warning inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium">
726
- <AlertTriangle className="h-3 w-3" /> Needs attention
727
- </span>
726
+ <StatusBadge tone="warning">Needs attention</StatusBadge>
728
727
  ) : (
729
- <span className="bg-success/15 text-success inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium">
730
- Complete
731
- </span>
728
+ <StatusBadge tone="success">Complete</StatusBadge>
732
729
  )}
733
730
  </td>
734
731
  </tr>
@@ -85,9 +85,7 @@ export function PageTagManager({ onNavigate, startCreating = false }: PageTagMan
85
85
 
86
86
  <div className="mb-5 flex items-center justify-between">
87
87
  <div>
88
- <h1 className="text-foreground mb-1 text-xl font-medium tracking-tight sm:text-2xl">
89
- Page Tags
90
- </h1>
88
+ <h1 className="text-foreground mb-1">Page Tags</h1>
91
89
  <p className="text-muted-foreground text-sm">
92
90
  Organize pages with reusable, color-coded labels.
93
91
  </p>
@@ -290,7 +290,7 @@ export function PagesListView({ onNavigate }: PagesListViewProps) {
290
290
  <div className="flex h-full flex-col gap-4 p-4 sm:p-6">
291
291
  {/* Header */}
292
292
  <div className="flex flex-col gap-1">
293
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">Pages</h1>
293
+ <h1 className="text-foreground">Pages</h1>
294
294
  <p className="text-muted-foreground text-sm">
295
295
  {grandTotal} page{grandTotal === 1 ? '' : 's'} · organized by tags
296
296
  </p>
@@ -276,7 +276,7 @@ export function Pages({ onNavigate }: PagesProps) {
276
276
  <FolderInput className="text-muted-foreground h-5 w-5" />
277
277
  </button>
278
278
  <div>
279
- <h1 className="text-foreground text-xl font-medium sm:text-2xl">Pages</h1>
279
+ <h1 className="text-foreground">Pages</h1>
280
280
  <p className="text-muted-foreground text-sm">
281
281
  {totalCount} page{totalCount !== 1 ? 's' : ''}
282
282
  </p>
@@ -62,9 +62,7 @@ export function NewPostPicker({ onNavigate }: { onNavigate: (path: string) => vo
62
62
  </button>
63
63
 
64
64
  <div className="flex flex-col gap-1">
65
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">
66
- Choose a Post Type
67
- </h1>
65
+ <h1 className="text-foreground">Choose a Post Type</h1>
68
66
  <p className="text-muted-foreground text-sm">
69
67
  Pick which content model you want to use for this post. Each type has its own fields, URL
70
68
  base, and SEO defaults.
@@ -114,9 +114,7 @@ export function NewPostTypeInstructions({ onNavigate }: { onNavigate: (p: string
114
114
  </button>
115
115
 
116
116
  <div className="flex flex-col gap-1">
117
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">
118
- Add a Post Type
119
- </h1>
117
+ <h1 className="text-foreground">Add a Post Type</h1>
120
118
  <p className="text-muted-foreground text-sm">
121
119
  Post Types are content models defined in{' '}
122
120
  <code className="font-mono">actuate.config.ts</code>. Start from a quick template or fill
@@ -61,9 +61,7 @@ export function PostTypesView({ onNavigate }: PostTypesViewProps) {
61
61
  return (
62
62
  <div className="flex h-full flex-col gap-6 p-4 sm:p-6">
63
63
  <div className="flex flex-col gap-1">
64
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">
65
- Post Types
66
- </h1>
64
+ <h1 className="text-foreground">Post Types</h1>
67
65
  <p className="text-muted-foreground text-sm">
68
66
  Define content models and their fields. All types share the same default fields.
69
67
  </p>
@@ -216,7 +216,7 @@ export function PostsListView({ onNavigate }: PostsListViewProps) {
216
216
  <div className="flex h-full flex-col gap-4 p-4 sm:p-6">
217
217
  {/* Header */}
218
218
  <div className="flex flex-col gap-1">
219
- <h1 className="text-foreground text-xl font-medium tracking-tight sm:text-2xl">Posts</h1>
219
+ <h1 className="text-foreground">Posts</h1>
220
220
  <p className="text-muted-foreground text-sm">
221
221
  {totalAll} total post{totalAll === 1 ? '' : 's'} across all types
222
222
  </p>
@@ -21,7 +21,7 @@ export function Profile({ session }: ProfileProps = {}) {
21
21
  return (
22
22
  <div className="p-3 pr-6 sm:p-4 sm:pr-8">
23
23
  <div className="mb-4">
24
- <h1 className="text-foreground mb-1 text-2xl font-medium">Your profile</h1>
24
+ <h1 className="text-foreground mb-1">Your profile</h1>
25
25
  <p className="text-muted-foreground text-sm">
26
26
  Manage your account details and sign-in security.
27
27
  </p>
@@ -143,7 +143,7 @@ export function Redirects({ onNavigate }: RedirectsProps) {
143
143
 
144
144
  <div className="mb-4 flex items-center justify-between">
145
145
  <div>
146
- <h1 className="text-foreground mb-1 text-xl font-medium sm:text-2xl">Redirects</h1>
146
+ <h1 className="text-foreground mb-1">Redirects</h1>
147
147
  <p className="text-muted-foreground text-sm">
148
148
  {filteredAndSorted.length} total redirects
149
149
  </p>
package/src/views/SEO.tsx CHANGED
@@ -138,7 +138,7 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
138
138
  <div className="space-y-6 p-6">
139
139
  <header className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
140
140
  <div>
141
- <h1 className="text-foreground text-2xl font-medium">SEO Operations Center</h1>
141
+ <h1 className="text-foreground">SEO Operations Center</h1>
142
142
  <p className="text-muted-foreground mt-1 text-sm">
143
143
  Monitor, fix, and automate SEO across your content, redirects, and technical setup.
144
144
  </p>
@@ -135,9 +135,7 @@ export function ScriptTagEditor({ tagId, onNavigate }: ScriptTagEditorProps) {
135
135
  <ArrowLeft className="h-4 w-4" />
136
136
  Back to Script Tags
137
137
  </button>
138
- <h1 className="text-foreground text-2xl font-medium">
139
- {isNew ? 'New Script Tag' : 'Edit Script Tag'}
140
- </h1>
138
+ <h1 className="text-foreground">{isNew ? 'New Script Tag' : 'Edit Script Tag'}</h1>
141
139
  </div>
142
140
 
143
141
  <div className="border-warning/30 bg-warning/10 mb-6 rounded-lg border p-3">
@@ -4,6 +4,7 @@ import { useMemo, useState } from 'react'
4
4
  import { Code2, Plus, Loader2, AlertTriangle } from 'lucide-react'
5
5
  import { toast } from 'sonner'
6
6
  import { EnabledSwitch } from '../components/EnabledSwitch.js'
7
+ import { StatPill } from '../components/ui/StatPill.js'
7
8
  import { useApiData } from '../lib/useApiData.js'
8
9
  import { cmsApi } from '../lib/api.js'
9
10
 
@@ -123,10 +124,25 @@ export function ScriptTags({ onNavigate }: ScriptTagsProps) {
123
124
 
124
125
  <div className="mb-4 flex items-center justify-between">
125
126
  <div>
126
- <h1 className="text-foreground mb-1 text-2xl font-medium">Script Tags</h1>
127
+ <h1 className="text-foreground mb-1">Script Tags</h1>
127
128
  <p className="text-muted-foreground text-sm">
128
129
  Manage tracking codes, analytics, and custom scripts injected into your site
129
130
  </p>
131
+ {tags.length > 0 && (
132
+ <div className="mt-3 flex flex-wrap gap-1.5">
133
+ <StatPill value={tags.length} label="Total" />
134
+ <StatPill
135
+ value={tags.filter((t) => t.enabled).length}
136
+ label="Active"
137
+ valueClassName="text-[var(--suc)]"
138
+ />
139
+ <StatPill
140
+ value={tags.filter((t) => !t.enabled).length}
141
+ label="Inactive"
142
+ valueClassName="text-[var(--muted)]"
143
+ />
144
+ </div>
145
+ )}
130
146
  </div>
131
147
  <button
132
148
  type="button"
@@ -179,7 +179,7 @@ export function Settings({
179
179
  )}
180
180
 
181
181
  <div className="mb-4">
182
- <h1 className="text-foreground mb-1 text-2xl font-medium">Settings</h1>
182
+ <h1 className="text-foreground mb-1">Settings</h1>
183
183
  <p className="text-muted-foreground text-sm">Manage your CMS configuration</p>
184
184
  </div>
185
185