Skip to main content
Lumo UI’s theming system provides a consistent design language across your application. It includes color schemes, typography scales, and automatic dark mode support.

Usage

import com.nomanr.lumo.ui.AppTheme

@Composable
fun MyApp() {
    AppTheme {
        // Your app content
        Text(
            text = "Hello World",
            style = AppTheme.typography.h1,
            color = AppTheme.colors.primary
        )
    }
}

Theme Components

The theme system consists of three main components:
  1. Colors - Color palette for light and dark themes
  2. Typography - Text styles and font definitions
  3. Theme - The wrapper that provides theming context

Colors

Access theme colors through AppTheme.colors:
@Composable
fun ColorExample() {
    Box(
        modifier = Modifier
            .background(AppTheme.colors.primary)
            .padding(16.dp)
    ) {
        Text(
            text = "Primary Background",
            color = AppTheme.colors.onPrimary
        )
    }
}

Color Palette

// Primary colors
AppTheme.colors.primary        // Main brand color
AppTheme.colors.onPrimary      // Content color on primary

// Secondary colors
AppTheme.colors.secondary      // Secondary brand color
AppTheme.colors.onSecondary    // Content color on secondary

// Tertiary colors
AppTheme.colors.tertiary       // Tertiary accent color
AppTheme.colors.onTertiary     // Content color on tertiary

// Surface colors
AppTheme.colors.surface        // Surface backgrounds
AppTheme.colors.onSurface      // Content color on surface

// Background colors
AppTheme.colors.background     // Screen backgrounds
AppTheme.colors.onBackground   // Content color on background

// Error colors
AppTheme.colors.error          // Error states
AppTheme.colors.onError        // Content color on error

// State colors
AppTheme.colors.disabled       // Disabled elements
AppTheme.colors.onDisabled     // Content on disabled elements

// Utility
AppTheme.colors.transparent    // Transparent color

Semantic Color Function

Automatically get the appropriate content color:
@Composable
fun contentColorFor(color: Color): Color {
    return AppTheme.colors.contentColorFor(color)
}

// Usage
val backgroundColor = AppTheme.colors.primary
val contentColor = contentColorFor(backgroundColor)

Box(
    modifier = Modifier.background(backgroundColor)
) {
    Text(
        text = "Auto-colored text",
        color = contentColor // Automatically onPrimary
    )
}

Typography

Access typography styles through AppTheme.typography:
@Composable
fun TypographyExample() {
    Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
        Text("Large Heading", style = AppTheme.typography.h1)
        Text("Body Text", style = AppTheme.typography.body1)
        Text("Small Label", style = AppTheme.typography.label3)
    }
}

Typography Scale

// Headings (largest to smallest)
AppTheme.typography.h1         // Main page titles
AppTheme.typography.h2         // Section headings
AppTheme.typography.h3         // Subsection headings
AppTheme.typography.h4         // Minor headings

// Body text (largest to smallest)
AppTheme.typography.body1      // Primary body text
AppTheme.typography.body2      // Secondary body text
AppTheme.typography.body3      // Small body text / captions

// Labels (largest to smallest)
AppTheme.typography.label1     // Form labels
AppTheme.typography.label2     // Secondary labels
AppTheme.typography.label3     // Small labels / badges

// Special
AppTheme.typography.button     // Button text style

Custom Typography

Text(
    text = "Custom styled text",
    style = AppTheme.typography.body1.copy(
        fontWeight = FontWeight.Bold,
        fontSize = 20.sp,
        letterSpacing = 0.5.sp
    )
)

Dark Theme

Lumo UI automatically supports dark mode:
@Composable
fun App() {
    // Automatically uses system dark mode setting
    AppTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = AppTheme.colors.background
        ) {
            MyContent()
        }
    }
}

Manual Dark Mode Control

@Composable
fun App() {
    var isDark by remember { mutableStateOf(false) }
    
    AppTheme(isDarkTheme = isDark) {
        Column {
            Switch(
                checked = isDark,
                onCheckedChange = { isDark = it }
            )
            Text(
                "Dark mode: ${if (isDark) "On" else "Off"}",
                color = AppTheme.colors.onBackground
            )
        }
    }
}

