radtools 0.1.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 (133) hide show
  1. package/README.md +108 -0
  2. package/bin/radtools.js +5 -0
  3. package/dist/cli/index.js +427 -0
  4. package/package.json +55 -0
  5. package/templates/api-routes/assets/optimize/route.ts +94 -0
  6. package/templates/api-routes/assets/route.ts +159 -0
  7. package/templates/api-routes/components/create-folder/route.ts +55 -0
  8. package/templates/api-routes/components/route.ts +156 -0
  9. package/templates/api-routes/fonts/route.ts +96 -0
  10. package/templates/api-routes/fonts/upload/route.ts +79 -0
  11. package/templates/api-routes/read-css/route.ts +29 -0
  12. package/templates/api-routes/write-css/route.ts +423 -0
  13. package/templates/components/Rad_os/AppWindow.tsx +423 -0
  14. package/templates/components/Rad_os/MobileAppModal.tsx +76 -0
  15. package/templates/components/Rad_os/WindowTitleBar.tsx +290 -0
  16. package/templates/components/icons/Icon.tsx +224 -0
  17. package/templates/components/icons/README.md +85 -0
  18. package/templates/components/icons/index.ts +20 -0
  19. package/templates/components/icons.tsx +164 -0
  20. package/templates/components/ui/Accordion.tsx +268 -0
  21. package/templates/components/ui/Alert.tsx +111 -0
  22. package/templates/components/ui/Badge.tsx +87 -0
  23. package/templates/components/ui/Breadcrumbs.tsx +88 -0
  24. package/templates/components/ui/Button.tsx +249 -0
  25. package/templates/components/ui/Card.tsx +137 -0
  26. package/templates/components/ui/Checkbox.tsx +137 -0
  27. package/templates/components/ui/ContextMenu.tsx +220 -0
  28. package/templates/components/ui/Dialog.tsx +264 -0
  29. package/templates/components/ui/Divider.tsx +70 -0
  30. package/templates/components/ui/DropdownMenu.tsx +301 -0
  31. package/templates/components/ui/HelpPanel.tsx +119 -0
  32. package/templates/components/ui/Input.tsx +176 -0
  33. package/templates/components/ui/Popover.tsx +211 -0
  34. package/templates/components/ui/Progress.tsx +158 -0
  35. package/templates/components/ui/Select.tsx +134 -0
  36. package/templates/components/ui/Sheet.tsx +316 -0
  37. package/templates/components/ui/Slider.tsx +223 -0
  38. package/templates/components/ui/Switch.tsx +155 -0
  39. package/templates/components/ui/Tabs.tsx +253 -0
  40. package/templates/components/ui/Toast.tsx +192 -0
  41. package/templates/components/ui/Tooltip.tsx +129 -0
  42. package/templates/components/ui/hooks/useModalBehavior.ts +66 -0
  43. package/templates/components/ui/index.ts +84 -0
  44. package/templates/devtools/DevToolsPanel.tsx +261 -0
  45. package/templates/devtools/DevToolsProvider.tsx +43 -0
  46. package/templates/devtools/components/BreakpointIndicator.tsx +49 -0
  47. package/templates/devtools/components/ColorPicker.tsx +33 -0
  48. package/templates/devtools/components/ComponentsSecondaryNav.tsx +44 -0
  49. package/templates/devtools/components/ContextualFooter.tsx +56 -0
  50. package/templates/devtools/components/DraggablePanel.tsx +43 -0
  51. package/templates/devtools/components/PrimaryNavigationFooter.tsx +254 -0
  52. package/templates/devtools/components/SearchableColorDropdown.tsx +253 -0
  53. package/templates/devtools/components/SecondaryNavigation.tsx +36 -0
  54. package/templates/devtools/components/TokenDropdown.tsx +47 -0
  55. package/templates/devtools/components/TypographyFooter.tsx +145 -0
  56. package/templates/devtools/hooks/useMockState.ts +16 -0
  57. package/templates/devtools/index.ts +17 -0
  58. package/templates/devtools/lib/componentScanner.ts +78 -0
  59. package/templates/devtools/lib/cssParser.ts +465 -0
  60. package/templates/devtools/lib/searchIndexes.ts +45 -0
  61. package/templates/devtools/lib/selectorGenerator.ts +86 -0
  62. package/templates/devtools/store/index.ts +66 -0
  63. package/templates/devtools/store/slices/assetsSlice.ts +106 -0
  64. package/templates/devtools/store/slices/componentsSlice.ts +59 -0
  65. package/templates/devtools/store/slices/mockStatesSlice.ts +77 -0
  66. package/templates/devtools/store/slices/panelSlice.ts +17 -0
  67. package/templates/devtools/store/slices/typographySlice.ts +538 -0
  68. package/templates/devtools/store/slices/variablesSlice.ts +167 -0
  69. package/templates/devtools/tabs/AssetsTab/AssetGrid.tsx +76 -0
  70. package/templates/devtools/tabs/AssetsTab/FolderTree.tsx +53 -0
  71. package/templates/devtools/tabs/AssetsTab/UploadDropzone.tsx +76 -0
  72. package/templates/devtools/tabs/AssetsTab/index.tsx +182 -0
  73. package/templates/devtools/tabs/ComponentsTab/AddTabButton.tsx +63 -0
  74. package/templates/devtools/tabs/ComponentsTab/ComponentList.tsx +153 -0
  75. package/templates/devtools/tabs/ComponentsTab/DesignSystemTab.tsx +1515 -0
  76. package/templates/devtools/tabs/ComponentsTab/DynamicFolderTab.tsx +113 -0
  77. package/templates/devtools/tabs/ComponentsTab/PropDisplay.tsx +55 -0
  78. package/templates/devtools/tabs/ComponentsTab/index.tsx +167 -0
  79. package/templates/devtools/tabs/ComponentsTab/previews/.gitkeep +4 -0
  80. package/templates/devtools/tabs/ComponentsTab/previews/Rad_os.tsx +262 -0
  81. package/templates/devtools/tabs/ComponentsTab/tabConfig.ts +53 -0
  82. package/templates/devtools/tabs/MockStatesTab/index.tsx +29 -0
  83. package/templates/devtools/tabs/TypographyTab/FontManager.tsx +421 -0
  84. package/templates/devtools/tabs/TypographyTab/TypographyStylesDisplay.tsx +290 -0
  85. package/templates/devtools/tabs/TypographyTab/index.tsx +98 -0
  86. package/templates/devtools/tabs/VariablesTab/BaseColorEditor.tsx +267 -0
  87. package/templates/devtools/tabs/VariablesTab/BorderRadiusEditor.tsx +37 -0
  88. package/templates/devtools/tabs/VariablesTab/ColorModeSelector.tsx +235 -0
  89. package/templates/devtools/tabs/VariablesTab/index.tsx +100 -0
  90. package/templates/devtools/types/index.ts +99 -0
  91. package/templates/globals.css +574 -0
  92. package/templates/hooks/index.ts +1 -0
  93. package/templates/hooks/useWindowManager.ts +212 -0
  94. package/templates/public/assets/icons/avatar.svg +18 -0
  95. package/templates/public/assets/icons/checkmark-filled.svg +14 -0
  96. package/templates/public/assets/icons/checkmark.svg +14 -0
  97. package/templates/public/assets/icons/chevron-down.svg +14 -0
  98. package/templates/public/assets/icons/close.svg +14 -0
  99. package/templates/public/assets/icons/copy.svg +14 -0
  100. package/templates/public/assets/icons/download.svg +14 -0
  101. package/templates/public/assets/icons/expand.svg +31 -0
  102. package/templates/public/assets/icons/file-blank.svg +17 -0
  103. package/templates/public/assets/icons/file-image.svg +19 -0
  104. package/templates/public/assets/icons/file-written.svg +17 -0
  105. package/templates/public/assets/icons/folder-closed.svg +17 -0
  106. package/templates/public/assets/icons/folder-open.svg +17 -0
  107. package/templates/public/assets/icons/hamburger.svg +18 -0
  108. package/templates/public/assets/icons/home-outline.svg +28 -0
  109. package/templates/public/assets/icons/home.svg +30 -0
  110. package/templates/public/assets/icons/hourglass.svg +25 -0
  111. package/templates/public/assets/icons/information-circle.svg +14 -0
  112. package/templates/public/assets/icons/information.svg +17 -0
  113. package/templates/public/assets/icons/lightning.svg +14 -0
  114. package/templates/public/assets/icons/locked.svg +17 -0
  115. package/templates/public/assets/icons/not-allowed.svg +14 -0
  116. package/templates/public/assets/icons/plus.svg +5 -0
  117. package/templates/public/assets/icons/power-thin.svg +17 -0
  118. package/templates/public/assets/icons/power.svg +17 -0
  119. package/templates/public/assets/icons/question-block.svg +14 -0
  120. package/templates/public/assets/icons/question.svg +17 -0
  121. package/templates/public/assets/icons/refresh-block.svg +14 -0
  122. package/templates/public/assets/icons/refresh.svg +17 -0
  123. package/templates/public/assets/icons/save.svg +14 -0
  124. package/templates/public/assets/icons/search.svg +25 -0
  125. package/templates/public/assets/icons/settings.svg +14 -0
  126. package/templates/public/assets/icons/trash-full.svg +21 -0
  127. package/templates/public/assets/icons/trash-open.svg +23 -0
  128. package/templates/public/assets/icons/trash.svg +18 -0
  129. package/templates/public/assets/icons/unlocked.svg +17 -0
  130. package/templates/public/assets/icons/waring-triangle-filled.svg +17 -0
  131. package/templates/public/assets/icons/warning-triangle-filled-2.svg +30 -0
  132. package/templates/public/assets/icons/warning-triangle-lines.svg +29 -0
  133. package/templates/public/assets/icons/wrench.svg +17 -0
