@toolr/ui-design 0.1.3 → 0.1.4

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 (44) hide show
  1. package/README.md +0 -7
  2. package/components/content/info-panel-primitives.tsx +3 -3
  3. package/components/lib/ai-tools.tsx +1 -1
  4. package/components/lib/theme-engine.ts +10 -0
  5. package/components/sections/captured-issues/captured-issues-panel.tsx +1 -1
  6. package/components/sections/golden-snapshots/file-diff-viewer.tsx +2 -2
  7. package/components/sections/golden-snapshots/snapshot-manager.tsx +3 -3
  8. package/components/sections/golden-snapshots/status-overview.tsx +4 -4
  9. package/components/sections/golden-snapshots/version-manager.tsx +3 -3
  10. package/components/sections/report-bug/screenshot-uploader.tsx +2 -2
  11. package/components/sections/snapshot-browser/snapshot-tree.tsx +1 -1
  12. package/components/sections/snippets-editor/snippets-editor.tsx +5 -5
  13. package/components/ui/action-dialog.tsx +1 -1
  14. package/components/ui/badge.tsx +4 -4
  15. package/components/ui/breadcrumb.tsx +1 -1
  16. package/components/ui/file-structure-section.tsx +4 -4
  17. package/components/ui/files-panel.tsx +5 -5
  18. package/components/ui/filter-dropdown.tsx +2 -2
  19. package/components/ui/frontmatter-form-header.tsx +2 -2
  20. package/components/ui/input.tsx +1 -1
  21. package/components/ui/label.tsx +4 -4
  22. package/components/ui/modal.tsx +1 -1
  23. package/components/ui/nav-card.tsx +1 -1
  24. package/components/ui/navigation-bar.tsx +1 -1
  25. package/components/ui/number-input.tsx +1 -1
  26. package/components/ui/registry-card.tsx +7 -7
  27. package/components/ui/registry-detail.tsx +2 -2
  28. package/components/ui/segmented-toggle.tsx +2 -2
  29. package/components/ui/select.tsx +2 -2
  30. package/components/ui/selection-grid.tsx +7 -19
  31. package/components/ui/snapshot-card.tsx +2 -2
  32. package/components/ui/snippets-panel.tsx +9 -9
  33. package/components/ui/tab-bar.tsx +1 -1
  34. package/dist/content.js +3 -3
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.js +78 -80
  37. package/dist/tokens/primitives.css +10 -0
  38. package/dist/tokens/semantic.css +3 -0
  39. package/package.json +1 -7
  40. package/tokens/primitives.css +10 -0
  41. package/tokens/semantic.css +3 -0
  42. package/dist/preset.d.ts +0 -24
  43. package/dist/preset.js +0 -17
  44. package/tailwind-preset.ts +0 -22
package/README.md CHANGED
@@ -14,19 +14,12 @@ Shared UI design system for toolr applications. Provides components, design toke
14
14
  @import "@toolr/ui-design/tokens";
