CSS-Only Responsive Layout with Smooth Transitions

A tutorial on how to create a 100% width and height smooth scrolling layout with CSS only. Using a radio button navigation and sibling combinators we will trigger transitions to the respective content panels, creating a "smooth scrolling" effect.

CSS-Only Responsive Layout with Smooth Transitions

View demo Download source

In this tutorial we will create a responsive 100% width/height layout with some smooth page transitions. The idea is to have some content panels and a navigation which will allow us to navigate between the panels. We’ll use radio buttons for the navigation and animate the content to the right position with a transition, creating a “smooth scrolling” effect. This layout idea could be useful for web pages or web apps where the content should be strictly the size of the screen (width and height). Note that this is, of course, highly experimental and just a proof-of-concept.

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

Note that we will exclude vendor prefixes in this tutorial. You will, of course, find them in the files.

The Markup

The structure will be consist of a main container with the class st-container which will contain the radio buttons and link, and the wrapper with the class st-scroll for the panels. Each panel will have some content elements:

<div class="st-container">
			
	<input type="radio" name="radio-set" checked="checked" id="st-control-1"/>
	<a href="#st-panel-1">Serendipity</a>
	
	<input type="radio" name="radio-set" id="st-control-2"/>
	<a href="#st-panel-2">Happiness</a>
	
	<input type="radio" name="radio-set" id="st-control-3"/>
	<a href="#st-panel-3">Tranquillity</a>
	
	<input type="radio" name="radio-set" id="st-control-4"/>
	<a href="#st-panel-4">Positivity</a>
	
	<input type="radio" name="radio-set" id="st-control-5"/>
	<a href="#st-panel-5">Passion</a>
	
	<div class="st-scroll">

		<section class="st-panel" id="st-panel-1">
			<div class="st-deco" data-icon="H"></div>
			<h2>Serendipity</h2>
			<p>Banksy adipisicing eiusmod banh mi sed...</p>
		</section>
		
		<section class="st-panel st-color" id="st-panel-2">
			<!-- ... -->
		</section>
		
		<!-- ... st-panel-3, st-panel-4, st-panel-5 -->

	</div><!-- // st-scroll -->
	
</div><!-- // st-container -->

What we want to do is basically move the panel wrapper by changing it’s top value and bringing the respective panel into the viewport. This we can do by selecting the sibling of a checked radio button, the st-scroll division, with the sibling combinator and target the correct panel inside. Because of this technique, we need to keep the radio buttons in the same level like the st-scroll and on top of the links (they will be invisible though, since we’ll give them 0 opacity). For being able to select the correct panel, we give IDs to them and to the radio buttons.

The reason why we use links and not, like usually, labels, is that we want to be able to create some kind of “fallback” for non-supportive browsers (sibling combinators don’t work in older browsers). The links have the href value of the panels’ IDs, so for the fallback, we’ll simply hide the radio buttons, making the links clickable which will make it possible to “jump” to the right panel.

I know that it might be desirable to have the respective hashtag in the URL once we click on an input but with this technique that we’ll use for this layout it is not (yet) possible with CSS-only (parent selectors would be really nice to have!).

OK, let’s style this thing!

The CSS

Now, how do we make this layout flexible and its panels exactly the size of the screen? The trick is to make the main container absolute with a width and height of 100% while setting the panels and their wrapper to position relative. But they will also have a width and height of 100%. This will make each panel be exactly the size of the screen (since the main container and the panel wrapper are) but allow an overflow of the content, stacking the panels in the classic way.

Since we’ll do the content navigation by animating the panel wrapper, we’ll set the body overflow to hidden:

body {
	overflow: hidden;
}

Let’s take a look at the main container’s style:

.st-container {
	position: absolute;
	width: 100%;
	height: 100%;
	top: 0;
	left: 0;
	font-family: 'Josefin Slab', 'Myriad Pro', Arial, sans-serif;
}

