Fullscreen Pageflip Layout

A tutorial on how to create a fullscreen pageflip layout using BookBlock. The idea is to flip the content like book pages and access the pages via a sidebar menu that will slide out from the left.

Fullscreen Pageflip Layout with BookBlock

We have received a couple of requests on how to use the BookBlock plugin in fullscreen. So, we decided to create a fullscreen layout, apply BookBlock to it and add a sidebar menu. We’ll show you how to customize BookBlock and use some available options for navigating the content.

The idea is to navigate the pages using the arrows or swiping the pages, and to slide out the menu when clicking on the menu button. The sidebar menu will contain links to different pages, i.e. the table of contents. When clicking on a table of contents entry, we’ll jump to the respective page.

We’ll also use jScrollPane by Kevin Luck to add a custom scrollbar for the content when needed.

Please note: CSS 3D transforms will only work in modern browsers that support those properties.

The demo contains excerpts form the hilarious “The Funny Side of Physic” by A. D. Crabtre from Project Gutenberg.

The following libraries and jQuery plugins will be used:

  1. BookBlock by Pedro Botelho
  2. Custom jQuery++ by Bitovi
  3. jScrollPane by Kevin Luck
  4. jQuery Mouse Wheel Plugin by Brandon Aaron
  5. Custom Modernizr (peek inside to see what this build includes)

So, let’s get started!

The Markup

Let’s have a main container for all our elements. Inside, we’ll add a division for the sidebar menu which we’ll give the class “menu-panel”, and the wrapper for the BookBlock with the class “bb-custom-wrapper”. The BookBlock will contain the wrapper (that we’ll apply the plugin to) and the structure that the plugin needs. Inside of each item, we’ll add a content wrapper with a division that we’ll need for the custom scroll functionality:

<div id="container" class="container">	

	<div class="menu-panel">
		<h3>Table of Contents</h3>
		<ul id="menu-toc" class="menu-toc">
			<li class="menu-toc-current"><a href="#item1">Self-destruction</a></li>
			<li><a href="#item2">Why we die</a></li>
			<li><a href="#item3">The honeymoon</a></li>
			<li><a href="#item4">A drawing joke</a></li>
			<li><a href="#item5">Commencing practice</a></li>
		</ul>
	</div>

	<div class="bb-custom-wrapper">

		<div id="bb-bookblock" class="bb-bookblock">

			<div class="bb-item" id="item1">
				<div class="content">
					<div class="scroller">
						<h2>Self-destruction</h2>
						<p>...</p>
					</div>
				</div><!-- /content -->
			</div><!-- /bb-item -->

			<div class="bb-item" id="item2"><!-- ... --></div>

			<div class="bb-item" id="item3"><!-- ... --></div>

			<div class="bb-item" id="item4"><!-- ... --></div>

			<div class="bb-item" id="item5"><!-- ... --></div>

		</div><!-- /bb-bookblock -->
		
		<nav>
			<a id="bb-nav-prev" href="#">←</a>
			<a id="bb-nav-next" href="#">→</a>
		</nav>

		<span id="tblcontents" class="menu-button">Table of Contents</span>

	</div><!-- /bb-custom-wrapper -->

</div><!-- /container -->

The menu items will point to the respective BookBlock pages (bb-item). We’ll also add navigation arrows and a button for toggling the opening and closing of the menu.

Let’s move on to the style.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

The CSS

Note that we won’t use any vendor specific prefixes here, but you’ll find them in the files.

We don’t discuss the style of the BookBlock plugin here (you can find the styles in bookblock.css), instead we’ll focus on all the other styles that are important for the layout and customize a couple of things.

Let’s start by importing the Lato font from Google web fonts:

