Skip to main content
April 1, 2026Dan Rodney/16 min read

Off-Screen Side Nav Using Only CSS

Create responsive navigation menus with pure CSS

Tutorial Coverage

11
step-by-step sections
0
lines of JavaScript required
3
core CSS techniques

Topics Covered in This HTML & CSS Tutorial:

Responsive Off-screen Navigation, Toggling the Navigation with a Checkbox, CSS Transitions

Exercise Preview

offscreen nav done

Exercise Overview

In this exercise, you'll master one of modern web development's most essential patterns: creating an elegant off-screen navigation menu that remains hidden until users need it. What makes this approach particularly powerful is that we'll build the entire interaction using only CSS—no JavaScript required. This technique has become a cornerstone of responsive design, offering users a clean interface while maintaining full navigational functionality across all device sizes.

By leveraging CSS pseudo-classes and the sibling selector, you'll discover how semantic HTML and strategic styling can create sophisticated user interactions. This approach not only reduces JavaScript dependencies but also ensures your navigation works reliably across all browsers and devices, including those with JavaScript disabled.

Key Features You'll Build

Off-Screen Navigation

Menu slides in from the side when activated. Uses CSS transforms and positioning for smooth animations.

Checkbox Toggle Control

Pure CSS solution using checkbox checked states. No JavaScript dependencies required for functionality.

Responsive Design

Different layouts for mobile and desktop breakpoints. Horizontal nav on large screens, slide-out on mobile.

Styling the Nav

We'll begin by establishing the foundational styles for our navigation system, starting with the mobile-first approach that has become the industry standard for responsive design.

  1. We'll be switching to a new folder of prepared files for this exercise. In your code editor, close all open files to avoid confusion and maintain a clean workspace.
  2. For this exercise we'll be working with the Off-Screen Nav folder located in Desktop > Class Files > Advanced HTML CSS Class. Open that folder in your code editor if it allows you to (like Visual Studio Code does).
  3. Open index.html from the Off-Screen Nav folder.
  4. Preview index.html in a browser.

    The navigation structure is already in place with basic styling applied. This gives us a solid foundation to build upon. Leave the page open in your browser so you can see changes in real-time.

  5. We'll start by positioning the mobile menu using CSS's powerful positioning system. In your code editor, open main.css from the css folder (in the Off-Screen Nav folder).
  6. Scroll down to the Media Queries comment. Below that, add the following code:

    @media (max-width: 700px) {
       nav {
          position: fixed;
       }
    }
  7. Save the file and observe how this single property transforms the navigation's behavior.
  8. Reload your browser to see the changes take effect.
  9. Resize the window to mobile phone dimensions, and you'll notice that the nav now sits on top of the page content, exactly as intended. This fixed positioning removes the navigation from the normal document flow, allowing us to control its placement precisely.
  10. Return to main.css in your code editor to refine the navigation's dimensions.
  11. Add the following width and height properties to the nav rule you just created:

    nav {
       position: fixed;
       width: 12em;
       height: 100%;
    }
  12. Save the file and observe how these dimensions create a full-height sidebar that's appropriately sized for mobile interaction.
  13. Reload your browser. The nav now occupies the full viewport height with a comfortable width for touch interaction.
  14. Resize the window to be very short vertically, simulating a phone in landscape orientation where some navigation items might get cut off.
  15. Try to scroll within the navigation area to see the missing items—you'll discover that scrolling isn't possible yet. This is a common usability issue that we need to address, especially for users with landscape-oriented devices or varying screen heights.
  16. Return to main.css and add the crucial overflow property to enable internal scrolling:

    nav {
       position: fixed;
       width: 12em;
       height: 100%;
       overflow-y: auto;
    }

    The overflow-y: auto property creates a scrollable container only when needed, ensuring users can always access all navigation items regardless of viewport height.

  17. Save the file and reload the page in your browser. Now test the scrolling functionality—you should be able to scroll through all navigation items even when the viewport is constrained.
  18. Resize the browser back to desktop view (the nav will return to its original position). Now we'll create the desktop layout that takes advantage of larger screen real estate.
  19. Return to main.css in your code editor to add desktop-specific styling.
  20. At the bottom of your styles, after the current media query, add this new desktop-focused media query:

    @media (min-width: 701px) {
       nav ul li {
          display: inline-block;
       }
    }

    This transforms the vertical navigation into a horizontal layout that's more appropriate for desktop screens where vertical space is at a premium.

  21. Save the file and test the transformation.
  22. Reload your browser. The navigation now displays as a single horizontal line across the top—a much more efficient use of desktop screen space.
  23. Return to your code editor to fine-tune the desktop navigation alignment.
  24. In the min-width: 701px media query, add styling to properly position the navigation:

    @media (min-width: 701px) {
       nav {
          text-align: right;
          padding-right:.6em;
       }
       nav ul li {
          display: inline-block;
       }
    }

    Right-alignment with appropriate padding creates a professional appearance that follows common web design patterns users expect.

  25. Save the file and verify the changes.
  26. Reload your browser and test both breakpoints by resizing the window. Your navigation should now provide an optimal experience at both mobile and desktop sizes.

