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.

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>
Remove 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

AttributeExampleWhat it does
data-monthly19Monthly price in your base currency.
data-yearly190Price for the whole year. Leave it out to use data-yearly-discount on the section.
data-quarterly51Price for 3 months (optional).
data-price149Fixed price that ignores billing cycles (packages, tickets, courses).
data-periodone-timeText after the price. Empty value hides it.
data-customLet's talkShows text instead of a number.
data-free-labelFreeShows this text when the price is 0.
data-old-price99Crossed-out regular price. Per cycle: data-old-monthly, data-old-yearly.
data-monthly-eur17Exact price for a currency (any cycle + currency code). Skips conversion.
data-notePay onceLine under the price for fixed prices and custom prices.
data-note-yearly{total} per yearOverride the auto note for one cycle. {total} is replaced.
data-note-freeFree foreverNote shown when the price is 0.
data-badgeMost PopularAdds a badge on the card edge. See badges below.
data-badge-positioncornerPlaces the badge in the top corner.
data-badge-kindvalueForce the badge color: popular, value, limited, soldout, current.
data-featuredSame as adding the class is-featured.
data-statesoldoutsoldout or current: styles the card and disables the button.
data-soldout-label / data-current-labelSold outButton text for those states.
data-ignore-multiplierlocationThis 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.

AttributeDefaultWhat it does
data-pt-billingmonthlyStarting cycle: monthly, quarterly or yearly.
data-pt-currencyUSDStarting currency. auto picks one from the visitor's browser language.
data-pt-remember-currencyRemember the visitor's choice (localStorage).
data-pt-displaymonthmonth 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-discountSame for quarterly.
data-pt-strikefalseShows the monthly price crossed out on longer cycles.
data-pt-animationcountcount, flip, fade or none.
data-pt-decimalsautoauto (cents only when needed), 0 or 2.
data-pt-notestruefalse hides the "Billed $x yearly" line.
data-label-per-month etc./moTranslate 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 / -yearlyNote template for all cards. {total} is the cycle total.

Helper elements

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>

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>
Browsers block loading JSON from pages opened directly from your disk (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.

indigoemerald roseamber skyviolet oceanslate

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

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

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

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.