@import url(http://fonts.googleapis.com/css?family=Lato:300,400,700);

The html element should have a height of 100% since we will need to set the heights of absolute elements inside to expand to the window’s height:

html { 
	height: 100%; 
}

Let’s use border-box for box-sizing which will allow us to define percentages for widths and heights of elements while using paddings, without worrying about oversizing elements:

*,
*:after,
*:before {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	padding: 0;
	margin: 0;
}

(We are brutally resetting the paddings in margins of all elements; you don’t have to do that, instead you could just normalize the respective elements.)

Let’s define the font for the body and set it’s height to 100%. Remember, we need to set it because of the children that will be absolute and 100% in height as well:

body {
	font-family: 'Lato', Calibri, Arial, sans-serif;
	font-weight: 400;
	font-size: 100%;
	color: #333;
	height: 100%;
}

We are using Modernizr and we’ve added the class “no-js” to the html element. When JavaScript is enabled, Modernizr will replace that class with “js”. This will allow us to give certain CSS properties to elements that we don’t want if JavaScript is disabled. Our 100% width and height layout only makes sense if we have JS enabled and only then we want the body to have its overflow hidden:

.js body {
	overflow: hidden;
}

Let’s define some link styles:

a {
	color: #555;
	text-decoration: none;
}

a:hover {
	color: #000;
}

We want our main container to occupy all the window width and height. The sidebar menu will be positioned outside of it by setting a negative left value (of its own width). The idea is to animate the whole container when we click to open the menu. It will slide to the right revealing the overflown sidebar menu.

So, let’s set the main wrappers’ widths and heights to 100% and add a transition to the container:

.container,
.bb-custom-wrapper,
.bb-bookblock {
	width: 100%;
	height: 100%;
}

.container {
	position: relative;
	left: 0px;
	transition: left 0.3s ease-in-out;
}

When we click on the menu button, we will add another class to the container which will set the left to 240 pixels (width of the sidebar menu):

.slideRight {
	left: 240px;
}

Without JavaScript enabled, we won’t be able to do this, so let’s add a left padding instead:

.no-js .container {
	padding-left: 240px;
}

By default, we want the sidebar menu to be fixed to the left side:

.menu-panel {
	background: #f1103a;
	width: 240px;
	height: 100%;
	position: fixed;
	z-index: 1000;
	top: 0;
	left: 0;
	text-shadow: 0 1px 1px rgba(0,0,0,0.1);
}

When JS is enabled, we’ll set the position to absolute and the left to -240 pixel:

.js .menu-panel {
	position: absolute;
	left: -240px;
}

Let’s style the elements of the menu:

.menu-panel h3 {
	font-size: 1.8em;
	padding: 20px;
	font-weight: 300;
	color: #fff;
	box-shadow: inset 0 -1px 0 rgba(0,0,0,0.05);
} 

.menu-toc {
	list-style: none;
}

.menu-toc li a {
	display: block;
	color: #fff;
	font-size: 1.1em;
	line-height: 3.5;
	padding: 0 20px;
	cursor: pointer;
	background: #f1103a;
	border-bottom: 1px solid #dd1338;
}

.menu-toc li a:hover,
.menu-toc li.menu-toc-current a{
	background: #dd1338;
}

The navigation will be positioned absolutely on top of everything:

.bb-custom-wrapper nav {
	top: 20px;
	left: 60px;
	position: absolute;
	z-index: 1000;
}

The arrow links and the menu button will also be positioned absolutely and we’ll make them round by setting the border-radius to 50%:

.bb-custom-wrapper nav span,
.menu-button {
	position: absolute;
	width: 32px;
	height: 32px;
	top: 0;
	left: 0;
	background: #f1103a;
	border-radius: 50%;
	color: #fff;
	line-height: 30px;
	text-align: center;
	speak: none;
	font-weight: bold;
	cursor: pointer;
}

.bb-custom-wrapper nav span:last-child {
	left: 40px;
}

.bb-custom-wrapper nav span:hover,
.menu-button:hover {
	background: #000;
}

The menu button will be positioned in the top left corner and we’ll hide its text:

.menu-button {
	z-index: 1000;
	left: 20px;
	top: 20px;
	text-indent: -9000px;
}

Let’s create a little menu icon by using a pseudo-element with a double box shadow for the upper and lower line:

.menu-button:after {
	position: absolute;
	content: '';
	width: 50%;
	height: 2px;
	background: #fff;
	top: 50%;
	margin-top: -1px;
	left: 25%;
	box-shadow: 0 -4px #fff, 0 4px #fff;
}

In case that there is no JS enabled, we don’t need any of those elements, so we’ll simply hide them:

.no-js .bb-custom-wrapper nav span,
.no-js .menu-button {
	display: none;
}

Let’s move to the inner parts of the BookBlock items. The content division needs to be absolute and we’ll set the overflow to hidden. This is important because we want to apply our custom scroll here and we’ll only do that when a page was turned. If we wouldn’t set the overflow to hidden, we’d see the content overflowing. Again, this only makes sense when we have JS enabled, so we’ll add the “js” class:

.js .content {
	position: absolute;
	top: 60px;
	left: 0;
	bottom: 50px;
	width: 100%;
	overflow: hidden;
}

The scroller div is the one that will grow with content, so let’s set some paddings here:

.scroller {
	padding: 10px 5% 10px 5%;
}

Using percentages as lateral padding will make the layout adjust liquidly to the screen size.

Let’s hide those sharp edges when we scroll by adding pseudo-elements with a white to transparent gradient to the top and the bottom of the content div:

.js .content:before,
.js .content:after {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 20px;
	z-index: 100;
	pointer-events: none;
	background: 
		linear-gradient(
			to bottom, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

.js .content:after {
	top: auto;
	bottom: 0;
	background: 
		linear-gradient(
			to top, 
			rgba(255,255,255,1) 0%, 
			rgba(255,255,255,0) 100%
		);
}

This will make the text appear slightly faded out.

Let’s style the text elements:

.content h2 {
	font-weight: 300;
	font-size: 4em;
	padding: 0 0 10px;
	color: #333;
	margin: 0 1% 40px;
	text-align: left;
	box-shadow: 0 10px 0 rgba(0,0,0,0.02);
	text-shadow: 0 0 2px #fff;
}

.no-js .content h2 {
	padding: 40px 1% 20px;
}

.content p {
	font-size: 1.2em;
	line-height: 1.6;
	font-weight: 300;
	padding: 5px 8%;
	text-align: justify;
}

Finally, let’s add some media queries. Without JavaScript enabled, we don’t want to show the menu anymore from 800 pixels on. This is just an example of how we can control these elements under certain conditions.

The last media query will resize the font a bit, for smaller devices. Using ems makes things easier for these cases.


@media screen and (max-width: 800px){
	.no-js .menu-panel {
		display: none;
	}

	.no-js .container {
		padding: 0;
	}
}

@media screen and (max-width: 400px){
	.menu-panel,
	.content {
		font-size: 75%;
	}
}

And that was all the style! Let’s add some funky dance moves, shall we?

The JavaScript

We will start by caching some elements and initializing the BookBlock plugin. We need to set some things after each page flip, mainly the current item’s index and the jScrollPane behavior. This is specified in the onEndFlip callback passed to the BookBlock.

var $container = $( '#container' ),

	// the element we will apply the BookBlock plugin to
	$bookBlock = $( '#bb-bookblock' ),

	// the BookBlock items (bb-item)
	$items = $bookBlock.children(),

	// index of the current item
	current = 0,

	// initialize the BookBlock
	bb = $( '#bb-bookblock' ).bookblock( {
		speed : 800,
		perspective : 2000,
		shadowSides	: 0.8,
		shadowFlip	: 0.4,
		// after each flip...
		onEndFlip : function(old, page, isLimit) {
			
			// update the current value
			current = page;

			// update the selected item of the table of contents (TOC)
			updateTOC();

			// show and/or hide the navigation arrows
			updateNavigation( isLimit );

			// initialize the jScrollPane on the content div for the new item
			setJSP( 'init' );

			// destroy jScrollPane on the content div for the old item
			setJSP( 'destroy', old );

		}
	} ),
	// the navigation arrows
	$navNext = $( '#bb-nav-next' ),
	$navPrev = $( '#bb-nav-prev' ).hide(),

	// the table of content items
	$menuItems = $container.find( 'ul.menu-toc > li' ),

	// button to open the TOC
	$tblcontents = $( '#tblcontents' ),

	transEndEventNames = {
		'WebkitTransition': 'webkitTransitionEnd',
		'MozTransition': 'transitionend',
		'OTransition': 'oTransitionEnd',
		'msTransition': 'MSTransitionEnd',
		'transition': 'transitionend'
	},

	// transition event name
	transEndEventName = transEndEventNames[Modernizr.prefixed('transition')],

	// check if transitions are supported
	supportTransitions = Modernizr.csstransitions;

First, let’s bind the events to some of the elements initialized before. Also, we need to initialize the jScrollPane for the first (current) item.

function init() {

	// initialize jScrollPane on the content div of the first item
	setJSP( 'init' );
	initEvents();

}

Since we’ll eventually need to initialize, reinitialize and destroy the jScrollPane, let’s define a function for this:

function setJSP( action, idx ) {
		
	var idx = idx === undefined ? current : idx,
		$content = $items.eq( idx ).children( 'div.content' ),
		apiJSP = $content.data( 'jsp' );
	
	if( action === 'init' && apiJSP === undefined ) {
		$content.jScrollPane({verticalGutter : 0, hideFocus : true });
	}
	else if( action === 'reinit' && apiJSP !== undefined ) {
		apiJSP.reinitialise();
	}
	else if( action === 'destroy' && apiJSP !== undefined ) {
		apiJSP.destroy();
	}

}

We will need to bind several events:

  1. We will call the BookBlock’s next() and prev() methods when we click the navigation arrows or when swiping the page
  2. The TOC will be shown / hidden when we click the $tblcontents (menu) button
  3. We will call the BookBlock jump() method when we click a TOC item
  4. The jScrollPane will be initialized on window resize

So, here we go:

function initEvents() {

	// add navigation events
	$navNext.on( 'click', function() {
		bb.next();
		return false;
	} );

	$navPrev.on( 'click', function() {
		bb.prev();
		return false;
	} );
	
	// add swipe events
	$items.on( {
		'swipeleft'		: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.next();
			return false;
		},
		'swiperight'	: function( event ) {
			if( $container.data( 'opened' ) ) {
				return false;
			}
			bb.prev();
			return false;
		}
	} );

	// show TOC
	$tblcontents.on( 'click', toggleTOC );

	// click a menu item
	$menuItems.on( 'click', function() {

		var $el = $( this ),
			idx = $el.index(),
			jump = function() {
				bb.jump( idx + 1 );
			};
		
		current !== idx ? closeTOC( jump ) : closeTOC();

		return false;
		
	} );

	// reinit jScrollPane on window resize
	$( window ).on( 'debouncedresize', function() {
		// reinitialise jScrollPane on the content div
		setJSP( 'reinit' );
	} );

}

The navigation arrows’ visibility will depend on the current page. If we are on the first page we’ll only see the next arrow and if we are on the last page we’ll only see the previous arrow:

function updateNavigation( isLastPage ) {
	
	if( current === 0 ) {
		$navNext.show();
		$navPrev.hide();
	}
	else if( isLastPage ) {
		$navNext.hide();
		$navPrev.show();
	}
	else {
		$navNext.show();
		$navPrev.show();
	}

}

When we open the TOC we want to hide the navigation arrows and we just show them again after closing the TOC.
We will animate the sidebar menu with a CSS transition. If there’s no support for transitions then a simple show/hide fallback will be used:

function toggleTOC() {
	var opened = $container.data( 'opened' );
	opened ? closeTOC() : openTOC();
}

function openTOC() {
	$navNext.hide();
	$navPrev.hide();
	$container.addClass( 'slideRight' ).data( 'opened', true );
}

function closeTOC( callback ) {

	$navNext.show();
	$navPrev.show();
	$container.removeClass( 'slideRight' ).data( 'opened', false );
	if( callback ) {
		if( supportTransitions ) {
			$container.on( transEndEventName, function() {
				$( this ).off( transEndEventName );
				callback.call();
			} );
		}
		else {
			callback.call();
		}
	}

}

Phew! 🙂 That’s it! I hope you enjoyed this tutorial and find it useful!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 151

Comments are closed.
  1. It’s a very nice new Js/css effect (but there is a little lag on my chrome). But very good job thanks 😉

  2. Really like it! 🙂

    Just a fix:

    When you are in first page, then click TOC button to show it and then click it again to hide, the $navPrev arrow appears.

    It could be fixed in closeTOC function and should be something like:

    updateNavigation();

    instead of:

    $navNext.show(); $navPrev.show();

    Hope it helps! Nice work!
    Fran

  3. It would be perfect, if there were two pages and not one to read. Full screen page is unreadable – too long lines. So try a two page version and you will hear the Christmas bells ringing for you.

  4. Very good tutorial.. 🙂

    But there is a problem. I added more topics on the table of contents. It goes beyond the browser’s height. It is not scrollable. Please fix it.
    Thank you.. 🙂

    • Modified CSS – Should be scrollable now 🙂

      .js .menu-panel {
      position: absolute;
      left: -240px;
      overflow:scroll;
      }

    • I would love to see the same concept/layout with a fade effect instead of flip too! That would be amazing!

    • Don’t get me wrong… this is pure magic already. But for my type of website/customers the Book effect might be a bit to fancy and overwhelming 🙂 Nice Job Mary Lou! You keep surprising us!

  5. Hey, I have problem. When I put image beetwen text in first page, scroll will not appear. When I click next arrow and prev arrow. Then everything is fine and I can scroll first page. Little help?

    • I fix it. In page.js I added function “autoReinitialise : true” in line 113 to jScrollPane. Now I can scroll first page with images.

  6. There seems to be an issue with swiping on the iPad (the only device I tested it on). It’s very difficult to get the page to turn when swiping. It seems the page wants to scroll as soon as you touch it and the swipe is not recognized, it’s very frustrating. I haven’t had time to look at the code but it the swipe worked correctly this would be a killer piece of kit.

    • Please let me know if you get an answer to this. I would like to show the toc at startup as well.

  7. Hy, i’ve problem with the double tap on iphone… it seems everything doesn’t works after a double tap 🙁

  8. This is a good idea for my UI theme site. But, I have created a book with 15 chapters, and over 40000 words per chapter. How can I make 15 chaps to load step by step with full effect like this plugin? (as 1 chapter per page?)

    I think, If 40000 words in a page at the same time can make the html file be bigger.

    I know something about load extenal contents from other html files, but it still not good for loading speed

  9. Really great Tutorial. Thnx a lot!
    To fix the scrolling in the leftmenubar I did the following:
    edit the custom.css to:
    .menu-panel { background: #f1103a; width: 240px; height: 100%; position: fixed; z-index: 1000; top: 0; left: 0; text-shadow: 0 1px 1px rgba(0,0,0,0.1); overflow: hidden; } .menu-panel-scroll { overflow-y: scroll; width:256px; height: 100%; }

    AND add in the index.html a right after
    don’t forget the closing

    Hope that helps…

  10. I have converted this into a wordpress theme so your lastest posts will be the next flipped page… if anyone in interested.

    Heres a preview

    jonathanbell.com.au/blog

  11. This effect is pretty cool, but is there any way to remove the Scrollbar? , i remove de but still in the web when i refresh. I think is in the JS, but to be honest i don’t know exactly.

    Thankyou.

  12. Thanks for the tutorial, but why is the page flipping down the middle of the full page? For me, that ruins the illusion completely. The way it works now is like folding a full page in half, flipping it, and then somehow another folded page magically appears. If the object of the excercise is to simulate a flipbook, this doesn’t quite cut it. Since if is essentially a one-page flipper, wouldn’t it be better if the fixed edge was along the left side of the page?

  13. Thank you Mary Lou – have enhance my simply 2013 MindNode calendar with your stuff for those people who need more than one page, more then 12 weeks and definitive 4 annual quarters … http://oyen.de/2013 – Partly the code is hanging on IO devices – seems like viewport and scalable values are not fitting well with my SVG graphics and/or my code interpretation 😉 – Fixing work for 2013.

  14. Hi, thanks for this amazing layout. One question. Can I use it in commercial use?
    Thanks.

  15. Hi Mary Lou,

    This is the great tutorial! I like it.

    I am trying to replace my personal site with this PageFlip but I got a problem
    when I have managed to add input tag (type =submit; button) into page.
    I got not response at all with my button. I’m using asp.net 4 web form.
    My scripts as following:

    I’m thinking you might make the pages display only.
    Are you able to point me how to enable this button?

    Thank you in advance,
    Suryamin

  16. Hi Mary Lou,

    Someone might have/probably has already mentioned this, but it doesn’t actually work in Opera browser. The pages change, but the effect doesn’t play.

    Thanks for all this amazing work.

    Jen

  17. Didn’t see anyone mention this, but when you first load it and choose something from the TOC the vertical scroll bar doesn’t work if the content goes below the fold (at least in the newest FF). It isn’t until you click the left and right arrow nav does it start to let you scroll.

    Anyone else getting this and know how to correct this?

    • I added a blank div after the last item I had and it got rid of the above problem. Basically I noticed the last item doesn’t show the scroll bar if you go to it directly from the TOC (not clicking any arrows). Adding a new div at the end (so if you have five “pages” add a

      after the last item five div. Fixed the issue.

  18. The page looks beautiful and attractive. The effect takes very less time that’s what making it so special I think.

  19. Same comment as Jen (Not working in Chrome version: Version 24.0.1312.56 m (win32 machine), Works fine (effect plays) in FFox V 18. I like the idea very much. Cheers GH

  20. Hy Marilou!!! Great article!!!
    Here is my question: How do I make each page has a different URL? ¿Is it possible? What about history.js?

    Lots of thanks!!!

  21. Hi! Is there a way to make the page flip more realistic, like a magazine-style page flip? Thank you!

  22. Hii…firstly i wanna say that ur web info is awesme provided in this website..I wanna know one thing…IN this Tutorial(fullscreen pageflip) how we can change the color of scroller in page…actuay the scrolloer provided by u is not seen’s clear…..due to same color as background…i am waiting for your reply;;;;;;;;;;THANKS

  23. How can i modify the Fullscreen Pageflip Layout turning 100% the sheet instead 50%? Thanks!!!