Kickstarting our Design System

Kickstarting our Design System

As your application, product and team rapidly develops and scales, you will undoubtedly have inconsistent UI components. It’s easy for developers and designers to create new elements that differ in really small specifications. This not only creates a bad branding experience, but also the feeling that there are several apps and styles inside the same app. In this talk, we will show you some design system fundamentals, how to properly map them to the Android framework and how you can build re-usable components on top of it. We will also tackle what are the biggest problems migrating to a design system from nothing, and how you should approach it. By the end of the talk, we just hope that you go and meet your designer and start developing your design system.

71b4edb678001f55b47bdde0741a0ff5?s=128

Fábio Carballo

September 19, 2019
Tweet

Transcript

  1. Kickstarting our Design System Omkar Pimple Android Core Engineering Fabio

    Carballo
  2. Building a bank the world loves to use

  3. Kick-Off Foundation Future Outlook

  4. Why ? now?

  5. 15 Designers & Engineers 80 Designers & Engineers

  6. Vienna Barcelona New York Berlin

  7. Where do you start?

  8. It’s a product

  9. It can fail

  10. Low Adoption Understanding Support

  11. Do your research

  12. • Some Colours • Text Styles • A few Components

    • Not much Documentation • A lot of Colours • No Text Styles • Some Components • No Documentation • A lot of Colours • Text Styles • Some Components • Some Documentation iOS Android Design
  13. None
  14. That one “Divider” Grey Is this the right grey? I

    thought it was this one? Yeah it should be the grey we used in that other screen :) Hmm, I was using this to be honest
  15. #1B1B1B &color-gray-11 #202020 &color-gray-13 #000000 &color-gray-0 #FFFFFF &color-gray-100 #747474 &color-gray-45

    #E6E6E6 &color-gray-90 #FBFBFB &color-gray-98 Name Value Visualization #2D2D2D &color-gray-18 #333333 &color-gray-20 #BFBFBF &color-gray-75 #2A7E6D &color-teal-49 Value Visualization Name
  16. None
  17. color.divider.standard

  18. Color Type Icons Spacing Shadows Corner R’s Opacity

  19. Take your time

  20. None
  21. Main Atom Start Atom End Atom

  22. None
  23. Let’s talk about implementation

  24. Spacing Corner Radius Colors Typography Elevation Opacity

  25. Spacing Corner Radius Colors Typography Elevation Opacity <resources> <attr name="attributeName"

    format="reference" /> </resources>
  26. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="spacingNone" format="reference"

    /> <attr name="spacingTightest" format="reference" /> <attr name="spacingTighter" format="reference" /> <attr name="spacingTight" format="reference" /> <attr name="spacingDefault" format="reference" /> <attr name="spacingLoose" format="reference" /> <attr name="spacingLooser" format="reference" /> <attr name="spacingLoosest" format="reference" />
  27. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="cornerRadiusHard" format="reference"

    /> <attr name="cornerRadiusSoft" format="reference" /> <attr name="cornerRadiusSmooth" format="reference" />
  28. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="colorTypographyDefault" format="reference"

    /> <attr name="colorTypographyDefaultInverted" format="reference" /> <attr name="colorBackgroundDefault" format="reference" /> <attr name="colorBackgroundCard" format="reference" /> <attr name="colorIconographyDefault" format="reference" /> <attr name="colorDividerDefault" format="reference" />
  29. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="colorTypographyDefault" format="reference"

    /> <attr name="colorTypographyDefaultInverted" format="reference" /> <attr name="colorBackgroundDefault" format="reference" /> <attr name="colorBackgroundCard" format="reference" /> <attr name="colorIconographyDefault" format="reference" /> <attr name="colorDividerDefault" format="reference" />
  30. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="colorTypographyDefault" format="reference"

    /> <attr name="colorTypographyDefaultInverted" format="reference" /> <attr name="colorBackgroundDefault" format="reference" /> <attr name="colorBackgroundCard" format="reference" /> <attr name="colorIconographyDefault" format="reference" /> <attr name="colorDividerDefault" format="reference" />
  31. Spacing Corner Radius Colors Typography Elevation Opacity <attr name="colorTypographyDefault" format="reference"

    /> <attr name="colorTypographyDefaultInverted" format="reference" /> <attr name="colorBackgroundDefault" format="reference" /> <attr name="colorBackgroundCard" format="reference" /> <attr name="colorIconographyDefault" format="reference" /> <attr name="colorDividerDefault" format="reference" />
  32. Declared ✅ Values ❓ Fundamentals

  33. Spacing Corner Radius Colors Typography Elevation Opacity Define theme-independent fundamentals

    first!
  34. Spacing Corner Radius Typography Elevation Opacity <style name="Nxd" /> <style

    name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> </style>
  35. Spacing <style name="Nxd" /> <style name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="spacingNone">@dimen/nxd_spacing_none</item> <item

    name="spacingTightest">@dimen/nxd_spacing_tightest</item> <item name="spacingTighter">@dimen/nxd_spacing_tighter</item> <item name="spacingTight">@dimen/nxd_spacing_tight</item> <item name="spacingDefault">@dimen/nxd_spacing_default</item> <item name="spacingLoose">@dimen/nxd_spacing_loose</item> <item name="spacingLooser">@dimen/nxd_spacing_looser</item> <item name="spacingLoosest">@dimen/nxd_spacing_loosest</item>
  36. Spacing <style name="Nxd" /> <style name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="spacingNone">@dimen/nxd_spacing_none</item> <item

    name="spacingTightest">@dimen/nxd_spacing_tightest</item> <item name="spacingTighter">@dimen/nxd_spacing_tighter</item> <item name="spacingTight">@dimen/nxd_spacing_tight</item> <item name="spacingDefault">@dimen/nxd_spacing_default</item> <item name="spacingLoose">@dimen/nxd_spacing_loose</item> <item name="spacingLooser">@dimen/nxd_spacing_looser</item> <item name="spacingLoosest">@dimen/nxd_spacing_loosest</item>
  37. <item name="spacingNone">@dimen/nxd_spacing_none</item> <item name="spacingTightest">@dimen/nxd_spacing_tightest</item> <item name="spacingTighter">@dimen/nxd_spacing_tighter</item> <item name="spacingTight">@dimen/nxd_spacing_tight</item> <item name="spacingDefault">16dp</item>

    <item name="spacingLoose">@dimen/nxd_spacing_loose</item> <item name="spacingLooser">@dimen/nxd_spacing_looser</item> <item name="spacingLoosest">@dimen/nxd_spacing_loosest</item> Spacing <style name="Nxd" /> <style name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
  38. Spacing <style name="Nxd" /> <style name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="spacingNone">0dp/item> <item

    name="spacingTightest">4dp</item> <item name="spacingTighter">8dp</item> <item name="spacingTight">12dp</item> <item name="spacingDefault">16dp</item> <item name="spacingLoose">24dp</item> <item name="spacingLooser">32dp</item> <item name="spacingLoosest">48dp</item>
  39. Spacing <style name="Nxd" /> <style name=“Nxd.BaseTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> Corner Radius <item

    name="cornerRadiusHard">@dimen/nxd_corner_radius_hard</item> <item name="cornerRadiusSoft">@dimen/nxd_corner_radius_soft</item> <item name="cornerRadiusSmooth">@dimen/nxd_corner_radius_smooth</item> <item name="spacingNone">0dp/item> <item name="spacingTightest">4dp</item> <item name="spacingTighter">8dp</item> <item name="spacingTight">12dp</item> <item name="spacingDefault">16dp</item> <item name="spacingLoose">24dp</item> <item name="spacingLooser">32dp</item> <item name="spacingLoosest">48dp</item>
  40. Spacing Corner Radius <item name="cornerRadiusHard">@dimen/nxd_corner_radius_hard</item> <item name="cornerRadiusSoft">@dimen/nxd_corner_radius_soft</item> <item name="cornerRadiusSmooth">@dimen/nxd_corner_radius_smooth</item> <item

    name="spacingTighter">8dp</item> <item name="spacingTight">12dp</item> <item name="spacingDefault">16dp</item> <item name="spacingLoose">24dp</item> <item name="spacingLooser">32dp</item> <item name="spacingLoosest">48dp</item> Opacity <item name=“opacityOpaque">@dimen/nxd_op_opaque</item> <item name=“opacityQuarterTransparent”>@dimen/nxd_op_quarter_transparent</item> <item name=“opacitySemiTransparent">@dimen/nxd_op_semi_transparent</item> <item name="opacityThreeQuarterTransparent">@dimen/nxd_op_tq_transparent</item> <item name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style>
  41. Opacity <item name=“opacityOpaque">@dimen/nxd_op_opaque</item> <item name=“opacityQuarterTransparent”>@dimen/nxd_op_quarter_transparent</item> <item name=“opacitySemiTransparent">@dimen/nxd_op_semi_transparent</item> <item name="opacityThreeQuarterTransparent">@dimen/nxd_op_tq_transparent</item> <item

    name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style> Define theme-specific fundamentals!
  42. <item name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style> Introducing … Bright Mode! <style name="Nxd.BaseTheme.Bright">

  43. <item name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style> Introducing … Bright Mode! <style name="Nxd.BaseTheme.Bright">

  44. <item name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style> Introducing … Bright Mode! <style name="Nxd.BaseTheme.Bright">

  45. <item name=“opacityTransparent">@dimen/nxd_op_transparent</item> </style> Introducing … Bright Mode! <style name="Nxd.BaseTheme.Bright"> <item

    name="colorTypographyDefault">@color/grey_11</item> <item name="colorTypographyDefaultInverted">@color/grey_100</item> <item name="colorBackgroundDefault">@color/grey_100</item> <item name="colorBackgroundCard">@color/grey_100</item> <item name="colorIconographyDefault">@color/grey_11</item> <item name="colorDividerDefault">@color/grey_90</item> Colors
  46. <style name="Nxd.BaseTheme.Bright"> <item name="colorTypographyDefault">@color/grey_11</item> <item name="colorTypographyDefaultInverted">@color/grey_100</item> <item name="colorBackgroundDefault">@color/grey_100</item> <item name="colorBackgroundCard">@color/grey_100</item>

    <item name="colorIconographyDefault">@color/grey_11</item> <item name="colorDividerDefault">@color/grey_90</item> <style name="Nxd.BaseTheme.Dark" /> <item name="colorTypographyDefault">@color/grey_100</item> <item name="colorTypographyDefaultInverted">@color/grey_11</item> <item name="colorBackgroundDefault">@color/grey_18</item> <item name="colorBackgroundCard">@color/grey_13</item> <item name="colorIconographyDefault">@color/grey_100</item> <item name="colorDividerDefault">@color/grey_20</item>
  47. None
  48. Android Q <item name="android:forceDarkAllowed">true</item>

  49. Android Q <item name="android:forceDarkAllowed">true</item>

  50. XML Programatically How to use our theme attributes?

  51. How to use our theme attributes? XML

  52. How to use our theme attributes? XML ?attr/attributeName

  53. How to use our theme attributes? XML <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"

    android:layout_margin=" "/>
  54. How to use our theme attributes? XML spacingNone spacingDefault spacingTight

    spacingTightest spacingLoose spacingLoosest <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin=" "/>
  55. How to use our theme attributes? XML spacingNone spacingDefault spacingTight

    spacingTightest spacingLoose spacingLoosest <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin=" "/>
  56. How to use our theme attributes? XML spacingNone spacingDefault spacingTight

    spacingTightest spacingLoose spacingLoosest <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin=" "/> ?attr/
  57. How to use our theme attributes? XML spacingDefault <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:layout_margin=" "/> ?attr/
  58. <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin=" “ android:textColor=“?attr/colorTypographyInteractive“ /> How to use

    our theme attributes? XML spacingDefault ?attr/
  59. <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin=" “ android:textColor=“?attr/colorTypographyInteractive“ /> How to use

    our theme attributes? XML spacingDefault ?attr/
  60. How to use our theme attributes? Programatically

  61. How to use our theme attributes? Programatically

  62. How to use our theme attributes? Programatically @AnyRes private fun

    Context.themeResourceId(@AttrRes attrRes: Int, expectedResourceType: String): Int { } val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  63. How to use our theme attributes? @AnyRes private fun Context.themeResourceId(@AttrRes

    attrRes: Int, expectedResourceType: String): Int { } Programatically val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  64. How to use our theme attributes? @AnyRes private fun Context.themeResourceId(@AttrRes

    attrRes: Int, expectedResourceType: String): Int { } Programatically val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  65. How to use our theme attributes? @AnyRes private fun Context.themeResourceId(@AttrRes

    attrRes: Int, expectedResourceType: String): Int { } Programatically val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  66. How to use our theme attributes? @AnyRes private fun Context.themeResourceId(@AttrRes

    attrRes: Int, expectedResourceType: String): Int { } Programatically val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  67. How to use our theme attributes? @AnyRes private fun Context.themeResourceId(@AttrRes

    attrRes: Int, expectedResourceType: String): Int { } Programatically val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) val resId = typedValue.resourceId val resourceType = resources.getResourceTypeName(resId) if (expectedResourceType != resourceType) { val attributeName = resources.getResourceName(attrRes) throw IllegalArgumentException("No resource found with the given type .......") } return resId
  68. How to use our theme attributes? Programatically @ColorRes fun Context.themeColorResId(@AttrRes

    attrRes: Int) = themeResourceId(attrRes, "color")
  69. How to use our theme attributes? Programatically @ColorRes fun Context.themeColorResId(@AttrRes

    attrRes: Int) = themeResourceId(attrRes, "color")
  70. How to use our theme attributes? Programatically @ColorRes fun Context.themeColorResId(@AttrRes

    attrRes: Int) = themeResourceId(attrRes, "color") // Example val colorResId = context.themeColorResId(R.attr.colorBackgroundDefault)
  71. How to use our theme attributes? Programatically fun Context.themeDimensionPixelSize(@AttrRes attrRes:

    Int) = resources.getDimensionPixelSize(themeResourceId(attrRes, "dimen"))
  72. How to use our theme attributes? Programatically fun Context.themeDimensionPixelSize(@AttrRes attrRes:

    Int) = resources.getDimensionPixelSize(themeResourceId(attrRes, "dimen"))
  73. How to use our theme attributes? Programatically fun Context.themeDimensionPixelSize(@AttrRes attrRes:

    Int) = resources.getDimensionPixelSize(themeResourceId(attrRes, "dimen")) val defaultPadding = context.themeDimensionPixelSize(R.attr.spacingDefault) setPadding(defaultPadding, 0, defaultPadding 0)
  74. How to use our theme attributes? Programatically fun Context.themeDimensionPixelSize(@AttrRes attrRes:

    Int) = resources.getDimensionPixelSize(themeResourceId(attrRes, "dimen")) val defaultPadding = context.themeDimensionPixelSize(R.attr.spacingDefault) setPadding(defaultPadding, 0, defaultPadding 0)
  75. Building Components

  76. Custom Views Building Components More control Restrict possible interactions

  77. Building Components class MyCustomView: View { fun bind(viewEntity: ViewEntity) data

    class ViewEntity }
  78. Building Components class TransactionContainer: FrameLayout { fun bind(viewEntity: ViewEntity) {

    title = viewEntity.title subtitle = viewEntity.title amount.bind(AmountViewEntity(amount, currency)) setOnClickListener { clickAction() } } data class ViewEntity( val title: String, val subtitle: String, val amount: Money, val currency: Currency, val clickAction: () -> Unit) }
  79. Building Components class TransactionContainer: FrameLayout { fun bind(viewEntity: ViewEntity) {

    title = viewEntity.title subtitle = viewEntity.title amount.bind(AmountViewEntity(amount, currency)) setOnClickListener { clickAction() } } data class ViewEntity( val title: String, val subtitle: String, val amount: Money, val currency: Currency, val clickAction: () -> Unit) }
  80. Building Components class TransactionContainer: FrameLayout { fun bind(viewEntity: ViewEntity) {

    title = viewEntity.title subtitle = viewEntity.title amount.bind(AmountViewEntity(amount, currency)) setOnClickListener { clickAction() } } data class ViewEntity( val title: String, val subtitle: String, val amount: Money, val currency: Currency, val clickAction: () -> Unit) }
  81. Okay .. But how can we ensure an easy migration?

  82. Hide it while it’s not stable Migration

  83. Static Analysis Migration

  84. Static Analysis Migration Detekt Lint

  85. Static Analysis Migration Detekt Lint My interactive Text View

  86. Static Analysis Migration Detekt Lint My interactive Text View

  87. Static Analysis Migration Detekt Lint <item name="colorTypograhyInteractive">@color/nxd_teal</item>

  88. Static Analysis Migration Detekt Lint <item name="colorTypograhyInteractive">@color/nxd_rhubarb</item>

  89. Static Analysis Migration Detekt Lint val color = context.getColor(R.color.nxd_teal_91) <TextView

    android:textColor=“@color/nxd_teal_91" />
  90. Static Analysis Migration Detekt Lint val color = context.getColor(R.color.nxd_teal_91) <TextView

    android:textColor=“@color/nxd_teal_91" />
  91. val color = context.themeColor(R.attr.colorTypographyInteractive) <TextView android:textColor="?attr/colorTypographyInteractive" /> Static Analysis Migration

    Detekt Lint val color = context.getColor(R.color.nxd_teal_91) <TextView android:textColor=“@color/nxd_teal_91" />
  92. RecyclerView Magic

  93. TransactionContainer ViewEntity SectionHeader ViewEntity SubSectionHeader ViewEntity ButtonContainer ViewEntity InfoCard ViewEntity

    RecyclerView NXDRecyclerView Adapter System Level ViewEntity to ViewHolder/Binder TransactionContainer ViewHolder/Binder SectionHeader ViewHolder/Binder SubSectionHeader ViewHolder/Binder ButtonContainer ViewHolder/Binder InfoCard ViewHolder/Binder
  94. None
  95. listOf(

  96. SectionHeaderViewEntity(“Transactions"), listOf(

  97. Vi listOf( SubSectionHeaderViewEntity(“Today”), SectionHeaderViewEntity("Transactions"),

  98. TransactionContainerViewEntity( "From Main Account", " to Home", Money(BigDecimal(5), Euro)), TransactionContainerViewEntity(

    "From Main Account", "Last one ... ", Money(BigDecimal(4.50), Euro)), TransactionContainerViewEntity( "From Main Account", "Just one more ", Money(BigDecimal(5), Euro)), TransactionContainerViewEntity( "From Main Account", "Money for drinks!", Money(BigDecimal(5), Euro)))) SectionHeaderViewEntity("Transactions"), listOf( SubSectionHeaderViewEntity(“Today”),
  99. Server elements: [ { type: “sectionHeader”, ... }, { type:

    “subSectionHeader”, ... }, { type: "transaction", ... }, { type: “transaction", ... } ] App
  100. Server elements: [ { type: “sectionHeader”, ... }, { type:

    “subSectionHeader”, ... }, { type: "transaction", ... }, { type: “transaction", ... } ] App Transformer Transaction ViewEntity SubSectionHeader ViewEntity SectionHeader ViewEntity RecyclerView
  101. System Glossary

  102. None
  103. None
  104. None
  105. None
  106. None
  107. None
  108. None
  109. Make it visible

  110. Where to next?

  111. Feature Design Conceptualisation Does the design contain a new component

    that is not in NXD? Check the NXD Glossary App and look for conceptually similar components Can the new component be replaced with something that exists in NXD? Create proposal for why we need the new component inline with the NXD contribution guidelines Submit Proposal Replace Component No Action Required YES NO YES NO
  112. Thank you

  113. Kickstarting our Design System Omkar Pimple (plonkgames.in) Android Core Engineering

    Fabio Carballo (@fabiocarballo)