--- title: Platform Themes description: Native look and feel on every platform with one line of code. Platform Themes set beautiful styling defaults based on the platform your app is running on. --- ## Installation Include the Platform Theme module in your app's dependencies: ```kotlin implementation("com.composables:composeunstyled-platformtheme:{{compose_unstyled_version}}") ``` ## Basic usage Use the `buildPlatformTheme` function to create your theme. Then wrap your app with the new theme and you are all set. ```compose id="platform-theme-demo" height=200 import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.composeunstyled.Button import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.dimmed import com.composeunstyled.platformtheme.EmojiVariant import com.composeunstyled.platformtheme.heading5 import com.composeunstyled.platformtheme.indications import com.composeunstyled.platformtheme.interactiveSizes import com.composeunstyled.platformtheme.interactiveSize import com.composeunstyled.platformtheme.roundedFull import com.composeunstyled.platformtheme.shapes import com.composeunstyled.platformtheme.sizeDefault import com.composeunstyled.platformtheme.text8 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme COMPOSE { val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( emojiVariant = EmojiVariant.Colored ) ) AppTheme { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text("πŸ₯°βœŒοΈπŸ’πŸ‡", style = Theme[textStyles][text8]) Text("Beautiful styling defaults on every platform", style = Theme[textStyles][heading5]) Row( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically ) { Button( onClick = { }, contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), shape = Theme[shapes][roundedFull], backgroundColor = Color(0xFF3B82F6), indication = Theme[indications][dimmed], modifier = Modifier .interactiveSize(Theme[interactiveSizes][sizeDefault]) ) { Text("Get Started", color = Color.White) } } } } } ``` ```kotlin import androidx.compose.runtime.Composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.composeunstyled.Button import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.dimmed import com.composeunstyled.platformtheme.EmojiVariant import com.composeunstyled.platformtheme.heading5 import com.composeunstyled.platformtheme.indications import com.composeunstyled.platformtheme.interactiveSizes import com.composeunstyled.platformtheme.interactiveSize import com.composeunstyled.platformtheme.roundedFull import com.composeunstyled.platformtheme.shapes import com.composeunstyled.platformtheme.sizeDefault import com.composeunstyled.platformtheme.text8 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme ``` ```kotlin val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( emojiVariant = EmojiVariant.Colored ) ) @Composable fun App() { AppTheme { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text("πŸ₯°βœŒοΈπŸ’πŸ‡", style = Theme[textStyles][text8]) Text( text = "Beautiful styling defaults on every platform", style = Theme[textStyles][heading5] ) Row( horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically ) { Button( onClick = { }, contentPadding = PaddingValues( horizontal = 16.dp, vertical = 8.dp ), shape = Theme[shapes][roundedFull], backgroundColor = Color(0xFF3B82F6), indication = Theme[indications][dimmed], modifier = Modifier .interactiveSize(Theme[interactiveSizes][sizeDefault]) ) { Text("Get Started", color = Color.White) } } } } } ``` ## Typography Platform Themes automatically apply text styles to every [`Text`](text.md) and [`TextField`](textfield.md) child. You can also make use of the theme's text style using the `LocalTextStyle` composition local. ### Typography Tokens We provide the `text` and `heading` typography tokens with 9 different sizes each. Each size is either defined in each platform's design guidelines (for example, Material for Android or HIG for Apple) or is a close approximation of one that is defined. ```compose id="typography-scale-demo" height=400 import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.heading1 import com.composeunstyled.platformtheme.heading2 import com.composeunstyled.platformtheme.heading3 import com.composeunstyled.platformtheme.heading4 import com.composeunstyled.platformtheme.heading5 import com.composeunstyled.platformtheme.heading6 import com.composeunstyled.platformtheme.heading7 import com.composeunstyled.platformtheme.heading8 import com.composeunstyled.platformtheme.heading9 import com.composeunstyled.platformtheme.text1 import com.composeunstyled.platformtheme.text2 import com.composeunstyled.platformtheme.text3 import com.composeunstyled.platformtheme.text4 import com.composeunstyled.platformtheme.text5 import com.composeunstyled.platformtheme.text6 import com.composeunstyled.platformtheme.text7 import com.composeunstyled.platformtheme.text8 import com.composeunstyled.platformtheme.text9 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.theme.Theme COMPOSE { val AppTheme = buildPlatformTheme() AppTheme { Row( modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(50.dp) ) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text("Heading 9", style = Theme[textStyles][heading9]) Text("Heading 8", style = Theme[textStyles][heading8]) Text("Heading 7", style = Theme[textStyles][heading7]) Text("Heading 6", style = Theme[textStyles][heading6]) Text("Heading 5", style = Theme[textStyles][heading5]) Text("Heading 4", style = Theme[textStyles][heading4]) Text("Heading 3", style = Theme[textStyles][heading3]) Text("Heading 2", style = Theme[textStyles][heading2]) Text("Heading 1", style = Theme[textStyles][heading1]) } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { Text("Text 9", style = Theme[textStyles][text9]) Text("Text 8", style = Theme[textStyles][text8]) Text("Text 7", style = Theme[textStyles][text7]) Text("Text 6", style = Theme[textStyles][text6]) Text("Text 5", style = Theme[textStyles][text5]) Text("Text 4", style = Theme[textStyles][text4]) Text("Text 3", style = Theme[textStyles][text3]) Text("Text 2", style = Theme[textStyles][text2]) Text("Text 1", style = Theme[textStyles][text1]) } } } } ``` This way, you can make sure that the sizing of your app feels cohesive on every platform without sweating the details. By default, scale number `4` is applied when you use the Theme. If you need to render text smaller than that, use a smaller scale. If you need larger text, use a bigger number. | Scale | Android | iOS | Desktop | Web | |------------|---------|------|---------|------| | `1` | 11sp | 12sp | 10sp | 10sp | | `2` | 12sp | 13sp | 11sp | 12sp | | `3` | 14sp | 16sp | 12sp | 14sp | | `4` (base) | 16sp | 17sp | 13sp | 16sp | | `5` | 22sp | 18sp | 14sp | 18sp | | `6` | 24sp | 20sp | 15sp | 20sp | | `7` | 28sp | 22sp | 17sp | 24sp | | `8` | 32sp | 28sp | 22sp | 28sp | | `9` | 36sp | 34sp | 26sp | 35sp | ### Using system fonts Platform Themes automatically apply system fonts on every platform. This way, you get the default typography on every platform without us having to bundle font files and increase the size of your app. The exception to this is the Web platform. Browsers do not currently have access to the computer's installed fonts outside of CSS. Because of this technical limitation, we bundle [Noto Sans](https://fonts.google.com/noto/specimen/Noto+Sans) on Web. Noto Sans is a global font that comes with variations with pretty much every script out there. ## Displaying non-Latin text on Web Use `webFontOptions` while building your Platform Theme to specify the scripts that your app needs. ```compose id="non-latin-demo" height=200 import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.heading5 import com.composeunstyled.platformtheme.SpokenLanguage import com.composeunstyled.platformtheme.text4 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme COMPOSE { val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( supportedLanguages = listOf(SpokenLanguage.Japanese) ) ) AppTheme { Text("ζ΅·θ³ŠηŽ‹γ«δΏΊγ―γͺγ‚‹", style = Theme[textStyles][heading5]) } } ``` ```kotlin import androidx.compose.runtime.Composable import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.heading5 import com.composeunstyled.platformtheme.SpokenLanguage import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme ``` ```kotlin val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( supportedLanguages = listOf(SpokenLanguage.Japanese) ) ) @Composable fun App() { AppTheme { Text("ζ΅·θ³ŠηŽ‹γ«δΏΊγ―γͺγ‚‹", style = Theme[textStyles][heading5]) } } ```

