create-rn-starter-kit
Version:
Interactive CLI for creating modular React Native apps with Expo
291 lines (279 loc) • 8.18 kB
text/typescript
// template/src/theme/tokenProcessor.ts
// Import all brand tokens statically
import lloydsTokens from './brands/lloyds.json';
import brandATokens from './brands/brandA.json';
import brandBTokens from './brands/brandB.json';
// Type definitions for token structure
interface TokenValue {
value: string;
type: string;
}
interface BrandTokens {
global: {
colors: {
primary: { [key: string]: TokenValue };
neutral: { [key: string]: TokenValue };
accent: { [key: string]: TokenValue };
semantic: {
error: TokenValue;
success: TokenValue;
warning: TokenValue;
};
};
spacing: { [key: string]: TokenValue };
borderRadius: { [key: string]: TokenValue };
fontSize: { [key: string]: TokenValue };
fontFamily: { [key: string]: TokenValue };
};
light: {
colors: { [key: string]: TokenValue };
};
dark: {
colors: { [key: string]: TokenValue };
};
}
// Type for the processed theme
export interface ProcessedTheme {
colors: {
primary: {
50: string;
100: string;
500: string;
600: string;
900: string;
};
neutral: {
50: string;
100: string;
200: string;
500: string;
900: string;
};
semantic: {
error: string;
success: string;
warning: string;
};
background: string;
surface: string;
text: string;
textSecondary: string;
border: string;
accent: string;
};
spacing: {
xs: number;
sm: number;
md: number;
lg: number;
xl: number;
};
borderRadius: {
sm: number;
md: number;
lg: number;
};
fontSize: {
sm: number;
base: number;
lg: number;
xl: number;
};
fontFamily: {
primary: string;
primaryBold: string;
primarySemiBold: string;
secondary: string;
};
textStyles: {
// Headings
heading: {
fontSize: number;
fontFamily: string;
};
subheading: {
fontSize: number;
fontFamily: string;
};
// Body text
body: {
fontSize: number;
fontFamily: string;
};
bodyLarge: {
fontSize: number;
fontFamily: string;
};
bodySmall: {
fontSize: number;
fontFamily: string;
};
// Labels and captions
caption: {
fontSize: number;
fontFamily: string;
};
label: {
fontSize: number;
fontFamily: string;
};
labelSmall: {
fontSize: number;
fontFamily: string;
};
// Financial/numbers
amount: {
fontSize: number;
fontFamily: string;
};
amountLarge: {
fontSize: number;
fontFamily: string;
};
// Account numbers and codes
accountNumber: {
fontSize: number;
fontFamily: string;
};
}
}
// Available brands
export type BrandName = 'lloyds' | 'brandA' | 'brandB';
// Function to get brand tokens synchronously
function getBrandTokens(brandName: BrandName): BrandTokens {
switch (brandName) {
case 'brandA':
return brandATokens as BrandTokens;
case 'brandB':
return brandBTokens as BrandTokens;
case 'lloyds':
default:
return lloydsTokens as BrandTokens;
}
}
// Function to resolve token references like {global.colors.neutral.50}
function resolveTokenReference(value: string, tokenData: BrandTokens): string {
if (!value.startsWith('{') || !value.endsWith('}')) {
return value;
}
const path = value.slice(1, -1).split('.');
let current: any = tokenData;
for (const key of path) {
current = current?.[key];
if (!current) {
console.warn(`Token reference not found: ${value}`);
return value;
}
}
return current.value || current;
}
// Process tokens for a specific brand and theme (light/dark) - now using JSON files
export function processTokens(brandName: BrandName, themeName: 'light' | 'dark'): ProcessedTheme {
const tokens = getBrandTokens(brandName);
const globalTokens = tokens.global;
const themeTokens = tokens[themeName];
return {
colors: {
primary: {
50: globalTokens.colors.primary['50'].value,
100: globalTokens.colors.primary['100'].value,
500: globalTokens.colors.primary['500'].value,
600: globalTokens.colors.primary['600'].value,
900: globalTokens.colors.primary['900'].value,
},
neutral: {
50: globalTokens.colors.neutral['50'].value,
100: globalTokens.colors.neutral['100'].value,
200: globalTokens.colors.neutral['200'].value,
500: globalTokens.colors.neutral['500'].value,
900: globalTokens.colors.neutral['900'].value,
},
semantic: {
error: globalTokens.colors.semantic.error.value,
success: globalTokens.colors.semantic.success.value,
warning: globalTokens.colors.semantic.warning.value,
},
background: resolveTokenReference(themeTokens.colors.background.value, tokens),
surface: resolveTokenReference(themeTokens.colors.surface.value, tokens),
text: resolveTokenReference(themeTokens.colors.text.value, tokens),
textSecondary: resolveTokenReference(themeTokens.colors.textSecondary.value, tokens),
border: resolveTokenReference(themeTokens.colors.border.value, tokens),
accent: resolveTokenReference(themeTokens.colors.accent.value, tokens),
},
spacing: {
xs: parseInt(globalTokens.spacing.xs.value),
sm: parseInt(globalTokens.spacing.sm.value),
md: parseInt(globalTokens.spacing.md.value),
lg: parseInt(globalTokens.spacing.lg.value),
xl: parseInt(globalTokens.spacing.xl.value),
},
borderRadius: {
sm: parseInt(globalTokens.borderRadius.sm.value),
md: parseInt(globalTokens.borderRadius.md.value),
lg: parseInt(globalTokens.borderRadius.lg.value),
},
fontSize: {
sm: parseInt(globalTokens.fontSize.sm.value),
base: parseInt(globalTokens.fontSize.base.value),
lg: parseInt(globalTokens.fontSize.lg.value),
xl: parseInt(globalTokens.fontSize.xl.value),
},
fontFamily: {
primary: globalTokens.fontFamily.primary.value,
primaryBold: globalTokens.fontFamily.primaryBold.value,
primarySemiBold: globalTokens.fontFamily.primarySemiBold.value,
secondary: globalTokens.fontFamily.secondary.value,
},
textStyles: {
// Headings
heading: {
fontSize: parseInt(globalTokens.fontSize.xl.value), // ~20px
fontFamily: globalTokens.fontFamily.primaryBold.value,
},
subheading: {
fontSize: parseInt(globalTokens.fontSize.lg.value), // ~18px
fontFamily: globalTokens.fontFamily.primarySemiBold.value,
},
// Body text variations
body: {
fontSize: parseInt(globalTokens.fontSize.base.value), // ~16px
fontFamily: globalTokens.fontFamily.secondary.value,
},
bodyLarge: {
fontSize: parseInt(globalTokens.fontSize.lg.value), // ~18px
fontFamily: globalTokens.fontFamily.secondary.value,
},
bodySmall: {
fontSize: 14, // Between sm and base
fontFamily: globalTokens.fontFamily.secondary.value,
},
// Labels and captions
caption: {
fontSize: parseInt(globalTokens.fontSize.sm.value), // ~12px
fontFamily: globalTokens.fontFamily.secondary.value,
},
label: {
fontSize: parseInt(globalTokens.fontSize.base.value), // ~16px
fontFamily: globalTokens.fontFamily.primarySemiBold.value,
},
labelSmall: {
fontSize: 14, // ~14px
fontFamily: globalTokens.fontFamily.primarySemiBold.value,
},
// Financial amounts
amount: {
fontSize: parseInt(globalTokens.fontSize.lg.value), // ~18px
fontFamily: globalTokens.fontFamily.primaryBold.value,
},
amountLarge: {
fontSize: 28, // Large amounts like £935.68
fontFamily: globalTokens.fontFamily.primaryBold.value,
},
// Account numbers
accountNumber: {
fontSize: parseInt(globalTokens.fontSize.sm.value), // ~12px
fontFamily: globalTokens.fontFamily.secondary.value,
},
}
};
}