Skip to content

Mobile-First Header — Complete Guide

DodaTech Updated 2026-06-28 11 min read

In this tutorial, you will learn about Mobile. We cover key concepts, practical examples, and best practices to help you master this topic.

Mobile-first headers use compact height, hamburger menus, sticky positioning, search integration, and safe-area insets for navigation on small screens.

What You'll Learn

  • Compact header sizing
  • Hamburger menu and overlay nav
  • Sticky vs static headers
  • Search bar integration
  • Header with actions (cart, profile)
  • Safe area insets for notched devices
  • Collapsible header on scroll

Why It Matters

  • Headers take valuable screen real estate on mobile
  • Oversized headers push content below the fold
  • Poor navigation hides important actions
  • Scrolling with a sticky header reduces content area

Real-World Use

  • An e-commerce header with search and cart
  • A news site with collapsible header on scroll
  • A dashboard with profile and notifications
  • A documentation site with hamburger menu
flowchart LR
  A[Mobile-First Header] --> B[Sizing]
  A --> C[Navigation]
  A --> D[Sticky]
  A --> E[Actions]
  B --> F[48-56px height]
  C --> G[Hamburger menu]
  D --> H[Sticky + collapse]
  E --> I[Search + icons]

Compact Header Sizing

Mobile headers should be compact — typically 48-56px — to maximize content area.

Code Example: Basic Compact Header

<header class="mobile-header">
    <button class="header-btn" aria-label="Open menu">
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M3 12h18M3 6h18M3 18h18"/>
        </svg>
    </button>

    <a href="/" class="header-logo">
        <span class="logo-text">DodaTech</span>
    </a>

    <div class="header-actions">
        <button class="header-btn" aria-label="Search">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"/>
                <path d="M21 21l-4.35-4.35"/>
            </svg>
        </button>
        <button class="header-btn" aria-label="Notifications">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/>
            </svg>
            <span class="header-badge">3</span>
        </button>
    </div>
</header>

<style>
.mobile-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 56px;
    padding: 0 0.75rem;
    background: #fff;
    border-bottom: 1px solid #e5e7eb;
    position: sticky;
    top: 0;
    z-index: 100;
}

.header-logo {
    text-decoration: none;
}

.logo-text {
    font-size: 1.25rem;
    font-weight: 700;
    color: #1f2937;
}

