gustafc
gustafc

Reputation: 28865

Make element consume free space in a container

I have a page which is supposed to look thus:

The way it's supposed to look

It's the header here that's the cause of my problems. It contains:

  1. An icon, in the far left end, which has a fixed size

  2. A title, just to the right of the icon, which takes up just as much space as needed to fit the title text on one line

  3. Sometimes, a subtitle arranged below the title

  4. Sometimes, a box with some auxiliary stuff arranged to the right of the titles, using up any left-over space

I don't want to nest these elements, for various reasons (including but not limited to perfectionism), and I'm almost there... except that I can't get the last box, "auxiliary stuff", to fill the remaining space.

I've built this as columns, with the icon being 100% height, title and subtitle being <50% height, and the aux box being having 100% height. Giving the aux box a fixed width works fine, but when I try to make it grow horizontally with width: 100%, the 100% seems to mean "100% of the surrounding box" (in my case, <heading>) rather than "100% of the remaining space", which makes it overflow.

html, body { padding: 0; margin: 0; height: 100%; width: 100%; font: 10px sans-serif }

/* Colors to please the eyes */
article { border: 5em solid limegreen; }
header { border: 1px solid tomato; }
.icon { background-color: cyan; }
h1 { background-color: magenta; }
h2 { background-color: yellow; }
.aux { background-color: lavender; }

/* Arrange them boxes */
article {
    display: flex;
    flex-flow: column nowrap;
}
header {
    display: flex;
    flex-flow: column wrap;
    align-content: flex-start;
    height: 5em;
}
.icon { width: 5em; height: 100% }
h1 { font-size: 200%; margin: 0 }
h2 { font-size: 150%; margin: 0  }
.aux { height: 100%; width: 100%; }
<article>
  <header>
    <div class=icon>ICON</div>
    <h1>Main header</h1>
    <h2>Subheading here</h2>
    <div class=aux>MOAR STUFF</div>
  </header>
  <section>
    Lorem ipsum dolor sit amet...
  </section>
</article>

Can I make the last box grow horizontally, without changing the markup?

Upvotes: 3

Views: 2155

Answers (4)

Michael Benjamin
Michael Benjamin

Reputation: 371203

UPDATED SOLUTION (now that CSS Grid is available)

With the advent of CSS Grid, the layout is relatively simple. There is no need to alter the mark-up.

One key rule is this one:

grid-template-columns: 5em auto 1fr;

It establishes a grid with three columns. The widths of each column are specified, and inline with the requirements of the question.

The fr unit is similar in concept to the flex-grow property. It is designed to consume free space.

header {
  display: grid;
  grid-template-columns: 5em auto 1fr; /* set 3 columns at specified widths */
  grid-template-rows: 50% 50%;         /* set 2 rows at specified heights */
  height: 5em;  
}

.icon {
  grid-row: span 2;                    /* span two rows (full height) */
}

h1 {}                                 /* placed automatically based on source order */

h2 {
  grid-column: 2;                      /* placed in column 2 */
}

.aux {
  grid-column: 3;
  grid-row: 1 / span 2;
}

/* non-essential decorative styles */
body    { margin: 0; font: 10px sans-serif }
article { border: 5em solid limegreen; }
header  { border: 1px solid tomato;    }
.icon   { background-color: cyan;      }
h1      { background-color: magenta; font-size: 200%; margin: 0; }
h2      { background-color: yellow;; font-size: 150%; margin: 0; }
.aux    { background-color: lavender;  }
<article>
  <header>
    <div class=icon>ICON</div>
    <h1>Main header</h1>
    <h2>Subheading here</h2>
    <div class=aux>MOAR STUFF</div>
  </header>
  <section>
    Lorem ipsum dolor sit amet...
  </section>
</article>

jsFiddle demo


OLD (PRE-GRID) SOLUTION

If you define a width for the column containing the h1 and h2, then getting the auxiliary box ("moar stuff") to stretch the remaining width of its parent is not a problem.

Try this:

HTML (no changes)

CSS

h1 {
   font-size: 200%;
   margin: 0;
   width: 10em; /* new */
}

.aux {
   height: 100%;
   width: calc(100% - 10em - 5em - 10em); /* width less header column less icon 
                                             less green border */
}

jsFiddle demo

Upvotes: 2

woestijnrog
woestijnrog

Reputation: 1579

My answer

html,
body {
  padding: 0;
  margin: 0;
  height: 100%;
  width: 100%;
  font: 10px sans-serif
}
/* Colors to please the eyes */

