Skip to content

Styling the Header and Navigation

The header is the first element a visitor sees on every page of the STO site. It sets the brand tone, orients users, and provides navigation. This lesson builds it to production quality — applying custom properties, Flexbox, responsive breakpoints, and hover/active states.

The HTML structure the CSS targets:

<header class="site-header">
<div class="content-wrapper header-inner">
<a href="index.html" class="site-logo">Summit Trail Outfitters</a>
<nav class="site-nav" aria-label="Main navigation">
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="tours.html">Tours</a></li>
<li><a href="blog.html">Blog</a></li>
<li><a href="about.html">About</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</div>
</header>
/* === Site Header === */
.site-header {
background-color: var(--color-primary);
box-shadow: var(--shadow-md);
position: sticky;
top: 0;
z-index: 100;
}
.header-inner {
display: flex;
flex-direction: column;
padding: var(--space-md);
gap: var(--space-sm);
}
/* Tablet and above: logo left, nav right */
@media (min-width: 768px) {
.header-inner {
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: var(--space-md) 0;
}
}

position: sticky; top: 0 keeps the header at the top of the viewport as the user scrolls. z-index: 100 ensures it appears above all other content.

.site-logo {
font-family: var(--font-heading);
font-size: 1.375rem;
font-weight: 700;
color: var(--color-bg);
text-decoration: none;
letter-spacing: -0.01em;
line-height: 1;
}
.site-logo:hover {
color: var(--color-accent);
}

The heading font (Playfair Display) gives the logo a distinctive, brand-appropriate feel while matching the heading style used throughout the site.

.nav-links {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
@media (min-width: 768px) {
.nav-links {
flex-direction: row;
gap: var(--space-lg);
align-items: center;
}
}
.nav-links a {
color: var(--color-accent);
text-decoration: none;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.02em;
text-transform: uppercase;
padding: var(--space-xs) 0;
border-bottom: 2px solid transparent;
transition: color var(--transition-fast), border-color var(--transition-fast);
}
.nav-links a:hover {
color: var(--color-white);
border-bottom-color: var(--color-accent);
}

The border-bottom: 2px solid transparent on the default state reserves space for the hover underline — preventing the layout from shifting when the border appears on hover.

The active page link should be visually distinct so users always know where they are:

.nav-links a[aria-current="page"],
.nav-links a.active {
color: var(--color-white);
border-bottom-color: var(--color-accent);
}

In your HTML, add aria-current="page" to the link that matches the current page:

<li><a href="tours.html" aria-current="page">Tours</a></li>

aria-current="page" serves double duty: it is a CSS hook for the active style, and it communicates to screen readers that this is the current page — an accessibility win that costs nothing.

A hamburger menu hides the nav links on mobile and reveals them when the user taps a button. The toggle behavior requires JavaScript — but the HTML and CSS live here, in the header.

First, add the button to every STO HTML file (index.html, about.html, tours.html, blog.html, blog-article.html, contact.html). Place it inside <div class="header-inner">, between the logo link and the <nav>:

<button class="hamburger-btn" aria-expanded="false" aria-label="Toggle navigation">
<span></span>
<span></span>
<span></span>
</button>

The three <span> elements are the three lines of the icon. aria-expanded="false" tells screen readers the nav is currently closed — JavaScript will keep this in sync when the nav opens and closes.

The .header-inner needs to change from a column stack to a row so the logo and button sit side by side, with the nav wrapping below on its own line:

.header-inner {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: var(--space-md);
gap: var(--space-sm);
}

Then add the following after the active link state styles:

/* === Mobile Nav Toggle === */
.hamburger-btn {
display: flex;
flex-direction: column;
gap: 5px;
background: none;
border: none;
cursor: pointer;
padding: var(--space-xs);
}
.hamburger-btn span {
display: block;
width: 24px;
height: 2px;
background-color: var(--color-bg);
border-radius: 2px;
}
@media (min-width: 768px) {
.hamburger-btn {
display: none;
}
}
.site-nav {
display: none;
width: 100%;
}
.site-nav.nav-open {
display: block;
}
@media (min-width: 768px) {
.site-nav {
display: block;
width: auto;
}
}

At mobile width, the hamburger button is now visible and the nav is hidden. The nav will not open yet — that requires the JavaScript you will write in JavaScript Foundations. At 768px and above, the button hides and the nav shows as normal.

A sticky header adds visual weight as the page scrolls. Adding a subtle box-shadow enhancement when the header is stuck is a common pattern:

.site-header {
position: sticky;
top: 0;
z-index: 100;
transition: box-shadow var(--transition-normal);
}

The base box-shadow from var(--shadow-md) is already set — it provides depth against the page content that scrolls beneath.

Apply the header and mobile nav styles to the STO site:

  1. Add all header CSS to styles.css, including the updated .header-inner, .hamburger-btn, and mobile nav toggle rules.

  2. Add the <button class="hamburger-btn"> to every STO HTML file inside .header-inner, between the logo and the <nav>.

  3. Update each HTML file to add aria-current="page" to the correct nav link for that page.

  4. Open index.html in Chrome at 375px (DevTools device toolbar). Confirm:

    • The hamburger button is visible in the top right of the header
    • The nav links are hidden
    • The logo and button sit on the same row
  5. Widen to 768px. Confirm:

    • The hamburger button disappears
    • The nav links appear in a row to the right of the logo
  6. Scroll down the page. The header should stay pinned to the top of the viewport.

  7. Hover over a nav link and verify the color change and underline animation work.

  • Use custom properties (var(--color-primary), etc.) for all color values in the header — this keeps it consistent with the rest of the site and easy to update.
  • position: sticky; top: 0; z-index: 100 pins the header to the top of the viewport during scroll.
  • flex-direction: row; flex-wrap: wrap on .header-inner lets the logo and hamburger button sit on one row, with the nav wrapping to a second row below on mobile.
  • aria-current="page" on the active link provides both the CSS hook for the active style and accessibility information for screen readers.
  • Reserve border space in the default state to prevent layout shift on hover.
  • The hamburger button and .site-nav hide/show CSS belong here — the toggle behavior requires JavaScript, but the HTML structure and visual states are a CSS concern.

Lesson 03 styles the homepage — the highest-impact page on the site, with a full-bleed hero, featured tour cards, and a call-to-action section.