Material Design 3 Theming in Jetpack Compose: The Complete Guide
Material Design 3 represents the latest evolution of Google's design system, bringing modern aesthetics and powerful theming capabilities to Android applications. When combined with Jetpack Compose, Material Design 3 offers developers an elegant, declarative way to build beautiful, consistent user interfaces. In this comprehensive guide, we'll explore every aspect of Material Design 3 theming in Jetpack Compose, from basic color schemes to advanced dynamic color implementation.
Understanding Material Design 3 Color System
Material Design 3 introduces a refined color system based on the principles of the CAM16 color space, which better aligns with human perception. The primary components of the color system are:
Primary Color
The primary color is the most prominent color in your app's color scheme. It typically represents your brand and is used for key interactive elements like buttons, links, and navigation items.
// In your Color.kt file
val md_theme_light_primary = Color(0xFF006874)
val md_theme_dark_primary = Color(0xFF4FD8EB)
Secondary Color
The secondary color provides an accent that complements your primary color. It's often used for secondary interactive elements and helps create visual hierarchy.
val md_theme_light_secondary = Color(0xFF4A6363)
val md_theme_dark_secondary = Color(0xFFB1CCCC)
Tertiary Color
The tertiary color is an alternative accent color that provides even more flexibility in your design system. It's useful for highlighting less critical information or providing additional visual variety.
val md_theme_light_tertiary = Color(0xFF006B5E)
val md_theme_dark_tertiary = Color(0xFF52D9C4)
Setting Up Your Color Scheme
A complete Material Design 3 color scheme includes far more than just primary, secondary, and tertiary colors. It also includes neutral colors, container variants, and on-color variants for text and icons.
// Complete theme setup
data class ColorScheme(
val primary: Color,
val onPrimary: Color,
val primaryContainer: Color,
val onPrimaryContainer: Color,
val secondary: Color,
val onSecondary: Color,
val secondaryContainer: Color,
val onSecondaryContainer: Color,
val tertiary: Color,
val onTertiary: Color,
val tertiaryContainer: Color,
val onTertiaryContainer: Color,
val error: Color,
val onError: Color,
val errorContainer: Color,
val onErrorContainer: Color,
val background: Color,
val onBackground: Color,
val surface: Color,
val onSurface: Color,
val surfaceVariant: Color,
val onSurfaceVariant: Color,
val outline: Color,
val outlineVariant: Color,
val scrim: Color
)
Implementing Dark Mode Support
Dark mode is not just a cosmetic feature—it's essential for modern app development. Material Design 3 provides separate color schemes for light and dark modes, allowing your app to adapt seamlessly based on system preferences.
private val LightColorScheme = lightColorScheme(
primary = md_theme_light_primary,
secondary = md_theme_light_secondary,
tertiary = md_theme_light_tertiary,
background = md_theme_light_background,
surface = md_theme_light_surface,
onBackground = md_theme_light_onBackground,
onSurface = md_theme_light_onSurface,
// ... other colors
)
private val DarkColorScheme = darkColorScheme(
primary = md_theme_dark_primary,
secondary = md_theme_dark_secondary,
tertiary = md_theme_dark_tertiary,
background = md_theme_dark_background,
surface = md_theme_dark_surface,
onBackground = md_theme_dark_onBackground,
onSurface = md_theme_dark_onSurface,
// ... other colors
)
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}
Dynamic Color on Android 12+
Android 12 introduced dynamic theming, which extracts colors from the user's wallpaper to create a personalized color scheme. Material Design 3 in Jetpack Compose provides seamless support for this feature.
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}
When users enable dynamic color in their system settings, their device's wallpaper colors automatically influence your app's appearance. This creates a personalized, harmonious experience without requiring any additional work from developers.
Typography System
Material Design 3 defines a comprehensive typography system with multiple scales for different text purposes.
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W400,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = 0.sp
),
displayMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W400,
fontSize = 45.sp,
lineHeight = 52.sp,
letterSpacing = 0.sp
),
displaySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W400,
fontSize = 36.sp,
lineHeight = 44.sp,
letterSpacing = 0.sp
),
headlineLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.W400,
fontSize = 32.sp,
lineHeight = 40.sp,
letterSpacing = 0.sp
),
// ... more styles for headlineMedium, headlineSmall, titleLarge, titleMedium, titleSmall, bodyLarge, bodyMedium, bodySmall, labelLarge, labelMedium, labelSmall
)
Shape System
The shape system in Material Design 3 uses a corner family system that allows you to define rounded corners consistently across your app.
val Shapes = Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp),
extraLarge = RoundedCornerShape(28.dp)
)
These shapes are used automatically by Material 3 components like buttons, cards, and dialog boxes, ensuring visual consistency throughout your application.
Using Material Theme Builder
The Material Theme Builder tool is an invaluable resource for creating custom Material Design 3 color schemes. Available at https://material-foundation.github.io/material-theme-builder/, this tool allows you to:
- Select a seed color - Choose a brand color that serves as the foundation for your entire color scheme
- Preview your theme - See how your colors look across various Material Design components
- Generate code - Export Kotlin or Compose code for your custom theme
- Adjust for accessibility - Ensure sufficient contrast ratios for text readability
Using the Material Theme Builder:
- Visit the web tool and select your primary brand color
- The tool automatically generates complementary primary, secondary, and tertiary colors
- Toggle between light and dark mode previews
- Copy the generated Kotlin code directly into your project
- Fine-tune colors if needed
Complete Theme Implementation Example
Here's a complete, production-ready theme implementation:
// Theme.kt
package com.example.myapp.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
// Color definitions
private val md_theme_light_primary = Color(0xFF006874)
private val md_theme_light_secondary = Color(0xFF4A6363)
private val md_theme_light_tertiary = Color(0xFF006B5E)
private val md_theme_dark_primary = Color(0xFF4FD8EB)
private val md_theme_dark_secondary = Color(0xFFB1CCCC)
private val md_theme_dark_tertiary = Color(0xFF52D9C4)
private val LightColorScheme = lightColorScheme(
primary = md_theme_light_primary,
secondary = md_theme_light_secondary,
tertiary = md_theme_light_tertiary,
)
private val DarkColorScheme = darkColorScheme(
primary = md_theme_dark_primary,
secondary = md_theme_dark_secondary,
tertiary = md_theme_dark_tertiary,
)
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}
Best Practices for Material Design 3 Theming
1. Consistency is Key
Use Material Theme Builder to generate your entire color scheme from a seed color. This ensures mathematical consistency across all colors.
2. Test Accessibility
Always verify that your color combinations meet WCAG AA standards for contrast ratios. Material Theme Builder includes accessibility checks.
3. Respect User Preferences
Always support dark mode and enable dynamic color when targeting Android 12+. Users appreciate apps that respect their system settings.
4. Use Semantic Colors
Instead of hardcoding colors in components, use the semantic color names from your theme (primary, secondary, tertiary, error, etc.).
5. Container Variants for Subtle Hierarchy
Use the container color variants (primaryContainer, secondaryContainer, etc.) for backgrounds of less emphasized elements.
Conclusion
Material Design 3 theming in Jetpack Compose represents the modern standard for Android app design. By mastering the color system, typography, shapes, and dynamic color support, you can create visually stunning and accessible applications that users love to interact with.
The combination of Material Theme Builder for design and Jetpack Compose for implementation makes it easier than ever to create consistent, beautiful Android apps. Start with a seed color in Material Theme Builder, generate your code, and leverage Compose's declarative nature to build interfaces that truly shine.
All 8 templates use Material3. https://myougatheax.gumroad.com
Top comments (0)