15
15
  ```
16
16
 
17
- ```ts
18
- // tailwind.config.ts
19
- import { toolrPreset } from '@toolr/ui-design/preset'
20
- export default { presets: [toolrPreset] }
21
- ```
22
-
23
17
  ## Exports
24
18
 
25
19
  | Path | Content |
26
20
  |------|---------|
27
21
  | `@toolr/ui-design` | All components, hooks, types, utilities |
28
22
  | `@toolr/ui-design/tokens` | CSS design tokens (semantic + primitives + keyframes) |
29
- | `@toolr/ui-design/preset` | Tailwind preset (Inter font, extended breakpoints) |
30
23
  | `@toolr/ui-design/content` | Info panel primitives |
31
24
  | `@toolr/ui-design/diagrams` | Diagram utilities |
32
25
 
@@ -114,7 +114,7 @@ export function Callout({ color, children }: { color: string; children: ReactNod
114
114
  export function CalloutCode({ color, children }: { color: string; children: ReactNode }) {
115
115
  const c = CALLOUT_COLORS[color] ?? CALLOUT_COLORS.blue
116
116
  return (
117
- <code className={`block bg-neutral-800/80 px-2 py-1 rounded mt-1.5 text-[13px] ${c.codeText}`}>
117
+ <code className={`block bg-neutral-800/80 px-2 py-1 rounded mt-1.5 text-sm ${c.codeText}`}>
118
118
  {children}
119
119
  </code>
120
120
  )
@@ -204,7 +204,7 @@ export function TitledLI({ color, title, children }: { color: string; title: str
204
204
 
205
205
  export function CalloutDialog({ color, lines }: { color: string; lines: { speaker: string; text: string }[] }) {
206
206
  return (
207
- <div className="bg-neutral-800/80 rounded px-2 py-1 mt-1.5 flex flex-col gap-0.5 text-[13px]">
207
+ <div className="bg-neutral-800/80 rounded px-2 py-1 mt-1.5 flex flex-col gap-0.5 text-sm">
208
208
  {lines.map((line, idx) => (
209
209
  <div key={idx}>
210
210
  <span className={`text-${color}-300 font-semibold mr-1`}>{line.speaker}:</span>
@@ -238,7 +238,7 @@ export function StatusBadge({ value, badgeColor, label, children, even }: {
238
238
  <DLRow
239
239
  term={
240
240
  <span className="flex items-center gap-1.5">
241
- <span className={`inline-flex items-center justify-center w-5 h-5 rounded-full bg-${badgeColor}-500/20 text-${badgeColor}-400 text-[11px] font-bold shrink-0`}>{value}</span>
241
+ <span className={`inline-flex items-center justify-center w-5 h-5 rounded-full bg-${badgeColor}-500/20 text-${badgeColor}-400 text-xss font-bold shrink-0`}>{value}</span>
242
242
  <span className={`text-${badgeColor}-400 font-semibold`}>{label}</span>
243
243
  </span>
244
244
  }
@@ -38,7 +38,7 @@ export function AiToolIcon({ tool, size, showName, className, style }: {
38
38
  return (
39
39
  <span style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
40
40
  {img}
41
- <span className="text-[11px] text-neutral-400">{AI_TOOL_NAMES[tool as AiToolKey] ?? tool}</span>
41
+ <span className="text-xss text-neutral-400">{AI_TOOL_NAMES[tool as AiToolKey] ?? tool}</span>
42
42
  </span>
43
43
  )
44
44
  }
@@ -109,6 +109,16 @@ export function isLightTheme(themeId: ThemeId): boolean {
109
109
  return LIGHT_THEMES.includes(themeId)
110
110
  }
111
111
 
112
+ /**
113
+ * Apply a theme to the document by setting CSS custom properties on the root element.
114
+ *
115
+ * IMPORTANT — body background: Portal-based components (Select, Tooltip) render at
116
+ * document.body via createPortal. Their backgrounds use --popover which is semi-transparent
117
+ * (rgba(0,0,0,0.8)). If <body> has no background-color, the browser default (white) bleeds
118
+ * through, making dropdowns/tooltips appear gray instead of dark.
119
+ *
120
+ * Consuming apps MUST set: body { background-color: var(--background); }
121
+ */
112
122
  export function applyTheme(themeId: ThemeId, accentHue: number | null, dims: Record<SurfaceKey, number> = DEFAULT_DIMS, outline: number = DEFAULT_OUTLINE, root: HTMLElement = document.documentElement): void {
113
123
  const scale = generateScale(themeId, accentHue, BASE_THEMES[themeId].maxSat, dims, outline)
114
124
  const light = isLightTheme(themeId)
@@ -163,7 +163,7 @@ export function CapturedIssuesPanel({
163
163
  <Check className="w-5 h-5 text-green-400" />
164
164
  </div>
165
165
  <div>
166
- <h3 className="text-neutral-300 font-medium">No Issues Captured</h3>
166
+ <h3 className="text-sm text-neutral-300 font-medium">No Issues Captured</h3>
167
167
  <p className="text-sm text-neutral-500">Everything is running smoothly.</p>
168
168
  </div>
169
169
  </div>
@@ -93,7 +93,7 @@ function DiffFileItem({ file, isSelected, onSelect, onReset, resettingFile, anyR
93
93
  {icon}
94
94
  <span className="truncate">{filename}</span>
95
95
  {statusLabel && (
96
- <span className={`text-[10px] font-medium ml-auto flex-shrink-0 ${statusColor}`}>
96
+ <span className={`text-xss font-medium ml-auto flex-shrink-0 ${statusColor}`}>
97
97
  {statusLabel}
98
98
  </span>
99
99
  )}
@@ -171,7 +171,7 @@ function DiffFileTreePanel({ sync, componentLabels, renderFileIcon }: DiffFileTr
171
171
  const chevronSize = isRoot ? 'w-4 h-4' : 'w-3 h-3'
172
172
 
173
173
  const countElement = isRoot ? (
174
- <span className={`ml-auto px-1.5 py-0.5 rounded-full text-[10px] font-medium ${color.pillBg} ${color.text}`}>
174
+ <span className={`ml-auto px-1.5 py-0.5 rounded-full text-xss font-medium ${color.pillBg} ${color.text}`}>
175
175
  {fileCount}
176
176
  </span>
177
177
  ) : (
@@ -54,7 +54,7 @@ export function SnapshotManager({ sync }: SnapshotManagerProps) {
54
54
  <div className="bg-neutral-900 rounded-lg p-4 border border-neutral-700">
55
55
  <div className="flex items-center gap-3 mb-3">
56
56
  <Plus className="w-5 h-5 text-green-400" />
57
- <h4 className="text-neutral-300 font-medium">Create Snapshot</h4>
57
+ <h4 className="text-sm text-neutral-300 font-medium">Create Snapshot</h4>
58
58
  </div>
59
59
  <p className="text-xs text-neutral-600 mb-3">
60
60
  Archives the current live state. If components differ from golden, their patch version is auto-bumped.
@@ -85,7 +85,7 @@ export function SnapshotManager({ sync }: SnapshotManagerProps) {
85
85
  <div className="flex items-center justify-between px-4 py-3 border-b border-neutral-700">
86
86
  <div className="flex items-center gap-2">
87
87
  <Archive className="w-4 h-4 text-neutral-500" />
88
- <h4 className="text-neutral-300 font-medium">Snapshots</h4>
88
+ <h4 className="text-sm text-neutral-300 font-medium">Snapshots</h4>
89
89
  <span className="text-xs text-neutral-500">({manifest?.snapshots.length ?? 0})</span>
90
90
  </div>
91
91
  <IconButton
@@ -114,7 +114,7 @@ export function SnapshotManager({ sync }: SnapshotManagerProps) {
114
114
  <div className="flex items-center gap-2">
115
115
  <span className="text-sm font-mono text-neutral-300">v{snap.version}</span>
116
116
  {snap.version === manifest.activeVersion && (
117
- <span className="px-1.5 py-0.5 bg-green-500/20 text-green-400 text-[10px] rounded font-medium">
117
+ <span className="px-1.5 py-0.5 bg-green-500/20 text-green-400 text-xss rounded font-medium">
118
118
  active
119
119
  </span>
120
120
  )}
@@ -84,7 +84,7 @@ export function StatusOverview({
84
84
  <div className="bg-neutral-900 rounded-lg p-4 border border-amber-500/30">
85
85
  <div className="flex items-center gap-3 mb-3">
86
86
  <Archive className="w-5 h-5 text-amber-400" />
87
- <h3 className="text-neutral-300 font-medium">Bundled Seed (App Distribution)</h3>
87
+ <h3 className="text-sm text-neutral-300 font-medium">Bundled Seed (App Distribution)</h3>
88
88
  {status?.seed.meta && renderVersionBadge(status.seed.meta, 'bg-amber-500/20 text-amber-400')}
89
89
  </div>
90
90
  <div className="grid grid-cols-2 gap-4 text-sm">
@@ -121,7 +121,7 @@ export function StatusOverview({
121
121
  <div className="bg-neutral-900 rounded-lg p-4 border border-blue-500/30">
122
122
  <div className="flex items-center gap-2 mb-3">
123
123
  <div className="w-3 h-3 rounded-full bg-blue-400" />
124
- <h4 className="text-neutral-300 font-medium">Golden (Reference)</h4>
124
+ <h4 className="text-sm text-neutral-300 font-medium">Golden (Reference)</h4>
125
125
  {renderVersionBadge(status?.goldenMeta, 'bg-blue-500/20 text-blue-400')}
126
126
  </div>
127
127
  <div className="space-y-2 text-sm">
@@ -153,7 +153,7 @@ export function StatusOverview({
153
153
  <div className="bg-neutral-900 rounded-lg p-4 border border-green-500/30">
154
154
  <div className="flex items-center gap-2 mb-3">
155
155
  <div className="w-3 h-3 rounded-full bg-green-400" />
156
- <h4 className="text-neutral-300 font-medium">Live (Working Copy)</h4>
156
+ <h4 className="text-sm text-neutral-300 font-medium">Live (Working Copy)</h4>
157
157
  {renderVersionBadge(status?.liveMeta, 'bg-green-500/20 text-green-400')}
158
158
  <div className="ml-auto relative" ref={resetMenuRef}>
159
159
  <IconButton
@@ -290,7 +290,7 @@ export function StatusOverview({
290
290
  <div className="bg-neutral-900 rounded-lg p-4 border border-neutral-700">
291
291
  <div className="flex items-center gap-2 mb-3">
292
292
  <Archive className="w-4 h-4 text-neutral-500" />
293
- <h4 className="text-neutral-300 font-medium">Local Snapshots</h4>
293
+ <h4 className="text-sm text-neutral-300 font-medium">Local Snapshots</h4>
294
294
  </div>
295
295
  <div className="flex gap-6 text-sm">
296
296
  <div>
@@ -56,7 +56,7 @@ export function VersionManager({ sync, components, componentLabels }: VersionMan
56
56
  <div className="bg-neutral-900 rounded-lg p-4 border border-teal-500/30">
57
57
  <div className="flex items-center gap-3 mb-3">
58
58
  <Tag className="w-5 h-5 text-teal-400" />
59
- <h4 className="text-neutral-300 font-medium">Golden Version</h4>
59
+ <h4 className="text-sm text-neutral-300 font-medium">Golden Version</h4>
60
60
  {status?.goldenMeta && (
61
61
  <span className="px-2 py-0.5 bg-teal-500/20 text-teal-400 text-xs rounded font-mono">
62
62
  {status.goldenMeta.version}
@@ -100,7 +100,7 @@ export function VersionManager({ sync, components, componentLabels }: VersionMan
100
100
  {/* Component Versions */}
101
101
  <div className="bg-neutral-900 rounded-lg border border-neutral-700 overflow-hidden">
102
102
  <div className="px-4 py-3 border-b border-neutral-700">
103
- <h4 className="text-neutral-300 font-medium">Component Versions</h4>
103
+ <h4 className="text-sm text-neutral-300 font-medium">Component Versions</h4>
104
104
  <p className="text-xs text-neutral-600 mt-1">
105
105
  Update individual component versions. Click a component to edit.
106
106
  </p>
@@ -122,7 +122,7 @@ export function VersionManager({ sync, components, componentLabels }: VersionMan
122
122
  <span className="text-xs font-mono text-neutral-500">v{currentVersion}</span>
123
123
  )}
124
124
  {mismatch && (
125
- <span className="text-[10px] text-yellow-400">
125
+ <span className="text-xss text-yellow-400">
126
126
  (live: v{liveVersion})
127
127
  </span>
128
128
  )}
@@ -195,7 +195,7 @@ export function ScreenshotUploader({
195
195
  alt={s.filename}
196
196
  className="w-full h-full object-cover"
197
197
  />
198
- <div className="absolute inset-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-between p-2">
198
+ <div className="absolute inset-0 bg-[var(--background)]/60 opacity-0 group-hover:opacity-100 transition-opacity flex flex-col justify-between p-2">
199
199
  <button
200
200
  type="button"
201
201
  onClick={(e) => {
@@ -209,7 +209,7 @@ export function ScreenshotUploader({
209
209
  </button>
210
210
  <span className="text-xs text-white truncate">{s.filename}</span>
211
211
  </div>
212
- <div className="absolute bottom-1 right-1 px-1.5 py-0.5 bg-black/70 rounded text-xs text-neutral-400">
212
+ <div className="absolute bottom-1 right-1 px-1.5 py-0.5 bg-[var(--background)]/70 rounded text-xs text-neutral-400">
213
213
  {formatFileSize(s.size)}
214
214
  </div>
215
215
  </div>
@@ -201,7 +201,7 @@ function SnapshotEntryRow({
201
201
  <span className="text-xs flex-1 truncate">
202
202
  {searchQuery ? highlightMatch(displayName, searchQuery) : displayName}
203
203
  </span>
204
- <span className="text-[10px] text-neutral-500 shrink-0" title={formatFullDate(entry.savedAt)}>
204
+ <span className="text-xss text-neutral-500 shrink-0" title={formatFullDate(entry.savedAt)}>
205
205
  {formatRelativeTime(entry.savedAt)}
206
206
  </span>
207
207
  <IconButton
@@ -144,7 +144,7 @@ export function SnippetsEditor({
144
144
  <p className="text-xs text-neutral-500 mb-1">
145
145
  {searchQuery ? 'No matching snippets' : 'No snippets defined'}
146
146
  </p>
147
- <p className="text-[10px] text-neutral-600">
147
+ <p className="text-xss text-neutral-600">
148
148
  {searchQuery ? 'Try a different search term' : 'Click + to add your first snippet'}
149
149
  </p>
150
150
  </div>
@@ -228,11 +228,11 @@ function SnippetListItem({ snippet, selected, onSelect, onDelete }: SnippetListI
228
228
  <p className="text-xs font-mono font-medium text-neutral-300 truncate">
229
229
  {snippet.name}
230
230
  </p>
231
- <p className="text-[10px] text-neutral-500 truncate mt-0.5">
231
+ <p className="text-xss text-neutral-500 truncate mt-0.5">
232
232
  {snippet.description}
233
233
  </p>
234
234
  {snippet.value && (
235
- <p className="text-[10px] text-neutral-600 truncate mt-0.5 font-mono">
235
+ <p className="text-xss text-neutral-600 truncate mt-0.5 font-mono">
236
236
  {snippet.value.slice(0, 80)}{snippet.value.length > 80 ? '...' : ''}
237
237
  </p>
238
238
  )}
@@ -292,7 +292,7 @@ function SnippetForm({
292
292
  error={nameHasError}
293
293
  autoFocus={!isEditing}
294
294
  />
295
- <p className="mt-1 text-[10px] text-neutral-600">
295
+ <p className="mt-1 text-xss text-neutral-600">
296
296
  Use in prompts as <span className="font-mono text-purple-400">{'{{' + (formData.name || 'NAME') + '}}'}</span>
297
297
  </p>
298
298
  </div>
@@ -319,7 +319,7 @@ function SnippetForm({
319
319
  onChange={(val) => setFormField('value', val)}
320
320
  minHeight={160}
321
321
  />
322
- <p className="mt-1 text-[10px] text-neutral-600">
322
+ <p className="mt-1 text-xss text-neutral-600">
323
323
  Can be a single value, multi-line text, or an entire document
324
324
  </p>
325
325
  </div>
@@ -194,7 +194,7 @@ export function ActionDialog({
194
194
 
195
195
  return createPortal(
196
196
  <div className="fixed inset-0 z-50 flex items-center justify-center">
197
- <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onCancel} />
197
+ <div className="absolute inset-0 bg-[var(--dialog-backdrop)] backdrop-blur-sm" onClick={onCancel} />
198
198
  <div
199
199
  className={cn(
200
200
  'relative bg-neutral-950 border border-neutral-700 rounded-xl shadow-2xl w-full max-w-[800px] mx-4 flex flex-col',
@@ -42,10 +42,10 @@ const colorClasses: Record<BadgeColor, string> = {
42
42
  }
43
43
 
44
44
  const sizeClasses = {
45
- xss: 'min-w-[14px] h-[14px] px-0.5 text-[9px]',
46
- xs: 'min-w-[16px] h-[16px] px-1 text-[10px]',
47
- sm: 'min-w-[18px] h-[18px] px-1 text-[10px]',
48
- md: 'min-w-[20px] h-[20px] px-1.5 text-[11px]',
45
+ xss: 'min-w-[14px] h-[14px] px-0.5 text-xss',
46
+ xs: 'min-w-[16px] h-[16px] px-1 text-xss',
47
+ sm: 'min-w-[18px] h-[18px] px-1 text-xss',
48
+ md: 'min-w-[20px] h-[20px] px-1.5 text-xss',
49
49
  lg: 'min-w-[22px] h-[22px] px-1.5 text-xs',
50
50
  }
51
51
 
@@ -66,7 +66,7 @@ export interface BreadcrumbProps {
66
66
  }
67
67
 
68
68
  const sizeConfig = {
69
- xss: { text: 'text-[10px]', icon: 'w-2.5 h-2.5', px: 'px-1', py: 'py-0.5', gap: 'gap-0.5', sep: 'w-2 h-2' },
69
+ xss: { text: 'text-xss', icon: 'w-2.5 h-2.5', px: 'px-1', py: 'py-0.5', gap: 'gap-0.5', sep: 'w-2 h-2' },
70
70
  xs: { text: 'text-xs', icon: 'w-3 h-3', px: 'px-1.5', py: 'py-0.5', gap: 'gap-1', sep: 'w-2.5 h-2.5' },
71
71
  sm: { text: 'text-sm', icon: 'w-3.5 h-3.5', px: 'px-2', py: 'py-1', gap: 'gap-1.5', sep: 'w-3 h-3' },
72
72
  md: { text: 'text-base', icon: 'w-4 h-4', px: 'px-2.5', py: 'py-1', gap: 'gap-1.5', sep: 'w-3.5 h-3.5' },
@@ -89,7 +89,7 @@ function renderMarkdownContent(content: string) {
89
89
  while (i < lines.length && lines[i] !== '---') { fmLines.push(lines[i]); i++ }
90
90
  i++ // skip closing ---
91
91
  nodes.push(
92
- <div key="fm" className="mb-3 font-mono text-[11px] text-neutral-500 border-l-2 border-neutral-700 pl-2 py-0.5">
92
+ <div key="fm" className="mb-3 font-mono text-xss text-neutral-500 border-l-2 border-neutral-700 pl-2 py-0.5">
93
93
  <div className="text-neutral-600">---</div>
94
94
  {fmLines.map((l, j) => <div key={j}>{l}</div>)}
95
95
  <div className="text-neutral-600">---</div>
@@ -104,12 +104,12 @@ function renderMarkdownContent(content: string) {
104
104
  i++
105
105
  while (i < lines.length && !lines[i].startsWith('```')) { codeLines.push(lines[i]); i++ }
