goobs-frontend
Version:
A comprehensive React-based libary for building modern web applications
694 lines (654 loc) • 20.9 kB
text/typescript
// --------------------------------------------------------------------------
// PRICING TABLE THEME SYSTEM
// --------------------------------------------------------------------------
import React from 'react'
import { TRANSITIONS, SHADOWS } from './shared'
export interface PricingTableTheme {
container: {
background: string
border: string
borderTop: string
borderRadius: string
boxShadow: string
backdropFilter: string
backgroundImage?: string
}
header: {
background: string
borderBottom: string
backgroundImage?: string
}
title: {
color: string
fontSize: string
fontFamily: string
fontWeight: string | number
letterSpacing: string
animation?: string
textShadow?: string
}
price: {
color: string
fontSize: string
fontFamily: string
fontWeight: string | number
letterSpacing: string
textShadow?: string
}
annualPrice: {
color: string
fontSize: string
fontFamily: string
fontWeight: string | number
fontStyle: string
letterSpacing: string
textShadow?: string
}
featureTitle: {
color: string
fontSize: string
fontFamily: string
fontWeight: string | number
letterSpacing: string
textShadow?: string
}
subFeatureTitle: {
color: string
fontSize: string
fontFamily: string
fontWeight: string | number
letterSpacing: string
textShadow?: string
}
buttonSection: {
background: string
borderTop: string
backgroundImage?: string
}
checkIcon: {
color: string
filter?: string
animation?: string
}
glyph: {
color: string
fontSize: string
animation?: string
}
transition: string
}
export interface PricingTableStyles {
// Theme selection
theme?: 'light' | 'dark' | 'sacred'
// Container styling
backgroundColor?: string
borderColor?: string
borderTopColor?: string
borderRadius?: string
borderWidth?: string
borderTopWidth?: string
boxShadow?: string
backdropFilter?: string
backgroundImage?: string
// Header styling
headerBackground?: string
headerBorderBottom?: string
headerBackgroundImage?: string
// Title styling
titleColor?: string
titleFontSize?: string
titleFontFamily?: string
titleFontWeight?: string | number
titleLetterSpacing?: string
titleAnimation?: string
titleTextShadow?: string
// Price styling
priceColor?: string
priceFontSize?: string
priceFontFamily?: string
priceFontWeight?: string | number
priceLetterSpacing?: string
priceTextShadow?: string
// Annual price styling
annualPriceColor?: string
annualPriceFontSize?: string
annualPriceFontFamily?: string
annualPriceFontWeight?: string | number
annualPriceFontStyle?: string
annualPriceLetterSpacing?: string
annualPriceTextShadow?: string
// Feature styling
featureTitleColor?: string
featureTitleFontSize?: string
featureTitleFontFamily?: string
featureTitleFontWeight?: string | number
featureTitleLetterSpacing?: string
featureTitleTextShadow?: string
// Sub-feature styling
subFeatureTitleColor?: string
subFeatureTitleFontSize?: string
subFeatureTitleFontFamily?: string
subFeatureTitleFontWeight?: string | number
subFeatureTitleLetterSpacing?: string
subFeatureTitleTextShadow?: string
// Button section styling
buttonSectionBackground?: string
buttonSectionBorderTop?: string
buttonSectionBackgroundImage?: string
// Icon styling
checkIconColor?: string
checkIconFilter?: string
checkIconAnimation?: string
// Glyph styling
glyphColor?: string
glyphFontSize?: string
glyphAnimation?: string
// Layout and spacing
padding?: string
headerPadding?: string
featuresPadding?: string
buttonPadding?: string
margin?: string
marginTop?: string
marginBottom?: string
marginLeft?: string
marginRight?: string
// Transitions
transitionDuration?: string
transitionEasing?: string
// States
disabled?: boolean
outline?: boolean
// Dimensions
width?: string
maxWidth?: string
minWidth?: string
height?: string
maxHeight?: string
minHeight?: string
}
export const pricingTableThemes: Record<
'light' | 'dark' | 'sacred',
PricingTableTheme
> = {
light: {
container: {
background: 'rgba(255, 255, 255, 0.95)',
border: '1px solid rgba(226, 232, 240, 0.8)',
borderTop: '12px solid #00B8D4',
borderRadius: '0.375rem',
boxShadow: SHADOWS.light.medium,
backdropFilter: 'blur(8px)',
},
header: {
background: 'rgba(248, 250, 252, 0.8)',
borderBottom: '1px solid rgba(226, 232, 240, 0.8)',
},
title: {
color: 'rgb(31, 41, 55)',
fontSize: '1.125rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '600',
letterSpacing: '-0.025em',
},
price: {
color: 'rgb(31, 41, 55)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '500',
letterSpacing: '0',
},
annualPrice: {
color: 'rgb(107, 114, 128)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '400',
fontStyle: 'italic',
letterSpacing: '0',
},
featureTitle: {
color: 'rgb(31, 41, 55)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '500',
letterSpacing: '0',
},
subFeatureTitle: {
color: 'rgb(55, 65, 81)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '400',
letterSpacing: '0',
},
buttonSection: {
background: 'rgba(248, 250, 252, 0.5)',
borderTop: '1px solid rgba(226, 232, 240, 0.8)',
},
checkIcon: {
color: 'rgb(34, 197, 94)',
filter: 'drop-shadow(0 1px 2px rgba(34, 197, 94, 0.3))',
},
glyph: {
color: 'rgba(107, 114, 128, 0.3)',
fontSize: '1.5rem',
},
transition: TRANSITIONS.medium,
},
dark: {
container: {
background: 'rgba(31, 41, 55, 0.95)',
border: '1px solid rgba(75, 85, 99, 0.8)',
borderTop: '12px solid #00B8D4',
borderRadius: '0.375rem',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.3)',
backdropFilter: 'blur(8px)',
},
header: {
background: 'rgba(17, 24, 39, 0.8)',
borderBottom: '1px solid rgba(75, 85, 99, 0.8)',
},
title: {
color: 'rgb(243, 244, 246)',
fontSize: '1.125rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '600',
letterSpacing: '-0.025em',
},
price: {
color: 'rgb(209, 213, 219)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '500',
letterSpacing: '0',
},
annualPrice: {
color: 'rgb(156, 163, 175)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '400',
fontStyle: 'italic',
letterSpacing: '0',
},
featureTitle: {
color: 'rgb(209, 213, 219)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '500',
letterSpacing: '0',
},
subFeatureTitle: {
color: 'rgb(156, 163, 175)',
fontSize: '1rem',
fontFamily: '"Inter", sans-serif',
fontWeight: '400',
letterSpacing: '0',
},
buttonSection: {
background: 'rgba(17, 24, 39, 0.8)',
borderTop: '1px solid rgba(75, 85, 99, 0.8)',
},
checkIcon: {
color: 'rgb(74, 222, 128)',
filter: 'drop-shadow(0 1px 2px rgba(74, 222, 128, 0.3))',
},
glyph: {
color: 'rgba(156, 163, 175, 0.3)',
fontSize: '1.5rem',
},
transition: TRANSITIONS.medium,
},
sacred: {
container: {
background: 'rgba(28, 25, 23, 0.95)',
border: '1px solid rgba(255, 215, 0, 0.3)',
borderTop: '12px solid #FFD700',
borderRadius: '0.375rem',
boxShadow: SHADOWS.sacred.large,
backdropFilter: 'blur(8px)',
backgroundImage: `
linear-gradient(rgba(255, 215, 0, 0.02), rgba(255, 215, 0, 0.02)),
radial-gradient(circle at top right, rgba(255, 215, 0, 0.08) 0%, transparent 50%)
`,
},
header: {
background: 'rgba(255, 215, 0, 0.1)',
borderBottom: '1px solid rgba(255, 215, 0, 0.3)',
backgroundImage:
'linear-gradient(to right, rgba(255, 215, 0, 0.1), transparent)',
},
title: {
color: '#FFD700',
fontSize: '1.125rem',
fontFamily: '"Cinzel", serif',
fontWeight: '600',
letterSpacing: '0.05em',
animation: 'sacred-glow 1.5s infinite alternate',
textShadow: '0 0 8px rgba(255, 215, 0, 0.6)',
},
price: {
color: '#FFD700',
fontSize: '1rem',
fontFamily: '"Cinzel", serif',
fontWeight: '500',
letterSpacing: '0.025em',
textShadow: '0 0 4px rgba(255, 215, 0, 0.5)',
},
annualPrice: {
color: 'rgba(255, 215, 0, 0.8)',
fontSize: '1rem',
fontFamily: '"Cinzel", serif',
fontWeight: '400',
fontStyle: 'italic',
letterSpacing: '0.025em',
textShadow: '0 0 4px rgba(255, 215, 0, 0.4)',
},
featureTitle: {
color: '#FFD700',
fontSize: '1rem',
fontFamily: '"Merriweather", serif',
fontWeight: '500',
letterSpacing: '0.025em',
textShadow: '0 0 4px rgba(255, 215, 0, 0.5)',
},
subFeatureTitle: {
color: 'rgba(255, 215, 0, 0.9)',
fontSize: '1rem',
fontFamily: '"Merriweather", serif',
fontWeight: '400',
letterSpacing: '0.025em',
textShadow: '0 0 2px rgba(255, 215, 0, 0.3)',
},
buttonSection: {
background: 'rgba(255, 215, 0, 0.05)',
borderTop: '1px solid rgba(255, 215, 0, 0.3)',
backgroundImage:
'linear-gradient(to top, rgba(255, 215, 0, 0.05), transparent)',
},
checkIcon: {
color: '#FFD700',
filter: 'drop-shadow(0 0 6px rgba(255, 215, 0, 0.8))',
animation: 'sacred-float 2s infinite',
},
glyph: {
color: 'rgba(255, 215, 0, 0.2)',
fontSize: '1.5rem',
animation: 'glyph-rotate 20s linear infinite',
},
transition: TRANSITIONS.slow,
},
}
// Helper function to get computed theme with custom style overrides
export const getPricingTableTheme = (
styles?: PricingTableStyles
): PricingTableTheme => {
const theme = styles?.theme || 'light'
const baseTheme = pricingTableThemes[theme]
if (!styles) {
return baseTheme
}
return {
container: {
background: styles.backgroundColor || baseTheme.container.background,
border: styles.borderColor
? `${styles.borderWidth || '1px'} solid ${styles.borderColor}`
: baseTheme.container.border,
borderTop: styles.borderTopColor
? `${styles.borderTopWidth || '12px'} solid ${styles.borderTopColor}`
: baseTheme.container.borderTop,
borderRadius: styles.borderRadius || baseTheme.container.borderRadius,
boxShadow: styles.boxShadow || baseTheme.container.boxShadow,
backdropFilter:
styles.backdropFilter || baseTheme.container.backdropFilter,
backgroundImage:
styles.backgroundImage || baseTheme.container.backgroundImage,
},
header: {
background: styles.headerBackground || baseTheme.header.background,
borderBottom: styles.headerBorderBottom || baseTheme.header.borderBottom,
backgroundImage:
styles.headerBackgroundImage || baseTheme.header.backgroundImage,
},
title: {
color: styles.titleColor || baseTheme.title.color,
fontSize: styles.titleFontSize || baseTheme.title.fontSize,
fontFamily: styles.titleFontFamily || baseTheme.title.fontFamily,
fontWeight: styles.titleFontWeight || baseTheme.title.fontWeight,
letterSpacing: styles.titleLetterSpacing || baseTheme.title.letterSpacing,
animation: styles.titleAnimation || baseTheme.title.animation,
textShadow: styles.titleTextShadow || baseTheme.title.textShadow,
},
price: {
color: styles.priceColor || baseTheme.price.color,
fontSize: styles.priceFontSize || baseTheme.price.fontSize,
fontFamily: styles.priceFontFamily || baseTheme.price.fontFamily,
fontWeight: styles.priceFontWeight || baseTheme.price.fontWeight,
letterSpacing: styles.priceLetterSpacing || baseTheme.price.letterSpacing,
textShadow: styles.priceTextShadow || baseTheme.price.textShadow,
},
annualPrice: {
color: styles.annualPriceColor || baseTheme.annualPrice.color,
fontSize: styles.annualPriceFontSize || baseTheme.annualPrice.fontSize,
fontFamily:
styles.annualPriceFontFamily || baseTheme.annualPrice.fontFamily,
fontWeight:
styles.annualPriceFontWeight || baseTheme.annualPrice.fontWeight,
fontStyle: styles.annualPriceFontStyle || baseTheme.annualPrice.fontStyle,
letterSpacing:
styles.annualPriceLetterSpacing || baseTheme.annualPrice.letterSpacing,
textShadow:
styles.annualPriceTextShadow || baseTheme.annualPrice.textShadow,
},
featureTitle: {
color: styles.featureTitleColor || baseTheme.featureTitle.color,
fontSize: styles.featureTitleFontSize || baseTheme.featureTitle.fontSize,
fontFamily:
styles.featureTitleFontFamily || baseTheme.featureTitle.fontFamily,
fontWeight:
styles.featureTitleFontWeight || baseTheme.featureTitle.fontWeight,
letterSpacing:
styles.featureTitleLetterSpacing ||
baseTheme.featureTitle.letterSpacing,
textShadow:
styles.featureTitleTextShadow || baseTheme.featureTitle.textShadow,
},
subFeatureTitle: {
color: styles.subFeatureTitleColor || baseTheme.subFeatureTitle.color,
fontSize:
styles.subFeatureTitleFontSize || baseTheme.subFeatureTitle.fontSize,
fontFamily:
styles.subFeatureTitleFontFamily ||
baseTheme.subFeatureTitle.fontFamily,
fontWeight:
styles.subFeatureTitleFontWeight ||
baseTheme.subFeatureTitle.fontWeight,
letterSpacing:
styles.subFeatureTitleLetterSpacing ||
baseTheme.subFeatureTitle.letterSpacing,
textShadow:
styles.subFeatureTitleTextShadow ||
baseTheme.subFeatureTitle.textShadow,
},
buttonSection: {
background:
styles.buttonSectionBackground || baseTheme.buttonSection.background,
borderTop:
styles.buttonSectionBorderTop || baseTheme.buttonSection.borderTop,
backgroundImage:
styles.buttonSectionBackgroundImage ||
baseTheme.buttonSection.backgroundImage,
},
checkIcon: {
color: styles.checkIconColor || baseTheme.checkIcon.color,
filter: styles.checkIconFilter || baseTheme.checkIcon.filter,
animation: styles.checkIconAnimation || baseTheme.checkIcon.animation,
},
glyph: {
color: styles.glyphColor || baseTheme.glyph.color,
fontSize: styles.glyphFontSize || baseTheme.glyph.fontSize,
animation: styles.glyphAnimation || baseTheme.glyph.animation,
},
transition: styles.transitionDuration
? `all ${styles.transitionDuration} ${styles.transitionEasing || 'cubic-bezier(0.4, 0, 0.2, 1)'}`
: baseTheme.transition,
}
}
// Main style generator function
export const getPricingTableStyles = (
styles?: PricingTableStyles,
isDisabled?: boolean
) => {
const themeConfig = getPricingTableTheme(styles)
const containerStyle: React.CSSProperties = {
display: 'flex',
width: styles?.width || '100%',
flexDirection: 'column',
height: styles?.height || '100%',
maxWidth: styles?.maxWidth,
minWidth: styles?.minWidth,
maxHeight: styles?.maxHeight,
minHeight: styles?.minHeight,
padding: styles?.padding,
margin: styles?.margin,
marginTop: styles?.marginTop,
marginBottom: styles?.marginBottom,
marginLeft: styles?.marginLeft,
marginRight: styles?.marginRight,
background: themeConfig.container.background,
border: themeConfig.container.border,
borderTop: themeConfig.container.borderTop,
borderRadius: themeConfig.container.borderRadius,
boxShadow: themeConfig.container.boxShadow,
backdropFilter: themeConfig.container.backdropFilter,
backgroundImage: themeConfig.container.backgroundImage,
position: 'relative',
overflow: 'hidden',
transition: themeConfig.transition,
opacity: isDisabled ? 0.5 : 1,
pointerEvents: isDisabled ? 'none' : 'auto',
}
const headerStyle: React.CSSProperties = {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: styles?.headerPadding || '1rem',
background: themeConfig.header.background,
borderBottom: themeConfig.header.borderBottom,
backgroundImage: themeConfig.header.backgroundImage,
}
const titleStyle: React.CSSProperties = {
color: themeConfig.title.color,
fontSize: themeConfig.title.fontSize,
fontFamily: themeConfig.title.fontFamily,
fontWeight: themeConfig.title.fontWeight,
letterSpacing: themeConfig.title.letterSpacing,
animation: themeConfig.title.animation,
textShadow: themeConfig.title.textShadow,
margin: 0,
}
const priceStyle: React.CSSProperties = {
color: themeConfig.price.color,
fontSize: themeConfig.price.fontSize,
fontFamily: themeConfig.price.fontFamily,
fontWeight: themeConfig.price.fontWeight,
letterSpacing: themeConfig.price.letterSpacing,
textShadow: themeConfig.price.textShadow,
}
const annualPriceStyle: React.CSSProperties = {
color: themeConfig.annualPrice.color,
fontSize: themeConfig.annualPrice.fontSize,
fontFamily: themeConfig.annualPrice.fontFamily,
fontWeight: themeConfig.annualPrice.fontWeight,
fontStyle: themeConfig.annualPrice.fontStyle as any,
letterSpacing: themeConfig.annualPrice.letterSpacing,
textShadow: themeConfig.annualPrice.textShadow,
}
const featuresSectionStyle: React.CSSProperties = {
display: 'flex',
flexDirection: 'column',
padding: styles?.featuresPadding || '1rem',
}
const featureItemStyle: React.CSSProperties = {
marginBottom: '1rem',
}
const featureTitleContainerStyle: React.CSSProperties = {
display: 'flex',
alignItems: 'center',
}
const featureTitleStyle: React.CSSProperties = {
color: themeConfig.featureTitle.color,
fontSize: themeConfig.featureTitle.fontSize,
fontFamily: themeConfig.featureTitle.fontFamily,
fontWeight: themeConfig.featureTitle.fontWeight,
letterSpacing: themeConfig.featureTitle.letterSpacing,
textShadow: themeConfig.featureTitle.textShadow,
}
const iconContainerStyle: React.CSSProperties = {
marginLeft: '0.5rem',
display: 'flex',
alignItems: 'center',
}
const subFeatureContainerStyle: React.CSSProperties = {
display: 'flex',
alignItems: 'center',
marginLeft: '1.5rem',
marginTop: '0.5rem',
}
const subFeatureTitleStyle: React.CSSProperties = {
color: themeConfig.subFeatureTitle.color,
fontSize: themeConfig.subFeatureTitle.fontSize,
fontFamily: themeConfig.subFeatureTitle.fontFamily,
fontWeight: themeConfig.subFeatureTitle.fontWeight,
letterSpacing: themeConfig.subFeatureTitle.letterSpacing,
textShadow: themeConfig.subFeatureTitle.textShadow,
}
const buttonSectionStyle: React.CSSProperties = {
padding: styles?.buttonPadding || '1rem',
background: themeConfig.buttonSection.background,
borderTop: themeConfig.buttonSection.borderTop,
backgroundImage: themeConfig.buttonSection.backgroundImage,
}
const checkIconStyle: React.CSSProperties = {
color: themeConfig.checkIcon.color,
filter: themeConfig.checkIcon.filter,
animation: themeConfig.checkIcon.animation,
}
const glyphStyle: React.CSSProperties = {
position: 'absolute',
top: '1.25rem',
right: '1.25rem',
color: themeConfig.glyph.color,
fontSize: themeConfig.glyph.fontSize,
animation: themeConfig.glyph.animation,
}
const sacredFooterStyle: React.CSSProperties = {
display: 'flex',
justifyContent: 'center',
gap: '0.25rem',
paddingBottom: '0.25rem',
}
const sacredFooterGlyphStyle: React.CSSProperties = {
color: 'rgba(255, 215, 0, 0.3)',
fontSize: '0.75rem',
animation: 'sacred-float 3s infinite ease-in-out',
}
return {
container: containerStyle,
header: headerStyle,
title: titleStyle,
price: priceStyle,
annualPrice: annualPriceStyle,
featuresSection: featuresSectionStyle,
featureItem: featureItemStyle,
featureTitleContainer: featureTitleContainerStyle,
featureTitle: featureTitleStyle,
iconContainer: iconContainerStyle,
subFeatureContainer: subFeatureContainerStyle,
subFeatureTitle: subFeatureTitleStyle,
buttonSection: buttonSectionStyle,
checkIcon: checkIconStyle,
glyph: glyphStyle,
sacredFooter: sacredFooterStyle,
sacredFooterGlyph: sacredFooterGlyphStyle,
}
}