OverviewFilesCommitsIssuesDocsReleasesPeers

favela-android commits / 3862272

0.1.7: the ceremony screens paint their own background
Reported on a handset: some of the text on the intro modal is very hard to read. AndroidManifest sets Theme.AppCompat.DayNight.NoActionBar, so on a phone in dark mode the window behind an unpainted view is DARK — while every colour on these two screens, and the default black of an unstyled <Text>, was chosen for white. Pair.jsx and Create.jsx never set a background; every other screen in the app does (Screens.jsx `list`). They were written as overlays on an app that was already painted, and became full screens without gaining the one thing a full screen has to do. On Create.jsx this is not a cosmetic bug. The recovery words are shown exactly once, and a user who cannot read them has permanently lost the identity. Painted explicitly rather than made theme-aware: the app is a light design throughout, and one ceremony screen quietly following the system theme would be its only dark surface. Also explicit now, for the same root cause: text colours that were inheriting the platform's, and the TextInputs — a TextInput with no `color` renders typed characters in the theme's colour, so on a dark phone you could not read the invite you had just pasted or the passphrase you were typing. `dim`/`foot` move off #888 (~3.5:1 on white, under AA for small text) to #5b6b78. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3862272217d99d454899502b8424a94f0818a7d8
by plankatron <fleeky@gmail.com> · 2026-08-25T23:29:54.000Z

Changed files (3) +40 -21