We’ll put the “navigation” at the bottom of the page by giving it a fixed position. Note that we are setting the same width and height for both, the input and the link. The idea is to overlay the radio button on the link elements so that they are clickable, but giving them 0 opacity so that they are not visible. Ans because of that it’s also important that we set the z-index of the radio buttons higher than the one of the link elements:

.st-container > input,
.st-container > a {
	position: fixed;
	bottom: 0px;
	width: 20%;
	cursor: pointer;
	font-size: 16px;
	height: 34px;
	line-height: 34px;
}

.st-container > input {
	opacity: 0;
	z-index: 1000;
}

.st-container > a {
	z-index: 10;
	font-weight: 700;
	background: #e23a6e;
	color: #fff;
	text-align: center;
	text-shadow: 1px 1px 1px rgba(151,24,64,0.2);
}

Since we are using percentages to spread the links and inputs across the width of the screen, we might get into some rounding trouble that will make some gaps appear. In order to hide that, well use a pseudo element that will be under the links and inputs. It will have the same background color like the link elements:

.st-container:before {
	content: '';
	position: fixed;
	width: 100%;
	height: 34px;
	background: #e23a6e;
	z-index: 9;
	bottom: 0;
}

Our links and inputs are still not positioned, so let’s give them their respective left values:

#st-control-1, #st-control-1 + a {
	left: 0;
}

#st-control-2, #st-control-2 + a {
	left: 20%;
}

#st-control-3, #st-control-3 + a {
	left: 40%;
}

#st-control-4, #st-control-4 + a {
	left: 60%;
}

#st-control-5, #st-control-5 + a {
	left: 80%;
}

As you can see, we are using the adjacent sibling selector to “reach” the direct sibling of an input which is the related link element.

Using the same principle, we will define a “selected” state for the link elements. Once we click on an input, we will give the sibling link element a different background color:


.st-container > input:checked + a,
.st-container > input:checked:hover + a{
	background: #821134;
}

Let’s also add a little triangle using the pseudo-class :after and give it the same color:

.st-container > input:checked + a:after,
.st-container > input:checked:hover + a:after{
	bottom: 100%;
	border: solid transparent;
	content: '';
	height: 0;
	width: 0;
	position: absolute;
	pointer-events: none;
	border-bottom-color: #821134;
	border-width: 20px;
	left: 50%;
	margin-left: -20px;
}

You can check out CSS Arrow, Please! if you want a quick way to create those arrows.

Let’s also define a hover state for the link element:

.st-container > input:hover + a{
	background: #AD244F;
}

.st-container > input:hover + a:after {
	border-bottom-color: #AD244F;
}

The wrapper for the panels and the panels will have relative position and we’ll give them a width and height of 100%. The panel wrapper will also get a top and left position of 0 while we don’t touch the the values for the panels (it will be auto).

The transition will be for animating the transform property value to the respective position:

.st-scroll,
.st-panel {
	position: relative;
	width: 100%;
	height: 100%;
}

.st-scroll {
	top: 0;
	left: 0;
	transition: all 0.6s ease-in-out;
	
	/* Let's enforce some hardware acceleration */
	-webkit-transform: translate3d(0, 0, 0);
	-webkit-backface-visibility: hidden;
}

.st-panel{
	background: #fff;
	overflow: hidden;
} 

Although I usually don’t add any vendor prefixed properties, I did wanted to leave these Webkit ones since they will help tremendously in creating a smooth experience.

Let’s define the positions for the st-scroll wrapper for each checked radio button. Since we know that every panel has a height of 100% we know the exact positions. We will use the transform property to translate the panel wrapper in the Y-dimension (up and down):

#st-control-1:checked ~ .st-scroll {
	transform: translateY(0%);
}
#st-control-2:checked ~ .st-scroll {
	transform: translateY(-100%);
}
#st-control-3:checked ~ .st-scroll {
	transform: translateY(-200%);
}
#st-control-4:checked ~ .st-scroll {
	transform: translateY(-300%);
}
#st-control-5:checked ~ .st-scroll {
	transform: translateY(-400%);
}

