Making a responsive navbar and placing it on a page with a sticky footer.

See it in action on CodePen:

See the Pen Responsive Navbar in page with sticky footer by Gavin Sykes (@gavinsykesuk) on CodePen.

Starting with the footer positioning

A common issue with placing footers can be a page looking unneat when its content does not reach to the bottom of a user’s viewport. This was not so complicated in earlier times, when the web was still on computers and there were limited different screen sizes.

via GIPHY

Nowadays though, with all sorts of different screen sizes available, and in the mobile age, it is more important than ever to make your content adapt to any screen size.

There are all sorts of ways to do this, mostly involving JavaScript, that have all sorts of dependencies such as fixed-height footers, which considering how dynamic content can be, simply isn’t practical.

Now with recent CSS features though, it is very simple to achieve, in one of 2 ways:

Flexbox

As the name might suggest, flexbox allows its child items to be displayed in a flexible manner, manipulating their positioning and sizing along an axis, and you as the developer get to decide if this axis is the horizontal or the vertical. To achieve a sticky footer we use flexbox vertically.

Imagine a simple page layout, with a <header>, <nav> (which we’ll come to sorting later), <main> and <footer>:

<header></header>
<nav></nav>
<main></main>
<footer></footer>

We set our surrounding body tag as a flex container. This in itself doesn’t do anything to its child elements, but it does tell them that they are in a flex container and can use certain properties as a result.

The next important piece of code is telling our <body> what to do with its flex children, we want it to arrange them in a column (because we’re stacking everything on top of each other rather than to the side) and not wrap them, we can do this in one line of CSS with flex-flow: column nowrap;. I haven’t seen what would happen if it did wrap, and given how we will set up our child elements I doubt it would try to wrap anyway, but if it ever did the results would be bad, so that nowrap acts as a safety feature more than anything.

In order to get our header and footer right to the edge of the viewport, it is often useful to set the margins on our <body>, and even our <html>, to 0, and their minimum heights to 100vh;

One additional thing we can do is set align-items to stretch, this stretches all of our elements across its axis, removing the need to set their widths individually.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
}

header {
}

main {
}

nav {
}

Now that the container is taken care of, we need to look at the children. The beauty of flex children is that they can stretch and shrink to accommodate any environment, but they can also be set to do the exact opposite of that, which is what we want to do here. We do this by utilising the flex-grow and flex-shrink properties.

flex-grow is set to a number and behaves according to that number in relation to its siblings, if two flex children each have a flex-grow value of 1 then they will grow at the same rate, if you change one of them to 2 however then that one will grow at twice the rate of the other one. flex-shrink works much the same way only it does the opposite, it shrinks items to fit in the available space rather than growing them to fill the available space.

If you set flex-grow to 0 it will not grow at all, and the same applies with shrinking when flex-shrink is set to 0.

With this in mind we want our <header>, <nav> and <footer> to keep their height i.e. not grow or shrink, and our <main> to take up the rest of the available room.

In order to have our <main> take up the rest of the available space, simply set its flex-grow to 1, this has the added bonus effect of not needing to set it to 0 on our other elements, as if only 1 element is expanding to take up all available room, no others will.

We should, however, make sure that none of our elements shrink, by setting their flex-shrink property to 0. On our <main> this can be done by the shorthand property flex, which sets flex-grow and flex-shrink in a single line, so we can set it to 1 0. It can also set flex-basis which you may want to play around with but isn’t really necessary here and defaults to auto.

Strangely, although we don’t need to set flex-grow on our other elements to 0, if we use this shorthand property to do it our CSS will end up shorter! Plus it provides a safety mechanism much like setting the nowrap on our flex container.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
  flex: 0 0;
}

header {
  flex: 0 0;
}

main {
  flex: 1 0;
}

nav {
  flex: 0 0;
}

The one (and so far only that I’ve found) drawback to this is that all of your content must be in a line for it to work, you cannot have an <aside>, for example, or any other <div>s, unless they are directly above or below something else, rather than to the side. This is where grid comes in.

Grid

Just as the name of Flexbox suggests you can treat its children flexibly, the name of Grid allows you to lay that grid’s children out in a grid fashion.

This now allows us to add additional items to our layout, so rather than just our <header>, <nav>, <main> and <footer>, we can now have an <aside>.

To get started with our grid we need to set the display of the body to grid, and then define that grid.

html {
  margin: 0;
  min-height: 100vh;
}
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

