@fragments-sdk/cli 0.6.0 → 0.7.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 (49) hide show
  1. package/dist/bin.js +294 -50
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-D35RGPAG.js → chunk-7OPWMLOE.js} +435 -19
  4. package/dist/chunk-7OPWMLOE.js.map +1 -0
  5. package/dist/{chunk-SSLQXHNX.js → chunk-CVXKXVOY.js} +1 -1
  6. package/dist/{chunk-SSLQXHNX.js.map → chunk-CVXKXVOY.js.map} +1 -1
  7. package/dist/{chunk-Q7GOHVOK.js → chunk-TJ34N7C7.js} +39 -2
  8. package/dist/{chunk-Q7GOHVOK.js.map → chunk-TJ34N7C7.js.map} +1 -1
  9. package/dist/{chunk-F7ITZPDJ.js → chunk-XHUDJNN3.js} +2 -2
  10. package/dist/{core-SKRPJQZG.js → core-W2HYIQW6.js} +2 -2
  11. package/dist/{generate-7AF7WRVK.js → generate-LMTISDIJ.js} +3 -3
  12. package/dist/index.js +3 -3
  13. package/dist/{init-WKGDPYI4.js → init-7CHRKQ7P.js} +3 -3
  14. package/dist/mcp-bin.js +2 -2
  15. package/dist/{scan-K6JNMCGM.js → scan-WY23TJCP.js} +4 -4
  16. package/dist/{service-F3E4JJM7.js → service-T2L7VLTE.js} +2 -2
  17. package/dist/{static-viewer-4LQZ5AGA.js → static-viewer-GBR7YNF3.js} +2 -2
  18. package/dist/{test-CJDNJTPZ.js → test-OJRXNDO2.js} +2 -2
  19. package/dist/{tokens-JAJABYXP.js → tokens-3BWDESVM.js} +3 -3
  20. package/dist/{viewer-R3Q6WAMJ.js → viewer-SUFOISZM.js} +12 -12
  21. package/package.json +2 -2
  22. package/src/bin.ts +23 -0
  23. package/src/build.ts +43 -0
  24. package/src/commands/graph.ts +274 -0
  25. package/src/core/composition.ts +64 -1
  26. package/src/core/graph-extractor.test.ts +542 -0
  27. package/src/core/graph-extractor.ts +601 -0
  28. package/src/core/importAnalyzer.ts +5 -0
  29. package/src/viewer/components/App.tsx +128 -30
  30. package/src/viewer/components/Icons.tsx +53 -1
  31. package/src/viewer/components/Layout.tsx +7 -3
  32. package/src/viewer/components/LeftSidebar.tsx +65 -87
  33. package/src/viewer/components/PreviewFrameHost.tsx +30 -1
  34. package/src/viewer/components/PreviewToolbar.tsx +57 -10
  35. package/src/viewer/components/ViewportSelector.tsx +56 -45
  36. package/src/viewer/constants/ui.ts +4 -4
  37. package/src/viewer/preview-frame.html +22 -13
  38. package/src/viewer/styles/globals.css +42 -81
  39. package/dist/chunk-D35RGPAG.js.map +0 -1
  40. /package/dist/{chunk-F7ITZPDJ.js.map → chunk-XHUDJNN3.js.map} +0 -0
  41. /package/dist/{core-SKRPJQZG.js.map → core-W2HYIQW6.js.map} +0 -0
  42. /package/dist/{generate-7AF7WRVK.js.map → generate-LMTISDIJ.js.map} +0 -0
  43. /package/dist/{init-WKGDPYI4.js.map → init-7CHRKQ7P.js.map} +0 -0
  44. /package/dist/{scan-K6JNMCGM.js.map → scan-WY23TJCP.js.map} +0 -0
  45. /package/dist/{service-F3E4JJM7.js.map → service-T2L7VLTE.js.map} +0 -0
  46. /package/dist/{static-viewer-4LQZ5AGA.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
  47. /package/dist/{test-CJDNJTPZ.js.map → test-OJRXNDO2.js.map} +0 -0
  48. /package/dist/{tokens-JAJABYXP.js.map → tokens-3BWDESVM.js.map} +0 -0
  49. /package/dist/{viewer-R3Q6WAMJ.js.map → viewer-SUFOISZM.js.map} +0 -0
@@ -12,13 +12,62 @@ export type { ZoomLevel, BackgroundOption };
12
12
  export { getBackgroundStyle } from '../constants/ui.js';
13
13
 
14
14
  // Background options with display metadata
15
- const BACKGROUND_OPTIONS_UI: { value: BackgroundOption; label: string; icon: string }[] = [
16
- { value: 'white', label: 'White', icon: '\u25FB' },
17
- { value: 'black', label: 'Black', icon: '\u25FC' },
18
- { value: 'checkerboard', label: 'Checkerboard', icon: '\u25A6' },
19
- { value: 'transparent', label: 'Transparent', icon: '\u25C7' },
15
+ const BACKGROUND_OPTIONS_UI: { value: BackgroundOption; label: string }[] = [
16
+ { value: 'white', label: 'White' },
17
+ { value: 'black', label: 'Black' },
18
+ { value: 'checkerboard', label: 'Checkerboard' },
19
+ { value: 'transparent', label: 'Transparent' },
20
20
  ];
21
21
 
22
+ function BackgroundSwatch({ background }: { background: BackgroundOption }) {
23
+ const baseStyle = {
24
+ width: '14px',
25
+ height: '14px',
26
+ borderRadius: '4px',
27
+ border: '1px solid var(--border)',
28
+ flexShrink: 0,
29
+ };
30
+
31
+ if (background === 'white') {
32
+ return <span aria-hidden="true" style={{ ...baseStyle, backgroundColor: '#ffffff' }} />;
33
+ }
34
+
35
+ if (background === 'black') {
36
+ return <span aria-hidden="true" style={{ ...baseStyle, backgroundColor: '#171717', borderColor: '#2a2a2a' }} />;
37
+ }
38
+
39
+ if (background === 'checkerboard') {
40
+ return (
41
+ <span
42
+ aria-hidden="true"
43
+ style={{
44
+ ...baseStyle,
45
+ backgroundColor: '#ffffff',
46
+ backgroundImage: `
47
+ linear-gradient(45deg, #d4d4d8 25%, transparent 25%),
48
+ linear-gradient(-45deg, #d4d4d8 25%, transparent 25%),
49
+ linear-gradient(45deg, transparent 75%, #d4d4d8 75%),
50
+ linear-gradient(-45deg, transparent 75%, #d4d4d8 75%)
51
+ `,
52
+ backgroundSize: '8px 8px',
53
+ backgroundPosition: '0 0, 0 4px, 4px -4px, -4px 0',
54
+ }}
55
+ />
56
+ );
57
+ }
58
+
59
+ return (
60
+ <span
61
+ aria-hidden="true"
62
+ style={{
63
+ ...baseStyle,
64
+ backgroundColor: 'transparent',
65
+ backgroundImage: 'linear-gradient(135deg, transparent 42%, var(--text-tertiary) 43%, var(--text-tertiary) 57%, transparent 58%)',
66
+ }}
67
+ />
68
+ );
69
+ }
70
+
22
71
  interface PreviewToolbarProps {
23
72
  zoom: ZoomLevel;
24
73
  background: BackgroundOption;
@@ -102,10 +151,8 @@ export function PreviewToolbar({
102
151
  <Menu.Trigger asChild>
103
152
  <Button variant="ghost" size="sm" title="Background color">
104
153
  <Stack direction="row" gap="xs" align="center">
105
- <span style={{ fontSize: '14px' }}>
106
- {BACKGROUND_OPTIONS_UI.find(o => o.value === background)?.icon}
107
- </span>
108
- <span style={{ textTransform: 'capitalize' }}>{background}</span>
154
+ <BackgroundSwatch background={background} />
155
+ <span>{BACKGROUND_OPTIONS_UI.find(o => o.value === background)?.label}</span>
109
156
  <span style={{ display: 'inline-flex', width: '12px', height: '12px' }}>
110
157
  <ChevronDownIcon />
111
158
  </span>
@@ -120,7 +167,7 @@ export function PreviewToolbar({
120
167
  {BACKGROUND_OPTIONS_UI.map((option) => (
121
168
  <Menu.RadioItem key={option.value} value={option.value}>
122
169
  <Stack direction="row" gap="sm" align="center">
123
- <span style={{ fontSize: '14px' }}>{option.icon}</span>
170
+ <BackgroundSwatch background={option.value} />
124
171
  {option.label}
125
172
  </Stack>
126
173
  </Menu.RadioItem>
@@ -1,7 +1,14 @@
1
- import { useState } from 'react';
2
1
  import { Button, Menu, Stack, Input, Text, Box } from '@fragments/ui';
3
2
  import { VIEWPORT_PRESETS, type ViewportPreset } from '../constants/ui.js';
4
- import { ViewportIcon, ChevronDownIcon } from './Icons.js';
3
+ import {
4
+ ViewportIcon,
5
+ ChevronDownIcon,
6
+ ResponsiveIcon,
7
+ DesktopIcon,
8
+ TabletIcon,
9
+ MobileIcon,
10
+ SettingsIcon,
11
+ } from './Icons.js';
5
12
 
6
13
  // Re-export for consumers
7
14
  export type { ViewportPreset };
@@ -16,9 +23,24 @@ const VIEWPORT_PRESETS_UI = Object.entries(VIEWPORT_PRESETS).map(([value, config
16
23
  value: value as ViewportPreset,
17
24
  label: config.label,
18
25
  width: config.width,
19
- icon: config.icon,
20
26
  }));
21
27
 
28
+ function PresetIcon({ preset }: { preset: ViewportPreset }) {
29
+ const commonStyle = { width: '16px', height: '16px' };
30
+
31
+ switch (preset) {
32
+ case 'desktop':
33
+ return <DesktopIcon style={commonStyle} />;
34
+ case 'tablet':
35
+ return <TabletIcon style={commonStyle} />;
36
+ case 'mobile':
37
+ return <MobileIcon style={commonStyle} />;
38
+ case 'responsive':
39
+ default:
40
+ return <ResponsiveIcon style={commonStyle} />;
41
+ }
42
+ }
43
+
22
44
  interface ViewportSelectorProps {
23
45
  viewport: ViewportPreset;
24
46
  customSize: ViewportSize;
@@ -32,9 +54,8 @@ export function ViewportSelector({
32
54
  onViewportChange,
33
55
  onCustomSizeChange,
34
56
  }: ViewportSelectorProps) {
35
- const [showCustom, setShowCustom] = useState(false);
36
-
37
57
  const currentPreset = VIEWPORT_PRESETS_UI.find(p => p.value === viewport);
58
+ const isCustomViewport = viewport === 'custom';
38
59
  const displayLabel = viewport === 'custom'
39
60
  ? `${customSize.width || '?'}\u00D7${customSize.height || 'auto'}`
40
61
  : currentPreset?.label || 'Responsive';
@@ -55,57 +76,47 @@ export function ViewportSelector({
55
76
  </Button>
56
77
  </Menu.Trigger>
57
78
  <Menu.Content side="bottom" align="end">
58
- {VIEWPORT_PRESETS_UI.map((preset) => (
59
- <Menu.Item
60
- key={preset.value}
61
- onSelect={() => {
62
- onViewportChange(preset.value);
63
- setShowCustom(false);
64
- }}
65
- >
66
- <Stack direction="row" gap="sm" align="center" justify="between" style={{ width: '100%' }}>
67
- <Stack direction="row" gap="sm" align="center">
68
- <span>{preset.icon}</span>
69
- <span style={{
70
- fontWeight: preset.value === viewport ? 600 : 400,
71
- color: preset.value === viewport ? 'var(--color-accent)' : undefined,
72
- }}>
73
- {preset.label}
74
- </span>
79
+ <Menu.RadioGroup
80
+ value={isCustomViewport ? 'custom' : viewport}
81
+ onValueChange={(value: string) => {
82
+ if (value === 'custom') {
83
+ onViewportChange('custom');
84
+ return;
85
+ }
86
+ onViewportChange(value as ViewportPreset);
87
+ }}
88
+ >
89
+ {VIEWPORT_PRESETS_UI.map((preset) => (
90
+ <Menu.RadioItem key={preset.value} value={preset.value}>
91
+ <Stack direction="row" gap="sm" align="center" justify="between" style={{ width: '100%' }}>
92
+ <Stack direction="row" gap="sm" align="center">
93
+ <PresetIcon preset={preset.value} />
94
+ <span>{preset.label}</span>
95
+ </Stack>
96
+ {preset.width && (
97
+ <Text size="2xs" color="tertiary">
98
+ {preset.width}px
99
+ </Text>
100
+ )}
75
101
  </Stack>
76
- {preset.width && (
77
- <Text size="2xs" color="tertiary">
78
- {preset.width}px
79
- </Text>
80
- )}
81
- </Stack>
82
- </Menu.Item>
83
- ))}
102
+ </Menu.RadioItem>
103
+ ))}
104
+ </Menu.RadioGroup>
84
105
 
85
106
  <Menu.Separator />
86
107
 
87
- <Menu.Item onSelect={() => setShowCustom(!showCustom)}>
108
+ <Menu.Item onSelect={() => onViewportChange('custom')}>
88
109
  <Stack direction="row" gap="sm" align="center" style={{
89
110
  width: '100%',
90
- fontWeight: viewport === 'custom' ? 600 : 400,
91
- color: viewport === 'custom' ? 'var(--color-accent)' : undefined,
111
+ fontWeight: isCustomViewport ? 600 : 400,
112
+ color: isCustomViewport ? 'var(--color-accent)' : undefined,
92
113
  }}>
93
- <span>{'\u2699'}</span>
114
+ <SettingsIcon style={{ width: '16px', height: '16px' }} />
94
115
  Custom size
95
- <span style={{
96
- display: 'inline-flex',
97
- width: '12px',
98
- height: '12px',
99
- marginLeft: 'auto',
100
- transition: 'transform 150ms ease',
101
- transform: showCustom ? 'rotate(180deg)' : 'none',
102
- }}>
103
- <ChevronDownIcon />
104
- </span>
105
116
  </Stack>
106
117
  </Menu.Item>
107
118
 
108
- {showCustom && (
119
+ {isCustomViewport && (
109
120
  <div
110
121
  onClick={(e) => e.stopPropagation()}
111
122
  onKeyDown={(e) => e.stopPropagation()}
@@ -118,10 +118,10 @@ export type ZoomLevel = (typeof ZOOM_LEVELS)[number];
118
118
  * Viewport presets.
119
119
  */
120
120
  export const VIEWPORT_PRESETS = {
121
- responsive: { label: 'Responsive', width: null, icon: '' },
122
- desktop: { label: 'Desktop', width: 1280, icon: '🖥' },
123
- tablet: { label: 'Tablet', width: 768, icon: '📱' },
124
- mobile: { label: 'Mobile', width: 375, icon: '📱' },
121
+ responsive: { label: 'Responsive', width: null, icon: 'responsive' },
122
+ desktop: { label: 'Desktop', width: 1280, icon: 'desktop' },
123
+ tablet: { label: 'Tablet', width: 768, icon: 'tablet' },
124
+ mobile: { label: 'Mobile', width: 375, icon: 'mobile' },
125
125
  } as const;
126
126
 
127
127
  export type ViewportPreset = keyof typeof VIEWPORT_PRESETS | 'custom';
@@ -4,12 +4,6 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Fragment Preview</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
11
- rel="stylesheet"
12
- />
13
7
  <style>
14
8
  /* Reset and base styles for isolated preview */
15
9
  *, *::before, *::after {
@@ -19,27 +13,42 @@
19
13
  html, body {
20
14
  margin: 0;
21
15
  padding: 0;
22
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ font-family: var(--fui-font-sans, 'Geist Sans', Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
23
17
  -webkit-font-smoothing: antialiased;
24
18
  -moz-osx-font-smoothing: grayscale;
25
19
  width: 100%;
26
20
  height: 100%;
27
- overflow: auto;
21
+ overflow: hidden;
28
22
  }
29
23
 
30
24
  body {
31
- background: transparent;
25
+ background: var(--fui-bg-primary, #ffffff);
32
26
  min-height: 100%;
33
27
  }
34
28
 
35
29
  #preview-root {
36
30
  width: 100%;
37
- min-height: 100%;
31
+ min-height: 100vh;
38
32
  display: flex;
39
- align-items: center;
40
- justify-content: center;
33
+ align-items: flex-start;
34
+ justify-content: flex-start;
41
35
  padding: 24px;
42
36
  box-sizing: border-box;
37
+ overflow: auto;
38
+ }
39
+
40
+ body[data-preview-mode='centered'] #preview-root {
41
+ align-items: center;
42
+ justify-content: center;
43
+ }
44
+
45
+ body[data-preview-mode='full-bleed'] #preview-root {
46
+ padding: 0;
47
+ min-height: 100vh;
48
+ }
49
+
50
+ body[data-preview-mode='full-bleed'] #preview-root > * {
51
+ width: 100%;
43
52
  }
44
53
 
45
54
  /* Hide scrollbars but allow scrolling */
@@ -110,7 +119,7 @@
110
119
  }
111
120
  </style>
112
121
  </head>
113
- <body>
122
+ <body data-preview-mode="centered">
114
123
  <main>
115
124
  <h1 class="sr-only">Component Preview</h1>
116
125
  <div id="preview-root">
@@ -1,90 +1,50 @@
1
1
  /* ============================================
2
2
  * Fragments Viewer Theme System
3
3
  * ============================================
4
- * The viewer shell (sidebar, toolbar, panels) uses these variables.
5
- * These are INDEPENDENT from the UI library's --fui-* variables.
6
- *
7
- * The UI library components are rendered in the preview area which
8
- * has its own scoped --fui-* variables (see below).
4
+ * The viewer shell (sidebar, toolbar, panels) mirrors @fragments/ui
5
+ * tokens so previews and shell feel like the live docs experience.
9
6
  * ============================================ */
10
7
 
11
- /* Light mode (default) */
12
8
  :root {
13
- --bg-primary: #ffffff;
14
- --bg-secondary: #f2f2f2;
15
- --bg-tertiary: #f5f5f5;
16
- --bg-elevated: #ffffff;
17
- --bg-hover: rgba(0, 0, 0, 0.04);
18
- --bg-active: rgba(0, 0, 0, 0.08);
19
-
20
- --text-primary: #171717;
21
- --text-secondary: #525252;
22
- --text-tertiary: #a3a3a3;
23
- --text-muted: #d4d4d4;
24
-
25
- --border: rgba(0, 0, 0, 0.08);
26
- --border-subtle: rgba(0, 0, 0, 0.04);
27
- --border-strong: rgba(0, 0, 0, 0.12);
28
-
29
- --color-accent: #10a37f;
30
- --color-accent-hover: #0d8a6a;
31
- --color-accent-subtle: rgba(16, 163, 127, 0.1);
32
-
33
- --color-success: #10a37f;
34
- --color-success-bg: rgba(16, 163, 127, 0.1);
35
- --color-warning: #f59e0b;
36
- --color-warning-bg: rgba(245, 158, 11, 0.1);
37
- --color-danger: #ef4444;
38
- --color-danger-bg: rgba(239, 68, 68, 0.1);
39
- --color-info: #3b82f6;
40
- --color-info-bg: rgba(59, 130, 246, 0.1);
41
-
42
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
43
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
44
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -4px rgba(0, 0, 0, 0.05);
45
-
46
- --radius-sm: 6px;
47
- --radius-md: 8px;
48
- --radius-lg: 12px;
49
-
50
- --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
51
- --transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1);
52
- }
53
-
54
- /* Dark mode - applied when .dark class is on html */
55
- .dark {
56
- --bg-primary: #0d0d0d;
57
- --bg-secondary: #171717;
58
- --bg-tertiary: #1f1f1f;
59
- --bg-elevated: #1a1a1a;
60
- --bg-hover: rgba(255, 255, 255, 0.04);
61
- --bg-active: rgba(255, 255, 255, 0.08);
62
-
63
- --text-primary: #ececec;
64
- --text-secondary: #9a9a9a;
65
- --text-tertiary: #666666;
66
- --text-muted: #404040;
67
-
68
- --border: rgba(255, 255, 255, 0.08);
69
- --border-subtle: rgba(255, 255, 255, 0.04);
70
- --border-strong: rgba(255, 255, 255, 0.12);
71
-
72
- --color-accent: #10a37f;
73
- --color-accent-hover: #1eb894;
74
- --color-accent-subtle: rgba(16, 163, 127, 0.15);
75
-
76
- --color-success: #10a37f;
77
- --color-success-bg: rgba(16, 163, 127, 0.15);
78
- --color-warning: #f59e0b;
79
- --color-warning-bg: rgba(245, 158, 11, 0.15);
80
- --color-danger: #ef4444;
81
- --color-danger-bg: rgba(239, 68, 68, 0.15);
82
- --color-info: #60a5fa;
83
- --color-info-bg: rgba(96, 165, 250, 0.15);
84
-
85
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
86
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -2px rgba(0, 0, 0, 0.2);
87
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -4px rgba(0, 0, 0, 0.3);
9
+ --bg-primary: var(--fui-bg-primary, #ffffff);
10
+ --bg-secondary: var(--fui-bg-secondary, #f7f7f8);
11
+ --bg-tertiary: var(--fui-bg-tertiary, #f2f2f2);
12
+ --bg-elevated: var(--fui-bg-elevated, #ffffff);
13
+ --bg-hover: var(--fui-bg-hover, rgba(0, 0, 0, 0.06));
14
+ --bg-active: var(--fui-bg-active, rgba(0, 0, 0, 0.1));
15
+
16
+ --text-primary: var(--fui-text-primary, #171717);
17
+ --text-secondary: var(--fui-text-secondary, #525252);
18
+ --text-tertiary: var(--fui-text-tertiary, #8a8a8a);
19
+ --text-muted: var(--fui-text-tertiary, #8a8a8a);
20
+
21
+ --border: var(--fui-border, rgba(0, 0, 0, 0.1));
22
+ --border-subtle: var(--fui-border, rgba(0, 0, 0, 0.1));
23
+ --border-strong: var(--fui-border-strong, rgba(0, 0, 0, 0.16));
24
+
25
+ --color-accent: var(--fui-color-accent, #10a37f);
26
+ --color-accent-hover: var(--fui-color-accent-hover, #0d8a6a);
27
+ --color-accent-subtle: var(--fui-color-success-bg, rgba(16, 163, 127, 0.1));
28
+
29
+ --color-success: var(--fui-color-success, #10a37f);
30
+ --color-success-bg: var(--fui-color-success-bg, rgba(16, 163, 127, 0.1));
31
+ --color-warning: var(--fui-color-warning, #f59e0b);
32
+ --color-warning-bg: var(--fui-color-warning-bg, rgba(245, 158, 11, 0.1));
33
+ --color-danger: var(--fui-color-danger, #ef4444);
34
+ --color-danger-bg: var(--fui-color-danger-bg, rgba(239, 68, 68, 0.1));
35
+ --color-info: var(--fui-color-info, #3b82f6);
36
+ --color-info-bg: var(--fui-color-info-bg, rgba(59, 130, 246, 0.1));
37
+
38
+ --shadow-sm: var(--fui-shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.05));
39
+ --shadow-md: var(--fui-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.08));
40
+ --shadow-lg: var(--fui-shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.12));
41
+
42
+ --radius-sm: var(--fui-radius-sm, 6px);
43
+ --radius-md: var(--fui-radius-md, 8px);
44
+ --radius-lg: var(--fui-radius-lg, 12px);
45
+
46
+ --transition-fast: var(--fui-transition-fast, 150ms ease);
47
+ --transition-normal: var(--fui-transition-normal, 200ms ease);
88
48
  }
89
49
 
90
50
  /* ============================================
@@ -105,6 +65,7 @@ html {
105
65
  }
106
66
 
107
67
  body {
68
+ font-family: var(--fui-font-sans, 'Geist Sans', Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
108
69
  background-color: var(--bg-primary);
109
70
  color: var(--text-primary);
110
71
  -webkit-font-smoothing: antialiased;