Skip to main content
Lumo UI components are highly customizable, allowing you to adjust colors, shapes, sizes, and behaviors to match your design system.

Component Anatomy

Most Lumo UI components follow a consistent pattern with these customization points:
  • Colors - Container, content, border colors with state variants
  • Shape - Corner radius and border styling
  • Elevation - Shadow depth and hover effects
  • Size - Padding, minimum dimensions, and spacing
  • Content - Icons, labels, and custom content slots

Customizing Buttons

Using Built-in Variants

Buttons come with pre-defined variants covering common use cases:
import com.nomanr.lumo.ui.components.Button
import com.nomanr.lumo.ui.components.ButtonVariant

Button(
    text = "Primary",
    variant = ButtonVariant.Primary,
    onClick = { /* Action */ }
)

Button(
    text = "Outlined",
    variant = ButtonVariant.PrimaryOutlined,
    onClick = { /* Action */ }
)

Button(
    text = "Elevated",
    variant = ButtonVariant.PrimaryElevated,
    onClick = { /* Action */ }
)

Button(
    text = "Ghost",
    variant = ButtonVariant.PrimaryGhost,
    onClick = { /* Action */ }
)

Creating Custom Button Styles

For advanced customization, create your own button configuration:
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.nomanr.lumo.ui.AppTheme
import com.nomanr.lumo.ui.components.ButtonComponent
import com.nomanr.lumo.ui.components.ButtonColors
import com.nomanr.lumo.ui.components.ButtonStyle
import com.nomanr.lumo.ui.foundation.ButtonElevation

@Composable
fun CustomButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
) {
    val customStyle = ButtonStyle(
        colors = ButtonColors(
            containerColor = Color(0xFF6200EE),
            contentColor = Color.White,
            disabledContainerColor = AppTheme.colors.disabled,
            disabledContentColor = AppTheme.colors.onDisabled,
            borderColor = null,
        ),
        shape = RoundedCornerShape(24.dp), // Fully rounded
        elevation = ButtonElevation(
            defaultElevation = 4.dp,
            pressedElevation = 8.dp,
            focusedElevation = 4.dp,
            hoveredElevation = 6.dp,
            disabledElevation = 0.dp,
        ),
        contentPadding = PaddingValues(
            horizontal = 24.dp,
            vertical = 12.dp
        )
    )

    ButtonComponent(
        text = text,
        modifier = modifier,
        enabled = enabled,
        style = customStyle,
        onClick = onClick
    )
}

Before and After Example

// Default Primary Button
Button(
    text = "Submit",
    variant = ButtonVariant.Primary,
    onClick = { /* Action */ }
)
  • Black background (AppTheme.colors.primary)
  • White text (AppTheme.colors.onPrimary)
  • 12dp corner radius
  • No elevation

Customizing Cards

Card Variants

Cards come in three pre-built variants:
import com.nomanr.lumo.ui.components.card.*

// Default Card - No elevation, surface color
Card(
    modifier = Modifier.fillMaxWidth()
) {
    Text("Default Card")
}

// Elevated Card - Shadow elevation
ElevatedCard(
    modifier = Modifier.fillMaxWidth()
) {
    Text("Elevated Card")
}

// Outlined Card - Border with no elevation
OutlinedCard(
    modifier = Modifier.fillMaxWidth()
) {
    Text("Outlined Card")
}

Custom Card Colors and Elevation

import androidx.compose.foundation.BorderStroke
import com.nomanr.lumo.ui.components.card.CardDefaults

Card(
    modifier = Modifier
        .fillMaxWidth()
        .height(200.dp),
    colors = CardDefaults.cardColors(
        containerColor = Color(0xFFF3E5F5),  // Light purple
        contentColor = Color(0xFF4A148C),     // Dark purple
    ),
    elevation = CardDefaults.cardElevation(
        defaultElevation = 2.dp,
        hoveredElevation = 8.dp,
    ),
    shape = RoundedCornerShape(16.dp),
    border = BorderStroke(2.dp, Color(0xFF9C27B0)),
) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Custom Styled Card",
            style = AppTheme.typography.h3
        )
        Spacer(modifier = Modifier.height(8.dp))
        Text(
            text = "With custom colors, elevation, and border",
            style = AppTheme.typography.body2
        )
    }
}

Interactive Cards

Make cards clickable with hover states:
var isSelected by remember { mutableStateOf(false) }

Card(
    onClick = { isSelected = !isSelected },
    modifier = Modifier.fillMaxWidth(),
    colors = CardDefaults.cardColors(
        containerColor = if (isSelected) 
            Color(0xFFE8EAF6) else Color.White,
        contentColor = if (isSelected)
            Color(0xFF3F51B5) else Color.Black,
    ),
    elevation = CardDefaults.cardElevation(
        defaultElevation = if (isSelected) 4.dp else 1.dp,
        pressedElevation = 8.dp,
    )
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text("Selectable Card")
        if (isSelected) {
            Icon(Icons.Default.Check, "Selected")
        }
    }
}

Customizing Text Fields

TextField Color Customization

import com.nomanr.lumo.ui.components.textfield.TextField
import com.nomanr.lumo.ui.components.textfield.TextFieldDefaults
import com.nomanr.lumo.ui.components.textfield.base.TextFieldColors

