Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Custom Design Systems in Compose - Chicago Roboto

Custom Design Systems in Compose - Chicago Roboto

While Jetpack Compose comes with Google’s Material Design system, many apps have their own design language and systems that they need to adhere to in order to offer consistency across platforms. As apps and teams scale, it eventually becomes beneficial to implement your own design system.

That’s exactly the case at Dropbox. We recently went through the process of implementing our DIG design library in Compose, and have seen the benefits first hand.

In this talk, we’ll discuss different approaches to implementing custom design systems in Compose, from customizing the Material Theme, to extending it, all the way to implementing a full blown custom design library. We’ll cover some of the benefits and drawbacks of each approach that you might want to consider.

Lastly, we’ll share the process we went through at Dropbox as we implemented our DIG design library. We’ll report on our experience and reflect on the benefits we’ve seen and what our plans are for the future.

Ryan Harter

August 02, 2022
Tweet

More Decks by Ryan Harter

Other Decks in Technology

Transcript

  1. Sidebar Design System Tokens @Stable class Colors( val primary: Color,

    val primaryVariant: Color, val secondary: Color, val secondaryVariant: Color, val background: Color, val surface: Color, val error: Color, val onPrimary: Color, val onSecondary: Color, val onBackground: Color, val onSurface: Color, val onError: Color, val isLight: Boolean )
  2. Sidebar Design System Tokens @Stable class Colors( val primary: Color,

    val primaryVariant: Color, val secondary: Color, val secondaryVariant: Color, val background: Color, val surface: Color, val error: Color, val onPrimary: Color, val onSecondary: Color, val onBackground: Color, val onSurface: Color, val onError: Color, val isLight: Boolean ) @Stable class Colors( val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Color, val : Boolean )
  3. Sidebar Design System Tokens @Stable class Colors( val primary: Color,

    val primaryVariant: Color, val secondary: Color, val secondaryVariant: Color, val background: Color, val surface: Color, val error: Color, val onPrimary: Color, val onSecondary: Color, val onBackground: Color, val onSurface: Color, val onError: Color, val isLight: Boolean ) @Immutable class Typography( val h1: TextStyle, val h2: TextStyle, val h3: TextStyle, val h4: TextStyle, val h5: TextStyle, val h6: TextStyle, val subtitle1: TextStyle, val subtitle2: TextStyle, val body1: TextStyle, val body2: TextStyle, val button: TextStyle, val caption: TextStyle, val overline: TextStyle )
  4. Sidebar Design System Tokens @Stable class Colors( val primary: Color,

    val primaryVariant: Color, val secondary: Color, val secondaryVariant: Color, val background: Color, val surface: Color, val error: Color, val onPrimary: Color, val onSecondary: Color, val onBackground: Color, val onSurface: Color, val onError: Color, val isLight: Boolean ) @Immutable class Typography( val h1: TextStyle, val h2: TextStyle, val h3: TextStyle, val h4: TextStyle, val h5: TextStyle, val h6: TextStyle, val subtitle1: TextStyle, val subtitle2: TextStyle, val body1: TextStyle, val body2: TextStyle, val button: TextStyle, val caption: TextStyle, val overline: TextStyle ) @Immutable class Typography( val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle, val : TextStyle )
  5. Sidebar Design System Tokens @Stable class Colors( val primary: Color,

    val primaryVariant: Color, val secondary: Color, val secondaryVariant: Color, val background: Color, val surface: Color, val error: Color, val onPrimary: Color, val onSecondary: Color, val onBackground: Color, val onSurface: Color, val onError: Color, val isLight: Boolean ) @Immutable class Typography( val h1: TextStyle, val h2: TextStyle, val h3: TextStyle, val h4: TextStyle, val h5: TextStyle, val h6: TextStyle, val subtitle1: TextStyle, val subtitle2: TextStyle, val body1: TextStyle, val body2: TextStyle, val button: TextStyle, val caption: TextStyle, val overline: TextStyle )
  6. Material Theme FAB Text Surface Text Image Surface Text Image

    TopAppBar Colors Typography Shape Material Theme
  7. Material Theme Surface Surface FAB Surface Text Image TopAppBar Colors

    Typography Shape TopAppBar Surface Text Image Text FAB Material Theme
  8. Sidebar CompositionLocal Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Text Image Text ImageButton TopAppBar Text FloatingActionButton Image
  9. Sidebar CompositionLocal Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Text Image Text ImageButton TopAppBar Text FloatingActionButton Image CompositionLocalProvider
  10. Sidebar CompositionLocalProvider CompositionLocal Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column

    Row Row Row Text Text Text Image Text ImageButton TopAppBar Text FloatingActionButton Image Colors Typography Shape
  11. Sidebar CompositionLocalProvider CompositionLocal Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column

    Row Row Row Text Text Text Image Text ImageButton TopAppBar Text FloatingActionButton Image Colors Typography Shape
  12. Material Theme Surface Surface FAB Surface Text Image TopAppBar Colors

    Typography Shape TopAppBar Surface Text Image Text FAB Material Theme
  13. Customizing • Customize Material • Extend Material • Replace Material

    Token Systems • Implement fully-custom design system Material Theme
  14. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material
  15. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material (content: @Composable () - > Unit) {
  16. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } : @Composable () -> Unit) { Customize Material (content
  17. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } : @Composable () -> Unit) { (content Customize Material content
  18. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } private val LightColors = lightColors( . .. ) private val DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material : @Composable () -> Unit) { content (content
  19. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } private val LightColors = lightColors( . .. ) private val DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material : @Composable () >
  20. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material
  21. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material
  22. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material
  23. private val LightColors = lightColors( . .. ) private val

    DarkColors = darkColors( . .. ) @Composable fun MyTheme(content: @Composable () - > Unit) { MaterialTheme( colors = if (isSystemInDarkTheme()) DarkColors else LightColors, shapes = Shapes( small = RoundedCornerShape(0.dp), medium = RoundedCornerShape(0.dp), large = RoundedCornerShape(0.dp) ), content = content ) } Customize Material @Composable
  24. Customizing • Customize Material • Extend Material • Replace Material

    Token Systems • Implement fully-custom design system Material Theme
  25. Extend Material (super simple) val Colors.snackbarAction: Color get() = if

    (isLight) Red300 else Red700 val Typography.textFieldInput: TextStyle get() = TextStyle( / * ... */ ) val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
  26. Extend Material (super simple) val Colors.snackbarAction: Color get() = if

    (isLight) Red300 else Red700 val Typography.textFieldInput: TextStyle get() = TextStyle( / * ... */ ) val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
  27. Extend Material (super simple) val Colors.snackbarAction: Color get() = if

    (isLight) Red300 else Red700 val Typography.textFieldInput: TextStyle get() = TextStyle( / * ... */ ) val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp) val Colors.snackbarAction: Color get() = if (isLight) Red300 else Red700 val Typography.textFieldInput: TextStyle get() = TextStyle( / * ... */ ) val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
  28. Extend Material (super simple) val Colors.snackbarAction: Color get() = if

    (isLight) Red300 else Red700 val Typography.textFieldInput: TextStyle get() = TextStyle( / * ... */ ) val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
  29. @Immutable data class ExtendedColors( val tertiary: Color, val onTertiary: Color

    ) val LocalExtendedColors = staticCompositionLocalOf {
  30. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  31. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  32. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  33. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  34. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  35. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  36. Extend Material Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Text Image Text ImageButton CompositionLocalProvider
  37. Extend Material Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Text Image Text ImageButton CompositionLocalProvider ExtendedTheme /CompositionLocalProvider/
  38. Extend Material Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Image Text ImageButton Text CompositionLocalProvider ExtendedTheme /CompositionLocalProvider/ Text Text
  39. Extend Material Root MaterialTheme Scaffold TopAppBar Content FloatingActionButton Column Row

    Row Row Text Text Image Text ImageButton Text CompositionLocalProvider ExtendedTheme /CompositionLocalProvider/ Text Text
  40. fun ExtendedTheme( /* .. . * / content: @Composable ()

    - > Unit ) { val extendedColors = ExtendedColors( tertiary = Color(0xFFA8EFF0), onTertiary = Color(0xFF002021) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ... , typography = .. . , shapes = .. . */ content = content ) } } Extend Material
  41. // Use with eg. ExtendedTheme.colors.tertiary object ExtendedTheme { val colors:

    ExtendedColors @Composable get() = LocalExtendedColors.current } tertiary = Color(0xFFA8EFF0),
  42. object ExtendedTheme { fun ExtendedButton( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = ExtendedTheme.colors.tertiary, contentColor = ExtendedTheme.colors.onTertiary /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
  43. object ExtendedTheme { fun ExtendedButton( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = ExtendedTheme.colors.tertiary, contentColor = ExtendedTheme.colors.onTertiary /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
  44. object ExtendedTheme { fun ExtendedButton( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = ExtendedTheme.colors.tertiary, contentColor = ExtendedTheme.colors.onTertiary /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
  45. object ExtendedTheme { fun ExtendedButton( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = ExtendedTheme.colors.tertiary, contentColor = ExtendedTheme.colors.onTertiary /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
  46. object ExtendedTheme { fun ExtendedButton( onClick: () -> Unit, modifier:

    Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = ExtendedTheme.colors.tertiary, contentColor = ExtendedTheme.colors.onTertiary /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
  47. Customizing • Customize Material • Extend Material • Replace Material

    Token Systems • Implement fully-custom design system Material Theme
  48. Replace Tokens @Composable fun CustomTheme( /* .. . * /

    content: @Composable () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) }
  49. Replace Tokens @Composable fun CustomTheme( /* .. . * /

    content: @Composable () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) }
  50. Replace Tokens @Composable fun CustomTheme( /* .. . * /

    content: @Composable () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) }
  51. @Composable fun CustomTheme( /* .. . * / content: @Composable

    () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } Replace Tokens
  52. @Composable fun CustomTheme( /* .. . * / content: @Composable

    () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } Replace Tokens
  53. @Composable fun CustomTheme( /* .. . * / content: @Composable

    () - > Unit ) { val customColors = ... val customTypography = .. . val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } Replace Tokens
  54. @Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier =

    Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = CustomTheme.colors.component, contentColor = CustomTheme.colors.content, disabledBackgroundColor = CustomTheme.colors.content .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = ContentAlpha.disabled) ), shape = ButtonShape, LocalCustomColors provides customColors,
  55. .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = ContentAlpha.disabled)

    ), shape = ButtonShape, elevation = ButtonDefaults.elevation( defaultElevation = CustomTheme.elevation.default, pressedElevation = CustomTheme.elevation.pressed ), onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = CustomTheme.typography.body ) { content() } } ) } Replace Tokens
  56. Customizing • Customize Material • Extend Material • Replace Material

    Token Systems • Implement fully-custom design system Material Theme
  57. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  58. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  59. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  60. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  61. CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box

    @Composable fun Surface( @Composable fun Surface( modifier: Modifier = Modifier, content: @Composable () - > Unit ) { content() } { } } Surface
  62. CompositionLocalProvider( color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation:

    Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, content: @Composable ()
  63. ( modifier = modifier .shadow(shadowElevation, shape, clip = false) .then(if

    (border != null) Modifier.border(border, shape) else Modifier) .background( color = surfaceColorAtElevation(color, absoluteElevation), shape = shape ) .clip(shape) .semantics(mergeDescendants = false) {}, propagateMinConstraints = true ) content: @Composable ()
  64. ( modifier = modifier .shadow(shadowElevation, shape, clip = false) !=

    @Composable fun Surface( modifier: Modifier = Modifier, shape: Shape = RectangleShape, color: Color = MaterialTheme.colorScheme.surface, contentColor: Color = contentColorFor(color), tonalElevation: Dp = 0.dp, shadowElevation: Dp = 0.dp, border: BorderStroke? = null, content: @Composable () - > Unit ) { val absoluteElevation = LocalAbsoluteTonalElevation.current + tonalElevation CompositionLocalProvider( LocalContentColor provides contentColor, LocalAbsoluteTonalElevation provides absoluteElevation ) { Box Surface
  65. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  66. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  67. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  68. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  69. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  70. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  71. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  72. Card @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 ) { Surface( modifier = modifier, shape = shape, color = backgroundColor, contentColor = contentColor, elevation = elevation, border = border, content = content ) }
  73. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  74. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  75. @Composable fun Button( onClick: () -> Unit, modifier: Modifier =

    Modifier, content: @Composable RowScope.() -> Unit ) { Surface( onClick = onClick, modifier = modifier, ) { Row( content = content ) } } Button
  76. @Composable fun Button( onClick: () -> Unit, modifier: Modifier =

    Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, elevation: ButtonElevation? = ButtonDefaults.elevation(), shape: Shape = MaterialTheme.shapes.small, border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) { Surface( onClick = onClick, modifier = modifier, ) { Row( content = content ) Button
  77. enabled: Boolean = true, onClick = onClick, modifier = modifier,

    enabled = enabled, shape = shape, color = colors.backgroundColor(enabled).value, contentColor = contentColor.copy(alpha = 1f), border = border, elevation = elevation ?. elevation(enabled, interactionSource) ?. value ?: 0.dp, interactionSource = interactionSource, ) { Row( content = content ) } Button
  78. @Composable fun Button( onClick: () -> Unit, modifier: Modifier =

    Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, elevation: ButtonElevation? = ButtonDefaults.elevation(), shape: Shape = MaterialTheme.shapes.small, border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) { val contentColor by colors.contentColor(enabled) Surface( onClick = onClick, modifier = modifier, enabled = enabled, @Composable fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, elevation: ButtonElevation? = ButtonDefaults.elevation(), shape: Shape = MaterialTheme.shapes.small, border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) { val contentColor by colors.contentColor(enabled) Surface( onClick = onClick, modifier = modifier, enabled = enabled, Button
  79. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  80. Custom Theme Compose Material Colors Typography Shape Surface Scaffold AppBar

    Button Card Checkbox FloatingAction Button Icon ListItem Menu Snackbar Text
  81. Where we started • Already have design system • Color

    Tokens • Typography Tokens • Components
  82. Goals • Get started fast • Easy to use (no

    cheat sheet) • Access to all of the existing Material composables • Use Compose while implementing design system • Incrementally migrate away from Material
  83. • Customize Material • Extend Material • Replace Material Token

    Systems • Implement fully-custom design system Customizing Material Theme
  84. • Customize Material • Extend Material • Wrap Material •

    Replace Material Token Systems • Implement fully-custom design system Customizing Material Theme
  85. • Customize Material • Extend Material • Wrap Material Customizing

    Material Theme • Replace Material Token Systems • Implement fully-custom design system
  86. /** * Converts the Dig colors to Material colors, so

    that standard Material * composables use Dig colors out of the box. */ internal fun Colors.toMaterialColors(): MaterialColors = MaterialColors( primary = accent, primaryVariant = accent, secondary = accent, secondaryVariant = accent, background = standard.background, surface = standard.background, error = alert.text, onPrimary = if (isLight) secondary else standard.text, onSecondary = if (isLight) standard.text else secondary, onBackground = standard.text, onSurface = standard.text, onError = if (isLight) secondary else standard.text, isLight = isLight, ) Wrap Material
  87. @Immutable data class Typography internal constructor( val titleLarge: TextStyle, val

    titleStandard: TextStyle, val titleSmall: TextStyle, val labelXLarge: TextStyle, … ) internal fun Typography.toMaterialTypography(): MaterialTypography = MaterialTypography(
  88. val titleLarge: TextStyle, defaultFontFamily = DefaultFontFamily, h1 = titleLarge, h2

    = titleLarge, h3 = titleLarge, h4 = titleLarge, h5 = titleStandard, h6 = titleStandard.copy(fontWeight = FontWeight.Medium), subtitle1 = paragraphLarge, subtitle2 = paragraphStandard, body1 = paragraphLarge, body2 = paragraphStandard, button = labelLarge.copy(fontWeight = FontWeight.Medium, lineHeight = 24.sp), caption = labelSmall, overline = labelXSmall, ) Wrap Material
  89. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  90. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  91. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  92. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  93. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  94. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  95. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  96. Wrap Material @Composable fun DigTheme( typography: Typography = DigTheme.typography, colors:

    Colors = DigTheme.colors, content: @Composable () - > Unit ) { val rememberedColors = … CompositionLocalProvider( LocalColors provides rememberedColors, LocalTypography provides typography ) { MaterialTheme( colors = rememberedColors.toMaterialColors(), typography = typography.toMaterialTypography(), shapes = …, content = content ) } }
  97. Wrap Material DigTheme { Row { Button(onClick = { })

    { Text("Button") } Button( onClick = { }, enabled = false ) { Text("Disabled") } } }
  98. Wrap Material DigTheme { Row { Button(onClick = { })

    { Text("Button") } Button( onClick = { }, enabled = false ) { Text("Disabled") } } }
  99. Migration • Not all Material elements perform like we expect

    • Ex. TopAppBar uses accent color, we use surface color. • Mitigate by creating custom composables • Which implementation to use?
  100. Migration Button() TextButton() OutlinedButton() FloatingActionButton() Switch() Checkbox() RadioButton() Text() TextField()

    LinearProgressIndicator() Card() Button() TextButton() OutlinedButton() FloatingActionButton() Switch() Checkbox() RadioButton() Text() TextField() LinearProgressIndicator() Card() TopAppBar() NavigationBar()
  101. dependencies { api(libs.androidx.compose.ui) api(libs.androidx.compose.runtime) api(libs.androidx.compose.foundation) api(libs.androidx.compose.material) implementation(libs.androidx.activity.activity.compose) implementation(libs.androidx.compose.animation) implementation(libs.androidx.lifecycle.lifecycle.viewmodel.compose) implementation(libs.androidx.compose.ui.ui.tooling.preview)

    debugImplementation(libs.androidx.compose.ui.ui.tooling) } dependencies { api(libs.androidx.compose.ui) api(libs.androidx.compose.runtime) api(libs.androidx.compose.foundation) implementation(libs.androidx.compose.material) implementation(libs.androidx.activity.activity.compose) implementation(libs.androidx.compose.animation) implementation(libs.androidx.lifecycle.lifecycle.viewmodel.compose) implementation(libs.androidx.compose.ui.ui.tooling.preview) debugImplementation(libs.androidx.compose.ui.ui.tooling) } Migration