Improve admin mobile dark mode contrast
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-22 22:02:45 +01:00
parent 6318aec3cb
commit 85f2c42fc5
5 changed files with 59 additions and 35 deletions

View File

@@ -46,14 +46,19 @@ function DashboardCard({
}: React.ComponentProps<typeof Card> & { variant?: 'default' | 'embedded' }) { }: React.ComponentProps<typeof Card> & { variant?: 'default' | 'embedded' }) {
const theme = useAdminTheme(); const theme = useAdminTheme();
const isEmbedded = variant === 'embedded'; const isEmbedded = variant === 'embedded';
const cardSurface = isEmbedded
? (theme.surfaceMuted ?? theme.glassSurface ?? theme.surface)
: (theme.glassSurfaceStrong ?? theme.surface);
const cardBorder = theme.glassBorder ?? theme.border;
const cardShadow = theme.glassShadow ?? theme.shadow;
return ( return (
<Card <Card
backgroundColor={theme.surface} backgroundColor={cardSurface}
borderRadius={isEmbedded ? 16 : 20} borderRadius={isEmbedded ? 16 : 20}
borderWidth={1} borderWidth={1}
borderColor={theme.border} borderColor={cardBorder}
padding="$3.5" padding="$3.5"
shadowColor={theme.shadow} shadowColor={cardShadow}
shadowOpacity={isEmbedded ? 0 : 0.16} shadowOpacity={isEmbedded ? 0 : 0.16}
shadowRadius={isEmbedded ? 0 : 16} shadowRadius={isEmbedded ? 0 : 16}
shadowOffset={isEmbedded ? { width: 0, height: 0 } : { width: 0, height: 10 }} shadowOffset={isEmbedded ? { width: 0, height: 0 } : { width: 0, height: 10 }}
@@ -444,7 +449,8 @@ function LifecycleHero({
alignItems="center" alignItems="center"
justifyContent="space-between" justifyContent="space-between"
borderWidth={1} borderWidth={1}
borderColor={theme.border} borderColor={theme.glassBorder ?? theme.border}
backgroundColor={theme.surfaceMuted}
borderRadius={12} borderRadius={12}
paddingHorizontal="$2.5" paddingHorizontal="$2.5"
paddingVertical="$2" paddingVertical="$2"

View File

@@ -18,9 +18,9 @@ export function BottomNav({ active, onNavigate }: { active: NavKey; onNavigate:
const theme = useAdminTheme(); const theme = useAdminTheme();
// Modern Glass Background // Modern Glass Background
const navSurface = 'rgba(255, 255, 255, 0.85)'; const navSurface = theme.glassSurfaceStrong ?? theme.surfaceMuted ?? theme.surface;
const navBorder = theme.border; const navBorder = theme.glassBorder ?? theme.border;
const navShadow = theme.shadow; const navShadow = theme.glassShadow ?? theme.shadow;
const [pressedKey, setPressedKey] = React.useState<NavKey | null>(null); const [pressedKey, setPressedKey] = React.useState<NavKey | null>(null);
@@ -62,7 +62,7 @@ export function BottomNav({ active, onNavigate }: { active: NavKey; onNavigate:
const IconCmp = item.icon; const IconCmp = item.icon;
// Dynamic Styles // Dynamic Styles
const color = activeState ? theme.primary : theme.subtle; const color = activeState ? theme.primary : theme.muted;
const strokeWidth = activeState ? 2.5 : 2; const strokeWidth = activeState ? 2.5 : 2;
return ( return (

View File

@@ -253,9 +253,13 @@ export function MobileSelect({
style, style,
...props ...props
}: MobileSelectProps) { }: MobileSelectProps) {
const { border, surface, text, primary, danger, subtle } = useAdminTheme(); const { border, surface, text, primary, danger, subtle, glassSurfaceStrong, surfaceMuted, glassBorder } = useAdminTheme();
const borderColor = hasError ? danger : border; const borderColor = hasError ? danger : (glassBorder ?? border);
const ringColor = hasError ? withAlpha(danger, 0.18) : withAlpha(primary, 0.18); const ringColor = hasError ? withAlpha(danger, 0.18) : withAlpha(primary, 0.18);
const triggerSurface = surfaceMuted ?? glassSurfaceStrong ?? surface;
const contentSurface = glassSurfaceStrong ?? surface;
const itemSurface = surface;
const itemHover = surfaceMuted ?? surface;
const hasSizing = const hasSizing =
typeof containerStyle === 'object' && typeof containerStyle === 'object' &&
containerStyle !== null && containerStyle !== null &&
@@ -298,7 +302,7 @@ export function MobileSelect({
borderRadius={12} borderRadius={12}
borderWidth={1} borderWidth={1}
borderColor={borderColor as any} borderColor={borderColor as any}
backgroundColor={surface as any} backgroundColor={triggerSurface as any}
paddingVertical={compact ? 6 : 10} paddingVertical={compact ? 6 : 10}
paddingHorizontal="$3" paddingHorizontal="$3"
disabled={props.disabled} disabled={props.disabled}
@@ -320,14 +324,22 @@ export function MobileSelect({
zIndex={200000} zIndex={200000}
{...({ borderRadius: 14 } as any)} {...({ borderRadius: 14 } as any)}
borderWidth={1} borderWidth={1}
borderColor={border} borderColor={glassBorder ?? border}
backgroundColor={surface as any} backgroundColor={contentSurface as any}
> >
<Select.Viewport {...({ padding: "$2" } as any)}> <Select.Viewport {...({ padding: "$2" } as any)}>
<Select.Group> <Select.Group>
{options.map((option, index) => ( {options.map((option, index) => (
<Select.Item index={index} key={`${option.value}-${index}`} value={option.value} disabled={option.disabled}> <Select.Item
<Select.ItemText>{option.label}</Select.ItemText> index={index}
key={`${option.value}-${index}`}
value={option.value}
disabled={option.disabled}
backgroundColor={itemSurface as any}
hoverStyle={{ backgroundColor: itemHover as any }}
pressStyle={{ backgroundColor: itemHover as any }}
>
<Select.ItemText {...({ color: text } as any)}>{option.label}</Select.ItemText>
</Select.Item> </Select.Item>
))} ))}
</Select.Group> </Select.Group>

View File

@@ -44,27 +44,27 @@ export function PillBadge({
tone?: 'success' | 'warning' | 'danger' | 'muted'; tone?: 'success' | 'warning' | 'danger' | 'muted';
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const { theme } = useAdminTheme(); const { successText, warningText, dangerText, muted, surfaceMuted, border } = useAdminTheme();
const palette: Record<typeof tone, { bg: string; text: string; border: string }> = { const palette: Record<typeof tone, { bg: string; text: string; border: string }> = {
success: { success: {
bg: String(theme.backgroundStrong?.val ?? '#ecfdf3'), bg: withAlpha(successText, 0.16),
text: String(theme.green10?.val ?? '#047857'), text: successText,
border: String(theme.green6?.val ?? '#bbf7d0'), border: withAlpha(successText, 0.35),
}, },
warning: { warning: {
bg: String(theme.yellow3?.val ?? '#fffbeb'), bg: withAlpha(warningText, 0.16),
text: String(theme.yellow11?.val ?? '#92400e'), text: warningText,
border: String(theme.yellow6?.val ?? '#fef3c7'), border: withAlpha(warningText, 0.35),
}, },
danger: { danger: {
bg: String(theme.red3?.val ?? '#FEE2E2'), bg: withAlpha(dangerText, 0.16),
text: String(theme.red11?.val ?? '#B91C1C'), text: dangerText,
border: String(theme.red6?.val ?? '#FCA5A5'), border: withAlpha(dangerText, 0.35),
}, },
muted: { muted: {
bg: String(theme.gray3?.val ?? '#f3f4f6'), bg: surfaceMuted,
text: String(theme.gray11?.val ?? '#374151'), text: muted,
border: String(theme.gray6?.val ?? '#e5e7eb'), border,
}, },
}; };
const colors = palette[tone] ?? palette.muted; const colors = palette[tone] ?? palette.muted;
@@ -210,7 +210,11 @@ export function KpiStrip({
tone?: 'accent' | 'neutral'; tone?: 'accent' | 'neutral';
}> }>
}) { }) {
const { glassSurface, border, textStrong, textMuted, primary, surfaceMuted, surface } = useAdminTheme(); const { glassSurface, glassSurfaceStrong, glassBorder, border, textStrong, textMuted, primary, surfaceMuted, surface } = useAdminTheme();
const cardSurface = surfaceMuted ?? glassSurfaceStrong ?? glassSurface ?? surface;
const cardBorder = glassBorder ?? border;
const separatorColor = withAlpha(textStrong, 0.12);
const innerSeparatorColor = withAlpha(textStrong, 0.08);
return ( return (
<XStack flexWrap="wrap" gap="$2"> <XStack flexWrap="wrap" gap="$2">
@@ -222,10 +226,10 @@ export function KpiStrip({
return ( return (
<Card <Card
key={index} key={index}
backgroundColor={glassSurface ?? surface} backgroundColor={cardSurface}
borderRadius={14} borderRadius={14}
borderWidth={1} borderWidth={1}
borderColor={border} borderColor={cardBorder}
padding="$2.5" padding="$2.5"
flexGrow={1} flexGrow={1}
flexBasis="48%" flexBasis="48%"
@@ -242,7 +246,7 @@ export function KpiStrip({
> >
{item.value} {item.value}
</Text> </Text>
<Separator vertical backgroundColor={border} height={32} opacity={0.6} /> <Separator vertical backgroundColor={separatorColor} height={32} />
<YStack alignItems="center" space="$0.5"> <YStack alignItems="center" space="$0.5">
<XStack <XStack
width={28} width={28}
@@ -264,7 +268,7 @@ export function KpiStrip({
> >
{item.label} {item.label}
</Text> </Text>
<Separator backgroundColor={border} opacity={0.4} width={28} alignSelf="center" /> <Separator backgroundColor={innerSeparatorColor} width={28} alignSelf="center" />
{item.note ? ( {item.note ? (
<Text fontSize={9} fontWeight="800" color={iconColor} opacity={0.9} textTransform="uppercase"> <Text fontSize={9} fontWeight="800" color={iconColor} opacity={0.9} textTransform="uppercase">
{item.note} {item.note}

View File

@@ -127,6 +127,8 @@ export function useAdminTheme() {
border, border,
text, text,
textStrong: text, // Alias textStrong: text, // Alias
textMuted: muted, // Alias for legacy usage
textSubtle: subtle, // Alias for legacy usage
muted, // Now properly derived from text color muted, // Now properly derived from text color
subtle, // Now properly derived from text color subtle, // Now properly derived from text color
primary, primary,
@@ -134,12 +136,12 @@ export function useAdminTheme() {
accentSoft: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft), accentSoft: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft),
accentStrong: String(theme.blue11?.val ?? ADMIN_COLORS.primaryStrong), accentStrong: String(theme.blue11?.val ?? ADMIN_COLORS.primaryStrong),
successBg: String(theme.backgroundStrong?.val ?? '#DCFCE7'), successBg: String(theme.backgroundStrong?.val ?? '#DCFCE7'),
successText: String(theme.green10?.val ?? '#166534'), successText: String(theme.green10?.val ?? ADMIN_COLORS.success),
dangerBg: String(theme.red3?.val ?? '#FEE2E2'), dangerBg: String(theme.red3?.val ?? '#FEE2E2'),
dangerText: String(theme.red11?.val ?? ADMIN_COLORS.danger), dangerText: String(theme.red11?.val ?? ADMIN_COLORS.danger),
warningBg: String(theme.yellow3?.val ?? '#FEF3C7'), warningBg: String(theme.yellow3?.val ?? '#FEF3C7'),
warningBorder: String(theme.yellow6?.val ?? '#FCD34D'), warningBorder: String(theme.yellow6?.val ?? '#FCD34D'),
warningText: String(theme.yellow11?.val ?? '#B45309'), warningText: String(theme.yellow11?.val ?? ADMIN_COLORS.warning),
infoBg: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft), infoBg: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft),
infoText: String(theme.blue10?.val ?? ADMIN_COLORS.primaryStrong), infoText: String(theme.blue10?.val ?? ADMIN_COLORS.primaryStrong),
danger: String(theme.danger?.val ?? ADMIN_COLORS.danger), danger: String(theme.danger?.val ?? ADMIN_COLORS.danger),