Wip on dark theme
This commit is contained in:
@@ -18,7 +18,7 @@ const PricingPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPrice = (basePrice: number) => {
|
const getPrice = (basePrice: number) => {
|
||||||
return isYearly ? basePrice * 10 : basePrice;
|
return isYearly ? Math.round(basePrice * 10 * 0.8) : basePrice;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBillingPeriod = () => {
|
const getBillingPeriod = () => {
|
||||||
@@ -39,34 +39,23 @@ const PricingPage = () => {
|
|||||||
|
|
||||||
{/* Pricing Toggle (Monthly/Yearly) */}
|
{/* Pricing Toggle (Monthly/Yearly) */}
|
||||||
<div className="mt-12 flex justify-center">
|
<div className="mt-12 flex justify-center">
|
||||||
<div className="relative inline-flex items-center">
|
<div className="relative inline-flex items-center bg-gray-200 dark:bg-gray-700 rounded-full p-1">
|
||||||
<button
|
<button
|
||||||
onClick={toggleBillingPeriod}
|
onClick={toggleBillingPeriod}
|
||||||
className={`relative px-4 py-2 text-sm font-medium rounded-l-full focus:outline-none transition-colors ${
|
className={`relative px-4 py-2 text-sm font-medium rounded-full focus:outline-none transition-colors ${
|
||||||
!isYearly
|
!isYearly
|
||||||
? 'bg-white text-gray-700 shadow'
|
? 'bg-white text-gray-900 shadow-sm'
|
||||||
: 'bg-gray-200 text-gray-500'
|
: 'text-gray-400'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Monthly
|
Monthly
|
||||||
</button>
|
</button>
|
||||||
<span
|
|
||||||
className={`w-12 h-6 px-1 bg-gray-300 rounded-full transition-transform duration-300 ease-in-out ${
|
|
||||||
isYearly ? 'translate-x-12' : 'translate-x-0'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`w-5 h-5 bg-white rounded-full shadow transform transition-transform duration-300 ease-in-out ${
|
|
||||||
isYearly ? 'translate-x-6' : 'translate-x-0'
|
|
||||||
}`}
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
<button
|
<button
|
||||||
onClick={toggleBillingPeriod}
|
onClick={toggleBillingPeriod}
|
||||||
className={`relative px-4 py-2 text-sm font-medium rounded-r-full focus:outline-none transition-colors ${
|
className={`relative px-4 py-2 text-sm font-medium rounded-full focus:outline-none transition-colors ${
|
||||||
isYearly
|
isYearly
|
||||||
? 'bg-white text-gray-700 shadow'
|
? 'bg-white text-gray-900 shadow-sm'
|
||||||
: 'bg-gray-200 text-gray-500'
|
: 'text-gray-400'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Yearly
|
Yearly
|
||||||
@@ -75,7 +64,7 @@ const PricingPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pricing Cards Grid */}
|
{/* Pricing Cards Grid */}
|
||||||
<div className="mt-16 space-y-16 sm:space-y-0 sm:grid sm:grid-cols-1 md:grid-cols-3 sm:gap-6 lg:gap-8">
|
<div className="mt-12 sm:mt-16 lg:mt-20 space-y-8 sm:space-y-12 md:space-y-0 md:grid md:grid-cols-1 lg:grid-cols-3 md:gap-6 lg:gap-8">
|
||||||
<PricingCard
|
<PricingCard
|
||||||
title="Start"
|
title="Start"
|
||||||
price={`$${getPrice(9)}`}
|
price={`$${getPrice(9)}`}
|
||||||
@@ -90,7 +79,7 @@ const PricingPage = () => {
|
|||||||
'Traffic & performance insights',
|
'Traffic & performance insights',
|
||||||
]}
|
]}
|
||||||
ctaText="Start Deploying"
|
ctaText="Start Deploying"
|
||||||
ctaHref="#"
|
ctaHref="/signup?plan=start"
|
||||||
/>
|
/>
|
||||||
<PricingCard
|
<PricingCard
|
||||||
title="Pro"
|
title="Pro"
|
||||||
@@ -106,7 +95,7 @@ const PricingPage = () => {
|
|||||||
'Email support',
|
'Email support',
|
||||||
]}
|
]}
|
||||||
ctaText="Start a free trial"
|
ctaText="Start a free trial"
|
||||||
ctaHref="#"
|
ctaHref="/signup?plan=pro"
|
||||||
isPopular={true}
|
isPopular={true}
|
||||||
/>
|
/>
|
||||||
<PricingCard
|
<PricingCard
|
||||||
@@ -123,7 +112,7 @@ const PricingPage = () => {
|
|||||||
'Advanced Support',
|
'Advanced Support',
|
||||||
]}
|
]}
|
||||||
ctaText="Contact Sales"
|
ctaText="Contact Sales"
|
||||||
ctaHref="#"
|
ctaHref="/contact-sales"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,48 +22,61 @@ const PricingCard: React.FC<PricingCardProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
// Add ARIA label for screen readers
|
||||||
|
const cardAriaLabel = `${title} pricing plan - $${price}/${billingPeriod}`;
|
||||||
|
|
||||||
const cardBg = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
|
const cardBg = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
|
||||||
const cardText = theme === 'dark' ? 'text-gray-100' : 'text-gray-900';
|
const cardText = theme === 'dark' ? 'text-gray-100' : 'text-gray-900';
|
||||||
const cardBorder = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
|
const cardBorder = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
|
||||||
const cardShadow = theme === 'dark' ? 'shadow-lg' : 'shadow-sm';
|
const cardShadow = theme === 'dark' ? 'shadow-lg' : 'shadow-sm';
|
||||||
const cardHover = theme === 'dark' ? 'hover:bg-gray-700' : 'hover:bg-indigo-700';
|
const cardHover = theme === 'dark' ? 'hover:bg-gray-700' : 'hover:bg-indigo-600';
|
||||||
const cardFocus = theme === 'dark' ? 'focus:ring-indigo-500' : 'focus:ring-indigo-500';
|
const cardFocus = theme === 'dark' ? 'focus:ring-indigo-500' : 'focus:ring-indigo-500';
|
||||||
const cardPopular = theme === 'dark' ? 'bg-indigo-700' : 'bg-indigo-600';
|
const cardPopular = theme === 'dark' ? 'bg-indigo-700' : 'bg-indigo-600';
|
||||||
const cardFeatureText = theme === 'dark' ? 'text-gray-300' : 'text-gray-700';
|
const cardFeatureText = theme === 'dark' ? 'text-gray-300' : 'text-gray-700';
|
||||||
const cardFeatureIcon = theme === 'dark' ? 'text-green-400' : 'text-green-500';
|
const cardFeatureIcon = theme === 'dark' ? 'text-green-400' : 'text-green-500';
|
||||||
|
const cardHoverShadow = theme === 'dark' ? 'hover:shadow-xl' : 'hover:shadow-2xl';
|
||||||
|
const cardTransition = 'transition-all duration-300';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col ${cardBg} border ${cardBorder} rounded-lg ${cardShadow} overflow-hidden transition-all duration-300`}>
|
<div
|
||||||
|
className={`flex flex-col ${cardBg} border ${cardBorder} rounded-xl ${cardShadow} overflow-hidden ${cardTransition} ${cardHoverShadow}`}
|
||||||
|
role="article"
|
||||||
|
aria-label={cardAriaLabel}
|
||||||
|
tabIndex={0} // Make the card focusable
|
||||||
|
>
|
||||||
{isPopular && (
|
{isPopular && (
|
||||||
<div className={`${cardPopular} text-white text-sm font-medium px-4 py-1`}>
|
<div
|
||||||
|
className={`${cardPopular} text-white text-xs font-semibold px-3 py-1.5 rounded-t-xl`}
|
||||||
|
>
|
||||||
Popular
|
Popular
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<h3 className={`text-xl font-semibold ${cardText}`}>{title}</h3>
|
<h3 className={`text-2xl font-bold ${cardText}`}>{title}</h3>
|
||||||
<p className="mt-4 text-sm text-gray-400">
|
<p className="mt-2 text-sm text-gray-400">
|
||||||
{title === 'Start' && 'The perfect starting place for your web app or personal project.'}
|
{title === 'Start' && 'The perfect starting place for your web app or personal project.'}
|
||||||
{title === 'Pro' && 'Everything you need to build and scale your app.'}
|
{title === 'Pro' && 'Everything you need to build and scale your app.'}
|
||||||
{title === 'Premium' && 'Critical security, performance, observability, platform SLAs, and support.'}
|
{title === 'Premium' && 'Critical security, performance, observability, platform SLAs, and support.'}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-8">
|
<p className="mt-6">
|
||||||
<span className="text-4xl font-extrabold">{price}</span>
|
<span className="text-4xl font-extrabold">{price}</span>
|
||||||
<span className="text-base font-medium text-gray-400">/{billingPeriod}</span>
|
<span className="text-base font-medium text-gray-400 ml-1">/{billingPeriod}</span>
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href={ctaHref}
|
href={ctaHref}
|
||||||
className={`mt-8 block w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-center text-white bg-indigo-600 ${cardHover} focus:outline-none focus:ring-2 focus:ring-offset-2 ${cardFocus}`}
|
className={`mt-6 block w-full py-3 px-4 border border-transparent rounded-md shadow text-center text-white bg-indigo-600 ${cardHover} focus:outline-none focus:ring-2 focus:ring-offset-2 ${cardFocus} ${cardTransition}`}
|
||||||
|
aria-label={`${ctaText} for ${title} plan`}
|
||||||
>
|
>
|
||||||
{ctaText}
|
{ctaText}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className={`border-t ${cardBorder} p-6`}>
|
<div className={`border-t ${cardBorder} p-6`}>
|
||||||
<h4 className={`text-sm font-medium ${cardText}`}>What's included:</h4>
|
<h4 className={`text-sm font-medium ${cardText}`}>What's included:</h4>
|
||||||
<ul className="mt-4 space-y-4">
|
<ul className="mt-4 space-y-3">
|
||||||
{features.map((feature, index) => (
|
{features.map((feature, index) => (
|
||||||
<li key={index} className="flex items-start">
|
<li key={index} className="flex items-start">
|
||||||
<svg
|
<svg
|
||||||
className={`h-5 w-5 ${cardFeatureIcon}`}
|
className={`h-5 w-5 ${cardFeatureIcon} flex-shrink-0`}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
@@ -84,4 +97,4 @@ const PricingCard: React.FC<PricingCardProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PricingCard;
|
export default React.memo(PricingCard);
|
||||||
@@ -32,6 +32,14 @@ const ThemeToggle = dynamic(() => import('./theme-toggle').then(mod => mod.defau
|
|||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ThemeToggleWrapper = () => {
|
||||||
|
return (
|
||||||
|
<div className="rounded-md px-3 py-2 text-sm font-medium transition-colors text-gray-300 hover:bg-gray-700 hover:text-white">
|
||||||
|
<ThemeToggle />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export function Header({ className, isLoggedIn = false, isAdmin = false }: HeaderProps) {
|
export function Header({ className, isLoggedIn = false, isAdmin = false }: HeaderProps) {
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
const [profileOpen, setProfileOpen] = useState(false)
|
const [profileOpen, setProfileOpen] = useState(false)
|
||||||
@@ -108,9 +116,7 @@ export function Header({ className, isLoggedIn = false, isAdmin = false }: Heade
|
|||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-md px-3 py-2 text-sm font-medium transition-colors text-gray-300 hover:bg-gray-700 hover:text-white">
|
<ThemeToggleWrapper />
|
||||||
<ThemeToggle />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user