font-size vs line-height vs actual height - css

This question's answer says that the font-size defines the height of a box so that all letters (with ascenders and descenders) can fit.
But why has a span with 40px font-size and line-height an actual size of 45px? If I understand the linked question correctly then "X" should be smaller than 40px but the overall height should be exactly 40px. I thought that maybe it is making some extra room above/below the ascenders/descenders but the image shows that the ascenders/descenders take all the space so there can't be much extra room:
When I wrap a div (green) around the span then the div has a height of 40px. Why does the div use the font-size of its child for its height but the child itself doesn't?:
Now when I set the span's line-height to 15px (less than the font-size) then the div's height changes to 26px. How is this value calculated? Has this something to do with the baseline?:
When I set the span's line-height to 65px (more than the font-size) then the div's height is the height of the line-height. I would have expected the div's height to be something like (65px - 45px) + 45px.:
So how do font-size and line-height affect the actual heights of elements? I read some questions that referenced the spec but I couldn't make much out of it. Are there any easy to understand rules?
JSFiddle

First, I recommend reading my answer in Inline elements and line-height. To summarize, there are various heights related to inline boxes:
Height of the inline box, given by line-height
Height of the line box, which in simple cases is also given by line-height, but not here.
Height of the content area of the inline box, which is implementation dependent. This is the area painted by the red background.
The other height in your case is the height of the parent div. This is determined by §10.6.3. In this case, since the box establishes an inline formatting context with one line,
The element's height is the distance from its top content edge to [...] the bottom edge of the last line box
So the height of the parent block is given by the height of the line box.
What happens here is that the height of the line box is not the line-height of your inline box. And that's because the line-height of the parent block is also taken into account:
On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element.
The minimum height consists of a minimum height
above the baseline and a minimum depth below it, exactly as if each
line box starts with a zero-width inline box with the element's font
and line height properties.
We call that imaginary box a "strut."
If you set parent's line-height to 0, and child's vertical-align to e.g top, then the height of the parent will exactly be the line-height of the child.
.outer {
margin-top: 50px;
background-color: green;
width: 150px;
font-family: "Times New Roman";
line-height: 0;
}
.letter-span-1 {
background-color: red;
line-height: 40px;
font-size: 40px;
vertical-align: top;
}
.letter-span-2 {
background-color: red;
line-height: 15px;
font-size: 40px;
vertical-align: top;
}
.letter-span-3 {
background-color: red;
line-height: 65px;
font-size: 40px;
vertical-align: top;
}
<span class="letter-span-1">XxÀg</span>
<div class="outer">
<span class="letter-span-1">XxÀg</span>
</div>
The parent block is 40px tall.
<div class="outer">
<span class="letter-span-2">XxAg</span>
</div>
The parent block is 15px tall.
<div class="outer">
<span class="letter-span-3">XxÀg</span>
</div>
The parent block is 65px tall.
If you don't set a line-height to the parent, it will be normal.
Tells user agents to set the used value to a "reasonable" value based
on the font of the element[...]. We recommend a used value for
'normal' between 1.0 to 1.2.
That means that there will be a minimum height for the parent which will be its font-size (which you don't specify, and the default is implementation-dependent) multiplied by that factor (implementation-dependent).
You should also consider the vertical-align of the span. By default it's baseline, and that may create a gap below. The image in web-tiki's answer is especially useful:
That's because vertical-align determines how the span will be aligned with the strut, and with baseline the alignment can depend on font-size and end up increasing the height of the line box. The line box height is the distance between the top of the uppermost and the bottom of the lowermost boxes in the line.
If you don't want the height of the parent div to be increased by that, you need some other vertical-align, like top, middle, or bottom. Then the font-size of the span shouldn't affect the height of the div.
To summarize, the height of the div depends on
Its line-height
... which by default depends on div's font-size
Span's line-height
... which by default depends on span's font-size
Possibly span's font-size, depending on span's vertical-align
And obviously height, min-height, max-height, etc.

Introduction
Good question,
I learn most of these things through personal experience.
In this case, the DIV height is set to auto. It will detect the height of its own contents, and evaluate its new height from there.
Clearly, the DIV only takes into account the line height of the . This is likely due to the diverse number of fonts. Line-height gives us the adaptability we need for various font types.
In Short
font-size
Font size only changes the actual font itself, and not the div elements around it
line-height
Line-height is the height of the actual line and will change the div elements around it
Wait a second...
If we have something like this:
div {
background: green;
margin-top: 50px;
}
.test-one {
font-size: 20px
}
.test-two {
font-size: 40px
}
<div>
<span class="test-one"> test one </span>
</div>
<div>
<span class="test-two"> test one </span>
</div>
Clearly the size of the DIV (height: auto;) changes according to font-size. That's because the line-height will automatically adjust accordingly if it is not set manually.
One Exception
Upon further inspection, I noticed that DIVs don't always match the line-height. This occurs if the line-height is very small, and the font exceeds it by some distance.
The example you gave -
.outer {
margin-top: 50px;
background-color: green;
width: 150px;
font-family: "Times New Roman"
}
.letter-span-1 {
background-color: red;
line-height: 40px;
font-size: 40px;
}
.letter-span-2 {
background-color: red;
line-height: 15px;
font-size: 40px;
}
.letter-span-3 {
background-color: red;
line-height: 65px;
font-size: 40px;
}
<span class="letter-span-1">XxÀg</span>
<div class="outer">
<span class="letter-span-1">XxÀg</span>
</div>
<div class="outer">
<span class="letter-span-2">XxÀg</span>
</div>
<div class="outer">
<span class="letter-span-3">XxÀg</span>
</div>
If you look closely,
letter-span-1 and letter-span-3 both result in the DIV equaling the line-height.
However, letter-span-2 does not.
-------------- Line-height - Actual-height
letter-span-1: 40px - 40px
letter-span-2: 15px - 25px
letter-span-3: 65px - 65px
Notice that letter-span-2 is the smallest. It is so small, it will actually limit the height of the div. You can test this by altering the font size.
The "Why?"
Why have these two different settings, and not just change height normally?
I honestly am not sure, but I speculate that it was because fonts aren't standard. If the computer misreads a particular font, it may incorrectly evaluate the line-height.
Not to mention the numerous "CSS Tricks" you can do with line-height. It is great for adding space for open designs.
Conclusion
Line-height defines div height, unless line-height is very small, in which case the font-size will define the size.

Related

Why does big negative margin-right make absolute element no wrap?

Update:
The position value will change the width.
The JSFiddle: http://jsfiddle.net/caicai/mbtb5m7p/2/
Why does the width of div .a2 equal to its parent's width, while the the text in the div .a1 won't wrap?
The JSFiddle: http://jsfiddle.net/caicai/mbtb5m7p/
.r {
position: relative;
width: 100px;
height: 22px;
background: blue;
}
.a1 {
position: absolute;
top: 30px;
margin-right: -9999px;
background: green;
}
.a2 {
position: absolute;
top: 60px;
background: orange;
}
<div class="r">
<div class="a1">
Why does this line no wrap.
</div>
<div class="a2">
Why does this line wrap.
</div>
</div>
The div.r has a width of 100px and is placed relative.
The children .a1 and .a2 are placed absolute. The related on their (relatively placed) parent.
Without a negative margin, the children will take a maximum width equal to its parents size.
But, by adding a negative margin, you allow the children to "float over" its parents borders.
It's not that the content of .a1 is no longer wrapping, it is that the content of .a1 has no need to wrap because the available interior space has been made wide enough for the string inside it by your negative margin.
So your div.a1 will essentially be the width of .r + the value of the negative margin, which is dragging out the right extremity and increasing the size of your div.
That's pretty much how the box model works, check it out here or here
The margin is something that depends on it's parent element(class/id) here class r for classes a1 and a2.
1) Now you have width:100px for parent class(Class .r) so all child classes(class a1 & a2) inherits same width.
2) There is no issue for class a2, that class is totally normal as no unique(different) property than parent class(.r), so behaves properly.
3) Now the case for class .a1, which behaves properly too till it finds margin-right:negativeValue;.
So first understanding margin, Margin is the space between class/element block and it's parent class/element block. For Example margin-left:5px; so that particular block starts after leaving 5px from left, respectively for rest three sides(top, bottom, right)
Now negative margins, when used what it does is it trespasses their parent blocks boundary. That is it gets beyond the boundaries of their parents.
So here you specified margin-right:-9999px; so it gets beyond the boundary of class .r, still it might be giving you a thought that -9999px is a huge value, should throw the div beyond the screen, but as of limited text it only shows the area contained with text.
Inserting more text in class a1 it will surely go beyond screen scope, but text should be more than enough.
Graphical Explanation Updated

