Pricing Table Master
15 responsive pricing table demos built on one shared CSS and JavaScript engine. Plans are plain HTML with data- attributes, so you change prices without writing code.
Thank you for your purchase. This guide covers everything from dropping a demo into your site to building your own layouts. If something is unclear, please contact us through the item support tab on ThemeForest.
- Pure HTML, CSS and vanilla JavaScript. No jQuery, no frameworks, no build step.
- Billing toggle (monthly, quarterly, yearly) with automatic savings labels.
- Currency switcher with 11 built-in currencies and correct formatting.
- Usage sliders, per-seat pricing, tiered pricing, add-ons and quantities.
- Comparison table with sticky headers, tooltips and mobile plan tabs.
- 8 accent themes, 6 card styles, light and dark mode, RTL support.
- Keyboard accessible controls, ARIA labels and reduced motion support.
Files & folders
pricing-table-master/
├── index.html Showcase of all demos
├── demos/ 15 demo pages (copy any of these)
├── assets/
│ ├── css/
│ │ ├── themes.css Colors, fonts, radius, shadows (CSS variables)
│ │ ├── core.css Layout, cards, prices, buttons, badges, toggles, add-ons
│ │ ├── card-styles.css Flat, bordered, glass, gradient, neumorphic, minimal
│ │ ├── table.css Comparison + spec tables
│ │ ├── animations.css Price animation, hover lift, scroll reveal
│ │ ├── index.css Showcase page only
│ │ └── demo-panel.css Demo top bar + customizer (preview only)
│ ├── js/
│ │ ├── pricing-engine.js Required. Reads data attributes and renders prices
│ │ ├── billing-toggle.js Monthly / quarterly / yearly controls
│ │ ├── currency.js Currency list, conversion and switcher
│ │ ├── slider-pricing.js Usage-based and per-seat pricing
│ │ ├── comparison.js Collapsible groups + mobile plan tabs
│ │ ├── tooltips.js Accessible info tooltips
│ │ ├── carousel.js Swipeable cards on mobile
│ │ └── demo-panel.js Demo top bar + customizer (preview only)
│ ├── data/plans-example.json
│ ├── fonts/ Inter + Plus Jakarta Sans (SIL OFL)
│ └── images/thumbs/ Showcase thumbnails
└── documentation/index.html This file
Add to your page
The fastest way is to open the demo you like, copy its <section class="pt-pricing"> into your page, and include the same CSS and JS files.
1. Stylesheets in the <head>
<link rel="stylesheet" href="assets/css/themes.css">
<link rel="stylesheet" href="assets/css/core.css">
<link rel="stylesheet" href="assets/css/card-styles.css"> <!-- optional: card skins -->
<link rel="stylesheet" href="assets/css/table.css"> <!-- optional: comparison tables -->
<link rel="stylesheet" href="assets/css/animations.css"> <!-- optional: animations -->
2. Scripts before </body>
<script src="assets/js/pricing-engine.js"></script> <!-- always first -->
<script src="assets/js/billing-toggle.js"></script> <!-- add only what you use -->
<script src="assets/js/currency.js"></script>
<script src="assets/js/slider-pricing.js"></script>
<script src="assets/js/comparison.js"></script>
<script src="assets/js/tooltips.js"></script>
<script src="assets/js/carousel.js"></script>
demo-panel.css and demo-panel.js on your live site. They only add the "All demos" bar and the Customize panel used in the previews.Your page should have class="pt-body" on the body (or your own base styles), and the html element can carry data-pt-theme, data-pt-mode and dir.
Plan cards
A pricing section is any element with data-pt (or the class pt-pricing). Every .pt-card inside it is a plan.
<section class="pt-pricing pt-section" data-pt>
<div class="pt-container">
<div class="pt-grid">
<div class="pt-card" data-monthly="19" data-yearly="190" data-badge="Most Popular">
<h3 class="pt-name">Pro</h3>
<p class="pt-desc">For growing teams.</p>
<div class="pt-price"></div> <!-- the engine fills this -->
<ul class="pt-features">
<li class="yes">10 projects</li>
<li class="yes">Priority support</li>
<li class="no">Custom domain</li>
</ul>
<div class="pt-card-foot">
<a href="#" class="pt-btn pt-btn--primary">Get Started</a>
</div>
</div>
</div>
</div>
</section>
The grid has 3 columns by default. Use pt-grid--2, pt-grid--4 or pt-grid--1 for other counts. pt-grid--lift makes the featured card slightly bigger and pt-hover-lift lifts cards on hover. For a wide horizontal plan use pt-card pt-card--row (see the Enterprise tab in Tabbed Pricing).
Any element with data-pt-plan also works as a plan, for example table headers or the radio rows in the Mobile App demo. Put a .pt-price inside it.
Card attributes
| Attribute | Example | What it does |
|---|---|---|
data-monthly | 19 | Monthly price in your base currency. |
data-yearly | 190 | Price for the whole year. Leave it out to use data-yearly-discount on the section. |
data-quarterly | 51 | Price for 3 months (optional). |
data-price | 149 | Fixed price that ignores billing cycles (packages, tickets, courses). |
data-period | one-time | Text after the price. Empty value hides it. |
data-custom | Let's talk | Shows text instead of a number. |
data-free-label | Free | Shows this text when the price is 0. |
data-old-price | 99 | Crossed-out regular price. Per cycle: data-old-monthly, data-old-yearly. |
data-monthly-eur | 17 | Exact price for a currency (any cycle + currency code). Skips conversion. |
data-note | Pay once | Line under the price for fixed prices and custom prices. |
data-note-yearly | {total} per year | Override the auto note for one cycle. {total} is replaced. |
data-note-free | Free forever | Note shown when the price is 0. |
data-badge | Most Popular | Adds a badge on the card edge. See badges below. |
data-badge-position | corner | Places the badge in the top corner. |
data-badge-kind | value | Force the badge color: popular, value, limited, soldout, current. |
data-featured | Same as adding the class is-featured. | |
data-state | soldout | soldout or current: styles the card and disables the button. |
data-soldout-label / data-current-label | Sold out | Button text for those states. |
data-ignore-multiplier | location | This plan is not affected by that multiplier control. |
Card classes: is-featured (highlight ring), is-inverted (accent gradient background), pt-card--row (horizontal).
Section attributes
Put these on the element with data-pt.
| Attribute | Default | What it does |
|---|---|---|
data-pt-billing | monthly | Starting cycle: monthly, quarterly or yearly. |
data-pt-currency | USD | Starting currency. auto picks one from the visitor's browser language. |
data-pt-remember-currency | Remember the visitor's choice (localStorage). | |
data-pt-display | month | month shows the per-month equivalent on longer cycles. total shows the full cycle price. |
data-yearly-discount | % discount used when a card has no data-yearly. | |
data-quarterly-discount | Same for quarterly. | |
data-pt-strike | false | Shows the monthly price crossed out on longer cycles. |
data-pt-animation | count | count, flip, fade or none. |
data-pt-decimals | auto | auto (cents only when needed), 0 or 2. |
data-pt-notes | true | false hides the "Billed $x yearly" line. |
data-label-per-month etc. | /mo | Translate any text: data-label-per-month, -per-quarter, -per-year, -show-all, -show-less, -sold-out, -current, -free, -save, -note-save. |
data-note-monthly / -quarterly / -yearly | Note template for all cards. {total} is the cycle total. |
Helper elements
<span class="pt-save" data-pt-save="Save {percent}%">shows the best yearly saving across plans. Adddata-pt-save-cycle="quarterly"for another cycle.<span data-pt-save-chip="yearly">shows "-20%" inside a billing button.<span data-pt-card-save="Save {percent}%">inside a card shows that plan's saving.data-pt-show="yearly"on any element shows it only on that cycle (or currency code).<span data-pt-amount="12">prints an amount in the active currency.
Badges & states
The color of a data-badge is chosen from its text: words like "Popular" use the accent, "Best Value" or "Save" are green, "Limited", "Offer", "Early" or "left" are orange, "Sold Out" is red and "Current" is neutral. Override with data-badge-kind.
You can also write badges by hand: <span class="pt-badge"> with pt-badge--soft, --success, --warning, --danger, --neutral, --dark or --shine.
Feature lists
<ul class="pt-features" data-limit="4"> <!-- data-limit adds "Show all features" -->
<li class="yes">Included feature</li>
<li class="no">Not included</li>
<li class="info">Neutral note</li>
<li class="yes"><span>API access <span data-pt-tip="REST and GraphQL"></span></span></li>
</ul>
Add data-show-all and data-show-less to the list to change the toggle text. A heading above the list: <p class="pt-features-title">.
Billing toggle
Requires billing-toggle.js. Choose one of three controls.
Switch (two cycles)
<div class="pt-billing" data-pt-billing-switch>
<span class="pt-billing-label" data-pt-cycle="monthly">Monthly</span>
<button class="pt-switch" type="button" aria-label="Bill yearly"></button>
<span class="pt-billing-label" data-pt-cycle="yearly">Yearly</span>
<span class="pt-save" data-pt-save="Save {percent}%"></span>
</div>
Segmented buttons (any number of cycles)
<div class="pt-segment" data-pt-billing-group aria-label="Billing period">
<button type="button" data-pt-cycle="monthly">Monthly</button>
<button type="button" data-pt-cycle="quarterly">Quarterly <span class="pt-save-chip" data-pt-save-chip="quarterly"></span></button>
<button type="button" data-pt-cycle="yearly">Yearly <span class="pt-save-chip" data-pt-save-chip="yearly"></span></button>
</div>
Native select
<select data-pt-billing-select><option value="monthly">Monthly</option><option value="yearly">Yearly</option></select>
On the yearly cycle the card shows the monthly equivalent (e.g. $15/mo) with a "Billed $180 yearly · save $48" note. Use data-pt-display="total" to show $180/yr instead.
Currency switcher
Requires currency.js. Write prices in USD (your base currency) and they are converted with the rates in currency.js. Converted prices are rounded so they look clean (whole euros, nearest 10 taka). For exact prices add per-currency attributes, e.g. data-monthly-eur="17" data-yearly-eur="168".
<!-- select (empty selects are filled automatically) -->
<label class="pt-select">
<select data-pt-currency-select>
<option value="USD">USD $</option>
<option value="EUR">EUR €</option>
<option value="BDT">BDT ৳</option>
</select>
</label>
<!-- or buttons -->
<div class="pt-segment" data-pt-currency-group>
<button type="button" data-pt-currency="USD">USD</button>
<button type="button" data-pt-currency="GBP">GBP</button>
</div>
Built-in: USD, EUR, GBP, BDT, INR, JPY, CAD, AUD, BRL, AED, CHF. Symbol position and separators follow each currency's locale (19 € in German format, ₹1,23,456 in Indian format). The exchange rates included are samples; update them to current rates for your site:
<script>
PricingTable.currencies.EUR.rate = 0.93;
PricingTable.addCurrency({ code: 'SEK', locale: 'sv-SE', rate: 10.5, round: 10, label: 'Swedish Krona' });
</script>
Add this after currency.js. Options per currency: rate, round (1, 10…), locale, symbol, symbolPosition ('before' / 'after'), decimals (0 or 2).
Usage sliders
Requires slider-pricing.js.
<input type="range" class="pt-range" data-pt-slider="users" min="1" max="100" value="10">
<output data-pt-slider-value="users" data-suffix=" users"></output>
<!-- uneven steps -->
<input type="range" class="pt-range" data-pt-slider="contacts"
data-steps="500,1000,5000,10000,50000" data-value="5000">
<!-- per seat: price = base + (users - included) × per-unit -->
<div class="pt-card" data-monthly="0" data-unit="users" data-per-unit="12" data-included="0">…</div>
<!-- graduated tiers: [up to, price per unit], null = no limit -->
<div class="pt-card" data-monthly="29" data-unit="contacts"
data-tiers='[[1000,0],[10000,0.006],[null,0.003]]'
data-max="100000" data-over-label="Contact sales"
data-range="5001-50000">…</div>
data-max replaces the price with data-over-label above that amount. data-range highlights the plan as the best fit while the value is inside the range. Output formats: data-format="compact" (10K), data-plus-at="250000" adds a "+" at the end of the scale. Per-unit prices are monthly and get the same yearly discount as the plan.
Add-ons & quantity
<div class="pt-addons">
<label class="pt-addon">
<input type="checkbox" data-monthly="5"> <!-- or data-price="20" for one-time -->
<span class="pt-addon-box"></span>
<span class="pt-addon-name">Towel service</span>
<span class="pt-addon-price"></span> <!-- filled automatically -->
</label>
</div>
<select class="pt-qty"><option>1</option><option>2</option></select>
<div class="pt-total"><span>Total</span><span class="pt-total-value"></span></div>
Checked add-ons are added to the main price. .pt-total-value shows price × quantity.
Multipliers (location, server size…)
<select data-pt-multiplier="location">
<option value="1">US East</option>
<option value="1.2">Singapore (+20%)</option>
</select>
Comparison table
Requires table.css and comparison.js. Open demos/comparison-table.html for the full markup.
<div class="pt-compare" data-pt-compare data-active="2">
<table class="pt-table">
<thead><tr>
<th>Features</th>
<th><div class="pt-table-plan" data-pt-plan data-monthly="12" data-yearly="120">
<span class="pt-table-name">Starter</span><div class="pt-price"></div>
</div></th>
<th class="is-featured-col">…</th>
</tr></thead>
<tbody class="pt-group"> <!-- add data-collapsed to start closed -->
<tr class="pt-group-head"><th colspan="3">Collaboration</th></tr>
<tr><th scope="row">Guest access</th><td><span class="pt-no"></span></td><td class="is-featured-col"><span class="pt-yes"></span></td></tr>
</tbody>
</table>
</div>
Below 768px (change with data-mobile="640") the table shows one plan at a time with tabs; data-active sets the first plan shown. Headers stick to the top while scrolling. If your site has a fixed header, set --pt-sticky-top: 72px on the body.
For a simple striped spec table use <table class="pt-spec"> inside <div class="pt-spec-wrap">.
Tooltips
Requires tooltips.js. An empty element becomes an info icon; an element with text becomes an underlined trigger.
Storage <span data-pt-tip="Total storage shared across your workspace."></span>
<span data-pt-tip="Shown on hover, focus and tap.">Webhooks</span>
Mobile carousel
Requires carousel.js. Add data-pt-carousel to a .pt-grid. Below 900px it becomes a swipeable row with arrows and dots, and opens on the featured plan. Set a different breakpoint with data-pt-carousel="1080".
Tabs, countdown & reveal
These are built into the engine.
<!-- Tabs -->
<div class="pt-segment" data-pt-tabs>
<button type="button" data-pt-tab="individual" aria-selected="true">Individual</button>
<button type="button" data-pt-tab="team">Team</button>
</div>
<div data-pt-panel="individual">…</div>
<div data-pt-panel="team" hidden>…</div>
<!-- Countdown to a date, or evergreen (restarts per visitor) -->
<div data-pt-countdown="2026-12-31T23:59:59"></div>
<div data-pt-countdown-hours="48" data-pt-countdown-labels="Days,Hours,Mins,Secs"></div>
<!-- Fade in on scroll: "", "zoom", "left" or "right" -->
<div class="pt-card" data-pt-reveal>…</div>
The FAQ accordion uses native <details> inside .pt-faq, so it needs no JavaScript.
Load plans from JSON
Instead of writing cards in HTML you can load them from a JSON file. See assets/data/plans-example.json for every field.
<section class="pt-pricing pt-section" data-pt data-pt-src="assets/data/plans-example.json">
<div class="pt-container">
<!-- billing / currency controls as usual -->
<div class="pt-grid" data-pt-cards></div>
</div>
</section>
file://). Use a web server: upload to your host, or run a local one such as npx serve or python3 -m http.server. All demos in this pack use HTML cards, so they work without a server.Plan fields: name, description, icon (user, team, rocket, building, spark), monthly, quarterly, yearly, price, period, custom, note, badge, state, featured, inverted, prices (exact per-currency prices), features (strings or { text, included, tip }), featureLimit, featuresTitle, cta ({ label, href, note, primary }). Settings: billing, currency, display, yearlyDiscount.
Themes & variables
Pick an accent with data-pt-theme on <html> or on one section.
To use your brand color, override the variables in your own stylesheet (loaded after themes.css):
:root {
--pt-accent: #ff5a1f; /* buttons, switches, highlights */
--pt-accent-hover: #e04812;
--pt-accent-2: #ff8a00; /* second gradient color */
--pt-accent-soft: #fff1ea; /* tinted backgrounds */
--pt-accent-text: #c2410c; /* accent used as text */
--pt-accent-contrast: #ffffff; /* text on accent */
--pt-radius: 16px; /* card corners */
--pt-font: "Inter", system-ui, sans-serif;
--pt-font-display: "Plus Jakarta Sans", sans-serif;
}
Other variables: --pt-bg, --pt-surface, --pt-surface-2, --pt-border, --pt-border-strong, --pt-text, --pt-text-2, --pt-muted, --pt-success, --pt-danger, --pt-warning (each with a -soft version), --pt-radius-sm, --pt-shadow-sm, --pt-shadow, --pt-shadow-lg, --pt-speed, --pt-ease, --pt-sticky-top.
Card styles
Add one class to the section, a grid or a single card: pt-style-flat, pt-style-bordered, pt-style-glass, pt-style-gradient, pt-style-neumorphic or pt-style-minimal. Requires card-styles.css. For glass layouts add the colorful background with <div class="pt-blobs"><span></span><span></span><span></span></div> as the first child of the section. Try every style live with the Customize button in the demos.
Dark mode & RTL
Dark mode: data-pt-mode="dark" on <html> or a section. To follow the visitor's system setting:
<script>
if (matchMedia('(prefers-color-scheme: dark)').matches) document.documentElement.setAttribute('data-pt-mode', 'dark');
</script>
Right-to-left: set dir="rtl" (and your lang) on <html>. All layouts use logical CSS properties, so switches, badges, arrows and carousels mirror automatically. Prices stay left-to-right so numbers read correctly.
Animations
- Price change:
data-pt-animation="count"(numbers roll),flip,fadeornone. - Hover lift: class
pt-hover-lifton the grid. - Scroll reveal:
data-pt-revealon any element; siblings are staggered. - Badge shine: class
pt-badge--shine. Float: classpt-float.
All motion turns off for visitors who enable "reduce motion" in their system settings.
JavaScript API & events
var pricing = PricingTable.get('#pricing'); // instance for a section
pricing.set({ billing: 'yearly' }); // change cycle
pricing.set({ currency: 'EUR' }); // change currency
pricing.set({ qty: { users: 25 } }); // set a slider value
pricing.state; // { billing, currency, qty, multipliers }
PricingTable.format(1234.5, 'EUR'); // "1.234,50 €"
PricingTable.init(element); // init a section added later (AJAX)
// Custom price rule for every plan (runs on each render)
PricingTable.modifiers.push(function (ctx) {
// ctx.card, ctx.price, ctx.billing, ctx.months, ctx.instance
if (ctx.card.dataset.vat) return ctx.price * 1.2;
});
document.addEventListener('pt:change', function (e) {
console.log(e.detail.state); // fires after every render
});
Events (bubble from the section): pt:ready, pt:change, pt:addon, pt:tab, pt:countdown-end.
Accessibility
- Billing and currency controls are real buttons with
role="switch"orrole="radiogroup", arrow-key navigation and visible focus rings. - Prices include screen-reader text ("$15 per month"); changes are announced in a polite live region.
- Check and cross icons in tables have text alternatives; tooltips use
role="tooltip"andaria-describedby. - Tabs follow the WAI-ARIA tabs pattern. The FAQ uses native
<details>. - Default colors meet WCAG AA contrast for body text in light and dark modes.
Browser support
Latest Chrome, Edge, Firefox, Safari (macOS and iOS) and Samsung Internet. Visual extras such as color-mix(), :has() and backdrop-filter degrade gracefully in older browsers. Internet Explorer is not supported.
FAQ
How do I change a price?
Edit data-monthly and data-yearly on the card. The yearly value is the total for the year.
The yearly price shows decimals. How do I avoid that?
Pick yearly totals that divide by 12, or add data-pt-decimals="0" to the section.
Can I use it in WordPress, Webflow, React or Laravel?
Yes. It is plain HTML, CSS and JS. Paste the markup into an HTML block or template and load the files. In single-page apps call PricingTable.init(element) after the markup is mounted.
How do I connect the buttons to my checkout?
Set the href of each .pt-btn. To send the chosen cycle, read PricingTable.get(section).state.billing in a click handler, or listen to pt:change.
Are the currency rates live?
No. The rates in currency.js are samples. Update them, set them from your server, or use exact per-currency attributes.
Can I have two pricing sections on one page?
Yes. Each data-pt section has its own state and controls.
Credits & changelog
- Fonts: Inter and Plus Jakarta Sans, SIL Open Font License, self-hosted in
assets/fonts. - Icons: hand-drawn inline SVG, included in the item.
- No images, logos or names in the demos are real brands; all content is placeholder text.
v1.0.0
Initial release: 15 demos, pricing engine, billing toggle, currency switcher, usage sliders, comparison tables, tooltips, carousel, 8 themes, 6 card styles, dark mode and RTL.