Detecting Current Theme

import androidx.compose.foundation.isSystemInDarkTheme

@Composable
fun ThemeAwareComponent() {
    val isDark = isSystemInDarkTheme()
    
    Icon(
        imageVector = if (isDark) {
            Icons.Filled.DarkMode
        } else {
            Icons.Filled.LightMode
        },
        contentDescription = "Theme"
    )
}

Local Providers

The theme provides several composition locals:
// Access current colors
val colors = LocalColors.current

// Access current typography
val typography = LocalTypography.current

// Access current content color
val contentColor = LocalContentColor.current

// Access current text style
val textStyle = LocalTextStyle.current

Theme Utilities

Text Selection Colors

Text selection colors are automatically configured:
// Selection handle and background colors are themed
SelectionContainer {
    Text("Selectable text with themed selection colors")
}

Ripple Effects

Interactive elements use themed ripple effects:
// Ripple color automatically matches theme
Button(onClick = { }) {
    Text("Ripple uses theme colors")
}

Customizing the Theme

Custom Colors

To customize colors, modify the color scheme:
val CustomLightColors = Colors(
    primary = Color(0xFF6200EE),
    onPrimary = Color.White,
    secondary = Color(0xFF03DAC6),
    onSecondary = Color.Black,
    // ... other colors
)

val CustomDarkColors = Colors(
    primary = Color(0xFFBB86FC),
    onPrimary = Color.Black,
    secondary = Color(0xFF03DAC6),
    onSecondary = Color.Black,
    // ... other colors
)

Custom Typography

Define custom typography:
val CustomTypography = Typography(
    h1 = TextStyle(
        fontSize = 32.sp,
        fontWeight = FontWeight.Bold,
        lineHeight = 40.sp
    ),
    body1 = TextStyle(
        fontSize = 16.sp,
        fontWeight = FontWeight.Normal,
        lineHeight = 24.sp
    ),
    // ... other styles
)

Best Practices

  1. Use Theme Colors: Always use AppTheme.colors instead of hardcoded colors
  2. Typography Styles: Use predefined typography styles for consistency
  3. Content Color: Use contentColorFor() for automatic color pairing
  4. Dark Mode: Test both light and dark themes
  5. Local Colors: Use LocalContentColor for adaptive text colors
  6. Surface Colors: Use surface colors for cards and elevated components
  7. Semantic Colors: Use error colors for error states, not arbitrary red

Composition Locals

The theme sets up these composition locals:
CompositionLocalProvider(
    LocalColors provides colors,
    LocalTypography provides typography,
    LocalIndication provides rippleIndication,
    LocalTextSelectionColors provides selectionColors,
    LocalContentColor provides contentColor,
    LocalTextStyle provides defaultTextStyle
) {
    content()
}

Common Patterns

Adaptive Components

@Composable
fun AdaptiveCard() {
    Card(
        backgroundColor = AppTheme.colors.surface,
        contentColor = contentColorFor(AppTheme.colors.surface),
        elevation = 4.dp
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = "Title",
                style = AppTheme.typography.h3
            )
            Text(
                text = "Description",
                style = AppTheme.typography.body2
            )
        }
    }
}

Themed Icons

@Composable
fun ThemedIcon() {
    Icon(
        imageVector = Icons.Default.Home,
        contentDescription = "Home",
        tint = LocalContentColor.current // Adapts to theme
    )
}

Status Colors

@Composable
fun StatusBadge(status: Status) {
    val color = when (status) {
        Status.Success -> Color.Green
        Status.Warning -> Color.Yellow
        Status.Error -> AppTheme.colors.error
        Status.Info -> AppTheme.colors.primary
    }
    
    Badge(
        containerColor = color,
        contentColor = contentColorFor(color)
    ) {
        Text(status.name)
    }
}

Source Reference

See the full implementation in Theme.kt:14-43.

Build docs developers (and LLMs) love