border-box not working on inline-block elements? - css

I have a list of inline-block elements, and I want to add a border to the element you hover over. However, notice how the border offsets the element, even when I use box-sizing: border-box and explicitly define the widths and heights of the elements. I illustrated the behavior below:
* { box-sizing: border-box }
ul { font-size: 0 }
li {
display: inline-block;
width: 100px; height: 40px; margin: 10px;
font-size: 20px; text-align: center;
background-color: #FFF176;
}
li:hover { border: 5px dashed grey }
<ul>
<li>hover</li>
<li>over</li>
<li>me!</li>
</ul>
The best solution I found is to use outline and outline-offset instead of border, but I'd really like to know why my original method doesn't work :/
UPDATE: While BoltClock gave a really great explanation and suggestion (which was all I was asking for), I just wanted to mention that I totally forgot about flexbox, which solved pretty much all the problems I was having with inline elements. I combined it with BoltClock's transparent border trick for my final JSFiddle solution

I see the problem now. What's happening is that box-sizing: border-box causes the content box of each element to shrink both horizontally and vertically once you add a border. Because your elements are inline-blocks, the vertical shrinking affects the baseline of the element being hovered, and therefore the baseline of the line it's on, resulting in the other elements on the same line being offset. If you look closely, you'll notice that the text actually stays aligned, which is the goal of offsetting the elements.
Changing the border to an outline works because outlines are designed to have no effect on layout (and also because you then take borders completely out of the picture).
However, it is for this reason that using an outline this way produces a significantly different effect from your original effect with a border. Setting an initial transparent border instead of an outline will ensure that your content stays offset the right amount whether the border is visible against the background (this was shown in a previous answer but it was deleted presumably because it was downvoted):
* { box-sizing: border-box }
ul { font-size: 0 }
li {
display: inline-block;
width: 100px; height: 40px; margin: 10px;
font-size: 20px; text-align: center;
background-color: #FFF176;
border: 5px dashed transparent;
}
li:hover { border-color: grey }
<ul>
<li>hover</li>
<li>over</li>
<li>me!</li>
</ul>

Related

Why does changing display: block to float: left make this layout work?

