Update & Summary
I feel obligated to make this question clearer, now that there is a bounty attached.
(Also, I'm pretty sure this will be child's play when the calc() CSS3 unit value is supported, doing something like width: calc(25% - 5px); though we'll probably be browsing the internet in our minds by that point)
I'm working on a CSS framework for a few projects that share design requirements; namely a fluid 12 column layout. Using floated .column elements with percentage widths of (100% / 12) x col_size, this is reasonably easy. However, the issue comes with the addition of fixed margins (or any form of spacing) between columns.
My initial attempt used the fluid columns as described, with a .panel child nested in each. HTML/CSS snippet follows (reduced for brevity):
.column{
float: left;
display: inline-block;
}
.width-01{ width: 8.3333%; }
.width-02{ width: 16.6666%; }
.width-03{ width: 25%; }
/* etc */
.panel{
width: 100%;
padding: 5px;
box-sizing: border-box; /* so padding doesn't increase width */
}
<div class="column width-02">
<div class="panel">Width-02</div>
</div>
<div class="column width-03">
<div class="panel">Width-03</div>
</div>
<div class="column width-02">
<div class="panel">Width-02</div>
</div>
<div class="column width-05">
<div class="panel">Width-05</div>
</div>
This snippet would produce a layout similar to that of the image below, however all .panel elements have 5px padding on all sides. I'm trying to make the content edge of the outside columns flush with the edge of the view-port (or parent container for that matter). Another approach would be to eliminate the .panel class altogether, and just go with columns:
.column{
float: left;
display: inline-block;
padding-left: 10px;
box-sizing: border-box;
}
.column:first-child{ padding-left: 0px; }
.width-01{ width: 8.3333%; }
.width-02{ width: 16.6666%; }
.width-03{ width: 25%; }
/* etc */
<div class="column width-02">Width-02</div>
<div class="column width-03">Width-03</div>
<div class="column width-02">Width-02</div>
<div class="column width-05">Width-05</div>
Again, this works well, producing results even closer to that of the image below, however now the (actual) problem is that the padding is eating into the width of the columns screwing up the width distribution. The :first-child column has 10 pixels (or whatever the margin size is) greater content area width than it's siblings.
This may seem innocuous, even unnoticeable; however there are a few instances where having exact (as exact as possible) width distribution between elements is either necessary, or would make things altogether easier.
And so, whether using padding, margin, or some combination thereof; is there any solution for fluid columns, fixed margins, with even distribution of gutter space that won't rob "marginal" (***haha*) content area from the adjacent columns?**
Original Question
Due to the simple lack of results in my searches and attempts, I've concluded this is impossible. If anywhere can yield an answer though, I'm certain it is here.
Is there any way, using pure CSS, to achieve a fluid width columned layout with fixed width margins?
Important note: This figure is only an example, and not the specific layout I'm looking to achieve. A given solution should permit any combination of adjacent columns, the total width distribution totaling 12 or less. Consider the popular 960 grid for reference.)
Note: In a 12 column layout, the width distribution of the columns in the image are 2, 3, 2, and 5 respectively.
So far, I've resorted to a grid that, using percentages, nearly accomplishes this. The problem is, in order to achieve the margins, each column requires an additional child (I call them .panel) with:
width: 100%;
box-sizing: border-box;
padding: 10px;
This is, again nearly, fine; the issue is with this approach is that the first and last column have outer "margins" (10px) and the "margins" between each column are doubled (2 x 10px)
Certainly, with the inclusion of the new CSS3 calc() value type, this could be solved much more easily. Something in the direction of:
.width-12 > .panel{ width: 100%; }
.width-09 > .panel{
width: calc(75% - 10px);
margin: ...;
}
I've got some Javascript fixes, I've hacked out some stuff that "works", but I'm on a quest. Hopefully the holiest of grails exists.
The following solution, and the one #avall provided (although certainly a good choice on simplifying) unfortunately aren't what I'm looking for. The main issue being, the margins are not distributed evenly among columns.
The only way I can see this working is reducing the .panel padding to 5px and something like:
.column:first-child > .panel {
padding-left: 0px;
}
.column:last-child > .panel {
padding-right: 0px;
}
/* not necessary? in any case, haven't tested */
.column:only-child > .panel {
padding-right: 0px;
padding-left: 0px;
}
This solution is not acceptable, only because IE8 fails to recognize the :last-child (and for that matter :only-child) pseudo selectors.
I finally figured out. After into the hundreds of hours wasted on and off over the last decade (though I'm relying on some css that wouldn't have worked year ago anyway). I solved it without any gotchas. and in IE8+.
Please prepare the 2001: A Space Odyssey Music because I'm landing this boat.
The genius and trick to this method is in using inline-block elements and then using word-spacing to counterbalance using a negative right margin. A negative right margin on it's own will pull elements together, allowing you to have 100% width set and still fit things in between, but leave the elements overlapping. Setting negative margin on the parent just undoes the child margin in regards to the effect on interacting with total width (the magic "100% width" mark we're trying to hit"). Padding only serves to increase the size of the element its on and is useless with regards to counter-acting margin. It is often used with box-sizing in the jury rigged solutions to this problem, at the expense of losing the ability to use padding at all otherwise (and margin) and likely requiring more wrapper elements.
word-spacing provides the magical "third way" to add or remove horizontal distance between two elements, provided they are inline-block, since they will be counted as a single "word" in that case, and any whitespace between will collpapse down to the one single controllable "word-spacing" property. Aside from this trick I'm not aware of another way to get this 100% result.
I humbly present the ultimate answer to the fixed-gutters flex-columns problem. I hereby name my solution "the omega maneuver". It comes with the ability to handle arbitrary mixed width columns (adding up to 100% total width exactly or slightly less for rounding), any gutter size, any predefined amount of columns in width, handles arbitrary amounts of rows with auto-wrapping, and uses inline-block elements so therefore provides the vertical-alignment options that come with inline-block, AND it doesn't require any extra markup and only requires a single class declaration on the container (not counting defining column widths). I think the code speaks for itself. Here's the code implementation for 2-6 columns using 10px gutters and bonus helper classes for percentages.
EDIT: interesting conundrum. I've managed to get two slightly different versions; one for mozilla and ie8+, the other for webkit. It seems the word-spacing trick doesn't work in webkit, and I don't know why the other version works in webkit but not ie8+/mozilla. Combining both gets you coverage over everything and I'm willing to bet there's a way to unify this tactic or something very similar to work around the issue.
EDIT2: Mostly got it! Magical text-align: justify gets WebKit almost there with the word-spacing one. The spacing just seems a tiny bit off, like a matter of pixels on the right and maybe one extra in the gutters. But it's usable and it seems more reliable about keeping the columns than anything I've used before. It never chops down to fewer columns, it'll compress until the browser gets a horizontal scrollbar.
Edit3: Got it a little close to perfect. Setting the font-size to 0 normalizes most of the remaining issues with spacing that's off. Just gotta fix IE9 now which collapses it if it font is size 0.
EDIT4: Got the answer to IE from some other fluid width posts: -ms-text-justify: distribute-all-lines. Tested in IE8-10.
/* The Omega Maneuver */
[class*=cols] { text-align: justify; padding-left: 10px; font-size: 0;
-ms-text-justify: distribute-all-lines; }
[class*=cols]>* { display: inline-block; text-align: left; font-size: 13px;
word-spacing: normal; vertical-align: top;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.cols2 { word-spacing: 20px; padding-right: 20px; }
.cols3 { word-spacing: 30px; padding-right: 30px; }
.cols4 { word-spacing: 40px; padding-right: 40px; }
.cols5 { word-spacing: 50px; padding-right: 50px; }
.cols6 { word-spacing: 60px; padding-right: 60px; }
.cols2 > * { margin-right: -10px; }
.cols3 > * { margin-right: -20px; }
.cols4 > * { margin-right: -30px; }
.cols5 > * { margin-right: -40px; }
.cols6 > * { margin-right: -50px; }
Some helpers:
.⅛, .⅛s >* { width: 12.50%; }
.⅙, .⅙s >* { width: 16.66%; }
.⅕, .⅕s >* { width: 20.00%; }
.¼, .¼s >* { width: 25.00%; }
.⅓, .⅓s >* { width: 33.00%; }
.⅜, .⅜s >* { width: 37.50%; }
.⅖, .⅖s >* { width: 40.00%; }
.½, .½s >* { width: 50.00%; }
.⅗, .⅗s >* { width: 60.00%; }
.⅝, .⅝s >* { width: 62.50%; }
.⅔, .⅔s >* { width: 66.00%; }
.¾, .¾s >* { width: 75.00%; }
.⅘, .⅘s >* { width: 80.00%; }
.⅚, .⅚s >* { width: 83.33%; }
.⅞, .⅞s >* { width: 87.50%; }
.blarg-five-twelfs { width: 41.66%; }
You can witness my magnum opus in action amongst a field of glory here: http://jsfiddle.net/xg7nB/15/
<div class="cols4">
<div class="⅙">This is my magnum opus</div>
<div class="¼">I finally beat css</div>
<div class="⅙">⚉ ☺ ☻ ♾ ☢</div>
<div class="blarg-five-twelfs">I BEAT IT FOREVER</div>
</div>
The absolute minimal implementation, using as an example 4 equal width (25%) width cols and 10px gutters is like so:
.fourEqualCols { word-spacing: 40px; padding: 0 40px 0 10px;
text-align: justify; font-size: 0;
-ms-text-justify: distribute-all-lines; }
.fourEqualCols>* { margin-right: -30px; width: 25%;
display: inline-block; word-spacing: normal;
text-align: left; font-size: 13px; }
<div class="fourEqualCols ">
<div>GLORIOUSLY CLEAN MARKUP</div>
<div>I hate extra markup and excessive class props</div>
<div>Naked code</div>
<div>get intimate</div>
</div>
Soooo this code essentially replaces pretty much any existing grid framework right? If you can arbitrarily set gutters and then just make sets of columns that hit 100% width, that's strictly superior to most/all grid frameworks in fact isn't it? If you're not developing for IE7 anymore like a lot of us then that combined with box-sizing: border-box renders padding and border also a non-issue.
Edit: oh right you wanted to be flush with the sides of the container. No problem with this, I had to specifically add side gutters so we can just change some values by 10 and get rid of the padding and voila. http://jsfiddle.net/bTty3/
[class^=cols] { text-align: justify; font-size: 0;
-ms-text-justify: distribute-all-lines; }
[class^=cols] >* { display: inline-block; text-align: left; font-size: 13px;
word-spacing: normal; vertical-align: top;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
.cols2 { word-spacing: 20px; padding-right: 10px; }
.cols3 { word-spacing: 30px; padding-right: 20px; }
.cols4 { word-spacing: 40px; padding-right: 30px; }
.cols5 { word-spacing: 50px; padding-right: 40px; }
.cols6 { word-spacing: 60px; padding-right: 50px; }
.cols2 >* { margin-right: 0 }
.cols2 >* { margin-right: -10px; }
.cols3 >* { margin-right: -20px; }
.cols4 >* { margin-right: -30px; }
.cols5 >* { margin-right: -40px; }
.cols6 >* { margin-right: -50px; }
Same html
<div class="cols4">
<div class="⅙">This is my magnum opus</div>
<div class="¼">I finally beat css</div>
<div class="⅙">⚉ ☺ ☻ ♾ ☢</div>
<div class="blarg-five-twelfs">I BEAT IT FOREVER</div>
</div>
Try this pure CSS2 solution: demo fiddle
Base CSS (fiddle without the cosmetics):
html, body {
padding: 0;
margin: 0;
}
#wrap {
padding-right: 30px;
overflow: hidden;
}
.col {
float: left;
margin-left: 40px;
margin-right: -30px;
}
.col:first-child {
margin-left: 0;
}
.small {
width: 16.66%;
}
.medium {
width: 25%;
}
.large {
width: 41.66%;
}
HTML:
<div id="wrap">
<div class="col small"></div>
<div class="col medium"></div>
<div class="col small"></div>
<div class="col large"></div>
</div>
Tested on Win7 in IE7, IE8, IE9, Opera 11.50, Safari 5.0.5, FF 6.0, Chrome 13.0.
Update:
Now, if you like this to work with an arbitrary number of columns, you have to add an extra class to the container specifying the column count:
<div class="cols-12 count-04">
<div class="col width-02"></div>
<div class="col width-03"></div>
<div class="col width-02"></div>
<div class="col width-05"></div>
</div>
See this updated fiddle demonstrating a number of various column counts.
Possible bug:
Theoretically, imho this solution should work for any number of columns for every possible minimum column width in any browser window width. But it seems, all browsers prove not being able to handle: 1. a large number of 1 column width columns, or 2. a small browser window width.
Note that all browsers with a minimum width of 1440 pixels, which equals 12 times 120 pixels (the space occupied by all 10px margins), handle the solution just fine. And when you use 2 or more column width colums, the requirement for the minimum browser width indeed drops to 720 pixels (6 * 120px). This last case sounds more realistic to be, but still, I cannot explain this browser behaviour.
I tried fixing the issue by introducing an additional last column class as demonstrated by this fiddle, but it does not solve the problem for small browser widths. It dóes solve a tiny rounding error due to the broken width percentages though, but that issue could be ignored I suppose.
I would like to hear from other css experts on this one, so I added a bounty.
Why don't you use
.column > .panel {
padding: 10px 0 10px 10px;
}
.column:first-child > .panel {
padding-left: 0px;
}
It will make 10px spaces only between boxes and without using last-child.
Check out thirtydot's answer in this thread for a pure CSS/HTML (Fluid layout with equally spaced "columns" without JavaScript)...
Fluid width with equally spaced DIVs
http://jsfiddle.net/thirtydot/EDp8R/
Modification of the JSFiddle demonstrates that the "columns" can be made to be different fixed widths and still have equal and fluid margins.
http://jsfiddle.net/EDp8R/43/
Then finally, another example using percentages while still maintaining equal and fluid margins.
http://jsfiddle.net/EDp8R/45/
I realize this may not be an exact solution but it gets you pretty close, I think.
In referrence to the Original Question "Is there any way, using pure CSS, to achieve a fluid width columned layout with fixed width margins?"
It is remarkable how extremely difficult CSS becomes with these kind of questions. The past week I've been working on a 'base template' to create my own 'holy grail', including border, margin and paddings... It seems CSS fails for these kind of questions. Though the question in mind is quite easy, it becomes (nearly?) impossible to achieve in CSS, especially cross-browser.
The funny part is that these questions are easily resolved by using tables. I do not understand why we are being forced by the web-society to use div's instead for such vague arguments like 'semantics' and 'easy overview' as most arguments are weak or even false. People saying tables are giving more trouble, clearly have no understanding of the real difficulty that lies within CSS.
Anyway, if you want to have an table-structure (as colums are part of a table) I suggest using 'display:table'.
To achieve the image beneath the original question with pure CSS, the following could be used:
CSS
html,body{
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
overflow: auto;
}
.table{
background: pink;
display: table;
width: 100%;
height: 100%;
}
.tableRow{
display: table-row;
}
.tableCell{
display: table-cell;
vertical-align: top;
height: 100%;
}
/*
Ensures the full size of the table-cell has the behaviour of a block-element.
This is needed, because 'table-cell' will behave differently in different browsers.
*/
.tableCell>div{
height: 100%;
}
/*
Padding has to be used instead of margin in 'border-box' modus.
*/
.tableCell>div>div{
height: 100%;
box-sizing:border-box;
-moz-box-sizing:border-box;
}
/*
The final content.
*/
.tableCell>div>div>div{
background: lightblue;
padding: 5px;
height: 100%;
box-sizing:border-box;
-moz-box-sizing:border-box;
}
#col1{
width: 16.66%;
}
#col1>div>div{
padding-right: 10px;
}
#col2{
width: 25%;
}
#col2>div>div{
padding-right: 10px;
}
#col3{
width: 16.66%;
}
#col3>div>div{
padding-right: 10px;
}
#col4{
width: 41.66%;
}
HTML
<div class="table">
<div class="tableRow">
<div id='col1' class="tableCell">
<div><div><div>16.66%</div></div></div>
</div>
<div id='col2' class="tableCell">
<div><div><div>25%</div></div></div>
</div>
<div id='col3' class="tableCell">
<div><div><div>16.66%</div></div></div>
</div>
<div id='col4' class="tableCell">
<div><div><div>41.66%</div></div></div>
</div>
</div>
</div>
I'd say it is quite overdone using additional divs for just a margin, but unfortunately CSS doesn't have a 'margin-box' model, which would actually solve a billion problems.
This amount of nested code might make you think 'why not using other techniques?' as that may result in less code. For a very specific wish that would be the case. However, other techniques often involve floating or absolute positioning. These techniques can not achieve the same thing: floats for example can achieve colums that are equal in length, but when you want a border or margin you'll find yourself in trouble. For absolute positioning it is more like the opposite: you can solve the margin-problem, but the height can only be based on one column.
In my opinion CSS has failed to meet the requirements. While it is ment to replace tables for positiong, after all these years it is still not possible to get the same results. To achieve 'the holy grail of holy grails' table structures are not just the easiest way, there are also the only way... at least, for as far I know after trying hundreds of possibilities.
The remaining question is: why using divs if you're using them as tables? This I do not fully understand myself, but people seem to have their reasons for that.
I use the grid of OOCSS for this
https://github.com/stubbornella/oocss
I recently put a demo online on my own site since there are no proper examples online :(
http://www.leipeshit.com/awesome_stuff/oocss/core/grid/grids_all.html
An easier way to get the same effect is to let the content inside your columns create your gutters rather than applying margins/padding to the columns themselves. This can be done with fixed, fluid, elastic, etc. grids.
For example:
/* Gutters */
h1, h2, h3, h4, h5, h6,
p, ul, ol, blockquote,
hr, address, pre, object, fieldset
{
margin-right: .75rem;
margin-left: .75rem;
padding-right: .75rem;
padding-left: .75rem;
}
This also simplifies sizing your columns, nesting, and applying backgrounds to your lego pieces.
Why not using the padding like in your first example and then set box-sizing: border-box on all elements?
I have recently developed an alternative solution for this which allows for any combination of flexible columns within a row with a fixed and consistent margin across all columns, regardless of the parent element's width.
This doesn't use any JavaScript and works in IE8+.
In this solution the margin is defined on two classes - so it is easy to change it for responsive designs. The column widths are also representative of the space they use, for example a 2 column row has widths of 50% and a 4 column row has widths of 25%.
You can see an example at http://www.andrewjamestait.co.uk/conflexgrids/
Or it is available on GitHub at https://github.com/andrewjtait/conflexgrids
If you can live with another nested div per column you could define the desired margin for each. To get rid of the margin on the left and right outer edges you can define a negative margin on the outer container.
E.g.: Using pureCSS pure-g is the outer container, pure-u-* is a column node (display: inline-block) containing the nested div. spacing is the name of this custom extension of the pureCSS grid system to allow column margins.
.pure-g.spacing {
margin: 0 -10px;
}
.pure-g.spacing [class *= "pure-u"] > div {
margin: 10px;
}
Should work on most browsers. Tell me if it doesn't - I am using it already.
regards,
Max
Related
I have a simple scenario where I would like two boxes to display next to each other and to take the full width of the page, with 0.75em margin between them. Here is my markup:
<div class="box"></div>
<div class="box"></div>
and the CSS:
.box {
float: left;
width: calc(50% - 0.375em); /* 0.375 * (2 boxes) = 0.75 */
margin-right: 0.75em;
}
.box:last-of-type {
margin-right: 0;
}
This works correctly on Chrome, but when I try to load it in IE11, the second box wraps to the next line. I have set the font-size on the html element to 16px. Interestingly, if I replace 0.375em with 6px (16px * 0.375 = 6px), the boxes show up correctly on one line in IE11. Any thoughts anyone?
Here is a JSFiddle where you can see this not working in IE: http://jsfiddle.net/Lcuu8/3/
IE miscalculates 0.375em when em equals 16px. Instead of the mathematically correct result 6px, it gets 5.92px. This can be seen by setting the width of an element to 0.375em and inspecting the element in developer tools. There you can see that IE also shows the declared with as 0.37em, so it seems to drop any digits after 0.37! This happens also when using simply width: 0.375em, without using calc() at all.
In this case, the bug makes the width of each box a fraction of a pixel larger than it should be, and thus the second box does not fit on the right of the first a box.
A simple solution is to use integers. Mathematically, 0.375 equals 3/8, so you would replace
width: calc(50% - 0.375em)
by
width: calc(50% - 3em/8)
It is probably due to the way IE is handling the percentage. My suggestion would be to simplify and allow IE to handle the percentage differently.
I've created an alternative method in this jsfiddle
Where I've created wrappers
<div class="boxWrapper"><div class="box"></div></div>
<div class="boxWrapper"><div class="box"></div></div>
And then set your CSS to handle the margin inside the wrappers
html, body { font-size: 16px; }
.boxWrapper {
width:50%;
float:left;
}
.box {
height : 5em;
background: slategray;
margin-right: 0.375em;
display:block;
}
.boxWrapper:last-child .box {
margin-left: 0.375em;
margin-right:0;
background: silver;
}
In your code, calc function is working correctly. But the problem comes from the decimal number you have used for calculation.
In Chrome the values are truncated. For ex, 50, 50.5 and 50.6 all show the same width in chrome which IE does not.
Use rounded values in the calculation part, you will get the desired result in IE and Chrome.
.box {
float: left;
width: calc(50% - 0.4em); /* 0.375 * (2 boxes) = 0.75 */
margin-right: 0.8em;
}
DEMO
I am learning how to code HTML and CSS, and I decided to make my own website in the process.
My question is: how would I align smaller text to a bigger object, for example, links to different pages on my website neatly aligned under my full name with the links flush to the of the beginning and end of my full name?
I know describing it may have been a bit confusing, so here's an image of what I mean:
Any suggestions?
Thanks!
You can approximate the look and design regardless of the header length, but in the end, CSS doesn't offer as precise typographical tools as you'd need and you will have to nudge the percentages one way or another once you know the length of your actual text.
Sample Jsfiddle
HTML:
<div id="container">
<h1>Large Title Here Etc</h1>
<div id="sub">
<span>music</span>
<span>film</span>
<span>web</span>
<span>photo</span>
</div>
</div>
CSS:
body {
text-align: center;
}
#container {
display: inline-block;
}
h1 {
font-size: 2em;
}
#sub {
font-size: 1em;
display: table;
width: 120%;
box-sizing: border-box;
margin: 0 -10%;
}
#sub span {
display: table-cell;
padding: 0 2%;
}
links flush to the beginning and end of my full name
Get out of the habit of thinking this way as you design websites. This will lead to endless headaches and frustrations for you, as it depends on browser rendering (and possibly rendering bugs), the user's font size, the user's font, and loads of other factors you cannot control. Instead of going for 'pixel precision', the idea is simply to make it look as good as you can on most things.
When designing things like this, consider the markup first. What is the structure of what you're actually writing? In your linked image, Full Name looks to me like a header (perhaps h1), while menus like that are normally done as styled unordered lists (ul) these days. Below is an example of how I might make something similar to what is in your image.
Here is the markup:
<div id="container">
<h1>Full Name</h1>
<ul>
<li>music</li>
<li>film</li>
<li>web</li>
<li>photo</li>
</ul>
</div>
and the CSS used, with comments:
#container { border: 1px solid; }
h1 {
margin-bottom: 0;
text-align: center;
}
ul {
margin: 0.5em;
/* remove default padding inserted by browser */
padding-left: 0;
/* no bullets */
list-style-type: none;
/* this works on inline objects, not just text */
text-align: center;
}
li {
/* hybrid of inline and block; obeys text-align */
/* Also note this does not work in IE <9. Workarounds exist. */
display: inline-block;
padding: 3px;
}
And here is the end result: http://jsfiddle.net/3PLgz/1/
I have a use case whereby I want to draw rectangles in CSS. I need them to look like this:
I've managed to get the smaller and taller boxes drawn but can't work out how to draw those that drop below the line. Here's a fiddle
Heres' my HTML:
<div class="word">
<p class="letter taller"></p>
<p class="letter"></p>
<p class="letter"></p>
<p class="letter hanging"></p>
<p class="letter"></p>
<p class="letter taller"></p>
<p class="letter"></p>
</div>
Here's my CSS so far:
p {
display: inline-block;
}
.letter {
padding 1.618em;
border-width: 1px;
border-style: solid;
width: 2em;
height: 2em;
}
.taller {
height: 4em;
}
.hanging {
/* not sure what to implement here */
}
Using margins may affect other elements, especially if you plan on including other content on your page. (See this) I'd recommend using position: relative combined with top: 2em. What that does is it pushes the element down 2em, relative to the original position of the element.
.hanging {
height: 4em;
position: relative;
top: 2em;
}
http://jsfiddle.net/WtuyL/6/
(On an unrelated note... here's a little bonus if you want to fully imitate the image and remove whitespace. You'll net to set a manual size to all <p> elements though.)
The simplest way is to use a negative margin-bottom to achieve this (you don't need to use positioning):
CSS:
.hanging {
margin-bottom: -16px;
height:4em;
}
JSFiddle
Note: also comment the whitespace between display:inline-block elements to remove it.
Reference - see this to see more hacks how to remove the whitespace between display:inline-block elements.
Try this.
.hanging {
height:4em;
margin-bottom:-1em;
}
I'm building a navigation using the simple <ul><li></li><ul> system and floating them to the left side so they appear inline. The follow code works in all browsers except IE 6.
The HTML
<div id="sandbox_container">
<div id="sandbox_modalbox">
<div>
<ul id="sandbox_modalbox_nav">
<li id="Intro" class="modal_active">Item 1</li>
<li id="Queries">Item 2</li>
</ul>
</div>
<!-- more content here -->
</div>
</div>
The CSS
#sandbox_container {
min-height: 385px;
width: 940px;
padding-bottom: 20px
}
#sandbox_modalbox {
width: 940px;
padding-top: 5px;
margin-bottom: -10px;
}
ul#sandbox_modalbox_nav {
width: 936px;
height: 52px;
margin: 0px 2px 0px 2px;
padding-top: 0px;
display: block;
}
ul#sandbox_modalbox_nav li {
height:52px;
float: left;
list-style: none;
padding: 0px;
display: block;
}
ul#sandbox_modalbox_nav li a {
padding: 12px 30px 0px 30px;
height: 52px;
display: block;
}
I also put it up on JSBin.
I understand the problem is that I must define a width for the <li> for IE to float it properly, however I would prefer these remain variable width. Is there anyway to float them properly without restricting the width?
If I am understanding the problem correctly then in browsers other than IE6 the list items appear next to each other, but in IE6 they appear on top of each other.
If this is the case, it may be because the a elements are not floated even though their containing elements are. I would just use a conditional comment and add the following for IE6 only:
ul#sandbox_modalbox_nav li a { float:left; }
Also, Neall is right on track with the whitespace issue, even if it doesn't fix your current display problem it may cause some unwanted space to appear between items later.
Not that I can think of, I can't imagine how to declare a width that can change, except by defining it in ems. If you have a content that you know is likely to be less than ten characters, then width: 11em; padding: 0.5em 1em; is likely to offer enough space for the content while still defining a width.
IE 6 has some bugs with whitespace between <li> elements. Try putting all your list items on the same line with no space between them.
Edit: On further inspection, I don't think the whitespace is your problem. Your example has a lot of extraneous styles - it's hard to tell what the problem is.
I usually solve this by setting the floated list items to width: 0 for IE6. This for one reason or other causes them to have the correct dynamic width.
You can either do this in a conditional comment:
<!--[if lte IE 6]>
<style type="text/css">ul#sandbox_modalbox_nav li { width: 0; }</style>
<![endif]-->
Or simply take advantage of IE's lack of support for CSS selectors, by setting the width to 0, and then back to the default "auto" for modern browsers:
ul#sandbox_modalbox_nav li { width: 0; }
ul#sandbox_modalbox_nav > li { width: auto; }
Ok, I had a simple layout problem a week or two ago. Namely sections of a page needed a header:
+---------------------------------------------------------+
| Title Button |
+---------------------------------------------------------+
Pretty simple stuff. Thing is table hatred seems to have taken over in the Web world, which I was reminded of when I asked Why use definition lists (DL,DD,DT) tags for HTML forms instead of tables? Now the general topic of tables vs divs/CSS has previously been discussed, for example:
DIV vs Table; and
Tables instead of DIVs.
So this isn't intended to be a general discussion about CSS vs tables for layout. This is simply the solution to one problem. I tried various solutions to the above using CSS including:
Float right for the button or a div containing the button;
Position relative for the button; and
Position relative+absolute.
None of these solutions were satisfactory for different reasons. For example the relative positioning resulted in a z-index issue where my dropdown menu appeared under the content.
So I ended up going back to:
<style type="text/css">
.group-header { background-color: yellow; width: 100%; }
.group-header td { padding: 8px; }
.group-title { text-align: left; font-weight: bold; }
.group-buttons { text-align: right; }
</style>
<table class="group-header">
<tr>
<td class="group-title">Title</td>
<td class="group-buttons"><input type="button" name="Button"></td>
</tr>
</table>
And it works perfectly. It's simple, as backward compatibile as it gets (that'll work probably even on IE5) and it just works. No messing about with positioning or floats.
So can anyone do the equivalent without tables?
The requirements are:
Backwards compatible: to FF2 and IE6;
Reasonably consistent: across different browsers;
Vertically centered: the button and title are of different heights; and
Flexible: allow reasonably precise control over positioning (padding and/or margin) and styling.
On a side note, I came across a couple of interesting articles today:
Why CSS should not be used for layout; and
Tables vs CSS: CSS Trolls begone
EDIT: Let me elaborate on the float issue. This sort of works:
<html>
<head>
<title>Layout</title>
<style type="text/css">
.group-header, .group-content { width: 500px; margin: 0 auto; }
.group-header { border: 1px solid red; background: yellow; overflow: hidden; }
.group-content { border: 1px solid black; background: #DDD; }
.group-title { float: left; padding: 8px; }
.group-buttons { float: right; padding: 8px; }
</style>
</head>
<body>
<div class="group-header">
<div class="group-title">This is my title</div>
<div class="group-buttons"><input type="button" value="Collapse"></div>
</div>
<div class="group-content">
<p>And it works perfectly. It's simple, as backward compatibile as it gets (that'll work probably even on IE5) and it just works. No messing about with positioning or floats.</p>
<p>So can anyone do the equivalent without tables that is backwards compatible to at least FF2 and IE6?</p>
<p>On a side note, I came across a couple of interesting articles today:</p>
</div>
</body>
</html>
Thanks to Ant P for the overflow: hidden part (still don't get why though). Here's where the problem comes in. Say I want the title and button to be vertically centered. This is problematic because the elements are of different height. Compare this to:
<html>
<head>
<title>Layout</title>
<style type="text/css">
.group-header, .group-content { width: 500px; margin: 0 auto; }
.group-header { border: 1px solid red; background: yellow; overflow: hidden; }
.group-content { border: 1px solid black; background: #DDD; }
.group-header td { vertical-align: middle; }
.group-title { padding: 8px; }
.group-buttons { text-align: right; }
</style>
</head>
<body>
<table class="group-header">
<tr>
<td class="group-title">This is my title</td>
<td class="group-buttons"><input type="button" value="Collapse"></td>
</tr>
</table>
<div class="group-content">
<p>And it works perfectly. It's simple, as backward compatibile as it gets (that'll work probably even on IE5) and it just works. No messing about with positioning or floats.</p>
<p>So can anyone do the equivalent without tables that is backwards compatible to at least FF2 and IE6?</p>
<p>On a side note, I came across a couple of interesting articles today:</p>
</div>
</body>
</html>
which works perfectly.
There is nothing wrong with using the tools that are available to you to do the job quickly and correctly.
In this case a table worked perfectly.
I personally would have used a table for this.
I think nested tables should be avoided, things can get messy.
Just float left and right and set to clear both and you're done. No need for tables.
Edit: I know that I got a lot of upvotes for this, and I believed I was right. But there are cases where you simply need to have tables. You can try doing everything with CSS and it will work in modern browsers, but if you wish to support older ones... Not to repeat myself, here the related stack overflow thread and rant on my blog.
Edit2: Since older browsers are not that interesting anymore, I'm using Twitter bootstrap for new projects. It's great for most layout needs and does using CSS.
Float title left, float button right, and (here's the part I never knew until recently) - make the container of them both {overflow:hidden}.
That should avoid the z-index problem, anyway. If it doesn't work, and you really need the IE5 support, go ahead and use the table.
This is kind of a trick question: it looks terribly simple until you get to
Say I want the title and button to be vertically centered.
I want to state for the record that yes, vertical centring is difficult in CSS. When people post, and it seems endless on SO, "can you do X in CSS" the answer is almost always "yes" and their whinging seems unjustified. In this case, yes, that one particular thing is hard.
Someone should just edit the entire question down to "is vertical centring problematic in CSS?".
In pure CSS, a working answer will one day be to just use "display:table-cell". Unfortunately that doesn't work across current A-grade browsers, so for all that you might as well use a table if you just want to achieve the same result anyway. At least you'll be sure it works far enough into the past.
Honestly, just use a table if it's easier. It won't hurt.
If the semantics and accessibility of the table element really matter to you, there is a working draft for making your table non-semantic:
http://www.w3.org/TR/wai-aria/#presentation
I think this requires a special DTD beyond XHTML 1.1, which would just stir up the whole text/html vs application/xml debate, so let's not go there.
So, on to your unresolved CSS problem...
To vertically align two elements on their center: it can be done a few different ways, with some obtuse CSS hackery.
If you can fit within the following constraints, then there is a relatively simple way:
The height of the two elements is fixed.
The height of the container is fixed.
The elements will be narrow enough not to overlap (or can be set to a fixed width).
Then you can use absolute positioning with negative margins:
.group-header { height: 50px; position: relative; }
.group-title, .group-buttons { position: absolute; top: 50%; }
# Assuming the height of .group-title is a known 34px
.group-title { left: 0; margin-top: -17px; }
# Assuming the height of .group-buttons is a known 38px
.group-buttons { right: 0; margin-top: -19px; }
But this is pointless in most situations... If you already know the height of the elements, then you can just use floats and add enough margin to position them as needed.
Here is another method which uses the text baseline to vertically align the two columns as inline blocks. The drawback here is that you need to set fixed widths for the columns to fill out the width from the left edge. Because we need to keep the elements locked to a text baseline, we can't just use float:right for the second column. (Instead, we have to make the first column wide enough to push it over.)
<html>
<head>
<title>Layout</title>
<style type="text/css">
.group-header, .group-content { width: 500px; margin: 0 auto; }
.group-header { border: 1px solid red; background: yellow; }
.valign { display: inline-block; vertical-align: middle; }
.group-content { border: 1px solid black; background: #DDD; }
.group-title { padding: 8px; width: 384px; }
.group-buttons { padding: 8px; width: 84px; text-align: right; }
</style>
<!--[if lt IE 8]>
<style type="text/css">
.valign { display: inline; margin-top: -2px; padding-top: 1px; }
</style>
<![endif]-->
</head>
<body>
<div class="group-header">
<div class="valign">
<div class="group-title">This is my title.</div>
</div><!-- avoid whitespace between these! --><div class="valign">
<div class="group-buttons"><input type="button" value="Collapse"></div>
</div>
</div>
<div class="group-content">
<p>And it works perfectly, but mind the hacks.</p>
</div>
</body>
</html>
The HTML: We add .valign wrappers around each column. (Give them a more "semantic" name if it makes you happier.) These need to be kept without whitespace in between or else text spaces will push them apart. (I know it sucks, but that's what you get for being "pure" with the markup and separating it from the presentation layer... Ha!)
The CSS: We use vertical-align:middle to line up the blocks to the text baseline of the group-header element. The different heights of each block will stay vertically centered and push out the height of their container. The widths of the elements need to be calculated to fit the width. Here, they are 400 and 100, minus their horizontal padding.
The IE fixes: Internet Explorer only displays inline-block for natively-inline elements (e.g. span, not div). But, if we give the div hasLayout and then display it inline, it will behave just like inline-block. The margin adjustment is to fix a 1px gap at the top (try adding background colors to the .group-title to see).
I would recommend not using a table in this instance, because that is not tabular data; it's purely presentational to have the button located at the far right. This is what I'd do to duplicate your table structure (change to a different H# to suit where you are in your site's hierarchy):
<style>
.group-header { background: yellow; zoom: 1; padding: 8px; }
.group-header:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
/* set width appropriately to allow room for button */
.group-header h3 { float: left; width: 300px; }
/* set line-height or margins to align with h3 baseline or middle */
.group-header input { float: right; }
</style>
<div class="group-header">
<h3>This is my title</h3>
<input type="button" value="Collapse"/>
</div>
If you want true vertical alignment in the middle (ie, if the text wraps the button is still middle-aligned with respect to both lines of text), then you either need to do a table or work something with position: absolute and margins. You can add position: relative to your drop-down menu (or more likely its parent) in order to pull it into the same ordering level as the buttons, allowing you to bump it above them with z-index, if it comes to that.
Note that you don't need width: 100% on the div because it's a block-level element, and zoom: 1 makes the div behave like it has a clearfix in IE (other browsers pick up the actual clearfix). You also don't need all those extraneous classes if you're targeting things a bit more specifically, although you might need a wrapper div or span on the button to make positioning easier.
Do a double float in a div and use the clearfix. http://www.webtoolkit.info/css-clearfix.html Do you have any padding/margin restrictions?
<div class="clearfix">
<div style="float:left">Title</div>
<input type="button" value="Button" style="float:right" />
</div>
<div class="group-header">
<input type="button" name="Button" value="Button" style="float:right" />
<span>Title</span>
</div>
I've chose to use Flexbox, because it made things so much easier.
You basically need to go to the parent of the children you want to align and add display:box (prefixed of course). To make them sit in the sides, use justify-content. Space between is the right thing when you have elements which need to be aligned to the end, like in this case (see link)...
Then the vertical align issue. Because I made the parent of the two elements, you want to align a Flexbox. It's easy now to use align-items: center.
Then I added the styles you wanted before, removed the float from the title and button in the header and added a padding:
.group-header, .group-content {
width: 500px;
margin: 0 auto;
}
.group-header{
border: 1px solid red;
background: yellow;
overflow: hidden;
display: -webkit-box;
display: -moz-box;
display: box;
display: -webkit-flex;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-justify-content: space-between;
-moz-justify-content: space-between;
-ms-justify-content: space-between;
-o-justify-content: space-between;
justify-content: space-between;
webkit-align-items: center;
-moz-align-items: center;
-ms-align-items: center;
-o-align-items: center;
align-items: center;
padding: 8px 0;
}
.group-content{
border: 1px solid black;
background: #DDD;
}
.group-title {
padding-left: 8px;
}
.group-buttons {
padding-right: 8px
}
See Demo
I agree that one should really only use tables for tabular data, for the simple reason that tables don't show until they're finished loading (no matter how fast that is; it's slower that the CSS method). I do, however, feel that this is the simplest and most elegant solution:
<html>
<head>
<title>stack header</title>
<style type="text/css">
#stackheader {
background-color: #666;
color: #FFF;
width: 410px;
height: 50px;
}
#title {
color: #FFF;
float: left;
padding: 15px 0 0 15px;
}
#button {
color: #FFF;
float: right;
padding: 15px 15px 0 0;
}
</style>
</head>
<body>
<div id="stackheader">
<div id="title">Title</div>
<div id="button">Button</div>
</div>
</body>
</html>
The button function and any extra detail can be styled from this basic form. Apologies for the bad tags.