.header-actions {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.header-btn {
    position: relative;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    color: #6b7280;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}

.header-btn:active {
    background: #f3f4f6;
    color: #374151;
}

.header-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    font-size: 0.625rem;
    font-weight: 700;
    color: #fff;
    background: #ef4444;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>

Expected output: A compact 56px header with hamburger menu on the left, logo in the center, and action icons (search, notifications with badge) on the right. All buttons are 44x44px touch targets.

Hamburger Menu and Overlay Navigation

The hamburger menu is the standard mobile navigation pattern.

Code Example: Hamburger Menu

<header class="mobile-header" id="app-header">
    <button class="header-btn hamburger-btn" id="menu-btn" aria-label="Open menu" aria-expanded="false">
        <span class="hamburger-line"></span>
        <span class="hamburger-line"></span>
        <span class="hamburger-line"></span>
    </button>
    <a href="/" class="header-logo"><span class="logo-text">DodaTech</span></a>
    <div class="header-actions">
        <button class="header-btn" aria-label="Search">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
            </svg>
        </button>
    </div>
</header>

<nav class="mobile-nav-overlay" id="nav-overlay" hidden>
    <div class="nav-overlay-backdrop"></div>
    <div class="nav-drawer" role="dialog" aria-modal="true" aria-label="Navigation menu">
        <div class="nav-drawer-header">
            <span class="logo-text">DodaTech</span>
            <button class="header-btn" id="close-menu" aria-label="Close menu">
                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M18 6L6 18M6 6l12 12"/>
                </svg>
            </button>
        </div>

        <ul class="nav-drawer-list">
            <li><a href="#" class="nav-drawer-link active">Home</a></li>
            <li><a href="#" class="nav-drawer-link">Tutorials</a></li>
            <li><a href="#" class="nav-drawer-link">Courses</a></li>
            <li><a href="#" class="nav-drawer-link">Blog</a></li>
            <li><a href="#" class="nav-drawer-link">About</a></li>
            <li><a href="#" class="nav-drawer-link">Contact</a></li>
        </ul>

        <div class="nav-drawer-footer">
            <a href="#" class="nav-drawer-link">Log In</a>
            <a href="#" class="nav-drawer-link nav-signup">Sign Up</a>
        </div>
    </div>
</nav>

<script>
const menuBtn = document.getElementById('menu-btn');
const closeMenu = document.getElementById('close-menu');
const overlay = document.getElementById('nav-overlay');
const backdrop = overlay.querySelector('.nav-overlay-backdrop');

function openNav() {
    overlay.hidden = false;
    document.body.style.overflow = 'hidden';
    menuBtn.setAttribute('aria-expanded', 'true');
    requestAnimationFrame(() => {
        overlay.querySelector('.nav-drawer').classList.add('open');
        backdrop.classList.add('open');
    });
}

function closeNav() {
    overlay.querySelector('.nav-drawer').classList.remove('open');
    backdrop.classList.remove('open');
    menuBtn.setAttribute('aria-expanded', 'false');
    overlay.addEventListener('transitionend', function handler() {
        overlay.hidden = true;
        overlay.removeEventListener('transitionend', handler);
        document.body.style.overflow = '';
    }, { once: true });
}

menuBtn.addEventListener('click', openNav);
closeMenu.addEventListener('click', closeNav);
backdrop.addEventListener('click', closeNav);
</script>

<style>
.hamburger-btn {
    display: flex;
    flex-direction: column;
    gap: 5px;
    padding: 10px;
}

.hamburger-line {
    display: block;
    width: 24px;
    height: 2px;
    background: #1f2937;
    border-radius: 1px;
    transition: transform 0.2s;
}

.mobile-nav-overlay {
    position: fixed;
    inset: 0;
    z-index: 200;
}

.nav-overlay-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    opacity: 0;
    transition: opacity 0.3s ease;
}

.nav-overlay-backdrop.open {
    opacity: 1;
}

.nav-drawer {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 300px;
    max-width: 85vw;
    background: #fff;
    transform: translateX(-100%);
    transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
    display: flex;
    flex-direction: column;
    padding-top: env(safe-area-inset-top, 0px);
}

.nav-drawer.open {
    transform: translateX(0);
}

.nav-drawer-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid #e5e7eb;
    min-height: 56px;
}

.nav-drawer-list {
    list-style: none;
    padding: 0.5rem 0;
    margin: 0;
    flex: 1;
    overflow-y: auto;
}

.nav-drawer-link {
    display: block;
    padding: 0.875rem 1rem;
    font-size: 1rem;
    font-weight: 500;
    color: #374151;
    text-decoration: none;
    min-height: 44px;
    line-height: 1.3;
    -webkit-tap-highlight-color: transparent;
}

.nav-drawer-link:active {
    background: #f3f4f6;
}

.nav-drawer-link.active {
    color: #3b82f6;
    font-weight: 600;
}

.nav-drawer-footer {
    border-top: 1px solid #e5e7eb;
    padding: 0.5rem 0;
}

.nav-signup {
    color: #3b82f6;
    font-weight: 600;
}
</style>

Expected output: Tapping the hamburger icon slides a navigation drawer from the left with a backdrop overlay. The drawer has a header with close button, navigation links, and a footer with login/signup. The backdrop is tappable to close.

Collapsible Header on Scroll

Hide the header when scrolling down and reveal it when scrolling up to maximize content space.

Code Example: Collapsible Header

<header class="collapsible-header" id="collapsible-header">
    <button class="header-btn" aria-label="Open menu">
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M3 12h18M3 6h18M3 18h18"/>
        </svg>
    </button>
    <a href="/" class="header-logo"><span class="logo-text">DodaTech</span></a>
    <div class="header-actions">
        <button class="header-btn" aria-label="Search">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
            </svg>
        </button>
    </div>
</header>

<main class="content" style="padding: 1rem; max-width: 480px;">
    <p style="margin-bottom: 1rem; color: #6b7280;">Scroll down to see the header collapse. Scroll up to reveal it again.</p>
    <div style="height: 2000px; background: linear-gradient(180deg, #f9fafb, #e5e7eb); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #9ca3af;">Content area</div>