⚠️ Use this API with caution Compose Web will cause your app to freeze while big sized fonts are being loaded for the first time. They are then cached by the browser. Only use the scripts that you need to reduce unresponsiveness.

We currently support Japanese, Korean, Chinese Traditional and Chinese Simplified. If there is a script you would like us to support, feel free to request it via a GitHub issue. ## Displaying emojis on Web Use `webFontOptions` while building your Platform Theme to specify the emoji variant you would like to use. By default, `Monochrome` is used as it is a good compromise between having emojis and speed: ```compose id="emoji-demo" height=200 import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.EmojiVariant import com.composeunstyled.platformtheme.heading8 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme COMPOSE { val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( emojiVariant = EmojiVariant.Colored ) ) AppTheme { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { Text("πŸŽ‰ πŸš€ ❀️ 🌟 🎨", style = Theme[textStyles][heading8]) } } } ``` ```kotlin import androidx.compose.runtime.Composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.EmojiVariant import com.composeunstyled.platformtheme.heading8 import com.composeunstyled.platformtheme.textStyles import com.composeunstyled.platformtheme.WebFontOptions import com.composeunstyled.theme.Theme ``` ```kotlin val AppTheme = buildPlatformTheme( webFontOptions = WebFontOptions( emojiVariant = EmojiVariant.Colored ) ) @Composable fun App() { AppTheme { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { Text("πŸŽ‰ πŸš€ ❀️ 🌟 🎨", style = Theme[textStyles][heading8]) } } } ``` ## Indications Platform Themes apply an `indication` to their children according to each platform's look and feel. We provide two Theme tokens: `bright` and `dimmed`. The default indication is `bright`.