So, I've managed to make this CSS work, but I'm not 100% sure why it does. I know that's the classic programmer's scenario. I'd like to know why it does, though, so that I can get better.
Here are the two JSfiddle cases (they're exactly the same but with one line different):
With display:block
With float:left
As you can see, the important line of CSS:
.name::before {
content: '';
background: purple;
position: relative;
float: left; /* OR -display: block;- */
height: 22px; width: 100%;
margin-top: -22px; margin-left: -11px;
padding: 0 0px 0 22px;
}
With display:block, the pseudo-element matches the width of the main element (including the borders and padding. However, with float:left, the pseudo-element actually extends the width of the main element; if you change the padding-left to 11px, the increased width disappears, but the ::before stops short and doesn't include the main element's padding+border. This makes me think that inline elements affect other elements that it doesn't share a line with, as long as they're in the same container. Is that right?
Oddly, if you make change the padding to padding: 0 11px, it doesn't extend the right side of the ::before to the edge of the main element like I thought it would. Why is that?
Thanks!
My opinion is:
display: block;
only display the element in block,
while
float: left;
does push the element to the very left of its parents.
If you want to have all the elements to be in one line,
try to use display: inline;

Why is the padding calculated differently between <a> and <button> elements?

Why exactly is the padding calculated differently between a and button elements?
HTML
<button type="button">CLICK</button>
LINK
CSS
button {
padding: 10px;
height: 30px;
border: 0;
background: #ccc;
line-height: 30px;
}
a {
display: inline-block;
padding: 10px;
height: 30px;
background: #ccc;
line-height: 30px;
}
The default box-sizing value for buttons in Chrome (and Firefox) is border-box:
DEMO
I.e. the total height, including padding (and border and margin), of the element is 30px, not 50px like for the link. You can fix this by setting
box-sizing: content-box;
explicitly.
DEMO
More info about the box model.
Why the border-box is the default value I cannot say. I haven't found a specification for it. Chrome, Firefox and Safari seem to do this (didn't test other browsers).
<a href...>
Links never have a set-height, if you inspect the html, you can see that; what is really done is giving it a line-height and padding. when you write height: 30px, it is useless.
With respect to that, you are defining a height for the <button>, which is why it does not look the same as how you styled your link
Here is a fiddle to show how to make them the same, by removing the set-height of the <button>

Fluid navigation items of different widths with equidistant spacing

I'd like to create a fluid horizontal navigation list of links, where the nav links are evenly spaced and take up the full width of the enclosing container . Nav links are all different widths. The first link should be left-aligned and the last link should be right aligned.
I've had limited success using techniques such as:
ul {display: table;width: 100%;}
li {display: table-cell;}
a {display: block;}
and also using
ul {text-align: justify}
li {inline-block }
but no code I've written seems to deal at all well with elements that are different widths. The spacing does not seem to stay equal as the nav is resized.
I need the nav to be fluid, first and last elements to be flush against the edge of the containing ul and for the elements to be equidistant from each other
I thought about this for a while and came up with two reasonable approaches, both of which are pretty good but not exactly pixel perfect. One is CSS based only and the second is aided by jQuery (JavaScript).
CSS Approach - pretty good approximation
Consider the following HTML:
<ul class="nav ex1">
<li class="first">Home</li>
<li>Collections</li>
<li class="tight">About Us</li>
<li>Slocklists</li>
<li class="tight">Trade Enquiries</li>
<li>Press</li>
<li class="last">Contact Us</li>
</ul>
I added some classes as hooks for the styling.
The CSS is as follows:
.nav.ex1 {
outline: 1px dashed blue;
width: 100%;
margin: 0;
padding: 0;
display: table;
}
.nav.ex1 li {
display: table-cell;
outline: 1px dotted gray;
width: 20%;
white-space: pre;
text-align: center;
}
.nav.ex1 li.first {
width: 1%;
}
.nav.ex1 li.last {
width: 1%;
}
.nav.ex1 li.tight {
width: 1%;
}
In Example 1, the ul.nav parent container uses display: table and width: 100%. The child li elements are table-cell's. I added white-space: pre to prevent some of the links from wrapping into two lines, and text-align: center to keep the text centered.
The trick is to force some of the table-cell's to shrink-to-fit the text, and you can do this by setting width: 1% which is non-zero but too small to hold the text (unless your screen is 10,000 pixels wide). I shrink-to-fit the first and last cells which forces them to align to the left and right edges of the parent container. I then force every other table-cell to shrink-to-fit by added the .tight class.
The remaining table's cells will have a width of 20% which will keep them evenly spaced between their two nearest neighbors. HOWEVER, there will be some slight variation in spacing among the links in the row, which is why I call it an approximation.
jQuery Aided Solution
In Example 2, the markup is essentially the same and the CSS is:
.nav.ex2 {
outline: 1px dashed blue;;
margin: 0;
padding: 0;
display: block;
overflow: auto;
width: 100%;
}
.nav.ex2 li {
float: left;
display: block;
outline: 1px dotted gray;
width: auto;
}
In this case, the li elements are floated left and I use width: auto.
The trick is to calculate the magic left-margin value and apply it to all the li elements except for the first one.
The jQuery action is:
$(window).resize(function () {
navResizer();
});
// On load, initially, make sure to set the size.
navResizer();
function navResizer() {
var $li_w = 0;
var $ul_w = $(".nav.ex2").innerWidth();
$( ".nav.ex2 li" ).each(function( index ) {
$li_w += $(this).innerWidth();
});
var li_margin = Math.floor(($ul_w-$li_w)/6);
$(".nav.ex2 li").not(".first").css("margin-left",li_margin);
$("p.note").text( "Widths: ul.nav: " + $ul_w + " all li: " + $li_w + " Left margin: " + li_margin);
}
Basically, the action calculates the width of ul.nav ($ul_w), and the total widths of all the li child elements ($li_w).
The left-margin is calculated by ($ul_w - $li_w)/6 where 6 is the number of gaps between the 7 links.
The key line of code is: $(".nav.ex2 li").not(".first").css("margin-left",li_margin);
I use .not(".first") to omit the first li element and then .css to set the left margin.
The one slight defect is at the far right where the link is not quite right justified, but you can fix that by floating the last li to the right.
For the most part, if your link texts were similar in length, you would be hard pressed to distinguish the two. Both approaches are not quite pixel perfect, but pretty good.
Fiddle: http://jsfiddle.net/audetwebdesign/xhSfs/
Footnote
I tried some other approaches using text-align: justify and inline-block, but the CSS engine does not treat inline-blocks like regular words, so will not justify a line of inline-blocks.
Setting left-margin to a % value will not quite work at some window widths and the right-most link will not be on the edge as desired.
The jQuery approach has been tried before, see:
Evenly-spaced navigation links that take up entire width of ul in CSS3
You can use text-align: justify; and ignore the last left-justified row. #last is invisible and takes up the last row because of padding-left: 100%;: http://jsfiddle.net/mwRbn/
if you want to align the text of the menu vertically, use height in combination with line-height:
ul#nav {
text-align: justify;
width: 100%;
background: yellow;
height: 2em;
line-height: 2em;
}
http://jsfiddle.net/mwRbn/1/. Do you need a IE<8 hack?