Responsive Breakpoints

Mobile (max-width)
700
Desktop (min-width)
701
Mobile-First Approach

Start with fixed positioning for mobile, then add horizontal layout for desktop. The overflow-y auto property ensures scrollable navigation on short screens.

Adding a Clickable Element to Open & Close the Nav

Now comes the innovative part: creating menu functionality without JavaScript. We'll use a checkbox input combined with CSS pseudo-classes—a technique that demonstrates the power of semantic HTML and modern CSS selectors.

The checkbox approach works because checkboxes have a :checked pseudo-class that allows us to style elements differently based on the checkbox state. This binary state (checked/unchecked) perfectly mirrors our menu needs (open/closed). While this might seem unconventional, it's a robust, accessible solution that works across all browsers.

  1. Let's add the checkbox that will control our menu state. In your code editor open index.html.
  2. Directly below the opening body tag, add the following input element:

    <body>
       <input id="nav-checkbox" type="checkbox">
       <img src="img/nyc-logo.svg" class="nyc-logo" ALT="NYC Logo">

    The placement is crucial—the checkbox must appear before the navigation in the DOM for our CSS selectors to work properly.

  3. Save the file and prepare to style this control element.
  4. Switch to main.css to make the checkbox temporarily visible for testing.
  5. At the bottom of your general styles, below the nav li a:hover rule, add this positioning rule:

    #nav-checkbox {
       position: absolute;
       left: 50%;
    }

    This temporary positioning makes the checkbox easy to find during development. We'll hide it properly once everything is working.

  6. Save the file and test the checkbox placement.
  7. Reload your browser. You should see a small checkbox centered at the top of the page—our temporary control interface.
  8. Now we'll create the CSS rule that responds to the checkbox state. In your code editor, return to main.css.
  9. In the max-width: 700px media query, below the nav rule, add this crucial selector:

    #nav-checkbox:checked ~ nav {
       display: none;
    }

    This CSS uses the general sibling combinator (~) to target the nav element, but only when it follows a checked checkbox with the ID "nav-checkbox". The order of elements in your HTML is critical—the checkbox must appear before the nav in the DOM tree for this selector to function.

  10. Save the file and test this interactive behavior.
  11. Reload your browser, ensuring the window width is less than 700px for mobile view.
  12. Click the checkbox and observe how the navigation disappears when checked—you've just created your first CSS-only interactive element!

This demonstrates the fundamental principle behind our off-screen navigation: using CSS selectors to create different visual states based on form input status.

Checkbox Implementation Process

1

Add Checkbox Input

Place checkbox directly after opening body tag with unique ID for targeting

2

Position for Testing

Use absolute positioning at 50% left to make checkbox visible during development

3

Create CSS Rule

Use sibling selector to target nav when checkbox is checked

Moving the Nav Off-Screen

