Theming & Branding
MSA Core ships with a complete design system but every part of it is overridable. You customize the look of your white-label app through a single entry point: a ThemeProvider class that you extend and pass into AppTheme.
This page covers:
- Colors — brand palette and payment scheme colors
- Typography — fonts and text styles
- Spacing, Icons, Radius, Touch Targets — adaptive sizing
- Shapes — corner radii for surfaces
- Drawables & Images — logos and icons
- Wiring It All Together — how to apply your theme
Colors
Colors are defined by the MsaColors interface. The library provides MsaColorsLight and MsaColorsDark as defaults — override whichever properties you want to rebrand.
The MsaColors interface
interface MsaColors {
val primary: Color
val primaryForeground: Color
val primaryGradient: Color
val secondary: Color
val secondaryForeground: Color
val background: Color
val foreground: Color
val muted: Color
val mutedForeground: Color
val accent: Color
val accentForeground: Color
val approval: Color // success / approved transaction
val approvalForeground: Color
val error: Color // failed / declined transaction
val errorForeground: Color
val highlightCard: Color
val input: Color // text field borders and placeholders
val ring: Color // focus rings
val statusYellow: Color // warnings / pending states
val processing: Color // in-flight transactions
val headerSurface: Color // top bar background
val headerForeground: Color // top bar text/icons
}Customizing the brand palette
Override MsaColorsLight (and optionally MsaColorsDark) with your brand colors:
@Immutable
data class MyBrandColorsLight(
override val primary: Color = Color(0xFF0066CC), // Your brand blue
override val primaryForeground: Color = Color(0xFFFFFFFF),
override val primaryGradient: Color = Color(0xFF3388DD),
override val secondary: Color = Color(0xFF1A1A1A),
override val secondaryForeground: Color = Color(0xFFFFFFFF),
override val background: Color = Color(0xFFFFFFFF),
override val foreground: Color = Color(0xFF0F172A),
override val approval: Color = Color(0xFF10B981),
override val approvalForeground: Color = Color(0xFFF0FDF4),
override val error: Color = Color(0xFFEF4444),
override val errorForeground: Color = Color(0xFFFEF2F2),
override val headerSurface: Color = Color(0xFF0066CC),
override val headerForeground: Color = Color(0xFFFFFFFF),
// ...override remaining fields
) : MsaColorsPayment scheme colors
SchemeColors holds brand colors for payment networks (Visa, Mastercard, etc.). Override this only if your brand guidelines require non-standard scheme colors.
@Immutable
data class SchemeColors(
val colorVisa: Color = Color(0xff1434CA),
val colorMastercard: Color = Color(0xffF79E1B),
val colorJcb: Color = Color(0xff40A737),
val colorUnionpay: Color = Color(0xffE0002B),
val colorAmex: Color = Color(0xff006FCF),
val colorAlipay: Color = Color(0xff1677FF),
val colorWechat: Color = Color(0xff09BB07),
val colorFps: Color = Color(0xff56BBE9),
val colorPayme: Color = Color(0xffDB0011),
val colorOctopus: Color = Color(0xffF5911E),
val colorCash: Color = Color(0xff00A590),
)Typography
Typography uses Material3's Typography class. The library defaults to the Inter font family, but you can swap it out for any font shipped as a resource in your app.
Add your font files
Drop your font files under app/src/main/res/font/, for example:
app/src/main/res/font/
├── mybrand_regular.ttf
├── mybrand_medium.ttf
├── mybrand_semibold.ttf
└── mybrand_bold.ttfDefine the typography
fun myBrandTypography(): Typography {
val brandFont = FontFamily(
Font(R.font.mybrand_regular, FontWeight.Normal),
Font(R.font.mybrand_medium, FontWeight.Medium),
Font(R.font.mybrand_semibold, FontWeight.SemiBold),
Font(R.font.mybrand_bold, FontWeight.Bold),
)
return Typography(
bodySmall = TextStyle(fontFamily = brandFont, fontSize = 16.sp, fontWeight = FontWeight.Normal),
bodyMedium = TextStyle(fontFamily = brandFont, fontSize = 16.sp, fontWeight = FontWeight.Medium),
titleMedium = TextStyle(fontFamily = brandFont, fontSize = 20.sp, fontWeight = FontWeight.SemiBold),
headlineSmall = TextStyle(fontFamily = brandFont, fontSize = 24.sp, fontWeight = FontWeight.SemiBold),
// ...define other styles
)
}| Style | Typical Use |
|---|---|
labelSmall / labelMedium / labelLarge | Buttons, captions, tiny labels |
bodySmall / bodyMedium / bodyLarge | Body text, form inputs |
titleSmall / titleMedium / titleLarge | Section headers |
headlineSmall | Screen titles |
Spacing, Icons, Radius, Touch Targets
MSA Core uses an adaptive sizing system — values scale based on window width (Compact / Medium / Expanded). You rarely need to override these, but you can.
The interfaces
interface Spacing {
val xs3: Dp // 2dp
val xs2: Dp // 4dp
val xs: Dp // 8dp
val sm: Dp // 12dp
val md: Dp // 16dp
val lg: Dp // 24dp
val xl: Dp // 32dp
val xl2: Dp // 48dp
val xl3: Dp // 64dp
val xl4: Dp // 80dp
val xl5: Dp // 96dp
}
interface IconSize {
val xs2: Dp // 12dp
val xs: Dp // 16dp
val sm: Dp // 20dp
val md: Dp // 24dp
val lg: Dp // 28dp
val xl: Dp // 36dp
val xl2: Dp // 48dp
val xl3: Dp // 64dp
}
interface Radius {
val xs: Dp // 2dp
val sm: Dp // 4dp
val md: Dp // 6dp
val lg: Dp // 8dp
val xl: Dp // 12dp
val xl2: Dp // 16dp
}
interface MinTouchSize {
val sm: Dp // 40dp
val md: Dp // 56dp
val lg: Dp // 60dp
val xl: Dp // 72dp
val xl2: Dp // 88dp
val xl3: Dp // 100dp
}Adaptive scaling
Values automatically scale by 1.0× on Compact, 1.2× on Medium, and 1.4× on Expanded windows. If you want different scaling for your brand, override the provider methods in your ThemeProvider.
Accessing in your UI
Column(
modifier = Modifier.padding(MsaTheme.spacing.md)
) {
Icon(
imageVector = Icons.Default.CheckCircle,
modifier = Modifier.size(MsaTheme.iconSize.lg)
)
}Shapes
Shapes control rounded corners for surfaces (buttons, cards, text fields).
@Composable
fun myBrandShapes(): Shapes {
return Shapes(
extraSmall = RoundedCornerShape(2.dp),
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(12.dp),
extraLarge = RoundedCornerShape(20.dp),
)
}Access in UI:
Box(
modifier = Modifier
.clip(MsaTheme.shapes.large)
.background(MsaTheme.colors.primary)
)Drawables & Images
MSA Core expects certain drawable resources to exist in your host app. To rebrand, drop your images into app/src/main/res/drawable/ using the same resource names.
Commonly customized drawables
| Resource Name | Purpose |
|---|---|
ic_launcher / ic_launcher_round | App icon on the launcher |
fingerprint_icon_button | Biometric login button |
ic_bio_face_icon / ic_bio_login | Biometric enrollment dialog |
ms_activation_qr | QR scan icon on login |
| Your brand logo | Used in splash, landing, receipts |
Wiring It All Together
Bundle your customizations into a single ThemeProvider. Override only the methods you care about — defaults are used otherwise.
1. Create your ThemeProvider
class MyBrandThemeProvider : ThemeProvider() {
@ReadOnlyComposable
override fun provideMsaColors(darkTheme: Boolean): MsaColors {
return if (darkTheme) MyBrandColorsDark() else MyBrandColorsLight()
}
@ReadOnlyComposable
override fun provideSchemeColors(): SchemeColors = SchemeColors(
colorVisa = Color(0xff1434CA),
// ...your overrides
)
@Composable
override fun provideTypography(): Typography = myBrandTypography()
@Composable
override fun provideShapes(): Shapes = myBrandShapes()
// Spacing, IconSize, Radius, MinTouchSize all have sensible adaptive defaults —
// only override if you need custom sizing.
}2. Apply it in your App entry point
Wrap App() with AppTheme and pass your provider:
class MainActivity : BaseMsaActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme(themeProvider = MyBrandThemeProvider()) {
App(screenProviders = providers)
}
}
}
override fun provideAdditionalKoinModules(): List<Module> {
return listOf(provideNFCManager())
}
}3. Use the theme in your screens
From anywhere inside AppTheme, access tokens via the MsaTheme object:
Text(
text = "Welcome",
color = MsaTheme.colors.foreground,
style = MsaTheme.typography.headlineSmall,
modifier = Modifier.padding(MsaTheme.spacing.md)
)Next Steps
- Screen Providers Reference — All available screens and their State/Action classes
- Example project (opens in a new tab) — See
ui/theme/for a complete theme implementation