Now, let’s style the content elements. For the upper triangle with the icon we’ll simply rotate and translate the st-deco division. We’ll position it in the center top of the screen by setting the top to 0 and the left to 50% while giving it a left margin of minus half of it’s width. Translating it -50% will make only half of the box appear thus creating a triangle:

.st-deco{
	width: 200px;
	height: 200px;
	position: absolute;
	top: 0px;
	left: 50%;
	margin-left: -100px;
	background: #fa96b5;
	transform: translateY(-50%) rotate(45deg);
}

For the icon we’ll use the RaphaĆ«l Icon-Set via @font-face and the data-attribute/pseudo-class technique. The content of the pseudo-element :after will be the data-icon value that we’ve set in the HTML for that element. Note, that we need to rotate it back into the opposite direction of the parent element in order to have it back to “normal”:

[data-icon]:after {
    content: attr(data-icon);
    font-family: 'RaphaelIcons';
    color: #fff;
	text-shadow: 1px 1px 1px rgba(151,24,64,0.2);
	position: absolute;
	width: 200px;
	height: 200px;
	line-height: 200px;
	text-align: center;
	font-size: 90px;
	top: 50%;
	left: 50%;
	margin: -100px 0 0 -100px;
	transform: rotate(-45deg) translateY(25%);
}

The heading will be placed in the center of the screen with a negative top margin in order to “pull” it up a bit:

.st-panel h2 {
	color: #e23a6e;
	text-shadow: 1px 1px 1px rgba(151,24,64,0.2);
	position: absolute;
	font-size: 54px;
	font-weight: 900;
	width: 80%;
	left: 10%;
	text-align: center;
	line-height: 50px;
	margin: -70px 0 0 0;
	padding: 0;
	top: 50%;
	-webkit-backface-visibility: hidden;
}

Every time we click on an input, we want the respective heading to run an animation. It will animate a bit from the top and fade in at the same time. In order to select the correct heading, we will use the general sibling combinator:

#st-control-1:checked ~ .st-scroll #st-panel-1 h2,
#st-control-2:checked ~ .st-scroll #st-panel-2 h2,
#st-control-3:checked ~ .st-scroll #st-panel-3 h2,
#st-control-4:checked ~ .st-scroll #st-panel-4 h2,
#st-control-5:checked ~ .st-scroll #st-panel-5 h2{
	animation: moveDown 0.6s ease-in-out 0.2s backwards;
}

@keyframes moveDown{
	0% { 
		transform: translateY(-40px); 
		opacity: 0;
	}
	100% { 
		transform: translateY(0px);  
		opacity: 1;
	}
}

The paragraph will have the following style:

.st-panel p {
	position: absolute;
	text-align: center;
	font-size: 16px;
	line-height: 22px;
	color: #8b8b8b;
	z-index: 2;
	padding: 0;
	width: 50%;
	left: 25%;
	top: 50%;
	margin: 10px 0 0 0;
	-webkit-backface-visibility: hidden;
}

While the heading of a panel will move down, the paragraph will move up:

#st-control-1:checked ~ .st-scroll #st-panel-1 p,
#st-control-2:checked ~ .st-scroll #st-panel-2 p,
#st-control-3:checked ~ .st-scroll #st-panel-3 p,
#st-control-4:checked ~ .st-scroll #st-panel-4 p,
#st-control-5:checked ~ .st-scroll #st-panel-5 p{
	animation: moveUp 0.6s ease-in-out 0.2s backwards;
}

@keyframes moveUp{
	0% { 
		transform: translateY(40px); 
		opacity: 0;
	}
	100% { 
		transform: translateY(0px);  
		opacity: 1;
	}
}

In order to make out layout a bit more fun, we’ll add a color class and “invert” the colors for those panels and its content elements:

/* Colored sections */

.st-color,
.st-deco{
	background: #fa96b5;
}
.st-color [data-icon]:after {
	color: #fa96b5;
}
.st-color .st-deco {
	background: #fff;
}
.st-color h2 {
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
} 
.st-color p {
	color: rgba(255,255,255,0.8);
}

Last, but not least, we will add some media queries to control the position and font size of the elements for smaller screens:

@media screen and (max-width: 520px) {
	.st-panel h2 {
		font-size: 42px;
	}
	
	.st-panel p {
		width: 90%;
		left: 5%;
		margin-top: 0;
	}
	
	.st-container > a {
		font-size: 13px;
	}
}

@media screen and (max-width: 360px) {
	.st-container > a {
		font-size: 10px;
	}
	
	.st-deco{
		width: 120px;
		height: 120px;
		margin-left: -60px;
	}
	
	[data-icon]:after {
		font-size: 60px;
		transform: rotate(-45deg) translateY(15%);
	}
}

For older browsers that don’t support some of the selectors we want to fall back to the classic “target jump”. We can do that by altering some of the style (simple.css). In particular, we will set the overflow of the body to “auto” and hide the inputs, making the link elements clickable (as href they have the ID of the respective panels):

body {
	overflow: auto;
}
.st-container > input{
	display: none;
}

And that’s all! I hope you enjoyed this tutorial and find it inspiring!

View demo Download source

Previous:
Next:

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

Website: http://tympanus.net/

Related Articles

Feedback 91

Comments are closed.
  1. 1

    I have got a question, how am i suppose to scroll the content, if i put the radio buttons in a separate div box, like this:

    div class=”checkboxex”
    input type=”radio” name=”radio-set” class=”rd2″ value=”Helo world” /
    input type=”radio” name=”radio-set” class=”rd3″ value=”Helo world” /
    /div
    /code

    what will this line become?

    #st-control-5:checked ~ .st-scroll
    {
    }

  2. 2

    Hi Mary Lou.

    Thanks for the tutorials. I made a test run on this template, and somehow back in the Europe and Us some people could not see the transition correctly and also the (viewing size) size on the page. Please help…
    http://www.illusionsign.com

    what am I doing wrong?

  3. 3

    Very interesting technic, but how we can scroll using a link outside navigation bar?

    inside my page content i have

    title

    Text Linnk check this out

    how can i scroll to section-5 ????

  4. 4

    Hi,
    What a great job you did, I find it really cool.
    But it seems that there is a difference between the .zip file and the demo page when you check it from ipad and iphone :
    On the demo here, the nav sticks the bottom of the screen. Unfortunately, i don’t get the same result when i make a test on ipad and iphone with the elements in the zip file : the page looks bigger and the content is hidden, we need to slide down to see the elements in the foot.
    you can see what i mean on iphone or ipad HERE. this is just an upload of the elements in the zip file. The result is really different from what we expect to see as we can see on the demo page…
    Any idea ?

  5. 5

    Ok ok ok… I understood the problem.
    Forget about my previous message…
    On iphone and Ipad, if you make an anonymous redirection to a sub folder of an existant website (like i did for a website i hosted in a folder of my old website ), it creates a framework that split your content into 2 frames (with only 1 visible) : and the result is that it finally hides the content of the page (only on mobiles)
    I don’t know if i’m very clear, my english is not perfect… But you can see the difference here between the direct URL and the hidden redirection :
    direct URL
    anonymous redirection

    interesting, isn’t it ?

  6. 6

    Hi Mary Lou,

    I really like the navigation with sliding content. After studying the CSS I would like to use it,
    But when seen on an iPad, there still is the possibiliyy to scroll by finger and after that
    the system is messed up.
    I tried loading it in an Iframe, in which the content can not be scrolled in this way, but it
    The dimensions are nor correct anymore.
    http://www.bart-merkelbach.com/pad

    Kind regards, bart

  7. 7

    This is really great…look awesome but i wanted to ask that if pages can be slide by left or right ?

  8. 10

    Question: If I wanted to switch on the color on the NAV for an image, how and where would I change that?

  9. 11

    Hello,

    What a truly wonderful idea. I just wanted to ask a question; I would like to use this idea, but I have more than 5 items to navigate through, so I was asking if there was a way to make the bottom navigation to have left and right arrow buttons to navigate to the rest of the content. If that was possible could you assist me to achieve this concept.

    Thank you!

Comments are closed.