Designing a checkout process is never easy, there's no universal recipe. If you're offering different product plans, your users will have to navigate through a pricing table first. We created a snippet for responsive pricing tables already. This time we tried to focus on what's next: what happens when the user selects a plan.

In most cases, you'll redirect your users to a sign up page. However, if your checkout process is quite simple, an alternative approach would be to animate the pricing table, and show the checkout form right away, in the same page. This is what we tried to achieve with today's snippet.

Enough talking, let's dive into the code ;)

Resources:

Velocity.js by Julian Shapiro.

Icons from Nucleo.

Creating the structure

The html structure is composed by 2 main elements: the ul.cd-pricing , which is the pricing table, and the div.cd-form , which is the form modal window.

Each plan includes a header with the plan title and price, a div.cd-pricing-features with a list of features and a footer with a call-to-action button.

The form structure is quite straightforward. Just one note: there's an empty div.cd-plan-info element, that will be filled using jQuery with the plan info selected by the user (basically with a clone of div.cd-pricing-header and .cd-pricing-features ).

<ul class="cd-pricing"> <li> <header class="cd-pricing-header"> <h2>Basic</h2> <div class="cd-price"> <span>$9.99</span> <span>month</span> </div> </header> <!-- .cd-pricing-header --> <div class="cd-pricing-features"> <ul> <li class="available"><em>Feature 1</em></li> <li><em>Feature 2</em></li> <li><em>Feature 3</em></li> <li><em>Feature 4</em></li> </ul> </div> <!-- .cd-pricing-features --> <footer class="cd-pricing-footer"> <a href="#0">Select</a> </footer> <!-- .cd-pricing-footer --> </li> <li> <!-- ... --> </li> <li> <!-- ... --> </li> </ul> <!-- .cd-pricing --> <div class="cd-form"> <div class="cd-plan-info"> <!-- content will be loaded using jQuery - according to the selected plan --> </div> <div class="cd-more-info"> <h3>Need help?</h4> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> </div> <form action=""> <fieldset> <legend>Account Info</legend>

<div class="half-width"> <label for="userName">Name</label> <input type="text" id="userName" name="userName"> </div> <div class="half-width"> <label for="userEmail">Email</label> <input type="email" id="userEmail" name="userEmail"> </div> <div class="half-width"> <label for="userPassword">Password</label> <input type="password" id="userPassword" name="userPassword"> </div> <div class="half-width"> <label for="userPasswordRepeat">Repeat Password</label> <input type="password" id="userPasswordRepeat" name="userPasswordRepeat"> </div> </fieldset> <fieldset> <!-- ... --> </fieldset> <fieldset> <!-- ... --> </fieldset> </form> <a href="#0" class="cd-close"></a> </div> <!-- .cd-form --> <div class="cd-overlay"></div> <!-- shadow layer -->

Adding style

CSS for this resource is quite simple. Just some hints: the class .empty-box is added to the .cd-pricing > li item when the form becomes visible. Since we clone the entire list item (and place it inside the form), the original one is still there. In this case, we used the .empty-box class to hide the original list item.

.cd-pricing > li { position: relative; margin: 0 auto 2.5em; background-color: #ffffff; border-radius: .3em .3em .25em .25em; box-shadow: 0 2px 8px rgba(2, 4, 5, 0.5); } .cd-pricing > li.empty-box { box-shadow: none; } .cd-pricing > li.empty-box::after { /* placeholder visible when .cd-form is open - in this case same color of the background */ content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #0f222b; }

We use the .empty-box class also to target the action button in the footer, so that it animates back when the user closes the form (from scale(0) to scale(1)).

.cd-pricing-footer a { transition: transform 0.3s; } .empty-box .cd-pricing-footer a { /* scale down to 0 the action button when sign up form is visible */ transform: scale(0); }

The div.cd-form is in position: fixed; and it doesn't have a size by default. When the user taps/clicks on the select button, we make the form visible (from visibility: hidden; to visibility: visible; ), assign it the same size of the pricing list item, and animate it by making it bigger. This creates the illusion that it's the pricing item itself that enlarges, while it's the div.cd-form , that has the same size and contains the same elements (clones - that then move to make space for the sign up form).

/* -------------------------------- Form -------------------------------- */ .cd-form { position: fixed; z-index: 2; background-color: #ffffff; border-radius: .25em; visibility: hidden; transition: visibility 0s 0.8s; /* Force Hardware Acceleration in WebKit */ transform: translateZ(0); backface-visibility: hidden; } .cd-form.is-visible { /* form is visible */ visibility: visible; transition: visibility 0s 0s; }

One last detail worth mentioning: when the form becomes visible, the green background of the plan title animates to become the background of the features list as well (desktop only). That is actually a different element ( .cd-form .cd-pricing-features::before ) whose height is animated (using the scale transformation).

.cd-form .cd-pricing-features::before { /* this is the layer which covers the .cd-pricing-features when the form is open - visible only on desktop */ content: ''; position: absolute; /* fix a bug while animating - 1px white space visible */ top: -5px; left: 0; height: calc(100% + 5px); width: 100%; background-color: #95ac5f; will-change: transform; transform: scaleY(0); transform-origin: center top; transition: transform 0.6s 0.2s; }

Events handling

The animateForm() function has been defined to animate the modal form: when a user selects a plan, the function evaluates the position and dimension of the selected pricing table item and assign them to the .cd-form so that it completely covers the pricing item (which is hidden using the .empty-box class).

Then the animation starts: we animate the .cd-form width and height to its final values and translate it so that it’s centered in the viewport.

//form is the .cd-form element form.velocity( { 'width': tableWidth+'px', //pricing table item width 'height': tableHeight+'px', //pricing table item height 'top': formTopValue, //final top value of the form 'left': formLeftValue, //final top value of the form 'translateX': formTranslateX+'px', //difference between formLeftValue and pricing table item left value 'translateY': formTranslateY+'px', //difference between formTopValue and pricing table item top value 'opacity': 1, }, 0, function(){ //table is the pricing table item table.addClass('empty-box'); form.velocity( { 'width': formFinalWidth+'px', //form final width 'height': formFinalHeight+'px', //form final height 'translateX': 0, 'translateY': 0, }, //animation duration animationDuration, //spring easing [ 220, 20 ]).addClass('is-visible'); });