Skip to content

Commit 661591c

Browse files
authored
Merge pull request #801 from plausible/new-pricing
Pricing section with Starter plans
2 parents 4073f87 + 9f5e6aa commit 661591c

File tree

3 files changed

+230
-311
lines changed

3 files changed

+230
-311
lines changed

_data/plans.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
- tier: "starter"
2+
title: "Starter"
3+
benefits:
4+
- "One site"
5+
- "3 years of data retention"
6+
- "Intuitive, fast and privacy-friendly dashboard"
7+
- "Email/Slack reports"
8+
- "Google Analytics import"
9+
- "Goals and custom events"
10+
- "Saved Segments"
11+
first_paint_price: "$9"
12+
13+
- tier: "growth"
14+
title: "Growth"
15+
benefits:
16+
- "Everything in Starter"
17+
- "Up to 3 sites"
18+
- "Up to 3 team members"
19+
- "Team Management"
20+
- "Shared Links"
21+
- "Embedded Dashboards"
22+
- "Shared Segments"
23+
default_price: "$14"
24+
first_paint_price: "$14"
25+
26+
- tier: "business"
27+
title: "Business"
28+
benefits:
29+
- "Everything in Growth"
30+
- "Up to 10 sites"
31+
- "Up to 10 team members"
32+
- "5 years of data retention"
33+
- "Custom Properties"
34+
- "Stats API (600 requests per hour)"
35+
- "Looker Studio Connector"
36+
- "Ecommerce revenue attribution"
37+
- "Funnels"
38+
first_paint_price: "$19"
39+
40+
- tier: "enterprise"
41+
title: "Enterprise"
42+
benefits:
43+
- "Everything in Business"
44+
- "10+ sites"
45+
- "10+ team members"
46+
- "600+ Stats API requests per hour"
47+
- '<p>Sites API access for <a class="text-indigo-600 hover:underline" href="/white-label-web-analytics">reselling</a></p>'
48+
- "5+ years of data retention"
49+
- "Technical onboarding"
50+
- "Priority support"
51+
style:
52+
main: "bg-gray-900 text-white"
53+
benefits_list: "text-gray-300"

