Skip to main content
TecMeli implements Material Design 3 theming with support for light/dark modes and dynamic colors on Android 12+.

Overview

The theming system consists of three main components:
  1. Color.kt - Color palette definitions
  2. Theme.kt - Theme composition and color schemes
  3. Type.kt - Typography system
The app automatically adapts to system theme preferences and supports dynamic color extraction on Android 12+.

Color System

Colors are defined following Material Design 3 specifications with separate palettes for light and dark themes.

Color Palette

package com.alcalist.tecmeli.ui.theme

import androidx.compose.ui.graphics.Color

// Dark Theme Colors
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)

// Light Theme Colors
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

Color Roles

Primary

Main brand color used for key components

Secondary

Accent color for less prominent components

Tertiary

Additional accent for complementary elements

Color Breakdown

Color VariableHex CodeThemeRole
Purple80#D0BCFFDarkPrimary
PurpleGrey80#CCC2DCDarkSecondary
Pink80#EFB8C8DarkTertiary
Purple40#6650a4LightPrimary
PurpleGrey40#625b71LightSecondary
Pink40#7D5260LightTertiary
The “80” suffix indicates high luminance for dark theme, while “40” indicates lower luminance for light theme, following Material Design 3 naming conventions.

Theme Implementation

The main theme composable manages color schemes and applies them throughout the app.

TECMELITheme Composable

@Composable
fun TECMELITheme(
    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,
        content = content
    )
}

Color Scheme Definitions

private val DarkColorScheme = darkColorScheme(
    primary = Purple80,
    secondary = PurpleGrey80,
    tertiary = Pink80
)

Theme Parameters

  • Default: isSystemInDarkTheme()
  • Determines whether to use dark or light color scheme
  • Automatically follows system preference
  • Can be overridden for manual control
  • Default: true
  • Enables Material You dynamic color on Android 12+
  • Extracts colors from wallpaper
  • Falls back to static colors on older Android versions
  • The app content wrapped by the theme
  • Receives the active color scheme and typography
  • All child composables inherit theme values

Dynamic Color Support

Android 12+ supports dynamic color extraction from the user’s wallpaper.

How It Works

1

Check Android Version

Verify device is running Android 12 (S) or higher
2

Extract Colors

Use dynamicDarkColorScheme() or dynamicLightColorScheme() from Material3
3

Apply Scheme

MaterialTheme automatically applies the dynamic colors
4

Fallback

On older devices, use predefined DarkColorScheme or LightColorScheme
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
}
Dynamic color is enabled by default. Set dynamicColor = false to use static colors on all devices.

Typography

The app uses Material Design 3 typography with customizable text styles.

Typography Definition

package com.alcalist.tecmeli.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

val Typography = Typography(
    bodyLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.5.sp
    )
    /* Additional text styles:
    titleLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 22.sp,
        lineHeight = 28.sp,
        letterSpacing = 0.sp
    ),
    labelSmall = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Medium,
        fontSize = 11.sp,
        lineHeight = 16.sp,
        letterSpacing = 0.5.sp
    )
    */
)

Text Style Properties

PropertyValuePurpose
fontFamilyFontFamily.DefaultUses system default font
fontWeightFontWeight.NormalRegular weight for body text
fontSize16.spComfortable reading size
lineHeight24.spOptimal spacing for readability
letterSpacing0.5.spSubtle spacing for clarity

Material3 Typography Scale

Display Styles

  • displayLarge (57sp)
  • displayMedium (45sp)
  • displaySmall (36sp)

Headline Styles

  • headlineLarge (32sp)
  • headlineMedium (28sp)
  • headlineSmall (24sp)

Title Styles

  • titleLarge (22sp)
  • titleMedium (16sp)
  • titleSmall (14sp)

Body Styles

  • bodyLarge (16sp)
  • bodyMedium (14sp)
  • bodySmall (12sp)

