skip to Main Content

I am trying to make a layout with:

  • A header (gray block in the snippet)
  • A body (lime borrder)
  • Main body content ( blocks with red border)

If you scroll horizontally, then the header should not scroll, it should be full width and stay in view. If you scroll vertically, then the header should scroll off the page as usual. The height of the header is dynamic, and fits the content within it (this SO answer works with a fixed height)..

The <main> element is allowed to be wider than the viewport, but the header is always the viewport width.
The reason I dont add max-width: 100%; overflow-x: auto on the <main> element (like this SO answer, is because then the horizontal scroll appears at the bottom of the element, and then say one is reading the first block, and you wish to scroll horizontally, you have to scroll to the bottom of the main element to see the horizontal scroll bar, scroll to the side, then scroll back up. I wish to have the horizontal scroll bar always present if main is wider than the view port.

I have tried position: sticky/fixed on the header but could not get it to work.

I would prefer not to use JavaScript if possible.

header {
  padding: 32px;
  background: gray;
  width: 100%;
}
main {
  border: 2px solid lime;
  min-width: 100%;
}
div {
  height: 200px;
  width: 120%; /* make it overflow horizontally */
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid red;
}
<header>The Header should not scroll horizntally<br>(is dynamic height)</header>
<main>
  <div>content 1</div>
  <div>content 2</div>
  <div>content 3</div>
  <div>content 4</div>
  <div>content 5</div>
  <div>content 6</div>
</main>

2

Answers


  1. What I have done here is make header sticky to the left part of the screen. Its parent element must be aware of size of your content to allow header to move. So I set body min-width to min-content and same with main so it can transfer its children’s size to body.

    You also may notice I used box-sizing: border-box; in the header, its so padding size is taken into account when element size is calculated(100vw in this case). You don´t want to use % on header width because it won´t have room to slide.

    Also div sizes must not be dependent on parent size, so you can´t use % here either.

    body{
      min-width: min-content;
    }
    
    header {
      box-sizing: border-box;
      position: sticky;
      left: 0;
      padding: 32px;
      background: gray;
      width: 100vw;
    }
    main {
      min-width: min-content;
      border: 2px solid lime;
    }
    div {
      height: 200px;
      width: 120vw; /* make it overflow horizontally */
      display: flex;
      align-items: center;
      justify-content: center;
      border: 2px solid red;
    }
    <body>
    
    <header>The Header should not scroll horizntally<br>(is dynamic height)</header>
    <main>
      <div>content 1</div>
      <div>content 2</div>
      <div>content 3</div>
      <div>content 4</div>
      <div>content 5</div>
      <div>content 6</div>
    </main>
    </body>
    Login or Signup to reply.
  2. Edit: @Max Tuzenko’s answer is better as it doesn’t need additional markup. We went the same route but I didn’t even think of simply leaving top unchecked. I’ll leave my answer up as it explains some things.


    You can get this to work with position: sticky if you’re willing to wrap your header content in another element. And there’s a caveat in that you can’t force the overflow in percentages as you do in your demo. But I guess that was just for exemplary purposes?

    The idea is to extend your header to whatever width your widest content elements are and have the inside wrapper only stick along the x-axis and also limit its width to the viewport width (100vw).

    For that we first need to tell body to extend beyond the width of the viewport, which it doesn’t do by default. See this question and its answer as to why. We’re going with min-width: min-content.

    I’m not really sure why forcing the overflow in percentages doesn’t trigger <main> to grow beyond the viewport limits but I guess it’s something to do that percentages are relative to the containing element.

    Anyways, see the following code snippet for a working example. Some "bumping" occurs due to the borders added.

    html { box-sizing: border-box; }
    *, *::before, *::after { box-sizing: inherit; }
    
    body {
      margin: 0;
      min-width: min-content; /* Have body extend past viewport. */
      padding: 0;
    }
    
    header {
      border: 2px solid blue;
    }
    
    header div {
      background: gray;
      left: 0;
      max-width: 100vw; /* Restricting width to viewport width. */
      padding: 32px;
      position: sticky;
      top: 0;
    }
    
    main {
      border: 2px solid lime;
    }
    
    main div {
      align-items: center;
      border: 2px solid red;
      display: flex;
      height: 200px;
      justify-content: center;
      width: 120vw; /* Horizontal overflow, can't use pecentages. */
    }
    <header>
      <div>
          The Header should not scroll horizntally<br>(is dynamic height)<br>more lines
      </div>
    </header>
    <main>
      <div>content 1</div>
      <div>content 2</div>
      <div>content 3</div>
      <div>content 4</div>
      <div>content 5</div>
      <div>content 6</div>
    </main>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search