fix: show the undo button on the undo bar in dark mode

This commit is contained in:
Linus Rath
2026-10-22 22:04:29 +02:00
parent 721c2e34a1
commit 33eb8f99e5
3 changed files with 56 additions and 3 deletions
+5 -3
View File
@@ -6,6 +6,7 @@ import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { spacing, radius, typography, type ThemePalette } from '../theme/tokens';
import { useColors } from '../theme/colors';
import { undoSnackbarColors } from './undo-snackbar-colors';
import { useEmailStore } from '../stores/email-store';
import { useSendUndoStore, restoreUndoneSend, type PendingUndoSend } from '../stores/send-undo-store';
import { useLocaleStore } from '../stores/locale-store';
@@ -158,6 +159,7 @@ export function UndoSnackbar() {
}
function makeStyles(c: ThemePalette) {
const colors = undoSnackbarColors(c);
return StyleSheet.create({
wrap: {
position: 'absolute',
@@ -170,7 +172,7 @@ function makeStyles(c: ThemePalette) {
paddingLeft: spacing.lg,
paddingRight: spacing.sm,
borderRadius: radius.md,
backgroundColor: c.text,
backgroundColor: colors.bar,
shadowColor: '#000',
shadowOpacity: 0.3,
shadowRadius: 12,
@@ -179,7 +181,7 @@ function makeStyles(c: ThemePalette) {
},
label: {
...typography.body,
color: c.background,
color: colors.label,
flex: 1,
},
undoBtn: {
@@ -192,7 +194,7 @@ function makeStyles(c: ThemePalette) {
},
undoText: {
...typography.bodySemibold,
color: c.primary,
color: colors.action,
letterSpacing: 0.5,
},
sendNowText: {
@@ -0,0 +1,41 @@
import { describe, expect, it } from 'vitest';
import { DARK_COLORS, LIGHT_COLORS, type ThemePalette } from '../../theme/tokens';
import { BUILTIN_THEMES } from '../../theme/builtin-themes';
import { undoSnackbarColors } from '../undo-snackbar-colors';
// The undo bar (UndoSnackbar) is drawn in the text colour. Its UNDO button
// used the primary colour, which is white in the default dark theme: white
// on white, so in dark mode the bar showed "Email archived" and no visible
// way to undo. The bound is 3:1, not 4.5:1, because Solarized's own text
// and background are a little under 4.5:1.
function luminance(hex: string): number {
const v = hex.replace('#', '');
const full = v.length === 3 ? v.split('').map((ch) => ch + ch).join('') : v.slice(0, 6);
const [r, g, b] = [0, 2, 4].map((i) => {
const c = parseInt(full.slice(i, i + 2), 16) / 255;
return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function contrast(a: string, b: string): number {
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05);
}
const palettes: Array<[string, ThemePalette]> = [
['default light', LIGHT_COLORS],
['default dark', DARK_COLORS],
...BUILTIN_THEMES.flatMap((theme): Array<[string, ThemePalette]> => [
[`${theme.name} light`, { ...LIGHT_COLORS, ...theme.light } as ThemePalette],
[`${theme.name} dark`, { ...DARK_COLORS, ...theme.dark } as ThemePalette],
]),
];
describe('undo bar', () => {
it.each(palettes)('UNDO is readable on the bar (%s)', (_name, palette) => {
const { bar, action } = undoSnackbarColors(palette);
expect(contrast(action, bar)).toBeGreaterThanOrEqual(3);
});
});
+10
View File
@@ -0,0 +1,10 @@
import type { ThemePalette } from '../theme/tokens';
/**
* Colours of the undo bar. The bar is drawn in the text colour, so what is
* on it takes the background colour. The primary colour can't be used for
* UNDO: the default dark theme's primary is white, like the bar.
*/
export function undoSnackbarColors(c: ThemePalette): { bar: string; label: string; action: string } {
return { bar: c.text, label: c.background, action: c.background };
}