Responsive Content Navigator with CSS3

Today we want to show you how to create a responsive, css-only content navigator.


View demo Download source

Today we’ll show you how to create a content navigator with CSS only. The idea is to have several slides or content layers that we’ll show or hide using the :target pseudo-class. With CSS transitions we can make the slides appear in a fancy way. We’ll also make the whole thing responsive.

The images used in the demo are by super-talented Karrie Nodalo. Check out her Flickr photostream. The images are licensed under the Creative Commons Attribution 2.0 Generic License.

Please note: the result of this tutorial will only work as intended in browsers that support the CSS properties in use.

In the following we’ll be going through demo 3.
Let’s do it!

The Markup

For each of the different slides that will either contain a set of links, sub-slides or some content, we will have a division with the class cn-slide. For the sub-slide that will contain two link boxes in a row we’ll add another class called cn-slide-sub. The content slides will simply have the general class cn-slide like all the others.

There will be one main slide that will have links to three sub-slides. Then, each of the sub-slides will have four links, each one pointing to a content slide. So, all together we have one main slide, three sub-slides and twelve content slides:

<section class="cn-container">
	<div class="cn-slide" id="slide-main">
		<h2>Explore this</h2>
			<a href="#slide-1">Philosphy</a>							
			<a href="#slide-2">Science</a>						
			<a href="#slide-3">Literature</a>
	<!-- Slide 1 and sub-slides -->
	<div class="cn-slide ts-slide-sub" id="slide-1">
		<a href="#slide-main" class="cn-back">Back</a>
			<a href="#slide-1-1">Epistemology</a>							
			<a href="#slide-1-2">Metaphysics</a>
			<a href="#slide-1-3">Aesthetics</a>
			<a href="#slide-1-4">Ethics</a>
	<div class="cn-slide" id="slide-1-1">
		<a href="#slide-1" class="cn-back">Back</a>
		<div class="cn-content">
			<p>Some text</p>
	<div class="cn-slide" id="slide-1-2">
		<!-- ... -->
	<div class="cn-slide" id="slide-1-3">
		<!-- ... -->
	<div class="cn-slide" id="slide-1-4">
		<!-- ... -->
	<!-- Slide 2 and Sub-slides -->
	<!-- ... -->


The sub-slides and the slides with content will have a link back to their previous “layer”. So, all the content slides of the “Philosophy” part will link back to “slide-1″ which is the sub-slide of “Philosophy”.

Let’s take a look at the CSS!


The main container will have a min-width and a max-width which will allow us using a percentage for the width but guarantee that it will not become too big or too small:

	width: 60%;
	min-width: 300px;
	max-width: 820px;
	margin: 10px auto 0 auto;
	text-align: left;
	position: relative;

The main headline will be placed absolutely and we will make it fade in and scale up when “opening” a slide. The transition will have a delay because we want it to appear after all the link boxes:

.cn-container h2{
	font-family: 'BebasNeueRegular', 'Arial Narrow', Arial, sans-serif;
	color: #fff;
	position: absolute;
	z-index: 10000;
		0 0 1px rgba(255,255,255,0.8), 
		1px 1px 1px rgba(0,0,0,0.2);
	font-size: 80px;
	line-height: 80px;
	top: 0px;
	right: 0px;
	white-space: nowrap;
	opacity: 0;
	transform: scale(0);
	transition: all 0.5s linear 0.7s;
.cn-container .cn-slide:target h2{
	opacity: 1;
	transform: scale(1);

The slides will also be placed absolutely. We’ll set the initial opacity of the slides to 0 and show it once we know that it’s the target:

	text-align: center;
	position: absolute;
	left: 0px;
	padding-top: 80px;
	margin: 0 5%;
	width: 90%;
	opacity: 0;
	opacity: 1;
	z-index: 1000;

Note that in some other demos we are using the transitions on the slides. Here we will use sequential transitions on the link elements.

Now, let’s take a look at the link elements. They will all have a single background image which we will later define for each element. By default, we’ll give 1.jpg as the background image. Initially, the opacity and scale will be 0. We add a transition for the two properties and also for the box-shadow which we need when we hover the element:

.cn-slide nav a{
	text-align: left;
	display: block;
	font-family: 'BebasNeueRegular', 'Arial Narrow', Arial, sans-serif;
	border: 8px solid #fff;
	padding: 2%;
	font-size: 66px;
	letter-spacing: 7px;
	text-shadow: 0px 5px 0px rgba(182,105,135,0.4);
	color: #fff;
	line-height: 66px;
	outline: none;
	margin: 0 0 10px 0;
	box-shadow: 1px 1px 3px rgba(0,0,0,0.1);
	background: #fff url(../images/1.jpg) no-repeat center center;
	background-size: 100%;
	background-clip: padding-box;
	opacity: 0;
	transform: scale(0);
		opacity 0.4s linear, 
		transform 0.4s linear, 
		box-shadow 0.3s ease-in-out;
.cn-slide nav a:hover{
	box-shadow: 1px 2px 4px rgba(0,0,0,0.2);

The target slide’s link elements will fade in and scale up to 1:

.cn-slide:target nav a{
	opacity: 1;
	transform: scale(1);

Let’s define a transition delay for the elements, so that they appear one after the other:

.cn-slide nav a:nth-child(2){
	transition-delay: 0.3s, 0.3s, 0s;
.cn-slide nav a:nth-child(3){
	transition-delay: 0.6s, 0.6s, 0s;
.cn-slide nav a:nth-child(4){
	transition-delay: 0.9s, 0.9s,0s;

As you can see, the last delay will be of 0 seconds since this is the delay for the box-shadow transition, the one that get’s applied on hover.

The link elements in the sub-slides will have a width of 42% because we want two to fit into a row:

.cn-slide-sub nav a{
	width: 42%;
	display: inline-block;
	font-size: 40px;

To create a little gap between the elements, we’ll give a right margin to the odd children:

.cn-slide-sub nav a:nth-child(odd){
	margin-right: 5px;

The slides with a content area are the “final” ones and we’ll also add a transition to that division:

	background: #80B8CE url(../images/1.jpg) no-repeat center center;
	background-size: cover;
	text-align: left;
	padding: 20px 20px 5px;
	box-shadow: 1px 1px 3px rgba(0,0,0,0.1);
	border: 8px solid #fff;
	margin-top: 5px;
	cursor: pointer;
	opacity: 0;
	transform: scale(0);
	transition: all 0.6s linear;
.cn-slide:target .cn-content{
	opacity: 1;
	transform: scale(1);

Each content area will, just like their corresponding link elements, have a background image that we will define later.

The paragraphs will also have a transition which will be applied when we click on the content. With the :active pseudo-class we can let the user view the background of the content div by clicking on it (and keeping it clicked):

.cn-content p{
	line-height: 24px;
	text-shadow: 1px 1px 1px rgba(255,255,255,0.9);
	color: rgba(103,59,77,0.9);
	padding: 15px 20px;
	margin-bottom: 10px;
	background: rgba(255,255,255,0.96);
	font-style: italic;
	border-top: 7px solid rgba(103,59,77,0.6);
	user-select: none;
	transform: scale(1);
	transition: all 0.3s linear;
.cn-content:active p{
	transform: scale(0);

The back arrow will be sliding in from the left. We set it to -100px initially and to 3px when a slide is the target:

	outline: none;
	text-indent: -9000px;
	width: 49px;
	height: 42px;
	background: transparent url(../images/arrow.png) no-repeat center center;
	position: absolute;
	top: 22px;
	left: -100px;
	cursor: pointer;
	opacity: 0;
	transition: all 0.4s ease-in-out 1s;
.cn-slide:target .cn-back{
	left: 3px;
	opacity: 1;

Now, let’s define the background images for each link element. With the very helpful attribute selector, we can specify which element will have what background image. We will look at what the href value is and define the image:

/* Main Items */
.cn-slide nav a[href="#slide-1"]{
	background-image: url(../images/1.jpg);
.cn-slide nav a[href="#slide-2"]{
	background-image: url(../images/2.jpg);
.cn-slide nav a[href="#slide-3"]{
	background-image: url(../images/3.jpg);

/* Items and sub-items of slide 1*/
.cn-slide nav a[href="#slide-1-1"],
#slide-1-1 .cn-content {
	background-image: url(../images/4.jpg);
.cn-slide nav a[href="#slide-1-2"],
#slide-1-2 .cn-content {
	background-image: url(../images/5.jpg);
.cn-slide nav a[href="#slide-1-3"],
#slide-1-3 .cn-content {
	background-image: url(../images/6.jpg);
.cn-slide nav a[href="#slide-1-4"],
#slide-1-4 .cn-content {
	background-image: url(../images/7.jpg);

/* Items and sub-items of slide 2 ... */
/* ... */

/* Items and sub-items of slide 3 ... */
/* ... */

Finally, we will define some media queries to fix the layout when we view this on a smaller screen. You can of course define the standard media queries for common devices but here we will simply check when our liquid layout breaks and define a rule for that “breaking-point”. In our case, that’s 1060 pixel and 900 pixel. We will adjust the font size and allow the sub-slides to have one item in a row:

@media screen and (max-width: 1060px){
	.cn-slide-sub nav a{
		font-size: 28px;
@media screen and (max-width: 900px){
	.cn-container h2{
		font-size: 48px;
		line-height: 95px;
	.cn-slide nav a{
		font-size: 38px;
	.cn-slide-sub nav a{
		width: auto;
		font-size: 36px;
		display: block;
	.cn-slide-sub nav a:nth-child(odd){
		margin-right: 0px;


  1. Demo 1: Slide from left
  2. Demo 2: Fade in sequentially
  3. Demo 3: Scale up sequentially
  4. Demo 4: Slide from top
  5. Demo 5: Rotate sequentially

And that’s it! I hope you enjoyed this tutorial and find it useful!
There are many possibilities for the transitions, just experiment!

View demo Download source


Tagged with:

Mary Lou (Manoela Ilic) is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by


Related Articles

Feedback 53

Comments are closed.
  1. 1

    i love the look of this navigator, but on a usability point of view?
    isn’t it a bit strange to make something responsive(available on mobile phone). But make it so that you can only read te text on hover? There could be something that i’m missing. But in my eyes thats contradicting the responsive part.

    • 2

      Hi Yannick,
      thank you for your feedback! Yes, that’s just a fancy hover effect which I’ve added in the first demo. The other demos don’t have it. One could also add that to a media query so that it’s readable for users with a smaller screen (mobile).
      Thanks again,

  2. 3

    You’re completely right. I shouldn’t have watched only one demo.
    i’m sorry for jumping to conclusions :)

  3. 5

    This looks very promising! I haven’t checked out the source or anything yet, but would it be possible to just go back a normap page when you hit the backbutton? Thanks in advance!

  4. 8

    Looks wonderful! The only thing I always wonder about with transitions like this that all keep you on the same HTML page….how do they translate in SEO? I suppose it is all relative, and if you have enough content you can get crafty and have multiple files….cant wait for the day that search engines will not judge based on # of files and more on rich content. Either way thanks for helping the progress of design! Kudos :)

  5. 10

    its greats tutorial.. but i have a problem..when adding a footer..
    in normal view.. my footer always in bottom of page.. but in mobile view the footer always in the middle content..
    footer {
    position : absolute;
    bottom: 0;
    have I missed something?

  6. 11

    How can I get it to load automatically rather than having to select a link to get it to appear?

    • 12

      hi Ste..

      To load automatically.. just add Jquery and add some script at body tag

      function goToAnchor() {
      location.href = “yourpage.html#slide-main”;

      maybe this is can help u ^_^

    • 14

      hi there. can u give me the script that made this slideshow load automatically, rahter than clikin in demo link…

  7. 15

    Thnx! For the excellent tutorial. This takes me to a higher level of css3. Thank you very much.

    P.S. You did write Philosophy on the main page without the second ‘o’. On purpose?

    • 16

      Thank you Marc! No, it was not on purpose, it was just one of my serious Dyslexia days, I guess :) Thanks for pointing that out! Cheers, ML

  8. 17

    A question that I couldn’t resolve, if more links were added to the div #slide-main, why did the browser automatically scroll a certain part of the website away when the links are clicked. It can also be seen from the demo, the codrops bar has been cut off and only visible if scrolled back up. May I know is there any solution for it?

  9. 21

    someone can help me to load it automatically? script on top comments doesnt work for me or i do stupid ^^

  10. 22

    The demo looks awesome! But when i download the sources, there is nothing but the empty background and the header? Any ideas how to fix this, please?

    • 23

      I found the solution by myself. There is nothing to see, until i load the url with “#slide-main”.

    • 24

      i dont want to use the ‘slide main’ tag in the url. any other way that it will be display automaticly?

  11. 26

    Is there any fallback measure for IE8 ?

    Does everyone else just not build to support IE8 anymore?

  12. 28

    i also DO NOT want to use “#slide-main” in the URL because the gallery is not at the top of the page and therefore “#slide-main” can be loaded upon pageload because the browser would jump to where the gallery is.. i want to the browser to load the website and show the gallery by default without jumping to its anchor tag.. how can i do that? please help :)

  13. 30

    I followed your other tutorials and implemented your other tuts slideshow-with-jmpress-js etc. I am able to implement them in WordPress responsive themes successfully.

    But this ” responsive-content-navigator ” is not showing up. The top navigation links were shown up. the actual content not shown up

    Can you please tell me if I am missing something here? There are no jquery files in the downloaded zip. Is that I am missing?


  14. 32

    Hey! I have one question! I want to use one of de sliders but how can i do to get the slider load without having to click in the link in demo? Thanks u!

Comments are closed.