Web Design & Development · CSS

WDD10 - CSS: horizontal navigation bar and descendant selectors

Tue 1 Dec 2026 · P2 (single)
Content scoped for a double period · see teacher notes for what to compress
Learning intentions
Success criteria
Warm up - descendant selectors and float recap

WDD10 reuses the descendant selector from WDD6 and the float property from WDD7 - both return today.

WU1
Write a descendant selector that targets paragraphs inside an element with class "sidebar".
 
WU2
What happens to a floated element with respect to normal document flow?
  • It is removed from normal flow entirely, not just shifted.
  • Correct.
  • Floated elements remain fully visible.
  • Float does not affect whether width/height apply.
WU3
Which display value lets an element sit inline with surrounding content while still accepting a width and height?
 

Key vocabulary

list-style: none
Removes the default bullet points (or numbers) from a list.
Descendant selector
A selector made of two selectors separated by a space, e.g. nav a, that targets an element only when it sits inside a given ancestor, at any depth.
display: block (on a link)
Makes an inline <a> behave as a block, so its whole padded area becomes clickable, not just its text.
:hover
A pseudo-class that applies a style only while the mouse pointer is positioned over an element.
Navigation bar
A horizontal row of links, usually built from a <ul> of <li> and <a> elements, used to move between pages of a site.

From vertical list to horizontal bar

A navigation bar starts as an ordinary list

The standard way to mark up a set of navigation links in HTML is a plain unordered list: a <ul> containing one <li> per link, each holding an <a>. By default this renders as a vertical, bulleted list - not remotely like the horizontal navigation bars seen across almost every website. Turning this ordinary list into a navigation bar is a specific, repeatable five-step CSS build, and every one of the five rules is written using a descendant selector that starts from nav, so the rules only affect this particular list, not every list on the page.

The five steps, in order

Step 1 removes the browser's default bullet points with list-style: none; on nav ul, and typically adds a background colour at the same time. Step 2 applies float: left; to nav li, pulling the list items out of normal flow and into a horizontal row instead of a vertical stack. Step 3 gives each floated nav li a fixed width, so every item in the row lines up evenly rather than being sized to its own text. Step 4 is easy to forget: setting display: block; (plus padding) on nav a, because a default inline <a> is only as clickable as its text - display: block expands the clickable area to fill the whole padded box. Step 5 adds nav a:hover with a different background or colour, giving a visible cue that the link is interactive.

Step 1
Remove bulletsnav ul { list-style: none; background: #042C53; }
Step 2
Float horizontalnav li { float: left; }
Step 3
Even widthsnav li { width: 120px; }
Step 4
Whole box clickablenav a { display: block; padding: 14px; }
Step 5
Hover statenav a:hover { background: #185FA5; }Working nav bar ✓

Live example above, built with the five rules from the pipeline - hover over a link to see step 5 in action.

Why every rule uses a descendant selector

WDD6 introduced the descendant selector; WDD10 is the lesson where it becomes essential rather than optional. If step 1 were written as a bare ul { list-style: none; } instead of nav ul { list-style: none; }, it would remove bullets from every list on the page, not just the navigation - including, for example, a bulleted list of features inside the page's main content. Scoping every rule with nav as the ancestor keeps the whole five-step build isolated to the navigation bar alone, which is exactly why descendant selectors matter here rather than just being a syntax alternative to global element selectors.

Same rule shape, different scope list-style: none;
li { list-style: none; } Every list on the page loses bullets
nav li { list-style: none; } Only the nav bar loses bullets

Worked examples

Example 1 - Full five-step build
nav ul { list-style: none; background: #042C53; }
nav li { float: left; width: 120px; }
nav a { display: block; padding: 14px; color: white; text-decoration: none; }
nav a:hover { background: #185FA5; }
1
Bullets removed, background colour added to the list itself.
2
List items float left and are given an even width, forming a horizontal row.
3
Each link becomes a block so its full padded area is clickable, not just the text.
Hover adds the final interactive cue - a background change while the mouse is over a link.
Example 2 - Why display: block matters on nav a
nav a (default, inline)
padding: 14px;

The padding is added, but only the text itself responds to clicks and hover - clicking beside the word does nothing.

nav a { display: block; }
padding: 14px;

The whole padded rectangle becomes clickable and responds to :hover, not just the text.

display: block is what turns padding on a link into a genuinely larger clickable target, not just visual spacing.
Example 3 - Descendant selector scoping in practice
<nav>
  <ul><li><a href="#">Home</a></li></ul>
</nav>

<section>
  <ul><li>Feature one</li></ul>
</section>
1
Both the nav bar and the feature list are <ul> elements.
2
nav li { float: left; } only matches <li> elements that sit inside <nav>, at any depth.
The feature list's bullets and vertical layout are completely unaffected - the descendant selector never matches it.
Now you try

A pupil writes nav li { float: left; width: 100px; } but forgets to add any rule for nav a at all. The links still float into a row, but clicking anywhere except directly on the text does nothing. Which step is missing?

Steps 1-3 (remove bullets, float, even width) are all present and working - the row layout is correct.
Step 4 is missing: nav a { display: block; padding: ...; }. Without it, the default inline <a> only responds to clicks on its own text, not the full 100px-wide box around it.

Missing step: display: block on nav a.

Practical: build a navigation bar, WebStorm

Download the starter file below - a plain, default bulleted list wrapped in <nav>.

Download WDD10_starter.html
  1. Add an internal stylesheet in <head>.
  2. Work through the five steps from the flow-pipeline above, saving and refreshing after each one.
  3. Use nav ul, nav li and nav a throughout - not bare ul, li or a.
  4. Check the paragraph below the navigation bar in the starter file still displays correctly once the list items are floated.
Common mistakes
Exam tip

Use the exact term descendant selector when describing why nav ul, nav li and nav a are used - not "child selector," which names a different, narrower CSS selector. If asked to list the steps in building a navigation bar, name all five in order: remove bullets, float horizontal, set even widths, make links display: block, then add :hover.

Task Set A - Core questions

Task Set A - Core questions
Complete all questions. Written and code answers reveal a model answer for self-assessment.
A1
Which value of the list-style property removes bullet points from a list?
 
A2
Which CSS property pulls list items out of normal flow and into a horizontal row?
 
A3
Why does the navigation bar CSS use nav ul, nav li and nav a instead of just ul, li and a?
  • Bare element selectors are valid CSS - this is not a syntax problem.
  • Correct.
  • Lists render with or without a nav wrapper - this is about styling scope, not recognition.
  • :hover works on any element, not only inside nav.
A4
Which display value must be set on the <a> inside each <li> so its whole padded box - not just the text - is clickable?
 
A5
What does the :hover pseudo-class do?
  • That describes a different, click-based state, not hover.
  • Hover is about mouse position, not page load state.
  • Correct.
  • That would describe a different selector, not :hover.
A6
Write a descendant selector that targets only <a> elements inside <nav>.
 
A7
Explain why float is the property used to arrange the list items horizontally, referring to what float does to normal document flow.
Model answer
A8
A pupil writes li { list-style: none; float: left; } instead of nav li { list-style: none; float: left; }. What is the consequence?
  • Correct.
  • Scope is exactly what differs between the two selectors.
  • li { } is valid CSS, it is just unscoped.
  • The bare selector matches every li on the page, not just the first.
A9
This CSS builds a navigation bar, but clicking anywhere on a link except directly on its text does nothing, even though the links sit in a row correctly. Find the missing step and write the corrected CSS.
nav ul { list-style: none; }
nav li { float: left; width: 100px; }
nav a { color: white; }
nav ul { list-style: none; }
nav li { float: left; width: 100px; }
nav a { display: block; padding: 12px; color: white; }

nav a is missing display: block (and padding). Without it, the default inline link only responds to clicks on its own text, not the full 100px-wide floated box around it.

Task Set B - Extension

Task Set B - Extension
This lesson is scoped for a double period but lands on a single - treat B3 as follow-up work if class time runs out.
B1
Describe, in your own words, all five steps of the navigation bar build, in order.
Model answer
B2
A pupil relies only on :hover to show which link is currently interactive. Explain one limitation of this for a user who navigates the page using only a keyboard, not a mouse.
Model answer
B3 - Practical (WebStorm, from scratch)
Build a second navigation bar from scratch, styled differently from the practical above.
  1. In WebStorm, create a new HTML file called nav-variant.html.
  2. Add a <nav> containing a <ul> of at least five <li><a> links.
  3. Build all five steps yourself from memory, without looking at the earlier example - use a different background colour and item width to check you understand the pattern rather than just copying values.
  4. Save, open in a browser, and check the bar is horizontal, fully clickable across each item's width, and changes colour on hover.
nav ul {
  list-style: none;
  background: #712B13;
}

nav li {
  float: left;
  width: 110px;
}

nav a {
  display: block;
  padding: 12px;
  color: white;
  text-decoration: none;
  text-align: center;
}

nav a:hover {
  background: #D85A30;
}

Compare your own file against this rather than copying it - the exact colours and width do not matter, but all five rules (list-style, float, width, display: block, :hover) should be present and scoped with nav.

File this in OneNote under:
Higher Computing Science > Web Design & Development > WDD10

Teacher notes - Shift+T to toggle

Timing risk: this lesson is scoped for a double period but is scheduled on a single (Tue 1 Dec 2026, per WDD.md §2a/§4 - one of the four flagged "too short" mismatches, along with WDD4, WDD7 and WDD16). Do not try to cover everything in class.

Suggested compressed timing (single period, ~50 min): 5 min warm up, 8 min five-step notes + flow-pipeline + live demo, 6 min descendant-selector-scoping notes + context box, 8 min Examples 2-3, remaining time on Task Set A questions A1-A6 only. Push A7-A9 and all of Task Set B, including the B3 WebStorm practical, to independent/homework time.

Scope control: deliberately does not cover flexbox or CSS Grid as alternative modern nav bar techniques - the SQA spec and Chris's own L9 both teach the float-based five-step build specifically, and flexbox is explicitly out of scope for this course (see CLAUDE.md WDD scope note).

Descendant selector terminology is the highest-value fact in this lesson for exam purposes - Chris's own materials use the incorrect term "child selector" for this pattern, and WDD6 already corrected this ahead of today. A3/A8/exam tip all reinforce the correct term and the practical consequence of getting scope wrong.