Growing Thumbnails Portfolio

Demo Download

In this tutorial we will be making a portfolio with HTML5, jQuery and CSS3 that features an interesting growing effect.


As usual, we start off with a blank HTML5 document, and add the needed stylesheets, markup and JavaScript includes.

<!DOCTYPE html>
        <meta charset="utf-8" />
        <title>Growing Thumbnails Portfolio with jQuery &amp; CSS3 | Tutorialzine Demo</title>

        <!-- The stylesheet -->
        <link rel="stylesheet" href="assets/css/styles.css" />

        <!-- Google Fonts -->
        <link rel="stylesheet" href="|Bree+Serif" />

        <!--[if lt IE 9]>
          <script src=""></script>


            <h2>Welcome to</h2>
            <h1>Dan's Portfolio</h1>

        <div id="main">

            <h3>My Latest Projects</h3>

            <a class="arrow prev">Prev</a>
            <a class="arrow next">Next</a>

            <ul id="carousel">
                <li class="visible"><a href=""><img src="assets/img/sites/s1.jpg" alt="" /></a></li>
            <!--  Place additional items here -->


        <!-- JavaScript includes - jQuery and our own script.js -->
        <script src=""></script>
        <script src="assets/js/script.js"></script>


The important bit to note here is the #carousel unordered list. This element holds a collection of li items that represent your recent works. The visible class is only added if the thumbnail is to be shown. Only three thumbnails will be visible at a time. The href attribute of the hyperlink can point to the website in question, or a larger version of the image if you plan to use a lightbox together with this example.


The JavaScript

All of the JavaScript/jQuery code for this example resides in assets/js/script.js. We will write a JavaScript class called Navigator that will manage the carousel for us. This will involve writing methods for listening for clicks on the arrows, partitioning the carousel into groups of 3 items and showing them.