106
106
  nodes.push(
107
- <pre key={i} className="mb-2 p-2 bg-black/30 rounded text-[11px] font-mono text-neutral-300 overflow-x-auto">
107
+ <pre key={i} className="mb-2 p-2 bg-[var(--background)]/30 rounded text-xss font-mono text-neutral-300 overflow-x-auto">
108
108
  {codeLines.join('\n')}
109
109
  </pre>
110
110
  )
111
111
  } else if (line.startsWith('### ')) {
112
- nodes.push(<h3 key={i} className="text-[11px] font-semibold text-neutral-300 mt-2 mb-0.5">{line.slice(4)}</h3>)
112
+ nodes.push(<h3 key={i} className="text-xss font-semibold text-neutral-300 mt-2 mb-0.5">{line.slice(4)}</h3>)
113
113
  } else if (line.startsWith('## ')) {
114
114
  nodes.push(<h2 key={i} className="text-xs font-semibold text-neutral-200 mt-2.5 mb-1">{line.slice(3)}</h2>)
115
115
  } else if (line.startsWith('# ')) {
@@ -117,7 +117,7 @@ function renderMarkdownContent(content: string) {
117
117
  } else if (line === '' || line === '\r') {
118
118
  nodes.push(<div key={i} className="h-1.5" />)
119
119
  } else {
120
- nodes.push(<p key={i} className="text-[11px] text-neutral-400 leading-relaxed">{line}</p>)
120
+ nodes.push(<p key={i} className="text-xss text-neutral-400 leading-relaxed">{line}</p>)
121
121
  }
122
122
  i++
123
123
  }
