What You Will Build

A neumorphic (soft UI) design screen featuring raised and inset elements that appear to be extruded from the background surface. Neumorphism creates depth through paired light and dark shadows on a monochrome surface, giving buttons and cards a tactile, almost physical appearance.

Why Neumorphism Is Interesting for Compose

While Material Design 3 uses tonal elevation, neumorphism creates depth differently: a light shadow on one side (simulating a light source) and a dark shadow on the opposite side. In Compose, you can achieve this with drawBehind to draw offset shadows manually, which is a powerful custom drawing technique.

Key Compose Concepts

  • Modifier.drawBehind for drawing custom shadows behind the composable.
  • drawCircle with offset centers for paired light/dark shadow simulation.
  • Consistent background color across the surface and elements (typically a light gray like #E0E5EC).

Step 1: The Neumorphic Background

Neumorphism requires a single consistent background color. Everything is the same hue; depth comes only from shadows:

val neuBackground = Color(0xFFE0E5EC)

Column(
    modifier = Modifier
        .fillMaxSize()
        .background(neuBackground)
        .padding(24.dp),
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Center
) {
    Text(
        "Neumorphism",
        fontSize = 24.sp,
        fontWeight = FontWeight.Bold,
        color = Color(0xFF2D3436)
    )
}

Step 2: Create the Raised Circle with Custom Shadows

The key technique is using drawBehind with two offset circles - a white one for the light side and a dark one for the shadow side:

Box(
    modifier = Modifier
        .size(150.dp)
        .clip(CircleShape)
        .background(Color(0xFFE0E5EC))
        .drawBehind {
            // Light shadow (upper-left)
            drawCircle(
                color = Color.White.copy(alpha = 0.7f),
                radius = size.minDimension / 2,
                center = center + Offset(-4f, -4f)
            )
            // Dark shadow (lower-right)
            drawCircle(
                color = Color(0xFFA3B1C6).copy(alpha = 0.5f),
                radius = size.minDimension / 2,
                center = center + Offset(4f, 4f)
            )
        },
    contentAlignment = Alignment.Center
) {
    Icon(
        Icons.Default.Fingerprint,
        contentDescription = null,
        modifier = Modifier.size(60.dp),
        tint = Color(0xFF6C5CE7)
    )
}

Step 3: Build a Neumorphic Card

For rectangular elements, use Card with the same background color and gentle elevation:

Card(
    modifier = Modifier.fillMaxWidth(),
    shape = RoundedCornerShape(16.dp),
    colors = CardDefaults.cardColors(
        containerColor = Color(0xFFE0E5EC)
    ),
    elevation = CardDefaults.cardElevation(
        defaultElevation = 8.dp
    )
) {
    Column(Modifier.padding(20.dp)) {
        Text("Neumorphic Card",
            fontWeight = FontWeight.Bold,
            color = Color(0xFF2D3436))
        Text("Soft UI design style",
            color = Color(0xFF636E72))
    }
}

Tips and Pitfalls

  • Shadow offset consistency: Always use the same direction for light (upper-left) and dark (lower-right) to maintain a consistent light source.
  • Avoid bright colors: Neumorphism works best with muted, desaturated palettes. Bright accents should be small (icon tints, text highlights).
  • Accessibility concern: Low contrast between elements and background can be difficult for visually impaired users. Consider providing a high-contrast mode.
  • drawBehind runs on every frame during recomposition, so keep the drawing operations simple for smooth performance.

Minimum SDK: API 24+ with Compose BOM 2024.01+

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.