From 9fc128d51b2aaeb4b31f151ddb938cc8f3c47634 Mon Sep 17 00:00:00 2001
From: ialokim <ialokim@mailbox.org>
Date: Sun, 8 May 2022 17:01:46 +0200
Subject: [PATCH] WIP: use consistent theming throughout Briar Desktop

Theming was inspired by Briar Android at first, but then quite heavily adapted to fit the Desktop use case. Uses mainly colors defined in Briar Styleguide with some custom overlay to distinguish different parts of the UI.
---
 .../desktop/contact/ConnectionIndicator.kt    | 43 ++++++++++++++
 .../briar/desktop/contact/ContactCard.kt      | 57 ++++++++----------
 .../briar/desktop/contact/ContactList.kt      | 23 ++++----
 .../contact/add/remote/AddContactDialog.kt    |  3 +-
 .../conversation/ConversationHeader.kt        | 21 ++-----
 .../conversation/ConversationScreen.kt        |  3 +-
 .../conversation/PrivateMessageScreen.kt      | 37 ++++++------
 .../introduction/ContactDrawerMakeIntro.kt    |  5 +-
 .../briar/desktop/login/ErrorScreen.kt        | 59 ++++++++++---------
 .../briar/desktop/login/StartupScreen.kt      | 41 +++++++------
 .../briar/desktop/navigation/BriarSidebar.kt  | 33 ++++++++---
 .../briar/desktop/theme/Colors.kt             | 30 +++++++---
 .../briarproject/briar/desktop/theme/Theme.kt | 25 ++++----
 .../briar/desktop/ui/BackgroundSurface.kt     | 51 ++++++++++++++++
 .../briar/desktop/ui/MessageCounter.kt        |  7 +--
 .../briar/desktop/ui/UiPlaceholder.kt         | 12 ++--
 .../briar/desktop/utils/PreviewUtils.kt       |  3 +-
 17 files changed, 282 insertions(+), 171 deletions(-)
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/contact/ConnectionIndicator.kt
 create mode 100644 src/main/kotlin/org/briarproject/briar/desktop/ui/BackgroundSurface.kt

diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ConnectionIndicator.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ConnectionIndicator.kt
new file mode 100644
index 0000000000..e3c80ae5c8
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ConnectionIndicator.kt
@@ -0,0 +1,43 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.contact
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import org.briarproject.briar.desktop.theme.contactConnected
+import org.briarproject.briar.desktop.theme.outline
+
+@Composable
+fun ConnectionIndicator(
+    modifier: Modifier = Modifier.size(16.dp),
+    isConnected: Boolean,
+    notConnectedColor: Color = Color.Transparent,
+) = Box(
+    modifier = modifier
+        .border(1.dp, MaterialTheme.colors.outline, CircleShape)
+        .background(if (isConnected) MaterialTheme.colors.contactConnected else notConnectedColor, CircleShape)
+)
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
index 0e98b2bb6f..9622f9083b 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactCard.kt
@@ -18,8 +18,7 @@
 
 package org.briarproject.briar.desktop.contact
 
-import androidx.compose.foundation.Canvas
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -30,8 +29,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Card
+import androidx.compose.foundation.selection.selectable
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
@@ -41,13 +39,12 @@ import androidx.compose.material.icons.filled.Delete
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis
 import androidx.compose.ui.unit.dp
 import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.bramble.api.identity.AuthorId
-import org.briarproject.briar.desktop.theme.outline
 import org.briarproject.briar.desktop.theme.selectedCard
-import org.briarproject.briar.desktop.theme.surfaceVariant
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 import org.briarproject.briar.desktop.ui.HorizontalDivider
 import org.briarproject.briar.desktop.ui.MessageCounter