@@ -144,7 +144,7 @@ function FileNode({ entry, depth, selectedPath, expandedPaths, onToggleExpand, o
144
144
  />
145
145
  <span className="truncate">{entry.name}</span>
146
146
  {entry.badge && (
147
- <span className="ml-auto shrink-0 px-1.5 py-0.5 text-[9px] rounded bg-neutral-700 text-neutral-500">
147
+ <span className="ml-auto shrink-0 px-1.5 py-0.5 text-xss rounded bg-neutral-700 text-neutral-500">
148
148
  {entry.badge}
149
149
  </span>
150
150
  )}
@@ -210,12 +210,12 @@ export function FilesPanel({
210
210
  return (
211
211
  <div className={cn('flex flex-col bg-neutral-800 rounded-lg overflow-hidden', className)}>
212
212
  <div className="flex items-center justify-between px-3 py-2 border-b border-neutral-700">
213
- <span className="text-[11px] font-semibold uppercase tracking-wider text-neutral-500">Files</span>
214
- <span className="text-[10px] text-neutral-500">{fileCount} files</span>
213
+ <span className="text-xss font-semibold uppercase tracking-wider text-neutral-500">Files</span>
214
+ <span className="text-xss text-neutral-500">{fileCount} files</span>
215
215
  </div>
216
216
  {showSearch && (
217
217
  <div className="px-2 py-2 border-b border-neutral-700">
218
- <div className="flex items-center gap-1.5 px-2 py-1 bg-black border border-neutral-700 rounded text-xs">
218
+ <div className="flex items-center gap-1.5 px-2 py-1 bg-[var(--background)] border border-neutral-700 rounded text-xs">
219
219
  <Search className="w-3 h-3 text-neutral-500 shrink-0" />
220
220
  <input
221
221
  type="text"
@@ -243,7 +243,7 @@ export function FilesPanel({
243
243
  ))}
244
244
  </ul>
245
245
  {displayedFiles.length === 0 && (
246
- <p className="text-[11px] text-neutral-500 text-center py-4">No files found</p>
246
+ <p className="text-xss text-neutral-500 text-center py-4">No files found</p>
247
247
  )}
248
248
  </div>
249
249
  </div>
@@ -135,7 +135,7 @@ export function FilterDropdown({
135
135
  onClick={() => handleSelect('all')}
136
136
  className={`w-full flex items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors cursor-pointer ${
137
137
  highlightIdx === 0
138
- ? 'bg-neutral-600 text-neutral-200'
138
+ ? `${FORM_COLORS[color].selectedBg} text-neutral-200`
139
139
  : !isActive ? `${FORM_COLORS[color].selectedBg} text-neutral-200` : `text-neutral-400 ${v.hoverBg}`
140
140
  }`}
141
141
  >
@@ -154,7 +154,7 @@ export function FilterDropdown({
154
154
  onClick={() => handleSelect(opt.value)}
155
155
  className={`w-full flex items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors cursor-pointer ${
156
156
  isHighlighted
157
- ? 'bg-neutral-600 text-neutral-200'
157
+ ? `${FORM_COLORS[color].selectedBg} text-neutral-200`
158
158
  : isSelected ? `${FORM_COLORS[color].selectedBg} text-neutral-200` : `text-neutral-400 ${v.hoverBg}`
159
159
  }`}
160
160
  >
@@ -43,12 +43,12 @@ export function FrontmatterFormHeader({
43
43
  Configuration
44
44
  </span>
45
45
  {collapsed && hasFm && (
46
- <span className="text-[11px] text-neutral-500 font-mono ml-2 truncate">
46
+ <span className="text-xss text-neutral-500 font-mono ml-2 truncate">
47
47
  {renderSummary()}
48
48
  </span>
49
49
  )}
50
50
  {collapsed && !hasFm && (
51
- <span className="text-[11px] text-neutral-600 ml-2">No frontmatter</span>
51
+ <span className="text-xss text-neutral-600 ml-2">No frontmatter</span>
52
52
  )}
53
53
  </button>
54
54
 
@@ -38,7 +38,7 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
38
38
  }
39
39
 
40
40
  const sizeClasses = {
41
- xss: 'px-1 py-0.5 text-[10px]',
41
+ xss: 'px-1 py-0.5 text-xss',
42
42
  xs: 'px-1.5 py-0.5 text-xs',
43
43
  sm: 'px-2 py-1 text-xs',
44
44
  md: 'px-3 py-1.5 text-sm',
@@ -86,10 +86,10 @@ const progressFillColors: Record<LabelColor, string> = {
86
86
  }
87
87
 
88
88
  const sizeConfig = {
89
- xss: { height: 14, padding: 'px-1', text: 'text-[9px]', iconSize: 'w-2 h-2', gap: 'gap-0.5' },
90
- xs: { height: 16, padding: 'px-1.5', text: 'text-[10px]', iconSize: 'w-2.5 h-2.5', gap: 'gap-1' },
91
- sm: { height: 18, padding: 'px-1.5', text: 'text-[10px]', iconSize: 'w-2.5 h-2.5', gap: 'gap-1.5' },
92
- md: { height: 20, padding: 'px-1.5', text: 'text-[11px]', iconSize: 'w-3 h-3', gap: 'gap-1' },
89
+ xss: { height: 14, padding: 'px-1', text: 'text-xss', iconSize: 'w-2 h-2', gap: 'gap-0.5' },
90
+ xs: { height: 16, padding: 'px-1.5', text: 'text-xss', iconSize: 'w-2.5 h-2.5', gap: 'gap-1' },
91
+ sm: { height: 18, padding: 'px-1.5', text: 'text-xss', iconSize: 'w-2.5 h-2.5', gap: 'gap-1.5' },
92
+ md: { height: 20, padding: 'px-1.5', text: 'text-xss', iconSize: 'w-3 h-3', gap: 'gap-1' },
93
93
  lg: { height: 22, padding: 'px-2', text: 'text-xs', iconSize: 'w-3 h-3', gap: 'gap-1' },
94
94
  }
95
95
 
@@ -61,7 +61,7 @@ function Modal({ isOpen, onClose, title, children, kind = 'info', size = 'md', h
61
61
 
62
62
  return createPortal(
63
63
  <div className="fixed inset-0 z-50 flex items-center justify-center">
64
- <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onClose} />
64
+ <div className="absolute inset-0 bg-[var(--dialog-backdrop)] backdrop-blur-sm" onClick={onClose} />
65
65
  <div
66
66
  ref={modalRef}
67
67
  data-testid={testId}
@@ -51,7 +51,7 @@ export function NavCard({
51
51
  disabled={disabled}
52
52
  className={cn(
53
53
  'relative w-full text-left rounded-lg border border-neutral-700 bg-neutral-800 p-4 transition-all duration-200 cursor-pointer',
54
- !disabled && 'hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/20 hover:border-neutral-600 hover:bg-neutral-700',
54
+ !disabled && 'hover:-translate-y-0.5 hover:border-neutral-600 hover:bg-neutral-700',
55
55
  disabled && 'opacity-50 cursor-not-allowed',
56
56
  className,
57
57
  )}
@@ -69,7 +69,7 @@ export interface NavigationBarProps {
69
69
  }
70
70
 
71
71
  const sizeConfig = {
72
- xss: { text: 'text-[10px]', segIcon: 'w-2.5 h-2.5', navIcon: 'w-2.5 h-2.5', navBtn: 'w-[18px] h-[18px] rounded-[3px]', px: 'px-1', py: 'py-0.5', sep: 'w-2 h-2', divH: 'h-3' },
72
+ xss: { text: 'text-xss', segIcon: 'w-2.5 h-2.5', navIcon: 'w-2.5 h-2.5', navBtn: 'w-[18px] h-[18px] rounded-[3px]', px: 'px-1', py: 'py-0.5', sep: 'w-2 h-2', divH: 'h-3' },
73
73
  xs: { text: 'text-xs', segIcon: 'w-3 h-3', navIcon: 'w-3 h-3', navBtn: 'w-6 h-6 rounded-[5px]', px: 'px-1.5', py: 'py-0.5', sep: 'w-2.5 h-2.5', divH: 'h-3.5' },
74
74
  sm: { text: 'text-sm', segIcon: 'w-3.5 h-3.5', navIcon: 'w-3.5 h-3.5', navBtn: 'w-7 h-7 rounded-md', px: 'px-2', py: 'py-1', sep: 'w-3 h-3', divH: 'h-4' },
75
75
  md: { text: 'text-base', segIcon: 'w-4 h-4', navIcon: 'w-4 h-4', navBtn: 'w-8 h-8 rounded-md', px: 'px-2.5', py: 'py-1', sep: 'w-3.5 h-3.5', divH: 'h-5' },
@@ -16,7 +16,7 @@ export interface NumberInputProps {
16
16
  }
17
17
 
18
18
  const SIZE_CONFIG = {
19
- xss: { wrapper: 'h-[18px]', input: 'px-1 text-[10px]', chevron: 'w-2.5 h-2.5', stepperW: 'w-4' },
19
+ xss: { wrapper: 'h-[18px]', input: 'px-1 text-xss', chevron: 'w-2.5 h-2.5', stepperW: 'w-4' },
20
20
  xs: { wrapper: 'h-6', input: 'px-1.5 text-xs', chevron: 'w-2.5 h-2.5', stepperW: 'w-5' },
21
21
  sm: { wrapper: 'h-7', input: 'px-2 text-xs', chevron: 'w-3 h-3', stepperW: 'w-5' },
22
22
  md: { wrapper: 'h-8', input: 'px-3 text-sm', chevron: 'w-3 h-3', stepperW: 'w-6' },
@@ -412,7 +412,7 @@ export function RegistryCard(props: RegistryCardProps) {
412
412
  <Tooltip key={`pkg-${key}`} content={{ description: `${count} ${count === 1 ? label : labelPlural}` }} position="top">
413
413
  <span className="flex items-center gap-0.5">
414
414
  <Icon className={`w-3 h-3 ${color}`} />
415
- <span className="text-[10px] text-neutral-500">{count}</span>
415
+ <span className="text-xss text-neutral-500">{count}</span>
416
416
  </span>
417
417
  </Tooltip>
418
418
  )]
@@ -469,7 +469,7 @@ export function RegistryCard(props: RegistryCardProps) {
469
469
  ...(props.stars != null && props.stars > 0 ? [(
470
470
  <CardClickable key="stars" onClick={() => props.onSortBy?.('stars')}>
471
471
  <Tooltip content={{ description: `${props.stars.toLocaleString()} stars \u00b7 Click to sort` }} position="top">
472
- <span className="flex items-center gap-1 text-[10px] text-amber-400/80">
472
+ <span className="flex items-center gap-1 text-xss text-amber-400/80">
473
473
  <Star className="w-3 h-3" />
474
474
  {formatCount(props.stars)}
475
475
  </span>
@@ -479,7 +479,7 @@ export function RegistryCard(props: RegistryCardProps) {
479
479
  ...(props.downloads != null && props.downloads > 0 ? [(
480
480
  <CardClickable key="downloads" onClick={() => props.onSortBy?.('downloads')}>
481
481
  <Tooltip content={{ description: `${props.downloads.toLocaleString()} downloads \u00b7 Click to sort` }} position="top">
482
- <span className="flex items-center gap-1 text-[10px] text-emerald-400/80">
482
+ <span className="flex items-center gap-1 text-xss text-emerald-400/80">
483
483
  <Download className="w-3 h-3" />
484
484
  {formatCount(props.downloads)}
485
485
  </span>
@@ -581,7 +581,7 @@ export function RegistryCard(props: RegistryCardProps) {
581
581
  if (props.installs != null && props.installs > 0) {
582
582
  bottomStats = [(
583
583
  <Tooltip key="installs" content={{ description: `${props.installs.toLocaleString()} installs` }} position="top">
584
- <span className="flex items-center gap-1 text-[10px] text-neutral-500">
584
+ <span className="flex items-center gap-1 text-xss text-neutral-500">
585
585
  <Download className="w-3 h-3" />
586
586
  {props.installs.toLocaleString()}
587
587
  </span>
@@ -642,7 +642,7 @@ export function RegistryCard(props: RegistryCardProps) {
642
642
 
643
643
  {/* Error/warning message */}
644
644
  {errorMessage && (
645
- <p className={`text-[11px] mb-2 break-all ${flash === 'warning' ? 'text-amber-400' : 'text-red-400'}`}>{errorMessage}</p>
645
+ <p className={`text-xss mb-2 break-all ${flash === 'warning' ? 'text-amber-400' : 'text-red-400'}`}>{errorMessage}</p>
646
646
  )}
647
647
 
648
648
  {/* Bottom row */}
@@ -650,7 +650,7 @@ export function RegistryCard(props: RegistryCardProps) {
650
650
  const dateNode = updatedAt ? (
651
651
  <Tooltip content={{ description: onDateClick ? `Last updated ${formatFullDate(updatedAt)} \u00b7 Click to sort by date` : `Last updated ${formatFullDate(updatedAt)}` }} position="top">
652
652
  <span
653
- className={`flex items-center gap-1 text-[10px] text-neutral-500 whitespace-nowrap${onDateClick ? ' cursor-pointer hover:brightness-125 transition-all' : ''}`}
653
+ className={`flex items-center gap-1 text-xss text-neutral-500 whitespace-nowrap${onDateClick ? ' cursor-pointer hover:brightness-125 transition-all' : ''}`}
654
654
  onClick={onDateClick ? (e: MouseEvent) => { e.stopPropagation(); onDateClick() } : undefined}
655
655
  >
656
656
  <Clock className="w-3 h-3" />
@@ -690,7 +690,7 @@ export function RegistryCard(props: RegistryCardProps) {
690
690
  // Basic fallback: just install on confirm
691
691
  return (
692
692
  <div onClick={(e) => e.stopPropagation()}>
693
- <div className="fixed inset-0 bg-black/50 z-50 flex items-center justify-center" onClick={() => setShowScopeConfirm(false)}>
693
+ <div className="fixed inset-0 bg-[var(--background)]/50 z-50 flex items-center justify-center" onClick={() => setShowScopeConfirm(false)}>
694
694
  <div className="bg-neutral-800 border border-neutral-700 rounded-lg p-4 max-w-sm" onClick={(e) => e.stopPropagation()}>
695
695
  <h3 className="text-sm font-medium text-neutral-200 mb-2">{ALREADY_AT_USER}</h3>
696
696
  <p className="text-xs text-neutral-400 mb-4">
@@ -122,7 +122,7 @@ function CompatibleWithSection({ tools }: { tools: string[] }) {
122
122
  {tools.map((tool) => (
123
123
  <div key={tool} className="flex flex-col items-center gap-1">
124
124
  <AiToolIcon tool={tool} size={18} />
125
- <span className="text-[10px] text-neutral-400">{AI_TOOL_NAMES[tool as AiToolKey] ?? tool}</span>
125
+ <span className="text-xss text-neutral-400">{AI_TOOL_NAMES[tool as AiToolKey] ?? tool}</span>
126
126
  </div>
127
127
  ))}
128
128
  </div>
@@ -162,7 +162,7 @@ export function RegistryDetail({
162
162
  <div className="min-w-0">
163
163
  <div className="flex items-center gap-3 flex-wrap">
164
164
  <Icon className={`w-6 h-6 shrink-0 ${iconColor}`} />
165
- <h2 className="text-xl font-semibold text-neutral-200">{title}</h2>
165
+ <h2 className="text-lg font-semibold text-neutral-200">{title}</h2>
166
166
  {labels && labels.length > 0 && labels.map((labelProps, i) => (
167
167
  <Label key={i} size="lg" {...labelProps} />
168
168
  ))}
@@ -34,8 +34,8 @@ const ICON_SIZE_CLASSES = {
34
34
 
35
35
  /** Text label button sizes — horizontal padding instead of fixed width */
36
36
  const TEXT_SIZE_CLASSES = {
37
- xss: 'h-[18px] px-1.5 text-[9px]',
38
- xs: 'h-6 px-2 text-[10px]',
37
+ xss: 'h-[18px] px-1.5 text-xss',
38
+ xs: 'h-6 px-2 text-xss',
39
39
  sm: 'h-7 px-2.5 text-xs',
40
40
  md: 'h-8 px-3 text-xs',
41
41
  lg: 'h-9 px-3.5 text-sm',
@@ -29,7 +29,7 @@ const VARIANT_CLASSES = {
29
29
  }
30
30
 
31
31
  const SIZE_CLASSES = {
32
- xss: 'h-[18px] px-1.5 text-[10px]',
32
+ xss: 'h-[18px] px-1.5 text-xss',
33
33
  xs: 'h-6 px-2 text-xs',
34
34
  sm: 'h-7 px-2 text-xs',
35
35
  md: 'h-8 px-3 text-sm',
@@ -154,7 +154,7 @@ export function Select<T extends string | number = string>({
154
154
  onPointerEnter={() => setHighlightIdx(idx)}
155
155
  className={`w-full flex items-center gap-2 px-3 py-1.5 text-xs text-left transition-colors cursor-pointer ${
156
156
  isHighlighted
157
- ? 'bg-neutral-600 text-neutral-200'
157
+ ? `${FORM_COLORS[color].selectedBg} text-neutral-200`
158
158
  : isSelected ? `${FORM_COLORS[color].selectedBg} text-neutral-200` : `text-neutral-400 ${v.hoverBg}`
159
159
  }`}
160
160
  >