Android

iOS

Desktop

Web

## Interaction sizes Platform Themes provide interaction size tokens that ensure your interactive elements meet accessibility standards on every platform. The sizing comes from each platform's design guidelines, ensuring optimal usability whether users are tapping on a touchscreen or clicking with a mouse. We provide two size tokens: `sizeDefault` and `sizeMinimum`. | Token | Android | iOS | Desktop | Web | |---------------|---------|------|---------|------| | `sizeDefault` | 48dp | 44dp | 28dp | 28dp | | `sizeMinimum` | 32dp | 28dp | 20dp | 20dp | Use the `interactiveSize` modifier to apply these sizes to your interactive elements: ```kotlin import com.composeunstyled.Button import com.composeunstyled.Text import com.composeunstyled.platformtheme.interactiveSize import com.composeunstyled.platformtheme.interactiveSizes import com.composeunstyled.platformtheme.sizeDefault import com.composeunstyled.theme.Theme ``` ```kotlin Button( onClick = { /* ... */ }, modifier = Modifier.interactiveSize(Theme[interactiveSizes][sizeDefault]) ) { Text("Click me") } ``` This ensures your buttons, checkboxes, and other interactive elements are always sized appropriately for the platform they're running on. ## Shapes Shape theme tokens are not platform specific, however they are very handy when building apps: ```compose id="shapes-demo" height=300 import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.composeunstyled.ProvideTextStyle import com.composeunstyled.Text import com.composeunstyled.platformtheme.buildPlatformTheme import com.composeunstyled.platformtheme.roundedLarge import com.composeunstyled.platformtheme.roundedMedium import com.composeunstyled.platformtheme.roundedNone import com.composeunstyled.platformtheme.roundedSmall import com.composeunstyled.platformtheme.shapes import com.composeunstyled.theme.Theme COMPOSE { val AppTheme = buildPlatformTheme() AppTheme { ProvideTextStyle(TextStyle(fontFamily = FontFamily.Monospace)) { Column( modifier = Modifier.widthIn(max = 380.dp).fillMaxWidth().padding(16.dp), verticalArrangement = Arrangement.spacedBy(32.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.Top ) { Column( modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Box( modifier = Modifier .size(60.dp) .background(Color(0xFF3B82F6), Theme[shapes][roundedNone]) ) Text("roundedNone") } Column( modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Box( modifier = Modifier .size(60.dp) .background(Color(0xFF3B82F6), Theme[shapes][roundedSmall]) ) Text("roundedSmall") } } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.Top ) { Column( modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Box( modifier = Modifier .size(60.dp) .background(Color(0xFF3B82F6), Theme[shapes][roundedMedium]) ) Text("roundedMedium") } Column( modifier = Modifier.weight(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Box( modifier = Modifier .size(60.dp) .background(Color(0xFF3B82F6), Theme[shapes][roundedLarge]) ) Text("roundedLarge") } } } } } } ```