Vertical gaps resulting from nested inline block elements inside DIV containers – II – getting control

In the first article of this mini series

Vertical gaps resulting from nested inline block elements inside DIV containers – I

we have seen that under certain circumstances (nested) inline-blocks may lead to vertical gaps between themselves and/or between their lower border and the lower bottom of surrounding DIV containers. (Others may describe it as an unexected spacing or margin). We tried to explain this behavior:

If no other precisely defined height scale is given the (min-) height of the innermost inline element – in our case an inline block – will be interpreted as if a letter of a corresponding font-size were present. We have shown that the baseline for letters indeed is set to the bottom of the innermost inline block if no (relatively positioned) contents of the innermost inline block is given.

In this article we want to play around with our example to get an improved understanding and to find options to control and eliminate the vertical gaps. Our example code is:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 3.0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		
	}
	
	div.hornav_txt {
		display:inline-block; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 2.8rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		
	}
	
	div.mp_cont {
		display:inline-block; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00; 
		
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position: absolute; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div> 
        <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div><span style="font-size:2.8rem; ">XÜj</span>jjgg
             	</div><span style="color:yellow;">jg</span>
            </div>
        </div>
        <p style="clear:left;" ></p>
</body>
</html>

Test 1 – line-height reduction for the inline-blocks

r
If our explanation of the gap occurrence has some truth in it we should see an effect when we reduce the line-height for the inline-blocks in their containers.
So, let us first try the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
           </div>
        </div> 

 
Now, our innermost inline-block element div.mp_cont gets no line-height in its container, but it still has a min-height. The result is:

nested_inline_blocks_5

Not quite, what we wanted. At least div.mp_cont shrank to the expected height. But, still, there is no defined inline-block height available and the browser still does not know what to do about the inline element div.hornav. Hence the vertical gap between div.hornav_txt and div.hornav.

Now, we go a step further and repeat our approach one tag-level higher:

        <div class="header">
            <div class="hornav" style="line-height:0;>
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div>
             	</div>
            </div>
        </div>
 

 
We get:

nested_inline_blocks_6

Yes! Now, we have at least found a remedy in case that we need to control the height for inline boxes with empty or absolutely positioned contents. Let us keep this option of line-height reduction in mind!

Test 2 – provide some relatively positioned contents in the innermost inline block

If our gap theory is valid we should provide an innermost inline content with a defined height to get a reasonable vertical adjustment in the end. To achieve this for our example we have to perform 2 steps:

  1. Set the CSS value of “position” for the innermost block element div.fg to “relative” instead of “absolute”.
  2. Put some inline text into div.fg

With the first step we fulfill an objective that we already discussed in our first article:

The innermost contents shall control the height expansion of all containers in a dynamical, flexible way. This is especially important for responsive layouts and CMS systems.

So, lets do the following:

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;"></div>
                	</div>
             	</div>
            </div>
        </div> 

 
Note that we eliminated the setting “line-heigt:0;” again. We get:

nested_inline_blocks_7
 
A bit frustrating, isn’t it? We gained nothing compared with our original example. But our finding is still consistent with our theory – we still have no defined height of an innermost inline element.

So, now let us look at a small wonder of CSS: We just add a text element – a tiny harmless letter
“x” –

        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg" style="position:relative;">x</div>
                	</div>
             	</div>
            </div>
        </div>
 

 
and, hey, everything gets fixed in the sense of our original expectations.

nested_inline_blocks_8

Intermediate Summary

If we had started with the very reasonable setting of our last test we would never have noted any problem in our example. So, why all the fuss?

One reason is that there are a lot of articles on the Internet about uncontrollable vertical gaps related to inline block elements. Actually these gaps are understandable and controllable.

The other reason is that in templates (e.g. for CMS systems) the (future) contents of inline-blocks may not be known – and the inline block elements may intentionally be left empty. Then we would run into the described situation of our example. The use of nested inline blocks on the other side makes horizontal centering easy if the width of the innermost contents is not really known.

Two options to get control over the gaps
Now, we have seen 2 remedies that can be applied to avoid gaps between (nested) inline blocks and their containers:

  • Set the line-height in each of the container of an inline-block to zero or a sufficiently small value. (But be careful – this may have an impact on other elements on the same HTML/DOM hierarchy level.)
  • Provide an innermost inline (text) element with a defined height (e.g. given by a font-size). This innermost element may be a “&nbsp;”. Ensure that the innermost inline element can have an impact on outer containers via relative (!) positioning.

Of course, you may/must in some situations also combine these options.

Note: The use of a “&nbsp;” may be helpful for (PHP) templates as a dummy contents. Just replace this dummy text during the creation of the real page by real contents – eg. the text of a real menu point.

What about <P>-tags with different font-sizes as the innermost tags?

Ok, up to now we used plain text inside the div.fg-container. What happens when we use

-tags and change the font sizes for these tags? Well, then it may get pretty weird:

A <p> may actually create vertical gaps again – despite the fact there is enough space for it inside its block container div.fg. One of the reasons is that browsers add their own bit of white space to a <p>. This can be controlled by margin- and padding-settings. However, in our case of a a <p> inside inline-blocks even a “margin-top:0; margin-bottom:0;” does not help.

See the following example:

       
	<div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                    <div class="mp_cont" >
                        <div class="bg"></div>
                        <div class="fg">
                            <p style="font-size:1.4rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p>
                        </div>
                    </div>
             	</div>
            </div>
        </div>

 
nested_inline_blocks_9

There is again (!) a small but clearly visible gap at the lower bottom. The inline text-element inside the <p>-tag affects the next inline-block element despite the fact that we have defined normal block element with sufficient vertical space in between. The gap gets bigger with the font-size. See the same example for “font-size:1.6rem;”:

nested_inline_blocks_10

For real CSS2/3 experts this may not be too surprising; for me it was.

So, how can we correct this effect? And what do we have to do, when the font-size of the encapsulated

-tag really exceeds the height and line-height of the surrounding container?

The answers are given in the following final code for our example:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	
	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		min-width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height: 0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		margin:0; 
		padding:0;
	}
	
	div.hornav_txt {
		display:inline-block; 
		position:relative; 
		min-width: 20.0rem;  
		min-height:2.8rem;
		line-height: 0rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		margin:0; 
		padding:0;
	}
	
	div.mp_cont {
		display:inline-block; 
		position: relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height: 2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00;
		margin:0; 
		padding:0;
	}
	
	div.bg{
		position: absolute; 
		min-width:10.0rem; 
		/* line below not required */
		/*min-height:2.6rem;*/
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position:relative; 
		min-width:10.0rem; 
		min-height:2.6rem;
		line-height:2.6rem; 
		border:transparent 0.0rem solid;
		padding:0;
		margin:0;
		z-index:2;
	}
	