_includes/pricing.html

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<div class="bg-gray-100 overflow-x-hidden" id="pricing">
2+
<div class="pt-12 sm:pt-16 lg:pt-20">
3+
<div class="px-4 mx-auto max-w-screen-xl sm:px-6 lg:px-8">
4+
<div class="text-center">
5+
<h2
6+
class="text-3xl font-extrabold text-gray-900 leading-9 sm:text-4xl sm:leading-10 lg:text-5xl lg:leading-none">
7+
Traffic based plans that match your growth
8+
</h2>
9+
<p class="mt-4 text-xl text-gray-600 leading-7">
10+
Sign up for 30-day free trial. No credit card required.
11+
</p>
12+
</div>
13+
</div>
14+
</div>
15+
16+
<script>
17+
const volumesWithPrices = [
18+
{ volume: "10k", starter: 9, growth: 14, business: 19 },
19+
{ volume: "100k", starter: 19, growth: 29, business: 39 },
20+
{ volume: "200k", starter: 29, growth: 44, business: 59 },
21+
{ volume: "500k", starter: 49, growth: 74, business: 99 },
22+
{ volume: "1M", starter: 69, growth: 104, business: 139 },
23+
{ volume: "2M", starter: 89, growth: 134, business: 179 },
24+
{ volume: "5M", starter: 129, growth: 194, business: 259 },
25+
{ volume: "10M", starter: 169, growth: 254, business: 339 },
26+
]
27+
28+
function volume(index) {
29+
index = parseInt(index)
30+
if (index === volumesWithPrices.length) {
31+
return "10M+"
32+
} else {
33+
return volumesWithPrices[index].volume
34+
}
35+
}
36+
37+
function price(currency, index, tier, billingInterval, withDiscount = false) {
38+
index = parseInt(index)
39+
if (index === volumesWithPrices.length) {
40+
return "Custom"
41+
}
42+
43+
let price
44+
45+
if (billingInterval === 'yearly') {
46+
price = volumesWithPrices[index][tier] * 10
47+
} else if (billingInterval === 'monthly' && withDiscount) {
48+
price = (volumesWithPrices[index][tier] * 10 / 12).toFixed(2)
49+
} else {
50+
price = volumesWithPrices[index][tier]
51+
}
52+
53+
return currency + price.toLocaleString("en-US")
54+
}
55+
56+
function calculateBubblePosition(volumeIndex) {
57+
const range = document.getElementById("volume")
58+
const newVal = Number((volumeIndex / range.max) * 100)
59+
return `left: calc(${newVal}% + (${13.87 - newVal * 0.26}px))`
60+
}
61+
</script>
62+
<div class="mx-auto max-w-screen-xl mt-12 p-4" x-data="{yearlyBilling: false, volumeIndex: 0, currency: '$'}"
63+
x-init="fetch('/api/paddle/currency').then(response => response.json()).then(data => currency = data.currency)">
64+
<div class="flex flex-col gap-4 lg:flex-row lg:gap-8 items-center lg:items-baseline">
65+
<div class="lg:flex-1 lg:order-3 lg:justify-end flex">
66+
<div class="relative">
67+
<span
68+
class="absolute whitespace-no-wrap w-max px-2.5 py-0.5 rounded-full text-xs font-medium leading-4 bg-yellow-100 border border-yellow-300 text-yellow-700"
69+
style="right: -20px; top: -15px;">
70+
2 months free
71+
</span>
72+
<div
73+
class="grid grid-cols-2 gap-x-1 rounded-full bg-white p-1 text-center text-sm font-semibold leading-5 border border-gray-200">
74+
<label class="cursor-pointer rounded-full px-2.5 py-1 transition-colors ease-in-out duration-200"
75+
:class="yearlyBilling ? 'text-gray-900 bg-white' : 'text-white bg-indigo-600'"
76+
@click="yearlyBilling = false">
77+
<input type="radio" name="frequency" value="monthly" class="sr-only" />
78+
<span>Monthly</span>
79+
</label>
80+
<label class="cursor-pointer rounded-full px-2.5 py-1 transition-colors ease-in-out duration-200"
81+
:class="yearlyBilling ? 'text-white bg-indigo-600' : 'text-gray-900 bg-white'"
82+
@click="yearlyBilling = true">
83+
<input type="radio" name="frequency" value="yearly" class="sr-only" />
84+
<span>Yearly</span>
85+
</label>
86+
</div>
87+
</div>
88+
</div>
89+
<p class="lg:w-1/4 lg:order-1 font-medium text-gray-600">
90+
<span x-show="volumeIndex < volumesWithPrices.length">Up to</span>
91+
<b class="text-gray-900" x-text="volume(volumeIndex)">10k</b>
92+
<span>monthly pageviews</span>
93+
</p>
94+
<div class="max-w-md lg:max-w-none w-full mt-6 lg:w-1/2 lg:order-2 flex items-baseline space-x-2">
95+
<span class="text-xs font-medium text-gray-600">10k</span>
96+
<div class="flex-1 relative">
97+
<input id="volume" class="shadow w-full" type="range" min="0" max="8" step="1" value="0"
98+
x-model="volumeIndex" />
99+
<output class="bubble text-xs font-medium" style="left: 13.87px;" x-text="volume(volumeIndex)"
100+
x-bind:style="`${calculateBubblePosition(volumeIndex)}`">
101+
10k
102+
</output>
103+
</div>
104+
<span class="text-xs font-medium text-gray-600">10M+</span>
105+
</div>
106+
</div>
107+
<div class="mt-6 overflow isolate mx-auto grid max-w-md grid-cols-1 gap-4 lg:mx-0 lg:max-w-none lg:grid-cols-4">
108+
{% for plan in site.data.plans %}
109+
<div
110+
class="rounded-xl px-4 py-4 shadow-md {{ plan.style.main | default: 'bg-white text-gray-900 border border-gray-200' }}">
111+
<h3 class="text-lg font-semibold leading-8">{{ plan.title }}</h3>
112+
<div class="h-20 max-h-20 pt-4">
113+
{% if plan.tier == "enterprise" %}
114+
<div class="text-3xl font-bold tracking-tight leading-8">Custom</div>
115+
{% else %}
116+
<div x-cloak x-show="yearlyBilling && volumeIndex < volumesWithPrices.length" class="flex">
117+
<div class="flex flex-col">
118+
<span class="h-8 leading-8 text-3xl font-bold tracking-tight"
119+
x-text="price(currency, volumeIndex, '{{ plan.tier }}', 'yearly')">
120+
</span>
121+
<span class="h-6 leading-6 font-bold tracking-tight text-sm">
122+
<span class="line-through tracking-tight text-gray-600"
123+
x-text="price(currency, volumeIndex, '{{ plan.tier }}', 'monthly')">
124+
</span>
125+
<span class="ml-1"
126+
x-text="price(currency, volumeIndex, '{{ plan.tier }}', 'monthly', withDiscount = true)">
127+
</span>
128+
</span>
129+
</div>
130+
<div class="flex flex-col text-sm text-gray-600 pl-1 leading-6 font-semibold">
131+
<span class="h-8 flex items-end">/year</span>
132+
<span class="h-6 ">/month</span>
133+
</div>
134+
</div>
135+
<div x-show="!yearlyBilling && volumeIndex < volumesWithPrices.length"
136+
class="flex items-baseline gap-x-1">
137+
<span class="text-3xl font-bold tracking-tight leading-8"
138+
x-text="price(currency, volumeIndex, '{{ plan.tier }}', 'monthly')">
139+
{{ plan.first_paint_price }}
140+
</span>
141+
<span class="text-sm font-semibold leading-6 text-gray-600 pl-1 self-end">/month</span>
142+
</div>
143+
<div x-cloak x-show="volumeIndex == volumesWithPrices.length"
144+
class="text-3xl font-bold tracking-tight leading-8">
145+
Custom</div>
146+
{% endif %}
147+
</div>
148+
{% if plan.tier == "enterprise" %}
149+
<a href="/contact"
150+
class="mt-4 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 transition duration-150 ease-in-out bg-gray-800 hover:bg-gray-700 text-white">
151+
Contact us
152+
</a>
153+
{% else %}
154+
<a :href="volumeIndex < volumesWithPrices.length && '/register' || '/contact'"
155+
x-text="volumeIndex < volumesWithPrices.length && 'Start your free trial' || 'Contact us'"
156+
class="mt-4 block rounded-md py-2 px-3 text-center text-sm font-semibold leading-6 transition duration-150 ease-in-out bg-indigo-600 hover:bg-indigo-500 text-white">
157+
Start your free trial
158+
</a>
159+
{% endif %}
160+
<ul class="mt-6 space-y-1 text-sm {{ plan.style.benefits_list | default: 'text-gray-600' }}">
161+
{% for benefit in plan.benefits %}
162+
<li class="flex gap-x-3">
163+
<svg class="h-6 w-5 flex-none text-indigo-600" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
164+
<path fill-rule="evenodd" clip-rule="evenodd"
165+
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" />
166+
</svg>
167+
{{ benefit }}
168+
</li>
169+
{% endfor %}
170+
</ul>
171+
</div>
172+
{% endfor %}
173+
</div>
174+
</div>
175+
</div>

0 commit comments

Comments
 (0)