Make element appear before/after previous text?

Given this:
<h1>This is a test <small>It is</small></h1>
Is there any CSS I could apply to the <small> that would have it appear BEFORE, and ON THE PREVIOUS LINE, before "This is..."? float:left; display:block; doesn't get it before the content.
In a relatively simple case like this, you can use positioning. Declare the heading as relatively positioned, set a suitable top padding on it, and make the small element “absolutely” positioned (i.e., positioned relative to its positioned ancestor, the heading). Example:
h1 { position: relative; padding-top: 1em; }
h1 small { position: absolute; top: 0; left: 0; height: 1.3em; }
Note that when setting the padding, em denotes the font size of the h1 element, whereas when declaring the height of the inner element, the small element, em denotes its font size, which is smaller. Hence 1em vs. 1.3em, even though the padding is meant to give room for the small element. You probably want to set font sizes on h1 and small (say, h1 { font-size: 150% } small { font-size: 80% }) to get more predictable rendering, and then experiment with the padding and height values to get the appearance you like. This tuning will also depend on the font.
Here it is:
HTML:
<h1 id="thisid">This is a test <small>It is</small></h1>
CSS:
#thisid:before {
content:"This is the content on the line above \A ";
white-space: pre;
}
The :before pseudo element will make the stuff go before. Whereas the \A will make a space break.
Here is the fiddle: http://jsfiddle.net/pikk/ZL54q/

CSS display but keep row height

I am using a table to display some data and for one row, I am displaying a 35-character preview of a (possibly) longer string. When the user does a mouse-over on one of the previews, it displays the full text next to it.
The problem is when the full text is displayed, the tr height is changed to fit the full text, but I want it to stay the same size.
<td><%=shortPreview%>
<br/><div class="details" style="display:none;" id="<%=ID%>"><%=rsTickets("details")%></div></td>
CSS
.details {
position:relative;
top:-15px;
left:260px;
background-color:#FFFFAA;
z-index:1;
padding-top:5px;
padding-bottom:5px;
padding-right:15px;
padding-left:5px;
border:1px;
border-style:solid;
border-color:#CCCC99;
-moz-border-radius: 1em 2em 2em 1em;
border-radius: 1em 2em 2em 1em;
}
Put a fixed size div inside your cell and set it to position: relative. Inside that div put your details one and set it to position: absolute;. This should work for 99%.
Thing is that when you set an object to be relative, then it is relative to the objects around it. This means that if you change the height of the div (or in this case, shows it), the surrounding objects will adapt to this change. So when you set the div to be visible the td, which encapsulates it, will change it's height as well.
Setting the div to an absolute position solves the problem, but then it might get difficult to position it right. Unless you put it in another div. If you put an absolute div within a relative div, then the absolute div will be absolute to the relavite div, thus solving the problem. Here's an example:
<table>
<tr><td><div style=position:relative>
This div will not affect the td more than will this text.
<div style=position:absolute;top10px;>
This div will not affect the td, since it's absolute, and will be
10px from the top of the outer div
</div>
</div></td></tr>
</table>
This should do the trick. Works for me at least.