</main>

<script>
const header = document.getElementById('collapsible-header');
let lastScrollY = 0;
let ticking = false;

window.addEventListener('scroll', () => {
    if (!ticking) {
        requestAnimationFrame(() => {
            const currentScrollY = window.scrollY;
            if (currentScrollY > 80) {
                if (currentScrollY > lastScrollY) {
                    header.style.transform = 'translateY(-100%)';
                } else {
                    header.style.transform = 'translateY(0)';
                }
            } else {
                header.style.transform = 'translateY(0)';
            }
            lastScrollY = currentScrollY;
            ticking = false;
        });
        ticking = true;
    }
}, { passive: true });
</script>

<style>
.collapsible-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 56px;
    padding: 0 0.75rem;
    background: #fff;
    border-bottom: 1px solid #e5e7eb;
    z-index: 100;
    transition: transform 0.3s ease;
}

.content {
    margin-top: 56px;
}
</style>

Expected output: When scrolling down past 80px, the header slides up and hides. When scrolling up, the header slides back into view. This maximizes content space while keeping navigation accessible.

Integrating search into the header for quick access.

<header class="search-header" id="search-header">
    <div class="search-header-row">
        <button class="header-btn" aria-label="Back">
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M19 12H5M12 19l-7-7 7-7"/>
            </svg>
        </button>

        <div class="search-box">
            <svg class="search-box-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
            </svg>
            <input
                type="search"
                class="search-box-input"
                placeholder="Search tutorials..."
                autocomplete="off"
                inputmode="search"
                enterkeyhint="search"
            >
            <button class="search-clear" aria-label="Clear search" hidden>
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M18 6L6 18M6 6l12 12"/>
                </svg>
            </button>
        </div>

        <button class="header-btn" aria-label="Cancel">Cancel</button>
    </div>

    <div class="search-suggestions" id="search-suggestions" hidden>
        <div class="suggestions-group">
            <h4 class="suggestions-label">Recent Searches</h4>
            <ul class="suggestions-list">
                <li><button class="suggestion-item">CSS Flexbox</button></li>
                <li><button class="suggestion-item">JavaScript promises</button></li>
                <li><button class="suggestion-item">Responsive design</button></li>
            </ul>
        </div>
        <div class="suggestions-group">
            <h4 class="suggestions-label">Popular</h4>
            <ul class="suggestions-list">
                <li><button class="suggestion-item">Mobile-first forms</button></li>
                <li><button class="suggestion-item">CSS Grid complete guide</button></li>
            </ul>
        </div>
    </div>
</header>

<script>
const searchInput = document.querySelector('.search-box-input');
const searchSuggestions = document.getElementById('search-suggestions');
const clearBtn = document.querySelector('.search-clear');

searchInput.addEventListener('focus', () => {
    searchSuggestions.hidden = false;
});

searchInput.addEventListener('input', function() {
    clearBtn.hidden = this.value.length === 0;
});

clearBtn.addEventListener('click', () => {
    searchInput.value = '';
    searchInput.focus();
    clearBtn.hidden = true;
});

document.addEventListener('click', (e) => {
    if (!e.target.closest('.search-header')) {
        searchSuggestions.hidden = true;
    }
});
</script>

<style>
.search-header {
    background: #fff;
    border-bottom: 1px solid #e5e7eb;
    position: sticky;
    top: 0;
    z-index: 100;
}

.search-header-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
}

.search-box {
    flex: 1;
    position: relative;
    display: flex;
    align-items: center;
}

.search-box-icon {
    position: absolute;
    left: 0.75rem;
    color: #9ca3af;
    pointer-events: none;
}

.search-box-input {
    width: 100%;
    height: 40px;
    padding: 0 2.5rem 0 2.5rem;
    font-size: 0.9375rem;
    border: 2px solid #e5e7eb;
    border-radius: 8px;
    background: #f9fafb;
    font-family: inherit;
    transition: border-color 0.2s;
    -webkit-appearance: none;
    appearance: none;
}

.search-box-input:focus {
    border-color: #3b82f6;
    outline: none;
    background: #fff;
}

.search-clear {
    position: absolute;
    right: 0.25rem;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    border-radius: 50%;
    cursor: pointer;
    color: #9ca3af;
}

