feat(provider-website/PromoBanner): make the promo bar theme-driven
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) <noreply@anthropic.com>
This commit is contained in:
parent
03d86597c0
commit
d077629876
1 changed files with 44 additions and 23 deletions
|
|
@ -28,17 +28,28 @@ const PLATFORM_GLYPH: Record<string, string> = {
|
|||
other: '🔗',
|
||||
};
|
||||
|
||||
const STYLE_GRADIENT: Record<string, string> = {
|
||||
'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<string, AccentKey> = {
|
||||
'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 (
|
||||
<Bar role="complementary" aria-label="Promotion" style={{ background: bannerBackground(banner) }}>
|
||||
<Bar role="complementary" aria-label="Promotion" $accent={bannerAccent(banner)}>
|
||||
<Inner>
|
||||
{banner.creativeKind === 'image' && banner.media ? (
|
||||
<Thumb>
|
||||
|
|
@ -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};
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue