On-Scroll Animated Header

A fixed header that will animate its size on scroll. The inner elements will also adjust their size with a transition.


An animated header that will change its size on scroll. Once a certain amount of the page gets scrolled, the header will decrease its size and the inner elements will adjust their font-size/line height. There are example media queries for dealing with different screen sizes.


<div class="cbp-af-header">
	<div class="cbp-af-inner">
			<a href="#">Broccoli</a>
			<a href="#">Almonds</a>
			<a href="#">Pears</a>
			<a href="#">Oranges</a>


.cbp-af-header {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	background: #f6f6f6;
	z-index: 10000;
	height: 230px;
	overflow: hidden;
	-webkit-transition: height 0.3s;
	-moz-transition: height 0.3s;
	transition: height 0.3s;

.cbp-af-header .cbp-af-inner {
	width: 90%;
	max-width: 69em;
	margin: 0 auto;
	padding: 0 1.875em;

.cbp-af-header h1,
.cbp-af-header nav {
	display: inline-block;
	position: relative;

 /* We just have one-lined elements, so we'll center the elements with the line-height set to the height of the header */
.cbp-af-header h1,
.cbp-af-header nav a {
	line-height: 230px;

.cbp-af-header h1 {
	text-transform: uppercase;
	color: #333;
	letter-spacing: 4px;
	font-size: 4em;
	margin: 0;
	float: left;

.cbp-af-header nav {
	float: right;

.cbp-af-header nav a {
	color: #aaa;
	font-weight: 700;
	margin: 0 0 0 20px;
	font-size: 1.4em;

.cbp-af-header nav a:hover {
	color: #333;

/* Transitions and class for reduced height */
.cbp-af-header h1,
.cbp-af-header nav a {
	-webkit-transition: all 0.3s;
	-moz-transition: all 0.3s;
	transition: all 0.3s;

.cbp-af-header.cbp-af-header-shrink {
	height: 90px;

.cbp-af-header.cbp-af-header-shrink h1,
.cbp-af-header.cbp-af-header-shrink nav a {
	line-height: 90px;

.cbp-af-header.cbp-af-header-shrink h1 {
	font-size: 2em;

/* Example Media Queries */
@media screen and (max-width: 55em) {
	.cbp-af-header .cbp-af-inner {
		width: 100%;

	.cbp-af-header h1,
	.cbp-af-header nav {
		display: block;
		margin: 0 auto;
		text-align: center;
		float: none;

	.cbp-af-header h1,
	.cbp-af-header nav a {
		line-height: 115px;

	.cbp-af-header nav a {
		margin: 0 10px;

	.cbp-af-header.cbp-af-header-shrink h1,
	.cbp-af-header.cbp-af-header-shrink nav a {
		line-height: 45px;

	.cbp-af-header.cbp-af-header-shrink h1 {
		font-size: 2em;

	.cbp-af-header.cbp-af-header-shrink nav a {
		font-size: 1em;

@media screen and (max-width: 32.25em) {
	.cbp-af-header nav a {
		font-size: 1em;

@media screen and (max-width: 24em) {
	.cbp-af-header nav a,
	.cbp-af-header.cbp-af-header-shrink nav a {
		line-height: 1;

The JavaScript

var cbpAnimatedHeader = (function() {

	var docElem = document.documentElement,
		header = document.querySelector( '.cbp-af-header' ),
		didScroll = false,
		changeHeaderOn = 300;

	function init() {
		window.addEventListener( 'scroll', function( event ) {
			if( !didScroll ) {
				didScroll = true;
				setTimeout( scrollPage, 250 );
		}, false );

	function scrollPage() {
		var sy = scrollY();
		if ( sy >= changeHeaderOn ) {
			classie.add( header, 'cbp-af-header-shrink' );
		else {
			classie.remove( header, 'cbp-af-header-shrink' );
		didScroll = false;

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;



Tagged with:

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

👾 Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 69

Comments are closed.
  1. This is ALMOST the same as the header in my new design, except mine scrolls up out of view (I hate headers that block part of the reading area) and will scroll down on click of a nav button :>

    • “…I hate headers that block part of the reading area…”

      I feel the same about fixed headers. It might result into a bad UX specifically for mobile users. The idea of collapsing it out of view as you’ve mentioned solves the issue though. 🙂

    • Mee too, a new design is very very similar to the Mary Lou concept !

      Yes it’s easy to add a collapse button to this concept, but I don’t agree your fixed header hate: I think fixed header is the future of web design: always visible means that you need only one touch to navigate in any menu item (usability) somore encouragement to navigate the site (or share an article on social networks, comment etc…)!

      Also, on mobile devices in portrait orientation this fixed menu is awesome IMHO: in these cases you have an aspect ratio that’s more height than width (not 4:3 or 16:9, but something like 2:1) so if you can “stole some height” on desktops layouts you surely use this extra space without any kind of problem on portrait mobile devices…

      On the other hand, yes on wider than normal landscape modes (android devices for instance, iPads are 4:3), this can be a problem: but solution is simple: you can write media queries to transform this fixed menu in a vertical iconic sidebar (like Google+ one) when scrolled in landscape mode. I’m working on that….

      PS: an Iconic mode is needed also on very tiny width devices:
      reducing the menu item font size to keep it fitting a single line on smartphones was also my solution, but after some testing i discovered that resulting menu item is too small to be touched, so better to use a square icon to represent the menu in order to offer more touchable pixels….

    • Can you show your design? I don’t hate the fixed header but it doesn’t work well with a design I am making.

    • I agree with Stefano. I’ve seen a site that took a different approach ( when you swipe up below the fold, the nav disappears and when you swipe down a bit, it will appear – much like the Pinterest app ), but it is kind of unsettling, appearing and disappearing with swipe down and swipe up, respectively.

      I don’t know much about UX/UI but I know that the eye tends to focus on something it can grasp, something “fixed”. You know: like, how car commercials and goPro cam views are fixed on the side and are more tolerable than a shaky mess of a shot.

      Fixed headers are here to stay. Mobile apps are embracing them, why not the web? Add this to a one-page site, and I don’t have to worry about carpal tunnel surgery from too much scrolling.

  2. This is awesome! I’ve been looking for this for a long time to put on my websites new design 😀

  3. What is the purpose of didScroll? It seems to just change back and forth from true to false without affecting any other property.

    • It’s optimization sugar, see here:

      if( !didScroll ) { didScroll = true; setTimeout( scrollPage, 250 ); }

      This implement a debouncer: the onscroll event may be called hundred of times per second when you are scrolling but the toggleClass code and related css animation will be executed only once every 250 milliseconds (that’s very acceptable compromise between percepibile UI latency (near to 0!) and performance optimization

      TEORICALLY, You can also do something like

      clearTimeout(lastScheduledTimeout); lastScheduledTimeout=setTimeout( scrollPage, 5 );

      so teorically you get it executed only once at the end of scrolling, but what if user scrolls for a long period?
      So it’s better update UI every 250!

      great work Mary Lou! As always you provide the best balance between technical and “congitive” properties of an UX 🙂

  4. I love your pure javascript implementations <3
    thanks for the tutorial 🙂

  5. I see this type of navigation more and more often on one-page websites and I`m glad you made a tutorial out of the idea! 🙂

    • look at the function scrollPage()
      this function just add a new class .
      you can do it with any div

  6. The header occupies half the screen on an iPhone 4 and does not change its size once I scroll.

    • It actually works fine on my iPhone 4s, you just need to scroll down more, although I agree it’s not that practical and needs some modifications.

      Great tutorial over all. Thanks!

  7. Cool stuff but it doesn’t work on ipad either. What would you need to do to make it work on mobile?

  8. This effect is really elegant and awesome, I will use it on my website. Thanks you!

  9. Had a lot of fun implementing this. I was wondering though, what’s the benefits of using Classie to add and remove classes on elements, as opposed to jQuery’s addClass and removeClass functions?

    • I have the same question, I’ve been trying for a few hours to put an image in the place where the word “fixed” is and can’t make it work.

  10. Hello Mary Lou,

    Great work on all your tutorials! I’m just discovering this site’s section. I’m very much at an intermediate/beginning level (although I did studied web design/dev for 2 years). Love these basic components and elements!

    Keep it up!

  11. This is really great work, but if only it worked in IE8!

    Really enjoying your work on a whole though. Thank you!

  12. I inserted .cbp-af-header {position: relative;} for IE8
    so it is working in IE8 without animation.

  13. To those of you asking what to do if you have images in your header – your logo, for instance – I came across a decent solution. It works well, though I am fairly novice so I can’t speak to its elegance or platform compatibility. Anyway, using jquery I did the following, to make my logo disappear when the user has scrolled a set distance from the top.

    jQuery(function($){ var showTopMenu = function() { var topMenu = $('#logo'); if ($(window).scrollTop() < 1000) { topMenu.css('display', 'block'); } else { topMenu.css('display', 'none'); } } $(window).scroll(function(){ showTopMenu(); }) });

    I have 1000 as the trigger point for the image to disappear, but the trick is that you will have to coordinate that number with wherever you want your header to shrink/collapse. I did this for the other tutorial ‘On Scroll Header Effects’, which uses the waypoint plugin, but the idea is the same. P.S. It would be really cool if codrops did a tutorial involving either bullseye.js or Zurb’s Foundation navigation tool magellan.js, or else about using offsets for single-page app navigation so that fixed headers (of whatever size) don’t get in the way of your content.

    Thanks again Mary Lou et al.


    • This is cool but I’ve got a question. How do you make didscroll work? I definitely need this function. Please help, God bless.

    • Yes Marcellius. You can edit any templates on WordPress and developing this example. You need only integrate this to WordPress template. It’s not so very complex.

    • Abdulrehman, you need a HTML, CSS, JS minimum skills for this. It’s so simple.

      Basicly, you need to substitute the “FIXED” text on top ( tag), for a logo (probably a image). Put image of you logo inside of this tag, this is solve your problem.

  14. What’s the function of the following?

    ? didScroll = false,
    ? if( !didScroll ) {
    didScroll = true;


    • The function you are asking about is what the event listener tied to the scroll event triggers. Setting the variable didScroll to false initially means it will the logic will occur first time some one scrolls. The function then sets didScroll to true then triggers the scrollPage function in 250ms. The scrollPage function sets didScroll to false as part of its execution.

      This is done so scrollPage is not repeatedly called during the scrolling action(only every 250ms).

  15. Hi ,

    I’ve seen a couple of comments in regard to this but nothing has really answered it. I need a logo (image) in place of the word FIXED. I can obviously just insert an image in place of this text, but it doesn’t resize. Is there a way do doing this please.

    Can this also be done for where the navigation text is, as I want to add some images for the nav too. This needs to resize as well.

    Any help much appreciated. Thanks

    • Copy the code in: cbpAnimatedHeader.min.js

      and rename the elements you want to effect like so.

      var cbpAnimatedHeaderLogo=(function(){var b=document.documentElement,g=document.querySelector(".cbp-af-header-logo"),e=false,a=40;function f(){window.addEventListener("scroll",function(h){if(!e){e=true;setTimeout(d,250)}},false)}function d(){var h=c();if(h>=a){classie.add(g,"cbp-af-header-shrink-logo")}else{classie.remove(g,"cbp-af-header-shrink-logo")}e=false}function c(){return window.pageYOffset||b.scrollTop}f()})();

  16. Hey, great header!! 🙂

    I have a problem though, I have this large space between the animated header and the content on the page, how do I fix that?

    Thanks again.

  17. THANKS A ZILLION TIME, I’ve learned a lot will implementing it in a WP test theme 🙂
    Keep up your ninja code going on 🙂

  18. everything is ok but tell me where to paste the java script >
    var cbpAnimatedHeader = (function() {

    var docElem = document.documentElement,
    header = document.querySelector( ‘.cbp-af-header’ ),………………..

  19. hi guys, has someone found a solution on how to resize a header image? i am not sure how to “call” the code from above from within the html -can someone help?

    cheers J

    • This should help. (www.seikouri.com)

      .head_logo {
      margin: 10em 0 0 0;
      width: auto;
      height: 10em;
      background: url(logo.svg) no-repeat center center;
      background-size: auto 10em;

      .cbp-af-header .head_logo {
      -webkit-transition: all 0.2s;
      -moz-transition: all 0.2s;
      -o-transition: scale(1);
      -o-transition: all 0.2s;
      transition: all 0.2s;
      -webkit-transform: scale(1);
      -moz-transform: scale(1);
      transform: scale(1);

      .cbp-af-header.cbp-af-header-shrink .head_logo {
      margin: 0.4em 0 0 0;
      -webkit-transition: all 0.2s;
      -moz-transition: all 0.2s;
      -o-transition: all 0.2s;
      -o-transition: scale(0.6);
      transition: all 0.2s;
      -webkit-transform: scale(0.6);
      -moz-transform: scale(0.6);
      transform: scale(0.6);

    • Thanks Mary Lou!, nice work.

      And thanks DJ, your image implementation works great 😉

  20. ADDING DROP DOWN MENU!**************************
    How can I add a drop down to this?
    Simply adding table elements ul, li to the css and html isn’t working out for me 🙁
    Please help.

  21. I really love this hiding menu. but is there a posibility to regain the full width by scrolling up. it should get big again soon as i scroll up one px not only when i reach the top of the page.

  22. Everything is fine and doing very well but i got some problem in loading stage. when i refresh the page it load from the very beginning like when you scroll down the page the header portion get smaller so do the logo. At this time if you load the page it give a glimpse of bigger sized header and then get smaller which i don’t want to, and it was supposed to load in small sized header. Same thing can happened reversely. So is there any solution that will help to get rid of that problem. I want to refresh the page exactly how it is loaded right now even i refresh the page.

  23. Very nice header but I can’t get it running.

    Getting this errors:

    Uncaught TypeError: Cannot read property 'classList' of null classie.js:36 remove scrollPage Uncaught TypeError: Cannot read property 'classList' of null classie.js:36 removeClass classie.js:36 d

    Any ideas how I can get it run?

  24. Hi !
    I’m trying to get this work on IE8.
    IE detect a problem on the AddEventListener function.
    It seems possible to make it work with this kind of code :

    if (!someElement.addEventListener) {
    someElement.attachEvent(“onclick”, someFunction);
    else {
    someElement.addEventListener(“click”, someFunction, false);

    Anyone can help me to do the right thing ?
    Thanks !

    • Hi, this is so easy try following code:
      var cbpAnimatedHeader = (function() { var docElem = document.documentElement, header = document.querySelector( '.header' ), didScroll = false, changeHeaderOn = 20; function init() { if (!window.addEventListener) { window.attachEvent("onscroll", function( event ) { if( !didScroll ) { didScroll = true; setTimeout( scrollPage, 0 ); } }); } else { window.addEventListener("scroll", function( event ) { if( !didScroll ) { didScroll = true; setTimeout( scrollPage, 0 ); } }, false); } } function scrollPage() { var sy = scrollY(); if ( sy >= changeHeaderOn ) { classie.add( header, 'cbp-af-header-shrink' ); } else { classie.remove( header, 'cbp-af-header-shrink' ); } didScroll = false; } function scrollY() { return window.pageYOffset || docElem.scrollTop; } init(); })();
      I tested it on IE8 and it works.

  25. you have not mentioned anything about using classie jq, i got so confused not keeping the jquery file

  26. Hi Guys,

    Just installed this plugin. And have one small issue.

    Maybe you can help me.

    Via CSS clases I’m changing the header position to Fixed and it works, but it doesn’t look nice. I would like it to fadeIn when triggered, how can I achieve that?

    Please help,


  27. Thanks for showing us this, I have tried to use it with wordpress but cannot get it to work 🙁 Can anyone give me some instructions on how to do this?

  28. Worked Perfectly! I was using Bootstrap, but I found it a little difficult to incorporate with Bootstraps styles. So instead I used all the code provided instead of the Bootstrap nav code.

    Was easy enough to make edits to.

    Thanks Mary Lou!

  29. Added the JS and CSS to files and linked them to my HTML but for some reason the js wont run..? the nav doesnt shrink when i scroll.

  30. This is fantastic!

    However I’m finding that the function that triggers the shrink animation upon scrolling down the page doesn’t activate in mobile browsers (I tested in chrome on ios) until the smooth scrolling animation of the browser decelerates and stops completely.

    Its possible to scroll from the top of a page to the bottom without the shrink animation being triggered. Anyone know a workaround for this?

  31. Preface: I’m a JS newb learning to code with my business partner while her learns more digital marketing from me.

    I was experiencing some of the issues read on the thread: no animation on scroll despite initial animation, fixed in mobile…until I realized the newbie mistake I was making: Put the links to the JS and CSS for the app in the footer! The reason it’s probably going flat is because the code isn’t loading first. Put them in the footer and it should help.

    I’ll probably use media queries to get it to work more consistently in mobile.

    My first JS troubleshoot. :-P.

  32. Hello! Please, I need help: how can I make this work only for document width more then some value? (for ex. width > 992px). So when I resize the window to width 700px, header must not collapse on scroll and vice-versa when I resize from little to big document width it must become collapsible. Now in this example on page load init() is invoked, how can I cancel it on other document widths?

  33. Unfortunately it doesn’t work properly on Google Chrome on iOS (tested on iPad), the effect does not triggers unless you fully stop scrolling, so you can basically scroll a whole page without the header triggers shrink.