While hiding the menu with display: none works functionally, it's not the elegant solution we want. Modern users expect smooth, animated transitions. We'll create a more sophisticated approach by positioning the navigation off-screen and animating it into view.

  1. In your code editor, return to main.css to implement the off-screen positioning technique.
  2. In the max-width: 700px media query, enhance the nav rule with positioning and layering properties:

    nav {
       position: fixed;
       width: 12em;
       left: -12em;
       height: 100%;
       overflow-y: auto;
       z-index: 100;
    }

    The negative left value must equal the element's width to ensure complete concealment. The high z-index ensures the navigation appears above all other page content when visible.

  3. Now modify the checkbox response rule to bring the navigation back into view:

    #nav-checkbox:checked ~ nav {
       left: 0;
    }

    Instead of hiding the navigation entirely, we're now repositioning it from off-screen to its intended location.

  4. Save the file and test this more sophisticated interaction.
  5. Reload your browser and toggle the checkbox. The navigation now slides into position rather than simply appearing—a much more professional user experience.

    The basic functionality works, but we can enhance it further with CSS transitions to create smooth, polished animations that match modern user expectations.

  6. Return to main.css to add the finishing touch.
  7. Add a transition property to the nav rule for smooth animations:

    nav {
       position: fixed;
       width: 12em;
       left: -12em;
       height: 100%;
       overflow-y: auto;
       z-index: 100;
       transition: all.2s ease;
    }

    This transition applies to all animatable properties, ensuring smooth movement regardless of which properties change. The 0.2-second duration with easing creates a responsive feel without being sluggish.

  8. Save the file and experience the polished result.
  9. Reload your browser and toggle the checkbox. Your navigation now slides smoothly from the side—a professional interaction that rivals any JavaScript-powered solution.

Critical Positioning Rule

The negative left position value must equal the nav width to ensure complete off-screen hiding. Set left: -12em when width is 12em.

CSS Transitions vs Display None

Pros
Smooth sliding animation with CSS transitions
Better user experience with visual feedback
Can control timing and easing functions
Cons
Requires precise positioning calculations
Element still exists in document flow
Needs z-index management for layering

Adding a Proper Menu Button

While our checkbox proves the concept works, users need something more intuitive to interact with. HTML form labels provide the perfect solution—when clicked, they activate their associated form controls. This semantic relationship gives us accessible, keyboard-friendly menu control.

  1. Return to index.html to add a user-friendly interface element.
  2. Just below the checkbox input, add this label element:

    <input id="nav-checkbox" type="checkbox">
    <label for="nav-checkbox" class="menu-button">MENU</label>

    The for attribute creates the essential connection between the label and checkbox. This relationship is what allows clicking the label to control the checkbox state.

  3. Save the file and prepare to style this new interface element.
  4. Switch to main.css to create an attractive, functional menu button.
  5. In the max-width: 700px media query, after the #nav-checkbox:checked ~ nav rule, add styling for the menu button:

    .menu-button {
       position: absolute;
       top: 10px;
       right: 10px;
       color: #fff;
    }

    Positioning the menu button in the upper-right corner follows established mobile UI conventions that users expect.

  6. Save the file and test the basic functionality.
  7. Reload your browser and interact with the new menu button.
  8. Click the MENU text in the upper-right corner and observe several behaviors:

    • The checkbox automatically changes state even though you're clicking the label—demonstrating the power of semantic HTML relationships.
    • The cursor doesn't change to indicate clickability, which could confuse users.
    • Double-clicking selects the text like any other page content, which feels unnatural for a button.
  9. Return to main.css to refine the user experience with proper interactive styling.
  10. Enhance the .menu-button rule with proper interactive properties:

    .menu-button {
       position: absolute;
       top: 10px;
       right: 10px;
       color: #fff;
       cursor: pointer;
       -webkit-user-select: none;
       -moz-user-select: none;
       -ms-user-select: none;
       user-select: none;
    }

    The cursor property provides immediate visual feedback, while the user-select properties prevent text selection across all browsers, making the label behave like a proper button.

  11. Save the file and test the improved interactions.
  12. Reload your browser and verify the enhanced behavior: proper cursor feedback and no unwanted text selection.
  13. Resize the window larger than 700px to test the desktop view—notice the menu button is still visible when it shouldn't be.
  14. Return to main.css to hide the menu button on desktop screens where it's unnecessary.
  15. At the beginning of the min-width: 701px media query, add this rule:

    .menu-button {
       display: none;
    }

    This ensures the mobile-specific menu button only appears where it's needed, maintaining clean desktop layouts.

  16. Save the file and verify the responsive behavior.
  17. Reload your browser and test both viewport sizes—the menu button should only appear in mobile view where the off-screen navigation is active.
  18. Resize back to mobile view to continue development.

