1. Web Design
  2. UI Design

How to Create a Stylish Pricing Table With a Sticky Header

Read Time:11 minsLanguages:

Sticky pricing tables are perfect for showing products and services with long lists of comparable features. In this tutorial we’re going to create a pricing table with a sticky header; along the way we’ll learn how to pin and unpin elements after a certain amount of scrolling. 

There are a number of JavaScript plugins out there offering this kind of functionality like ScrollMagic.js. In actual fact, I’ve already published a tutorial showing how you can implement a similar effect with this plugin. 

Today, however, let’s challenge ourselves and write our own code.

The Pricing Table We’re Building

For this hands-on exercise we’ll design a pricing page which will include the different subscription plans in columns of a pricing table. As we scroll down, the header will become sticky so as to stay in view, then will be released at a later point. The pricing table with sticky header combo is a popular UI pattern which you may have seen on websites like

Bear in mind the sticky functionality of our demo triggers on viewports wider than 779px. You may need to check out the full screen demo to fully appreciate it!

When Should You Use a Pricing Table?

Pricing tables come into their own when a collection of products, services, or variants, offer the consumer too much choice. If the consumer finds it difficult to decide which option to choose, they’re more likely to opt for nothing instead. Information overload = failure to convert.

Wasting time making choices negates any advantage to actually having the choice in the first place!

“Research [..] shows that an excess of choices often leads us to be less, not more, satisfied once we actually decide.” – Alina Tugend, NYT

The role of a pricing table is to help the user visually interpret the options available to them, perhaps sometimes even encouraging them to focus on the most appealing choice.

Read more in Val Geisler’s How to design a pricing page that converts on the InVision blog.

Pricing Table Plugins for WordPress

Before diving into the tutorial, you may want to take a look at the array of pricing table plugins available for WordPress on Envato Market.

ARPrice - WordPress Pricing Table PluginARPrice - WordPress Pricing Table PluginARPrice - WordPress Pricing Table Plugin
One of the many examples from ARPrice - WordPress Pricing Table Plugin

With that said, let’s now help our users by creating a super clear table for them to choose from!

1. Begin With the Page Markup

We’ll start with three sections:

The first and third sections won’t play an important role; they’ll contain dummy content and styles to ensure that the page is long enough for triggering the desired scrolling effect.

Inside the second section, we’ll place the table. We’ll wrap it within a container, which will make it behave responsively on small screens:

The table itself will contain four columns and represent the pricing plans for a product or service. In our case, we’ll have three subscription plans: starter, essential, and professional.

Table Headers

The table’s sticky header will clearly identify those plans. Each plan will also include a call-to-action button.

The table headersThe table headersThe table headers

Here’s the markup for the table headers:

Table Rows

Each table row will describe the availability of a feature across all plans. If a plan includes this feature, an SVG check mark icon will appear. Otherwise, a gray cross icon will appear.

The table rowsThe table rowsThe table rows

Here’s the markup for a feature that is supported by all plans:

And here’s the markup for a feature which is available only on the “Professional” plan:

2. Define Some Basic Styles

With the markup ready, we’ll forge on with the CSS. Our first step is to set up some CSS variables and common reset styles.

For each plan, we’ll define its corresponding variable. That will give us the ability to easily change its look and feel, if needed.

Here are the reset styles:

Note: for simplicity I won’t walk through all the CSS rules in the tutorial. There are almost 290 lines of CSS here. I’ll only discuss the most important ones. Make sure to check them all by clicking at the CSS tab of the demo project.

3. Style the Pricing Table

The pricing table will have a maximum width and be horizontally centered within the page:

The rows will behave as flex containers:

All cells will have the same width:

To separate the plans and provide a clear view of which cell belongs to each plan, we’ll add a light gray border to the target elements:

Making the Pricing Table Responsive

As we’ll see in the upcoming section, the sticky effect will trigger on viewports greater than 779px (you can change this value to suit). On smaller viewports, we’ll show a typical table which can be scrolled horizontally.  

