Compose Multiplatform UI library for WebAssembly/JS target - declarative framework for sharing UIs across multiple platforms with Kotlin.
—
Compose Multiplatform for WASM/JS includes the complete Material Design system, providing a comprehensive set of components, theming capabilities, and design tokens that follow Google's Material Design guidelines. All Material components are optimized for web deployment while maintaining consistency with other platforms.
The foundation of Material Design theming.
@Composable
fun MaterialTheme(
colors: Colors = MaterialTheme.colors,
typography: Typography = MaterialTheme.typography,
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
)Basic Setup:
@Composable
fun App() {
MaterialTheme {
Surface {
// Your app content
MainContent()
}
}
}Custom Theme:
@Composable
fun CustomTheme(content: @Composable () -> Unit) {
MaterialTheme(
colors = lightColors(
primary = Color(0xFF6200EE),
primaryVariant = Color(0xFF3700B3),
secondary = Color(0xFF03DAC6),
surface = Color.White,
background = Color(0xFFF5F5F5)
),
typography = Typography(
h1 = TextStyle(fontSize = 32.sp, fontWeight = FontWeight.Bold),
body1 = TextStyle(fontSize = 16.sp, lineHeight = 24.sp)
),
shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(16.dp)
),
content = content
)
}Material Design color system with light and dark theme support.
fun lightColors(
primary: Color = Color(0xFF6200EE),
primaryVariant: Color = Color(0xFF3700B3),
secondary: Color = Color(0xFF03DAC6),
secondaryVariant: Color = Color(0xFF018786),
background: Color = Color.White,
surface: Color = Color.White,
error: Color = Color(0xFFB00020),
onPrimary: Color = Color.White,
onSecondary: Color = Color.Black,
onBackground: Color = Color.Black,
onSurface: Color = Color.Black,
onError: Color = Color.White
): ColorsDark Theme:
fun darkColors(
primary: Color = Color(0xFFBB86FC),
primaryVariant: Color = Color(0xFF3700B3),
secondary: Color = Color(0xFF03DAC6),
secondaryVariant: Color = Color(0xFF03DAC6),
background: Color = Color(0xFF121212),
surface: Color = Color(0xFF121212),
error: Color = Color(0xFFCF6679),
onPrimary: Color = Color.Black,
onSecondary: Color = Color.Black,
onBackground: Color = Color.White,
onSurface: Color = Color.White,
onError: Color = Color.Black
): ColorsDynamic Theme Selection:
@Composable
fun AdaptiveTheme(content: @Composable () -> Unit) {
val isDarkMode = isSystemInDarkTheme()
MaterialTheme(
colors = if (isDarkMode) darkColors() else lightColors(),
content = content
)
}Material Design typography scale.
data class Typography(
val h1: TextStyle = TextStyle(/* defaults */),
val h2: TextStyle = TextStyle(/* defaults */),
val h3: TextStyle = TextStyle(/* defaults */),
val h4: TextStyle = TextStyle(/* defaults */),
val h5: TextStyle = TextStyle(/* defaults */),
val h6: TextStyle = TextStyle(/* defaults */),
val subtitle1: TextStyle = TextStyle(/* defaults */),
val subtitle2: TextStyle = TextStyle(/* defaults */),
val body1: TextStyle = TextStyle(/* defaults */),
val body2: TextStyle = TextStyle(/* defaults */),
val button: TextStyle = TextStyle(/* defaults */),
val caption: TextStyle = TextStyle(/* defaults */),
val overline: TextStyle = TextStyle(/* defaults */)
)Usage:
@Composable
fun TypographyExample() {
Column {
Text("Headline 1", style = MaterialTheme.typography.h1)
Text("Headline 6", style = MaterialTheme.typography.h6)
Text("Body text", style = MaterialTheme.typography.body1)
Text("Caption text", style = MaterialTheme.typography.caption)
}
}Main layout structure for Material Design apps.
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState = rememberScaffoldState(),
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
isFloatingActionButtonDocked: Boolean = false,
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
drawerGesturesEnabled: Boolean = true,
drawerShape: Shape = MaterialTheme.shapes.large,
drawerElevation: Dp = DrawerDefaults.Elevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
drawerScrimColor: Color = DrawerDefaults.scrimColor,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
content: @Composable (PaddingValues) -> Unit
)Basic Scaffold:
@Composable
fun MainScreen() {
Scaffold(
topBar = {
TopAppBar(title = { Text("My App") })
},
floatingActionButton = {
FloatingActionButton(onClick = { /* action */ }) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
) { paddingValues ->
LazyColumn(
modifier = Modifier.padding(paddingValues)
) {
// Content
}
}
}Application top bar with title and actions.
@Composable
fun TopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable (() -> Unit)? = null,
actions: @Composable RowScope.() -> Unit = {},
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = AppBarDefaults.TopAppBarElevation
)Usage:
TopAppBar(
title = { Text("Screen Title") },
navigationIcon = {
IconButton(onClick = { /* navigate back */ }) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { /* search */ }) {
Icon(Icons.Default.Search, contentDescription = "Search")
}
IconButton(onClick = { /* more options */ }) {
Icon(Icons.Default.MoreVert, contentDescription = "More")
}
}
)Foundation component for elevation and clipping.
@Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
content: @Composable () -> Unit
)Usage:
Surface(
modifier = Modifier.padding(16.dp),
shape = RoundedCornerShape(8.dp),
elevation = 4.dp
) {
Text(
text = "Elevated content",
modifier = Modifier.padding(16.dp)
)
}Bottom navigation bar for primary destinations.
@Composable
fun BottomNavigation(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = BottomNavigationDefaults.Elevation,
content: @Composable RowScope.() -> Unit
)Usage:
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Home", "Search", "Profile")
BottomNavigation {
items.forEachIndexed { index, item ->
BottomNavigationItem(
icon = { Icon(getIcon(index), contentDescription = item) },
label = { Text(item) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}Side navigation for larger screens.
@Composable
fun NavigationRail(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = NavigationRailDefaults.Elevation,
header: @Composable (ColumnScope.() -> Unit)? = null,
content: @Composable ColumnScope.() -> Unit
)Outlined text input field.
@Composable
fun OutlinedTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
label: @Composable (() -> Unit)? = null,
placeholder: @Composable (() -> Unit)? = null,
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
isError: Boolean = false,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
)Usage:
var email by remember { mutableStateOf("") }
var isError by remember { mutableStateOf(false) }
OutlinedTextField(
value = email,
onValueChange = {
email = it
isError = !android.util.Patterns.EMAIL_ADDRESS.matcher(it).matches()
},
label = { Text("Email") },
placeholder = { Text("Enter your email") },
leadingIcon = {
Icon(Icons.Default.Email, contentDescription = "Email")
},
isError = isError,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email)
)Selection control for binary choices.
@Composable
fun Checkbox(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: CheckboxColors = CheckboxDefaults.colors()
)Usage:
var isChecked by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
Text("Accept terms and conditions")
}Toggle control for binary choices.
@Composable
fun Switch(
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: SwitchColors = SwitchDefaults.colors()
)Usage:
var isEnabled by remember { mutableStateOf(true) }
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Notifications")
Switch(
checked = isEnabled,
onCheckedChange = { isEnabled = it }
)
}Brief messages about app processes.
@Composable
fun Snackbar(
modifier: Modifier = Modifier,
action: @Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = MaterialTheme.shapes.small,
backgroundColor: Color = SnackbarDefaults.backgroundColor,
contentColor: Color = MaterialTheme.colors.surface,
elevation: Dp = 6.dp,
content: @Composable () -> Unit
)Usage:
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(key1 = someEvent) {
snackbarHostState.showSnackbar(
message = "Item deleted",
actionLabel = "Undo",
duration = SnackbarDuration.Short
)
}
SnackbarHost(
hostState = snackbarHostState,
snackbar = { data ->
Snackbar(
action = {
data.actionLabel?.let { actionLabel ->
TextButton(onClick = { data.performAction() }) {
Text(actionLabel)
}
}
}
) {
Text(data.message)
}
}
)Loading indicator for indeterminate progress.
@Composable
fun CircularProgressIndicator(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.primary,
strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
)Determinate Progress:
@Composable
fun CircularProgressIndicator(
progress: Float,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.primary,
strokeWidth: Dp = ProgressIndicatorDefaults.StrokeWidth
)Usage:
// Indeterminate
CircularProgressIndicator()
// Determinate
var progress by remember { mutableStateOf(0.0f) }
CircularProgressIndicator(progress = progress)Material Design card container.
@Composable
fun Card(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
border: BorderStroke? = null,
elevation: Dp = 1.dp,
content: @Composable () -> Unit
)Usage:
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clickable { /* card clicked */ },
elevation = 4.dp
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "Card Title",
style = MaterialTheme.typography.h6
)
Spacer(Modifier.height(8.dp))
Text(
text = "Card content goes here with additional information.",
style = MaterialTheme.typography.body2
)
}
}Modal dialog for important decisions.
@Composable
fun AlertDialog(
onDismissRequest: () -> Unit,
buttons: @Composable () -> Unit,
modifier: Modifier = Modifier,
title: (@Composable () -> Unit)? = null,
text: (@Composable () -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor)
)Usage:
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text("Confirm Action") },
text = { Text("Are you sure you want to delete this item?") },
buttons = {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = { showDialog = false }) {
Text("Cancel")
}
TextButton(onClick = {
// Perform action
showDialog = false
}) {
Text("Delete")
}
}
}
)
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-ui--ui-wasm-js