app/Create.jsx +16 -11
……@@ -59,7 +59,7 @@ export default function Create ({ client, onCreated, onCancel = null }) {
5959 if (made) {
6060 const words = String(made.mnemonic || '').split(/\s+/).filter(Boolean)
6161 return (
62 <ScrollView contentContainerStyle={s.wrap}>
62 <ScrollView style={s.screen} contentContainerStyle={s.wrap}>
6363 <Text style={s.h1}>Write these down</Text>
6464 <View style={s.danger}>
6565 <Text style={s.dangerH}>This is shown once and never again</Text>
……@@ -107,7 +107,7 @@ export default function Create ({ client, onCreated, onCancel = null }) {
107107
108108 // ── PHASE 1: what it costs, and the passphrase ───────────────────────────────────────────────────
109109 return (
110 <ScrollView contentContainerStyle={s.wrap}>
110 <ScrollView style={s.screen} contentContainerStyle={s.wrap}>
111111 <Text style={s.h1}>Create a new identity</Text>
112112
113113 <Text style={s.body}>
……@@ -173,8 +173,13 @@ export default function Create ({ client, onCreated, onCancel = null }) {
173173 )
174174}
175175
176// ⛔ PAINTS ITS OWN BACKGROUND — see Pair.jsx for the full reason. AndroidManifest sets
177// Theme.AppCompat.DayNight.NoActionBar, so an unpainted view sits on a DARK window in dark mode and
178// every colour here was chosen for white. On THIS screen that is not merely ugly: the recovery words
179// are shown exactly once, and a user who cannot read them has lost the identity permanently.
176180const s = StyleSheet.create({
177 wrap: { padding: 24, gap: 10 },
181 screen: { flex: 1, backgroundColor: '#fff' },
182 wrap: { padding: 24, gap: 10, backgroundColor: '#fff' },
178183 h1: { fontSize: 22, fontWeight: '700', marginBottom: 4 },
179184 body: { fontSize: 15, lineHeight: 21, color: '#333' },
180185 b: { fontWeight: '700' },
……@@ -185,11 +190,11 @@ const s = StyleSheet.create({
185190 dangerH: { fontWeight: '700', color: '#7a1f1f' },
186191 dangerB: { fontSize: 14, lineHeight: 20, color: '#7a1f1f' },
187192 words: { backgroundColor: '#f7f7f7', borderRadius: 8, padding: 14, gap: 4, marginVertical: 6 },
188 word: { fontFamily: 'monospace', fontSize: 15, lineHeight: 22 },
189 wordN: { color: '#999' },
190 label: { fontWeight: '600', marginTop: 10 },
191 code: { fontFamily: 'monospace', backgroundColor: '#f2f2f2', padding: 10, borderRadius: 6, fontSize: 12 },
192 input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 6, padding: 10, fontSize: 14, marginTop: 6 },
193 word: { fontFamily: 'monospace', fontSize: 15, lineHeight: 22, color: '#14171a' },
194 wordN: { color: '#5b6b78' },
195 label: { fontWeight: '600', marginTop: 10, color: '#14171a' },
196 code: { fontFamily: 'monospace', backgroundColor: '#f2f2f2', padding: 10, borderRadius: 6, fontSize: 12, color: '#14171a' },
197 input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 6, padding: 10, fontSize: 14, marginTop: 6, color: '#14171a', backgroundColor: '#fff' },
193198 btn: { backgroundColor: '#111', borderRadius: 8, padding: 14, alignItems: 'center', marginTop: 16 },
194199 btnOff: { opacity: 0.4 },
195200 btnT: { color: '#fff', fontWeight: '600' },
……@@ -197,11 +202,11 @@ const s = StyleSheet.create({
197202 box: { width: 24, height: 24, borderWidth: 2, borderColor: '#888', borderRadius: 4, alignItems: 'center', justifyContent: 'center' },
198203 boxOn: { borderColor: '#111', backgroundColor: '#111' },
199204 tick: { color: '#fff', fontWeight: '700' },
200 checkT: { fontSize: 15, flexShrink: 1 },
201 dim: { color: '#888', fontSize: 13 },
205 checkT: { fontSize: 15, flexShrink: 1, color: '#14171a' },
206 dim: { color: '#5b6b78', fontSize: 13 },
202207 err: { color: '#b00', marginTop: 8 },
203208 skip: { alignItems: 'center', paddingVertical: 14, marginTop: 6 },
204209 skipT: { color: '#2563eb', fontSize: 16 },
205210 skipOff: { opacity: 0.4 },
206 foot: { color: '#888', fontSize: 13, marginTop: 20, lineHeight: 19 }
211 foot: { color: '#5b6b78', fontSize: 13, marginTop: 20, lineHeight: 19 }
207212})
app/Pair.jsx +23 -9
……@@ -49,7 +49,7 @@ export default function Pair ({ client, onPaired, onSkip = null, onCreate = null
4949 }
5050
5151 return (
52 <ScrollView contentContainerStyle={s.wrap}>
52 <ScrollView style={s.screen} contentContainerStyle={s.wrap}>
5353 <Text style={s.h1}>Connect this phone</Text>
5454
5555 <Text style={s.body}>
……@@ -135,28 +135,42 @@ export default function Pair ({ client, onPaired, onSkip = null, onCreate = null
135135 )
136136}
137137
138// ⛔ THIS SCREEN MUST PAINT ITS OWN BACKGROUND. AndroidManifest sets
139// Theme.AppCompat.DayNight.NoActionBar, so on a handset in dark mode the window behind an unpainted
140// view is DARK — while every colour here (and the default black of an unstyled <Text>) was chosen for
141// white. The result was a screen of near-invisible text, reported on a handset 2026-08-25. Every other
142// screen in the app already does this (Screens.jsx `list`); these two ceremonies were the ones that
143// never did, because they were written as overlays on top of an app that was already painted.
144//
145// Painted explicitly rather than made theme-aware: the app is a light design throughout, and a single
146// ceremony screen quietly following the system theme would be the only dark surface in it.
147//
148// TEXT COLOURS ARE EXPLICIT for the same reason — an unstyled <Text> inherits the platform's, which is
149// what flips to white and vanishes. Same for the inputs: a TextInput with no `color` renders typed
150// characters in the theme's colour, so on a dark phone you could not read what you had pasted.
138151const s = StyleSheet.create({
139 wrap: { padding: 24, gap: 10 },
140 h1: { fontSize: 22, fontWeight: '700', marginBottom: 4 },
152 screen: { flex: 1, backgroundColor: '#fff' },
153 wrap: { padding: 24, gap: 10, backgroundColor: '#fff' },
154 h1: { fontSize: 22, fontWeight: '700', marginBottom: 4, color: '#14171a' },
141155 body: { fontSize: 15, lineHeight: 21, color: '#333' },
142156 warn: { backgroundColor: '#fff6e5', borderColor: '#e8c98a', borderWidth: 1, borderRadius: 8, padding: 14, gap: 8, marginVertical: 10 },
143157 warnH: { fontWeight: '700' },
144158 warnB: { fontSize: 14, lineHeight: 20, color: '#4a3c1a' },
145 label: { fontWeight: '600', marginTop: 10 },
146 input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 6, padding: 10, fontSize: 14 },
159 label: { fontWeight: '600', marginTop: 10, color: '#14171a' },
160 input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 6, padding: 10, fontSize: 14, color: '#14171a', backgroundColor: '#fff' },
147161 mono: { fontFamily: 'monospace', minHeight: 70 },
148 code: { fontFamily: 'monospace', backgroundColor: '#f2f2f2', padding: 10, borderRadius: 6 },
162 code: { fontFamily: 'monospace', backgroundColor: '#f2f2f2', padding: 10, borderRadius: 6, color: '#14171a' },
149163 btn: { backgroundColor: '#111', borderRadius: 8, padding: 14, alignItems: 'center', marginTop: 16 },
150164 btnOff: { opacity: 0.4 },
151165 btnT: { color: '#fff', fontWeight: '600' },
152 dim: { color: '#888', fontSize: 13 },
166 dim: { color: '#5b6b78', fontSize: 13 },
153167 err: { color: '#b00', marginTop: 8 },
154168 alt: { borderTopWidth: 1, borderTopColor: '#e5e5e5', marginTop: 22, paddingTop: 18, gap: 6 },
155 altH: { fontWeight: '700', fontSize: 15 },
169 altH: { fontWeight: '700', fontSize: 15, color: '#14171a' },
156170 altBtn: { borderWidth: 1, borderColor: '#111', borderRadius: 8, padding: 13, alignItems: 'center', marginTop: 8 },
157171 altBtnT: { color: '#111', fontWeight: '600' },
158172 skip: { alignItems: 'center', paddingVertical: 14, marginTop: 6 },
159173 skipT: { color: '#2563eb', fontSize: 16 },
160174 skipOff: { opacity: 0.4 },
161 foot: { color: '#888', fontSize: 13, marginTop: 20, lineHeight: 19 }
175 foot: { color: '#5b6b78', fontSize: 13, marginTop: 20, lineHeight: 19 }
162176})
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-android",
3 "version": "0.1.6",
3 "version": "0.1.7",
44 "description": "favela-android — a React Native client over a bare-kit worklet running a FULL favela node (hyper-proto-peer + appview + hyperswarm) on the phone. favela-web is reference for the logic, NOT the rendering: this is a different rendering surface. See spec/architecture.md.",
55 "private": true,
66 "type": "module",