Label Styles

  • labelLarge (14sp)
  • labelMedium (12sp)
  • labelSmall (11sp)

Usage Examples

Applying the Theme

import com.alcalist.tecmeli.ui.theme.TECMELITheme

@Composable
fun MyApp() {
    TECMELITheme {
        // Your app content
        NavigationWrapper()
    }
}

Using Theme Colors

@Composable
fun MyComponent() {
    Surface(
        color = MaterialTheme.colorScheme.primary
    ) {
        Text(
            text = "Hello TecMeli",
            color = MaterialTheme.colorScheme.onPrimary
        )
    }
}

Accessing Color Scheme

val primary = MaterialTheme.colorScheme.primary
val onPrimary = MaterialTheme.colorScheme.onPrimary
val primaryContainer = MaterialTheme.colorScheme.primaryContainer

Using Typography

@Composable
fun TextExample() {
    Column {
        Text(
            text = "Title",
            style = MaterialTheme.typography.titleLarge
        )
        
        Text(
            text = "Body text content",
            style = MaterialTheme.typography.bodyLarge
        )
        
        Text(
            text = "Small label",
            style = MaterialTheme.typography.labelSmall
        )
    }
}

Customization

Adding Custom Colors

// In Color.kt
val CustomBlue = Color(0xFF1976D2)
val CustomGreen = Color(0xFF388E3C)

// In Theme.kt
private val LightColorScheme = lightColorScheme(
    primary = Purple40,
    secondary = PurpleGrey40,
    tertiary = Pink40,
    background = Color(0xFFFFFBFE),
    surface = Color(0xFFFFFBFE)
)

Custom Typography

import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily

val CustomFontFamily = FontFamily(
    Font(R.font.custom_regular, FontWeight.Normal),
    Font(R.font.custom_bold, FontWeight.Bold)
)

val Typography = Typography(
    bodyLarge = TextStyle(
        fontFamily = CustomFontFamily,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.5.sp
    )
)

Disabling Dynamic Color

@Composable
fun MyApp() {
    TECMELITheme(
        dynamicColor = false  // Always use static colors
    ) {
        NavigationWrapper()
    }
}

Theme Components in Use

HomeScreen Example

@Composable
fun HomeScreen() {
    Scaffold { paddingValues ->
        Column(modifier = Modifier.padding(paddingValues)) {
            Text(
                text = "Search Results",
                style = MaterialTheme.typography.titleLarge,
                color = MaterialTheme.colorScheme.onSurface
            )
            
            ProductList(products = products)
        }
    }
}

Error State Example

@Composable
fun ErrorMessage(message: String) {
    Text(
        text = message,
        style = MaterialTheme.typography.bodyMedium,
        color = MaterialTheme.colorScheme.error
    )
}

Best Practices

Always use MaterialTheme.colorScheme rather than hardcoded colors:
// ✓ Good
color = MaterialTheme.colorScheme.primary

// ✗ Avoid
color = Color(0xFF6650a4)
Apply predefined typography styles for consistency:
// ✓ Good
style = MaterialTheme.typography.bodyLarge

// ✗ Avoid
fontSize = 16.sp, fontWeight = FontWeight.Normal
Allow users to use their preferred theme:
// ✓ Good - follows system preference
TECMELITheme {
    // content
}

// ✗ Avoid forcing a theme
TECMELITheme(darkTheme = true) {
    // content
}
Always test your UI in both light and dark themes to ensure proper contrast and readability.

Color Contrast Guidelines

Primary Content

Minimum 4.5:1 contrast ratio for body text

Large Text

Minimum 3:1 contrast ratio for 18sp+ or bold 14sp+

Icons & Graphics

Minimum 3:1 contrast ratio for UI components

Error States

High contrast for critical information

Screens

See theming applied in screens

Navigation

Explore navigation patterns

Material Design 3

Official Material Design guidelines

Build docs developers (and LLMs) love