On Github Hackapalooza / Webpage-to-Website
Not much! A website is made up of one or more web pages.
What's a web page?
We already have a navigation menu, but currently it only links to different sections on the same web page
Let's create some new web pages and update our links to point to them
<ul class="menu"> <li><a href="index.html">Home</a></li> <li><a href="about.html">About</a></li> <li><a href="products.html">Products</a></li> <li><a href="contact.html">Contact</a></li> </ul>
So we'll need to separate the contact link from the rest of the links
Which means we need to wrap our menu in another element
<nav class="main-navigation"> <ul> <li><a href="index.html">Home</a></li> <li><a href="about.html">About</a></li> <li><a href="products.html">Products</a></li> </ul> <a href="contact.html">Contact</a> </nav>
Now we need to adjust the CSS to match the markup changes we made
remove the padding around list items
.main-navigation { border-bottom: 1px solid #434A5C; background: #ffffff; position: fixed; top: 0; left: 0; width: 100%; } .main-navigation ul { list-style: none; margin: 0; padding: 0; } .main-navigation li { display: inline-block; }
Next, let's add some space at the sides of the navigation
Remember, CSS shorthand for padding lets us set the top and bottom values and then the left and right values
.main-navigation { ... padding: 0 50px; }
Remember how we removed padding from the <li> elements earlier? Let's add it back on, but to the <a> elements instead
.main-navigation a { ... padding: 20px 35p; }
This should add 20px to the top and bottom, and 35px to the left and right
But it only seems to work on the left and right...
Let's compare two common elements in HTML: the <p> element and the <a> element
<a href="#">Link 1</a> <a href="#">Link 2</a> <p>Lorem ipsum dolor sit amet...</p> <p>...perferendis ipsum quia voluptates quo sint at animi dolorum.</p>
This can be easier to visualize if we put an inline element inside a block element
The <a> element sits on the same line as the surrounding text, and doesn't break the flow of the text.
However, if we nest a block element inside another block element...
<div>This is some text in a div. <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. </p> </div>
The block element creates a new line:
This can have some interesting consequences when we try to apply styling to different types of elements.
For example, what happens if we add some padding around our nested <a> element?
a { padding: 20px; background: red; }
We can see the padding on all 4 sides of our element, but the element refuses to disrupt the spacing of the line, so we're not getting the proper effect for the padding.
Those links in our navigation that weren't respecting the padding we wanted to add to the top and bottom!
So how do we fix this?
Let's add display: block; to the <a> elements in our navigation, to force them to accept our padding
They won't create line breaks, because their parent elements are set to display: inline-block;
.main-navigation a { padding: 20px 35px; display: block; }
Now we need to get the <ul> off to the left and the "contact" link off to the right.
We can do this by floating the list to the left and the link to the right.
.main-navigation ul { ... float: left; } .main-navigation > a { float: right; }
Wait a minute, why did that work?
Shouldn't all the <a> tags have floated to the right?
.main-navigation > a { float: right; }
The > character is one you might not have come across yet. It's an advanced CSS selector that specifies that we only want to look one level deep for children.
This is read as "anchor tags which are direct children of elements with the class main-navigation"
This selector will not apply to the other <a> tags in our markup, because they are nested within <li> tags, and therefore not direct children.
How come our contact link is being cut off?
It has to do with the way widths are calculated in CSS.
Remember how we set the .main-navigation item to width: 100%; and then added 50px of padding to each side?
Turns out, that caused some weird math to happen.
100% + 50px + 50px = more than 100%
So since our .main-navigation item is wider than the page, our right-most element is being cut off
So do we have to do the math and subtract 100px from 100%?
Nope (though you could)
We can force the browser to do the math for us and make sure padding is calculated within the element, instead of making the element bigger.
We do this with the box-sizing property.
Add box-sizing: border-box; to the main-navigation element
.main-navigation { box-sizing: border-box; }
Looking good! Let's just remove that black border at the bottom.
Delete border-bottom: 1px solid #434A5C; from the .main-navigation selector.
Next, we'll add some styles to the hover state for our links.
We can style the hover state of an element using th CSS pseudo-selector :hover
Pseudo-selectors always start with a ":" and come immediately after the selector preceding them.
Let's change the background colour of our links when they're hovered over.
.main-navigation a:hover { background-color: #ff7ee5; }
It would be nice if our users had an easy way to tell which page they were on. We can style the current link differently to make this more obvious.
First of all, let's copy the markup changes we made in index.html to our other HTML files
Now, we're going to add a class to the selected link on each page. For example, on index.html we'll add the class "active" to the link that goes to "Home".
<li><a class="active" href="index.html">Home</a></li>
Do the same for your other pages.
Let's make a selector for the active link. We'll use the . syntax to specify we only want links "with the class of active"
.main-navigation a.active { background: #D300A9; color: white; }
Looks great!
Except... what's with that white space between the two links?
This is a bit of an annoying bug that has to do with the fact that our <li> elements are set to display: inline-block;. The browser adds about 4px of space between the elements. Not good!
There's a simple solution, fortunately: set the font-size on the parent element to 0, and then set it back to 16 on the list item elements.
.main-navigation ul { ... font-size: 0; } .main-navigation li { ... font-size: 16px; }
Here's the fun part: we're going to add a "drop-down" menu to our navigation, so that users can see links to sub-pages when they hover over the "Products" link.
The first step for making a dropdown menu is adding the new markup to our index.html file.
Since the sub-pages are nested under the "Products" page, we're going to nest a list of subpages inside the "Products" list item. This is called a "nested list" and is a common way of marking up lists of lists.
<li> <a href="products.html">Products</a> <ul> <li><a href="#">Category 1</a></li> <li><a href="#">Category 2</a></li> <li><a href="#">Category 3</a></li> </ul> </li>
Doesn't look so great yet.
Let's get those list items to stack on top of each other rather than sitting side-by-side.
We'll bring back an old friend: display: block;
Let's write a selector for the list items inside list items (nested <li> tags)
.main-navigation li ul li { display: block; }
Ok, here's where things get a little weird.
Right now, our submenu is pushing the rest of the page down. We don't want that. We want it to stick to its parent and overlay on top of the rest of the page.
To force an element to anchor itself to another without interfering with its surroundings, we use position: absolute;
Absolutely positioned elements are positioned in relation to another element, but the rest of the page doesn't leave any space for the element, so it just sits on top.
Let's add position: absolute; to our nested list.
.main-navigation li ul { position: absolute; }
It will be positioned in relation to its nearest ancestor, which in this case is the parent <li> item.
We also have to explicitly add our white background back in, since it's no longer inheriting it from its parent.
.main-navigation li ul { background: white; }
We only want our dropdown list to show up when the user hovers over it, so its default state should be hidden. To make an element disappear, we can set the display property to none:
.main-navigation li ul { display: none; }
Next, we can take advantage of the :hover pseudo-selector to set the display property to block only when the user hovers over the list item.
.main-navigation li:hover ul { display: block; }
There's just one thing we could do to make our menu a little bit more awesome...
Our menu doesn't really look that great if we resize it down to the width of a mobile phone
Fortunately, CSS gives us a way to change our styles depending on how wide a user's browser is. There are two steps to this process.
Add the following code to the <head> section of your html:
<meta name="viewport" content="width=device-width, initial-scale=1">
(this prevents phones from zooming out and showing your website super tiny)
And add this code to the bottom of your CSS file:
@media all and (max-width: 699px) { // code goes here }
The code between the two curly brackets will only apply when the screen is narrower than 700px.
The first thing we want to change for mobile users is the fact that the menu is stuck to the stop of the screen. This would be annoying if you had a really small screen. We got it to stick to the top of the browser using position: fixed, we can undo this using position: static;
.main-navigation { position: static; }
We'll lose our white background doing this, that's because we have floated elements inside our container. There are a couple workarounds for this, but in our case, we're just going to unfloat the elements, becasue we want to stack all the links on top of each other.
.main-navigatoin ul { float: none; } .main-navigation > a { float: none; }
Let's get all those list items to stack on top of each other too. Our old friend display: block; can help us here too:
.main-navigation li { display: block; }
We can also get rid of the white space at the sides by cancelling out that 50px of padding we added earlier:
.main-navigation { padding: 0; }
Okay! All that's left to do is to make our sub-menu behave more normally on mobile. Let's undo all this position: absolute; stuff and make the submenu push the items beneath it down like a normal element.
.main-navigation li ul { position: static; }
You might be wondering how users are going to hover over the parent list item on their mobile phones.
The short answer is: don't worry about it too much. Most mobile browsers translate :hover events into touch events, so when a user touches on the "products" list item, it will apply the :hover styles you've added and open up the menu. This doesn't work on 100% of browsers, so if you're worried about it, you can either choose to show the submenu by default on mobile, or use JavaScript to attach the styles to a touch event.