So what exactly have we done here? Without going too in depth into grid (because grid is insanely powerful and doing that would take us well away from what we’re focussing on here) we have defined 2 columns and 4 rows for a total of 8 cells. The columns have been defined as 2fr wide and 1fr wide, what this means is the left column is 2 portions wide of whatever space is left after any other columns that have rigidly-defined sizes (50px, 30% and so on) and 1fr means 1 portion wide. Essentially our left column in this case takes up 2 thirds and our right column takes up the remaining third.

Looking at our rows, the browser will first look at the rows defined as auto (which means “look at my content and set my height appropriately”) before portioning out the remaining space to the row set to 1fr – in theory as that’s our only fr-defined row, we could have that as any positive number!

Using grid-template-areas, in conjunction with the grid-area property on our child elements allows us to define where things will go on our page.

html {
  margin: 0;
  min-height: 100vh;
}
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}
aside {
  grid-area: aside;
}
footer {
  grid-area: footer;
}
header {
  grid-area: header;
}
main {
  grid-area: main;
}
nav {
  grid-area: nav;
}

We have told our <header> to fill up all areas where the grid displays header and so on, it is important to note that you cannot set items to be L-shaped or any other shape than a rectangle.

The reason I haven’t just defined one column is because we need our page/application to look good on different screen sizes, so lining all our elements up on top of one another means we can read everything on a small screen then use media queries to rearrange the items on larger screens.

You can define these media queries however you like, but the way I have done it is to go mobile-first (which I always recommend you do as well) and rearrange things as you go up the screen sizes. Borrowing a breakpoint from Bootstrap I have set it to be at 768px wide. If our screen is narrower than that we keep our <aside> underneath our <main>, and once it hits 768px we move it to the side.

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

Notice how we now only have 4 rows, and that <main> and <aside> share a row? This is also why the columns earlier were 2fr and 1fr wide, to keep the main content bigger, although you could set this how you want, 3fr and 1fr, 3fr and 2fr, 10fr and 1fr, even 1fr and 1fr!

When doing media queries, if one of them evaluates to true then the browser will overwrite only the rules that are part of that media query, so in our media query we can set only the grid-template-rows and grid-template-areas property, without having to set display: grid or anything else, as it simply keeps the already-defined rule.

By naming our grid areas and setting each element to take up the space defined by those areas, this also means we needn’t do a single thing to our child elements in media queries, simply rearranging the grid layout will move them.

And here we have it! A layout grid that adjusts to your screen size and always keeps the footer at the bottom of the page.

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

footer {
  grid-area: footer;
}

header {
  grid-area: header;
}

main {
  grid-area: main;
}

nav {
  grid-area: nav;
}

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

TL;DR

Flexbox

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-flow: column nowrap;
  align-items: stretch;
}

footer {
  flex: 0 0;
}

header {
  flex: 0 0;
}

main {
  flex: 1 0;
}

nav {
  flex: 0 0;
}

Grid

html {
  margin: 0;
  min-height: 100vh;
}

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto auto 1fr auto auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main main"
   "aside aside"
   "footer footer";
}

footer {
  grid-area: footer;
}

header {
  grid-area: header;
}

main {
  grid-area: main;
}

nav {
  grid-area: nav;
}

@media (min-width: 768px) {
 body {
  grid-template-rows: auto auto 1fr auto;
  grid-template-areas:
   "header header"
   "nav nav"
   "main aside"
   "footer footer";
 }
}

And now the navbar

First of all we need to lay out our navbar’s links inside a <nav> tag:

<nav>
 <a class="active" href="#top">Home</a>
 <a href="#flex-footer">Flexbox Footer</a>
 <a href="#grid-footer">Grid Footer</a>
 <a href="#tldr-footer">Footer (TL;DR)</a>
 <a href="#navbar">Navbar</a>
 <div class="middle"></div>
 <a  class="codepen" href="https://codepen.io/gavinsykesuk"  target="_blank"><i class="fab  fa-codepen"></i><span> Codepen</span></a>
 <a  class="github" href="https://github.com/gavinsykes"  target="_blank"><i class="fab fa-github"></i><span>  Github</span></a>
 <a class="linkedin"  href="https://www.linkedin.com/in/gavinsykes/" target="_blank"><i  class="fab fa-linkedin"></i><span>  LinkedIn</span></a>
 <a class="stackoverflow"  href="https://stackoverflow.com/users/8640133/gavin"  target="_blank"><i class="fab  fa-stack-overflow"></i><span> Stack  Overflow</span></a>
 <a class="twitter"  href="https://www.twitter.com/gavinsykes_uk" target="_blank"><i  class="fab fa-twitter"></i><span>  Twitter</span></a>
 <div id="nav-open" class="open-icon">≡</div>