.search-suggestions {
    padding: 0.5rem 1rem 1rem;
    border-top: 1px solid #e5e7eb;
}

.suggestions-group {
    margin-bottom: 1rem;
}

.suggestions-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: #9ca3af;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin: 0 0 0.5rem;
}

.suggestions-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.suggestion-item {
    width: 100%;
    text-align: left;
    padding: 0.625rem 0;
    font-size: 0.875rem;
    color: #374151;
    background: transparent;
    border: none;
    cursor: pointer;
    min-height: 44px;
    -webkit-tap-highlight-color: transparent;
}

.suggestion-item:active {
    color: #3b82f6;
}
</style>

Expected output: The header has a centered search input with back button and cancel action. On focus, recent searches and popular suggestions appear below the search bar. The clear button appears when text is entered.

Common Mistakes

  1. Oversized header (60px+) on mobile — Every extra pixel of header height pushes content below the fold. Keep it 48-56px.
  2. Sticky header without collapse — A permanently visible sticky header reduces content height by 56px on every screen.
  3. Hamburger too small — The hamburger icon itself must be inside a 44x44px touch target, not just the icon size.
  4. No active state on nav links — Users cannot tell which page they are on. Use a highlighted active state.
  5. Header without safe area padding — On notched devices, content hides behind the notch or status bar.
  6. Search bar that covers the header — A full-width search bar that replaces the header loses navigation. Use an overlay or push layout.
  7. No backdrop close on drawer — Users must find a close button instead of tapping the overlay to dismiss the menu.

Practice Questions

  1. What is the ideal height for a mobile header? 48-56px. This provides enough space for touch targets without wasting screen real estate.
  2. How does a collapsible header improve UX? It hides on scroll down to maximize content area and reveals on scroll up to provide navigation access.
  3. What is the purpose of the hamburger menu? It hides navigation links behind a button, saving header space while providing access to the full navigation.
  4. What CSS property prevents header overlap on notched devices? padding-top: env(safe-area-inset-top, 0px) adds padding for the status bar area.
  5. How do you ensure hamburger menu Accessibility? Use aria-label on the button, aria-expanded for menu state, and role="dialog" with aria-modal="true" on the drawer.

Challenge

Build a complete header system for a documentation site. Include: (1) a sticky 56px header with hamburger menu, logo, and search icon, (2) a collapsible behavior that hides on scroll down and reveals on scroll up, (3) a left drawer navigation with 3 sections (Getting Started, API Reference, Guides) each with 4-5 links, (4) active state highlighting, (5) search overlay that pushes content down, (6) safe area insets, (7) backdrop close for the drawer, (8) focus trap when the drawer is open, (9) Escape key to close the drawer.

FAQ

Should I use a sticky header or a static one?

Use sticky headers for content-heavy sites where navigation must always be accessible (docs, e-commerce). Use static headers for storytelling pages where immersion matters (landing pages, articles).

Is the hamburger menu bad for UX?

Research shows hamburger menus have lower engagement than visible tabs. Use hamburger menus for secondary navigation. Primary navigation items should be visible as icons or tabs in the header.

How do I handle the header on desktop?

On desktop (768px+), show navigation links horizontally in the header instead of hiding them behind a hamburger. Use media queries to switch between mobile and desktop navigation patterns.

What is the best way to implement search in the header?

Use an expandable search bar: a search icon that expands to a full input on tap. For search-heavy sites, use a persistent search bar as shown in the example above.

How do I prevent the header from flickering on scroll?

Use requestAnimationFrame to throttle scroll events and transition: transform with a small delay (0.15-0.3s) for smooth hide/show animation.

Mini Project

Build a responsive header for an e-commerce site. On mobile: (1) 56px sticky header with hamburger, logo, search icon, and cart icon with badge, (2) collapsible on scroll down, (3) left drawer with 15 links organized in 4 categories (Men, Women, Kids, Sale) with a footer for Account and Help, (4) search overlay that shows recent searches and popular products, (5) cart preview dropdown showing 3 items with total and checkout button. On desktop: show horizontal nav links, hide the hamburger, keep search and cart icons.

What's Next

Continue with Lesson 15: Mobile-First Performance to optimize web performance for mobile devices.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro