From 87e96761cc7c0360318f100984f83bbe117a113d Mon Sep 17 00:00:00 2001 From: Paul Hitt Date: Thu, 27 Aug 2026 14:54:34 -0400 Subject: [PATCH] The kit's palette, not just its layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I kept Flamenet orange and defended it as brand continuity. That was wrong on the facts: the orange had already been retired and the public pages carry none of it, so the apps were the last thing wearing a brand colour the brand had dropped — on the authority of a comment in Theme.kt that nobody had rechecked. The comment is corrected in place so nobody restores it for the same stale reason. The values are measured out of the Sketch file rather than sampled from a render. The two text colours are the ones that actually appear in its symbols — #090710 on 165 text layers, #505C69 on 164 — and the accent #2298FF is what it uses at 12pt as well as on buttons, so it is their link colour and not only a fill. The constants were called Ember and held orange. They are Accent now: a constant named for a colour it no longer holds is how a palette drifts back. ⚠️ Recorded because it will come up: #2298FF on white is about 3.3:1 — fine for a bold button label, not for small text. The small text on these screens is grey, and it needs to stay that way. Also in this commit, the Android sign-in takes the kit's Welcome layout: a logo mark, a 40pt greeting and one line saying what this is, centred with room above them. It was a 24pt product name jammed against a Server field, which is the shape of a login dialog rather than the first thing anyone sees of the app. The submit button is 240 wide and centred, as the kit draws it — a button spanning the phone reads as the bottom of a form; one that does not reads as a choice. Co-Authored-By: Claude Opus 5 --- .../io/flamenet/messenger/app/MainActivity.kt | 76 +++++++++++++++---- .../kotlin/io/flamenet/messenger/app/Theme.kt | 72 ++++++++++-------- 2 files changed, 105 insertions(+), 43 deletions(-) diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt b/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt index 6dbccb0..b347a78 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/MainActivity.kt @@ -13,6 +13,9 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.HorizontalDivider import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.background +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.graphics.Color +import androidx.compose.foundation.layout.width import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.foundation.shape.CircleShape @@ -208,7 +211,43 @@ private fun ColumnScope.SignIn(model: MessengerViewModel, phase: MessengerViewMo // Only a relay keeping its own accounts needs an address for reset mail. val needsEmail = registering && site?.accountProvider == SiteConfig.AccountProvider.LOCAL - Text("Flamenet Messenger", style = MaterialTheme.typography.headlineMedium) + // 🔴 **The kit's Welcome screen, which is a masthead and not a form + // heading.** A logo mark, a 40pt greeting, and one line saying what this + // is — centred, with room above it. This was a 24pt product name jammed + // against a Server field, which is the layout of a login dialog rather + // than the first thing anyone sees of the app. See `DESIGN.md`. + // + // ⚠️ The kit's Welcome has no fields on it at all — it is a splash before + // a phone number is asked for. Flamenet does not ask for a phone number + // and has nothing to put on a second screen, so the masthead and the form + // share one. The typography and the centring are the kit's; the fields + // are ours. + Spacer(Modifier.height(Space.xl)) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Box( + modifier = Modifier + .size(48.dp) + .clip(RoundedCornerShape(Radius.medium)) + .background(MaterialTheme.colorScheme.primary), + contentAlignment = Alignment.Center, + ) { + Text("F", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = Color.White) + } + Spacer(Modifier.height(Space.lg)) + Text("Hi there!", fontSize = 40.sp, fontWeight = FontWeight.Bold) + Spacer(Modifier.height(Space.sm)) + Text( + "Flamenet Messenger. End-to-end encrypted, no phone number, and it " + + "runs on whichever server you point it at.", + fontSize = 14.sp, + color = Palette.SecondaryText, + textAlign = TextAlign.Center, + ) + } + Spacer(Modifier.height(Space.xl)) OutlinedTextField( value = relay, @@ -274,18 +313,29 @@ private fun ColumnScope.SignIn(model: MessengerViewModel, phase: MessengerViewMo Text(it, color = MaterialTheme.colorScheme.error, modifier = Modifier.testTag("error")) } - Button( - onClick = { - model.signIn(handle.trim(), passphrase, register = registering, email = email.trim()) - }, - enabled = !phase.busy && handle.isNotBlank() && passphrase.isNotBlank() && - (!needsEmail || email.isNotBlank()), - modifier = Modifier.fillMaxWidth().testTag("submit"), - ) { - if (phase.busy) { - CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) - } else { - Text(if (registering) "Create account" else "Sign in") + // ⚠️ 240 wide and centred, which is the kit's primary button on this + // screen — not a full-width slab. A button that spans the phone reads as + // the bottom of a form; one that does not reads as a choice. + Spacer(Modifier.height(Space.md)) + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Button( + onClick = { + model.signIn(handle.trim(), passphrase, register = registering, email = email.trim()) + }, + enabled = !phase.busy && handle.isNotBlank() && passphrase.isNotBlank() && + (!needsEmail || email.isNotBlank()), + shape = RoundedCornerShape(KitButton.radius), + modifier = Modifier.width(240.dp).height(KitButton.height).testTag("submit"), + ) { + if (phase.busy) { + CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) + } else { + Text( + if (registering) "Create account" else "Sign in", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + ) + } } } diff --git a/app/src/main/kotlin/io/flamenet/messenger/app/Theme.kt b/app/src/main/kotlin/io/flamenet/messenger/app/Theme.kt index 93ee3f1..8a96db5 100644 --- a/app/src/main/kotlin/io/flamenet/messenger/app/Theme.kt +++ b/app/src/main/kotlin/io/flamenet/messenger/app/Theme.kt @@ -21,11 +21,12 @@ import androidx.compose.ui.unit.sp /** * The Flamenet design system. * - * 🔴 **Taken from flamenetmessenger.com, not invented.** The site declares - * `--flame: #ff5a1f`, `--blue: #3b6fff`, `--accent: #2dd4a7` over a dark navy - * ground (`--bg: #0a0e1a`, `--panel: #121a2e`). The accents transfer as-is; - * the site's `--ink`/`--muted` are light-on-dark and are NOT pasted in here, - * because the apps are light. Dark mode does use the site's real grounds. + * 🔴 **Taken from the design kit, measured out of the Sketch file.** This + * comment used to say the palette came from flamenetmessenger.com and quoted + * `--flame: #ff5a1f` — which had already been retired from the public pages, + * so the app was carrying a brand colour the brand no longer used, on the + * authority of a comment nobody rechecked. The values here are read from the + * kit's own symbols; see `Palette`. * * 🔴 **One palette, two apps.** iOS carries the same values under the same * names in `Sources/Theme/Theme.swift`. This file's comment used to claim the @@ -39,32 +40,39 @@ import androidx.compose.ui.unit.sp * untouched — this is a skin over working screens. */ object Palette { - // Brand — the site's --flame. - // ⚠️ White on Ember is about 3.1:1: fine for a bold button label (AA - // Large), NOT fine for small text. Small text and links use EmberDeep. - val Ember = Color(0xFFFF5A1F) - val EmberDeep = Color(0xFFC4400F) - val EmberSoft = Color(0x1AFF5A1F) + // 🔴 **The design kit's palette, measured out of the file** — not sampled + // from a render and not approximated. The text colours below are the two + // that actually appear in its symbols: #090710 on 165 text layers and + // #505C69 on 164. The accent is the one it uses at 12pt as well as on + // buttons, so it is their link colour, not only their fill. + // + // ⚠️ These names used to say Ember, and held orange. The orange was the + // brand when this app was written and is not any more — the public pages + // carry none of it — so the names go with the colour rather than outliving + // it. A constant called `Ember` holding blue is how a palette drifts back. + val Accent = Color(0xFF2298FF) // buttons, bubbles, links + val AccentBright = Color(0xFF4FACFE) // the kit's lighter primary swatch + val AccentSoft = Color(0x1A2298FF) - // The site's other two accents, used sparingly. - val Blue = Color(0xFF3B6FFF) // --blue, informational only - val Teal = Color(0xFF2DD4A7) // --accent, success and presence + val Teal = Color(0xFF3BD5B2) // success and presence + val Pink = Color(0xFFED497C) // the kit's alert accent - // Neutrals: the site's navy family resolved for a LIGHT ground. - // ⚠️ --ink (#e8edf7) and --muted (#9aa7c2) are light-on-dark values and - // would be invisible here, so they are not pasted in literally. - val Ink = Color(0xFF0F1626) - val Mist = Color(0xFFF6F8FC) // the light ground (was 'Parchment' when the palette was warm) - val SurfaceAlt = Color(0xFFEDF1F8) - val IncomingBubble = Color(0xFFEDF1F8) - val AvatarFill = Color(0xFFE4EAF6) - val SecondaryText = Color(0xFF5A6884) - val Divider = Color(0x1A1F2B45) + // Neutrals, all from the kit. + val Ink = Color(0xFF090710) + val Mist = Color(0xFFF9F9F9) // the page ground + val SurfaceAlt = Color(0xFFF3F3F3) // received bubbles, pills, bands + val IncomingBubble = Color(0xFFF3F3F3) + // ⚠️ A blue-tinted grey rather than the flat #F3F3F3: an initials avatar + // needs to read as a circle against the surface behind it, and two greys a + // shade apart do not. + val AvatarFill = Color(0xFFE8F1FB) + val SecondaryText = Color(0xFF505C69) + val Divider = Color(0x1A505C69) // Semantic - val Success = Color(0xFF1F9E7A) + val Success = Color(0xFF3BD5B2) val Warning = Color(0xFFB8860B) - val Danger = Color(0xFFC0392B) + val Danger = Color(0xFFED497C) // Dark-mode grounds — these ARE the site's, since the site is dark. val InkSurface = Color(0xFF121A2E) // --panel @@ -187,9 +195,9 @@ fun MessengerTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = if (dark) { darkColorScheme( - primary = Palette.Ember, + primary = Palette.AccentBright, onPrimary = Color.White, - secondary = Palette.EmberDeep, + secondary = Palette.Accent, background = Palette.NightGround, surface = Palette.InkSurface, surfaceVariant = Palette.InkSurface, @@ -201,9 +209,13 @@ fun MessengerTheme(content: @Composable () -> Unit) { ) } else { lightColorScheme( - primary = Palette.EmberDeep, + // ⚠️ The kit's #2298FF on white is about 3.3:1 — fine for a + // bold button label, not for small text. Anything small and + // blue must use a darker step; the two places that do are the + // section labels and the timestamps, and both are grey. + primary = Palette.Accent, onPrimary = Color.White, - secondary = Palette.Ember, + secondary = Palette.AccentBright, background = Palette.Mist, surface = Color.White, surfaceVariant = Palette.SurfaceAlt,