From a637b55ce6c71b033fc68876b08f2217d6808e19 Mon Sep 17 00:00:00 2001 From: Dennis Falling Date: Mon, 17 Aug 2026 11:36:40 +0100 Subject: [PATCH] Put the app on the culpeo palette MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The blue/teal/purple/green scheme had four accent hues doing work that wording and icons already did, and its neutrals were cool greys sitting against what is now a warm canvas — two palettes running against each other. This moves to the same ten roles the web app was rewritten onto, so a place looks the same wherever it's opened and there is one declaration per role rather than one per shade. Accent is now spent only where the style guide sanctions it: the primary button, map pins, focus rings, and selected states. Badges, chips, category labels, and the avatar go neutral. Search results were told apart by the hue behind each glyph — three colours 1.1:1 apart in luminance, which is no distinction at all without colour vision — and now share one treatment, since the section headings above them already say which is which. Several colours turned out to be supplied by dependencies rather than by us, and no grep of this repo would have found them: MapLibre hardcodes a blue location puck in its layer paint, the emoji picker ships a fixed light theme that opened as a white sheet in dark mode, and the switch, every spinner, the caret and the ripple all drew from Android's theme instead of ours. Those are pinned explicitly, via values-night/ where the platform is the only thing that can reach them. Two fixes that are corrections to the shared palette, not ports of it: - Focus was invisible. Inputs used the same neutral border at rest and focused, so focusing a field changed nothing on screen. The new TextField moves the border to accent with a ring. - `line` was doing two jobs with two different bars. As a divider it can be as quiet as it likes, but it was also the border of a text input — whose surface fill is only 1.06:1 against the canvas, so the border is the entire affordance. In dark mode that measured 1.32:1: a field you cannot find at night. Split out `lineControl` (one value for both schemes) for boundaries that are load-bearing; dividers keep `line` and are unchanged. Basemap goes from `liberty` to `positron` for the same reason web moved to a dataviz style: 44 declared colours at 0.178 mean saturation down to 29 at 0.026. The pins are the content, the map is context. Every control boundary and text pairing now clears WCAG AA in both schemes, worst case 3.29:1 against the 3:1 non-text bar. Not yet driven on a device — the states that need eyes on them are a focused field, the Completed toggle, the emoji picker, and the map at night. Co-Authored-By: Claude Opus 5 (1M context) --- App.tsx | 16 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 2876 -> 2559 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3976 -> 3787 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1800 -> 1486 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2517 -> 2464 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4021 -> 3598 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 5565 -> 5416 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6620 -> 5978 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 8841 -> 8569 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 9075 -> 8266 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 12155 -> 11812 bytes .../app/src/main/res/values-night/colors.xml | 15 ++ android/app/src/main/res/values/colors.xml | 23 +- android/app/src/main/res/values/styles.xml | 25 +- src/account/AccountMenu.tsx | 15 +- src/auth/LoginScreen.tsx | 36 +-- src/map/ElementDetailScreen.tsx | 54 +++-- src/map/ElementEditScreen.tsx | 115 +++++---- src/map/ElementPreviewCard.tsx | 14 +- src/map/FilterChips.tsx | 14 +- src/map/MapScreen.tsx | 25 +- src/map/PinIcon.tsx | 17 +- src/map/SearchOverlay.tsx | 76 +++--- src/map/UserLocationPuck.tsx | 81 +++++++ src/map/usePinImages.tsx | 7 +- src/photos/PhotoViewer.tsx | 11 +- src/share/ImportShareScreen.tsx | 19 +- src/theme/colors.ts | 229 ++++++++++-------- src/theme/navigationTheme.ts | 41 ++++ src/ui/Sheet.tsx | 6 +- src/ui/TextField.tsx | 99 ++++++++ src/ui/emojiPickerTheme.ts | 45 ++++ 32 files changed, 707 insertions(+), 276 deletions(-) create mode 100644 android/app/src/main/res/values-night/colors.xml create mode 100644 src/map/UserLocationPuck.tsx create mode 100644 src/theme/navigationTheme.ts create mode 100644 src/ui/TextField.tsx create mode 100644 src/ui/emojiPickerTheme.ts diff --git a/App.tsx b/App.tsx index 101a0a0..88d8b9c 100644 --- a/App.tsx +++ b/App.tsx @@ -6,8 +6,6 @@ import {ApolloProvider} from '@apollo/client/react'; import { - DarkTheme, - DefaultTheme, NavigationContainer, useNavigationContainerRef, } from '@react-navigation/native'; @@ -25,6 +23,7 @@ import { usePendingShare, useShareListener, } from './src/share/shareImport'; +import {useNavigationTheme} from './src/theme/navigationTheme'; import {useTheme} from './src/theme/useTheme'; function App() { @@ -44,6 +43,7 @@ function App() { function AppContent() { const theme = useTheme(); + const navigationTheme = useNavigationTheme(); const hydrated = useAuthHydrated(); const auth = useAuth(); const navigationRef = useNavigationContainerRef(); @@ -76,9 +76,9 @@ function AppContent() { style={[ styles.container, styles.splash, - {backgroundColor: theme.background}, + {backgroundColor: theme.canvas}, ]}> - + ); } @@ -87,14 +87,14 @@ function AppContent() { return ; } - // Match React Navigation's container background to the active scheme so the - // gap shown during the slide transition between screens isn't a white flash - // in dark mode. + // The navigator's own theme is built from our tokens, so the gap shown during + // the slide transition between screens is the canvas rather than React + // Navigation's default background. return ( + theme={navigationTheme}> ); diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 55427ccc3f1d6603f9ea29f5d488207ad3236981..3511267b85b7d5b3b302ac500170f7f1619ed8a3 100644 GIT binary patch delta 2480 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 diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index ce2027c4e47ac906d08075215b78c7c066e2ff37..9d307f813393d5ba5e13cd28742a07fc152b8f29 100644 GIT binary patch delta 3717 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 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 5bbb1150739bf11b6d8f33767c990ccbf42c1342..2f90eb8a348496f32c126e82ff31119338b20210 100644 GIT binary patch delta 1437 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 delta 2437 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 literal 4021 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 E1Iq{txBvhE delta 5509 zcmZ{oWl+@Nx5sx`SW;S$4rvf@kzAy^yB3f}Qk2dg;F2QXf*{=u(jn#2s7QA%DAKYB z$RgqOKX>NN+&gn$J#WtE^F7a;SLaj;^a!T90R*fR>@*D8Y)c>zh}%!q!q3Ux&soOt zh4a4$1QUfxJP;LsASP<^P+Ugrq0FO)f})}_qM{=T4Ho|k@OtLt<`Vq>18pos-~R!8 z|C8Y7=H=|`XAgM!{9gzr^HBP~Q=W@X!$Bb0JWUlvlc43JJU3741^*G?qF3@tiTt8S zB_X3SW}VBUwR<*42=NBRsM`$kw!&ANrd6D7;ZN1K`Bk**j@Et2>6OZ<`?6ebaxvk+ zTEhtt=&hTs$ns2>1bim5!dy|CB5IwvbG<&M>Ek$^^`aTcD z@BsRSsE5h|vI_Ae{E43d2J4QVqoEGycpgW2Ea}a~n=Q75>?9zY%0LNgiwVFG+g0!s zHmar2K%kKYkd1{>6%xQekh5A(fw(U&st0|Hhln!JU2l=yLK|YFZ=?;cU%I0V#qqR= zB3Q77n1SWN+12X@cn~y2fr$|Daouq7+z5QXYY!th>{00 z?muW0Kv#)}3i>d2Xb@;vz`&~gz&)G{78hE%0Awn+OVp*IsT6D|h_yKj6>!u+)VOML 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 mFTE6Hl3;|bv>_a~Y@>g%BA@ue_X1 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index abf283ca43561846a491fd8b3f6f6a5bbeeade36..eebfe5f56685584a05cdcca31502590ad2bb2c54 100644 GIT binary patch literal 5978 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 T42b{CJOk8}wUz1=-h}-R8+0{Q literal 6620 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 delta 8811 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 TiJ$V^X$Pn%XvkNAEkphn=qJNR literal 9075 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

~yJH8CTC*5M#Tb7u!`v z4B?q7k{I0RK>x78am%p3{?G|Ol@Ec300{Z``0jpP=CUzUCdK6Dox;FeUT zoET6aN@+r6*imFJ7(>+?sYP3+;$5NQb?C0kuav64W63QT;92t#G==2k52>n6vJ#?2 zhzaAkT~FQpx)x9tDnRB?6>JYdO~RQxmDgEfcB2r_{vA8c`}w9wS}6BRwiM|NUa}&PFC#-F}KJS>=!biL{!fvjD~;dSxF^J~oCl zSiy(+@gYh1b8p~v1r}-DYtn?eL%$o;ER;!Zyw1W7oW$@nb$T7N-_>YeQh;CjseY$? zjFywMPfX_Z3izt$?eznGY^Ya)rA{KaU=LlwZ9+zXQgv@{V-FTQ?vNm~8lp1LFsJL|z7yGxbM%Po*? zsYAM*COull*J06>cMPGguj}%1+lV1^AOG41yRmD~^67#C?Ay`au0zw*trL<`k2}>L z78&zrfn{#9$I4f>m2rKs&O>v0S^{Ut7jI$rR}S{&GNIIWNPh9b!?aL{$xaM*f;@f9 zJm%MZ>@^V__}NQ zDNf4DX@*&jx%FiHHSr%Jf45db1Q9<|?4;7&iu2XYxD3oN|=7 z1#@h8@Z3vj=rLe+Bm3Hg&H@qFdyekzNYTKe`uXq{Bu|U3xY5-&@saXtC}+c=hrqSd zycyFo{9q8O*(j!-I)~43S%$eLSlgdxr5op$fW{^rxOdDwn855!bb7QV#ztw< z_NG5+X??wIPs{#axU>5-CG*LjlHn0FCsy%n&2KX7L8Nyo@F@KWLZIfsL~J zQ6+z6Tz@X@zIxHCH=Wj%#6Wr#u(ltIDuoKga|X5HxO}etEW!YhT}Q6S{YxX@)+;FE zGdG`LD(y4ZVvtxa%m0nw-6gobB9n-9n=wKXwf{~w-C(2hdsg9x9ETzaxm%c{z?;}- zqKnt*J!$L!8g_`6C9`MaTAdlTbVy9~S2>M;sOP<*2`Wnuyq8y&fQC^$Z(3W~nP@km zWf`#+EFIJea({d*H#60f%cM=1qP#u2lJdpkR^Khp+Uq=J_oo~x3vOY&JrobK-8hS& zm;>{GsIv_#*U_>3Mx&K$P#@N{!#edoE~HeOUOeSr0AtGO*j%RCIgI7Ja1vb#*PW#Y zH=^DqB3Cg9o0eJLp0uGfFW%!_d(|Y5HlOEcXesT|#73DiE(3pWi&!`m2XLB0mbuA}b!jDD!8f3G?SFFR^)3-Pe z{r6qw(A*;COr;{6l&WC+lfm?5c|+fTMDI0c&vWTJ?V3@8>slic*)A7A@19~C*Z=VIysHNmPiRisQx=7J243RB2_j=Y$hg?FYJ?0zkvt($W?3!_-SbW39-hn zP^vj0XRwL?Ms3U1?ekzYMp@AQ{aL}X>s0{VGh$jc?ZVkw>F#cz4Jt@W0XOu9#BhmU z;ctGBy1&!bAKZOUYh82OH)0Kjood0fZ^Fcl$I`YweeVR1>E=?Hg(Kp{FH*k3vFet< z^}@kTc&ZOO0Q-&}Htrv4 zDC;zI4v^5Km(ok~1CEST-u=DZ+@H^>74;3KPFia#N}w1(r6n%Ff5ZQJwj*3?3Hw5D z=5UfUsK8)w2cNX_yR3n(bv`}cLe0-shQd_UVagG@)a*6+A&)siV`DuuHG-jS)+zJD z?XHIWt?LyRRk>|P<9(kcJTJJ<)k071m3x&K2v7umvF}M&|3OzS`;-5zf2-%8msg^m zu7Z;ZIooY_J~xPPZK}#CQ!8s;^e;2ccdG{`Fo{>&p8`XPSIcT- z8<7H}p1aOG%ZBVoT{k7r>&cn9nhU<5w~d3atL3lgMh zy_t|lh5oXeTgBgomOkDerj0teqpK&q?f6u5Ku59P^WFz?-t$Ks;RDdwrm>^ZMV>z7 z;K=;7qTv0TQy=|E6DZ1fh`^p?bi9W6JJJ3lpQVmKA6$fY+f-rSisI4xp`Tm_b^m!V zdVweP4IEu9WoO5ys#YkLb7)udHwC2j7xSnWb&bGgNKyW}4xiuFEOD;2nuyT{?{u`? z&GB(vo^`kk?Tj6DObYMgUzos#Oh=~akRanlFD$rkZuK(aev?c4Z=#+T_NZ(8V}U}l zJl|R~9DH0KvQ3w`kLx=a$l&x<%X>TZL_M0ac1%UHj0=ZtxLvg(Sz0=#dPw}^w0K&L z>-IXPn>8%4ziYdvhf}lQ-jCUt19u~26(E^6!sxr+BG&w{u7i3$VJgOXTpGjkO3lK& z3ba5J`Fn}YfD}c3@dht+V5Z&9?T3KFA+y6y{fnznW}EJdW1*M*TCP8TP8`VcqUSL~ znX;gveVXUf_&DG1Ko{>k+t&;X+$*jea#h1s zQ}MTyvtFy~59rJGPa#q&KB%RTOBJ;9g1)h(zGvZci*_ZtlyTYj&XGkb(l;-HBv-pg zJC8!8uxw+>$604{5<_$&T2dlq79>JE(?L*;y`#ai8!GvHG`)p)JV9LB*$VN>{S*ej0#`Qd}aF?SYuH+E(^NY}@ z3bYL||M2tRfAhZARU>b&HX!W9u*->;Iwfc9)5q#HV!!L-8;l@lutZ_Lg$fc^`>q5BuL2ubgYh+s6 zG>6jQFmoJd)#9wyOvRRYG)>0~`}evu>>%fcQbGCn$y}6*WQRZtQ5Hu6qOgC%E5%>H z6e778^=~m~1D*>ef_GK~70c%YfP@tq!_QnTs7uTfsb{uR#}@@K|Ahz11WJCjol*_T zy`q0VQ7+pj!;nwkrN)f0%o&{MV8?KeL1M6ZVB=d8VcjYXro1b$jlcc^RSW#&Z0U z8?h-*VJMN2XOBxXSMYsMx6i(^!08m5fD$|lYG7!6KJ5)o=0hJ^m{pcQ56F5DUr>Et z)c>hg3=>mrW{!|y9M~%sMU$yvW=p> zkt-j0s5=Y=anjY*xQHulDS|E7g(BqhLOg4j`VdGBEFu}4;iJ&QMdj!UV~-yGi5Si% zmR`05dS6bL5c7XshuD=5`JMkbqlNdm2G2{-R75+`U8ga1l@@@Nv9rFVm~jZ&W3tjx z8lKoH-)&CP%0Wg^v@L&&d%(HAJ(oc>!bEpsm1o@~3$+0E4)G=wSu-~Z(Jf;*fG+LGFA0>TmuVBn*rNY>U(v?BXqddIn?befjgZhG|5GB(3b%sk{Rqo6)p+*Hfb$INA2oO)& z!F7)FH|@H_jbcR%eBQqjuYF6qRMmtZUsd?exO$_}5y-6rUWYZ*+8vP#C{G?h8f-&Z zTW>4VohF5@pg_Io>y%Z5XW{>8gPqq1OEB_$%xtH%uTCIRZqhR_svoJj!o2WkuEs9s z{ixj2#OOdMxo5ixTx;pPt&z`|MS3#vi*-^;D9Tt~$)E=*5G|}$H7g)ex7gN?FaFx? z-M@(>T3OA`;<^SkRx^c>tCUgPt*lj9^IX1k7&i6v2;j}Lgc*(W;rB?e_jL3iI3_F? z;)@ol&NK<65*Qn6My8!J1gm2{k028Gb{qsw9_M8>5s7)$PhZ|cYjaVZ2|v4zXb7=& zFUV@rM{yDzaK&Bf)Z<4X$$~o!RHdZ9|QMWpqR%$Tto@Q zc+!5`1LY-?*yHrh%M$S=ZvSjrqPFL1kU(T3tLJ%Vb`y%&Y`4&{cgL0g)+@Y{_0-bK z|Mdm^qF%SvIbng>q~%{|rf_)&>EgV3cfQ(RclZJHANqVomzhB9z7gEw;B40zV`SQx z6zDxhhUE%(g9}&+&KRAwE*KJPefxCj-y9TRB!gfe0PxAtKq=VDg+D7deChbRn4R{s z#_+VrzE-etB);g|K4>p(2fss?5wfXCFP-pB1^>=RV5KMjdB>RV>5Ypq;0Gblnea{z zUg=D*D=;PmmjH&Mlo<_17THa95q{7rp@jz>%L~>m4R$Tjgutk4@%I{RF&66mb}I4Z zA*O%i!rm4q574Rf6R;}0en(;)j*C`s4wwt?9ZR?4|iMuiwTL4p1Fh~XFN$xq#?~b`ZopH zyic*Wmn0>};N)@$?)%!gQ;A~rmdiKi1+~Q0Q;?Tt-WO{9mMw>BW1uZ7O$4%D{pH|s z`h-kIBUN+QbrhEjnNT21@O*yrf#lL)2<~kqWKeRPUk}pzW49=+?p|qZXayO#1Q@pD% zt)xGGxt|E;hL?F;1g5j|l|0?Me;ed1_!oBLe%&z-=0@>}rd4^}DD(X9BmA>>sW>=7 wzxV@JHP)V%o*8Wo=D!D1t}3z+s~3skPcMx%3U(h5sp$YEId$23Y0I$x11n@D!T + + + #1a1817 + #ebe7e2 + #c47a52 + #4d3a3532 + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 5e335da..6dff3f1 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,4 +1,25 @@ + - #2A2A2E + #faf8f5 + #221f1d + #a8623d + + #4d8c8478 + + + #1a1817 diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 7ba83a2..af6b578 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,9 +1,30 @@ - + diff --git a/src/account/AccountMenu.tsx b/src/account/AccountMenu.tsx index 272930e..be62ece 100644 --- a/src/account/AccountMenu.tsx +++ b/src/account/AccountMenu.tsx @@ -63,13 +63,18 @@ export function AccountMenu() { const makeStyles = (theme: Theme) => StyleSheet.create({ + // Neutral: an avatar is an identity marker, not an action, and it sits over + // the map where an accent fill would pull against the pins. Matches the + // search button opposite it — both are map chrome. avatarButton: { position: 'absolute', right: 16, width: 40, height: 40, borderRadius: 20, - backgroundColor: theme.accent, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.lineControl, alignItems: 'center', justifyContent: 'center', shadowColor: '#000', @@ -80,13 +85,13 @@ const makeStyles = (theme: Theme) => }, avatarPressed: {opacity: 0.8}, avatarText: { - color: '#ffffff', + color: theme.ink, fontSize: 16, fontWeight: '600', }, sheetEmail: { fontSize: 12, - color: theme.textSecondary, + color: theme.muted, paddingHorizontal: 12, paddingBottom: 8, }, @@ -96,10 +101,10 @@ const makeStyles = (theme: Theme) => borderRadius: 8, }, sheetItemPressed: { - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, sheetItemText: { fontSize: 16, - color: theme.textPrimary, + color: theme.ink, }, }); diff --git a/src/auth/LoginScreen.tsx b/src/auth/LoginScreen.tsx index 5286711..8dfbe3c 100644 --- a/src/auth/LoginScreen.tsx +++ b/src/auth/LoginScreen.tsx @@ -7,12 +7,12 @@ import { Pressable, StyleSheet, Text, - TextInput, View, } from 'react-native'; import {LoginDocument} from '../graphql/__generated__/types'; import type {Theme} from '../theme/colors'; import {useTheme} from '../theme/useTheme'; +import {TextField} from '../ui/TextField'; import {clearSecurityWarning, useSecurityWarning} from './authClient'; import {tokenStore} from './tokenStore'; @@ -85,10 +85,8 @@ export function LoginScreen() { Sign in to Culpeos - - {loading ? ( - + ) : ( Log in )} @@ -138,7 +134,7 @@ export function LoginScreen() { const makeStyles = (theme: Theme) => StyleSheet.create({ - flex: {flex: 1, backgroundColor: theme.background}, + flex: {flex: 1, backgroundColor: theme.canvas}, container: { flex: 1, padding: 24, @@ -150,30 +146,24 @@ const makeStyles = (theme: Theme) => fontWeight: '600', marginBottom: 16, textAlign: 'center', - color: theme.textPrimary, - }, - input: { - borderWidth: 1, - borderColor: theme.border, - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 12, - fontSize: 16, - color: theme.textPrimary, + color: theme.ink, }, error: { - color: theme.error, + color: theme.danger, fontSize: 14, textAlign: 'center', }, warning: { - color: theme.error, + color: theme.danger, fontSize: 13, textAlign: 'center', fontStyle: 'italic', }, + // The screen's one CTA, so this is where accent is spent. The pressed + // state is the fill at 85%, which is what the opacity below amounts to + // over the canvas. button: { - backgroundColor: theme.action, + backgroundColor: theme.accent, borderRadius: 8, paddingVertical: 14, alignItems: 'center', @@ -182,7 +172,7 @@ const makeStyles = (theme: Theme) => buttonDisabled: {opacity: 0.5}, buttonPressed: {opacity: 0.85}, buttonText: { - color: theme.onAction, + color: theme.onAccent, fontSize: 16, fontWeight: '600', }, diff --git a/src/map/ElementDetailScreen.tsx b/src/map/ElementDetailScreen.tsx index a683223..99a28e4 100644 --- a/src/map/ElementDetailScreen.tsx +++ b/src/map/ElementDetailScreen.tsx @@ -122,8 +122,11 @@ function ModalContents({ {element.location.address} ) : null} {element.completed ? ( + // Neutral, with a tick doing the work the green used to: status + // is carried by icon and copy, not hue. `danger` is the app's + // only status color, and this isn't one. - Completed + ✓ Completed ) : null} @@ -232,7 +235,7 @@ function ModalContents({ ) : ( - {loading ? : null} + {loading ? : null} )} @@ -273,19 +276,22 @@ const makeStyles = (theme: Theme) => StyleSheet.create({ screen: { flex: 1, - backgroundColor: theme.background, + backgroundColor: theme.canvas, }, container: { flex: 1, - backgroundColor: theme.background, + backgroundColor: theme.canvas, }, + // Canvas plus a bottom rule, not a filled slab: the screen measures one + // background value end to end. A full point rather than a hairline, which + // at low brightness or through a screen protector barely renders. header: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingBottom: 12, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: theme.border, + borderBottomWidth: 1, + borderBottomColor: theme.line, }, closeButton: { width: 32, @@ -293,19 +299,19 @@ const makeStyles = (theme: Theme) => borderRadius: 16, alignItems: 'center', justifyContent: 'center', - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, marginRight: 12, }, closeIcon: { fontSize: 20, lineHeight: 22, - color: theme.textPrimary, + color: theme.ink, }, headerTitle: { flex: 1, fontSize: 16, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, }, editButton: { marginLeft: 12, @@ -313,7 +319,7 @@ const makeStyles = (theme: Theme) => }, editButtonText: { fontSize: 16, - color: theme.action, + color: theme.accentText, fontWeight: '600', }, scrollContent: { @@ -336,17 +342,19 @@ const makeStyles = (theme: Theme) => }, subtitle: { fontSize: 13, - color: theme.textSecondary, + color: theme.muted, }, completedTag: { alignSelf: 'flex-start', - backgroundColor: theme.successMuted, + backgroundColor: theme.lineFill, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 10, }, completedTagText: { - color: theme.success, + // `ink`, not `muted`: muted on a lineFill sits at 4.04:1 in light mode, + // under the 4.5:1 bar for text this size. + color: theme.ink, fontSize: 11, fontWeight: '600', }, @@ -358,7 +366,7 @@ const makeStyles = (theme: Theme) => width: 200, height: 140, borderRadius: 10, - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, photoPressed: { opacity: 0.7, @@ -369,7 +377,7 @@ const makeStyles = (theme: Theme) => sectionTitle: { fontSize: 12, fontWeight: '700', - color: theme.textTertiary, + color: theme.muted, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6, @@ -377,12 +385,15 @@ const makeStyles = (theme: Theme) => body: { fontSize: 15, lineHeight: 21, - color: theme.textPrimary, + color: theme.ink, }, + // Prose links carry a standing underline, so they don't rely on hue alone. link: { fontSize: 15, lineHeight: 21, - color: theme.accent, + color: theme.accentText, + textDecorationLine: 'underline', + textDecorationColor: theme.accentText, }, bookingRows: { gap: 10, @@ -398,7 +409,7 @@ const makeStyles = (theme: Theme) => width: 84, fontSize: 13, lineHeight: 21, - color: theme.textSecondary, + color: theme.muted, }, bookingValue: { flex: 1, @@ -411,17 +422,18 @@ const makeStyles = (theme: Theme) => flexWrap: 'wrap', gap: 6, }, + // A category label, so neutral — the wording is the label. labelChip: { - backgroundColor: theme.accentMuted, + backgroundColor: theme.lineFill, paddingHorizontal: 10, paddingVertical: 5, borderRadius: 12, }, labelChipPressed: { - backgroundColor: theme.border, + backgroundColor: theme.lineFillStrong, }, labelChipText: { - color: theme.accent, + color: theme.ink, fontSize: 12, fontWeight: '500', }, diff --git a/src/map/ElementEditScreen.tsx b/src/map/ElementEditScreen.tsx index bc2ea42..a8bfec5 100644 --- a/src/map/ElementEditScreen.tsx +++ b/src/map/ElementEditScreen.tsx @@ -12,7 +12,6 @@ import { StyleSheet, Switch, Text, - TextInput, View, } from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; @@ -30,7 +29,9 @@ import {photoImageSource} from '../photos/photoImageSource'; import {usePhotoUploader} from '../photos/photoUpload'; import type {Theme} from '../theme/colors'; import {useTheme} from '../theme/useTheme'; +import {emojiPickerTheme} from '../ui/emojiPickerTheme'; import {Sheet} from '../ui/Sheet'; +import {TextField} from '../ui/TextField'; type Props = NativeStackScreenProps; @@ -67,7 +68,7 @@ export function ElementEditScreen({route, navigation}: Props) { /> ) : ( - {loading ? : null} + {loading ? : null} )} @@ -91,6 +92,7 @@ function EditForm({ }) { const theme = useTheme(); const styles = useMemo(() => makeStyles(theme), [theme]); + const pickerTheme = useMemo(() => emojiPickerTheme(theme), [theme]); const safeAreaInsets = useSafeAreaInsets(); const [name, setName] = useState(element.name); const [uri, setUri] = useState(element.uri); @@ -272,7 +274,7 @@ function EditForm({ disabled={!canSave} style={styles.headerButton}> {saving ? ( - + ) : ( - - {uriError ? {uriError} : null} Completed + {/* + Android tints a switch from the Material theme's colorPrimary, not + from ours, so both states are set explicitly. On is a solid mid-tone + against a translucent off — the same fill-weight cue the map pins + use, which survives desaturation. Deliberately not accent: several + switched-on controls would out-shout the pins. + */} @@ -366,7 +376,7 @@ function EditForm({ onPress={onAddPhoto} style={styles.photoAdd}> {uploading ? ( - + ) : ( )} @@ -375,8 +385,8 @@ function EditForm({ - ) : null} - {deleting ? ( - + ) : ( Delete element )} @@ -479,6 +489,7 @@ function EditForm({ open={pickerOpen} onClose={() => setPickerOpen(false)} onEmojiSelected={emoji => setIcon(emoji.emoji)} + theme={pickerTheme} /> flex: {flex: 1}, screen: { flex: 1, - backgroundColor: theme.background, + backgroundColor: theme.canvas, }, loadingPane: { flex: 1, @@ -550,8 +561,10 @@ const makeStyles = (theme: Theme) => alignItems: 'center', paddingHorizontal: 16, paddingBottom: 12, - borderBottomWidth: StyleSheet.hairlineWidth, - borderBottomColor: theme.border, + // Canvas plus a rule, not a filled bar — the screen is one background + // value end to end. A full point, since a hairline barely renders. + borderBottomWidth: 1, + borderBottomColor: theme.line, }, headerButton: { minWidth: 56, @@ -559,17 +572,17 @@ const makeStyles = (theme: Theme) => }, headerButtonText: { fontSize: 16, - color: theme.textPrimary, + color: theme.ink, }, headerTitle: { flex: 1, fontSize: 16, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, textAlign: 'center', }, saveText: { - color: theme.action, + color: theme.accentText, fontWeight: '600', textAlign: 'right', }, @@ -586,25 +599,16 @@ const makeStyles = (theme: Theme) => fieldLabel: { fontSize: 12, fontWeight: '700', - color: theme.textTertiary, + color: theme.muted, textTransform: 'uppercase', letterSpacing: 0.5, }, - input: { - borderWidth: 1, - borderColor: theme.border, - borderRadius: 8, - paddingHorizontal: 12, - paddingVertical: 12, - fontSize: 16, - color: theme.textPrimary, - }, iconButton: { width: 64, height: 64, borderRadius: 8, borderWidth: 1, - borderColor: theme.border, + borderColor: theme.lineControl, alignItems: 'center', justifyContent: 'center', }, @@ -613,7 +617,7 @@ const makeStyles = (theme: Theme) => }, iconButtonPlaceholder: { fontSize: 28, - color: theme.textTertiary, + color: theme.muted, }, multiline: { minHeight: 120, @@ -631,7 +635,7 @@ const makeStyles = (theme: Theme) => width: 80, height: 80, borderRadius: 8, - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, photoRemove: { position: 'absolute', @@ -642,12 +646,12 @@ const makeStyles = (theme: Theme) => borderRadius: 11, // Inverts against the screen so the badge reads on any photo in either // appearance: a dark chip in light mode, a light chip in dark mode. - backgroundColor: theme.textPrimary, + backgroundColor: theme.ink, alignItems: 'center', justifyContent: 'center', }, photoRemoveIcon: { - color: theme.background, + color: theme.canvas, fontSize: 16, lineHeight: 18, fontWeight: '600', @@ -657,14 +661,14 @@ const makeStyles = (theme: Theme) => height: 80, borderRadius: 8, borderWidth: 1, - borderColor: theme.border, + borderColor: theme.lineControl, borderStyle: 'dashed', alignItems: 'center', justifyContent: 'center', }, photoAddIcon: { fontSize: 28, - color: theme.textTertiary, + color: theme.muted, }, switchRow: { flexDirection: 'row', @@ -673,7 +677,7 @@ const makeStyles = (theme: Theme) => }, tripsSelector: { borderWidth: 1, - borderColor: theme.border, + borderColor: theme.lineControl, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 12, @@ -682,7 +686,7 @@ const makeStyles = (theme: Theme) => }, tripsPlaceholder: { fontSize: 16, - color: theme.textTertiary, + color: theme.muted, }, tripPills: { flexDirection: 'row', @@ -693,7 +697,7 @@ const makeStyles = (theme: Theme) => flexDirection: 'row', alignItems: 'center', maxWidth: '100%', - backgroundColor: theme.accentMuted, + backgroundColor: theme.lineFill, borderRadius: 16, paddingHorizontal: 12, paddingVertical: 6, @@ -707,18 +711,18 @@ const makeStyles = (theme: Theme) => flexShrink: 1, fontSize: 14, fontWeight: '600', - color: theme.accent, + color: theme.ink, }, sheetTitle: { fontSize: 16, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, paddingHorizontal: 12, paddingBottom: 8, }, sheetEmpty: { fontSize: 14, - color: theme.textSecondary, + color: theme.muted, paddingHorizontal: 12, paddingVertical: 12, }, @@ -733,7 +737,7 @@ const makeStyles = (theme: Theme) => borderRadius: 8, }, tripRowPressed: { - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, tripRowIcon: { fontSize: 18, @@ -743,8 +747,11 @@ const makeStyles = (theme: Theme) => tripRowName: { flex: 1, fontSize: 16, - color: theme.textPrimary, + color: theme.ink, }, + // A selected state, which is a sanctioned use of accent. The tick carries + // the meaning on its own, so the hue is reinforcement rather than the + // only signal. tripRowCheck: { fontSize: 18, fontWeight: '700', @@ -760,14 +767,14 @@ const makeStyles = (theme: Theme) => flexDirection: 'row', alignItems: 'center', gap: 4, - backgroundColor: theme.accentMuted, + backgroundColor: theme.lineFill, paddingLeft: 10, paddingRight: 6, paddingVertical: 5, borderRadius: 12, }, labelChipText: { - color: theme.accent, + color: theme.ink, fontSize: 12, fontWeight: '500', }, @@ -778,7 +785,7 @@ const makeStyles = (theme: Theme) => justifyContent: 'center', }, labelChipRemoveIcon: { - color: theme.accent, + color: theme.ink, fontSize: 16, lineHeight: 18, fontWeight: '600', @@ -788,12 +795,14 @@ const makeStyles = (theme: Theme) => alignItems: 'center', gap: 8, }, + // A secondary action: a quiet fill with an ink label, rather than a solid + // one. A white label on `muted` is only 3.48:1, and flipping the label + // doesn't help — `muted` sits mid-scale in both appearances. labelAdd: { paddingHorizontal: 16, paddingVertical: 12, borderRadius: 8, - borderWidth: 1, - borderColor: theme.border, + backgroundColor: theme.lineFill, alignItems: 'center', justifyContent: 'center', }, @@ -801,12 +810,12 @@ const makeStyles = (theme: Theme) => opacity: 0.4, }, labelAddText: { - color: theme.action, + color: theme.ink, fontSize: 16, fontWeight: '600', }, error: { - color: theme.error, + color: theme.danger, fontSize: 14, }, deleteButton: { @@ -814,7 +823,7 @@ const makeStyles = (theme: Theme) => paddingVertical: 16, borderRadius: 8, borderWidth: 1, - borderColor: theme.error, + borderColor: theme.danger, alignItems: 'center', justifyContent: 'center', }, @@ -822,7 +831,7 @@ const makeStyles = (theme: Theme) => opacity: 0.4, }, deleteButtonText: { - color: theme.error, + color: theme.danger, fontSize: 16, fontWeight: '600', }, diff --git a/src/map/ElementPreviewCard.tsx b/src/map/ElementPreviewCard.tsx index 2e101b9..59deee2 100644 --- a/src/map/ElementPreviewCard.tsx +++ b/src/map/ElementPreviewCard.tsx @@ -67,7 +67,9 @@ const makeStyles = (theme: Theme) => position: 'absolute', left: 12, right: 12, - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.line, borderRadius: 12, padding: 12, flexDirection: 'row', @@ -92,16 +94,16 @@ const makeStyles = (theme: Theme) => title: { fontSize: 16, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, }, subtitle: { fontSize: 12, - color: theme.textSecondary, + color: theme.muted, marginTop: 2, }, description: { fontSize: 13, - color: theme.textSecondary, + color: theme.muted, marginTop: 4, }, closeButton: { @@ -110,11 +112,11 @@ const makeStyles = (theme: Theme) => borderRadius: 16, alignItems: 'center', justifyContent: 'center', - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, closeIcon: { fontSize: 20, lineHeight: 22, - color: theme.textSecondary, + color: theme.ink, }, }); diff --git a/src/map/FilterChips.tsx b/src/map/FilterChips.tsx index c917cb4..03917a7 100644 --- a/src/map/FilterChips.tsx +++ b/src/map/FilterChips.tsx @@ -104,11 +104,17 @@ const makeStyles = (theme: Theme) => justifyContent: 'center', gap: 8, }, + // Neutral, deliberately. These sit over the map, and an accent-filled chrome + // element competes with the accent pins it's filtering — content should win. + // A pill is only on screen while its filter is active, so its presence is + // already the "on" state; it doesn't need a hue to say so as well. pill: { flexDirection: 'row', alignItems: 'center', maxWidth: '100%', - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.line, borderRadius: 20, paddingLeft: 14, paddingRight: 6, @@ -128,7 +134,7 @@ const makeStyles = (theme: Theme) => flexShrink: 1, fontSize: 14, fontWeight: '600', - color: theme.accent, + color: theme.ink, }, clearButton: { width: 24, @@ -137,10 +143,10 @@ const makeStyles = (theme: Theme) => alignItems: 'center', justifyContent: 'center', marginLeft: 8, - backgroundColor: theme.accentMuted, + backgroundColor: theme.lineFill, }, clearIcon: { - color: theme.accent, + color: theme.ink, fontSize: 16, lineHeight: 18, }, diff --git a/src/map/MapScreen.tsx b/src/map/MapScreen.tsx index 9e4b352..db9c17c 100644 --- a/src/map/MapScreen.tsx +++ b/src/map/MapScreen.tsx @@ -46,6 +46,7 @@ import { type SearchPlace, type SearchTrip, } from './SearchOverlay'; +import {UserLocationPuck} from './UserLocationPuck'; import {usePinImages} from './usePinImages'; import {type Viewport, viewportStore} from './viewportStore'; @@ -524,10 +525,20 @@ export function MapScreen() { // real, as a disc over the top-right corner. We never rotate the map, // so turn it off explicitly. compass={false} + // The attribution button is the map SDK's own chrome and ships a light + // theme; left untinted it's one of the brightest things on a dark + // basemap. It stays visible — OpenFreeMap's data requires the credit — + // but as ink rather than the SDK's default. + tintColor={theme.ink} onPress={() => setSelectedElementId(null)} onRegionDidChange={onRegionDidChange}> - + + + StyleSheet.create({ container: { flex: 1, - backgroundColor: theme.background, + backgroundColor: theme.canvas, }, map: { flex: 1, }, + // Map chrome, not map content: neutral, so it doesn't compete with the + // pins it sits over. The ring is `lineControl` because it's the button's + // only edge — `surface` is 1.22:1 against the dark basemap and the drop + // shadow is black on near-black, so neither one bounds it. recenterButton: { position: 'absolute', right: 16, width: 48, height: 48, borderRadius: 24, - backgroundColor: theme.mapButtonBg, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.lineControl, alignItems: 'center', justifyContent: 'center', shadowColor: '#000', @@ -606,6 +623,6 @@ const makeStyles = (theme: Theme) => recenterIcon: { fontSize: 24, lineHeight: 28, - color: theme.mapButtonIcon, + color: theme.ink, }, }); diff --git a/src/map/PinIcon.tsx b/src/map/PinIcon.tsx index 1619726..a104f56 100644 --- a/src/map/PinIcon.tsx +++ b/src/map/PinIcon.tsx @@ -7,8 +7,12 @@ import type {Theme} from '../theme/colors'; // Web draws it at 30px; we size up for touch. Exported for `pinHitTest`, which // measures taps against the shape these describe. export const PIN_SIZE = 36; -// Selected pins only grow (and draw on top); the fill stays red, as on web. +// Selected pins only grow (and draw on top); the fill stays accent, as on web. export const PIN_SELECTED_SCALE = 1.25; +// Border weight. Web went 1px → 2px because a hairline smears or vanishes on an +// e-ink refresh; the mobile equivalent is a hairline at low brightness or +// through a screen protector. Scaled with the pin so it holds at capture size. +const PIN_BORDER = 2; // Rotating the square makes it occupy a box of side × √2, with the square // corner — the teardrop's tip — landing at the bottom centre of that box. export const PIN_DIAGONAL = PIN_SIZE * Math.SQRT2; @@ -64,9 +68,14 @@ const makeStyles = (theme: Theme, scale: number) => borderTopRightRadius: (PIN_SIZE / 2) * scale, borderBottomRightRadius: (PIN_SIZE / 2) * scale, borderBottomLeftRadius: 0, - backgroundColor: theme.pin, - borderWidth: scale, - borderColor: theme.pinBorder, + // A saved location is a solid accent fill with an ink border — one of the + // four sanctioned uses of accent, and the heaviest mark on the map. The + // weight, not the hue, is what separates it from everything else: a + // distinction by hue alone disappears under a color vision deficiency or + // on a grayscale screen. + backgroundColor: theme.accent, + borderWidth: PIN_BORDER * scale, + borderColor: theme.ink, alignItems: 'center', justifyContent: 'center', boxShadow: [ diff --git a/src/map/SearchOverlay.tsx b/src/map/SearchOverlay.tsx index 87b4588..5194bb2 100644 --- a/src/map/SearchOverlay.tsx +++ b/src/map/SearchOverlay.tsx @@ -116,7 +116,14 @@ export function SearchOverlay({ onChangeText={setText} onSubmitEditing={submit} placeholder="Search elements, trips, places" - placeholderTextColor={theme.textTertiary} + // Not a `TextField`: this one has no border of its own — the pill + // around it is the field — so there's no rest/focus border to move, + // and it's autofocused the moment it appears. It still needs the + // caret and selection colors Android would otherwise theme itself. + placeholderTextColor={theme.muted} + cursorColor={theme.accent} + selectionColor={theme.accentRing} + selectionHandleColor={theme.accent} returnKeyType="search" style={styles.input} /> @@ -136,7 +143,7 @@ export function SearchOverlay({ {loading ? ( - + ) : hasResults ? ( [styles.row, pressed && styles.rowPressed]}> - + {glyph} @@ -278,13 +286,19 @@ function ResultRow({ const makeStyles = (theme: Theme) => StyleSheet.create({ + // Chrome floating over the map, so: a surface fill plus a ring. The ring is + // `lineControl`, not `line` — over the basemap it's the only thing bounding + // the button, since `surface` is 1.22:1 against the dark basemap and a black + // drop shadow is invisible on it. iconButton: { position: 'absolute', left: 16, width: 40, height: 40, borderRadius: 20, - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.lineControl, alignItems: 'center', justifyContent: 'center', shadowColor: '#000', @@ -297,7 +311,7 @@ const makeStyles = (theme: Theme) => searchGlyph: { fontSize: 22, lineHeight: 26, - color: theme.accent, + color: theme.ink, }, expandedRoot: { position: 'absolute', @@ -308,7 +322,9 @@ const makeStyles = (theme: Theme) => fieldRow: { flexDirection: 'row', alignItems: 'center', - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.lineControl, borderRadius: 22, paddingLeft: 14, paddingRight: 6, @@ -322,13 +338,13 @@ const makeStyles = (theme: Theme) => fieldGlyph: { fontSize: 20, lineHeight: 24, - color: theme.textTertiary, + color: theme.muted, marginRight: 8, }, input: { flex: 1, fontSize: 15, - color: theme.textPrimary, + color: theme.ink, padding: 0, }, fieldClose: { @@ -337,16 +353,18 @@ const makeStyles = (theme: Theme) => borderRadius: 16, alignItems: 'center', justifyContent: 'center', - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, fieldCloseIcon: { fontSize: 18, lineHeight: 20, - color: theme.textSecondary, + color: theme.ink, }, resultsCard: { marginTop: 8, - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.line, borderRadius: 12, shadowColor: '#000', shadowOpacity: 0.15, @@ -365,7 +383,7 @@ const makeStyles = (theme: Theme) => }, emptyText: { fontSize: 14, - color: theme.textTertiary, + color: theme.muted, }, section: { paddingTop: 10, @@ -374,7 +392,7 @@ const makeStyles = (theme: Theme) => sectionTitle: { fontSize: 11, fontWeight: '700', - color: theme.textTertiary, + color: theme.muted, textTransform: 'uppercase', letterSpacing: 0.5, paddingHorizontal: 14, @@ -387,7 +405,7 @@ const makeStyles = (theme: Theme) => paddingVertical: 8, }, rowPressed: { - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, glyphWrap: { width: 36, @@ -396,15 +414,7 @@ const makeStyles = (theme: Theme) => alignItems: 'center', justifyContent: 'center', marginRight: 12, - }, - elementGlyphWrap: { - backgroundColor: theme.accent, - }, - tripGlyphWrap: { - backgroundColor: theme.trip, - }, - placeGlyphWrap: { - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, }, glyph: { fontSize: 18, @@ -418,21 +428,23 @@ const makeStyles = (theme: Theme) => rowTitle: { fontSize: 15, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, }, rowSubtitle: { fontSize: 12, - color: theme.textSecondary, + color: theme.muted, marginTop: 2, }, + // Neutral, like every other badge: it already says "Trip", so the hue was + // doing nothing a reader relied on. badge: { - backgroundColor: theme.tripMuted, + backgroundColor: theme.lineFill, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 10, }, badgeText: { - color: theme.trip, + color: theme.ink, fontSize: 11, fontWeight: '600', }, diff --git a/src/map/UserLocationPuck.tsx b/src/map/UserLocationPuck.tsx new file mode 100644 index 0000000..0d2797d --- /dev/null +++ b/src/map/UserLocationPuck.tsx @@ -0,0 +1,81 @@ +import {Layer} from '@maplibre/maplibre-react-native'; +import type {Theme} from '../theme/colors'; + +// The id `` gives the annotation's source. Passed to each layer +// so these draw at the user's position, exactly as the SDK's own puck does. +const SOURCE_ID = 'mlrn-user-location'; + +/** + * The user's location, in our tokens. + * + * MapLibre's own puck hardcodes `#33B5E5` in the layer paint — there's no color + * prop to reassign, so the only way to restyle it is to replace it, which is + * what ``'s `children` are for. Left alone it puts a saturated + * blue dot on the map beside the rust pins, and nothing in our source would + * have turned it up. + * + * Neutral rather than accent: the pins are the content, and accent is spent on + * them. This is the same white-ring-around-a-solid-core construction as the + * SDK's, so it still reads as "you are here" — the core just inverts with the + * appearance instead of being blue in both. + */ +export function UserLocationPuck({ + theme, + accuracy, +}: { + theme: Theme; + /** Accuracy radius in meters, or undefined to skip the accuracy circle. */ + accuracy?: number; +}) { + return ( + <> + {typeof accuracy === 'number' ? ( + + ) : null} + + + + ); +} + +const RING_RADIUS = 9; +const CORE_RADIUS = 6; diff --git a/src/map/usePinImages.tsx b/src/map/usePinImages.tsx index b9aef41..0e00daa 100644 --- a/src/map/usePinImages.tsx +++ b/src/map/usePinImages.tsx @@ -48,10 +48,9 @@ export function usePinImages( const hosts = useRef(new Map()); // A rasterised pin bakes in the colours it was drawn with, so those are part - // of its name: should the palette ever stop being shared between light and - // dark, the new pins get new names and are captured afresh rather than - // quietly keeping the old bitmaps. - const variant = `${theme.pin}|${theme.pinBorder}`; + // of its name: switching appearance changes both, and the new pins get new + // names and are captured afresh rather than quietly keeping the old bitmaps. + const variant = `${theme.accent}|${theme.ink}`; const nameFor = useCallback( (icon: string | null | undefined) => `pin:${variant}:${icon ?? ''}`, [variant], diff --git a/src/photos/PhotoViewer.tsx b/src/photos/PhotoViewer.tsx index 414b432..f813c20 100644 --- a/src/photos/PhotoViewer.tsx +++ b/src/photos/PhotoViewer.tsx @@ -383,9 +383,14 @@ export function PhotoViewer({uri, description, onClose}: Props) { ); } -// Not themed: a photo viewer is black in both appearances, so the image sets -// the mood and its edges are unambiguous. The chrome is therefore fixed to -// translucent white rather than reading from the theme. +// Deliberately literal, and the only place in the app that is. Text and chrome +// over photography can't be checked against a token — the backdrop changes +// pixel to pixel — and shouldn't follow the color scheme either: a photo viewer +// is black in both appearances so the image sets the mood and its edges stay +// unambiguous. Hence fixed white on fixed black rather than ink on canvas. +// +// Leave these alone in any palette sweep. An accent here would do no work: over +// a photo it can't read as interactive, it just reads as dim. const styles = StyleSheet.create({ overlay: { ...StyleSheet.absoluteFill, diff --git a/src/share/ImportShareScreen.tsx b/src/share/ImportShareScreen.tsx index 081e716..27841c3 100644 --- a/src/share/ImportShareScreen.tsx +++ b/src/share/ImportShareScreen.tsx @@ -90,7 +90,7 @@ export function ImportShareScreen({route, navigation}: Props) { ) : ( <> - + Saving shared link… {content} @@ -106,7 +106,7 @@ const makeStyles = (theme: Theme) => StyleSheet.create({ screen: { flex: 1, - backgroundColor: theme.background, + backgroundColor: theme.canvas, }, closeButton: { position: 'absolute', @@ -116,13 +116,13 @@ const makeStyles = (theme: Theme) => borderRadius: 16, alignItems: 'center', justifyContent: 'center', - backgroundColor: theme.surfaceMuted, + backgroundColor: theme.lineFill, zIndex: 1, }, closeIcon: { fontSize: 20, lineHeight: 22, - color: theme.textPrimary, + color: theme.ink, }, center: { flex: 1, @@ -134,33 +134,34 @@ const makeStyles = (theme: Theme) => title: { fontSize: 18, fontWeight: '600', - color: theme.textPrimary, + color: theme.ink, textAlign: 'center', }, subtitle: { fontSize: 14, lineHeight: 20, - color: theme.textSecondary, + color: theme.muted, textAlign: 'center', }, + // The screen's one CTA — the other action is a plain text Cancel. retryButton: { marginTop: 8, paddingHorizontal: 24, paddingVertical: 12, borderRadius: 24, - backgroundColor: theme.action, + backgroundColor: theme.accent, }, retryButtonPressed: { opacity: 0.85, }, retryText: { - color: theme.onAction, + color: theme.onAccent, fontSize: 15, fontWeight: '600', }, cancelText: { marginTop: 4, - color: theme.textSecondary, + color: theme.muted, fontSize: 14, }, }); diff --git a/src/theme/colors.ts b/src/theme/colors.ts index 63d0621..d4d0ed0 100644 --- a/src/theme/colors.ts +++ b/src/theme/colors.ts @@ -1,9 +1,17 @@ /** - * Semantic color tokens for light and dark mode. + * The culpeo palette: ten semantic roles, declared once per appearance. + * + * The app is a warm neutral shell. The only saturated things on screen are + * photographs, map pins, and the primary action — everything else is ink, + * muted, line, canvas, surface. Matches the web app's token set so the two + * can't drift; see the style guide for the reasoning behind each value. * * Components consume these through {@link useTheme} rather than reading the - * device color scheme directly, so the same `Theme` shape drives both - * appearances and there is a single place to tune either palette. + * device color scheme directly, so there is exactly one declaration per role + * and no `isDark ? … : …` at any use site. + * + * Contrast ratios below are measured against that appearance's own canvas. AA + * is 4.5:1 for body text and 3:1 for borders and other non-text UI. */ export type ColorScheme = 'light' | 'dark'; @@ -11,131 +19,160 @@ export type ColorScheme = 'light' | 'dark'; export type Theme = { scheme: ColorScheme; - // Surfaces, from furthest-back to nearest. - background: string; // screen background behind everything - card: string; // cards, sheets, floating overlays - surfaceMuted: string; // subtle fills: inputs, close buttons, pills - - // Text, in descending emphasis. - textPrimary: string; - textSecondary: string; - textTertiary: string; // placeholders, disabled, faint captions - - // Lines. - border: string; - - // Brand accent (blue) and its tints. - accent: string; // primary actions, links, icons - accentStrong: string; // pressed/selected accent - accentMuted: string; // accent-tinted background (badge/glyph wrap) - - // Secondary "action" color (teal): solid buttons and inline actions - // (login button, Edit/Save text). - action: string; - onAction: string; // text/icon drawn on top of an `action` fill - - // Secondary "trip" accent (purple) and its tint. - trip: string; - tripMuted: string; - - // Status colors and their tints. - error: string; - success: string; - successMuted: string; - - // Scrim behind modal sheets. + /** Page background. Every full-screen region is canvas, never surface. */ + canvas: string; + /** Raised above the page only: sheets, popovers, menus, floating cards. */ + surface: string; + + /** Body text, headings, icons. */ + ink: string; + /** Secondary text, placeholders, disabled. */ + muted: string; + /** + * Dividers and rules — decoration, so deliberately quiet. Nothing relies on + * seeing it, which is why it's allowed to sit under the 3:1 non-text bar. + */ + line: string; + /** + * The boundary of a control: an input's border, a tappable button's edge. + * `line`'s stronger sibling, the way `accentText` is `accent`'s — same role, + * but this one is load-bearing, so it has to clear 3:1. + * + * Reach for this whenever the border is what tells you a control is there. A + * text input is `surface` on `canvas`, and those two are 1.06:1 apart in light + * and 1.10:1 in dark — the fill does no work in either scheme, so the border + * is the only thing saying "field". At plain `line` in dark that came to + * 1.32:1: a field you cannot find at night. + */ + lineControl: string; + + /** + * Culpeo rust. Sanctioned in exactly four places: the primary button, map + * pins, focus rings and focused input borders, and active/selected states. + * Everything else — badges, chips, category labels, counts, avatars — is + * neutral, because each already carries an icon or its own wording. + */ + accent: string; + /** Rust for links and other small text; darker so it still clears AA. */ + accentText: string; + /** The label sitting *on* an accent fill. Flips dark in dark mode. */ + onAccent: string; + /** Validation text and borders. The only status color. */ + danger: string; + + // Alpha variants of the tokens above. Not new roles — the same token at an + // opacity, kept here so use sites never compose one by hand. + /** `line` at 15%: quiet secondary-button fill, subtle panel fill. */ + lineFill: string; + /** `line` at 30%: pressed state of the above, and a switch's "off" track. */ + lineFillStrong: string; + /** `accent` at 20%: focus ring. */ + accentRing: string; + + /** Scrim behind modal sheets. */ scrim: string; - // Map. + /** Basemap style; see the note above MAP_STYLE_LIGHT. */ mapStyleUrl: string; - pin: string; // teardrop pin fill - pinBorder: string; // hairline outline around a pin, for contrast - mapButtonBg: string; // recenter button background - mapButtonIcon: string; }; -const MAP_STYLE_LIGHT = 'https://tiles.openfreemap.org/styles/liberty'; +// A dataviz basemap, not a streets one. The map is the largest colored surface +// in the app and a streets style fights the palette: `liberty` declares 44 +// distinct colors at 0.178 mean saturation, including green parks and blue +// water that argue directly with a rust accent. `positron` is 29 colors at +// 0.026 — built as a substrate for overlaid data, which is exactly the +// arrangement here: the pins are the content, the map is context. It keeps +// faint land/water/road separation, so it still reads as a map rather than +// going flat the way a toner style would. `dark` is the same family. +const MAP_STYLE_LIGHT = 'https://tiles.openfreemap.org/styles/positron'; const MAP_STYLE_DARK = 'https://tiles.openfreemap.org/styles/dark'; -// Pins are the same Google-Maps red in both appearances, matching the web app -// (`.maplibre-marker-pin` in culpeos assets/css/app.css) so the same place -// reads the same wherever it's opened. The outline is a translucent black -// hairline rather than a theme color, which works over either basemap. -const PIN_RED = '#ea4335'; -const PIN_OUTLINE = 'rgba(0,0,0,0.2)'; - -export const lightTheme: Theme = { - scheme: 'light', +/** + * A token at partial opacity, as an `rgba()` string. + * + * Kept as alpha rather than pre-composited against the canvas because these + * fills sit on both canvas and surface, and an alpha composites correctly + * against whichever is behind it. + */ +function alpha(hex: string, a: number): string { + const [r, g, b] = [1, 3, 5].map(i => + Number.parseInt(hex.slice(i, i + 2), 16), + ); + return `rgba(${r},${g},${b},${a})`; +} - background: '#ffffff', - card: '#ffffff', - surfaceMuted: '#f1f1f1', +const LIGHT_LINE = '#8c8478'; +const DARK_LINE = '#3a3532'; +const LIGHT_ACCENT = '#a8623d'; +const DARK_ACCENT = '#c47a52'; - textPrimary: '#111111', - textSecondary: '#666666', - textTertiary: '#999999', +// One value for both appearances, because one value clears the bar in both: +// 4.60:1 / 4.88:1 on the light canvas and surface, 3.63:1 / 3.29:1 on the dark +// pair. It sits below dark `muted` (5.70:1), so it still reads as a border +// rather than as text. A second declaration would only be two things to keep in +// step for no gain. +const LINE_CONTROL = '#7a6f69'; - border: '#dddddd', +export const lightTheme: Theme = { + scheme: 'light', - accent: '#1d6fe0', - accentStrong: '#0b4ea2', - accentMuted: '#eef3fb', + canvas: '#faf8f5', + surface: '#ffffff', - action: '#0a7ea4', - onAction: '#ffffff', + ink: '#221f1d', // 15.46:1 AAA + muted: '#776e63', // 4.72:1 AA + line: LIGHT_LINE, // 3.48:1 — decorative, no bar to clear + lineControl: LINE_CONTROL, // 4.60:1 on canvas, 4.88:1 on surface - trip: '#7a3ff2', - tripMuted: '#efe9fd', + accent: LIGHT_ACCENT, // 4.42:1 as a fill + accentText: '#9d5a37', // 5.01:1 AA + onAccent: '#ffffff', // 4.69:1 on accent + danger: '#a8322d', // 6.27:1 AA - error: '#cc0000', - success: '#1e8e3e', - successMuted: '#e6f4ea', + lineFill: alpha(LIGHT_LINE, 0.15), + lineFillStrong: alpha(LIGHT_LINE, 0.3), + accentRing: alpha(LIGHT_ACCENT, 0.2), scrim: 'rgba(0,0,0,0.35)', mapStyleUrl: MAP_STYLE_LIGHT, - pin: PIN_RED, - pinBorder: PIN_OUTLINE, - mapButtonBg: '#ffffff', - mapButtonIcon: '#1d6fe0', }; export const darkTheme: Theme = { scheme: 'dark', - background: '#121212', - card: '#1e1e1e', - surfaceMuted: '#2a2a2a', - - textPrimary: '#f2f2f2', - textSecondary: '#b0b0b0', - textTertiary: '#7e7e7e', - - border: '#3a3a3a', + canvas: '#1a1817', + surface: '#232120', - accent: '#4f93f0', - accentStrong: '#7badf5', - accentMuted: '#1b2a44', + ink: '#ebe7e2', // 14.37:1 AAA + muted: '#9a9186', // 5.70:1 AA + // 1.46:1 on the dark canvas. Fine for a rule between two things, which is all + // this is for — anything whose border has to be *seen* uses lineControl. + line: DARK_LINE, + lineControl: LINE_CONTROL, // 3.63:1 on canvas, 3.29:1 on surface - action: '#3aa9cf', - onAction: '#ffffff', + // Accent lightens in dark mode, so a white label on it drops to 3.36:1 — + // hence onAccent flipping dark rather than staying a literal white. + accent: DARK_ACCENT, // 5.27:1 as a fill + accentText: DARK_ACCENT, // 5.27:1 AA — same value as accent here + onAccent: '#1a1817', // 5.27:1 on accent + danger: '#dd8577', // 6.48:1 AA, tuned to read as *text* on a dark canvas - trip: '#a988f7', - tripMuted: '#2a2147', - - error: '#ff6b6b', - success: '#5bd07f', - successMuted: '#16331f', + lineFill: alpha(DARK_LINE, 0.15), + lineFillStrong: alpha(DARK_LINE, 0.3), + accentRing: alpha(DARK_ACCENT, 0.2), scrim: 'rgba(0,0,0,0.6)', mapStyleUrl: MAP_STYLE_DARK, - pin: PIN_RED, - pinBorder: PIN_OUTLINE, - mapButtonBg: '#1e1e1e', - mapButtonIcon: '#4f93f0', }; +// There is no `dangerFill` because nothing in the app fills with danger yet — +// the one destructive control is an outlined button using `danger` as border +// and text. Should a filled destructive button arrive, it needs its own token: +// dark `danger` is tuned to read as text on a dark canvas, which leaves it far +// too light under a white button label (2.73:1). + export function themeForScheme(scheme: string | null | undefined): Theme { return scheme === 'dark' ? darkTheme : lightTheme; } diff --git a/src/theme/navigationTheme.ts b/src/theme/navigationTheme.ts new file mode 100644 index 0000000..60d0f80 --- /dev/null +++ b/src/theme/navigationTheme.ts @@ -0,0 +1,41 @@ +import { + DarkTheme, + DefaultTheme, + type Theme as NavigationTheme, +} from '@react-navigation/native'; +import type {Theme} from './colors'; +import {useTheme} from './useTheme'; + +/** + * React Navigation's theme, built from our tokens. + * + * Handing it `DefaultTheme`/`DarkTheme` instead would leave a second palette + * running alongside ours — its blue `primary`, its cool `border`, and a + * `background` (`#f2f2f2` light, pure black dark) that differs from our canvas, + * which shows as a flash of the wrong color in the gap during a push + * transition. There is one declaration per role, and this is where the + * navigator reads it. + * + * Only `background` is really load-bearing today — headers are hidden and each + * screen draws its own chrome — but the rest are mapped so nothing new picks up + * a default. + */ +export function navigationThemeFor(theme: Theme): NavigationTheme { + const base = theme.scheme === 'dark' ? DarkTheme : DefaultTheme; + return { + ...base, + dark: theme.scheme === 'dark', + colors: { + primary: theme.accent, + background: theme.canvas, + card: theme.canvas, + text: theme.ink, + border: theme.line, + notification: theme.accent, + }, + }; +} + +export function useNavigationTheme(): NavigationTheme { + return navigationThemeFor(useTheme()); +} diff --git a/src/ui/Sheet.tsx b/src/ui/Sheet.tsx index 9005418..e50ac94 100644 --- a/src/ui/Sheet.tsx +++ b/src/ui/Sheet.tsx @@ -102,8 +102,12 @@ const makeStyles = (theme: Theme) => right: 0, bottom: 0, }, + // Genuinely raised, so genuinely a surface: rounded, over a scrim, with a + // `line` ring to hold its top edge in dark mode where the shadow can't. card: { - backgroundColor: theme.card, + backgroundColor: theme.surface, + borderTopWidth: 1, + borderTopColor: theme.line, borderTopLeftRadius: 16, borderTopRightRadius: 16, paddingTop: 20, diff --git a/src/ui/TextField.tsx b/src/ui/TextField.tsx new file mode 100644 index 0000000..be37d15 --- /dev/null +++ b/src/ui/TextField.tsx @@ -0,0 +1,99 @@ +import {useMemo, useState} from 'react'; +import { + type StyleProp, + StyleSheet, + TextInput, + type TextInputProps, + View, + type ViewStyle, +} from 'react-native'; +import type {Theme} from '../theme/colors'; +import {useTheme} from '../theme/useTheme'; + +type Props = TextInputProps & { + /** Draws the error border. The message itself is the caller's to render. */ + invalid?: boolean; + /** Layout for the wrapper, e.g. `flex: 1` inside a row. */ + containerStyle?: StyleProp; +}; + +/** + * The app's bordered text input. + * + * Exists so focus is visible. Rest and focus were previously the same neutral + * border, which meant focusing a field produced no visible change at all — + * fine-looking code, and a WCAG 2.4.7 failure. Focus now moves the border to + * `accent` and adds a ring, which is a sanctioned accent use. + * + * It also owns the handful of colors Android would otherwise supply from its + * own theme rather than ours: the caret, the selection highlight, the selection + * handles, and the placeholder. + */ +export function TextField({invalid, containerStyle, style, ...props}: Props) { + const theme = useTheme(); + const styles = useMemo(() => makeStyles(theme), [theme]); + const [focused, setFocused] = useState(false); + + return ( + + {/* Behind the input and outside its bounds, so the ring costs no layout + and doesn't shift the field when focus arrives. A shadow wouldn't do: + it's invisible over a dark canvas. */} + {focused ? : null} + { + setFocused(true); + props.onFocus?.(event); + }} + onBlur={event => { + setFocused(false); + props.onBlur?.(event); + }} + placeholderTextColor={theme.muted} + cursorColor={theme.accent} + selectionColor={theme.accentRing} + selectionHandleColor={theme.accent} + style={[ + styles.input, + focused && styles.inputFocused, + invalid && styles.inputInvalid, + style, + ]} + /> + + ); +} + +/** Ring thickness, and the input's corner radius it has to sit outside of. */ +const RING = 4; +const RADIUS = 8; + +const makeStyles = (theme: Theme) => + StyleSheet.create({ + ring: { + position: 'absolute', + top: -RING, + left: -RING, + right: -RING, + bottom: -RING, + borderRadius: RADIUS + RING, + backgroundColor: theme.accentRing, + }, + input: { + backgroundColor: theme.surface, + borderWidth: 1, + borderColor: theme.lineControl, + borderRadius: RADIUS, + paddingHorizontal: 12, + paddingVertical: 12, + fontSize: 16, + color: theme.ink, + }, + inputFocused: { + borderColor: theme.accent, + }, + inputInvalid: { + borderColor: theme.danger, + }, + }); diff --git a/src/ui/emojiPickerTheme.ts b/src/ui/emojiPickerTheme.ts new file mode 100644 index 0000000..dc0ef02 --- /dev/null +++ b/src/ui/emojiPickerTheme.ts @@ -0,0 +1,45 @@ +import type {Theme} from '../theme/colors'; + +/** + * The emoji picker's palette, in our tokens. + * + * `rn-emoji-keyboard` ships a hardcoded light theme — a white container and + * `#005b96` for the active category and pressed buttons. It has no notion of a + * color scheme, so in dark mode it opens as a white sheet with a blue highlight + * over the app's dark canvas, and nothing in our own source would have shown + * that. Its `theme` prop is the whole fix. + * + * The emoji themselves stay full color, of course: imagery carrying the color + * is the point of this palette, not an exception to it. + */ +export function emojiPickerTheme(theme: Theme) { + return { + backdrop: theme.scrim, + knob: theme.line, + container: theme.surface, + header: theme.ink, + skinTonesContainer: theme.lineFill, + category: { + icon: theme.muted, + // Selecting a category is an active state, which is a sanctioned use. + iconActive: theme.onAccent, + container: theme.lineFill, + containerActive: theme.accent, + }, + search: { + background: theme.lineFill, + text: theme.ink, + placeholder: theme.muted, + icon: theme.muted, + }, + customButton: { + icon: theme.ink, + iconPressed: theme.ink, + background: theme.lineFill, + backgroundPressed: theme.lineFillStrong, + }, + emoji: { + selected: theme.lineFillStrong, + }, + }; +}