@@ -90,13 +87,16 @@ fun ContactCard(
     onRemovePending: () -> Unit,
     padding: PaddingValues = PaddingValues(0.dp),
 ) {
-    val bgColor = if (selected) MaterialTheme.colors.selectedCard else MaterialTheme.colors.surfaceVariant
+    val bgColor = if (selected) MaterialTheme.colors.selectedCard else Color.Transparent
 
-    Card(
-        modifier = Modifier.fillMaxWidth().defaultMinSize(minHeight = HEADER_SIZE).clickable(onClick = onSel),
-        shape = RoundedCornerShape(0.dp),
-        backgroundColor = bgColor,
-        contentColor = MaterialTheme.colors.onSurface
+    Column(
+        modifier = Modifier
+            .fillMaxWidth()
+            .defaultMinSize(minHeight = HEADER_SIZE)
+            .selectable(selected, onClick = onSel)
+            .background(bgColor)
+            .padding(vertical = 8.dp),
+        verticalArrangement = Arrangement.Center
     ) {
         when (contactItem) {
             is ContactItem -> {
@@ -112,16 +112,15 @@ fun ContactCard(
 
 @Composable
 private fun RealContactRow(contactItem: ContactItem, padding: PaddingValues) {
-    val outlineColor = MaterialTheme.colors.outline
-    val briarSecondary = MaterialTheme.colors.secondary
-    val briarSurfaceVar = MaterialTheme.colors.surfaceVariant
-
-    Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.padding(padding)) {
+    Row(
+        horizontalArrangement = Arrangement.SpaceBetween,
+        modifier = Modifier.padding(padding),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
         Row(
-            modifier = Modifier.align(Alignment.CenterVertically).padding(start = 16.dp, end = 8.dp)
-                .weight(1f, fill = false)
+            modifier = Modifier.padding(horizontal = 16.dp).weight(1f)
         ) {
-            Box(modifier = Modifier.align(Alignment.CenterVertically)) {
+            Box {
                 ProfileCircle(36.dp, contactItem)
                 MessageCounter(
                     unread = contactItem.unread,
@@ -130,19 +129,11 @@ private fun RealContactRow(contactItem: ContactItem, padding: PaddingValues) {
             }
             RealContactInfo(
                 contactItem = contactItem,
-                modifier = Modifier.align(Alignment.CenterVertically)
             )
         }
-        Canvas(
-            modifier = Modifier.size(24.dp).align(Alignment.CenterVertically),
-            onDraw = {
-                val size = 16.dp
-                drawCircle(color = outlineColor, radius = size.toPx() / 2f)
-                drawCircle(
-                    color = if (contactItem.isConnected) briarSecondary else briarSurfaceVar,
-                    radius = (size - 2.dp).toPx() / 2f
-                )
-            }
+        ConnectionIndicator(
+            modifier = Modifier.padding(end = 18.dp).size(16.dp),
+            isConnected = contactItem.isConnected
         )
     }
 }
@@ -151,8 +142,8 @@ private fun RealContactRow(contactItem: ContactItem, padding: PaddingValues) {
 private fun PendingContactRow(contactItem: PendingContactItem, onRemove: () -> Unit, padding: PaddingValues) {
     Row(horizontalArrangement = Arrangement.SpaceBetween) {
         Row(
-            modifier = Modifier.align(Alignment.CenterVertically).padding(start = 16.dp, end = 8.dp)
-                .weight(1f, fill = false)
+            modifier = Modifier.align(Alignment.CenterVertically).padding(horizontal = 16.dp)
+                .weight(1f)
         ) {
             ProfileCircle(36.dp)
             PendingContactInfo(
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
index b0324ffa50..9e0dc02552 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
@@ -32,13 +32,12 @@ import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollbarAdapter
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Scaffold
+import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import org.briarproject.briar.desktop.theme.surfaceVariant
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.Constants.COLUMN_WIDTH
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 
@@ -54,10 +53,11 @@ fun ContactList(
 ) {
     val scrollState = rememberLazyListState()
 
-    Scaffold(
+    BackgroundSurface(
         modifier = Modifier.fillMaxHeight().width(COLUMN_WIDTH),
-        backgroundColor = MaterialTheme.colors.surfaceVariant,
-        topBar = {
+        overlayAlpha = 0.04f
+    ) {
+        Column {
             Column(
                 modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp),
             ) {
@@ -67,10 +67,9 @@ fun ContactList(
                     onContactAdd = onContactAdd,
                 )
             }
-        },
-        content = { padding ->
-            Box(modifier = Modifier.padding(padding).fillMaxSize()) {
-                LazyColumn(state = scrollState) {
+
+            Box(modifier = Modifier.fillMaxSize()) {
+                LazyColumn(state = scrollState, modifier = Modifier.selectableGroup()) {
                     items(contactList) { contactItem ->
                         ContactCard(
                             contactItem,
@@ -87,6 +86,6 @@ fun ContactList(
                     modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight()
                 )
             }
-        },
-    )
+        }
+    }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt
index c497b390ef..c162d9bee9 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/add/remote/AddContactDialog.kt
@@ -95,6 +95,7 @@ import org.briarproject.briar.desktop.dialogs.DialogType.WARNING
 import org.briarproject.briar.desktop.theme.Orange500
 import org.briarproject.briar.desktop.theme.Red500
 import org.briarproject.briar.desktop.theme.surfaceVariant
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.Constants.DIALOG_WIDTH
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18nF
@@ -203,7 +204,7 @@ fun AddContactDialog(
         val scaffoldState = rememberScaffoldState()
         val coroutineScope = rememberCoroutineScope()
         val aliasFocusRequester = remember { FocusRequester() }
-        Surface {
+        BackgroundSurface {
             Scaffold(
                 modifier = Modifier.padding(horizontal = 24.dp).padding(top = 24.dp, bottom = 12.dp),
                 topBar = {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
index a161b184b3..ea9a365d2e 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
@@ -18,7 +18,6 @@
 
 package org.briarproject.briar.desktop.conversation
 
-import androidx.compose.foundation.Canvas
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
@@ -38,14 +37,12 @@ import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.drawscope.withTransform
 import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis
 import androidx.compose.ui.unit.dp
+import org.briarproject.briar.desktop.contact.ConnectionIndicator
 import org.briarproject.briar.desktop.contact.ContactDropDown
 import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ProfileCircle
-import org.briarproject.briar.desktop.theme.outline
-import org.briarproject.briar.desktop.theme.surfaceVariant
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 import org.briarproject.briar.desktop.ui.HorizontalDivider
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
@@ -59,9 +56,6 @@ fun ConversationHeader(
     onDeleteContact: () -> Unit,
 ) {
     val (menuState, setMenuState) = remember { mutableStateOf(ContactDropDown.State.CLOSED) }
-    val onlineColor =
-        if (contactItem.isConnected) MaterialTheme.colors.secondary else MaterialTheme.colors.surfaceVariant
-    val outlineColor = MaterialTheme.colors.outline
 
     Box(modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp)) {
         Row(
@@ -71,15 +65,10 @@ fun ConversationHeader(
             Row(modifier = Modifier.fillMaxHeight().padding(start = 8.dp).weight(1f, fill = false)) {
                 Box(modifier = Modifier.align(Alignment.CenterVertically)) {
                     ProfileCircle(36.dp, contactItem)
-                    Canvas(
-                        modifier = Modifier,
-                        onDraw = {
-                            val size = 10.dp.toPx()
-                            withTransform({ translate(left = 30f, top = 30f) }) {
-                                drawCircle(color = outlineColor, radius = (size + 2.dp.toPx()) / 2f)
-                                drawCircle(color = onlineColor, radius = size / 2f)
-                            }
-                        }
+                    ConnectionIndicator(
+                        modifier = Modifier.align(Alignment.BottomEnd).size(12.dp),
+                        isConnected = contactItem.isConnected,
+                        notConnectedColor = MaterialTheme.colors.background,
                     )
                 }
                 Text(
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
index 89abe704c9..bbfba16678 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
@@ -45,7 +45,6 @@ import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.briar.desktop.contact.ContactInfoDrawer
 import org.briarproject.briar.desktop.contact.ContactInfoDrawerState
 import org.briarproject.briar.desktop.navigation.SIDEBAR_WIDTH
-import org.briarproject.briar.desktop.theme.surfaceVariant
 import org.briarproject.briar.desktop.ui.Constants.COLUMN_WIDTH
 import org.briarproject.briar.desktop.ui.Loader
 import org.briarproject.briar.desktop.viewmodel.viewModel
@@ -135,7 +134,7 @@ fun ConversationScreen(
             Column(
                 modifier = Modifier.fillMaxHeight().width(COLUMN_WIDTH)
                     .offset(maxWidth + animatedInfoDrawerOffsetX)
-                    .background(MaterialTheme.colors.surfaceVariant)
+                    .background(MaterialTheme.colors.background)
             ) {
                 ContactInfoDrawer(
                     contactItem,
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
index 1b374b6dc8..f99908b6af 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/PrivateMessageScreen.kt
@@ -48,6 +48,7 @@ import org.briarproject.briar.desktop.contact.PendingContactIdWrapper
 import org.briarproject.briar.desktop.contact.RealContactIdWrapper
 import org.briarproject.briar.desktop.contact.add.remote.AddContactDialog
 import org.briarproject.briar.desktop.contact.add.remote.AddContactViewModel
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.BriarLogo
 import org.briarproject.briar.desktop.ui.Constants.PARAGRAPH_WIDTH
 import org.briarproject.briar.desktop.ui.VerticalDivider
@@ -132,21 +133,23 @@ fun PendingContactSelected() = Explainer(
 
 @Composable
 fun Explainer(headline: String, text: String, content: @Composable () -> Unit = {}) =
-    Column(
-        modifier = Modifier.padding(16.dp).fillMaxSize(),
-        verticalArrangement = Arrangement.Center,
-        horizontalAlignment = Alignment.CenterHorizontally
-    ) {
-        BriarLogo(modifier = Modifier.size(200.dp))
-        Text(
-            text = headline,
-            modifier = Modifier.padding(top = 16.dp, bottom = 4.dp),
-            style = MaterialTheme.typography.h3
-        )
-        Text(
-            text = text,
-            modifier = Modifier.padding(top = 4.dp, bottom = 16.dp).widthIn(max = PARAGRAPH_WIDTH),
-            style = MaterialTheme.typography.body2.copy(textAlign = TextAlign.Center)
-        )
-        content()
+    BackgroundSurface {
+        Column(
+            modifier = Modifier.padding(16.dp).fillMaxSize(),
+            verticalArrangement = Arrangement.Center,
+            horizontalAlignment = Alignment.CenterHorizontally
+        ) {
+            BriarLogo(modifier = Modifier.size(200.dp))
+            Text(
+                text = headline,
+                modifier = Modifier.padding(top = 16.dp, bottom = 4.dp),
+                style = MaterialTheme.typography.h3
+            )
+            Text(
+                text = text,
+                modifier = Modifier.padding(top = 4.dp, bottom = 16.dp).widthIn(max = PARAGRAPH_WIDTH),
+                style = MaterialTheme.typography.body2.copy(textAlign = TextAlign.Center)
+            )
+            content()
+        }
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
index ea12210b75..32e55d138b 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/introduction/ContactDrawerMakeIntro.kt
@@ -32,7 +32,6 @@ import androidx.compose.material.Button
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.material.icons.Icons
@@ -48,7 +47,7 @@ import androidx.compose.ui.unit.dp
 import org.briarproject.briar.desktop.contact.ContactCard
 import org.briarproject.briar.desktop.contact.ContactItem
 import org.briarproject.briar.desktop.contact.ProfileCircle
-import org.briarproject.briar.desktop.theme.surfaceVariant
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.Constants.HEADER_SIZE
 import org.briarproject.briar.desktop.ui.HorizontalDivider
 import org.briarproject.briar.desktop.utils.InternationalizationUtils
@@ -65,7 +64,7 @@ fun ContactDrawerMakeIntro(
     LaunchedEffect(contactItem) {
         viewModel.setFirstContact(contactItem)
     }
-    Surface(color = MaterialTheme.colors.surfaceVariant, contentColor = MaterialTheme.colors.onSurface) {
+    BackgroundSurface {
         Column {
             if (!viewModel.secondScreen.value) {
                 Row(Modifier.fillMaxWidth().height(HEADER_SIZE)) {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
index fc3ba64c07..16e6599113 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/ErrorScreen.kt
@@ -53,6 +53,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.DB_ER
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SERVICE_ERROR
 import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS
 import org.briarproject.briar.desktop.theme.Red500
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.Constants.STARTUP_FIELDS_WIDTH
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
@@ -109,37 +110,39 @@ fun ErrorScreen(
     text: String,
     onShowAbout: () -> Unit,
     onBackButton: (() -> Unit)? = null,
-) = Box {
-    Column(
-        modifier = Modifier.fillMaxSize().padding(32.dp),
-        horizontalAlignment = CenterHorizontally,
-        verticalArrangement = spacedBy(32.dp)
-    ) {
-        Icon(
-            imageVector = Icons.Filled.Error,
-            contentDescription = i18n("error"),
-            modifier = Modifier.size(128.dp),
-            tint = Red500
-        )
+) = BackgroundSurface {
+    Box {
+        Column(
+            modifier = Modifier.fillMaxSize().padding(32.dp),
+            horizontalAlignment = CenterHorizontally,
+            verticalArrangement = spacedBy(32.dp)
+        ) {
+            Icon(
+                imageVector = Icons.Filled.Error,
+                contentDescription = i18n("error"),
+                modifier = Modifier.size(128.dp),
+                tint = Red500
+            )
 
-        Text(i18n("sorry"), style = MaterialTheme.typography.h5)
-        Text(
-            text = text,
-            style = MaterialTheme.typography.body1,
-            modifier = Modifier.widthIn(max = STARTUP_FIELDS_WIDTH)
-        )
-    }
+            Text(i18n("sorry"), style = MaterialTheme.typography.h5)
+            Text(
+                text = text,
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier.widthIn(max = STARTUP_FIELDS_WIDTH)
+            )
+        }
 
-    if (onBackButton != null) {
-        IconButton(onClick = onBackButton) {
-            Icon(Icons.Filled.ArrowBack, i18n("back"))
+        if (onBackButton != null) {
+            IconButton(onClick = onBackButton) {
+                Icon(Icons.Filled.ArrowBack, i18n("back"))
+            }
         }
-    }
 
-    IconButton(
-        onClick = onShowAbout,
-        modifier = Modifier.align(Alignment.BottomStart)
-    ) {
-        Icon(Icons.Filled.Info, i18n("access.about_briar_desktop"))
+        IconButton(
+            onClick = onShowAbout,
+            modifier = Modifier.align(Alignment.BottomStart)
+        ) {
+            Icon(Icons.Filled.Info, i18n("access.about_briar_desktop"))
+        }
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
index cae1225cf1..e2ad0cbc99 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/StartupScreen.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally
 import androidx.compose.ui.Alignment.Companion.CenterVertically
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.BriarLogo
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.viewmodel.viewModel
@@ -61,30 +62,32 @@ fun StartupScreenScaffold(
     onBackButton: () -> Unit = {},
     onShowAbout: () -> Unit = {},
     content: @Composable () -> Unit
-) = Box {
-    Column(
-        modifier = Modifier.padding(16.dp).fillMaxSize(),
-        horizontalAlignment = CenterHorizontally
-    ) {
-        HeaderLine(title)
-        content()
-    }
+) = BackgroundSurface {
+    Box {
+        Column(
+            modifier = Modifier.padding(16.dp).fillMaxSize(),
+            horizontalAlignment = CenterHorizontally
+        ) {
+            HeaderLine(title)
+            content()
+        }
+
+        if (showBackButton) {
+            IconButton(
+                onClick = onBackButton,
+                modifier = Modifier.align(Alignment.TopStart)
+            ) {
+                Icon(Icons.Filled.ArrowBack, i18n("back"))
+            }
+        }
 
-    if (showBackButton) {
         IconButton(
-            onClick = onBackButton,
-            modifier = Modifier.align(Alignment.TopStart)
+            onClick = onShowAbout,
+            modifier = Modifier.align(Alignment.BottomStart)
         ) {
-            Icon(Icons.Filled.ArrowBack, i18n("back"))
+            Icon(Icons.Filled.Info, i18n("access.about_briar_desktop"))
         }
     }
-
-    IconButton(
-        onClick = onShowAbout,
-        modifier = Modifier.align(Alignment.BottomStart)
-    ) {
-        Icon(Icons.Filled.Info, i18n("access.about_briar_desktop"))
-    }
 }
 
 @Composable
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt b/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
index fe080e6ead..4a2f458135 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/navigation/BriarSidebar.kt
@@ -25,10 +25,11 @@ import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ChromeReaderMode
 import androidx.compose.material.icons.filled.Contacts
@@ -40,17 +41,23 @@ import androidx.compose.material.icons.filled.WifiTethering
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import org.briarproject.bramble.api.identity.LocalAuthor
 import org.briarproject.briar.desktop.contact.ProfileCircle
-import org.briarproject.briar.desktop.theme.sidebarSurface
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.UiMode
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.getDesktopFeatureFlags
 
 val SIDEBAR_WIDTH = 56.dp
 
+/**
+ * The sidebar used in Briar Desktop as main navigation.
+ * It currently follows the MaterialTheme Navigation Rail compact design for all screen sizes,
+ * but could be adapted in future to extend to standard size (72.dp) or even a standard Navigation Drawer for large screens.
+ */
 @Composable
 fun BriarSidebar(
     account: LocalAuthor?,
@@ -67,7 +74,10 @@ fun BriarSidebar(
         )
     }
 
-    Surface(modifier = Modifier.width(SIDEBAR_WIDTH).fillMaxHeight(), color = MaterialTheme.colors.sidebarSurface) {
+    BackgroundSurface(
+        modifier = Modifier.width(SIDEBAR_WIDTH).fillMaxHeight().selectableGroup(),
+        overlayAlpha = 0.08f,
+    ) {
         Column(verticalArrangement = Arrangement.Top) {
             // profile button
             Box(
@@ -109,11 +119,16 @@ fun BriarSidebar(
 
 @Composable
 fun BriarSidebarButton(selected: Boolean, onClick: () -> Unit, icon: ImageVector, contentDescription: String?) {
-    val tint = if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface
-    IconButton(
-        modifier = Modifier.padding(vertical = 4.dp, horizontal = 12.dp),
-        onClick = onClick
+    val tint = if (selected) MaterialTheme.colors.primaryVariant else MaterialTheme.colors.onBackground.copy(alpha = 0.6f)
+    Box(
+        contentAlignment = Alignment.Center,
+        modifier = Modifier.clip(CircleShape).selectable(selected, onClick = onClick).size(SIDEBAR_WIDTH)
     ) {
-        Icon(icon, contentDescription, tint = tint, modifier = Modifier.size(30.dp))
+        Icon(
+            icon,
+            contentDescription,
+            tint = tint,
+            modifier = Modifier.size(24.dp)
+        )
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/theme/Colors.kt b/src/main/kotlin/org/briarproject/briar/desktop/theme/Colors.kt
index 7ff1bedee6..4759bf2939 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/theme/Colors.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/theme/Colors.kt
@@ -20,10 +20,17 @@ package org.briarproject.briar.desktop.theme
 
 import androidx.compose.ui.graphics.Color
 
+/**
+ * All the colors defined in this file are taken from the Briar Styleguide.
+ * See https://briar-styleguide.netlify.app/design/
+ */
+
 val Night50 = Color(0xffebf3fa)
 val Night500 = Color(0xff435b77)
+val Night600 = Color(0xff374b61)
 val Night700 = Color(0xff2e3d4f)
 val Night800 = Color(0xff212d3b)
+val Night900 = Color(0xff15212d)
 val Night950 = Color(0xff0e171f)
 
 val Gray50 = Color(0xfffafafa)
@@ -36,27 +43,32 @@ val Gray700 = Color(0xff707070)
 val Gray800 = Color(0xff4f4f4f)
 val Gray900 = Color(0xff2e2e2e)
 val Gray950 = Color(0xff1f1f1f)
-val materialDarkBg = Color(0xff121212)
 
 val Red500 = Color(0xffdb3b21)
-val Orange500 = Color(0xfffc9403)
 
-// taken from Android AppCompat DayNight theme as error colors
-// https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-appcompat-release/appcompat/appcompat/src/main/res/values/colors_material.xml#102
-val DeepOrange400 = Color(0xffff7043)
-val DeepOrange500 = Color(0xffff5722)
+val Orange200 = Color(0xfffed69f)
+val Orange500 = Color(0xfffc9403)
+val Orange800 = Color(0xffa35200)
 
+val Blue300 = Color(0xff73afea)
 val Blue400 = Color(0xff418cd8)
-val Blue500 = Color(0xff1f78d1) // todo: unused in Android
+val Blue500 = Color(0xff1f78d1)
 val Blue600 = Color(0xff1b69b6)
 val Blue800 = Color(0xff134a81)
 
 val Lime300 = Color(0xff95DE2D)
+val Lime400 = Color(0xff82c91e)
 val Lime500 = Color(0xff74B816)
+val Lime600 = Color(0xff67a60f)
+val Lime800 = Color(0xff3e620c)
 
+// taken from Android AppCompat DayNight theme as error colors
+// https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-appcompat-release/appcompat/appcompat/src/main/res/values/colors_material.xml#102
+val DeepOrange400 = Color(0xffff7043)
+val DeepOrange500 = Color(0xffff5722)
+
+// taken from Briar Android
 val TextPrimaryMaterialDark = Color(0xffffffff)
 val TextPrimaryMaterialLight = Color(0xde000000)
 val TextSecondaryMaterialDark = Color(0xb3ffffff)
 val TextSecondaryMaterialLight = Color(0x8a000000)
-
-val briarError = Color(0xffb00020)
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/theme/Theme.kt b/src/main/kotlin/org/briarproject/briar/desktop/theme/Theme.kt
index 69d6ae7d85..6833cebead 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/theme/Theme.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/theme/Theme.kt
@@ -35,11 +35,12 @@ import androidx.compose.ui.text.font.FontFamily
 import androidx.compose.ui.text.platform.Font
 import androidx.compose.ui.unit.sp
 
-val Colors.divider: Color get() = if (isLight) Gray300 else Gray800
+val Colors.divider: Color get() = if (isLight) Gray300 else Night700
 val Colors.outline: Color get() = if (isLight) Gray900 else Gray200
+@Deprecated("use BackgroundSurface with a suitable overlayAlpha instead")
 val Colors.surfaceVariant: Color get() = if (isLight) Gray100 else Gray950
-val Colors.sidebarSurface: Color get() = if (isLight) Gray200 else Gray900
-val Colors.selectedCard: Color get() = if (isLight) Gray400 else Gray700
+val Colors.selectedCard: Color get() = if (isLight) Gray300 else Night600
+val Colors.contactConnected: Color get() = Lime500
 val Colors.msgStroke: Color get() = if (isLight) Gray300 else Gray900
 val Colors.msgIn: Color get() = if (isLight) Color.White else Night700
 val Colors.msgOut: Color get() = if (isLight) Blue400 else Blue600
@@ -55,10 +56,10 @@ val Colors.warningForeground get() = Color.White
 
 val DarkColors = darkColors(
     primary = Blue500,
-    primaryVariant = Night500,
-    secondary = Lime500,
-    background = materialDarkBg,
-    surface = materialDarkBg,
+    primaryVariant = Blue500,
+    secondary = Lime400,
+    background = Night900,
+    surface = Night800,
     error = DeepOrange400,
     onPrimary = Color.White,
     onSecondary = Color.White,
@@ -67,14 +68,14 @@ val DarkColors = darkColors(
     onError = Color.White,
 )
 val LightColors = lightColors(
-    primary = Blue500,
-    primaryVariant = Night500,
-    secondary = Lime300,
-    background = Color.White,
+    primary = Blue800,
+    primaryVariant = Blue600,
+    secondary = Lime600,
+    background = Gray100,
     surface = Color.White,
     error = DeepOrange500,
     onPrimary = Color.White,
-    onSecondary = Color.Black,
+    onSecondary = Color.White,
     onBackground = Color.Black,
     onSurface = Color.Black,
     onError = Color.White,
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/BackgroundSurface.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/BackgroundSurface.kt
new file mode 100644
index 0000000000..01f7194b01
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/BackgroundSurface.kt
@@ -0,0 +1,51 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.ui
+
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.contentColorFor
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.compositeOver
+
+/**
+ * A surface that is meant to be used as background, with [MaterialTheme.colors.background] as default color.
+ * It will automatically match the corresponding [contentColor] if the given [color] is part of the Material theme.
+ * An [overlayAlpha] value bigger than zero applies a color overlay to the background color,
+ * that can be used to generate slightly varying colors for different parts of the UI.
+ */
+@Composable
+fun BackgroundSurface(
+    modifier: Modifier = Modifier,
+    shape: Shape = RectangleShape,
+    overlayAlpha: Float = 0f,
+    color: Color = MaterialTheme.colors.background,
+    contentColor: Color = contentColorFor(color),
+    content: @Composable () -> Unit
+) = Surface(
+    modifier = modifier,
+    shape = shape,
+    color = if (overlayAlpha != 0f) contentColor.copy(alpha = overlayAlpha).compositeOver(color) else color,
+    contentColor = contentColor,
+    content = content
+)
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
index 07f2c8d71f..938230e75a 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/MessageCounter.kt
@@ -37,19 +37,18 @@ import org.briarproject.briar.desktop.theme.outline
 
 @Composable
 fun MessageCounter(unread: Int, modifier: Modifier = Modifier) {
-    val outlineColor = MaterialTheme.colors.outline
-    val briarSecondary = MaterialTheme.colors.secondary
     if (unread > 0) {
         Box(
             modifier = modifier
                 .height(20.dp)
                 .widthIn(min = 20.dp, max = Dp.Infinity)
-                .border(1.dp, outlineColor, CircleShape)
-                .background(briarSecondary, CircleShape)
+                .border(2.dp, MaterialTheme.colors.outline, CircleShape)
+                .background(MaterialTheme.colors.primary, CircleShape)
                 .padding(horizontal = 6.dp)
         ) {
             Text(
                 modifier = Modifier.align(Alignment.Center),
+                color = MaterialTheme.colors.onPrimary,
                 style = MaterialTheme.typography.overline,
                 textAlign = TextAlign.Center,
                 text = unread.toString(),
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/UiPlaceholder.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/UiPlaceholder.kt
index 39c7f93306..4c984fa4de 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/ui/UiPlaceholder.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/UiPlaceholder.kt
@@ -26,9 +26,11 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 
 @Composable
-fun UiPlaceholder() = Box(
-    contentAlignment = Alignment.Center,
-    modifier = Modifier.fillMaxSize()
-) {
-    Text("TBD")
+fun UiPlaceholder() = BackgroundSurface {
+    Box(
+        contentAlignment = Alignment.Center,
+        modifier = Modifier.fillMaxSize()
+    ) {
+        Text("TBD")
+    }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
index fa5c8e5267..a376f5ae14 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/utils/PreviewUtils.kt
@@ -52,6 +52,7 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.singleWindowApplication
 import org.briarproject.bramble.api.UniqueId
 import org.briarproject.briar.desktop.theme.BriarTheme
+import org.briarproject.briar.desktop.ui.BackgroundSurface
 import org.briarproject.briar.desktop.ui.LocalWindowScope
 import kotlin.random.Random
 
@@ -225,7 +226,7 @@ object PreviewUtils {
                     }
 
                     BriarTheme(isDarkTheme = scope.getBooleanParameter("darkTheme")) {
-                        Box(Modifier.fillMaxSize(1f)) {
+                        BackgroundSurface(Modifier.fillMaxSize(1f)) {
                             Column(Modifier.padding(10.dp)) {
                                 content(scope)
                             }
-- 
GitLab