Slide 1

Slide 1 text

Kickstarting our Design System Omkar Pimple Android Core Engineering Fabio Carballo

Slide 2

Slide 2 text

Building a bank the world loves to use

Slide 3

Slide 3 text

Kick-Off Foundation Future Outlook

Slide 4

Slide 4 text

Why ? now?

Slide 5

Slide 5 text

15 Designers & Engineers 80 Designers & Engineers

Slide 6

Slide 6 text

Vienna Barcelona New York Berlin

Slide 7

Slide 7 text

Where do you start?

Slide 8

Slide 8 text

It’s a product

Slide 9

Slide 9 text

It can fail

Slide 10

Slide 10 text

Low Adoption Understanding Support

Slide 11

Slide 11 text

Slide 12

Slide 12 text

Do your research

Slide 13

Slide 13 text

• 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

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

#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

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

color.divider.standard

Slide 19

Slide 19 text

Color Type Icons Spacing Shadows Corner R’s Opacity

Slide 20

Slide 20 text

Take your time

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Main Atom Start Atom End Atom

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Let’s talk about implementation

Slide 25

Slide 25 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 26

Slide 26 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 27

Slide 27 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 28

Slide 28 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 29

Slide 29 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 30

Slide 30 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 31

Slide 31 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 32

Slide 32 text

Spacing Corner Radius Colors Typography Elevation Opacity

Slide 33

Slide 33 text

Declared ✅ Values ❓ Fundamentals

Slide 34

Slide 34 text

Spacing Corner Radius Colors Typography Elevation Opacity Define theme-independent fundamentals first!

Slide 35

Slide 35 text

Spacing Corner Radius Typography Elevation Opacity

Slide 36

Slide 36 text

Spacing <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>

Slide 37

Slide 37 text

Spacing <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>

Slide 38

Slide 38 text

@dimen/nxd_spacing_none @dimen/nxd_spacing_tightest @dimen/nxd_spacing_tighter @dimen/nxd_spacing_tight 16dp @dimen/nxd_spacing_loose @dimen/nxd_spacing_looser @dimen/nxd_spacing_loosest Spacing

Slide 39

Slide 39 text

Spacing <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>

Slide 40

Slide 40 text

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="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>

Slide 41

Slide 41 text

Spacing Corner Radius @dimen/nxd_corner_radius_hard @dimen/nxd_corner_radius_soft @dimen/nxd_corner_radius_smooth 8dp 12dp 16dp 24dp 32dp 48dp Opacity @dimen/nxd_op_opaque @dimen/nxd_op_quarter_transparent @dimen/nxd_op_semi_transparent @dimen/nxd_op_tq_transparent @dimen/nxd_op_transparent

Slide 42

Slide 42 text

Opacity @dimen/nxd_op_opaque @dimen/nxd_op_quarter_transparent @dimen/nxd_op_semi_transparent @dimen/nxd_op_tq_transparent @dimen/nxd_op_transparent Define theme-specific fundamentals!

Slide 43

Slide 43 text

@dimen/nxd_op_transparent Introducing … Bright Mode!

Slide 44

Slide 44 text

@dimen/nxd_op_transparent Introducing … Bright Mode!

Slide 45

Slide 45 text

@dimen/nxd_op_transparent Introducing … Bright Mode!

Slide 46

Slide 46 text

@dimen/nxd_op_transparent Introducing … Bright Mode! <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

Slide 47

Slide 47 text

<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>

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Android Q true

Slide 50

Slide 50 text

Android Q true

Slide 51

Slide 51 text

XML Programatically How to use our theme attributes?

Slide 52

Slide 52 text

How to use our theme attributes? XML

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

How to use our theme attributes? XML

Slide 55

Slide 55 text

How to use our theme attributes? XML spacingNone spacingDefault spacingTight spacingTightest spacingLoose spacingLoosest

Slide 56

Slide 56 text

How to use our theme attributes? XML spacingNone spacingDefault spacingTight spacingTightest spacingLoose spacingLoosest

Slide 57

Slide 57 text

How to use our theme attributes? XML spacingNone spacingDefault spacingTight spacingTightest spacingLoose spacingLoosest ?attr/

Slide 58

Slide 58 text

How to use our theme attributes? XML spacingDefault ?attr/

Slide 59

Slide 59 text

Slide 60

Slide 60 text

Slide 61

Slide 61 text

How to use our theme attributes? Programatically

Slide 62

Slide 62 text

How to use our theme attributes? Programatically

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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)

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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)

Slide 75

Slide 75 text

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)

Slide 76

Slide 76 text

Building Components

Slide 77

Slide 77 text

Custom Views Building Components More control Restrict possible interactions

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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) }

Slide 80

Slide 80 text

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) }

Slide 81

Slide 81 text

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) }

Slide 82

Slide 82 text

Okay .. But how can we ensure an easy migration?

Slide 83

Slide 83 text

Hide it while it’s not stable Migration

Slide 84

Slide 84 text

Static Analysis Migration

Slide 85

Slide 85 text

Static Analysis Migration Detekt Lint

Slide 86

Slide 86 text

Static Analysis Migration Detekt Lint My interactive Text View

Slide 87

Slide 87 text

Static Analysis Migration Detekt Lint My interactive Text View

Slide 88

Slide 88 text

Static Analysis Migration Detekt Lint @color/nxd_teal

Slide 89

Slide 89 text

Static Analysis Migration Detekt Lint @color/nxd_rhubarb

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

val color = context.themeColor(R.attr.colorTypographyInteractive) Static Analysis Migration Detekt Lint val color = context.getColor(R.color.nxd_teal_91)

Slide 93

Slide 93 text

RecyclerView Magic

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

listOf(

Slide 97

Slide 97 text

SectionHeaderViewEntity(“Transactions"), listOf(

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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”),

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

Server elements: [ { type: “sectionHeader”, ... }, { type: “subSectionHeader”, ... }, { type: "transaction", ... }, { type: “transaction", ... } ] App Transformer Transaction ViewEntity SubSectionHeader ViewEntity SectionHeader ViewEntity RecyclerView

Slide 102

Slide 102 text

System Glossary

Slide 103

Slide 103 text

No content

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

No content

Slide 106

Slide 106 text

No content

Slide 107

Slide 107 text

No content

Slide 108

Slide 108 text

No content

Slide 109

Slide 109 text

No content

Slide 110

Slide 110 text

Make it visible

Slide 111

Slide 111 text

Where to next?

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

Thank you

Slide 114

Slide 114 text

Kickstarting our Design System Omkar Pimple (plonkgames.in) Android Core Engineering Fabio Carballo (@fabiocarballo)