Why padding is included in height sometimes?

I have a text area and I added some CSS to it
<textarea watermark="Have a question? ..." class="test-input" rows="4" cols="15" name="">Have a question? ...</textarea>
and CSS applied to it -
.test-input {
border: 1px solid #D0D0D0;
border-radius: 3px 3px 3px 3px;
color: #646464;
float: left;
font-family: Arial;
font-size: 12px;
height: 20px;
margin: 0 0 0 15px;
padding: 5px;
resize: none;
width: 264px;
}
And I get the text area with some padding inside it. In the cases when it works absolutely fine, text area height comes to be 20px with 5px padding not included in height. But, in few cases height of the text area includes padding and the height gets reduced to 8px. I have looked for any css if its overriding it but I didn't find. And I compared the result in both cases. Left is the reduced height and right is the expected height.
I can fix this issue, in other case managing height specifically, adding !important or with help of some JavaScript. But, I am wondering what's the cause here that's making such effect. Why and in which cases paddings are getting included with height or width?
That depends on what box-sizing attribute you have used:
border-box means that the height and width of the box, defined/calculated in CSS, will also include the padding(s) and border width(s) applied to it
content-box is the default behavior, where padding(s) and border width(s) are added onto the defined/calculated height and width of the box.
By setting box-sizing: border-box as seen in your left example, you have defined the height of the element at 20px. This means that the actual content box will only be 8px tall, because the browser will subtract the border (1px top, 1px bottom) and padding (5px top, 5px bottom) form the defined height, leaving only 8px left, which is a tad bit too short to contain height of the entire line (therefore the word appears to be cut off).
jQuery seems consistent with these definitions:
.height() does not include padding (even where box-sizing: border-box)
.innerHeight() includes padding but not border
.outerHeight() includes padding and border
.outerHeight(true) includes padding and border and margin
Each can set or get, e.g. $element.outerHeight(desired_height_including_margins, true);
(Images excerpted from linked pages.)
Refer to https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing,
The box-sizing property can be used to adjust this behavior:
content-box gives you the default CSS box-sizing behavior. If you set an element's width to 100 pixels, then the element's content box
will be 100 pixels wide, and the width of any border or padding will
be added to the final rendered width, making the element wider than
100px.
border-box tells the browser to account for any border and padding in the values you specify for an element's width and height. If you
set an element's width to 100 pixels, that 100 pixels will include any
border or padding you added, and the content box will shrink to absorb
that extra width. This typically makes it much easier to size
elements.

Vertical margin not collapsing in Chrome

In this fiddle, http://jsfiddle.net/munkii/tpQQN/ I have some margin bottom on the paragraph elements via the intro class and some margin bottom on the list items via the what-is class.
i.e.
article.about .what-is {
height: 100%;
margin-bottom: 34px;
padding-right: 34px;
width: 600px;
}
article.about p.intro {
font-weight: bold;
margin-bottom: 43px;
}
I have removed the unnecessary margin from my work but am still interested to know why Chrome is not collapsing the vertical margin when FF and IE does.
Any thoughts?
It can only be a bug.
According to http://www.w3.org/TR/CSS2/box.html#collapsing-margins these margins should be collapsed as we are in this case:
bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
.what-is' height should be computed as auto because
If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'.
(http://www.w3.org/TR/CSS2/visudet.html#the-height-property)
The weirdest thing is that the computed height is indeed auto, but it seems Chrome doesn't do what it implies.
As Alex' comment states you can remove the height: 100%; rule, which lets the element takes its default height into account. auto that is.
Well said MatTheCat.
Would it be because your height is 100%, Firefox takes the height of the containing Div as the height. Where as Chrome seems to include the margin bottom on the p tag into the height of the containing Div.
If that makes any sense...

Resources