</style>
</head>

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div 
class="fg"> </div>
                	</div>
             	</div>
	    </div>
        </div> 
       <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg"><p style="font-size:1.8rem; margin-top:0rem; margin-bottom:0rem;">Ügj</p></div>
		  	</div>
                </div>
	    </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:0px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height: normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <div class="header" style="margin-left:50px; margin-top:50px;">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Ügj<br>Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
        <p style="clear:left;"></p>
        <div class="header" style="margin-left:50px; margin-top:50px; min-width: 50.0rem; ">
            <div class="hornav">
                <div class="hornav_txt" >
                	<div class="mp_cont" >
                		<div class="bg"></div>
                		<div class="fg" style="line-height:normal; padding-left:1.0rem; padding-right:1.0rem;"><p style="font-size:5.0rem; margin-top:0.6rem; margin-bottom:0.6rem;">Hallo Hallo<br>Hallo Hallo Ügj</p></div>
                	</div>
             	</div>
            </div>
        </div>
</body>
</html>

 
What did we change ?

The first thing you may note is that in the CSS definitions we used our first trick:
We explicitly set the line-heights for div.hornav and div.hornav_txt to zero.

The second important point is that when the font gets too large we must adjust the line-height for the container div.fg. We use “line-height:normal;” to adjust the line-height to the height of the included contents.

So, in case you should build a CMS in which the user can choose font-sizes by himself this is something you have to take care about. You may provide a standard setting; but if in situations like in our example case the font size of a <P>-tag exceeds defined limits our page creation program has to modify style settings explicitly to guarantee a flexible height adjustment.

The result is shown in following picture:

nested_inline_blocks_12

This gives us exactly what we wanted:

  • Easy horizontal centering via the use of (nested) inline block elements (even if the content length gets
    bigger than some threshold).
  • Vertical flexibility – even if we work with font-sizes bigger than a threshold.
  • NO VERTICAL GAPS between (nested) containers of inline-block elements.

It is easy to see how we can use the discussed elements in templates to create horizontally centered menus with menu points for which the horizontal and vertical size is not known.

CSS is a bit weird sometimes – but fun, too!

Vertical gaps resulting from nested inline block elements inside DIV containers – I

In the course of a series of articles about Responsive Web Design I made use of some elements in a horizontal menu line in the form of “inline block elements”. See:
Responsive fluid multi-column layouts – main menu control with JS/jQuery and font-size adaption – V
and other articles quoted there.

One reason for using inline elements was/is that the centering of inline blocks without a defined width inside a DIV block container is easy. One may need this e.g. in templates for a CMS where you do not know the number and the text of menu points in advance.

When my wife recently started building a new website for a customer she used part of my prescriptions and came back with an annoying finding:

If you embed and nest inline block elements inside standard block DIV containers and define

  • both the container and its inline block elements to have some (min-) heights
  • and define in addition for each of the (nested) inline elements a line-height

then FF (better the Gecko engine) will create some vertical pixels distance – a gap – between the bottom of the DIV-container and its enclosed inline block element(s) and/or between the lower borders of nested inline block elements. This happens when

  • the heights of all nested elements (blocks, inline blocks) are not defined exactly
  • AND no innermost real inline element (as a text) with a precisely defined height can be found.

The vertical gaps are an interesting though counter-intuitive consequence of the application of CSS2/3 rules. You may by accident stumble across similar effects when working with nested inline blocks. So, I thought it may be interesting to write a bit about it – and discuss some precautions and remedies in case you need to deal with unexpected vertical element gaps.

Why use undefined DIV container and undefined (inline) block heights?

Why would we use nested block or inline-block elements without a precise height definitions? Well, in some contexts and especially when we design responsive layouts for mobiles we may need vertical flexibility. One example is the creation of flexible menus in templates for CM-systems: To make them generally applicable one cannot make assumptions on the specific size/length of the menu point texts. We do not know whether line wrapping will occur and how much the container of a menu element will have to extend its vertical height.

In such a case min-height style definitions are required instead of precise “height”-definitions. Additional “line-height” definitions may be required

  • to vertically center text in standard cases with the text fitting into a one line geometry
  • and/or to control the vertical space requirement when line wrapping occurs.

Vertical height control is simple when you work with standard block elements – it is the standard box model that defines everything. However, some surprising things may occur when we deal with (nested) inline blocks with defined line-heights.

A simple example for nested inline blocks with unexpected vertical gaps

Just to get an example for the occurence of vertical gaps associated with inline block elements let us have a look at the following simple HTML code:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
<style type="text/css">
	

	html {
		font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
		font-size: 10px;
	}
	
	div {
		position:relative;
		margin:0;
		padding:0;
	}
	
	div.header {
		float:left; 
		width: 30.0rem; 
		min-height: 3.2rem; 
		background-color: #DDDDDD;
	}
	
	div.hornav {
		width: auto; 
		min-height: 3.0rem;
		line-height:0;
		line-height: 3.0rem;
		border: #F00 0.1rem solid;
		background-color: black; 
		text-align: center; 
		
	}
	
	div.hornav_txt {
		display:inline-block; 
		width: 20.0rem;  
		min-height:2.8rem;
		line-height:0;
		line-height: 2.8rem;
		border: solid 0.1rem #FF0; 
		background-color: #FFF;
		text-align:center; 
		
	}
	
	div.mp_cont {
		display:inline-block; 
		width:10.0rem; 
		min-height:2.6rem;
		line-height:0;
		line-height:2.6rem; 
		border:solid 0.1rem #090;
		background-color:#F00; 
		
	}
	
	div.bg{
		position: absolute; 
		width:10.0rem; 
		min-height:2.6rem;
		line-height:0;
		line-height:2.6rem; 
		top:0;
		bottom:0;
		left:0;
		right:0;
		border:0;
		background-color:#FF0;
		opacity: 0.5;
		z-index:1;  
	}
	
	div.fg {
		position: relative; 
		width:10.0rem; 
		min-height:2.6rem;
		line-height:0;
		line-height:2.6rem; 
		border:0;
		z-index:2;
	}
	
</style>
</head>

<body>
   <div class="header">
       <div class="hornav">
           <div class="hornav_txt">
               <div class="mp_cont">
                   <div class="bg"></div>
                   <div class="fg"></div>
                </div>
           </div>
       </div>
    </div> 
</body>
</html>

 
When we open a HTML file with the above contents in a present Firefox version (41.x) for Linux or e.g. the browser built into Eclipse we get the following picture (the KDE lineal on the right side was added for pixel measurement).

inline_blck2

We see that our horizontal centering works perfectly – however, regarding the heights we get a weird vertical gap of a size around 8 to 10 pixels. This seems to be wrong – at least compared with (naive) expectations based on the CSS definitions given above: all min-heights plus border dimensions were chosen with values such that all nested elements should vertically fit into each other seamlessly.

What exactly did we do in our example?

We defined an outer container DIV “div.header” which works like a kind of header section. Inside we placed a new standard block container “div.hornav”. Let us look at it as a frame for an expandable menu. Then we placed and horizontally centered an inline block element “div.hornav_txt” inside our “div.hornav”. This inline block element shall work as the container of a menu point. A menu point itself “div.mp_cont” then contains 2 absolutely positioned layers over each other. This allows for the opacity control of the background-color and a menu text with no opacity (opacity:1.0). All containers have only defined min-heights – but as long as the menu text uses only one line all elements should fit perfectly into each other (including borders).

So why does the obvious visible vertical distance occur at all?

A plausible reason for the vertical gaps

