@actuate-media/cms-admin 0.54.0 → 0.56.2

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 (229) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/__tests__/components/posts-badges.test.js +3 -3
  3. package/dist/__tests__/components/posts-badges.test.js.map +1 -1
  4. package/dist/__tests__/lib/page-editor-service.test.js +1 -0
  5. package/dist/__tests__/lib/page-editor-service.test.js.map +1 -1
  6. package/dist/actuate-admin.css +1 -1
  7. package/dist/components/AICoauthorBubbleMenu.js +2 -2
  8. package/dist/components/AICoauthorBubbleMenu.js.map +1 -1
  9. package/dist/components/Breadcrumbs.js +2 -2
  10. package/dist/components/Breadcrumbs.js.map +1 -1
  11. package/dist/components/CoauthorResultPopover.d.ts.map +1 -1
  12. package/dist/components/CoauthorResultPopover.js +1 -1
  13. package/dist/components/CoauthorResultPopover.js.map +1 -1
  14. package/dist/components/CommandPalette.d.ts.map +1 -1
  15. package/dist/components/CommandPalette.js +11 -11
  16. package/dist/components/CommandPalette.js.map +1 -1
  17. package/dist/components/CommentSidePanel.js +11 -11
  18. package/dist/components/CommentSidePanel.js.map +1 -1
  19. package/dist/components/ContentOverviewChart.js +2 -2
  20. package/dist/components/ContentOverviewChart.js.map +1 -1
  21. package/dist/components/FocalPointPicker.js +1 -1
  22. package/dist/components/FocalPointPicker.js.map +1 -1
  23. package/dist/components/LivePreview.d.ts.map +1 -1
  24. package/dist/components/LivePreview.js +5 -5
  25. package/dist/components/LivePreview.js.map +1 -1
  26. package/dist/components/LogoPlaceholder.js +1 -1
  27. package/dist/components/LogoPlaceholder.js.map +1 -1
  28. package/dist/components/MentionableTextarea.js +3 -3
  29. package/dist/components/MentionableTextarea.js.map +1 -1
  30. package/dist/components/NotificationBell.js +1 -1
  31. package/dist/components/NotificationBell.js.map +1 -1
  32. package/dist/components/OfflineStatus.js +8 -8
  33. package/dist/components/OfflineStatus.js.map +1 -1
  34. package/dist/components/PresenceChips.d.ts.map +1 -1
  35. package/dist/components/PresenceChips.js +10 -6
  36. package/dist/components/PresenceChips.js.map +1 -1
  37. package/dist/components/SEOConfigPanel.d.ts.map +1 -1
  38. package/dist/components/SEOConfigPanel.js +10 -10
  39. package/dist/components/SEOConfigPanel.js.map +1 -1
  40. package/dist/components/SEOPanel.js +12 -12
  41. package/dist/components/SEOPanel.js.map +1 -1
  42. package/dist/components/SEOPerformance.d.ts.map +1 -1
  43. package/dist/components/SEOPerformance.js +3 -3
  44. package/dist/components/SEOPerformance.js.map +1 -1
  45. package/dist/components/TipTapEditor.js +6 -6
  46. package/dist/components/TipTapEditor.js.map +1 -1
  47. package/dist/components/pages/tag-badges.js +6 -6
  48. package/dist/components/pages/tag-badges.js.map +1 -1
  49. package/dist/components/posts/badges.d.ts +0 -4
  50. package/dist/components/posts/badges.d.ts.map +1 -1
  51. package/dist/components/posts/badges.js +28 -42
  52. package/dist/components/posts/badges.js.map +1 -1
  53. package/dist/components/ui/Badge.js +5 -5
  54. package/dist/components/ui/Badge.js.map +1 -1
  55. package/dist/components/ui/Toast.d.ts.map +1 -1
  56. package/dist/components/ui/Toast.js +4 -4
  57. package/dist/components/ui/Toast.js.map +1 -1
  58. package/dist/fields/ComponentBlockField.js +2 -2
  59. package/dist/fields/ComponentBlockField.js.map +1 -1
  60. package/dist/fields/FieldRenderer.js +1 -1
  61. package/dist/fields/FieldRenderer.js.map +1 -1
  62. package/dist/fields/PropInput.js +1 -1
  63. package/dist/fields/PropInput.js.map +1 -1
  64. package/dist/fields/RelationshipField.js +5 -5
  65. package/dist/fields/RelationshipField.js.map +1 -1
  66. package/dist/fields/ToggleField.js +1 -1
  67. package/dist/fields/ToggleField.js.map +1 -1
  68. package/dist/lib/seo-service.d.ts +0 -2
  69. package/dist/lib/seo-service.d.ts.map +1 -1
  70. package/dist/lib/seo-service.js.map +1 -1
  71. package/dist/views/Dashboard.js +5 -5
  72. package/dist/views/Dashboard.js.map +1 -1
  73. package/dist/views/DocumentEdit.d.ts.map +1 -1
  74. package/dist/views/DocumentEdit.js +5 -5
  75. package/dist/views/DocumentEdit.js.map +1 -1
  76. package/dist/views/ForgotPassword.d.ts.map +1 -1
  77. package/dist/views/ForgotPassword.js +3 -2
  78. package/dist/views/ForgotPassword.js.map +1 -1
  79. package/dist/views/FormEditor.d.ts.map +1 -1
  80. package/dist/views/FormEditor.js +5 -5
  81. package/dist/views/FormEditor.js.map +1 -1
  82. package/dist/views/Login.d.ts.map +1 -1
  83. package/dist/views/Login.js +17 -17
  84. package/dist/views/Login.js.map +1 -1
  85. package/dist/views/PageEditor.d.ts.map +1 -1
  86. package/dist/views/PageEditor.js +2 -2
  87. package/dist/views/PageEditor.js.map +1 -1
  88. package/dist/views/Pages/PagesListView.js +6 -6
  89. package/dist/views/Pages/PagesListView.js.map +1 -1
  90. package/dist/views/Pages.d.ts.map +1 -1
  91. package/dist/views/Pages.js +16 -19
  92. package/dist/views/Pages.js.map +1 -1
  93. package/dist/views/ResetPassword.d.ts.map +1 -1
  94. package/dist/views/ResetPassword.js +3 -2
  95. package/dist/views/ResetPassword.js.map +1 -1
  96. package/dist/views/Settings.js +2 -2
  97. package/dist/views/Settings.js.map +1 -1
  98. package/dist/views/SetupWizard.d.ts.map +1 -1
  99. package/dist/views/SetupWizard.js +6 -5
  100. package/dist/views/SetupWizard.js.map +1 -1
  101. package/dist/views/page-builder/DesignScore.js +12 -12
  102. package/dist/views/page-builder/DesignScore.js.map +1 -1
  103. package/dist/views/page-builder/SEOPanel.js +9 -9
  104. package/dist/views/page-builder/SEOPanel.js.map +1 -1
  105. package/dist/views/page-editor/EditorCanvas.js +1 -1
  106. package/dist/views/page-editor/EditorCanvas.js.map +1 -1
  107. package/dist/views/page-editor/EditorTopBar.js +2 -2
  108. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  109. package/dist/views/page-editor/PagePreview.js +1 -1
  110. package/dist/views/page-editor/PagePreview.js.map +1 -1
  111. package/dist/views/page-editor/SectionInspector.js +1 -1
  112. package/dist/views/page-editor/SectionInspector.js.map +1 -1
  113. package/dist/views/page-editor/SitePreviewFrame.js +1 -1
  114. package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
  115. package/dist/views/page-editor/sections/ArticleBodySection.js +1 -1
  116. package/dist/views/page-editor/sections/ArticleBodySection.js.map +1 -1
  117. package/dist/views/page-editor/sections/AuthorBioSection.js +2 -2
  118. package/dist/views/page-editor/sections/AuthorBioSection.js.map +1 -1
  119. package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
  120. package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
  121. package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
  122. package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
  123. package/dist/views/page-editor/sections/EmbedSection.d.ts +10 -0
  124. package/dist/views/page-editor/sections/EmbedSection.d.ts.map +1 -0
  125. package/dist/views/page-editor/sections/EmbedSection.js +24 -0
  126. package/dist/views/page-editor/sections/EmbedSection.js.map +1 -0
  127. package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
  128. package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
  129. package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
  130. package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
  131. package/dist/views/page-editor/sections/GallerySection.js +1 -1
  132. package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
  133. package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
  134. package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
  135. package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
  136. package/dist/views/page-editor/sections/MissionSection.js +1 -1
  137. package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
  138. package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
  139. package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
  140. package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
  141. package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
  142. package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
  143. package/dist/views/page-editor/sections/SectionRenderer.js +5 -1
  144. package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
  145. package/dist/views/page-editor/sections/parts.js +6 -6
  146. package/dist/views/page-editor/sections/parts.js.map +1 -1
  147. package/dist/views/post-editor/PostEditorCanvas.js +2 -2
  148. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
  149. package/dist/views/post-editor/PostHeader.js +2 -2
  150. package/dist/views/post-editor/PostHeader.js.map +1 -1
  151. package/dist/views/post-editor/PostHeaderPanel.js +1 -1
  152. package/dist/views/post-editor/PostHeaderPanel.js.map +1 -1
  153. package/dist/views/post-editor/PostPreview.js +1 -1
  154. package/dist/views/post-editor/PostPreview.js.map +1 -1
  155. package/dist/views/profile/AccountSecurity.js +1 -1
  156. package/dist/views/profile/AccountSecurity.js.map +1 -1
  157. package/dist/views/seo/TechnicalTab.d.ts.map +1 -1
  158. package/dist/views/seo/TechnicalTab.js +0 -10
  159. package/dist/views/seo/TechnicalTab.js.map +1 -1
  160. package/dist/views/settings/components.js +1 -1
  161. package/dist/views/settings/components.js.map +1 -1
  162. package/package.json +2 -2
  163. package/src/__tests__/components/posts-badges.test.tsx +3 -3
  164. package/src/__tests__/lib/page-editor-service.test.ts +1 -0
  165. package/src/components/AICoauthorBubbleMenu.tsx +4 -4
  166. package/src/components/Breadcrumbs.tsx +5 -5
  167. package/src/components/CoauthorResultPopover.tsx +26 -23
  168. package/src/components/CommandPalette.tsx +41 -39
  169. package/src/components/CommentSidePanel.tsx +20 -20
  170. package/src/components/ContentOverviewChart.tsx +4 -4
  171. package/src/components/FocalPointPicker.tsx +2 -2
  172. package/src/components/LivePreview.tsx +20 -17
  173. package/src/components/LogoPlaceholder.tsx +1 -1
  174. package/src/components/MentionableTextarea.tsx +5 -5
  175. package/src/components/NotificationBell.tsx +10 -10
  176. package/src/components/OfflineStatus.tsx +8 -8
  177. package/src/components/PresenceChips.tsx +12 -8
  178. package/src/components/SEOConfigPanel.tsx +40 -38
  179. package/src/components/SEOPanel.tsx +15 -15
  180. package/src/components/SEOPerformance.tsx +25 -23
  181. package/src/components/TipTapEditor.tsx +7 -7
  182. package/src/components/pages/tag-badges.tsx +6 -6
  183. package/src/components/posts/badges.tsx +28 -46
  184. package/src/components/ui/Badge.tsx +5 -5
  185. package/src/components/ui/Toast.tsx +4 -5
  186. package/src/fields/ComponentBlockField.tsx +2 -2
  187. package/src/fields/FieldRenderer.tsx +1 -1
  188. package/src/fields/PropInput.tsx +1 -1
  189. package/src/fields/RelationshipField.tsx +5 -5
  190. package/src/fields/ToggleField.tsx +1 -1
  191. package/src/lib/seo-service.ts +0 -2
  192. package/src/views/Dashboard.tsx +5 -5
  193. package/src/views/DocumentEdit.tsx +13 -11
  194. package/src/views/ForgotPassword.tsx +21 -18
  195. package/src/views/FormEditor.tsx +74 -69
  196. package/src/views/Login.tsx +45 -39
  197. package/src/views/PageEditor.tsx +26 -22
  198. package/src/views/Pages/PagesListView.tsx +6 -6
  199. package/src/views/Pages.tsx +64 -65
  200. package/src/views/ResetPassword.tsx +32 -29
  201. package/src/views/Settings.tsx +2 -2
  202. package/src/views/SetupWizard.tsx +42 -33
  203. package/src/views/page-builder/DesignScore.tsx +12 -12
  204. package/src/views/page-builder/SEOPanel.tsx +12 -12
  205. package/src/views/page-editor/EditorCanvas.tsx +1 -1
  206. package/src/views/page-editor/EditorTopBar.tsx +2 -2
  207. package/src/views/page-editor/PagePreview.tsx +1 -1
  208. package/src/views/page-editor/SectionInspector.tsx +1 -1
  209. package/src/views/page-editor/SitePreviewFrame.tsx +1 -1
  210. package/src/views/page-editor/sections/ArticleBodySection.tsx +5 -5
  211. package/src/views/page-editor/sections/AuthorBioSection.tsx +6 -6
  212. package/src/views/page-editor/sections/ByTheNumbersSection.tsx +5 -5
  213. package/src/views/page-editor/sections/CtaBannerSection.tsx +1 -1
  214. package/src/views/page-editor/sections/EmbedSection.tsx +43 -0
  215. package/src/views/page-editor/sections/FeatureHighlightSection.tsx +7 -5
  216. package/src/views/page-editor/sections/GallerySection.tsx +6 -4
  217. package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
  218. package/src/views/page-editor/sections/MissionSection.tsx +4 -2
  219. package/src/views/page-editor/sections/PullQuoteSection.tsx +4 -4
  220. package/src/views/page-editor/sections/RelatedPostsSection.tsx +5 -5
  221. package/src/views/page-editor/sections/SectionRenderer.tsx +6 -2
  222. package/src/views/page-editor/sections/parts.tsx +6 -6
  223. package/src/views/post-editor/PostEditorCanvas.tsx +4 -4
  224. package/src/views/post-editor/PostHeader.tsx +5 -5
  225. package/src/views/post-editor/PostHeaderPanel.tsx +1 -1
  226. package/src/views/post-editor/PostPreview.tsx +1 -1
  227. package/src/views/profile/AccountSecurity.tsx +1 -1
  228. package/src/views/seo/TechnicalTab.tsx +0 -10
  229. package/src/views/settings/components.tsx +1 -1
