Component · Pricing tables
Pricing Comparison
Vue 3 plan comparison table with grouped feature rows, check and dash cells, a sticky plan header on desktop and stacked cards on mobile. Tailwind CSS.
Single-plan Vue 3 pricing card with a feature checklist, a sale price, a money-back guarantee and one clear CTA. Tailwind CSS, dark mode, typed props.
by Vueplayv1.0.0Updated 3 hours ago
When you sell one thing, a three-column pricing table just adds noise. This card puts the plan name, a two-column checklist of what is included and a focused price panel side by side, with the guarantee right under the button where hesitation happens.
It suits courses, templates, lifetime deals, desktop apps and flat-rate SaaS. Set compareAtPrice to show a struck-through launch discount (announced properly to screen readers), change period to "/ month" for a subscription, and use the default slot to add a testimonial or FAQ link under the feature list.
On mobile the price panel stacks under the features so the call to action is the last thing people see before they scroll on.
<script setup lang="ts">
withDefaults(defineProps<{
heading?: string
subheading?: string
planName?: string
planDescription?: string
/** Price in whole currency units. */
price?: number
/** Optional original price, shown struck through. null hides it. */
compareAtPrice?: number | null
currency?: string
/** Text after the price, e.g. "one-time" or "/ month". */
period?: string
features?: string[]
ctaLabel?: string
/** Guarantee line under the button. Empty string hides it. */
guarantee?: string
/** Small note under the guarantee, e.g. taxes or payment methods. */
note?: string
}>(), {
heading: 'One price. Everything included.',
subheading: 'No tiers, no seat math, no upsells. Buy Fretboard Pro once and keep every update for the life of the product.',
planName: 'Lifetime licence',
planDescription: 'The full course library, practice tools and every future lesson, for as long as you play.',
price: 149,
compareAtPrice: 229,
currency: '$',
period: 'one-time',
features: () => [
'180+ video lessons from beginner to advanced',
'Interactive fretboard and chord trainer',
'Downloadable tabs and backing tracks',
'Progress tracking across all your devices',
'Monthly live Q&A sessions',
'All future courses at no extra cost',
],
ctaLabel: 'Get lifetime access',
guarantee: '30-day money-back guarantee. If it is not for you, email us and we refund you in full.',
note: 'Prices in USD. Taxes calculated at checkout.',
})
const emit = defineEmits<{
select: []
}>()
</script>
<template>
<section class="px-6 py-20 sm:py-24 lg:px-8">
<div class="mx-auto max-w-2xl text-center">
<h2 class="text-3xl font-bold tracking-tight text-balance text-gray-900 sm:text-4xl dark:text-white">{{ heading }}</h2>
<p class="mt-4 text-lg text-pretty text-gray-600 dark:text-gray-400">{{ subheading }}</p>
</div>
<article class="mx-auto mt-14 grid max-w-4xl overflow-hidden rounded-3xl bg-white shadow-xl ring-1 shadow-gray-900/5 ring-gray-200 lg:grid-cols-[1fr_20rem] dark:bg-gray-900 dark:shadow-black/30 dark:ring-gray-800">
<div class="p-8 sm:p-10">
<h3 class="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">{{ planName }}</h3>
<p class="mt-3 text-base text-gray-600 dark:text-gray-400">{{ planDescription }}</p>
<div class="mt-8 flex items-center gap-4">
<h4 class="shrink-0 text-sm font-semibold text-rose-600 dark:text-rose-400">What is included</h4>
<div class="h-px flex-1 bg-gray-100 dark:bg-gray-800" />
</div>
<ul class="mt-6 grid gap-4 text-sm text-gray-700 sm:grid-cols-2 sm:gap-x-6 dark:text-gray-300">
<li v-for="feature in features" :key="feature" class="flex gap-3">
<svg viewBox="0 0 20 20" aria-hidden="true" class="size-5 shrink-0 fill-rose-600 dark:fill-rose-400"><path fill-rule="evenodd" d="M16.7 5.3a1 1 0 0 1 0 1.4l-8 8a1 1 0 0 1-1.4 0l-4-4a1 1 0 1 1 1.4-1.4L8 12.6l7.3-7.3a1 1 0 0 1 1.4 0Z" clip-rule="evenodd" /></svg>
{{ feature }}
</li>
</ul>
<slot />
</div>
<div class="m-2 flex flex-col justify-center rounded-2xl bg-gray-50 px-8 py-10 text-center ring-1 ring-gray-900/5 ring-inset dark:bg-gray-950/60 dark:ring-white/5">
<p class="text-sm font-semibold text-gray-600 dark:text-gray-400">Pay once, own it forever</p>
<p class="mt-5 flex items-baseline justify-center gap-x-2">
<span v-if="compareAtPrice && compareAtPrice > price" class="text-lg font-medium text-gray-400 line-through dark:text-gray-500">
<span class="sr-only">Was</span>{{ currency }}{{ compareAtPrice }}
</span>
<span class="text-5xl font-bold tracking-tight text-gray-900 dark:text-white">
<span v-if="compareAtPrice && compareAtPrice > price" class="sr-only">now</span>{{ currency }}{{ price }}
</span>
<span v-if="period" class="text-sm font-medium text-gray-600 dark:text-gray-400">{{ period }}</span>
</p>
<button
type="button"
class="mt-8 w-full rounded-xl bg-rose-600 px-4 py-3 text-sm font-semibold text-white shadow-sm transition hover:bg-rose-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-rose-600 dark:bg-rose-500 dark:hover:bg-rose-400"
@click="emit('select')"
>
{{ ctaLabel }}
</button>
<p v-if="guarantee" class="mt-5 flex items-start gap-2 text-left text-xs leading-5 text-gray-600 dark:text-gray-400">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="mt-0.5 size-4 shrink-0 text-rose-600 dark:text-rose-400"><path d="M12 3 5 6v5c0 4.5 3 8.3 7 10 4-1.7 7-5.5 7-10V6l-7-3Z" /><path d="m9 12 2 2 4-4" /></svg>
{{ guarantee }}
</p>
<p v-if="note" class="mt-3 text-xs text-gray-500 dark:text-gray-500">{{ note }}</p>
</div>
</article>
</section>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
| heading | string | 'One price. Everything included.' | Section heading. |
| subheading | string | 'No tiers, no seat math, no upsells…' | Supporting text under the heading. |
| planName | string | 'Lifetime licence' | Plan title inside the card. |
| planDescription | string | 'The full course library, practice tools…' | One-line plan summary. |
| price | number | 149 | Price in whole currency units. |
| compareAtPrice | number | null | 229 | Original price shown struck through when higher than price. Pass null to hide. |
| currency | string | '$' | Currency symbol before prices. |
| period | string | 'one-time' | Text after the price, e.g. '/ month'. |
| features | string[] | 6 course features | Checklist items. |
| ctaLabel | string | 'Get lifetime access' | Button text. |
| guarantee | string | '30-day money-back guarantee…' | Guarantee line with a shield icon. Empty string hides it. |
| note | string | 'Prices in USD. Taxes calculated at checkout.' | Small print at the bottom of the price panel. |
| Event | Payload | Description |
|---|---|---|
| select | () | Fired when the CTA button is clicked. |
| Slot | Description |
|---|---|
| default | Extra content under the feature list, e.g. a testimonial. |
Component · Pricing tables
Vue 3 plan comparison table with grouped feature rows, check and dash cells, a sticky plan header on desktop and stacked cards on mobile. Tailwind CSS.
Component · Pricing tables
Three-tier Vue 3 pricing table with a monthly/yearly toggle, a highlighted plan and a select event. Tailwind CSS, dark mode, fully typed props.
Component · Hero sections
Split Vue 3 hero section with copy and two CTAs on the left and a Tailwind-drawn app window mockup on the right. Dark mode, typed props, no images.
Component · Feature sections
Bento-style Vue 3 feature grid with wide and tall tiles and small CSS-drawn visuals like maps, timelines and code. Tailwind CSS, dark mode, typed tiles.