Home / Snippets / Layout /

Nav collapse by container width

A navigation bar that collapses its links into a scrollable row or a compact stack based on the container width, using only CSS container queries.

Drag the bottom-right corner of the box to resize and watch the nav collapse into a stack.

New feature
layoutno-js

Quick implementation

.nav-cq {
  container-type: inline-size;
}

.cq-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: var(--card);
  border-radius: var(--radius);
}

.cq-nav__logo {
  font-weight: 700;
  margin-right: auto;
  color: var(--text);
}

.cq-nav__links {
  display: flex;
  gap: 0.25rem;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
}

.cq-nav__link {
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius);
  color: var(--muted);
  text-decoration: none;
  font-size: 0.875rem;
  white-space: nowrap;
}

.cq-nav__link:hover,
.cq-nav__link[aria-current="page"] {
  background: oklch(0.22 0.04 265);
  color: var(--text);
}

@container (max-width: 28rem) {
  .cq-nav {
    flex-direction: column;
    align-items: flex-start;
  }

  .cq-nav__links {
    flex-direction: column;
    width: 100%;
  }

  .cq-nav__link {
    display: block;
    width: 100%;
  }
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building component-driven navigation.

Goal: A navigation bar that switches from a horizontal row of links to a
full-width stacked list based on its container width — using only CSS
container queries, no JavaScript.

Technical constraints:
- Apply container-type: inline-size to a wrapper div around the nav.
- Use @container (max-width: 28rem) to trigger the stacked layout.
- In wide mode: nav uses display: flex with flex-wrap: wrap; logo gets
  margin-right: auto to push links right.
- In narrow mode: nav switches to flex-direction: column; links become
  full-width block elements.
- The active link uses aria-current="page" as the selector hook.
- Use oklch() for all color values — no hex or rgba.
- Do not use media queries — only @container queries.

Framework variant (pick one):
A) Vanilla CSS utility — .nav-cq wrapper + .cq-nav, .cq-nav__links,
   .cq-nav__link, .cq-nav__logo BEM classes.
B) React component — accepts a links array and a logoText prop; renders
   the nav with container-type applied via a CSS Module.

Edge cases to handle:
- If the nav appears inside a container query context itself, add
  container-name to scope the query to the correct boundary.
- Ensure aria-label="Main navigation" is present on the <nav> element.
- The wrapping approach avoids a hamburger menu and is preferable for
  simple navigation that does not need JavaScript interaction.

Return CSS only (or a React component if variant B).

Why

Navigation patterns that collapse based on viewport width break in component-driven UIs where a nav appears in sidebars, modals, or narrow columns. Container queries allow the same nav component to behave correctly regardless of where it is mounted.

The logic

container-type: inline-size on the nav wrapper creates the containment context. By default the nav uses flex-wrap: wrap so links flow naturally. Below 28rem container width, @container kicks in: the nav switches to flex-direction: column and the links become full-width block elements. margin-right: auto on the logo pushes links to the right at wide widths and has no effect in column mode.

Accessibility & performance

Ensure the nav has aria-label="Main navigation" and the active link has aria-current="page". The wrapping approach is preferable to a hamburger menu (which requires JavaScript) for simple navigation.