How to Fix Collapsing Top and Bottom Margins?

I'm new to CSS and I'm trying to understand how to fix the following line from not working for top and bottom margins. It works for side margins just fine, however:
.contents {
...
margin: 10px 10px 10px 10px;
}
Example: http://jsfiddle.net/LCTeU/
How do I fix this?
Edit:
I've also tried padding the container instead, and that just expands the container to maximum size (why?):
.container {
...
padding: 10px 10px 10px 10px;
}
Use overflow:auto on any of the elements that are involved with the collapse. For example:
article {
overflow:auto;
}
jsFiddle example
This answer is based off of the fiddle you provided.
I think your approach is incorrect in that your applying a margin to the article to space it within the parent div tag. It is better to use padding in this case, since your attempting to separate the content from its outside border. So apply:
article {
//display: block;
clear: both;
padding: 10px;
}
This will cause the article tags to increase in size, however the borders of the container div elements will now be touching. To create space between elements a margin is applied.
.rounded-box {
background-color: #959392;
border-radius: 15px;
margin: 10px 0px;
}
Working Example http://jsfiddle.net/LCTeU/4/
So just to recap, when you want to create space between two elements use margin. When you want to create space between an element and its border (or you want an element to be surrounded by whitespace) use padding.
I found a fix that does not require a padding, and does not require changing the overflow of the container element:
article:after {
content: "";
display: block;
overflow: auto;
}
The idea being that we add another element at the bottom that disrupts the collapsing margin, without affecting the height or padding.
As per the fix that Erik Rothoff suggested, which does not seem to work in Safari, first I tried the following:
article:after {
content: "";
display: inline-block;
overflow: auto;
}
This does work in Safari but takes up space which I could not get rid off, messing up the grid so much that I would need to change margins.
Then I decided to combine the two by doing the following:
article:after {
content: "";
display: block;
padding-top: 1px;
margin-top: -1px;
}
This works in Safari, has an acceptable height of 1px which is negated by the negative margin top.

How to force Firefox to render textarea padding the same as in a div?

