ok, on to the CSS.
The first thing I put in EVERY CSS file I work with is a reset. Not all browsers start out with the same paddings or margins on everything, or even the same borders. A lot of people argue against the use of resets, but I find them to use less code overall and in general prevent a lot of cross browser headaches. There are smaller resets, like the so called “universal” reset using the * selector, but it tends to mess up form elements. There are larger ones, like Eric Meyer’s “Reset Reloaded” – but that fat bloated pig borders on being a CSS framework in scope, and on the whole is responsible for why “CSS resets” get a bad rap in some circles.
html, body – After the margin/padding/border resets comes setting a fixed 100% height on HTML and BODY. In CSS elements cannot accept height unless their parent has a height declared on it (or positioning). Setting 100% height on both these parent elements lets us set 100% min-height on our pageWrapper. Opera sometimes screws up redrawing this or even calculating it – adding position:relative clears up that little issue nicely.
body – I set up a default font size in percentages. Where possible your “content” area on the page should use dynamic fonts (%/em) and not fixed fonts (px). PX is often the only solution to certain layouts, but if you have to go PX for your content area, you should rethink the design instead. 85% gives 14px to 96dpi/Small font users, and 17px to large font/120dpi users – a nice comfortable font size for both.
When declaring my body font, I ALWAYS declare a line-height – and by the time you declare font-size, line-height and font-family, you might as well use the condensed font declaration. WHENEVER you change the font size, I HIGHLY recommend setting the line-height again as you CANNOT trust it to inherit… This is why whenever I change the font, I just use the full property.
The background setting gives us that grey for the content, and the left border image. I actually put both sides of the border into a single file 2560 wide and 1px tall. It is VERY unlikely anyone would view it wider than that, and by using a ‘play’ on sliding-doors (combined with #pageWrapper) we can use one really small .png instead of the two separate .gif you had.
More files is bad – there’s this thing called “handshaking”, the communication between the browser and the server to request files. The less times you go to the server, the faster a page can load. It’s where image-recombination and image-sprites can really come in handy.
* html body – this declaration loads a behavior file, csshover3.htc. Behavior files are IE specific javascripts you can load from your CSS. A lot of people won’t use them because some broken ass servers don’t send it with the right mime-type breaking how they work. I refuse to not use something just because some nimrod is too much of a {expletive omitted} to configure their servers properly… especially since on REAL servers said mime-type issue can be circumvented via .htaccess.
The file in question is called “Whatever:hover” from PeterNed’s website. Horrible website, but bloody useful little script.
Whatever:hover
The main point of that file, it makes the menu work by letting IE6 and lower do :hover on any element.
You may not have seen “* html” before. It’s a hack – it’s an ugly hack, that says “all html tags that have a parent” – HTML can’t have a parent element, but IE6 and lower incorrectly thinks it does. The selector grouping itself IS valid CSS, it just shouldn’t target anything. It’s a quick and easy way to say “IE6 and lower” without bloating out the markup with an extra stylesheet include stuffed inside a conditional comment.
body:before – this is bugfix for opera. When resizing min-height layouts Opera doesn’t like to do it unless there’s a 100% height child-element directly off body. The :before psuedo-element and content attribute are what’s called “generated content” – CSS making it’s own elements. This is a VERY powerful tool, but support across browsers limits it’s utility. In this case we’re really only using it for Opera so all the compatibility stuff doesn’t matter. Giving it a a float and MASSIVE negative top margin hides it from interfering with other browsers.
#pageWrapper – I give this a min-width since the layout falls apart below that anyways… That’s a 800 friendly min-width anyhow – anything below that should proably be getting a handheld/mobile stylesheeet all it’s own…
The min-height declaration is where the magic happens. This means that if the content is taller than screen height, we get that height on #pageWrapper. If the content isn’t that tall, #pageWrapper is the same height as the screen.
Finally the background – this just puts the right side border in place.
* html #pageWrapper – The comment says it all… IE6 and lower have no min-height, but for some weird reason treat height like it was min-height… so we just need to set height for IE6 and lower. Again “* html” makes this easy.
h1 – as there should only ever be ONE H1 on a page, and that all other headings for subsections of the page should be considered subsections of that H1, usually the only sane candidate is your site heading/title/logo/etc.
The height:1% fixes an odd bug in IE8 where it kinda behaves like Opera does. A lot of people were using a fix similar to the opera one, but I discovered that if you set a small percentage height on a child element of the min-heighted element (#pagewrapper) it fixes IE8’s behavior. Oddly this is NOT the usual reason height:1% is used – which is for a thing called “haslayout”. (We’ll get to that one in a bit).
The relative positioning allows us to absolute position an element inside this, with top/left 0:0 being the top left corner of the H1 instead of the page.
The text-align is nothing fancy, and the font declaration gives a similar appearance to images off users as your image version does. Load up the page in Opera (or firefox with the web developer toolbar installed) and disabled images to see what I mean.
Finally the background gets the color we want and the top gradient. The gradients I also combined into a 5120x32 file. Despite the massive resolution of that file, it still ends up under 1k as a png – PNG is BRUTALLY EFFECTIVE at compressing image files that have long horizontal runs of the same color. The left half of the image is the top gradient, the right half is the bottom one… “top left” therin shows the part we want at the top – again one file instead of two.
h1 small – small is an inline-level tag, and we want that subtext on it’s own line… so we set it to display:block for that behavior, then I just set the text size as desired.
h1 span – spans are also inline-level, so they to are set to block so they have height and can accept padding. The outermost span gets the bottom part of the gradient.
h1 span span – the next span in the h1 then gets the right logo. Rather than play with positioning or floats, your layout makes it pretty easy to just add a span, and use background-position to move it where we want. The auto-centering also means no headaches in terms of figuring out the math.
h1 span span span – the innermost wrapping span is of course the image for the opposite side. The padding is applied so that we have our total desired height around the text. We have one line of 60px line-height, one line of 28px line-height (small) and 32px padding, which gives us a total height of 152px which looks ok. Since all the backgrounds are positioned automatically, you can increase/decrease the height by changing the padding easily without touching anything else.
h1 span span span span – the fourth span is an empty (except for a comment) sandbag. I absolute position this over the text inside it – then call the actual logo image. Net result? it hides the text that’s in there for screen readers/search engines/images off/non-css users for your pretty image instead.
Because the text is in flow, we have a height to push the menu where it needs to be – which is where APO does the job nicely. This is what absolute positioning is for… to be an element placed inside/over something that has either a fixed height, or other content that makes it have height.
#mainMenu – list-style turns off bullets, the position:relative makes it depth sort over any position:relative elements before it… text-align is how the centering is done…
Then there’s the negative margin which will take a bit of explaining. I like to describe elements on a page as having two separate but related boxes – the ‘flow box’ which says where it is and how big it is in ‘flow’ – how the page is laid out. Then there’s the render box, which says how large it is when it’s drawn. Absolute positioning and floats remove items from ‘flow’ – margins on the other hand change the size of the flow box… so a negative top margin makes the element ‘shorter’ in flow, while the render box stays the same – bottom line? It rides it up OVER the h1 into the desired position.
It’s not a technically accurate description, but it works.
It oversimplifies and it ain’t quite right, but for purposes here it will have to do, 'cause I ain’t got the time to explain it to you… – MC Hawking
I violate one of my basic rules by using PX fonts. With the interaction to the H1 background and the images it loads on itself, PX is the only way to accurately predict it’s behavior across systems. When I’m forced to use PX, I consider 12px the smallest acceptable size, and really I get squirrelly over anything smaller than 14px.
#mainMenu li – the relative positioning allows us to position the nested UL dropdowns. The inline-block behavior lets them have width and height and positioning… then we have TWO hacks.
First up is the !ie hack. Legacy versions of IE (prior to IE8) either don’t have or screw up inline-block on block-level tags. (like LI)… but incorrectly treat display:inline as that. !IE is a hack that doesn’t work in newer versions of IE, but works in all the versions we need to fix that for. I don’t like resorting to that particular hack as it’s invalid CSS, but it’s the cleanest solution to the problem.
You can read up on selector hacks here:
CSS Hacks
Then there’s zoom:1; – the zoom property is also invalid CSS as it’s IE specific. Given all the browser specific nonsense people are doing with CSS3, I no longer feel guilt about using it to trip ‘haslayout’ – which is why it’s here.
Legacy (and sometimes modern) versions of IE often screw up on positioning elements and sometimes even rendering them. This all stems from a shortcut they made to try and make IE faster than Netscape. While it worked for that, it made CSS 2 a bit painful at times (though to be fair, IE5 was the first to even TRY to implement CSS2 in a real world deployable manner). Setting this makes the dropdowns behave in IE.
You can read up on haslayout here:
On having layout
#mainMenu a – the top level anchors get inline-block set on them so we can set top/bottom padding. Again I avoid setting heights since I don’t need to… 16px line-height + 6 px top padding + 2px bottom padding == that 24 px we wanted.
Rest of it’s just fonts, colors and images – the main-menu.png I made also has both states in it… hovered and unhovered. This pre-caches the hover state, and again uses one file instead of two.
#mainMenu li:hover a – since we want it to stay lit up when you’re in the submenu, we need to hover off the LI, hence the htc file up above. When you add border the size changes, so I decrease the padding by the border size to account for this. Rest of it is fonts, colors, border, and sliding the background-position to reveal the hover state part of the background image.
#mainMenu ul – the nested lists also get stripped of bullets, and are absolute positioned. I start them out positioned off screen using a massive negative left position. We have to fix the height for proper legacy IE behavior… the negative left margin slides it over half it’s width, which we’ll make use of in the hover state. The display:inline is there so that when we show it we can set display:block – sometimes IE7 and IE8 will refuse to render a change in position unless you change the display state, positioning type, or overflow. I use the display state for that fix. Without it, IE7 doesn’t like to hover, and when it does, it tends to not unhover.
#mainMenu ul li – if you don’t set these to block, IE7 and 8 will fail to let the block anchors will the width. Since this is safe to send to all modern browsers we set it first.
* html #mainMenu ul li – but, that screws up IE6, so we have to set it back to inline for those.
#mainMenu li:hover li a – we have to say the :hover for what’s called ‘specificity’ reasons. Some browsers will ignore the declaration without it, failing to override any values we set on “#mainMenu li:hover a”
Block, padding, no background, no border (unsetting the border set by the above hover).
#mainMenu li:hover ul – as mentioned, we change the display state straightening out IE 7/8’s behavior on them, and I move them over to “left:50%” – halfway across the parent element. Remember that 80px negative margin? Add them together and they’re centered.
#mainMenu li:hover li ul – the next menu in though we don’t want showing, so we have to hide it.
#mainMenu li li:hover ul – until we hover the correct element, which is when we show it. Different positioning, but same idea.
#mainMenu li li:hover a AND
#mainMenu li li li a:hover – just colors for the nested menu states.
Wow, that’s it for the menu.
Content – the top padding of 1EM is to match the bottom padding I usually put on paragraphs and other elements. The side padding is to push it in from the side borders we have on body and #pageWrapper, and the bottom padding makes ‘room’ for the footer. That bottom padding should be the same height as your footer in a min-height layout.
Content p – I set up paragraphs even though you don’t have them yet. I use 1em because it’s most always attractive and adjusts to the font size – I use padding instead of margins so I don’t have to even worry about margin collapse, and I set bottom padding only since there is no collapse.
.plate – The print term for a image in a document is a ‘plate’ – stemming from the large metal plates that were used in fixed typesetting. I usually have three classes set up… “.plate” for centered images, “leadingPlate” for images text is supposed to wrap around (float:left; margin:0 1em 1em 0;) and “trailingPlate” for images text is supposed to wrap around the other way (float:right; margin:0 0 1em 1em;) – I call them this instead of ‘left’ and ‘right’ because I might not be displaying them that way on all targets (like say handheld) if at all (like say… print).
#footer – More magic goes on here. To do a min-height layout the height of your footer MUST remain fixed, and with firefox being a total dip about how %/EM adds up or even means unable to calculate it the same way twice in a row, we’re stuck back on PX fonts. Position:relative, fix the height, and then use a negative margin to ride it up over Content’s bottom padding… since we ride it up over #pageWrapper we end up at it’s 100% min-height for the whole document… from there it’s just colors and the top gradient.
#search – I pad the top and bottom just to align it where it looks good.
#search input – vertical-align makes sure the search input and the submit image line up with each-other.
#search_field – width, and since input doesn’t inherit fonts, set the font size so the min-height layout doesn’t break on large font machines.
… and that’s the whole beast in a nutshell.