料金テーブル/

トグル料金表

Preview

月額/年額切り替えトグル付き料金テーブル

Source Code
tsx
222 lines
1"use client";
2
3import { useState } from "react";
4
5export function PricingToggle001() {
6 const [isYearly, setIsYearly] = useState(false);
7
8 const plans = [
9 {
10 name: "STARTER",
11 description: "For individuals and small projects",
12 monthlyPrice: 12,
13 yearlyPrice: 120,
14 features: [
15 "Up to 5 projects",
16 "Basic analytics",
17 "Email support",
18 "1GB storage",
19 ],
20 cta: "Get Started",
21 featured: false,
22 },
23 {
24 name: "PROFESSIONAL",
25 description: "For teams and growing businesses",
26 monthlyPrice: 39,
27 yearlyPrice: 390,
28 features: [
29 "Unlimited projects",
30 "Advanced analytics",
31 "Priority support",
32 "50GB storage",
33 "API access",
34 "Custom domains",
35 ],
36 cta: "Start Free Trial",
37 featured: true,
38 },
39 {
40 name: "ENTERPRISE",
41 description: "For large scale deployments",
42 monthlyPrice: 99,
43 yearlyPrice: 990,
44 features: [
45 "Everything in Pro",
46 "Unlimited storage",
47 "Dedicated support",
48 "Custom integrations",
49 "SLA guarantee",
50 "SSO & SAML",
51 ],
52 cta: "Contact Sales",
53 featured: false,
54 },
55 ];
56
57 // コーナードット
58 const CornerDots = ({ visible = false }: { visible?: boolean }) => {
59 if (!visible) return null;
60 return (
61 <>
62 <div className="absolute top-3 left-3 h-1 w-1 rounded-full bg-foreground/40" />
63 <div className="absolute top-3 right-3 h-1 w-1 rounded-full bg-foreground/40" />
64 <div className="absolute bottom-3 left-3 h-1 w-1 rounded-full bg-foreground/40" />
65 <div className="absolute bottom-3 right-3 h-1 w-1 rounded-full bg-foreground/40" />
66 </>
67 );
68 };
69
70 return (
71 <section className="bg-background py-24">
72 <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
73 {/* Header */}
74 <div className="mx-auto mb-12 max-w-2xl text-center">
75 <p className="mb-3 text-xs font-medium uppercase tracking-[0.3em] text-muted-foreground">
76 Pricing
77 </p>
78 <h2 className="text-3xl font-light tracking-wide text-foreground sm:text-4xl">
79 Simple, transparent pricing
80 </h2>
81 <p className="mt-4 text-base tracking-wide text-muted-foreground">
82 Choose the plan that works for you
83 </p>
84 </div>
85
86 {/* Toggle Switch */}
87 <div className="mb-16 flex items-center justify-center gap-4">
88 <span
89 className={`text-sm tracking-wide transition-colors ${
90 !isYearly ? "text-foreground" : "text-muted-foreground"
91 }`}
92 >
93 Monthly
94 </span>
95
96 <button
97 onClick={() => setIsYearly(!isYearly)}
98 className="relative h-8 w-14 rounded-full border border-border bg-muted/50 transition-colors hover:bg-muted"
99 aria-label={isYearly ? "Switch to monthly billing" : "Switch to yearly billing"}
100 >
101 <span
102 className={`absolute top-1 h-6 w-6 rounded-full bg-primary transition-all ${
103 isYearly ? "left-7" : "left-1"
104 }`}
105 />
106 </button>
107
108 <span
109 className={`text-sm tracking-wide transition-colors ${
110 isYearly ? "text-foreground" : "text-muted-foreground"
111 }`}
112 >
113 Yearly
114 </span>
115
116 {/* Savings Badge */}
117 <span className="ml-2 rounded-sm bg-muted px-2 py-1 text-[10px] font-medium uppercase tracking-[0.15em] text-muted-foreground">
118 Save 17%
119 </span>
120 </div>
121
122 {/* Pricing Cards */}
123 <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
124 {plans.map((plan) => (
125 <div
126 key={plan.name}
127 className={`relative p-8 transition-all ${
128 plan.featured
129 ? "border border-border bg-muted/50"
130 : "border border-border/50 bg-transparent"
131 }`}
132 >
133 <CornerDots visible={plan.featured} />
134
135 {/* Featured Badge */}
136 {plan.featured && (
137 <div className="absolute -top-3 left-1/2 -translate-x-1/2">
138 <span className="bg-primary px-4 py-1 text-[10px] font-medium uppercase tracking-[0.2em] text-primary-foreground">
139 Most Popular
140 </span>
141 </div>
142 )}
143
144 {/* Plan Name */}
145 <h3 className="text-xs font-medium uppercase tracking-[0.25em] text-muted-foreground">
146 {plan.name}
147 </h3>
148
149 {/* Price */}
150 <div className="mt-4 mb-2">
151 <span className="text-5xl font-light text-foreground">
152 ${isYearly ? plan.yearlyPrice : plan.monthlyPrice}
153 </span>
154 <span className="ml-1 text-sm text-muted-foreground">
155 /{isYearly ? "year" : "month"}
156 </span>
157 </div>
158
159 {/* Per month equivalent for yearly */}
160 {isYearly && (
161 <p className="text-xs tracking-wide text-muted-foreground">
162 ${Math.round(plan.yearlyPrice / 12)}/month billed annually
163 </p>
164 )}
165
166 {/* Description */}
167 <p className="mt-4 text-sm tracking-wide text-muted-foreground">
168 {plan.description}
169 </p>
170
171 {/* Divider */}
172 <div className="my-6 h-px bg-border" />
173
174 {/* Features */}
175 <ul className="space-y-3">
176 {plan.features.map((feature) => (
177 <li
178 key={feature}
179 className="flex items-center gap-3 text-sm tracking-wide text-muted-foreground"
180 >
181 <svg
182 className="h-4 w-4 flex-shrink-0 text-foreground/40"
183 fill="none"
184 viewBox="0 0 24 24"
185 stroke="currentColor"
186 >
187 <path
188 strokeLinecap="round"
189 strokeLinejoin="round"
190 strokeWidth={1.5}
191 d="M5 13l4 4L19 7"
192 />
193 </svg>
194 {feature}
195 </li>
196 ))}
197 </ul>
198
199 {/* CTA Button */}
200 <button
201 className={`mt-8 w-full py-3 text-xs font-medium uppercase tracking-[0.15em] transition-all ${
202 plan.featured
203 ? "bg-primary text-primary-foreground hover:bg-primary/90"
204 : "border border-border text-muted-foreground hover:border-foreground/30 hover:text-foreground"
205 }`}
206 >
207 {plan.cta}
208 </button>
209 </div>
210 ))}
211 </div>
212
213 {/* Footer */}
214 <div className="mt-12 text-center">
215 <p className="text-xs tracking-wide text-muted-foreground">
216 All prices in USD. Taxes may apply.
217 </p>
218 </div>
219 </div>
220 </section>
221 );
222}