@metacells/mcellui-core 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.
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Typography Tokens
3
+ *
4
+ * Consistent typography scale for all text elements.
5
+ * Based on iOS Human Interface Guidelines with
6
+ * consideration for React Native defaults.
7
+ *
8
+ * Supports Geist font family when loaded, falls back to system fonts.
9
+ *
10
+ * Usage:
11
+ * ```tsx
12
+ * const { typography, fontFamily } = useTheme();
13
+ * <Text style={typography.body} />
14
+ * <Text style={{ fontFamily: fontFamily.sans, fontSize: fontSize.lg }} />
15
+ * ```
16
+ */
17
+
18
+ import { TextStyle, Platform } from 'react-native';
19
+
20
+ // =============================================================================
21
+ // Font Sizes
22
+ // =============================================================================
23
+
24
+ export const fontSize = {
25
+ /** 10px - fine print */
26
+ '2xs': 10,
27
+ /** 11px - captions, labels */
28
+ xs: 11,
29
+ /** 12px - small text, badges */
30
+ sm: 12,
31
+ /** 14px - secondary text */
32
+ base: 14,
33
+ /** 16px - body text (default) */
34
+ md: 16,
35
+ /** 18px - emphasized body */
36
+ lg: 18,
37
+ /** 20px - small headings */
38
+ xl: 20,
39
+ /** 24px - section headings */
40
+ '2xl': 24,
41
+ /** 30px - page headings */
42
+ '3xl': 30,
43
+ /** 36px - large headings */
44
+ '4xl': 36,
45
+ /** 48px - display */
46
+ '5xl': 48,
47
+ } as const;
48
+
49
+ export type FontSizeKey = keyof typeof fontSize;
50
+
51
+ // =============================================================================
52
+ // Font Weights
53
+ // =============================================================================
54
+
55
+ export const fontWeight = {
56
+ /** 100 */
57
+ thin: '100' as const,
58
+ /** 200 */
59
+ extralight: '200' as const,
60
+ /** 300 */
61
+ light: '300' as const,
62
+ /** 400 - body text */
63
+ normal: '400' as const,
64
+ /** 500 - slightly emphasized */
65
+ medium: '500' as const,
66
+ /** 600 - buttons, labels */
67
+ semibold: '600' as const,
68
+ /** 700 - headings */
69
+ bold: '700' as const,
70
+ /** 800 */
71
+ extrabold: '800' as const,
72
+ /** 900 */
73
+ black: '900' as const,
74
+ } as const;
75
+
76
+ export type FontWeightKey = keyof typeof fontWeight;
77
+
78
+ // =============================================================================
79
+ // Line Heights
80
+ // =============================================================================
81
+
82
+ export const lineHeight = {
83
+ /** Tight: 1.0 */
84
+ none: 1,
85
+ /** Tight: 1.25 */
86
+ tight: 1.25,
87
+ /** Snug: 1.375 */
88
+ snug: 1.375,
89
+ /** Normal: 1.5 (default) */
90
+ normal: 1.5,
91
+ /** Relaxed: 1.625 */
92
+ relaxed: 1.625,
93
+ /** Loose: 2.0 */
94
+ loose: 2,
95
+ } as const;
96
+
97
+ export type LineHeightKey = keyof typeof lineHeight;
98
+
99
+ // =============================================================================
100
+ // Letter Spacing
101
+ // =============================================================================
102
+
103
+ export const letterSpacing = {
104
+ tighter: -0.8,
105
+ tight: -0.4,
106
+ normal: 0,
107
+ wide: 0.4,
108
+ wider: 0.8,
109
+ widest: 1.6,
110
+ } as const;
111
+
112
+ export type LetterSpacingKey = keyof typeof letterSpacing;
113
+
114
+ // =============================================================================
115
+ // Font Family
116
+ // =============================================================================
117
+
118
+ /**
119
+ * System font fallbacks for when custom fonts aren't loaded.
120
+ */
121
+ export const systemFonts = {
122
+ /** Sans-serif system font */
123
+ sans: Platform.select({
124
+ ios: 'System',
125
+ android: 'Roboto',
126
+ default: 'System',
127
+ }) as string,
128
+ /** Monospace system font */
129
+ mono: Platform.select({
130
+ ios: 'Menlo',
131
+ android: 'monospace',
132
+ default: 'monospace',
133
+ }) as string,
134
+ } as const;
135
+
136
+ /**
137
+ * Semantic font tokens.
138
+ *
139
+ * Three fonts cover all use cases:
140
+ * - `sans`: Body text, UI elements (default)
141
+ * - `heading`: Headlines h1-h4 (can be display/serif font)
142
+ * - `mono`: Code blocks, monospace text
143
+ *
144
+ * @example
145
+ * ```tsx
146
+ * // Use defaults (Geist)
147
+ * <ThemeProvider>
148
+ *
149
+ * // Custom fonts
150
+ * <ThemeProvider fonts={{
151
+ * sans: 'Inter_400Regular',
152
+ * heading: 'PlayfairDisplay_700Bold',
153
+ * mono: 'FiraCode_400Regular',
154
+ * }}>
155
+ * ```
156
+ */
157
+ export interface Fonts {
158
+ /** Body text, UI elements */
159
+ sans: string;
160
+ /** Headlines (h1-h4) - defaults to bold variant of sans */
161
+ heading: string;
162
+ /** Code, monospace text */
163
+ mono: string;
164
+ }
165
+
166
+ /**
167
+ * Default fonts using Geist font family.
168
+ * Used when no custom fonts are provided to ThemeProvider.
169
+ */
170
+ export const defaultFonts: Fonts = {
171
+ sans: 'Geist_400Regular',
172
+ heading: 'Geist_700Bold',
173
+ mono: 'GeistMono_400Regular',
174
+ };
175
+
176
+ /**
177
+ * Legacy fontFamily export for backwards compatibility.
178
+ * @deprecated Use `fonts` from useTheme() instead
179
+ */
180
+ export const fontFamily = {
181
+ /** Sans-serif: Geist or system default */
182
+ sans: systemFonts.sans,
183
+ /** Monospace: Geist Mono or system default */
184
+ mono: systemFonts.mono,
185
+ /** System font - always available */
186
+ system: systemFonts.sans,
187
+ } as const;
188
+
189
+ /**
190
+ * Font family with weight suffix for Geist.
191
+ * Use these when Geist fonts are loaded.
192
+ */
193
+ export const geistFontFamily = {
194
+ // Sans variants
195
+ 'sans-thin': 'Geist_100Thin',
196
+ 'sans-extralight': 'Geist_200ExtraLight',
197
+ 'sans-light': 'Geist_300Light',
198
+ 'sans-regular': 'Geist_400Regular',
199
+ 'sans-medium': 'Geist_500Medium',
200
+ 'sans-semibold': 'Geist_600SemiBold',
201
+ 'sans-bold': 'Geist_700Bold',
202
+ 'sans-extrabold': 'Geist_800ExtraBold',
203
+ 'sans-black': 'Geist_900Black',
204
+ // Mono variants
205
+ 'mono-thin': 'GeistMono_100Thin',
206
+ 'mono-extralight': 'GeistMono_200ExtraLight',
207
+ 'mono-light': 'GeistMono_300Light',
208
+ 'mono-regular': 'GeistMono_400Regular',
209
+ 'mono-medium': 'GeistMono_500Medium',
210
+ 'mono-semibold': 'GeistMono_600SemiBold',
211
+ 'mono-bold': 'GeistMono_700Bold',
212
+ 'mono-extrabold': 'GeistMono_800ExtraBold',
213
+ 'mono-black': 'GeistMono_900Black',
214
+ } as const;
215
+
216
+ export type FontFamilyKey = keyof typeof fontFamily;
217
+ export type GeistFontFamilyKey = keyof typeof geistFontFamily;
218
+
219
+ // =============================================================================
220
+ // Typography Presets
221
+ // =============================================================================
222
+
223
+ /**
224
+ * Typography preset styles type.
225
+ */
226
+ export interface Typography {
227
+ display: TextStyle;
228
+ h1: TextStyle;
229
+ h2: TextStyle;
230
+ h3: TextStyle;
231
+ h4: TextStyle;
232
+ bodyLg: TextStyle;
233
+ body: TextStyle;
234
+ bodySm: TextStyle;
235
+ label: TextStyle;
236
+ labelSm: TextStyle;
237
+ button: TextStyle;
238
+ buttonSm: TextStyle;
239
+ buttonLg: TextStyle;
240
+ caption: TextStyle;
241
+ overline: TextStyle;
242
+ badge: TextStyle;
243
+ code: TextStyle;
244
+ }
245
+
246
+ export type TypographyKey = keyof Typography;
247
+
248
+ /**
249
+ * Creates typography presets with the given fonts.
250
+ * Used internally by ThemeProvider to generate typography styles.
251
+ */
252
+ export function createTypography(fonts: Fonts): Typography {
253
+ return {
254
+ // Display/Hero
255
+ display: {
256
+ fontFamily: fonts.heading,
257
+ fontSize: fontSize['5xl'],
258
+ fontWeight: fontWeight.bold,
259
+ lineHeight: fontSize['5xl'] * lineHeight.tight,
260
+ letterSpacing: letterSpacing.tight,
261
+ },
262
+
263
+ // Headings - use heading font
264
+ h1: {
265
+ fontFamily: fonts.heading,
266
+ fontSize: fontSize['3xl'],
267
+ fontWeight: fontWeight.bold,
268
+ lineHeight: fontSize['3xl'] * lineHeight.tight,
269
+ letterSpacing: letterSpacing.tight,
270
+ },
271
+
272
+ h2: {
273
+ fontFamily: fonts.heading,
274
+ fontSize: fontSize['2xl'],
275
+ fontWeight: fontWeight.bold,
276
+ lineHeight: fontSize['2xl'] * lineHeight.tight,
277
+ },
278
+
279
+ h3: {
280
+ fontFamily: fonts.heading,
281
+ fontSize: fontSize.xl,
282
+ fontWeight: fontWeight.semibold,
283
+ lineHeight: fontSize.xl * lineHeight.snug,
284
+ },
285
+
286
+ h4: {
287
+ fontFamily: fonts.heading,
288
+ fontSize: fontSize.lg,
289
+ fontWeight: fontWeight.semibold,
290
+ lineHeight: fontSize.lg * lineHeight.snug,
291
+ },
292
+
293
+ // Body - use sans font
294
+ bodyLg: {
295
+ fontFamily: fonts.sans,
296
+ fontSize: fontSize.lg,
297
+ fontWeight: fontWeight.normal,
298
+ lineHeight: fontSize.lg * lineHeight.normal,
299
+ },
300
+
301
+ body: {
302
+ fontFamily: fonts.sans,
303
+ fontSize: fontSize.md,
304
+ fontWeight: fontWeight.normal,
305
+ lineHeight: fontSize.md * lineHeight.normal,
306
+ },
307
+
308
+ bodySm: {
309
+ fontFamily: fonts.sans,
310
+ fontSize: fontSize.base,
311
+ fontWeight: fontWeight.normal,
312
+ lineHeight: fontSize.base * lineHeight.normal,
313
+ },
314
+
315
+ // UI Text - use sans font
316
+ label: {
317
+ fontFamily: fonts.sans,
318
+ fontSize: fontSize.base,
319
+ fontWeight: fontWeight.medium,
320
+ lineHeight: fontSize.base * lineHeight.snug,
321
+ },
322
+
323
+ labelSm: {
324
+ fontFamily: fonts.sans,
325
+ fontSize: fontSize.sm,
326
+ fontWeight: fontWeight.medium,
327
+ lineHeight: fontSize.sm * lineHeight.snug,
328
+ },
329
+
330
+ button: {
331
+ fontFamily: fonts.sans,
332
+ fontSize: fontSize.md,
333
+ fontWeight: fontWeight.semibold,
334
+ lineHeight: fontSize.md * lineHeight.none,
335
+ },
336
+
337
+ buttonSm: {
338
+ fontFamily: fonts.sans,
339
+ fontSize: fontSize.base,
340
+ fontWeight: fontWeight.semibold,
341
+ lineHeight: fontSize.base * lineHeight.none,
342
+ },
343
+
344
+ buttonLg: {
345
+ fontFamily: fonts.sans,
346
+ fontSize: fontSize.lg,
347
+ fontWeight: fontWeight.semibold,
348
+ lineHeight: fontSize.lg * lineHeight.none,
349
+ },
350
+
351
+ // Small text - use sans font
352
+ caption: {
353
+ fontFamily: fonts.sans,
354
+ fontSize: fontSize.xs,
355
+ fontWeight: fontWeight.normal,
356
+ lineHeight: fontSize.xs * lineHeight.normal,
357
+ },
358
+
359
+ overline: {
360
+ fontFamily: fonts.sans,
361
+ fontSize: fontSize.xs,
362
+ fontWeight: fontWeight.semibold,
363
+ lineHeight: fontSize.xs * lineHeight.normal,
364
+ letterSpacing: letterSpacing.wider,
365
+ textTransform: 'uppercase',
366
+ },
367
+
368
+ badge: {
369
+ fontFamily: fonts.sans,
370
+ fontSize: fontSize.sm,
371
+ fontWeight: fontWeight.medium,
372
+ lineHeight: fontSize.sm * lineHeight.none,
373
+ },
374
+
375
+ // Code - use mono font
376
+ code: {
377
+ fontFamily: fonts.mono,
378
+ fontSize: fontSize.sm,
379
+ fontWeight: fontWeight.normal,
380
+ lineHeight: fontSize.sm * lineHeight.normal,
381
+ },
382
+ };
383
+ }
384
+
385
+ /**
386
+ * Default typography presets using default fonts.
387
+ * @deprecated Use typography from useTheme() for font customization support
388
+ */
389
+ export const typography = createTypography(defaultFonts);
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Design Tokens: Colors
3
+ *
4
+ * Inspired by:
5
+ * - iOS 17+ Dynamic Colors
6
+ * - Arc Browser palette
7
+ * - Linear App design system
8
+ */
9
+
10
+ export const colors = {
11
+ // Neutral scale (gray)
12
+ neutral: {
13
+ 50: '#fafafa',
14
+ 100: '#f5f5f5',
15
+ 200: '#e5e5e5',
16
+ 300: '#d4d4d4',
17
+ 400: '#a3a3a3',
18
+ 500: '#737373',
19
+ 600: '#525252',
20
+ 700: '#404040',
21
+ 800: '#262626',
22
+ 900: '#171717',
23
+ 950: '#0a0a0a',
24
+ },
25
+
26
+ // Primary (customizable accent)
27
+ primary: {
28
+ 50: '#eff6ff',
29
+ 100: '#dbeafe',
30
+ 200: '#bfdbfe',
31
+ 300: '#93c5fd',
32
+ 400: '#60a5fa',
33
+ 500: '#3b82f6',
34
+ 600: '#2563eb',
35
+ 700: '#1d4ed8',
36
+ 800: '#1e40af',
37
+ 900: '#1e3a8a',
38
+ 950: '#172554',
39
+ },
40
+
41
+ // Semantic colors
42
+ success: {
43
+ light: '#4ade80',
44
+ DEFAULT: '#22c55e',
45
+ dark: '#16a34a',
46
+ },
47
+
48
+ warning: {
49
+ light: '#fbbf24',
50
+ DEFAULT: '#f59e0b',
51
+ dark: '#d97706',
52
+ },
53
+
54
+ error: {
55
+ light: '#f87171',
56
+ DEFAULT: '#ef4444',
57
+ dark: '#dc2626',
58
+ },
59
+
60
+ // Special
61
+ white: '#ffffff',
62
+ black: '#000000',
63
+ transparent: 'transparent',
64
+ } as const;
65
+
66
+ export type Colors = typeof colors;
67
+ export type ColorKey = keyof Colors;
@@ -0,0 +1,29 @@
1
+ export { colors, type Colors, type ColorKey } from './colors';
2
+ export { spacing, type Spacing, type SpacingKey } from './spacing';
3
+ export {
4
+ fontFamily,
5
+ fontSize,
6
+ fontWeight,
7
+ lineHeight,
8
+ letterSpacing,
9
+ type FontFamily,
10
+ type FontSize,
11
+ type FontWeight,
12
+ type LineHeight,
13
+ type LetterSpacing,
14
+ } from './typography';
15
+ export { radius, type Radius, type RadiusKey } from './radius';
16
+ export { shadows, type Shadows, type ShadowKey } from './shadows';
17
+
18
+ // Theme type combining all tokens
19
+ export interface Theme {
20
+ colors: typeof import('./colors').colors;
21
+ spacing: typeof import('./spacing').spacing;
22
+ fontFamily: typeof import('./typography').fontFamily;
23
+ fontSize: typeof import('./typography').fontSize;
24
+ fontWeight: typeof import('./typography').fontWeight;
25
+ lineHeight: typeof import('./typography').lineHeight;
26
+ letterSpacing: typeof import('./typography').letterSpacing;
27
+ radius: typeof import('./radius').radius;
28
+ shadows: typeof import('./shadows').shadows;
29
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Design Tokens: Border Radius
3
+ */
4
+
5
+ export const radius = {
6
+ none: 0,
7
+ sm: 4,
8
+ DEFAULT: 8,
9
+ md: 12,
10
+ lg: 16,
11
+ xl: 24,
12
+ '2xl': 32,
13
+ '3xl': 48,
14
+ full: 9999,
15
+ } as const;
16
+
17
+ export type Radius = typeof radius;
18
+ export type RadiusKey = keyof Radius;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Design Tokens: Shadows
3
+ *
4
+ * Cross-platform shadow definitions
5
+ */
6
+
7
+ import { Platform, ViewStyle } from 'react-native';
8
+
9
+ type ShadowStyle = Pick<
10
+ ViewStyle,
11
+ 'shadowColor' | 'shadowOffset' | 'shadowOpacity' | 'shadowRadius' | 'elevation'
12
+ >;
13
+
14
+ const createShadow = (
15
+ offsetY: number,
16
+ blur: number,
17
+ opacity: number,
18
+ elevation: number
19
+ ): ShadowStyle => ({
20
+ shadowColor: '#000',
21
+ shadowOffset: { width: 0, height: offsetY },
22
+ shadowOpacity: opacity,
23
+ shadowRadius: blur,
24
+ ...(Platform.OS === 'android' && { elevation }),
25
+ });
26
+
27
+ export const shadows = {
28
+ none: createShadow(0, 0, 0, 0),
29
+ sm: createShadow(1, 2, 0.05, 1),
30
+ DEFAULT: createShadow(2, 4, 0.1, 2),
31
+ md: createShadow(4, 6, 0.1, 4),
32
+ lg: createShadow(8, 10, 0.1, 8),
33
+ xl: createShadow(12, 16, 0.15, 12),
34
+ '2xl': createShadow(16, 24, 0.2, 16),
35
+ } as const;
36
+
37
+ export type Shadows = typeof shadows;
38
+ export type ShadowKey = keyof Shadows;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Design Tokens: Spacing
3
+ *
4
+ * 4px base unit system
5
+ */
6
+
7
+ export const spacing = {
8
+ 0: 0,
9
+ 0.5: 2,
10
+ 1: 4,
11
+ 1.5: 6,
12
+ 2: 8,
13
+ 2.5: 10,
14
+ 3: 12,
15
+ 3.5: 14,
16
+ 4: 16,
17
+ 5: 20,
18
+ 6: 24,
19
+ 7: 28,
20
+ 8: 32,
21
+ 9: 36,
22
+ 10: 40,
23
+ 11: 44,
24
+ 12: 48,
25
+ 14: 56,
26
+ 16: 64,
27
+ 20: 80,
28
+ 24: 96,
29
+ 28: 112,
30
+ 32: 128,
31
+ 36: 144,
32
+ 40: 160,
33
+ 44: 176,
34
+ 48: 192,
35
+ 52: 208,
36
+ 56: 224,
37
+ 60: 240,
38
+ 64: 256,
39
+ 72: 288,
40
+ 80: 320,
41
+ 96: 384,
42
+ } as const;
43
+
44
+ export type Spacing = typeof spacing;
45
+ export type SpacingKey = keyof Spacing;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Design Tokens: Typography
3
+ *
4
+ * System font stack with iOS/Android optimization
5
+ */
6
+
7
+ import { Platform } from 'react-native';
8
+
9
+ export const fontFamily = {
10
+ // System defaults (platform-optimized)
11
+ sans: Platform.select({
12
+ ios: 'System',
13
+ android: 'Roboto',
14
+ default: 'System',
15
+ }),
16
+ mono: Platform.select({
17
+ ios: 'Menlo',
18
+ android: 'monospace',
19
+ default: 'monospace',
20
+ }),
21
+ } as const;
22
+
23
+ export const fontSize = {
24
+ xs: 12,
25
+ sm: 14,
26
+ base: 16,
27
+ lg: 18,
28
+ xl: 20,
29
+ '2xl': 24,
30
+ '3xl': 30,
31
+ '4xl': 36,
32
+ '5xl': 48,
33
+ '6xl': 60,
34
+ } as const;
35
+
36
+ export const fontWeight = {
37
+ thin: '100',
38
+ extralight: '200',
39
+ light: '300',
40
+ normal: '400',
41
+ medium: '500',
42
+ semibold: '600',
43
+ bold: '700',
44
+ extrabold: '800',
45
+ black: '900',
46
+ } as const;
47
+
48
+ export const lineHeight = {
49
+ none: 1,
50
+ tight: 1.25,
51
+ snug: 1.375,
52
+ normal: 1.5,
53
+ relaxed: 1.625,
54
+ loose: 2,
55
+ } as const;
56
+
57
+ export const letterSpacing = {
58
+ tighter: -0.8,
59
+ tight: -0.4,
60
+ normal: 0,
61
+ wide: 0.4,
62
+ wider: 0.8,
63
+ widest: 1.6,
64
+ } as const;
65
+
66
+ export type FontFamily = typeof fontFamily;
67
+ export type FontSize = typeof fontSize;
68
+ export type FontWeight = typeof fontWeight;
69
+ export type LineHeight = typeof lineHeight;
70
+ export type LetterSpacing = typeof letterSpacing;