From 3e0d1539c3c7fce77635458463c395f2e1cbcbea Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 08:22:48 -0700 Subject: [PATCH 01/11] fix(ramps): dynamic provider background color for checkout BottomSheet (TMCU-1087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the hardcoded Transak-specific colors with a provider lookup so the checkout BottomSheet surface always matches the embedded iframe, regardless of which provider is shown. Adds app/components/UI/Ramp/constants/providerWebviewColors.ts — a single source of truth mapping provider IDs to their iframe background colors for dark and light mode. Unknown providers fall back to the design-system surface. Applies to both the native unified flow (twClassName on the MMDS BottomSheet) and the legacy aggregator flow (style prop on the component-library BottomSheet). The WebView backgroundColor is also set via the same lookup to prevent a content-load flash. Transak is the only provider with known colors so far. TODO entries in the constants file mark the remaining ~6 providers pending visual verification. --- .../Views/Checkout/Checkout.styles.ts | 6 ++- .../Aggregator/Views/Checkout/Checkout.tsx | 19 +++++++- .../UI/Ramp/Views/Checkout/Checkout.styles.ts | 6 ++- .../UI/Ramp/Views/Checkout/Checkout.tsx | 19 +++++++- .../Ramp/constants/providerWebviewColors.ts | 43 +++++++++++++++++++ 5 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 app/components/UI/Ramp/constants/providerWebviewColors.ts diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts index df5fea793168..cd063bc929df 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts @@ -1,13 +1,15 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../../util/theme/models'; -const styleSheet = (params: { theme: Theme }) => +const styleSheet = (params: { theme: Theme; providerBg?: string }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, webview: { - backgroundColor: params.theme.colors.background.default, + // providerBg matches the provider iframe — prevents a flash before content loads. + // Falls back to undefined (transparent) if the provider color is unknown. + backgroundColor: params.providerBg, }, }); diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index 1d2253510e8a..b4bc35317810 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -38,6 +38,9 @@ import { } from '../../../../../../component-library/components/Icons/Icon'; import { useStyles } from '../../../../../../component-library/hooks'; import styleSheet from './Checkout.styles'; +import { useTheme } from '../../../../../../util/theme'; +import { AppThemeKey } from '../../../../../../util/theme/models'; +import { getProviderWebviewColors } from '../../../constants/providerWebviewColors'; import Device from '../../../../../../util/device'; import { shouldStartLoadWithRequest } from '../../../../../../util/browser'; import { CHECKOUT_TEST_IDS } from './Checkout.testIds'; @@ -66,10 +69,21 @@ const CheckoutWebView = () => { const params = useParams(); const handleSuccessfulOrder = useHandleSuccessfulOrder(); - const { styles } = useStyles(styleSheet, {}); - const { url: uri, customOrderId, provider } = params; + const { themeAppearance } = useTheme(); + const providerColors = getProviderWebviewColors(provider?.id); + const isDark = themeAppearance === AppThemeKey.dark; + const providerBg = providerColors + ? isDark + ? providerColors.dark + : providerColors.light + : undefined; + const { styles } = useStyles(styleSheet, { providerBg }); + const providerBgStyle = providerBg + ? { backgroundColor: providerBg } + : undefined; + const handleCancelPress = useCallback(() => { const chainId = selectedAsset?.network?.chainId || ''; if (isBuy) { @@ -252,6 +266,7 @@ const CheckoutWebView = () => { isFullscreen isInteractable={!Device.isAndroid()} keyboardAvoidingViewEnabled={false} + style={providerBgStyle} > +const styleSheet = (params: { theme: Theme; providerBg?: string }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, webview: { - backgroundColor: params.theme.colors.background.default, + // providerBg matches the provider iframe — prevents a flash before content loads. + // Falls back to undefined (transparent) if the provider color is unknown. + backgroundColor: params.providerBg, }, }); diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 36112af360fb..1e8244c5da5b 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -44,6 +44,9 @@ import { } from '../../headless/headlessEntryNavigation'; import { useStyles } from '../../../../hooks/useStyles'; import styleSheet from './Checkout.styles'; +import { useTheme } from '../../../../../util/theme'; +import { AppThemeKey } from '../../../../../util/theme/models'; +import { getProviderWebviewColors } from '../../constants/providerWebviewColors'; import Device from '../../../../../util/device'; import { shouldStartLoadWithRequest } from '../../../../../util/browser'; import { CHECKOUT_TEST_IDS } from './Checkout.testIds'; @@ -98,7 +101,7 @@ const Checkout = () => { const [key, setKey] = useState(0); const navigation = useNavigation(); const params = useParams(); - const { styles } = useStyles(styleSheet, {}); + const { themeAppearance } = useTheme(); const { addOrder, addPrecreatedOrder, getOrderFromCallback } = useRampsOrders(); const { trackEvent, createEventBuilder } = useAnalytics(); @@ -116,6 +119,19 @@ const Checkout = () => { cryptocurrency, headlessSessionId, } = params ?? {}; + + const providerColors = getProviderWebviewColors(providerCode); + const isDark = themeAppearance === AppThemeKey.dark; + const providerBg = providerColors + ? isDark + ? providerColors.dark + : providerColors.light + : undefined; + const { styles } = useStyles(styleSheet, { providerBg }); + // Tailwind arbitrary-value class for the MMDS BottomSheet — only set when + // we know the provider's color so unknown providers keep the design-system default. + const providerBgTwClassName = providerBg ? `bg-[${providerBg}]` : undefined; + const effectiveOrderId = (orderIdParam ?? customOrderId)?.trim() || null; // Headless deposit (TRAM-3623): when a headless session drives this Checkout, @@ -695,6 +711,7 @@ const Checkout = () => { isFullscreen isInteractable={!Device.isAndroid()} keyboardAvoidingViewEnabled={false} + twClassName={providerBgTwClassName} > {sharedHeader} = { + transak: { dark: '#1a1a1a', light: '#ffffff' }, + // moonpay: { dark: '???', light: '???' }, + // banxa: { dark: '???', light: '???' }, + // sardine: { dark: '???', light: '???' }, + // stripe: { dark: '???', light: '???' }, + // paypal: { dark: '???', light: '???' }, +}; + +/** + * Returns the provider's iframe background colors for the given provider ID, + * or undefined if unknown (caller should fall back to the design-system surface). + */ +export function getProviderWebviewColors( + providerCode: string | undefined, +): ProviderWebviewColors | undefined { + if (!providerCode) return undefined; + const lower = providerCode.toLowerCase(); + const key = Object.keys(PROVIDER_WEBVIEW_COLORS).find((k) => + lower.includes(k), + ); + return key ? PROVIDER_WEBVIEW_COLORS[key] : undefined; +} From 9ad1c71bcd7540168fc4d4a24437944fe5a6b8de Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 11:33:38 -0700 Subject: [PATCH 02/11] fix(ramps): move provider webview colors to common.ts and add MoonPay (TMCU-1087) Moves the dark-mode hex values to app/styles/common.ts (the established home for custom HEX constants) and imports brandColor.white from @metamask/design-tokens for all light-mode entries, reducing standalone hex literals. Adds MoonPay checkout dark color (#131416). Banxa and remaining providers are still marked as TODOs pending visual verification. --- .../UI/Ramp/constants/providerWebviewColors.ts | 9 +++++---- app/styles/common.ts | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/components/UI/Ramp/constants/providerWebviewColors.ts b/app/components/UI/Ramp/constants/providerWebviewColors.ts index ec8e844fada2..0c27fb30dc23 100644 --- a/app/components/UI/Ramp/constants/providerWebviewColors.ts +++ b/app/components/UI/Ramp/constants/providerWebviewColors.ts @@ -1,4 +1,5 @@ -/* eslint-disable @metamask/design-tokens/color-no-hex */ +import { brandColor } from '@metamask/design-tokens'; +import { colors } from '../../../../styles/common'; /** * Background colors used by each provider's iframe/webview in dark and light mode. @@ -9,7 +10,7 @@ * Keys are lowercase substrings of the provider ID returned by the ramps controller * (e.g. "transak" matches both "transak" and "transak-native"). * - * Use undefined colors to fall back to the design-system default surface. + * Providers not listed here fall back to the design-system default surface. * TODO: verify dark/light values visually in the simulator for each provider below. */ @@ -19,8 +20,8 @@ export interface ProviderWebviewColors { } const PROVIDER_WEBVIEW_COLORS: Record = { - transak: { dark: '#1a1a1a', light: '#ffffff' }, - // moonpay: { dark: '???', light: '???' }, + transak: { dark: colors.transakCheckoutDark, light: brandColor.white }, + moonpay: { dark: colors.moonpayCheckoutDark, light: brandColor.white }, // banxa: { dark: '???', light: '???' }, // sardine: { dark: '???', light: '???' }, // stripe: { dark: '???', light: '???' }, diff --git a/app/styles/common.ts b/app/styles/common.ts index 13de042dbbea..127f71c852f5 100644 --- a/app/styles/common.ts +++ b/app/styles/common.ts @@ -27,6 +27,10 @@ export const colors = { gettingStartedPageBackgroundColor: '#EAC2FF', gettingStartedTextColor: '#3D065F', gettingStartedPageBackgroundColorLightMode: '#FFF2EB', + // Provider iframe backgrounds — matched in the checkout BottomSheet so the + // native chrome feels seamless with the embedded webview. + transakCheckoutDark: '#1a1a1a', + moonpayCheckoutDark: '#131416', }; export const onboardingCarouselColors: Record< From 636d24d91e847cb32f18efb78e933a38dc4468bc Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 11:36:48 -0700 Subject: [PATCH 03/11] fix(ramps): add Banxa checkout webview colors (TMCU-1087) --- app/components/UI/Ramp/constants/providerWebviewColors.ts | 2 +- app/styles/common.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/components/UI/Ramp/constants/providerWebviewColors.ts b/app/components/UI/Ramp/constants/providerWebviewColors.ts index 0c27fb30dc23..d5204baa8a12 100644 --- a/app/components/UI/Ramp/constants/providerWebviewColors.ts +++ b/app/components/UI/Ramp/constants/providerWebviewColors.ts @@ -22,7 +22,7 @@ export interface ProviderWebviewColors { const PROVIDER_WEBVIEW_COLORS: Record = { transak: { dark: colors.transakCheckoutDark, light: brandColor.white }, moonpay: { dark: colors.moonpayCheckoutDark, light: brandColor.white }, - // banxa: { dark: '???', light: '???' }, + banxa: { dark: colors.banxaCheckoutDark, light: brandColor.white }, // sardine: { dark: '???', light: '???' }, // stripe: { dark: '???', light: '???' }, // paypal: { dark: '???', light: '???' }, diff --git a/app/styles/common.ts b/app/styles/common.ts index 127f71c852f5..5d2a3ac0d19f 100644 --- a/app/styles/common.ts +++ b/app/styles/common.ts @@ -31,6 +31,7 @@ export const colors = { // native chrome feels seamless with the embedded webview. transakCheckoutDark: '#1a1a1a', moonpayCheckoutDark: '#131416', + banxaCheckoutDark: '#0D0D0F', }; export const onboardingCarouselColors: Record< From 81b255e773b89cd6a0c81b8a53fb8fd2df7767bb Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 11:39:23 -0700 Subject: [PATCH 04/11] fix(ramps): clean up provider color TODOs and align code comments (TMCU-1087) --- app/components/UI/Ramp/constants/providerWebviewColors.ts | 8 ++++---- app/styles/common.ts | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/components/UI/Ramp/constants/providerWebviewColors.ts b/app/components/UI/Ramp/constants/providerWebviewColors.ts index d5204baa8a12..2cfdcf70ba40 100644 --- a/app/components/UI/Ramp/constants/providerWebviewColors.ts +++ b/app/components/UI/Ramp/constants/providerWebviewColors.ts @@ -11,7 +11,6 @@ import { colors } from '../../../../styles/common'; * (e.g. "transak" matches both "transak" and "transak-native"). * * Providers not listed here fall back to the design-system default surface. - * TODO: verify dark/light values visually in the simulator for each provider below. */ export interface ProviderWebviewColors { @@ -19,13 +18,14 @@ export interface ProviderWebviewColors { light: string; } +// Intentionally overrides the design-system BottomSheet background to match +// each provider's iframe, which we cannot style. Keeps the native chrome +// seamless with the embedded checkout flow. See colors.*CheckoutDark in +// app/styles/common.ts. Update only if a provider changes their iframe theme. const PROVIDER_WEBVIEW_COLORS: Record = { transak: { dark: colors.transakCheckoutDark, light: brandColor.white }, moonpay: { dark: colors.moonpayCheckoutDark, light: brandColor.white }, banxa: { dark: colors.banxaCheckoutDark, light: brandColor.white }, - // sardine: { dark: '???', light: '???' }, - // stripe: { dark: '???', light: '???' }, - // paypal: { dark: '???', light: '???' }, }; /** diff --git a/app/styles/common.ts b/app/styles/common.ts index 5d2a3ac0d19f..a6b7035547b9 100644 --- a/app/styles/common.ts +++ b/app/styles/common.ts @@ -27,8 +27,10 @@ export const colors = { gettingStartedPageBackgroundColor: '#EAC2FF', gettingStartedTextColor: '#3D065F', gettingStartedPageBackgroundColorLightMode: '#FFF2EB', - // Provider iframe backgrounds — matched in the checkout BottomSheet so the - // native chrome feels seamless with the embedded webview. + // Provider iframe backgrounds — these colors are set by each provider and + // outside our control. We match them in the checkout BottomSheet so the + // native chrome feels seamless with the embedded webview. Update only if + // a provider changes their iframe theme colors. transakCheckoutDark: '#1a1a1a', moonpayCheckoutDark: '#131416', banxaCheckoutDark: '#0D0D0F', From 56706758979b944460cf0dc6e5710af5a2634101 Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 12:06:16 -0700 Subject: [PATCH 05/11] fix(ramps): remove WebView background style, let BottomSheet own the surface --- .../UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts | 7 +------ .../UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx | 3 +-- app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts | 7 +------ app/components/UI/Ramp/Views/Checkout/Checkout.tsx | 3 +-- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts index cd063bc929df..cd6bc1c96a98 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts @@ -1,16 +1,11 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../../util/theme/models'; -const styleSheet = (params: { theme: Theme; providerBg?: string }) => +const styleSheet = (_params: { theme: Theme }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, - webview: { - // providerBg matches the provider iframe — prevents a flash before content loads. - // Falls back to undefined (transparent) if the provider color is unknown. - backgroundColor: params.providerBg, - }, }); export default styleSheet; diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index b4bc35317810..bb566c4afb82 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -79,7 +79,7 @@ const CheckoutWebView = () => { ? providerColors.dark : providerColors.light : undefined; - const { styles } = useStyles(styleSheet, { providerBg }); + const { styles } = useStyles(styleSheet, {}); const providerBgStyle = providerBg ? { backgroundColor: providerBg } : undefined; @@ -282,7 +282,6 @@ const CheckoutWebView = () => { /> { const { nativeEvent } = syntheticEvent; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts index 26df87500d78..bc65312aa590 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts @@ -1,16 +1,11 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../util/theme/models'; -const styleSheet = (params: { theme: Theme; providerBg?: string }) => +const styleSheet = (_params: { theme: Theme }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, - webview: { - // providerBg matches the provider iframe — prevents a flash before content loads. - // Falls back to undefined (transparent) if the provider color is unknown. - backgroundColor: params.providerBg, - }, }); export default styleSheet; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 1e8244c5da5b..eadee79a58ad 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -127,7 +127,7 @@ const Checkout = () => { ? providerColors.dark : providerColors.light : undefined; - const { styles } = useStyles(styleSheet, { providerBg }); + const { styles } = useStyles(styleSheet, {}); // Tailwind arbitrary-value class for the MMDS BottomSheet — only set when // we know the provider's color so unknown providers keep the design-system default. const providerBgTwClassName = providerBg ? `bg-[${providerBg}]` : undefined; @@ -716,7 +716,6 @@ const Checkout = () => { {sharedHeader} { From cfaeced8ab82ab8f307be6e8a46cafda88398c0c Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 12:07:50 -0700 Subject: [PATCH 06/11] fix(ramps): restore WebView background style to suppress load flash --- .../UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts | 8 +++++++- .../UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx | 3 ++- app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts | 8 +++++++- app/components/UI/Ramp/Views/Checkout/Checkout.tsx | 3 ++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts index cd6bc1c96a98..33c91748071a 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts @@ -1,11 +1,17 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../../util/theme/models'; -const styleSheet = (_params: { theme: Theme }) => +const styleSheet = (params: { theme: Theme; providerBg?: string }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, + webview: { + // Matches the provider iframe background to suppress the native white + // flash before content loads. Falls back to undefined (transparent) for + // unknown providers. + backgroundColor: params.providerBg, + }, }); export default styleSheet; diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index bb566c4afb82..b4bc35317810 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -79,7 +79,7 @@ const CheckoutWebView = () => { ? providerColors.dark : providerColors.light : undefined; - const { styles } = useStyles(styleSheet, {}); + const { styles } = useStyles(styleSheet, { providerBg }); const providerBgStyle = providerBg ? { backgroundColor: providerBg } : undefined; @@ -282,6 +282,7 @@ const CheckoutWebView = () => { /> { const { nativeEvent } = syntheticEvent; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts index bc65312aa590..d9fd0fd81329 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts @@ -1,11 +1,17 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../util/theme/models'; -const styleSheet = (_params: { theme: Theme }) => +const styleSheet = (params: { theme: Theme; providerBg?: string }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, + webview: { + // Matches the provider iframe background to suppress the native white + // flash before content loads. Falls back to undefined (transparent) for + // unknown providers. + backgroundColor: params.providerBg, + }, }); export default styleSheet; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index eadee79a58ad..1e8244c5da5b 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -127,7 +127,7 @@ const Checkout = () => { ? providerColors.dark : providerColors.light : undefined; - const { styles } = useStyles(styleSheet, {}); + const { styles } = useStyles(styleSheet, { providerBg }); // Tailwind arbitrary-value class for the MMDS BottomSheet — only set when // we know the provider's color so unknown providers keep the design-system default. const providerBgTwClassName = providerBg ? `bg-[${providerBg}]` : undefined; @@ -716,6 +716,7 @@ const Checkout = () => { {sharedHeader} { From 18d6da0535aa3408311a7eccab26f0768283b62e Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 12:14:35 -0700 Subject: [PATCH 07/11] fix(ramps): use nullish coalescing for WebView background fallback to background.alternative (TMCU-1087) --- .../UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts | 7 ++++--- .../UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx | 4 +++- app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts | 7 ++++--- app/components/UI/Ramp/Views/Checkout/Checkout.tsx | 8 +++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts index 33c91748071a..2d78b3b0c80d 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts @@ -8,9 +8,10 @@ const styleSheet = (params: { theme: Theme; providerBg?: string }) => }, webview: { // Matches the provider iframe background to suppress the native white - // flash before content loads. Falls back to undefined (transparent) for - // unknown providers. - backgroundColor: params.providerBg, + // flash before content loads. Falls back to background.alternative + // (the BottomSheet default) for unknown providers. + backgroundColor: + params.providerBg ?? params.theme.colors.background.alternative, }, }); diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index b4bc35317810..4386cd9afbf0 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -80,7 +80,9 @@ const CheckoutWebView = () => { : providerColors.light : undefined; const { styles } = useStyles(styleSheet, { providerBg }); - const providerBgStyle = providerBg + // Only override the BottomSheet for known providers — background.alternative + // is already the component-library BottomSheet default, so no style needed for unknowns. + const providerBgStyle = providerColors ? { backgroundColor: providerBg } : undefined; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts index d9fd0fd81329..23fc9b62add3 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts @@ -8,9 +8,10 @@ const styleSheet = (params: { theme: Theme; providerBg?: string }) => }, webview: { // Matches the provider iframe background to suppress the native white - // flash before content loads. Falls back to undefined (transparent) for - // unknown providers. - backgroundColor: params.providerBg, + // flash before content loads. Falls back to background.alternative + // (the BottomSheet default) for unknown providers. + backgroundColor: + params.providerBg ?? params.theme.colors.background.alternative, }, }); diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 1e8244c5da5b..7129d47a4ae0 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -128,9 +128,11 @@ const Checkout = () => { : providerColors.light : undefined; const { styles } = useStyles(styleSheet, { providerBg }); - // Tailwind arbitrary-value class for the MMDS BottomSheet — only set when - // we know the provider's color so unknown providers keep the design-system default. - const providerBgTwClassName = providerBg ? `bg-[${providerBg}]` : undefined; + // Only override the BottomSheet for known providers — background.alternative + // is already the MMDS BottomSheet default, so no twClassName needed for unknowns. + const providerBgTwClassName = providerColors + ? `bg-[${providerBg}]` + : undefined; const effectiveOrderId = (orderIdParam ?? customOrderId)?.trim() || null; From a605387e38d9b516b349d7a946d117e436091083 Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 12:26:36 -0700 Subject: [PATCH 08/11] refactor(ramps): move getProviderWebviewColors to utils (TMCU-1087) --- app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx | 2 +- app/components/UI/Ramp/Views/Checkout/Checkout.tsx | 2 +- .../getProviderWebviewColors.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename app/components/UI/Ramp/{constants/providerWebviewColors.ts => utils/getProviderWebviewColors.ts} (100%) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index 4386cd9afbf0..2b79b8d5ab6c 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -40,7 +40,7 @@ import { useStyles } from '../../../../../../component-library/hooks'; import styleSheet from './Checkout.styles'; import { useTheme } from '../../../../../../util/theme'; import { AppThemeKey } from '../../../../../../util/theme/models'; -import { getProviderWebviewColors } from '../../../constants/providerWebviewColors'; +import { getProviderWebviewColors } from '../../../utils/getProviderWebviewColors'; import Device from '../../../../../../util/device'; import { shouldStartLoadWithRequest } from '../../../../../../util/browser'; import { CHECKOUT_TEST_IDS } from './Checkout.testIds'; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 7129d47a4ae0..08843ade32fb 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -46,7 +46,7 @@ import { useStyles } from '../../../../hooks/useStyles'; import styleSheet from './Checkout.styles'; import { useTheme } from '../../../../../util/theme'; import { AppThemeKey } from '../../../../../util/theme/models'; -import { getProviderWebviewColors } from '../../constants/providerWebviewColors'; +import { getProviderWebviewColors } from '../../utils/getProviderWebviewColors'; import Device from '../../../../../util/device'; import { shouldStartLoadWithRequest } from '../../../../../util/browser'; import { CHECKOUT_TEST_IDS } from './Checkout.testIds'; diff --git a/app/components/UI/Ramp/constants/providerWebviewColors.ts b/app/components/UI/Ramp/utils/getProviderWebviewColors.ts similarity index 100% rename from app/components/UI/Ramp/constants/providerWebviewColors.ts rename to app/components/UI/Ramp/utils/getProviderWebviewColors.ts From acff4c1fa85b89f3cb8f6ba6df84db69ad34c297 Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 14:02:11 -0700 Subject: [PATCH 09/11] fix(ramps): always resolve WebView background via getProviderWebviewColors with default fallback (TMCU-1087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adds a 'default' entry to PROVIDER_WEBVIEW_COLORS mirroring the MMDS BottomSheet surface (background.alternative dark / background.default light), so the function never returns undefined and the WebView always has a matching background. - Fixes stylesheet params to use params.vars.providerBg — useStyles wraps extra params in a vars object, so params.providerBg was always undefined before, meaning the WebView backgroundColor was never actually being applied. --- .../Views/Checkout/Checkout.styles.ts | 9 +++---- .../Aggregator/Views/Checkout/Checkout.tsx | 15 +++-------- .../UI/Ramp/Views/Checkout/Checkout.styles.ts | 9 +++---- .../UI/Ramp/Views/Checkout/Checkout.tsx | 15 +++-------- .../UI/Ramp/utils/getProviderWebviewColors.ts | 25 ++++++++++++------- 5 files changed, 32 insertions(+), 41 deletions(-) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts index 2d78b3b0c80d..fd6bf4dd51d6 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.styles.ts @@ -1,17 +1,16 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../../util/theme/models'; -const styleSheet = (params: { theme: Theme; providerBg?: string }) => +const styleSheet = (params: { theme: Theme; vars: { providerBg: string } }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, webview: { // Matches the provider iframe background to suppress the native white - // flash before content loads. Falls back to background.alternative - // (the BottomSheet default) for unknown providers. - backgroundColor: - params.providerBg ?? params.theme.colors.background.alternative, + // flash before content loads. Unknown providers receive the BottomSheet + // default surface color from getProviderWebviewColors. + backgroundColor: params.vars.providerBg, }, }); diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index 2b79b8d5ab6c..86848218aebb 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -72,19 +72,12 @@ const CheckoutWebView = () => { const { url: uri, customOrderId, provider } = params; const { themeAppearance } = useTheme(); - const providerColors = getProviderWebviewColors(provider?.id); + const { dark: providerBgDark, light: providerBgLight } = + getProviderWebviewColors(provider?.id); const isDark = themeAppearance === AppThemeKey.dark; - const providerBg = providerColors - ? isDark - ? providerColors.dark - : providerColors.light - : undefined; + const providerBg = isDark ? providerBgDark : providerBgLight; const { styles } = useStyles(styleSheet, { providerBg }); - // Only override the BottomSheet for known providers — background.alternative - // is already the component-library BottomSheet default, so no style needed for unknowns. - const providerBgStyle = providerColors - ? { backgroundColor: providerBg } - : undefined; + const providerBgStyle = { backgroundColor: providerBg }; const handleCancelPress = useCallback(() => { const chainId = selectedAsset?.network?.chainId || ''; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts index 23fc9b62add3..6aa7f8b3906f 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.styles.ts @@ -1,17 +1,16 @@ import { StyleSheet } from 'react-native'; import { Theme } from '../../../../../util/theme/models'; -const styleSheet = (params: { theme: Theme; providerBg?: string }) => +const styleSheet = (params: { theme: Theme; vars: { providerBg: string } }) => StyleSheet.create({ headerWithoutPadding: { paddingVertical: 0, }, webview: { // Matches the provider iframe background to suppress the native white - // flash before content loads. Falls back to background.alternative - // (the BottomSheet default) for unknown providers. - backgroundColor: - params.providerBg ?? params.theme.colors.background.alternative, + // flash before content loads. Unknown providers receive the BottomSheet + // default surface color from getProviderWebviewColors. + backgroundColor: params.vars.providerBg, }, }); diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 08843ade32fb..3c98bfcf54af 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -120,19 +120,12 @@ const Checkout = () => { headlessSessionId, } = params ?? {}; - const providerColors = getProviderWebviewColors(providerCode); + const { dark: providerBgDark, light: providerBgLight } = + getProviderWebviewColors(providerCode); const isDark = themeAppearance === AppThemeKey.dark; - const providerBg = providerColors - ? isDark - ? providerColors.dark - : providerColors.light - : undefined; + const providerBg = isDark ? providerBgDark : providerBgLight; const { styles } = useStyles(styleSheet, { providerBg }); - // Only override the BottomSheet for known providers — background.alternative - // is already the MMDS BottomSheet default, so no twClassName needed for unknowns. - const providerBgTwClassName = providerColors - ? `bg-[${providerBg}]` - : undefined; + const providerBgTwClassName = `bg-[${providerBg}]`; const effectiveOrderId = (orderIdParam ?? customOrderId)?.trim() || null; diff --git a/app/components/UI/Ramp/utils/getProviderWebviewColors.ts b/app/components/UI/Ramp/utils/getProviderWebviewColors.ts index 2cfdcf70ba40..2dc674ac6a68 100644 --- a/app/components/UI/Ramp/utils/getProviderWebviewColors.ts +++ b/app/components/UI/Ramp/utils/getProviderWebviewColors.ts @@ -1,4 +1,4 @@ -import { brandColor } from '@metamask/design-tokens'; +import { brandColor, darkTheme, lightTheme } from '@metamask/design-tokens'; import { colors } from '../../../../styles/common'; /** @@ -10,7 +10,8 @@ import { colors } from '../../../../styles/common'; * Keys are lowercase substrings of the provider ID returned by the ramps controller * (e.g. "transak" matches both "transak" and "transak-native"). * - * Providers not listed here fall back to the design-system default surface. + * The "default" entry mirrors the MMDS BottomSheet surface colors and is used + * when no provider-specific color is known. */ export interface ProviderWebviewColors { @@ -23,22 +24,28 @@ export interface ProviderWebviewColors { // seamless with the embedded checkout flow. See colors.*CheckoutDark in // app/styles/common.ts. Update only if a provider changes their iframe theme. const PROVIDER_WEBVIEW_COLORS: Record = { + // Defaults to MMDS BottomSheet surface colors when no provider-specific + // color is known, ensuring the WebView background always matches the sheet. + default: { + dark: darkTheme.colors.background.alternative, + light: lightTheme.colors.background.default, + }, transak: { dark: colors.transakCheckoutDark, light: brandColor.white }, moonpay: { dark: colors.moonpayCheckoutDark, light: brandColor.white }, banxa: { dark: colors.banxaCheckoutDark, light: brandColor.white }, }; /** - * Returns the provider's iframe background colors for the given provider ID, - * or undefined if unknown (caller should fall back to the design-system surface). + * Returns the provider's iframe background colors for the given provider ID. + * Falls back to the MMDS BottomSheet default surface for unknown providers. */ export function getProviderWebviewColors( providerCode: string | undefined, -): ProviderWebviewColors | undefined { - if (!providerCode) return undefined; +): ProviderWebviewColors { + if (!providerCode) return PROVIDER_WEBVIEW_COLORS.default; const lower = providerCode.toLowerCase(); - const key = Object.keys(PROVIDER_WEBVIEW_COLORS).find((k) => - lower.includes(k), + const key = Object.keys(PROVIDER_WEBVIEW_COLORS).find( + (k) => k !== 'default' && lower.includes(k), ); - return key ? PROVIDER_WEBVIEW_COLORS[key] : undefined; + return PROVIDER_WEBVIEW_COLORS[key ?? 'default']; } From e8df53f936d6451aa0deb0979137fb8a48bf3693 Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 14:16:27 -0700 Subject: [PATCH 10/11] refactor(ramps): simplify getProviderWebviewColors to return resolved string (TMCU-1087) --- .../Aggregator/Views/Checkout/Checkout.tsx | 4 +--- .../UI/Ramp/Views/Checkout/Checkout.tsx | 4 +--- .../UI/Ramp/utils/getProviderWebviewColors.ts | 20 ++++++++++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index 86848218aebb..6be7bf1ad5ae 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -72,10 +72,8 @@ const CheckoutWebView = () => { const { url: uri, customOrderId, provider } = params; const { themeAppearance } = useTheme(); - const { dark: providerBgDark, light: providerBgLight } = - getProviderWebviewColors(provider?.id); const isDark = themeAppearance === AppThemeKey.dark; - const providerBg = isDark ? providerBgDark : providerBgLight; + const providerBg = getProviderWebviewColors(provider?.id, isDark); const { styles } = useStyles(styleSheet, { providerBg }); const providerBgStyle = { backgroundColor: providerBg }; diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 3c98bfcf54af..32cb58f88b2c 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -120,10 +120,8 @@ const Checkout = () => { headlessSessionId, } = params ?? {}; - const { dark: providerBgDark, light: providerBgLight } = - getProviderWebviewColors(providerCode); const isDark = themeAppearance === AppThemeKey.dark; - const providerBg = isDark ? providerBgDark : providerBgLight; + const providerBg = getProviderWebviewColors(providerCode, isDark); const { styles } = useStyles(styleSheet, { providerBg }); const providerBgTwClassName = `bg-[${providerBg}]`; diff --git a/app/components/UI/Ramp/utils/getProviderWebviewColors.ts b/app/components/UI/Ramp/utils/getProviderWebviewColors.ts index 2dc674ac6a68..35f6fcea1a7e 100644 --- a/app/components/UI/Ramp/utils/getProviderWebviewColors.ts +++ b/app/components/UI/Ramp/utils/getProviderWebviewColors.ts @@ -14,7 +14,7 @@ import { colors } from '../../../../styles/common'; * when no provider-specific color is known. */ -export interface ProviderWebviewColors { +interface ProviderWebviewColors { dark: string; light: string; } @@ -36,16 +36,18 @@ const PROVIDER_WEBVIEW_COLORS: Record = { }; /** - * Returns the provider's iframe background colors for the given provider ID. + * Returns the provider's iframe background color for the current theme. * Falls back to the MMDS BottomSheet default surface for unknown providers. */ export function getProviderWebviewColors( providerCode: string | undefined, -): ProviderWebviewColors { - if (!providerCode) return PROVIDER_WEBVIEW_COLORS.default; - const lower = providerCode.toLowerCase(); - const key = Object.keys(PROVIDER_WEBVIEW_COLORS).find( - (k) => k !== 'default' && lower.includes(k), - ); - return PROVIDER_WEBVIEW_COLORS[key ?? 'default']; + isDark: boolean, +): string { + const key = + providerCode && + Object.keys(PROVIDER_WEBVIEW_COLORS).find( + (k) => k !== 'default' && providerCode.toLowerCase().includes(k), + ); + const entry = PROVIDER_WEBVIEW_COLORS[key || 'default']; + return isDark ? entry.dark : entry.light; } From 3925babcebd543730722f872012d33228ac0255b Mon Sep 17 00:00:00 2001 From: georgewrmarshall Date: Thu, 16 Jul 2026 14:19:12 -0700 Subject: [PATCH 11/11] docs(ramps): add code comments explaining provider background color logic (TMCU-1087) --- app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx | 4 ++++ app/components/UI/Ramp/Views/Checkout/Checkout.tsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx index 6be7bf1ad5ae..588ffc0fdf43 100644 --- a/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Aggregator/Views/Checkout/Checkout.tsx @@ -71,6 +71,10 @@ const CheckoutWebView = () => { const { url: uri, customOrderId, provider } = params; + // Resolve the provider's iframe background color for the current theme. + // Applied to both the component-library BottomSheet (via providerBgStyle) and + // the WebView (via styles.webview) so the native chrome matches the embedded + // checkout seamlessly. Unknown providers fall back to the BottomSheet default surface. const { themeAppearance } = useTheme(); const isDark = themeAppearance === AppThemeKey.dark; const providerBg = getProviderWebviewColors(provider?.id, isDark); diff --git a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx index 32cb58f88b2c..156f77118950 100644 --- a/app/components/UI/Ramp/Views/Checkout/Checkout.tsx +++ b/app/components/UI/Ramp/Views/Checkout/Checkout.tsx @@ -120,6 +120,10 @@ const Checkout = () => { headlessSessionId, } = params ?? {}; + // Resolve the provider's iframe background color for the current theme. + // Applied to both the MMDS BottomSheet (via twClassName) and the WebView + // (via styles.webview) so the native chrome matches the embedded checkout + // seamlessly. Unknown providers fall back to the BottomSheet default surface. const isDark = themeAppearance === AppThemeKey.dark; const providerBg = getProviderWebviewColors(providerCode, isDark); const { styles } = useStyles(styleSheet, { providerBg });