@@ -192,7 +192,7 @@ export function SEOConfigPanel() {
192
192
 
193
193
  if (loading) {
194
194
  return (
195
- <div className="flex items-center gap-2 py-8 text-sm text-gray-600">
195
+ <div className="text-muted-foreground flex items-center gap-2 py-8 text-sm">
196
196
  <Loader2 className="h-4 w-4 animate-spin" /> Loading SEO defaults…
197
197
  </div>
198
198
  )
@@ -200,12 +200,12 @@ export function SEOConfigPanel() {
200
200
 
201
201
  if (error) {
202
202
  return (
203
- <div className="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700">
203
+ <div className="border-destructive/30 bg-destructive/10 text-destructive rounded-lg border p-4 text-sm">
204
204
  <div className="mb-2 font-medium">Failed to load SEO config</div>
205
205
  <div className="font-mono text-xs">{error}</div>
206
206
  <button
207
207
  onClick={() => void load()}
208
- className="mt-3 inline-flex items-center gap-1.5 rounded-md border border-red-300 bg-white px-3 py-1.5 text-xs font-medium text-red-700 hover:bg-red-100"
208
+ className="border-destructive bg-card text-destructive hover:bg-destructive/10 mt-3 inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-xs font-medium"
209
209
  >
210
210
  <RefreshCw className="h-3.5 w-3.5" /> Retry
211
211
  </button>
@@ -217,21 +217,23 @@ export function SEOConfigPanel() {
217
217
  <div className="space-y-6">
218
218
  <div className="flex items-start justify-between gap-4">
219
219
  <div>
220
- <p className="text-sm text-gray-600">
220
+ <p className="text-muted-foreground text-sm">
221
221
  Edit the SEO defaults that flow into{' '}
222
- <code className="rounded bg-gray-100 px-1 py-0.5 text-[11px]">/sitemap.xml</code>,{' '}
223
- <code className="rounded bg-gray-100 px-1 py-0.5 text-[11px]">/robots.txt</code>,{' '}
224
- <code className="rounded bg-gray-100 px-1 py-0.5 text-[11px]">/og.png</code>, and the{' '}
225
- <code className="rounded bg-gray-100 px-1 py-0.5 text-[11px]">/resolve</code> meta +
222
+ <code className="bg-muted rounded px-1 py-0.5 text-[11px]">/sitemap.xml</code>,{' '}
223
+ <code className="bg-muted rounded px-1 py-0.5 text-[11px]">/robots.txt</code>,{' '}
224
+ <code className="bg-muted rounded px-1 py-0.5 text-[11px]">/og.png</code>, and the{' '}
225
+ <code className="bg-muted rounded px-1 py-0.5 text-[11px]">/resolve</code> meta +
226
226
  JSON-LD response. Empty fields fall back to{' '}
227
- <code className="rounded bg-gray-100 px-1 py-0.5 text-[11px]">actuate.config.ts</code>.
227
+ <code className="bg-muted rounded px-1 py-0.5 text-[11px]">actuate.config.ts</code>.
228
228
  </p>
229
- {updatedAt && <p className="mt-1 text-xs text-gray-500">Last saved: {updatedAt}</p>}
229
+ {updatedAt && (
230
+ <p className="text-muted-foreground mt-1 text-xs">Last saved: {updatedAt}</p>
231
+ )}
230
232
  </div>
231
233
  <button
232
234
  onClick={handleSave}
233
235
  disabled={saving}
234
- className="inline-flex shrink-0 items-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50"
236
+ className="bg-primary hover:bg-primary/90 inline-flex shrink-0 items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-white shadow-sm disabled:opacity-50"
235
237
  >
236
238
  {saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
237
239
  Save SEO defaults
@@ -239,10 +241,10 @@ export function SEOConfigPanel() {
239
241
  </div>
240
242
 
241
243
  {/* Site-wide section */}
242
- <section className="rounded-lg border border-gray-200 bg-white">
243
- <header className="flex items-center gap-2 border-b border-gray-200 px-4 py-3">
244
- <Globe className="h-4 w-4 text-blue-600" />
245
- <h3 className="text-sm font-medium text-gray-900">Site-wide defaults</h3>
244
+ <section className="border-border bg-card rounded-lg border">
245
+ <header className="border-border flex items-center gap-2 border-b px-4 py-3">
246
+ <Globe className="text-primary h-4 w-4" />
247
+ <h3 className="text-foreground text-sm font-medium">Site-wide defaults</h3>
246
248
  </header>
247
249
  <div className="grid grid-cols-1 gap-4 p-4 md:grid-cols-2">
248
250
  <Field
@@ -330,11 +332,11 @@ export function SEOConfigPanel() {
330
332
  </section>
331
333
 
332
334
  {/* Per-collection section */}
333
- <section className="rounded-lg border border-gray-200 bg-white">
334
- <header className="flex items-center gap-2 border-b border-gray-200 px-4 py-3">
335
- <FileText className="h-4 w-4 text-blue-600" />
336
- <h3 className="text-sm font-medium text-gray-900">Per-collection defaults</h3>
337
- <span className="text-xs text-gray-500">
335
+ <section className="border-border bg-card rounded-lg border">
336
+ <header className="border-border flex items-center gap-2 border-b px-4 py-3">
337
+ <FileText className="text-primary h-4 w-4" />
338
+ <h3 className="text-foreground text-sm font-medium">Per-collection defaults</h3>
339
+ <span className="text-muted-foreground text-xs">
338
340
  — shown placeholders are the static defaults
339
341
  </span>
340
342
  </header>
@@ -346,14 +348,14 @@ export function SEOConfigPanel() {
346
348
  <div key={col.slug} className="p-4">
347
349
  <div className="mb-3 flex items-baseline justify-between gap-4">
348
350
  <div>
349
- <h4 className="text-sm font-medium text-gray-900">{col.label}</h4>
350
- <p className="text-xs text-gray-500">
351
- <code className="rounded bg-gray-100 px-1 py-0.5">{col.slug}</code>
351
+ <h4 className="text-foreground text-sm font-medium">{col.label}</h4>
352
+ <p className="text-muted-foreground text-xs">
353
+ <code className="bg-muted rounded px-1 py-0.5">{col.slug}</code>
352
354
  {col.urlPrefix !== undefined && (
353
355
  <>
354
356
  {' '}
355
357
  prefix:{' '}
356
- <code className="rounded bg-gray-100 px-1 py-0.5">/{col.urlPrefix}</code>
358
+ <code className="bg-muted rounded px-1 py-0.5">/{col.urlPrefix}</code>
357
359
  </>
358
360
  )}{' '}
359
361
  — {col.type}
@@ -414,7 +416,7 @@ export function SEOConfigPanel() {
414
416
  )
415
417
  })}
416
418
  {data?.collections.length === 0 && (
417
- <div className="p-4 text-sm text-gray-500">
419
+ <div className="text-muted-foreground p-4 text-sm">
418
420
  No collections configured in <code>actuate.config.ts</code>.
419
421
  </div>
420
422
  )}
@@ -425,7 +427,7 @@ export function SEOConfigPanel() {
425
427
  <button
426
428
  onClick={handleSave}
427
429
  disabled={saving}
428
- className="inline-flex items-center gap-1.5 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50"
430
+ className="bg-primary hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-md px-4 py-2 text-sm font-medium text-white shadow-sm disabled:opacity-50"
429
431
  >
430
432
  {saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
431
433
  Save SEO defaults
@@ -465,15 +467,15 @@ function Field({
465
467
  }) {
466
468
  return (
467
469
  <div>
468
- <label className="mb-1 block text-xs font-medium text-gray-700">{label}</label>
470
+ <label className="text-foreground mb-1 block text-xs font-medium">{label}</label>
469
471
  <input
470
472
  type="text"
471
473
  value={value}
472
474
  placeholder={placeholder}
473
475
  onChange={(e) => onChange(e.target.value)}
474
- className="w-full rounded-md border border-gray-300 px-2.5 py-1.5 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
476
+ className="border-border text-foreground focus-visible:border-ring focus-visible:ring-ring/50 w-full rounded-md border px-2.5 py-1.5 text-sm placeholder-gray-400 focus:ring-1 focus:outline-none"
475
477
  />
476
- {hint && <p className="mt-1 text-[11px] text-gray-500">{hint}</p>}
478
+ {hint && <p className="text-muted-foreground mt-1 text-[11px]">{hint}</p>}
477
479
  </div>
478
480
  )
479
481
  }
@@ -499,7 +501,7 @@ function NumberField({
499
501
  }) {
500
502
  return (
501
503
  <div>
502
- <label className="mb-1 block text-xs font-medium text-gray-700">{label}</label>
504
+ <label className="text-foreground mb-1 block text-xs font-medium">{label}</label>
503
505
  <input
504
506
  type="number"
505
507
  value={value ?? ''}
@@ -513,9 +515,9 @@ function NumberField({
513
515
  const n = Number(raw)
514
516
  if (Number.isFinite(n)) onChange(n)
515
517
  }}
516
- className="w-full rounded-md border border-gray-300 px-2.5 py-1.5 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
518
+ className="border-border text-foreground focus-visible:border-ring focus-visible:ring-ring/50 w-full rounded-md border px-2.5 py-1.5 text-sm placeholder-gray-400 focus:ring-1 focus:outline-none"
517
519
  />
518
- {hint && <p className="mt-1 text-[11px] text-gray-500">{hint}</p>}
520
+ {hint && <p className="text-muted-foreground mt-1 text-[11px]">{hint}</p>}
519
521
  </div>
520
522
  )
521
523
  }
@@ -537,11 +539,11 @@ function Select({
537
539
  }) {
538
540
  return (
539
541
  <div>
540
- <label className="mb-1 block text-xs font-medium text-gray-700">{label}</label>
542
+ <label className="text-foreground mb-1 block text-xs font-medium">{label}</label>
541
543
  <select
542
544
  value={value}
543
545
  onChange={(e) => onChange(e.target.value)}
544
- className="w-full rounded-md border border-gray-300 bg-white px-2.5 py-1.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
546
+ className="border-border bg-card text-foreground focus-visible:border-ring focus-visible:ring-ring/50 w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-1 focus:outline-none"
545
547
  >
546
548
  {options.map((opt) => (
547
549
  <option key={opt} value={opt}>
@@ -549,7 +551,7 @@ function Select({
549
551
  </option>
550
552
  ))}
551
553
  </select>
552
- {hint && <p className="mt-1 text-[11px] text-gray-500">{hint}</p>}
554
+ {hint && <p className="text-muted-foreground mt-1 text-[11px]">{hint}</p>}
553
555
  </div>
554
556
  )
555
557
  }
@@ -566,16 +568,16 @@ function Toggle({
566
568
  hint?: string
567
569
  }) {
568
570
  return (
569
- <label className="flex cursor-pointer items-center gap-2 text-sm text-gray-700">
571
+ <label className="text-foreground flex cursor-pointer items-center gap-2 text-sm">
570
572
  <input
571
573
  type="checkbox"
572
574
  checked={checked}
573
575
  onChange={(e) => onChange(e.target.checked)}
574
- className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
576
+ className="border-border text-primary focus-visible:ring-ring/50 h-4 w-4 rounded"
575
577
  />
576
578
  <span>
577
579
  {label}
578
- {hint && <span className="ml-1 text-[11px] text-gray-500">— {hint}</span>}
580
+ {hint && <span className="text-muted-foreground ml-1 text-[11px]">— {hint}</span>}
579
581
  </span>
580
582
  </label>
581
583
  )
@@ -387,9 +387,9 @@ function computeOverallScore(checks: SEOCheck[]): number {
387
387
  }
388
388
 
389
389
  function StatusDot({ status }: { status: 'good' | 'ok' | 'bad' }) {
390
- if (status === 'good') return <CheckCircle2 className="h-4 w-4 shrink-0 text-green-500" />
391
- if (status === 'ok') return <AlertCircle className="h-4 w-4 shrink-0 text-amber-500" />
392
- return <XCircle className="h-4 w-4 shrink-0 text-red-500" />
390
+ if (status === 'good') return <CheckCircle2 className="text-success h-4 w-4 shrink-0" />
391
+ if (status === 'ok') return <AlertCircle className="text-warning h-4 w-4 shrink-0" />
392
+ return <XCircle className="text-destructive h-4 w-4 shrink-0" />
393
393
  }
394
394
 
395
395
  function ScoreRing({ score }: { score: number }) {
@@ -486,7 +486,7 @@ function ToggleSwitch({
486
486
  className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full transition-colors ${checked ? 'bg-[var(--primary)]' : 'bg-[var(--muted)]'}`}
487
487
  >
488
488
  <span
489
- className={`pointer-events-none mt-0.5 block h-5 w-5 rounded-full bg-white shadow-sm transition-transform ${checked ? 'translate-x-[22px]' : 'translate-x-0.5'}`}
489
+ className={`bg-card pointer-events-none mt-0.5 block h-5 w-5 rounded-full shadow-sm transition-transform ${checked ? 'translate-x-[22px]' : 'translate-x-0.5'}`}
490
490
  />
491
491
  </button>
492
492
  </div>
@@ -616,10 +616,10 @@ export function SEOPanel({
616
616
  : 'Very difficult'
617
617
  const fleschColor =
618
618
  readability.fleschScore >= 60
619
- ? 'text-green-500'
619
+ ? 'text-success'
620
620
  : readability.fleschScore >= 30
621
- ? 'text-amber-500'
622
- : 'text-red-500'
621
+ ? 'text-warning'
622
+ : 'text-destructive'
623
623
 
624
624
  return (
625
625
  <div className="rounded-lg border border-[var(--border)] bg-[var(--card)]">
@@ -643,13 +643,13 @@ export function SEOPanel({
643
643
  <ScoreRing score={score} />
644
644
  <div className="space-y-0.5 text-xs text-[var(--muted-foreground)]">
645
645
  <div className="flex items-center gap-1.5">
646
- <CheckCircle2 className="h-3 w-3 text-green-500" /> {goodCount} passed
646
+ <CheckCircle2 className="text-success h-3 w-3" /> {goodCount} passed
647
647
  </div>
648
648
  <div className="flex items-center gap-1.5">
649
- <AlertCircle className="h-3 w-3 text-amber-500" /> {okCount} improvements
649
+ <AlertCircle className="text-warning h-3 w-3" /> {okCount} improvements
650
650
  </div>
651
651
  <div className="flex items-center gap-1.5">
652
- <XCircle className="h-3 w-3 text-red-500" /> {badCount} issues
652
+ <XCircle className="text-destructive h-3 w-3" /> {badCount} issues
653
653
  </div>
654
654
  </div>
655
655
  </div>
@@ -723,8 +723,8 @@ export function SEOPanel({
723
723
  <span
724
724
  className={
725
725
  readability.passiveEstimate > 15
726
- ? 'font-medium text-amber-500'
727
- : 'font-medium text-green-500'
726
+ ? 'text-warning font-medium'
727
+ : 'text-success font-medium'
728
728
  }
729
729
  >
730
730
  {readability.passiveEstimate}%
@@ -849,10 +849,10 @@ export function SEOPanel({
849
849
  onToggle={toggleSection}
850
850
  >
851
851
  <div className="rounded-lg border border-[var(--border)] bg-[var(--background)] p-3">
852
- <div className="line-clamp-1 cursor-pointer text-sm text-blue-600 hover:underline">
852
+ <div className="text-primary line-clamp-1 cursor-pointer text-sm hover:underline">
853
853
  {metaTitle || title || 'Page Title'}
854
854
  </div>
855
- <div className="mt-1 truncate text-xs text-green-700">
855
+ <div className="text-success mt-1 truncate text-xs">
856
856
  {siteUrl}/{slug}
857
857
  </div>
858
858
  <div className="mt-1 line-clamp-2 text-sm text-[var(--muted-foreground)]">
@@ -971,7 +971,7 @@ export function SEOPanel({
971
971
  onChange={(v) => update({ isCornerstone: v })}
972
972
  />
973
973
  {seoData.isCornerstone && (
974
- <div className="flex items-center gap-1.5 rounded-md bg-amber-50 px-2.5 py-1.5 text-xs text-amber-600">
974
+ <div className="text-warning bg-warning/10 flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs">
975
975
  <Star className="h-3.5 w-3.5" />
976
976
  Cornerstone content is held to stricter SEO standards
977
977
  </div>
@@ -23,8 +23,8 @@ interface SEOSummary {
23
23
  }
24
24
 
25
25
  function ScoreBadge({ score }: { score: number }) {
26
- const color = score >= 80 ? 'text-green-600' : score >= 60 ? 'text-amber-500' : 'text-red-500'
27
- const bg = score >= 80 ? 'bg-green-50' : score >= 60 ? 'bg-amber-50' : 'bg-red-50'
26
+ const color = score >= 80 ? 'text-success' : score >= 60 ? 'text-warning' : 'text-destructive'
27
+ const bg = score >= 80 ? 'bg-success/10' : score >= 60 ? 'bg-warning/10' : 'bg-destructive/10'
28
28
  return (
29
29
  <span
30
30
  className={`inline-flex h-9 w-9 items-center justify-center rounded-full text-sm font-medium ${color} ${bg}`}
@@ -53,38 +53,40 @@ export function SEOPerformance({ onNavigate }: SEOPerformanceProps) {
53
53
  const visible = topContent.slice(page * perPage, (page + 1) * perPage)
54
54
 
55
55
  return (
56
- <div className="rounded-lg border border-gray-200 bg-white">
57
- <div className="flex items-center justify-between border-b border-gray-200 p-4">
56
+ <div className="border-border bg-card rounded-lg border">
57
+ <div className="border-border flex items-center justify-between border-b p-4">
58
58
  <div className="flex items-center gap-2">
59
- <Search className="h-4 w-4 text-gray-500" />
60
- <h2 className="text-sm font-medium text-gray-900">SEO Performance</h2>
59
+ <Search className="text-muted-foreground h-4 w-4" />
60
+ <h2 className="text-foreground text-sm font-medium">SEO Performance</h2>
61
61
  </div>
62
62
  {totalIssues > 0 && (
63
- <span className="text-xs text-gray-500">
63
+ <span className="text-muted-foreground text-xs">
64
64
  {totalIssues} issue{totalIssues !== 1 ? 's' : ''} found
65
65
  </span>
66
66
  )}
67
67
  </div>
68
68
 
69
- <div className="grid grid-cols-1 divide-y divide-gray-200 lg:grid-cols-12 lg:divide-x lg:divide-y-0">
69
+ <div className="divide-border grid grid-cols-1 divide-y lg:grid-cols-12 lg:divide-x lg:divide-y-0">
70
70
  <div className="p-4 lg:col-span-7">
71
- <h3 className="mb-3 text-sm font-medium text-gray-700">Top Performing Content</h3>
71
+ <h3 className="text-foreground mb-3 text-sm font-medium">Top Performing Content</h3>
72
72
  <div className="space-y-2">
73
73
  {visible.map((item) => (
74
74
  <div key={item.id} className="flex items-center justify-between py-2">
75
75
  <div className="mr-3 min-w-0 flex-1">
76
- <p className="truncate text-sm font-medium text-gray-900">{item.title}</p>
77
- <p className="text-xs text-gray-500 capitalize">{item.collection}</p>
76
+ <p className="text-foreground truncate text-sm font-medium">{item.title}</p>
77
+ <p className="text-muted-foreground text-xs capitalize">{item.collection}</p>
78
78
  </div>
79
79
  <ScoreBadge score={item.score} />
80
80
  </div>
81
81
  ))}
82
82
  {visible.length === 0 && (
83
- <p className="py-4 text-center text-sm text-gray-400">No published content yet</p>
83
+ <p className="text-muted-foreground py-4 text-center text-sm">
84
+ No published content yet
85
+ </p>
84
86
  )}
85
87
  </div>
86
88
  {topContent.length > perPage && (
87
- <div className="mt-3 flex items-center justify-between border-t border-gray-100 pt-3 text-xs text-gray-500">
89
+ <div className="text-muted-foreground mt-3 flex items-center justify-between border-t border-gray-100 pt-3 text-xs">
88
90
  <span>
89
91
  Showing {page * perPage + 1}-{Math.min((page + 1) * perPage, topContent.length)} of{' '}
90
92
  {topContent.length}
@@ -93,7 +95,7 @@ export function SEOPerformance({ onNavigate }: SEOPerformanceProps) {
93
95
  <button
94
96
  onClick={() => setPage(Math.max(0, page - 1))}
95
97
  disabled={page === 0}
96
- className="rounded p-1 hover:bg-gray-100 disabled:opacity-30"
98
+ className="hover:bg-accent rounded p-1 disabled:opacity-30"
97
99
  >
98
100
  <ChevronLeft className="h-3.5 w-3.5" />
99
101
  </button>
@@ -103,7 +105,7 @@ export function SEOPerformance({ onNavigate }: SEOPerformanceProps) {
103
105
  <button
104
106
  onClick={() => setPage(Math.min(totalPages - 1, page + 1))}
105
107
  disabled={page >= totalPages - 1}
106
- className="rounded p-1 hover:bg-gray-100 disabled:opacity-30"
108
+ className="hover:bg-accent rounded p-1 disabled:opacity-30"
107
109
  >
108
110
  <ChevronRight className="h-3.5 w-3.5" />
109
111
  </button>
@@ -113,38 +115,38 @@ export function SEOPerformance({ onNavigate }: SEOPerformanceProps) {
113
115
  </div>
114
116
 
115
117
  <div className="p-4 lg:col-span-5">
116
- <h3 className="mb-3 text-sm font-medium text-gray-700">SEO Issues</h3>
118
+ <h3 className="text-foreground mb-3 text-sm font-medium">SEO Issues</h3>
117
119
  <div className="space-y-3">
118
120
  <div className="flex items-center justify-between">
119
121
  <div className="flex items-center gap-2">
120
122
  <FileWarning className="h-4 w-4 text-red-400" />
121
- <span className="text-sm text-gray-700">Missing meta descriptions</span>
123
+ <span className="text-foreground text-sm">Missing meta descriptions</span>
122
124
  </div>
123
- <span className="text-sm font-medium text-gray-900">
125
+ <span className="text-foreground text-sm font-medium">
124
126
  {issues.missingMetaDescriptions}
125
127
  </span>
126
128
  </div>
127
129
  <div className="flex items-center justify-between">
128
130
  <div className="flex items-center gap-2">
129
131
  <LinkIcon className="h-4 w-4 text-red-400" />
130
- <span className="text-sm text-gray-700">Broken internal links</span>
132
+ <span className="text-foreground text-sm">Broken internal links</span>
131
133
  </div>
132
- <span className="text-sm font-medium text-gray-900">
134
+ <span className="text-foreground text-sm font-medium">
133
135
  {issues.brokenInternalLinks}
134
136
  </span>
135
137
  </div>
136
138
  <div className="flex items-center justify-between">
137
139
  <div className="flex items-center gap-2">
138
140
  <ImageOff className="h-4 w-4 text-red-400" />
139
- <span className="text-sm text-gray-700">Missing alt text</span>
141
+ <span className="text-foreground text-sm">Missing alt text</span>
140
142
  </div>
141
- <span className="text-sm font-medium text-gray-900">{issues.missingAltText}</span>
143
+ <span className="text-foreground text-sm font-medium">{issues.missingAltText}</span>
142
144
  </div>
143
145
  </div>
144
146
  {totalIssues > 0 && (
145
147
  <button
146
148
  onClick={() => onNavigate?.('/seo')}
147
- className="mt-4 text-sm font-medium text-blue-600 hover:text-blue-700"
149
+ className="text-primary hover:text-primary/80 mt-4 text-sm font-medium"
148
150
  >
149
151
  View All Issues
150
152
  </button>
@@ -233,8 +233,8 @@ function ToolbarButton({
233
233
  title={title}
234
234
  className={`rounded p-1.5 transition-colors ${
235
235
  isActive
236
- ? 'bg-blue-100 text-blue-700'
237
- : 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
236
+ ? 'bg-info/10 text-info'
237
+ : 'text-muted-foreground hover:bg-accent hover:text-foreground'
238
238
  } ${disabled ? 'cursor-not-allowed opacity-40' : ''}`}
239
239
  >
240
240
  {children}
@@ -243,7 +243,7 @@ function ToolbarButton({
243
243
  }
244
244
 
245
245
  function Divider() {
246
- return <div className="mx-1 h-6 w-px bg-gray-200" />
246
+ return <div className="bg-muted mx-1 h-6 w-px" />
247
247
  }
248
248
 
249
249
  function Toolbar({ editor, onOpenMediaPicker }: { editor: Editor; onOpenMediaPicker: () => void }) {
@@ -265,7 +265,7 @@ function Toolbar({ editor, onOpenMediaPicker }: { editor: Editor; onOpenMediaPic
265
265
  const isInTable = editor.isActive('table')
266
266
 
267
267
  return (
268
- <div className="sticky top-0 z-10 flex flex-wrap items-center gap-0.5 border-b border-gray-200 bg-gray-50 p-2">
268
+ <div className="border-border bg-muted sticky top-0 z-10 flex flex-wrap items-center gap-0.5 border-b p-2">
269
269
  {/* Group 1: Text formatting */}
270
270
  <ToolbarButton
271
271
  onClick={() => editor.chain().focus().toggleBold().run()}
@@ -544,7 +544,7 @@ export function TipTapEditor({
544
544
  UnderlineExt,
545
545
  Link.configure({
546
546
  openOnClick: false,
547
- HTMLAttributes: { class: 'text-blue-600 underline' },
547
+ HTMLAttributes: { class: 'text-primary underline' },
548
548
  }),
549
549
  Image.configure({
550
550
  inline: true,
@@ -656,10 +656,10 @@ export function TipTapEditor({
656
656
 
657
657
  const editorPane = (
658
658
  <div
659
- className={`flex-1 overflow-hidden rounded-lg border border-gray-200 bg-white ${className ?? ''}`}
659
+ className={`border-border bg-card flex-1 overflow-hidden rounded-lg border ${className ?? ''}`}
660
660
  >
661
661
  {collaboration && awareness && (
662
- <div className="flex items-center justify-between gap-2 border-b border-gray-100 bg-white px-2 py-1.5">
662
+ <div className="bg-card flex items-center justify-between gap-2 border-b border-gray-100 px-2 py-1.5">
663
663
  <PresenceChips awareness={awareness} status={collabStatus} />
664
664
  <OfflineStatus connection={collabStatus} offline={offlineStatus} />
665
665
  </div>
@@ -14,12 +14,12 @@ import { paletteFor } from '../posts/badges.js'
14
14
  */
15
15
 
16
16
  const DOT_COLOR: Record<string, string> = {
17
- purple: 'bg-violet-500',
18
- green: 'bg-emerald-500',
19
- orange: 'bg-amber-500',
20
- red: 'bg-red-500',
21
- blue: 'bg-blue-500',
22
- pink: 'bg-pink-500',
17
+ purple: 'bg-brand',
18
+ green: 'bg-success',
19
+ orange: 'bg-warning',
20
+ red: 'bg-destructive',
21
+ blue: 'bg-info',
22
+ pink: 'bg-chart-4',
23
23
  gray: 'bg-muted-foreground',
24
24
  }
25
25
 
@@ -5,53 +5,45 @@ import type { PostStatus } from '../../lib/posts-service.js'
5
5
 
6
6
  /**
7
7
  * Shared status / type / SEO indicators for the Posts admin area.
8
- *
9
- * Colors are restricted to the theme tokens already used elsewhere in
10
- * the admin (`success`, `warning`, `destructive`, `primary`, `muted`)
11
- * plus a small named-color palette (`purple`, `green`, `orange`, ...)
12
- * that maps to fixed Tailwind utilities. This keeps the badges working
13
- * in both light and dark mode and avoids leaking `bg-[#...]` arbitrary
14
- * values into components.
8
+ * Colors use semantic theme tokens only.
15
9
  */
16
10
 
17
- // ─── Named color palette ─────────────────────────────────────────────
18
-
19
11
  const PALETTE: Record<string, { bg: string; text: string; ring: string; border: string }> = {
20
12
  purple: {
21
- bg: 'bg-violet-50 dark:bg-violet-950/40',
22
- text: 'text-violet-700 dark:text-violet-300',
23
- ring: 'ring-violet-200 dark:ring-violet-900',
24
- border: 'border-violet-500',
13
+ bg: 'bg-brand/10',
14
+ text: 'text-brand',
15
+ ring: 'ring-brand/20',
16
+ border: 'border-brand',
25
17
  },
26
18
  green: {
27
- bg: 'bg-emerald-50 dark:bg-emerald-950/40',
28
- text: 'text-emerald-700 dark:text-emerald-300',
29
- ring: 'ring-emerald-200 dark:ring-emerald-900',
30
- border: 'border-emerald-500',
19
+ bg: 'bg-success/10',
20
+ text: 'text-success',
21
+ ring: 'ring-success/20',
22
+ border: 'border-success',
31
23
  },
32
24
  orange: {
33
- bg: 'bg-amber-50 dark:bg-amber-950/40',
34
- text: 'text-amber-700 dark:text-amber-300',
35
- ring: 'ring-amber-200 dark:ring-amber-900',
36
- border: 'border-amber-500',
25
+ bg: 'bg-warning/10',
26
+ text: 'text-warning',
27
+ ring: 'ring-warning/20',
28
+ border: 'border-warning',
37
29
  },
38
30
  red: {
39
- bg: 'bg-red-50 dark:bg-red-950/40',
40
- text: 'text-red-700 dark:text-red-300',
41
- ring: 'ring-red-200 dark:ring-red-900',
42
- border: 'border-red-500',
31
+ bg: 'bg-destructive/10',
32
+ text: 'text-destructive',
33
+ ring: 'ring-destructive/20',
34
+ border: 'border-destructive',
43
35
  },
44
36
  blue: {
45
- bg: 'bg-blue-50 dark:bg-blue-950/40',
46
- text: 'text-blue-700 dark:text-blue-300',
47
- ring: 'ring-blue-200 dark:ring-blue-900',
48
- border: 'border-blue-500',
37
+ bg: 'bg-info/10',
38
+ text: 'text-info',
39
+ ring: 'ring-info/20',
40
+ border: 'border-info',
49
41
  },
50
42
  pink: {
51
- bg: 'bg-pink-50 dark:bg-pink-950/40',
52
- text: 'text-pink-700 dark:text-pink-300',
53
- ring: 'ring-pink-200 dark:ring-pink-900',
54
- border: 'border-pink-500',
43
+ bg: 'bg-chart-4/15',
44
+ text: 'text-chart-4',
45
+ ring: 'ring-chart-4/20',
46
+ border: 'border-chart-4',
55
47
  },
56
48
  gray: {
57
49
  bg: 'bg-muted',
@@ -66,8 +58,6 @@ export function paletteFor(color: string | null | undefined) {
66
58
  return PALETTE[color] ?? PALETTE.gray!
67
59
  }
68
60
 
69
- // ─── PostStatusBadge ─────────────────────────────────────────────────
70
-
71
61
  const STATUS_LABEL: Record<PostStatus, string> = {
72
62
  PUBLISHED: 'Published',
73
63
  DRAFT: 'Draft',
@@ -104,8 +94,6 @@ export function PostStatusBadge({ status }: { status: PostStatus }) {
104
94
  )
105
95
  }
106
96
 
107
- // ─── PostTypeBadge ───────────────────────────────────────────────────
108
-
109
97
  export function PostTypeBadge({
110
98
  label,
111
99
  color,
@@ -127,12 +115,6 @@ export function PostTypeBadge({
127
115
  return <span className={className}>{label}</span>
128
116
  }
129
117
 
130
- // ─── SeoScoreIndicator ───────────────────────────────────────────────
131
-
132
- /**
133
- * Returns the palette key for an SEO score so callers can match the
134
- * colored dot. 80+ green, 60–79 orange, 1–59 red, missing = gray.
135
- */
136
118
  export function seoScorePalette(score: number | null): string {
137
119
  if (score === null || Number.isNaN(score)) return 'gray'
138
120
  if (score >= 80) return 'green'
@@ -158,10 +140,10 @@ export function SeoScoreIndicator({ score }: { score: number | null }) {
158
140
  aria-hidden
159
141
  className={`h-2 w-2 rounded-full ${
160
142
  colorKey === 'green'
161
- ? 'bg-emerald-500'
143
+ ? 'bg-success'
162
144
  : colorKey === 'orange'
163
- ? 'bg-amber-500'
164
- : 'bg-red-500'
145
+ ? 'bg-warning'
146
+ : 'bg-destructive'
165
147
  }`}
166
148
  />
167
149
  <span className="tabular-nums">{score}</span>
@@ -4,11 +4,11 @@ import { cv, type VariantProps } from '../../lib/cv.js'
4
4
  const badge = cv('inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium', {
5
5
  variants: {
6
6
  tone: {
7
- neutral: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300',
8
- success: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400',
9
- warning: 'bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400',
10
- danger: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400',
11
- info: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400',
7
+ neutral: 'bg-muted text-muted-foreground',
8
+ success: 'bg-success/10 text-success',
9
+ warning: 'bg-warning/10 text-warning',
10
+ danger: 'bg-destructive/10 text-destructive',
11
+ info: 'bg-info/10 text-info',
12
12
  primary: 'bg-[var(--primary)]/15 text-[var(--primary)] dark:bg-[var(--primary)]/25',
13
13
  },
14
14
  size: {