The table with scrollbar on small screensThe table with scrollbar on small screensThe table with scrollbar on small screens

Bootstrap also uses similar functionality for responsive tables. There are other (better?) approaches for handling responsive tables out there, but this method certainly does the job here.

4. Pin/Unpin the Table Header

With the HTML and CSS in place, we’ll now concentrate on the scrolling effect using some JavaScript. This will enable us to make our header sticky.


For our first step we’ll grab a copy of the desired elements. We’ll store in variables the two classes which we’re going to use later: 

The next step is to perform some calculations. Specifically, we want to calculate the following things:

  • The table width.
  • The top position of the table relative to the viewport.
  • The height of the thead. This is the element will be pinned/unpinned.

Here’s the required JavaScript to do that:

Notice that we store the aforementioned values in let variables. We did this intentionally. When the page gets resized, we should recalculate the stuff described above and thus, reassign those new values to these variables.

On Scroll

Each time we scroll up or down, the scrollHandler function will be executed:

Inside that function we’ll perform the following actions, which will run only if the window width is at least 780px:

  1. Get the number of pixels a user has scrolled from the top of the viewport.
  2. Get the top position of the last section relative to the viewport.
  3. Check if a user has scrolled more than or equal to the table’s initial top position.
  4. If that happens, we set the thead’s width equal to the table’s initial width.
  5. Then, we check whether the resulting value of the step 2 is greater than the thead’s height.
  6. If that happens, we pin the thead element by adding the sticky-table class to the body and removing the sticky-table2 class from the same element. At that point, the thead becomes a fixed positioned element. We then position it at the top of the viewport. We also give the body a top padding equal to the thead’s height.
  7. If that doesn’t happen, we stop pinning the thead by adding the sticky-table2 class to the body and removing the sticky-table class from the same element. At that point, the thead is released and becomes an absolutely positioned element. We then position it at the bottom of the table.
  8. In case a user has scrolled less than the table’s initial top position (the thead hasn’t been pinned yet), we remove the sticky-table and stick-table2 classes from the body. Also, we set its top padding to 0. At that point, the thead hasn’t any position (static element), so we reset its default top position. Finally, we set its width to 100% (we can skip it).

The code that implements all this behavior is as follows:

And the related styles for each class:

5. Resizing the Page

We’ve almost done the job, folks! So far, the sticky header effect will work fine when the page loads. But what happens when the page gets resized? Well, it’d be really nice if we could make this demo work on resize as well, right? Let’s do it!

So each time we resize the page, the resizeHandler function will be executed.

Inside that function, we’ll first check the window width, then either update the values of the aforementioned let variables, or reset the thead and body inline styles. Notice that we retrieve the top position of the table by grabbing the first section’s height. You might be wondering why we didn’t use the table’s offsetTop property? During my tests I’ve noticed that it doesn’t always give accurate results on resize. Also, the getBoundingClientRect() method won’t work because it gives incorrect (even negative) values as well.

The required JavaScript code:


That’s all, folks! In this tutorial we’ve managed to build a useful scrolling effect, without using any external library. Not only did we learn how to create sticky elements, but also how to unpin (release) them after a certain amount of scrolling. 

All this combined has given us a really useful pricing table with a sticky header.

I hope that this exercise helped you learn something new and inspired you for using it in an upcoming project.

Before closing, I’d like to highlight two things:

  • I used the table element to build this effect. This might not always be the ideal method, considering the fact that there are more flexible and powerful layout solutions today like CSS Grid. However, a table is often the best element for displaying data, so I went with it on this occasion.
  • It’s always a big challenge to present tabular data on mobile devices. On this occasion I went with a simple scrolling solution. Another approach might be to discard the table method entirely (keep it only on >779px), for a carousel solution with three slides where each slide will represent a pricing plan. Perhaps you have a better idea which we could discuss in the comments below.

As always, thanks a lot for reading!

More Sticky Tutorials

We have a few tutorials which explore different approaches to sticking elements whilst scrolling a web page:

Learn More About Pricing Tables

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.