Here is how the class will be used:


    // Initialize the object on dom load
    var navigator = new Navigator({
        carousel: '#carousel',
        nextButton: '',
        prevButton: '.arrow.prev',
        // chunkSize:3,
        shuffle: true


When the document is loaded, we will create an instance of the class, passing the carousel div, the arrows and an optional parameter for whether you want the list to be shuffled. There is one more parameter that can go here - chunkSize. This property determines how many thumbnails will be featured at once, the default is 3.

The first step to achieve this, is to write the layout of the class:

// A Navigator "class" responsible for navigating through the carousel.
function Navigator(config) {

    this.carousel = $(config.carousel); //the carousel element
    this.nextButton = $(config.nextButton); //the next button element
    this.prevButton = $(config.prevButton); //the previous button element
    this.chunkSize = config.chunkSize || 3; //how many items to show at a time (maximum)
    this.shuffle = config.shuffle || false; //should the list be shuffled first? Default is false.

    //private variables
    this._items = $(config.carousel + ' li'); //all the items in the carousel
    this._chunks = []; //the li elements will be split into chunks.
    this._visibleChunkIndex = 0; //identifies the index from the this._chunks array that is currently being shown

    this.init = function () {

        // This will initialize the class, bind event handlers,
        // shuffle the li items, split the #carousel list into chunks


    // Method for handling arrow clicks
    this.handlePrevClick = function(e) {};
    this.handleNextClick = function(e) {};

    // show the next chunk of 3 lis
    this.showNextItems = function() {};

    // show the previous chunk of 3 lis
    this.showPrevItems = function() {};

    // These methods will determine whether to
    // show or hide the arrows (marked as private)
    this._checkForBeginning = function() {};
    this._checkForEnd = function() {};

    // A helper function for splitting the li
    // items into groups of 3
    this._splitItems = function(items, chunk) {};

We are using an underscore to denote which properties and methods are private. External code should not use any property that starts with an underscore.

In the fragments below you can see how each of the methods are implemented. First comes init(), which sets up the carousel by binding event listeners and partitioning the carousel ul.

this.init = function () {

    //Shuffle the array if neccessary
    if (this.shuffle) {
        //remove visible tags

        //shuffle list
        this._items.sort(function() { return 0.5 - Math.random() });

        //add visible class to first "chunkSize" items
        this._items.slice(0, this.chunkSize).addClass('visible');

    //split array of items into chunks
    this._chunks = this._splitItems(this._items, this.chunkSize);

    var self = this;

    //Set up the event handlers for previous and next button click
    self.nextButton.on('click', function(e) {

    self.prevButton.on('click', function(e) {

    // Showing the carousel on load

Next are the methods for handling arrow clicks.

this.handlePrevClick = function (e) {


    //as long as there are some items before the current visible ones, show the previous ones
    if (this._chunks[this._visibleChunkIndex - 1] !== undefined) {

this.handleNextClick = function(e) {


    //as long as there are some items after the current visible ones, show the next ones
    if (this._chunks[this._visibleChunkIndex + 1] !== undefined) {

They call showPrevItems and showNextItems respectfully:

this.showNextItems = function() {

    //remove visible class from current visible chunk

    //add visible class to the next chunk
    $(this._chunks[this._visibleChunkIndex + 1]).addClass('visible');

    //update the current visible chunk

    //see if the end of the list has been reached.


this.showPrevItems = function() {

    //remove visible class from current visible chunk

    //add visible class to the previous chunk
    $(this._chunks[this._visibleChunkIndex - 1]).addClass('visible');

    //update the current visible chunk

    //see if the beginning of the carousel has been reached.


The above methods remove or assign the visible class, which is how we control the visibility of the thumbnails. It is a good idea to hide the previous/next arrow if there are no further items to show. This is done with the checkForBeginning and checkForEnd methods.

this._checkForBeginning = function() {; //the prev button was clicked, so the next button can show.

    if (this._chunks[this._visibleChunkIndex - 1] === undefined) {
    else {;

this._checkForEnd = function() {; //the next button was clicked, so the previous button can show.

    if (this._chunks[this._visibleChunkIndex + 1] === undefined) {
    else {;

Lastly, here is the splitItems method, which generates the chunks. It relies on the splice JavaScript method for removing parts of the array and adding them to the splitItems array (it becomes an array of arrays):

this._splitItems = function(items, chunk) {

    var splitItems = [],
    i = 0;

    while (items.length > 0) {
        splitItems[i] = items.splice(0, chunk);

    return splitItems;


Congrats! You now have a working example. We are only left with styling it.



The styling of the portfolio is defined in assets/css/styles.css. Only the more interesting parts are shown here, as the rest is omitted for brevity.

    box-shadow:0 3px 5px #111;

    /* Initially hidden */

    /* Will animate the grow effect */
    -moz-transition:0.4s opacity;
    -webkit-transition:0.4s opacity;
    transition:0.4s opacity;

/* The thumbnails, hidden by default */

#carousel li{
    margin: -82px 18px 0;

    -moz-transition:0.4s all;
    -webkit-transition:0.4s all;
    transition:0.4s all;

/* This class will show the respective thumbnail */

#carousel li.visible{

#carousel li a img{

#carousel li img{

/* Creating the cradle below the thumbnails.
    Uses % so that it grows with the image. */

#carousel li:after{
    background:url('../img/cradle.png') no-repeat top center;
    bottom: 4%;
    content: "";
    height: 50px;
    left: -6.5%;
    position: absolute;
    right: -6.5%;
    width: auto;
    z-index: 1;

/* Enlarging the thumbnail */

#carousel li:hover{
    height: 197px;
    margin-top: -152px;
    width: 222px;

With this our Growing Thumbnails Portfolio is complete!

It's a wrap!

You can easily customize today's example by incorporating a lightbox script, increasing the number of thumbnails shown at once, or even by turning it into a gallery. If you do something interesting be sure to share it in the comment section below!

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles


Good Thumbnails Portfolio.

Great stuff man!

Thomas Vanhoutte

Looking very good, thanks for sharing!

I like this thumbnail portfolio but it would be more good if when we click on next the thumbnail should move with animation not just bump to next 3 pics.
Otherwise Its very good, I like it.

Imran, the beauty of this code is that it's nice and organized. To add in your desired effect, you just have to edit the showNextItems() and showPrevItems() method.

In each of these, you would add a CSS classname to the <li> elements, and you can specify the transition inside the CSS.

Jorge Sariego

Nice css3 effect. Thank you

Thanks you for giving us free tutorials anyway your my hero i wish if i can think and code like you nice tutorial

Good thumbnails protfolio and nice CSS3 effect
thank you

Nice, but the mousover could be a bit softer.

Jonathan Holroyd

Really nice portfolio effect, thanks for sharing! :)

Great portfolio! Excellent!


Sam Wightwick

Nice and clever use of CSS3

I did think of this kind of animation earlier, but just didn't get a chance to implement it.The key part where your idea differs from mine is that I wanted to do this in pure CSS and make it cross-browser compliant.If I do get some time, I will try to implement my solution & will leave you the demo URL so that you can check it out & leave me your feedback.