article {
  border: 5em solid limegreen;
}
header {
  border: 1px solid tomato;
}
.icon {
  background-color: cyan;
}
h1 {
  background-color: magenta;
}
h2 {
  background-color: yellow;
}
.aux {
  background-color: lavender;
}
header {
  height: 5em;
  position: relative;
}
.icon {
  width: 5em;
  height: 100%;
  position: absolute;
}
h1 {
  font-size: 200%;
  margin: 0 0 0 calc(5em / 2); /* adjust for the font size */
  float: left;
  height: 1px;
}
h2 {
  font-size: 150%;
  margin: calc(1em * 200 / 150) 0 0 calc(5em / 1.5); /* adjust for the font size */
  float: left;
  clear: left;
}
.aux {
  overflow: hidden;
  height: 100%;
  text-align: center;
}
<article>
  <header>
    <div class=icon>ICON</div>
    <h1>Main header</h1>
    <h2>Subheader here</h2>
    <div class=aux>
      MOAR STUFF MOAR STUFF
    </div>
  </header>
  <section>
    Lorem ipsum dolor sit amet...
  </section>
</article>

The advantage is that .aux takes on the correct size, so you can for example center stuff in it. The disadvantage is that you lose the background for the main header so you have to fake it on another element. But that can be done.

Upvotes: 1

Rene van der Lende
Rene van der Lende

Reputation: 5281

What essentially needs to be changed (given your rule to leave the HTML as-is), is:

  • make header fill its parent
  • header background to lavender to fake aux fill of 100% (aux will grow as needed)
  • min/max height/width of aux so it stays inside parent
  • give h1 and h2 a height of 50% so they wrap.
  • h1 and h2 need to fill their boxes, so flex grow.

small update

  • Make header chop off overflow
  • Add box-sizing rules to make everything align properly

html,
body {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
  height: 100%;
  width: 100%;
}
*,
*:before,
*:after {
  box-sizing: inherit
}
/* Colors to please the eyes */

article {
  border: 5em solid limegreen;
}
header {
  border: 1px solid tomato;
}
.icon {
  background-color: cyan;
}
h1 {
  background-color: magenta;
}
h2 {
  background-color: yellow;
}
.aux {
  background-color: lavender;
}
/* Arrange them boxes */

article {
  display: flex;
  flex-flow: column nowrap;
}
header {
  background-color: lavender;
  display: flex;
  overflow: hidden;
  width: 100%;
  flex-flow: column wrap;
  align-content: flex-start;
  height: 5em;
}
.icon {
  width: 5em;
  height: 100%
}
h1 {
  font-size: 200%;
  margin: 0;
  height: 50%;
}
h2 {
  font-size: 150%;
  margin: 0;
  height: 50%;
}
.aux {
  min-height: 100%;
  max-width: 100%
}
<article>
  <header>
    <div class=icon>ICON</div>
    <h1>Main header</h1>
    <h2>Subheading here</h2>
    <div class=aux>MOAR STUFF</div>
  </header>
  <section>
    Lorem ipsum dolor sit amet...
  </section>
</article>

Upvotes: 0

Akash Gaonkar
Akash Gaonkar

Reputation: 197

I don't know of any simple way to fix this without changing the markup. The trouble is that you have wrapping and stretching at the same time. If you removed the part that wraps, you could change the flex direction and stretch the final element.

Below is a solution with very little change to the markup. I placed the heading and subheading in a container.

html, body { padding: 0; margin: 0; height: 100%; width: 100%; font: 10px sans-serif }

/* Colors to please the eyes */
article { border: 5em solid limegreen; }
header { border: 1px solid tomato; }
.icon { background-color: cyan; }
h1 { background-color: magenta; }
h2 { background-color: yellow; }
.aux { background-color: lavender; }

/* Arrange them boxes */
article {
    display: flex;
    flex-flow: column nowrap;
}

header {
    display: flex;
    flex-flow: row wrap;
    align-content: flex-start;
    height: 5em;
}

.icon, h1, h2 {
  flex-shrink: noshrink;
}

.icon { width: 5em; height: 100% }
h1 { font-size: 200%; margin: 0 }
h2 { font-size: 150%; margin: 0  }

.aux { height: 100%; flex-grow: 1; }
<!DOCTYPE html>
<html>
    <head>
        <title>Flexicols</title>
    </head>
    <body>
        <article>
            <header>
                <div class=icon>ICON</div>
                <div class='subtitled-heading'>
                  <h1>Main header</h1>
                  <h2>Subheading here</h2>
                </div>
                <div class=aux>MOAR STUFF</div>
            </header>
            <section>
                Lorem ipsum dolor sit amet...
            </section>
        </article>
    </body>
</html>

Upvotes: 0

Related Questions