Label-Checkbox Connection

The label's 'for' attribute must match the checkbox's 'id' exactly. This creates the functional connection that allows the label to control the checkbox state.

Button Usability Checklist

0/3

Adding a Close Button

Good user experience design provides multiple ways to complete actions. While our menu button opens the navigation, users should have a clear, prominent way to close it again. We'll add a close button directly within the menu using the same label technique.

  1. Return to index.html to add the close button interface.
  2. At the top of the nav element, add this close button code:

    <nav>
       <label for="nav-checkbox" class="close-button">
          <img src="img/close.svg" ALT="close">
       </label>
       <ul>

    Notice we're using the same for attribute value as the menu button. Multiple labels can control a single form element, giving us flexible control options without complex JavaScript event handling.

  3. Switch to main.css to style the close button appropriately.
  4. In the max-width: 700px media query, after the .menu-button rule, add close button styling:

    .close-button img {
       width: 30px;
       margin: 15px;
       cursor: pointer;
    }

    Sizing the close icon appropriately and adding margin creates comfortable touch targets while maintaining visual hierarchy.

  5. The close button should also be hidden on desktop where the navigation is always visible. In the min-width: 701px media query, extend the existing rule:

    .menu-button,.close-button img {
       display: none;
    }

    Using a grouped selector keeps our CSS efficient while ensuring both mobile-specific buttons are hidden on desktop.

  6. Save the file and test the complete menu system.
  7. Reload your browser in mobile view.
  8. Test the complete interaction flow: click MENU to open the navigation, then click the close icon to dismiss it.

    The close button works immediately because it's simply another label controlling the same checkbox. The underlying mechanism handles the state management automatically.

This demonstrates the elegance of our CSS-only approach—adding new interactive elements requires only HTML markup, with the existing CSS logic handling all the behavioral complexity.

We can have multiple labels for the same checkbox, allowing us to create two different buttons that both control a single checkbox
This is the key insight that enables multiple trigger points for the same navigation state without JavaScript.

Creating an Overlay

Modern mobile interfaces often allow users to close overlays by tapping outside the menu area. We can implement this expected behavior using the same label technique, creating an invisible clickable layer that covers the page content.

  1. Return to index.html to add the overlay element.
  2. Below the menu button label, add this overlay label:

    <label for="nav-checkbox" class="menu-button">MENU</label>
    <label for="nav-checkbox" class="overlay"></label>

    This empty label will become our invisible clickable backdrop, using the same checkbox control mechanism as our other interface elements.

  3. Switch to main.css to create the overlay styling.
  4. In the max-width: 700px media query, after the .close-button img rule, add the overlay styling:

    .overlay {
       position: fixed;
       height: 100%;
       width: 100%;
       background: rgba(0,0,0,.5);
    }

    This creates a full-screen element with a semi-transparent dark background—a common mobile UI pattern that focuses attention on the menu.

  5. Save the file and observe the overlay effect.
  6. Reload your browser and notice the dark background covering the page content.
  7. Click anywhere on the dark overlay—the menu opens because the overlay is a label connected to our navigation checkbox. This demonstrates the functionality, but we need to refine the timing.
  8. We only want the overlay visible when the menu is open, not all the time. Return to main.css to implement conditional visibility.
  9. Modify the .overlay rule to hide it by default:

    .overlay {
       display: none;
       position: fixed;
       height: 100%;
       width: 100%;
       background: rgba(0,0,0,.5);
    }
  10. Below the .overlay rule, add a rule that shows the overlay only when the menu is open:

    #nav-checkbox:checked ~.overlay {
       display: block;
    }

    This selector targets the overlay, but only when it follows a checked navigation checkbox—precisely when we want the backdrop to be clickable.

  11. Save the file and test the improved behavior.
  12. Reload your browser. The overlay should now be invisible by default.
  13. Click MENU to open the navigation—notice both the menu and the semi-transparent background appear.
  14. Click anywhere on the dark background to close the menu, demonstrating intuitive mobile interaction patterns.
  15. For a cleaner aesthetic, we can maintain the functionality while removing the visual overlay. Return to main.css.
  16. In the .overlay rule, remove the background property to create an invisible but functional clickable area.
  17. Save the file and test the refined interaction.
  18. Reload your browser, open the menu, and click outside the navigation area—it still closes despite the invisible overlay, providing the expected user experience without visual distraction.

