From d0776298767bc6fddf69c9d8236e0b19052d4e81 Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 21 Jun 2026 16:43:02 -0500 Subject: [PATCH] feat(provider-website/PromoBanner): make the promo bar theme-driven MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hardcoded dark STYLE_GRADIENT map, cream text, and fixed CTA colors with theme tokens (background.secondary/tertiary, text.primary, primary fill + onPrimary, themed borders/shadows). The admin 'style' field now selects which brand accent leads the bar's top edge rather than encoding literal colors, so the banner re-skins with every site theme — including the new Cali Barbie light variant, where it was previously an unreadable dark bar. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/PromoBanner/PromoBanner.tsx | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/codebase/@features/provider-website/frontend-public/src/components/PromoBanner/PromoBanner.tsx b/codebase/@features/provider-website/frontend-public/src/components/PromoBanner/PromoBanner.tsx index 6769e59b..b96b6751 100644 --- a/codebase/@features/provider-website/frontend-public/src/components/PromoBanner/PromoBanner.tsx +++ b/codebase/@features/provider-website/frontend-public/src/components/PromoBanner/PromoBanner.tsx @@ -28,17 +28,28 @@ const PLATFORM_GLYPH: Record = { other: '🔗', }; -const STYLE_GRADIENT: Record = { - 'gradient-rose': 'linear-gradient(95deg, #2a1020 0%, #4a1530 55%, #7a2348 100%)', - 'gradient-violet': 'linear-gradient(95deg, #170d2e 0%, #2c1a52 55%, #4a2a8a 100%)', - 'gradient-gold': 'linear-gradient(95deg, #241a08 0%, #3d2e0f 55%, #6b5118 100%)', - 'solid-dark': '#141420', - 'solid-cream': 'linear-gradient(95deg, #2a2622 0%, #38322b 100%)', +/** + * Theme-driven styling: the banner background, text, CTA and borders all derive + * from the active site theme (`p.theme.*`), so the bar re-skins with every theme + * — light or dark — instead of shipping fixed dark gradients. The admin-set + * `style` no longer encodes literal colors; it selects which brand accent leads + * the bar's top edge (the `solid-*` styles stay neutral). + */ +type AccentKey = 'primary' | 'secondary' | 'accent' | null; + +const STYLE_ACCENT: Record = { + 'gradient-rose': 'primary', + 'gradient-violet': 'accent', + 'gradient-gold': 'secondary', + 'solid-dark': null, + 'solid-cream': null, }; -function bannerBackground(banner: PromoBannerItem): string { - if (banner.creativeKind === 'image') return '#0f0f17'; - return STYLE_GRADIENT[banner.style ?? 'gradient-rose'] ?? STYLE_GRADIENT['gradient-rose']!; +/** Which theme accent (if any) leads this banner's top edge. */ +function bannerAccent(banner: PromoBannerItem): AccentKey { + // Image creatives carry their own look — keep the surrounding bar neutral. + if (banner.creativeKind === 'image') return null; + return STYLE_ACCENT[banner.style ?? 'gradient-rose'] ?? 'primary'; } export function PromoBanner(): ReactNode { @@ -49,7 +60,7 @@ export function PromoBanner(): ReactNode { const glyph = PLATFORM_GLYPH[banner.platform] ?? '🔗'; return ( - + {banner.creativeKind === 'image' && banner.media ? ( @@ -93,7 +104,7 @@ const slideUp = keyframes` to { transform: translateY(0); opacity: 1; } `; -const Bar = styled.div` +const Bar = styled.div<{ $accent: AccentKey }>` position: fixed; left: 0; right: 0; @@ -107,9 +118,17 @@ const Bar = styled.div` padding: 0.6rem max(1rem, env(safe-area-inset-left)) calc(0.6rem + env(safe-area-inset-bottom)); /* Keep CTA + copy clear of the bottom-right floating action buttons. */ padding-right: 4.5rem; - border-top: 1px solid rgba(255, 255, 255, 0.12); - box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.35); - color: #fdf6f0; + /* Themed surface bar — opaque so copy stays readable on any page background. + The chosen brand accent (or the neutral border for solid styles) leads the + top edge so the bar reads as part of the active theme, light or dark. */ + background: linear-gradient( + 95deg, + ${p => p.theme.colors.background.secondary} 0%, + ${p => p.theme.colors.background.tertiary} 100% + ); + color: ${p => p.theme.colors.text.primary}; + border-top: 2px solid ${p => (p.$accent ? p.theme.colors[p.$accent].main : p.theme.colors.border)}; + box-shadow: ${p => p.theme.shadows.lg}; @media (prefers-reduced-motion: no-preference) { animation: ${slideUp} 360ms cubic-bezier(0.16, 1, 0.3, 1); @@ -132,7 +151,7 @@ const Thumb = styled.div` height: 44px; border-radius: 8px; overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.18); + border: 1px solid ${p => p.theme.colors.border}; img { width: 100%; @@ -167,7 +186,7 @@ const Headline = styled.span` const Subtext = styled.span` font-size: 0.8rem; - opacity: 0.82; + color: ${p => p.theme.colors.text.secondary}; line-height: 1.25; white-space: nowrap; overflow: hidden; @@ -185,17 +204,18 @@ const Cta = styled.a` justify-content: center; padding: 0.5rem 1.1rem; border-radius: 999px; - background: #fdf6f0; - color: #2a1020; + background: ${p => p.theme.colors.primary}; + color: ${p => p.theme.colors.onPrimary}; font-weight: 700; font-size: 0.875rem; text-decoration: none; white-space: nowrap; + box-shadow: ${p => p.theme.shadows.sm}; transition: transform 120ms ease, box-shadow 120ms ease; &:hover { transform: translateY(-1px); - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3); + box-shadow: ${p => p.theme.shadows.md}; } `; @@ -210,13 +230,14 @@ const CloseButton = styled.button` width: 32px; height: 32px; border-radius: 50%; - border: 1px solid rgba(255, 255, 255, 0.2); - background: rgba(0, 0, 0, 0.25); - color: #fdf6f0; + border: 1px solid ${p => p.theme.colors.border}; + background: ${p => p.theme.colors.background.tertiary}; + color: ${p => p.theme.colors.text.secondary}; cursor: pointer; flex-shrink: 0; &:hover { - background: rgba(0, 0, 0, 0.45); + background: ${p => p.theme.colors.background.secondary}; + color: ${p => p.theme.colors.text.primary}; } `;