callum
callum

Reputation: 37729

Making an element expand to the width of its contents (when the contents are wider than the viewport)

After about 15 years writing CSS, I'm still discovering things I don't understand...

Below is a simple page with an h1 element that is 2000px wide. As expected, this causes the page to have a horizontal scrollbar.

But the problem is, the parent div.wrapper does not expand to the width of its contents – its yellow background only extends as far as the width of the viewport. (Run the below snippet and scroll horizontally to see the problem.)

body { margin: 0; }

.wrapper { background: yellow; }

h1 {
  width: 2000px;
  border: 2px solid red;
}
<!doctype html>
<html>
  <body>
    <div class="wrapper">
      <h1>Hello</h1>

      <p>More content</p>
    </div>
    
    <p>More content outside wrapper</p>
  </body>
</html>

Here's the weird thing: Try adding body { position: absolute } to the above CSS and it fixes it. The div.wrapper now extends to the width of its contents – the yellow goes all the way to the right of the document.

Questions:

  1. Why does setting body { position: absolute } fix the problem?
  2. Are there any better (more intuitive) ways to fix the problem?

Constraints: I do not always know the width of the inner contents, I just want the wrapper to always extend so its background color goes all the way to the right of the contents.

Upvotes: 2

Views: 135

Answers (3)

Temani Afif
Temani Afif

Reputation: 272744

Let's start with this:

But the problem is, the parent div.wrapper does not expand to the width of its contents – its yellow background only extends as far as the width of the viewport.

By default a div is a block element and a block element takes up the whole width of it's parent container so your wrapper in this case has the width of the body which is the width of the screen. In addition to this we are facing an overflow as the child content width is bigger than the parent width and by default:

Content is not clipped and may be rendered outside the padding boxref

This explain why the background doesn't cover the h1 as this one is rendred outside.

To change this behavior we have two solutions:

  1. We change the behavior of overflow by specifing a value different from visible (the default one). By doing this you will also notice some changes to margin because you are also facing a margin collapsing (margin of h1 and p are collpasing with the margin of div.wrapper).

body {
  margin: 0;
}

.wrapper {
  background: yellow;
  margin:10px 0;
}

h1 {
  width: 2000px;
  border: 2px solid red;
}
<div class="wrapper" style="overflow: auto;">
  <h1>Hello</h1>
  <p>More content</p>
</div>
<div class="wrapper" style="overflow: hidden;">
  <h1>Hello</h1>
  <p>More content</p>
</div>
<div class="wrapper" style="overflow: scroll;">
  <h1>Hello</h1>
  <p>More content</p>
</div>
<p>More content outside wrapper</p>

  1. We change the display property of the element to something else than block. We can for example use inline-block or inline-flex and in this case the wrapper will fit the content of its element and he will overflow the body

body {
  margin: 0;
}

.wrapper {
  background: yellow;
  display: inline-block;
}

h1 {
  width: 2000px;
  border: 2px solid red;
}
<div class="wrapper">
  <h1>Hello</h1>
  <p>More content</p>
</div>
<p>More content outside wrapper</p>


Concerning this:

Why does setting body { position: absolute } fix the problem?

We all know what position:absolute means but the intresting part is this one:

Most of the time, absolutely positioned elements that have height and width set to auto are sized so as to fit their contents. However, non-replaced, absolutely positioned elements can be made to fill the available vertical space by specifying both top and bottom and leaving height unspecified (that is, auto). They can likewise be made to fill the available horizontal space by specifying both left and right and leaving width as auto. ref

Upvotes: 2

giorgio
giorgio

Reputation: 10202

Yeah, you can use width: fit-content;

MDN describes it as;

fit-content
The larger of:

  • the intrinsic minimum width
  • the smaller of the intrinsic preferred width and the available width

It works as expected; the containing element expands. But, as usual, IE lags behind and doesn't support it...

EDIT To be clear; this specification is still in Working Draft status, and as such should not be used in production environments (except if you don't care about Internet Explorer).

Upvotes: 0

Andy G
Andy G

Reputation: 19367

You haven't told the div how to handle the overflow caused by the width of the inner element. Add overflow: auto.

body { margin: 0; }

.wrapper { background: yellow; overflow: auto; }

h1 {
  width: 2000px;
  border: 2px solid red;
}
<!doctype html>
<html>
  <body>
    <div class="wrapper">
      <h1>Hello</h1>

      <p>More content</p>
    </div>
    
    <p>More content outside wrapper</p>
  </body>
</html>

I do not believe that there is an obvious reason why adding position: absolute to the body fixes this. It does take body out of the document flow, but body is the container for all the content. So I would describe it as a quirk.

We could describe body as being the initial constraint for the width of the content, the .wrapper. Being absolute removes this constraint. Actually, it likely remove the width constraint for any further elements on the page, so they will probably all expand to contain any inner content.

Upvotes: 1

Related Questions