Overlay Implementation

1

Create Full-Screen Label

Position fixed label with 100% height and width to cover entire viewport

2

Control Visibility

Hide by default, show only when checkbox is checked using sibling selector

3

Remove Visual Background

Keep functionality but remove background for invisible click area

Hiding the Checkbox with CSS

Our off-screen navigation system now functions perfectly, with multiple intuitive ways to open and close the menu. The final step is hiding the checkbox control since users no longer need to see it directly.

  1. Return to main.css to properly hide the checkbox while maintaining functionality.
  2. Find the #nav-checkbox rule in your general styles section.
  3. Remove the temporary left: 50% positioning we used for testing.
  4. Replace it with proper accessibility-friendly hiding techniques:

    #nav-checkbox {
       position: fixed;
       clip: rect(0,0,0,0);
    }

    Here's why this approach is superior to simple display: none:

    • The clip property visually hides the element while keeping it in the DOM and accessible to screen readers and keyboard navigation.
    • Fixed positioning prevents browser quirks where some browsers (particularly Chrome) might scroll to the checkbox when it's toggled.
    • While clip is technically deprecated in favor of clip-path, we use it here because clip-path lacks support in older versions of Internet Explorer and Edge that may still be encountered in professional environments.
  5. Save the file and test the completed navigation system.
  6. Reload your browser and thoroughly test all functionality—the checkbox should be completely invisible while all interactive elements continue working perfectly.

Hiding Methods Comparison

Featureclip: rect(0,0,0,0)display: none
AccessibilityKeyboard accessibleNot accessible
Browser SupportUniversal supportUniversal support
Page JumpingNo jumping (with fixed position)May cause jumping
Recommended: Use clip property despite deprecation for better accessibility and user experience

Optional Bonus: Flipping the Nav to the Right Side

One of the beauties of our CSS-based approach is how easily it can be modified. With just two property changes, you can completely alter the navigation's entrance direction—perfect for different design requirements or user preferences.

  1. Return to main.css to implement the directional change.
  2. In the max-width: 700px media query, change both instances of left to right in these rules:

    nav {
       position: fixed;
       width: 12em;
       right: -12em;
       height: 100%;
       overflow-y: auto;
       z-index: 100;
       transition: all.2s ease;
    }
    #nav-checkbox:checked ~ nav {
       right: 0;
    }

    This simple change moves the navigation's origin point from the left edge to the right edge of the viewport.

  3. Save the file and experience the transformation.
  4. Reload your browser and test the navigation. It now slides in elegantly from the right side, demonstrating the flexibility of our CSS-only solution.

  5. For additional practice, explore the bonus exercise Slide-Down Top Nav Using Only CSS in your course materials. You'll discover how minimal CSS modifications can transform this side navigation into a top-sliding menu, further showcasing the versatility of this approach.

This completes your comprehensive off-screen navigation system—a professional, accessible, and completely CSS-powered solution that works across all modern browsers and devices. You've mastered techniques that are fundamental to contemporary responsive web design, creating user experiences that rival those built with complex JavaScript frameworks.

Easy Directional Change

Simply change 'left' properties to 'right' in both the nav positioning and checked state rules. The negative positioning principle remains the same.

Key Takeaways

1Off-screen navigation can be created entirely with CSS using checkbox states and sibling selectors, eliminating JavaScript dependencies
2The CSS clip property is preferred over display none for hiding form elements while maintaining keyboard accessibility
3Multiple labels can target the same checkbox, enabling different UI elements to control the same functionality
4Negative positioning values must exactly match element width to ensure complete off-screen hiding
5CSS transitions provide smooth sliding animations when changing between on-screen and off-screen positions
6Fixed positioning with z-index ensures navigation stays on top while preventing page scroll jumping
7Media queries enable responsive behavior with horizontal navigation on desktop and slide-out menu on mobile
8User experience enhancements like cursor pointer, text selection prevention, and click-outside-to-close improve usability significantly

RELATED ARTICLES