I'm attempting to provide a consistent width per line in pixels inside of a textarea across IE8, Firefox and Safari, so that text content wraps lines as predictably and consistently as possible.
Firefox is doing something a little bit odd: it has an extra pixel of padding eating out of the content space of the textarea vs the other two browsers, and vs a similarly equipped div block.
When applying this class to both a textarea and a div the difference is visible, with the text in the div touching the outer left edge of the red background but the text in the textarea have 1 px padding-like offset in spite of padding being zero:
.testbox{
padding:0;
margin:0;
border:0;
background: red;
width: 40px;
height: 40px;
font-size: 12px;
line-height: 16px;
}
Other values for padding wind up displaying one extra pixel of offset vs a div.
Any ideas on if there's a way to trick Firefox to render a textarea as if it were a div, or to adjust this not-padding-but-looks-like-padding property for a textarea?
I have recently been doing some researching on the problem described by OP for a similar question on SO. It seems that a bug in Firefox is causing the rendering of this so called "not-padding-but-looks-like-padding" on textarea elements.
Usually this extra padding is not really an issue, but it becomes an issue when you want to keep two elements the same width, and you care about getting its content to wrap the same way in both elements.
Getting textarea's to wrap content the same as e.g. div elements in Firefox
It seems to be impossible to get rid of this 1.5px wide padding on the textarea in Firefox, so if you want to ensure that the content wrapping inside a div in Firefox behaves exactly the same as the content wrapping inside a textarea in Firefox, the best approach seems to be to add an additional 1.5px of padding on the right and the left hand side inside the div, but only in Firefox. You can accomplish this by setting the following vendor specific prefixed CSS properties on your div:
-moz-box-sizing: border-box;
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
The first ensures that the padding set on the div does not increase the width of the div, and the next two ensure that 1.5px of padding will be set on the right and the left hand side of the div.
This approach does not affect the rendering of the div's in any other browsers, it doesn't need to, as textarea's in other browsers don't render any extra padding. But it ensures that there are no content wrapping differences between div's and textarea's inside Firefox as long as they share the same font-family and font-size properties and so on.
Here's a jsFiddle for demonstration purposes.
Getting textarea's to wrap content consistently across browsers
If you only wanted to ensure that a textarea in Firefox has the same width and wrapping behaviour as a textarea in other browsers, you can set its box-sizing to border-box, add a padding on both sides of 5.5px and set -moz-padding-end and -moz-padding-start to 0px.
textarea {
padding: 0 5.5px 0 5.5px;
-moz-padding-end: 0px;
-moz-padding-start: 0px;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
Here's a jsFiddle showing this approach.
Wow, I don't know the answer yet but I did try some stuff, and it appears as though a textarea, when you apply borders, margins and padding to it, doesn't change its width but puts the borders etc. on the inside. Try this:
.testbox {
padding: 10;
margin: 10;
border: 5px solid black;
background: red;
width: 40px;
height: 40px;
font-size: 12px;
line-height: 16px;
}
You could work around this by using something like this:
<div class="testbox">
<textarea class="testarea"></textarea>
</div>
css:
.testbox {
padding: 0;
margin: 0;
border: 0;
background: red;
width: 40px;
height: 40px;
font-size: 12px;
line-height: 16px;
}
.testarea {
padding: 0;
margin: 0 -1px;
border: 0;
background: red;
width: 100%;
height: 100%;
font-size: 12px;
line-height: 16px;
}
This also seems to work in IE, except for the -1px, which throws the layout off (by one).
This is a bug in firefox which got fixed a few days ago. The fix will be released with Firefox 29.
I already tried the latest nightly build and the textara bug is gone!
I was facing the same problem and although my solution seemed like bending backwards too much for that one pixle, but it fixed the problem, here goes: To unify the width because of this weird behavior, Instead of using a div, i used a disabled textarea with a white background and a default cursor to act as a mimic the div.
I was having a similar problem, a link tag with a background image and padding did not display well on firefox. The padding and background seemed to apply to the line of text, not the block of text, when multiline. I tested out a few things, and ended up using a "display:block;" on the element css. Worked for me.

Resources