</nav>

As with our grid, we want to lay it out differently based on the width of the screen we are viewing it on, but also we now want to introduce a bit of JavaScript to prevent the navbar taking up too much space when not needed, and show us all our navigation options when needed. I have used the jQuery library to make our code more concise.

$(() => {
 $('#nav-open').click(() => {
  $('nav').toggleClass('open');
 });
});

If you prefer to use raw JavaScript then you can, but all I’ll say is there is a reason I’ve used jQuery!:

document.addEventListener("DOMContentLoaded",() => {
 document.getElementById('nav-open').onclick = (e) => {
  document.getElementsByTagName('nav')[0].classList.toggle('open');
 }
});

It looks complicated, but basically what this code does is add a function to an item called nav-open (which we’ll come to later), so that when the user clicks it it either adds or removes the open class as necessary to (or from) the navbar.

Of course there is also the Pandora’s Box of frameworks that would have their own methods of doing this such as React and Vue!

We will use flexbox for our navbar, as it is linear i.e. everything will be in a line rather than some bits being to the side of each other.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

The position: sticky and top: 0 properties tell the navbar that when we scroll away from it on the page, it is to stick to the top of our screen.

As we are mobile-first, we want to set everything inside our navbar to not display, then only pick the items we do want to show.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

Depending what you have as your open icon you may not need to set font-weight: bold, but in this case I have. What we have done here is set everything within our navbar to not display, and then overridden that in our active link and the opening icon.

Next we need to worry about what our navbar looks like when it is open, meaning that we want to show everything when it has the open class, and hide it when we click the icon again.

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

nav.open a {
 display: block;
}

Now we need to turn to what our navbar looks like on bigger screens, one thing we can do straightaway is set our middle div to flex-grow: 1 to make it push everything to its left all the way to the left, and everything to its right all the way to the right, but as we’re doing it outside the media query we need to also set it to display: none so that it doesn’t appear on mobile and take up the whole screen!

nav {
 grid-area: nav;
 display: flex;
 flex-flow: column nowrap;
 align-items: stretch;
 position: sticky;
 top: 0;
}

nav * {
 display: none;
 text-align: center;
}

nav a.active {
 display: block;
}

nav div.open-icon {
 display: block;
 font-weight: bold;
}

nav.open a {
 display: block;
}

nav div.middle {
 flex-grow: 1;
 display: none;
}

@media (min-width: 768px) {
 nav {
  flex-flow: row nowrap;
 }
 nav a:not(.active),
 nav div.middle {
  display: block;
 }
 nav div.open-icon {
  display: none;
 }
}

So what have we done in our media query? Firstly we have set our navbar to display as a row rather than as a column, set all the links within the navbar to display given that we now don’t need to save any space, and set our middle div to display (although it’s empty) to push everything else to the relevant edges as above. Finally as we no longer need our opening button, set it to display: none.

CSS nth-child selectors

You can do all sorts of thing with nth-child selectors, such as selecting the first child element, 5th, 10th, 14th or even last, 2nd-last etc.and primarily that is what it was used for, but there are so many more thing you can (and probably would want to in certain situations) do besides…

Now I haven’t ever really come across anywhere on the web that has everything you can do with CSS nth-child selectors all in one place, so this is what I’m aiming to achieve.

Now there are 2 main flavours of child selectors in CSS, they are :nth-child(n) and :nth-of-type(n). :nth-child can be used on its own as it can simply select the nth child of, say a div element. Each of these can be used along with a selector, say, p:nth-of-type(n) or li:nth-child(n).

So what would be the difference be between p:nth-child(3) and p:nth-of-type(3)?

  • p:nth-child(3) will select a p element that is the 3rd element of its parent element. If the 3rd child of that parent element isn’t a p element, it simply won’t select anything.
  • p:nth-of-type(3) will select the third p element within the parent element, even if it is not the 3rd child, it could be the 4th, or the 15th. If there are fewer than 3 p elements within the parent element, it simply won’t select anything.
  • p:nth-child(3) and p:nth-of-type(3) will select the same p element if, and only if, we start off our parent element with 3 uninterrupted p elements.

So now that that’s out of the way, on to what we can do with the selectors!

Selecting all child elements

You may have, say, a div tag where you want to do something to each of its children, but not the div itself, for this you simply combine the div with the universal selector, which is *, for a result of div * {your style here}.

Selecting a certain child

The most simple thing to do is select a single child element based on its position within its parent element, for this we would use, for example, nth-child(4) to select the 4th child element.

A couple of important shortcuts are below with any more to follow:

  • :nth-child(1) = :first-child
  • :nth-of-type(1) = :first-of-type

