A well-designed filter is a powerful tool users can take advantage of. It is actually an essential feature if your website has lots of content, distributed across different categories. For an e-commerce, it is a way to increase conversion rates by reducing the time needed by the user to find what he's looking for.

Building this kind of features is never easy: filters highly depend on the website content; besides a filter panel shouldn't be distracting, the main focus should be the content/products. Hence, we tried to simplify a bit your life by building an easy-to-customize and easy-to-integrate CSS filter panel for you. High five!

It takes advantage of CSS Transitions, CSS Transformations and jQuery muscles to smoothly slide in when needed.

Credits: we integrated MixItUp, which is a great jQuery plugin (free to use in non-commercial projects) created by the talented Patrick Kunka.

👋 A new version of this component is available. Download now →.

Creating the structure

The HTML structure is a bit more complex than usual. First of all, there are 2 main blocks of content: the <header> and the <main> elements, the second one being used to wrap both the gallery ( .cd-gallery ) and the filter ( .cd-filter ). In addition to that, we have the tabbed navigation (nested in 2 <div> elements, due to the drop-down effect visible on mobile devices) and the filter trigger ( .cd-filter-trigger ).

You may also notice a lot of class names (i.e. on the gallery list items) and data-filters: they are used to filter the content, not for styling purpose.

Note: the purpose of the .cd-gallery > li.gap element is to work in combo with the text: justify; property applied to .cd-gallery in order to create the gallery grid. You need to create as many .gap elements as the max number of items in a row -1.

<header class="cd-header"> <h1>Content Filter</h1> </header> <main class="cd-main-content"> <div class="cd-tab-filter-wrapper"> <div class="cd-tab-filter"> <ul class="cd-filters"> <li class="placeholder"> <a data-type="all" href="#0">All</a> <!-- selected option on mobile --> </li> <li class="filter"><a class="selected" href="#0" data-type="all">All</a></li> <li class="filter" data-filter=".color-1"><a href="#0" data-type="color-1">Color 1</a></li> <li class="filter" data-filter=".color-2"><a href="#0" data-type="color-2">Color 2</a></li> </ul> <!-- cd-filters --> </div> <!-- cd-tab-filter --> </div> <!-- cd-tab-filter-wrapper --> <section class="cd-gallery"> <ul> <li class="mix color-1 check1 radio2 option3"><img src="img/img-1.jpg" alt="Image 1"></li> <li class="mix color-2 check2 radio2 option2"><img src="img/img-2.jpg" alt="Image 2"></li> <li><!-- ... --></li> <li class="gap"></li> </ul> <div class="cd-fail-message">No results found</div> </section> <!-- cd-gallery --> <div class="cd-filter"> <form> <div class="cd-filter-block"> <h4>Block title</h4> <div class="cd-filter-content"> <!-- filter content --> </div> <!-- cd-filter-content --> </div> <!-- cd-filter-block --> </form> <a href="#0" class="cd-close">Close</a> </div> <!-- cd-filter --> <a href="#0" class="cd-filter-trigger">Filters</a> </main> <!-- cd-main-content -->

Adding style

Most of the CSS is about styling form elements and other basic embellishments. What's interesting is how we defined and used some classes - in combo with jQuery - to change the behaviour of some elements, based on specific events.

For example: on all devices, the filter bar gets fixed once it reaches the top of the viewport. To achieve this effect, we used the class .is-fixed , applied to the <main> element ( .cd-main-content ), so that we could target some of its children. Specifically: the .cd-tab-filter-wrapper is in static position, while the .cd-filter and .cd-filter-trigger are in absolute position (relative to the .cd-main-content ). When we apply the .is-fixed class to the .cd-main-content , we switch the position of all those elements to Fixed.

.cd-tab-filter-wrapper { background-color: #ffffff; z-index: 1; } .cd-filter { position: absolute; top: 0; left: 0; width: 280px; height: 100%; background: #ffffff; z-index: 2; transform: translateX(-100%); transition: transform 0.3s, box-shadow 0.3s; } .cd-filter-trigger { position: absolute; top: 0; left: 0; height: 50px; width: 60px; z-index: 3; } .cd-main-content.is-fixed .cd-tab-filter-wrapper { position: fixed; top: 0; left: 0; width: 100%; } .cd-main-content.is-fixed .cd-filter { position: fixed; height: 100vh; overflow: hidden; } .cd-main-content.is-fixed .cd-filter-trigger { position: fixed; }

Another thing worth mentioning is the .filter-is-visible class. It is applied to several elements when the user fires the filter panel. On all devices it's used to modify the translateX value of the .cd-filter element (from -100% to 0). On bigger devices only (>1170px), we target also the .cd-gallery and .cd-tab-filter , and we reduce their width: this way the panel won't overlap the content, and the user takes advantage of the additional space to apply filters and see changes simultaneously, with no need to close the panel.

Events handling