Shiny pricing page

This commit is contained in:
kbe
2025-08-14 18:30:29 +02:00
parent 8aba6e31c8
commit 9d5f57c13e
6 changed files with 347 additions and 111 deletions

View File

@@ -25,7 +25,7 @@ const PricingCard: React.FC<PricingCardProps> = ({
const cardBg = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
const cardText = theme === 'dark' ? 'text-gray-100' : 'text-gray-900';
const cardBorder = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
const cardShadow = theme === 'dark' ? 'shadow-lg' : 'shadow-sm';
const cardShadow = theme === 'dark' ? 'shadow-xl' : 'shadow-2xl';
const cardHover = theme === 'dark' ? 'hover:bg-gray-700' : 'hover:bg-indigo-700';
const cardFocus = theme === 'dark' ? 'focus:ring-indigo-500' : 'focus:ring-indigo-500';
const cardPopular = theme === 'dark' ? 'bg-indigo-700' : 'bg-indigo-600';
@@ -33,37 +33,37 @@ const PricingCard: React.FC<PricingCardProps> = ({
const cardFeatureIcon = theme === 'dark' ? 'text-green-400' : 'text-green-500';
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 transition-all duration-300 transform hover:scale-105`}>
{isPopular && (
<div className={`${cardPopular} text-white text-sm font-medium px-4 py-1`}>
Popular
<div className={`${cardPopular} text-white text-xs font-semibold px-3 py-1.5 rounded-t-xl`}>
Most Popular
</div>
)}
<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">
{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 === 'Premium' && 'Critical security, performance, observability, platform SLAs, and support.'}
</p>
<p className="mt-8">
<span className="text-4xl font-extrabold">{price}</span>
<span className="text-5xl font-extrabold">{price}</span>
<span className="text-base font-medium text-gray-400">/{billingPeriod}</span>
</p>
<a
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-8 block w-full py-3 px-6 border border-transparent rounded-md shadow-lg text-center text-white bg-indigo-600 ${cardHover} focus:outline-none focus:ring-2 focus:ring-offset-2 ${cardFocus} transition-all duration-200`}
>
{ctaText}
</a>
</div>
<div className={`border-t ${cardBorder} p-6`}>
<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) => (
<li key={index} className="flex items-start">
<svg
className={`h-5 w-5 ${cardFeatureIcon}`}
className={`h-5 w-5 ${cardFeatureIcon} flex-shrink-0`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"

View File

@@ -108,9 +108,11 @@ export function Header({ className, isLoggedIn = false, isAdmin = false }: Heade
</Link>
))}
</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">
<ThemeToggle />
</div>
</div>
*/}
</div>
</div>
)}

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { Switch } from '@headlessui/react';
import { useTheme } from '@/lib/theme-context';
interface PricingToggleProps {
isYearly: boolean;
onToggle: () => void;
}
const PricingToggle: React.FC<PricingToggleProps> = ({ isYearly, onToggle }) => {
const { theme } = useTheme();
const thumbColor = theme === 'dark' ? 'bg-white' : 'bg-gray-900';
const trackColor = theme === 'dark' ? 'bg-gray-700' : 'bg-gray-200';
const activeTrackColor = theme === 'dark' ? 'bg-indigo-600' : 'bg-indigo-500';
return (
<div className="flex items-center justify-center mt-12 space-x-4">
<span className={`text-sm font-medium ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
Monthly
</span>
<div className="relative inline-flex items-center">
<Switch
checked={isYearly}
onChange={onToggle}
className={`relative inline-flex items-center h-6 rounded-full w-16 transition-colors duration-200 ease-in-out ${
isYearly ? activeTrackColor : trackColor
}`}
>
<span
className={`inline-block w-7 h-5 transform rounded-full shadow transition-transform duration-200 ease-in-out ${
isYearly ? 'translate-x-8' : 'translate-x-0'
} ${thumbColor}`}
/>
</Switch>
</div>
<span className={`text-sm font-medium ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
Yearly
</span>
</div>
);
};
export default PricingToggle;