Our intuition for a seamless vertical fitting is based on 2 assumptions, namely

  • that an inline-block would behave more or less like a standard block element,
  • and that a surrounding block would adjust to an encapsulated inline block as if the latter were a block element.

Both assumptions are wrong. To understand a bit better what happens let us first look at how the height of a block DIV container responds to its contents if no precise height value is defined:

The browser will analyze the required height of the block contents and adjust the block height according to the box model with paddings and margins (if defined). In case the encapsulated contents is an inline element – as e.g. a text – then its space requirements is analyzed – e.g. the font-size plus required space above and below to account for all types of letters of this font – e.g. “&Uuml”; or “j”. If you artificially reduce the line-height of the contents and the encapsulating box vertical below the height required for a full display of the font letters vertical overlaps of the several text lines may occur and the box size will shrink to defined limits related to the font size according to CSS rules.

E.g.:

	<div style="clear:left; width: 20.0rem; margin-top:150px; line-height:0.0rem; background-color: yellow;">
		<span style="font-size:8.8rem; line-height:0.0rem; ">ÜJ<br>jÜ</span>
	</div>

 
leads to:
nested_inline_blocks_2

In contrast

	<div style="clear:left; width: 20.0rem; margin-top:150px; line-height:0.0rem; background-color: yellow;">
		<span style="font-size:8.8rem; line-height:0.0rem; ">ÜJ<br>jÜ</span>
	</div>

 
leads to:
nested_inline_blocks_3

So far, so good. In any case: What happens exactly depends on the (inline) contents, its height and the line-height calculated or defined for the display of the contents.

Now back to our example:

As there is no container element with a defined height the browser engine has to analyze successively nested inner elements to determine defined or calculable height scales. But in our case we never get to such a finite height scale for the relatively positioned elements inside the innermost inline block container.

The only precisely defined height scale in our example is eventually given by a letter “x” – however, the height of this letter only affects layers absolutely positioned at z-indices above their containers. It has no impact on the sizing of the container “div.mp_cont”.

So what can the browser engine actually cling to and rely on when following the successively embedded and relatively positioned elements? The last height scales given is the min-height and the line-height of the innermost inline (!) block element div#mp_cont. But from the perspective of the surrounding encapsulating containers “div.hornav_txt” and “div.hornav” this actually has the same effect as if a big letter had been placed inside them.

By using a small trick we can actually see that our FF browser engine (Gecko; but KHTML/Webkit does it as well) adjusts the baseline for letters according to this pseudo-“letter”-dimension (i.e. according to the height of the inline box “div.mp_cont”)
and reserves vertical space in between the two successive inline-block elements.

<body>
        <div class="header">
            <div class="hornav">
                <div class="hornav_txt" style="line-height:0;">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg">x</div>
                	</div>
             	</div>
			</div>
        </div> 
        <div class="header" style="margin-left:50px;">
            <div class="hornav">
                <div class="hornav_txt">
                	<div class="mp_cont">
                		<div class="bg"></div>
                		<div class="fg"></div>
                	</div><span style="font-size:2.8rem; ">XÜj</span>jjgg
             	</div><span style="color:yellow;">jg</span>
			</div>
        </div>
</body>

 
results in :

nested_inline_blocks_4

So, indeed: If we add some normal letters we see that the baseline for letters inside “div.hornav_txt” is the bottom of the included “div.mp_cont”. But letters require additional vertical space below their baseline!

So, the browser recognizes the existence of some inline (!) contents with a defined (min-) height inside the first inline-block “div.hornav_txt” and adjusts everything else according to some complicated height calculations. See http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height.

I have to admit that I do not really understand all details and their consequences of the W3C height calculation rules. But the overall effect of nesting an inline block with a min-height inside another inline block without any further defined (inner) height scale resembles pretty much a situation in which a letter with the same font-height value were placed inside the enclosing outer inline block (here inside “div.hornav_txt”).

Conclusion: In the absence of any further inline element with precisely defined height the min-height of the innermost identifiable inline element determines the full layout and the resulting height calculations

  • for any surrounding (inline) block elements with flexible height
  • and then the innermost relatively positioned standard block container enclosing the nested inline blocks

just as a letter with the same font-size would do. And a letter needs more vertical space then its font-size! The nested inline blocks transfer the resulting excess height requirement to the enclosing standard block element and therefore automatically lead to a vertical gap! (I suspect that the size of the gap depends on the rendering engine of the browser, but it did not investigate this point, yet).

Note: This may also happen inside horizontal lists where inline blocks are used instead of floated elements.

In the next article

Vertical gaps resulting from nested inline block elements inside DIV containers – II – getting control

we shall play around with our example and show what happens if
we change the innermost contents and/or some CSS settings. Our objective then is to get control over the vertical gaps introduced by the rendering engine.