Now we go on to selecting a subset of child elements. This is where the real fun begins…

Selecting a subset of child elements

Starting off with a really simple and common example – we have a table with a header row, and a few rows where we want to alternate the background colours.

The term itself – nth – has its roots in calculus, and as such we can use an expression such as 2n to select every even child element of a parent, a popular example is styling alternate rows in a table, for which you would use table tr:nth-child(2n). This means that our styling will apply to 2 x 0 = the 0th child (no such thing), the 2 x 1 = 2nd child, the 2 x 2 = 4th child, and so on until the 2nth child doesn’t exist i.e. we’ve reached the end of the table.

To select all the odd rows instead we can replace the 2n with 2n+1, so our styling will now apply to 2 x 0 + 1 = the 1st child, 2 x 1 + 1 = 3rd child, 2 x 2 + 1 = 5th child and so on, again until the 2n+1th child doesn’t exist.

As you may be suspecting at this point however, we again have some useful shortcuts:

  • :nth-child(2n) = :nth-child(even)
  • :nth-child(2n+1) = :nth-child(odd)
  • :nth-of-type(2n) = :nth-of-type(even)
  • :nth-of-type(2n+1) = :nth-of-type(odd)

Of course however there are far more subsets then just even and odd, what if we are wanting something a bit more advanced, like selecting every 4th child, or selecting every 5th child, starting at the 2nd one (so 2nd, 7th, 12th etc.)?

Luckily the 2 in 2n and 1 in, uh, 1, can be replaced by any integer, so if we wanted to select every 4th child it would simply be :nth-child(4n), if we wanted to select every 5th child starting from the second we’d start with :nth-child(5n) but then we’d need to “shift” it by adding -3, as 5 – 3 = 2, so our final result would be :nth-child(5n-3).

There aren’t any shortcuts for these particular setups unfortunately. However we can use them to create another 2 subsets: the first few child elements or all but those first few:

Selecting all but, or only, the first or last few elements

The way to select the first 5 child elements seems a bit counterintuitive, but it would be :nth-child(-n+5), as it would go through -0 + 5 = 5th child, -1 + 5 = 4th child, -2 + 5 = 3rd child and so on until it arrives at the 0th child which of course doesn’t exist. For selecting all but the first 5, we essentially want to start our selection at the 6th child, so this is a much simpler :nth-child(n+6).

Now we know how to select single child elements and subsets of those child elements, however there is one other thing that I myself have recently run into. All of our subsets have either started at the beginning or ended at the end, but what if we want a subset that starts and ends somewhere in the middle?

I came across this when doing a piece on SVG transformations and in particular matrices, a mathematical concept that is essentially a grid of numbers, so I used CSS grid to create my matrix, being a 3×3 matrix I simply called it matrix-3:

.matrix-3 {
    display: inline-grid;
    grid-template-columns: repeat(3, auto);
    grid-template-rows: repeat(3, auto);
}

Now I will want the first 3 children on the first row, the next 3 on the second and the final 3 on the third, as well as this I will want the 1st, 4th and 7th children in the 1st column, the 2nd, 5th and 8th children in the 2nd column, and the 3rd, 6th and 9th children in the 3rd column.

Now I could go through each child element, tell the first child to be in row 1 column 1, tell nth-child(2) to be in row 1 column 2 etc. or I could reduce 9 statements into 6 by starting off with nth:child(3n+1) {grid-column: 1;} to tell the first, 4th and 7th elements their column, then nth:child(3n+2) {grid-column: 2;} and finally nth:child(3n) {grid-column: 3;}.

Now we can select our first 3 elements no problem, and tell them to go into row 1, :nth-child(-n+3) {grid-row: 1;}, and our last 3 elements we can tell to go into row 3, :nth-child(n+7) {grid-row: 3;}. But how do we now select our middle 3?

Selecting a central subset of elements

i.e. one that doesn’t start at the beginning of our parent, or end at the end

The neatest (and so far only, so also messiest) way I have found of doing this is by doubling-up on our selectors, so starting with :nth-child(n+4) to give us the last 6 elements we can now add :nth-child(-n+6) to pick those elements that are also in the first 6, so we basically end up with :nth-child(n+4):nth-child(-n+6) {grid-row: 2;}. So think of it as chaining multiple nth-child selectors together as a sort of AND statement if you will.

So there we have it, everything (that I can think of at least) that you may want to do with CSS nth-child selectors!

Questions or comments? Or anything that you’d want to do with nth-child selectors that perhaps doesn’t appear in this post? Let me know in the comments below!