@@ -0,0 +1,235 @@
1
+ 'use client';
2
+
3
+ import { useDevToolsStore } from '../../store';
4
+ import { useEffect, useState } from 'react';
5
+ import { Button } from '@/components/ui/Button';
6
+ import { useToast } from '@/components/ui/Toast';
7
+
8
+ // Available semantic tokens that can be overridden in color modes
9
+ const OVERRIDABLE_TOKENS = [
10
+ { id: 'primary', label: 'Primary' },
11
+ { id: 'secondary', label: 'Secondary' },
12
+ { id: 'tertiary', label: 'Tertiary' },
13
+ { id: 'alternate', label: 'Alternate' },
14
+ { id: 'heading', label: 'Heading' },
15
+ { id: 'body', label: 'Body' },
16
+ { id: 'caption', label: 'Caption' },
17
+ { id: 'border', label: 'Border' },
18
+ { id: 'muted', label: 'Muted' },
19
+ ];
20
+
21
+ export function ColorModeSelector() {
22
+ const { colorModes, activeColorMode, setActiveColorMode, addColorMode, deleteColorMode, baseColors } = useDevToolsStore();
23
+ const [showAddForm, setShowAddForm] = useState(false);
24
+ const [newModeName, setNewModeName] = useState('');
25
+ const [overrides, setOverrides] = useState<Record<string, string>>({});
26
+ const { addToast } = useToast();
27
+
28
+ // Apply color mode to document
29
+ useEffect(() => {
30
+ const html = document.documentElement;
31
+
32
+ // Remove all color mode classes
33
+ colorModes.forEach((mode) => {
34
+ html.classList.remove(mode.name);
35
+ });
36
+
37
+ // Add active color mode class
38
+ if (activeColorMode) {
39
+ const activeMode = colorModes.find((m) => m.id === activeColorMode);
40
+ if (activeMode) {
41
+ html.classList.add(activeMode.name);
42
+ }
43
+ }
44
+ }, [activeColorMode, colorModes]);
45
+
46
+ const handleAddOverride = (token: string, colorId: string) => {
47
+ if (colorId) {
48
+ setOverrides({ ...overrides, [token]: colorId });
49
+ } else {
50
+ const { [token]: _, ...rest } = overrides;
51
+ setOverrides(rest);
52
+ }
53
+ };
54
+
55
+ const handleCreateColorMode = () => {
56
+ if (!newModeName.trim()) {
57
+ addToast({
58
+ title: 'Missing name',
59
+ description: 'Please enter a mode name',
60
+ variant: 'warning',
61
+ });
62
+ return;
63
+ }
64
+ if (Object.keys(overrides).length === 0) {
65
+ addToast({
66
+ title: 'No overrides',
67
+ description: 'Please add at least one override',
68
+ variant: 'warning',
69
+ });
70
+ return;
71
+ }
72
+
73
+ // Convert color IDs to CSS variable references
74
+ const cssOverrides: Record<string, string> = {};
75
+ for (const [token, colorId] of Object.entries(overrides)) {
76
+ const color = baseColors.find(c => c.id === colorId);
77
+ if (color) {
78
+ // Store as reference format: "neutral-darkest" or "sun-yellow"
79
+ const varName = color.category === 'neutral' ? `neutral-${color.name}` : color.name;
80
+ cssOverrides[token] = varName;
81
+ }
82
+ }
83
+
84
+ addColorMode({
85
+ name: newModeName.toLowerCase().replace(/\s+/g, '-'),
86
+ className: `.${newModeName.toLowerCase().replace(/\s+/g, '-')}`,
87
+ overrides: cssOverrides,
88
+ });
89
+
90
+ // Reset form
91
+ setNewModeName('');
92
+ setOverrides({});
93
+ setShowAddForm(false);
94
+ };
95
+
96
+ const handleDeleteColorMode = (id: string) => {
97
+ if (activeColorMode === id) {
98
+ setActiveColorMode(null);
99
+ }
100
+ deleteColorMode(id);
101
+ };
102
+
103
+ // Get all base colors for the dropdown
104
+ const allColors = [...baseColors.filter(c => c.category === 'brand'), ...baseColors.filter(c => c.category === 'neutral')];
105
+
106
+ return (
107
+ <div className="space-y-3">
108
+ <div className="flex items-center justify-between">
109
+ <h3>Color Mode</h3>
110
+ </div>
111
+
112
+ <div className="flex flex-wrap gap-2">
113
+ <Button
114
+ variant={activeColorMode === null ? 'primary' : 'ghost'}
115
+ size="md"
116
+ onClick={() => setActiveColorMode(null)}
117
+ className={activeColorMode === null ? 'border-2' : ''}
118
+ >
119
+ Default
120
+ </Button>
121
+ <Button
122
+ variant="outline"
123
+ size="md"
124
+ iconName={showAddForm ? "close" : undefined}
125
+ onClick={() => setShowAddForm(!showAddForm)}
126
+ >
127
+ {showAddForm ? 'Cancel' : '+ Add Mode'}
128
+ </Button>
129
+
130
+ {colorModes.map((mode) => (
131
+ <div key={mode.id} className="flex items-center gap-1">
132
+ <Button
133
+ variant={activeColorMode === mode.id ? 'primary' : 'ghost'}
134
+ size="md"
135
+ onClick={() => setActiveColorMode(mode.id)}
136
+ className={activeColorMode === mode.id ? 'border-2' : ''}
137
+ >
138
+ {mode.name}
139
+ </Button>
140
+ <Button
141
+ variant="ghost"
142
+ size="md"
143
+ iconOnly={true}
144
+ iconName="close"
145
+ onClick={() => handleDeleteColorMode(mode.id)}
146
+ className="text-error-red hover:bg-error-red/10 active:bg-error-red/20"
147
+ title="Delete color mode"
148
+ />
149
+ </div>
150
+ ))}
151
+ </div>
152
+
153
+ {/* Add Color Mode Form */}
154
+ {showAddForm && (
155
+ <div className="p-3 bg-black/10 rounded-md space-y-3">
156
+ <h4>Create Color Mode</h4>
157
+
158
+ <div>
159
+ <label className="block font-mondwest text-sm text-black/60 mb-1">Mode Name</label>
160
+ <input
161
+ type="text"
162
+ value={newModeName}
163
+ onChange={(e) => setNewModeName(e.target.value)}
164
+ placeholder="e.g., dark"
165
+ className="w-full px-2 py-1.5 font-mondwest text-base bg-warm-cloud border border-black rounded-sm text-black"
166
+ />
167
+ </div>
168
+
169
+ <div>
170
+ <label className="block font-mondwest text-sm text-black/60 mb-2">Token Overrides</label>
171
+ <div className="space-y-2">
172
+ {OVERRIDABLE_TOKENS.map((token) => (
173
+ <div key={token.id} className="flex items-center gap-2">
174
+ <span className="w-24 font-mondwest text-sm text-black">{token.label}</span>
175
+ <select
176
+ value={overrides[token.id] || ''}
177
+ onChange={(e) => handleAddOverride(token.id, e.target.value)}
178
+ className="flex-1 px-2 py-1 font-mondwest text-sm bg-warm-cloud border border-black rounded-sm text-black"
179
+ >
180
+ <option value="">— No override —</option>
181
+ <optgroup label="Brand Colors">
182
+ {allColors.filter(c => c.category === 'brand').map((color) => (
183
+ <option key={color.id} value={color.id}>
184
+ {color.displayName}
185
+ </option>
186
+ ))}
187
+ </optgroup>
188
+ <optgroup label="Neutral Colors">
189
+ {allColors.filter(c => c.category === 'neutral').map((color) => (
190
+ <option key={color.id} value={color.id}>
191
+ {color.displayName}
192
+ </option>
193
+ ))}
194
+ </optgroup>
195
+ </select>
196
+ {overrides[token.id] && (
197
+ <div
198
+ className="w-6 h-6 rounded-sm border border-black"
199
+ style={{ backgroundColor: baseColors.find(c => c.id === overrides[token.id])?.value }}
200
+ />
201
+ )}
202
+ </div>
203
+ ))}
204
+ </div>
205
+ </div>
206
+
207
+ <Button
208
+ variant="secondary"
209
+ size="md"
210
+ onClick={handleCreateColorMode}
211
+ >
212
+ Create Mode
213
+ </Button>
214
+ </div>
215
+ )}
216
+
217
+ {/* Active Overrides Display */}
218
+ {activeColorMode && (
219
+ <div className="p-3 bg-black/10 rounded-md">
220
+ <h4 className="mb-2">Active Overrides</h4>
221
+ <div className="space-y-1">
222
+ {Object.entries(colorModes.find((m) => m.id === activeColorMode)?.overrides || {}).map(([token, ref]) => (
223
+ <div key={token} className="flex items-center gap-2 font-mondwest text-base">
224
+ <span className="text-black font-mono">{token}</span>
225
+ <span className="text-black/60">→</span>
226
+ <span className="text-black/60 font-mono">{ref}</span>
227
+ </div>
228
+ ))}
229
+ </div>
230
+ </div>
231
+ )}
232
+ </div>
233
+ );
234
+ }
235
+
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState, useRef } from 'react';
4
+ import { useDevToolsStore } from '../../store';
5
+ import { BaseColorEditor } from './BaseColorEditor';
6
+ import { ColorModeSelector } from './ColorModeSelector';
7
+ import { BorderRadiusEditor } from './BorderRadiusEditor';
8
+ import { Button } from '@/components/ui/Button';
9
+ import { Divider } from '@/components/ui/Divider';
10
+
11
+ export function VariablesTab() {
12
+ const { loadFromCSS, syncToCSS, baseColors } = useDevToolsStore();
13
+ const [isSaving, setIsSaving] = useState(false);
14
+ const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
15
+ const messageTimerRef = useRef<NodeJS.Timeout>(undefined);
16
+
17
+ // Cleanup timeout on unmount
18
+ useEffect(() => {
19
+ return () => {
20
+ if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
21
+ };
22
+ }, []);
23
+
24
+ // Load CSS on mount to sync with actual file
25
+ useEffect(() => {
26
+ loadFromCSS();
27
+ }, [loadFromCSS]);
28
+
29
+ const handleSave = async () => {
30
+ setIsSaving(true);
31
+ setMessage(null);
32
+ try {
33
+ await syncToCSS();
34
+ setMessage({ type: 'success', text: 'Saved to globals.css!' });
35
+ } catch {
36
+ setMessage({ type: 'error', text: 'Failed to save. Check console.' });
37
+ } finally {
38
+ setIsSaving(false);
39
+ if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
40
+ messageTimerRef.current = setTimeout(() => setMessage(null), 3000);
41
+ }
42
+ };
43
+
44
+ const handleReload = async () => {
45
+ await loadFromCSS();
46
+ setMessage({ type: 'success', text: 'Reloaded from CSS!' });
47
+ if (messageTimerRef.current) clearTimeout(messageTimerRef.current);
48
+ messageTimerRef.current = setTimeout(() => setMessage(null), 3000);
49
+ };
50
+
51
+ return (
52
+ <div className="flex flex-col h-full overflow-auto pt-4 pb-4 pl-4 pr-2 bg-[var(--color-white)] border border-black rounded space-y-4">
53
+ {/* Header Actions */}
54
+ <div className="flex items-center justify-between">
55
+ <h2>Design Tokens</h2>
56
+ <div className="flex items-center gap-2">
57
+ <Button
58
+ variant="outline"
59
+ size="md"
60
+ iconName="refresh"
61
+ onClick={handleReload}
62
+ >
63
+ Reload
64
+ </Button>
65
+ <Button
66
+ variant="secondary"
67
+ size="md"
68
+ iconName="save"
69
+ onClick={handleSave}
70
+ disabled={isSaving}
71
+ >
72
+ {isSaving ? 'Saving...' : 'Save to CSS'}
73
+ </Button>
74
+ </div>
75
+ </div>
76
+
77
+ {/* Status Message */}
78
+ {message && (
79
+ <div
80
+ className={`px-3 py-2 font-mondwest text-base rounded-sm ${
81
+ message.type === 'success'
82
+ ? 'bg-green text-black'
83
+ : 'bg-error-red text-warm-cloud'
84
+ }`}
85
+ >
86
+ {message.text}
87
+ </div>
88
+ )}
89
+
90
+ {/* Editors */}
91
+ <div className="space-y-4" data-edit-scope="theme-variables">
92
+ <ColorModeSelector />
93
+ <Divider />
94
+ <BaseColorEditor />
95
+ <Divider />
96
+ <BorderRadiusEditor />
97
+ </div>
98
+ </div>
99
+ );
100
+ }
@@ -0,0 +1,99 @@
1
+ // Design Token Types
2
+
3
+ // Base Color (brand or neutral primitive)
4
+ export interface BaseColor {
5
+ id: string;
6
+ name: string; // e.g., "sun-yellow", "lightest"
7
+ displayName: string; // e.g., "Sun Yellow", "Lightest"
8
+ value: string; // e.g., "#FCE184"
9
+ category: 'brand' | 'neutral';
10
+ }
11
+
12
+ // Legacy alias for backwards compatibility
13
+ export type BrandColor = BaseColor;
14
+
15
+ export interface ColorMode {
16
+ id: string;
17
+ name: string; // e.g., "dark"
18
+ className: string; // e.g., ".dark"
19
+ overrides: Record<string, string>; // token name -> brand color reference
20
+ }
21
+
22
+ // Component Discovery Types
23
+ export interface DiscoveredComponent {
24
+ name: string;
25
+ path: string;
26
+ props: PropDefinition[];
27
+ }
28
+
29
+ export interface PropDefinition {
30
+ name: string;
31
+ type: string;
32
+ required: boolean;
33
+ defaultValue?: string;
34
+ }
35
+
36
+ // Asset Types
37
+ export interface AssetFile {
38
+ name: string;
39
+ path: string;
40
+ type: 'image' | 'video' | 'other';
41
+ size: number;
42
+ dimensions?: { width: number; height: number };
43
+ }
44
+
45
+ export interface AssetFolder {
46
+ name: string;
47
+ path: string;
48
+ children: (AssetFolder | AssetFile)[];
49
+ }
50
+
51
+ // Mock State Types
52
+ export interface MockState {
53
+ id: string;
54
+ name: string;
55
+ description: string;
56
+ category: string;
57
+ values: Record<string, unknown>;
58
+ active: boolean;
59
+ }
60
+
61
+ // Tab Types
62
+ export type Tab = 'variables' | 'typography' | 'components' | 'assets' | 'mockStates';
63
+
64
+ // Typography Types
65
+
66
+ // Font file within a font definition
67
+ export interface FontFile {
68
+ id: string;
69
+ weight: number; // 400, 700, etc.
70
+ style: string; // "normal", "italic"
71
+ format: 'woff2' | 'woff' | 'ttf' | 'otf';
72
+ path: string; // "/fonts/Mondwest-Regular.woff2"
73
+ }
74
+
75
+ // Font Definition
76
+ export interface FontDefinition {
77
+ id: string;
78
+ name: string; // "Mondwest", "Joystix Monospace"
79
+ family: string; // CSS font-family value
80
+ files: FontFile[]; // Array of font files (Regular, Bold, etc.)
81
+ weights: number[]; // [400, 700]
82
+ styles: string[]; // ["normal", "italic"]
83
+ }
84
+
85
+ // Typography Style Definition (for @layer base HTML elements)
86
+ // Matches Webflow style guide sizing/font choices but uses Tailwind @apply
87
+ export interface TypographyStyle {
88
+ id: string;
89
+ element: string; // "h1", "h2", "h3", "h4", "h5", "h6", "p", "a", "ul", "ol", "li"
90
+ fontFamilyId: string; // References FontDefinition.id
91
+ fontSize: string; // Tailwind class: "text-4xl", "text-3xl", etc.
92
+ lineHeight?: string; // Tailwind class: "leading-relaxed", "leading-tight", etc.
93
+ letterSpacing?: string; // Tailwind class: "tracking-wide", "tracking-normal", etc.
94
+ fontWeight: string; // Tailwind class: "font-bold", "font-semibold", "font-normal", etc.
95
+ baseColorId: string; // References BaseColor.id for text color
96
+ displayName: string; // "Heading 1", "Heading 2", "Paragraph", etc.
97
+ utilities?: string[]; // Additional Tailwind utilities: ["underline"], ["hover:text-link-hover"]
98
+ }
99
+