My Text colour is not the same as that defined in the theme despite it being inside Surface; it appears almost the same but not quite.

This simplistic sample layout is bare bones:
MyTheme {
Surface {
Column(Modifier.padding(12.dp)) {
Text(
text = "This is a line of text, uses surface",
)
Text(
text = "This is a line of text, forced white",
color = Color.White,
modifier = Modifier.padding(top = 4.dp)
)
}
}
}
My theme is simple:
@Composable
fun MyTheme(
content: @Composable () -> Unit,
) {
MaterialTheme(
colors = DarkColours,
...,
content = content
)
}
internal val DarkColours = darkColors(
surface = Color(0xFF043143),
onSurface = Color.White,
... // None of the other colours are close to off-white
)
Even if I explicitly specify the colours to use in the surface, the top Text is still off-white (no change):
Surface(
color = Color(0xFF043143),
contentColor = Color.White
) { ... }
Elevation is 0.0dp and uses DefaultElevationOverlay so shouldn't have any effect on contentColor
LocalContentColor.current reports Color(1.0, 1.0, 1.0) when printed by the first Text.
Figured it out while typing this question up so figured I'd share it.
LocalContentAlpha was set to 0.87 inside the contents of MaterialTheme. This is because of its line
LocalContentAlpha provides ContentAlpha.high,
which ultimately resolves to this due to the fact that I'm using a material Color scheme with isLight = false (thanks to darkColors).
private object LowContrastContentAlpha {
const val high: Float = 0.87f
const val medium: Float = 0.60f
const val disabled: Float = 0.38f
}
A workaround is to re-set alpha back to 1.0:
MaterialTheme(
colors = DarkColours,
...
) {
CompositionLocalProvider(
// Undo our "dark" colours triggering Material theme
// to use a low contrast alpha (87%).
LocalContentAlpha provides 1.0f,
content = content
)
}
(or just use lightColors / set isLight = true if you can afford to do this)
This kind of blows my mind, that Material would just change the alpha of literally all of my app's content - I can't find any doc on why it does this either in the material spec!
It happens only with M2 and it doesn't depends on the Surface, but on the Text itself.
The color in the Text is defined by the color parameter or applying a TextStyle. The default value is Color.Unspecified.
If color = Color.Unspecified and style has no color set, this will be LocalContentColor mixed with LocalContentAlpha.current.
In the Text.kt you can find:
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
}
As you described in your answer in the MaterialTheme is defined:
fun MaterialTheme( /* .. */ ) {
//...
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high, //0.87f
//...
)
In M3 (androidx.compose.material3) it doesn't happen since LocalContentColor.current is not mixed:
val textColor = color.takeOrElse {
style.color.takeOrElse {
LocalContentColor.current
}
}

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With