var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Custom Styled") },
    colors = TextFieldColors(
        focusedTextColor = Color(0xFF1976D2),
        unfocusedTextColor = AppTheme.colors.text,
        focusedContainerColor = Color(0xFFE3F2FD),
        unfocusedContainerColor = AppTheme.colors.surface,
        focusedOutlineColor = Color(0xFF1976D2),
        unfocusedOutlineColor = Color(0xFFBDBDBD),
        cursorColor = Color(0xFF1976D2),
        focusedLabelColor = Color(0xFF1976D2),
        unfocusedLabelColor = Color(0xFF757575),
        // ... other color properties
    ),
    shape = RoundedCornerShape(12.dp)
)

TextField with Icons and Helper Text

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Error

var email by remember { mutableStateOf("") }
val isError = email.isNotEmpty() && !email.contains("@")

TextField(
    value = email,
    onValueChange = { email = it },
    label = { Text("Email") },
    placeholder = { Text("[email protected]") },
    leadingIcon = {
        Icon(Icons.Default.Email, contentDescription = "Email")
    },
    trailingIcon = if (isError) {
        { Icon(Icons.Default.Error, contentDescription = "Error") }
    } else null,
    supportingText = if (isError) {
        { Text("Please enter a valid email") }
    } else null,
    isError = isError,
    singleLine = true
)

Before and After: TextField

TextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Label") }
)
// - Surface background
// - Primary accent color
// - 8dp rounded corners

Customizing Other Components

Badge

import com.nomanr.lumo.ui.components.Badge
import com.nomanr.lumo.ui.components.BadgeDefaults

Badge(
    count = 5,
    containerColor = Color(0xFFD32F2F),
    contentColor = Color.White,
    shape = RoundedCornerShape(8.dp)
) {
    Icon(Icons.Default.Notifications, "Notifications")
}

Chip

import com.nomanr.lumo.ui.components.Chip

var selected by remember { mutableStateOf(false) }

Chip(
    text = "Custom Chip",
    selected = selected,
    onClick = { selected = !selected },
    containerColor = if (selected) Color(0xFF4CAF50) else Color(0xFFE0E0E0),
    contentColor = if (selected) Color.White else Color.Black,
    shape = RoundedCornerShape(16.dp),
    leadingIcon = if (selected) {
        { Icon(Icons.Default.Check, null) }
    } else null
)

Switch

import com.nomanr.lumo.ui.components.Switch

var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it },
    thumbColor = if (checked) Color(0xFF00BCD4) else Color.White,
    trackColor = if (checked) 
        Color(0xFF00BCD4).copy(alpha = 0.5f) 
    else 
        Color(0xFFBDBDBD)
)

Advanced Customization Patterns

Creating a Custom Defaults Object

Create reusable component defaults for your design system:
object MyAppDefaults {
    @Composable
    fun primaryButton() = ButtonStyle(
        colors = ButtonColors(
            containerColor = Color(0xFF6200EE),
            contentColor = Color.White,
            disabledContainerColor = Color(0xFFE0E0E0),
            disabledContentColor = Color(0xFF9E9E9E),
        ),
        shape = RoundedCornerShape(12.dp),
        elevation = null,
        contentPadding = PaddingValues(16.dp, 12.dp)
    )

    @Composable
    fun cardColors() = CardDefaults.cardColors(
        containerColor = Color(0xFFF5F5F5),
        contentColor = Color(0xFF212121),
    )

    @Composable
    fun textFieldColors() = TextFieldDefaults.colors().copy(
        focusedOutlineColor = Color(0xFF6200EE),
        focusedLabelColor = Color(0xFF6200EE),
        cursorColor = Color(0xFF6200EE),
    )
}

// Usage
ButtonComponent(
    text = "Submit",
    style = MyAppDefaults.primaryButton(),
    onClick = { }
)

Component Composition

Build complex components from simpler ones:
@Composable
fun ProfileCard(
    name: String,
    email: String,
    avatarUrl: String,
    onEditClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Card(
        modifier = modifier,
        colors = CardDefaults.cardColors(
            containerColor = Color.White
        ),
        elevation = CardDefaults.cardElevation(
            defaultElevation = 2.dp
        )
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            horizontalArrangement = Arrangement.spacedBy(16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            // Avatar (using custom component)
            Surface(
                modifier = Modifier.size(56.dp),
                shape = CircleShape,
                color = AppTheme.colors.primary
            ) {
                // Avatar content
            }

            // Info
            Column(
                modifier = Modifier.weight(1f),
                verticalArrangement = Arrangement.spacedBy(4.dp)
            ) {
                Text(
                    text = name,
                    style = AppTheme.typography.h4
                )
                Text(
                    text = email,
                    style = AppTheme.typography.body2,
                    color = AppTheme.colors.textSecondary
                )
            }

            // Action
            IconButton(
                onClick = onEditClick,
                variant = IconButtonVariant.Ghost
            ) {
                Icon(Icons.Default.Edit, "Edit")
            }
        }
    }
}

Best Practices

Use Theme Colors

Prefer AppTheme.colors.* over hardcoded colors for consistency

Create Defaults

Build reusable defaults objects for your design system

State Management

Use remember and state for interactive customizations

Composition

Compose complex components from simpler building blocks
When customizing components, maintain accessibility standards:
  • Ensure sufficient color contrast (4.5:1 for text)
  • Keep touch targets at least 48dp
  • Provide content descriptions for icons
  • Support keyboard navigation where applicable

Next Steps

Theming Guide

Learn how to customize the global theme

Component Reference

Explore all component APIs and parameters

Build docs developers (and LLMs) love