CSS Design

From Elvanör's Technical Wiki
Jump to navigation Jump to search

General Techniques

Selectors & Rules

Selectors

  • To apply an "AND" on a selector in CSS (for example, to apply a style to elements belonging to two classes), just repeat the normal selector, like this:
*.FirstClass.SecondClass
  • This would probably also work on class and ID, etc (untested yet):
*.first_class#myId
  • To select every column in a table except the first one, use the following selector:
tr td + td
  • To eliminate the first row from the selector, just add tr +:
tr + tr td + td
  • There is no negative selector in CSS 2.1 (for example, select elements that don't have this class name). There is one in CSS 3.
  • The :lang(xx) pseudo selector allows you to select all the elements that have a particular language. You can define a language on all elements, it should at least be done on the root <html> element. This would be the correct way to specify language-dependent CSS data (widths for some text blocks for instance), but IE until version 8 does not support it. So to support IE 7, by convention one can use (example):
html[lang="fr"] form#myFormId label

which works well.

Rules

  • Rules do not apply in the order they are encountered, but in the order of their specificity. For example if you have a rule with a selector of p and a rule with a selector of p.Class, the later will be favored even if it is encountered earlier in the CSS file. This makes sense.
  • The computing of specificity is not very clear in the CSS specification. It seems the counted numbers of ids and classes only take into account the current element; eg ids and classes for parents are not considered.

Hovering

  • Changing an image based on hovering, or creating an hovering menu, can almost always be done in CSS. There is no need for JS. Typically you would write rules such as:
div#base img {
    display: inline;
}

div#base img.Hover {
    display: none;
}

div#base:hover img {
    display: none;
}

div#base:hover img.Hover {
    display: inline;
}

Display

  • Note that display: inline; does not restrict the width whatsoever. This means that if you have a <div> element with display: inline, and a child that creates a block element like <p>, the child element will take all the place available. This will actually nullify the effect of the parent display: inline property.
  • Don't use an element that has display: inline property if it contains block children.
  • Images are inline-replaced elements. This means that vertical margins are possible on those elements.

display: table-x

  • These properties don't work in IE. However they can be extremely useful on standard browsers and make elements behave as tables. It can for example allow two paragraphs to exist in two columns, and get the second paragraph easily aligned on the bottom of the first [(see this example)].

Overflow

  • On IE 7, overflow-x: hidden; should be placed on the html element, not the body. This has the effect that horizontal scrollbars won't appear, it can be very useful when using a central fixed layout and placing decorations on the edges.

Hiding elements

  • visibility: hidden; will hide the elemement while keeping its layout space. display: none; will hide the element and rearrange the flows (eg, containers of the element will resize, etc).

Positioning

  • Do not put a position: relative on your body element. This causes all sort of display problems with Firefox, and probably other browsers as well. It also confuses Prototype to the point that some functions such as cumulativeOffset() don't work anymore.

Absolute Positioning

  • This is done with respect to the parent element/block (the container). Thus, if you have a containing block, and inside a div with absolute positioning and bottom: 0px; it will get drawn at the bottom of the containing block. The parent block needs to have its positioning set as relative, fixed or absolute.
  • A table cannot be used as a containing block for absolutely positioned elements.
  • An absolute positioned element get its width equal to the containing block by default. If you want to have a list of links, the white-space: nowrap; property can be useful.
  • Don't forget that by default the body has not a position: relative and is thus not the default containing block. This means that a child element of the body with position: absolute and height: 100% will only get its height equal to the viewport height, not the actual page height.

Fixed Positioning

  • Centering a div with a fixed position with the usual margin: 0 auto; technique will *not* work. Currently I use JS to center such an element.
  • UPDATE: It can be done without any JS. There is a standard technique to center absolutely positioned or fixed positioned elements. See below the section on centering.

Floats

  • Floats don't work with absolutely or relatively positioned elements, only with those elements in the normal flow.
  • A div that is positioned to the right of a left floating block still takes the whole width that it is allowed to take. It is only its content that is positioned next to the floating block. This makes it impossible to use properties such as padding-left, they won't apply as expected. You can use a span instead of a div, or position the block as relative and use left: 25px instead of padding: 25px.
  • This also means (and this is extremely important), that normal elements that don't float, when they are placed next to a float, won't have a correct width (if the width is set). The fix is to make those elements themselves float.
  • An element positioned next to a float still seems to have its top-left coordinate equal to the top-left coordinate of the floating element (this seems strange)! Note that this can cause problems with absolute positioned elements inside such a container (I am speaking of a container next to a float).
  • An inline element next to a float still "breaks" the flow. For example if you have a left floating div followed by an image, then a div, the last div won't float. It will be placed below the first div.
  • The clear property applies even when the element that has this property is inside a block. You can fix that behavior by floating the container block.

Inline elements

  • You cannot force an inline element to have a given width. One workaround is to float this element: floated elements are no longer inline, they are considered as blocks.
  • Images are by default inline elements and as such, a block containing an image will not have its height equal to the image height (this is expected behavior). You can change this by setting display: block; on the image, or vertical-align: bottom.

Margins

  • Vertical margins are not posible on an inline element.

Line-height

  • Line-height property can be very useful for specifying a height on inline elements. In particular, to create a navigation menus with links, this technique can be used.
  • Note that the height of the inline elements don't change via line-height. It is only the height of the block level element containing the inline elements. For this reason inline-block is still very useful.

Links (anchors)

  • To make an entire div (block) clickable via a link to navigate to another page, you can use the following CSS on the <a> element:
display: block;
width: 100%;
height: 100%;
margin: -5px;
padding: 5px;

The negative margins and paddings should be equal to the padding (and borders if needed) of the container div. This allows the padded areas to be clickable too which is usually want you want. This allows to make clickable buttons made of text (and not images), so this is very useful.

  • Unfortunately, the above technique does not work so well with table rows. The problem is that the table cells may not have the same height and thus the height: 100% does not really fill the actual row, but only a given cell. In this case the problem may be fixed via JavaScript if absolutely needed.

Fonts and texts

  • If you specify font-size in "em" units, this refers to the font-size of the parent element. For any other property, specifying "em" units refers to the font-size of the current element.
  • Note that you cannot override a text-decoration setting (you can only add a new effect to an existing one). This means that this setting should not be used on containers, since all their children will get the same setting applied.

Dimensions

Widths and Heights

  • If you have an empty div, setting a width won't make it appear on the page. You will also need to set the height.
  • You can also explicitly set the height of the parent block.
  • By default, the width of a block level element is 100% of the available width (which is given by the containing block).
  • Using the display: table property will cause a div width to be shrink wrapped to its content. This can be extremely useful, for example when centering horizontally a div of unknown width. Unfortunately this is not supported on IE 7. Note that this property seems to be incompatible with floating on Opera.
  • Setting a height: 100%; property, without the parent having a fixed height, can be dangerous on Opera / Safari. It seems than bottom margins of elements won't be applied correctly (problems with collapsing margins maybe).

Stacking (z-index)

  • z-index only applies to elements with a position of relative or absolute. Beware of this.
  • The stacking always applies with respect to a parent element establishing the stacking context (this element itself has by convention a z-index of 0 in the new stacking context it creates).

Empty Elements

  • Setting a height on an empty div will prevent Firefox from not inserting it into the DOM. This can be very useful.

Scrollbars

  • Scrollbars can be added via overflow-y and overflow-x. The best is to set this property to auto; the scrollbar will be added only when needed.

Containing Floats

  • Note that the height of a parent block level element is the sum of the height of its children, however floated elements are NOT taken into account. To force them to be taken into account, use a clearer div after the float element. This is a div that has clearance applied to it. Note that there may be other techniques to achieve the same result. See this link. Absolutely positioned elements are also taken out of the normal flow and thus don't contribute to the parent block height.
  • Clearer divs can be buggy in IE 7. If you use this code as a clearer div:
div.Clearer {
	clear: both;
	line-height: 0px;
	height: 0px;	
	overflow: hidden;
}

you can have problems in IE 7. Sometimes just using clear: both; will solve the problem.

  • The best is to not use clearer divs at all. Using overflow: hidden; will work in IE 7 and all standard browsers. Another technique is to use the pseudo :after CSS class to insert what is in fact a clearer div. However, IE 7 does not recognize this class.

Margins and Paddings

  • The difference between margins and paddings is that margins don't get the element's background color or image. Margins are just transparent. Padding on the other hand get the background color/image.
  • Margins don't work on table cells; use paddings.
  • Margins can also collapse. See this W3C reference. The idea is that adjoining elements margins can join together. This can happen especially happen with empty elements and cause troubles. I am not sure of the best way to prevent margins from collapsing. One way seems to set overflow: hidden, another is probably to apply a clearer div (a div with clearance).

Adjoining margins

  • The CSS box model specifies that when using height: auto, the height of a box is from the outer top border edge to the outer bottom border edge. This is surprising at it does not start from the outer margin edge. To change that you only need to add a top border or a top padding. See this link for a full explanation.
  • When encoutering this problem, this usually has the effect that the margin seems to escapes out of a child and goes out to the parent.

Alignment

Horizontal Centering

  • text-align: center; will only work with inline elements. For block level elements you must specify a width on them and use the margin properties (margin: auto).
  • For absolute or fixed positioning, the technique is different. You must use left: 50% and a negative margin equal to half the container's (known) width.
  • To center an element of unknown width, use display: table and margin: auto on the element. This won't work on old IE versions.
  • Centering an absolute element of unknown width is possible. Use a code like this (the white-space: nowrap is very useful to get the absolute element to the proper dimension, and not the dimension of the parent element to which it is place):
<style type="text/css">
div#absoluteElement {
	position: absolute;
	top: 50px;
	white-space: nowrap;
	left: 50%;
}

#inner {
	position: relative;
	left: -50%;
	background-color: blue;
}
</style>
<div id="absoluteElement" >
	<div id="inner">
		Hello Gentoo, you are a great OS.
	</div>
</div>
  • If you don't have a width for elements you want to center, things can quickly become very hard. For example, centering a span of unknown width and put next to it (left and right) two other spans is impossible (or at least I have not found a way). I was forced to specify a width on the three elements to get it to work.
  • Update: without knowing all widths, CSS centering is extremely limited. For example you cannot center an element of known width in a 2-column layout where the columns have no fixed widths.

Horizontal Right Alignment

  • There are basically two methods to align to the right for example. The first is to use an absolutely positioned element with a right: 0px CSS property. The second is to float the element to the right. WARNING: There seems to be a bug with Firefox. If you float to the right a div that has a width, inside an absolutely positioned element, this element won't count towards the computing of the parent absolutely positioned element. Thus the parent div will likely take all available space. To workaround this, give a width directly on the parent element.

Vertical Centering

  • See here for a good article.
  • Basically the following code will work. It does require you to know the height of the block you want to center (another technique allows you to center without assigning a height to the element, but is more complex):
<div style="position: relative; height: 200px;">
    <div style="position: absolute; top: 50%; margin-top: -50px; height: 100px;">
	Hello World!
    </div>
</div>
  • For an inline element, vertical-align should work well (coupled with a line-height if need be). However for input elements, the element is not perfectly centered (see this link). It can be fixed with a position: relative and top: -1px. I don't know why it is not correctly centered though.

Specific Elements

Body

  • When applying a background on the body, and if the html element contains a transparent background (eg, no background), then the background will be applied to the whole page (it will span the viewport), even if the body itself is shorter. There is no need to add a height: 100% CSS property.
  • The standard specifies this applies to HTML documents, not XHTML ones (but an XHTML document served as HTML counts as an HTML document apparently, at least on Firefox).

Forms

  • To get all form elements correctly aligned, the best is to set the width of the labels to an equal value (and to set them as floats, so this width actually applies).
  • Unfortunately, a div element floating next to input fields can mask access to these fields (making impossible to click on the field to select it). One work around is to use a span or restrict the width of the div.
  • Don't use the HTML size properties in form elements. For example, don't use the rows or col attributes in text areas, or the size attribute for text inputs. Always use CSS to dimension your elements, as the HTML attributes render differently on different browsers.
  • It is possible to restyle the default looks for fieldsets and input text fields. This has the advantage of making them consistent across browsers. This is an example for styling text inputs:
form#loginForm input[type="text"], form#loginForm input[type="password"] {
	border-color: #aaaaaa #c8c8c8 #c8c8c8 #aaaaaa;
	border-style: solid;
	border-width: 1px;	
	padding: 2px;
	font-size: 11px;
	width: 170px;
}

Tables

  • Margins don't seem to work at all with table elements (it would work with the table itself, though). Paddings seem to work only with elements, not .
  • If you want to specify spacings between elements *borders* in a table, use the specific 'border-spacing' property. Note that this property should be applied to the table element, not the <td>. Also not that if you use border-collapse: collapse;, this will nullify the effects of this property.
  • Note that the border property, specifying the border of the cell, should be on the contrary applied to each cell, like this:
table.WithBorders td {border: 1px solid black; padding: 3px;}

If you write this, borders will have in fact most of the time 2 pixels, because the borders will not be merged between two cells. If you want them to merge (and thus always have only 1 pixel), use border-collapse: collapse; in your table element.

  • Paddings on table cells have their expected effect.
  • You can specify certain properties to be applied to an entire column in a table (for example the 'border' property). However not all properties can be such applied, apparently. For example, I don't know how to apply the text-align property to an entire column (or if it is even possible). Usually it should just be applied to all the tds with a certain CSS class name.
  • To set the width of a column in CSS, the best is to use the <th> element, like this:
th.QuantityHeader {
	width: 75px;
	text-align: center;
}

Note that the text-align will only work for the cell, not the column; but the width will apply to the whole column.

  • Having different borders (colors, for example) for the outer edges of the table and for the inner cells is not possible easily. You have to write complex code for each td individually.
  • Tables are the best solution when you have a list of elements (labels and fields, for instance) that you want to align horizontally. The other solution is to fix a width on the label elements, but this is not portable (if the labels change).

Non standard properties

  • Opacity is not standard in CSS 2.1, but is supported by all browsers. I think it's better to obtain this effect via JavaScript. Prototype has a setOpacity() method which works accross all browsers.
  • Without JavaScript, one can use a black 1x1 image with transparency (a PNG file generated by GIMP works perfectly).

Layouts

Two column layout

  • One technique is to just float left the inside divs (columns) and use text-align: right on the one who needs to go to the right.

Two column layout with an unknown container width, a given width for the right block, and a fluid width for the left one

  • A good technique is to float right the right block, and use a margin equal to the width of the right block in the left one. Do not float the left block! If you float it, the browser will allocate it width based on content, so if the content is wide enough, it will go below the right block.
  • Note that the margin is necessary if you have blocks with border inside your left block - else the borders will take the whole width and go "behind" the right block.

Two column layout with equal heights, and a fluid width for the second one

  • For the left column to get the height of the right one, create an absolutely positioned element beneath the column and apply a 100% height to it. You will need to create a general container with position: relative, that contains both columns. The absolutely positioned div just needs to be a child of the general container.
  • The technique is described here. Warning: in this example, they don't apply a height of 100%, but instead use top: 0px; AND bottom: 0px; which has the same effect.
  • Don't forget to apply a clearer div at the bottom of the right column, so that the height of this right column will be always at least equal to the left one.
  • Finally, to get the fluid layout working, a margin-left has to be applied to the right column. Its value should be exactly equal to the width of the first (left) column.

Creating a box with custom borders and decorations, such as rounded corners

  • There are a lot of ways to create rounded borders. Some use JavaScript, some use pure CSS (no images), some support transparencies, others not... Below is a list of interesting links (that support transparency and don't use JS):
  • Here are some of my methods. They are adapted from common methods.
    • Create a content div and 8 divs inside. Set the background-position accordingly for each of the divs. The last thing is to add a last div, the content div, and set the padding in that div equal to the widths/heights of the outer divs. This method does not support transparency, and uses overwriting, so this means you must be careful in the order of the 8 divs. This is not a recommended way.
    • Create 3 divs with position: relative (so that they start a parent block), each containing 3 divs. The left and right divs (for every one of the 3 rows) must be positioned absolutely, respectively to the left and right. The central div must have correct margins applied to it. The HTML code looks like:
    <div class="TopRow">
        <div class="TopLeftBorder"></div>
	<div class="TopBorder"></div>
	<div class="TopRightBorder"></div>
    </div>
		
    <div class="MiddleRow">
	<div class="LeftBorder"></div>
	<div class="RightBorder"></div>	
	<div class="InnerContent">
            Here some content.						
	</div>
    </div>
		
    <div class="BottomRow">
	<div class="BottomLeftBorder"></div>
	<div class="BottomBorder"></div>
	<div class="BottomRightBorder"></div>
    </div>

This is probably the best technique available. It forces you to have the three additional rows, which is bad, but it's the most versatile as it will adapt to varying heights and widths.

  • The newest technique I have found is just to have inside the main div, 9 other divs. The 8 outer ones will be absolutely positioned with negative offsets corresponding to their width / height. This method is slightly better than the previous one as it does not need the three additional row divs, and allows you to place the outer divs where you want. It is recommended to place them before the central div, as it will allow to use some Prototype DOM walking functions without problems. Note that the inconvenient of this method when compared to the previous one is that it forces you to calculate the width of the top and bottom divs, and the height of the left and right divs. This method is recommended when you can draw outside the decorated block, as it is intended to overflow.

Hovering menus (links with images)

  • This can be a little bit tricky. In order to not have any "jumps" (IE has some strange jump bugs, other browsers have jumps because they have to do another HTTP request the first time when hovering), it is best to get all the images in a sprite image. Performance wise it is better too. The CSS code will look like this:
.HoverContainer a[href="/signup"] {
	height: 35px;
	vertical-align: bottom;
	display: inline-block;
}

.HoverContainer a[href="/signup"]:hover.en {
	background-position: -102px 0px;
}

.HoverContainer a[href="/signup"].en {
	width: 102px;
	background-image: url("http://images.kameleoon-dev.net/common/site/en/signup.png");
}

So we basically use display: inline-block on a simple <a> element.

  • The only minor problem with this technique is that elements will have some margins by default on standard browser, but not on IE where they are next to each other.

Footers

  • To have a footer at the bottom of the page, even if the body does not take 100% of the viewport height, follow this technique. This seems to work only with a fixed height footer.
  • The other thing I would like to be able to handle with footers is growing footers, stretching until they go to the bottom of the page in case the body takes less than 100% height of the viewport. So far I don't know a technique for that.

Serving different CSS for different browsers

  • The best technique is to sniff the browser version on the server side, and then serve CSS specific files for each browser. Note that generally, depending on the level of browser support you want to achieve, you may need only two browser specific files (one for IE and one for all the standard browsers).
  • Currently, if you want to build a complex site that works on IE 7 and other browsers, it is very hard to avoid this technique.
  • The sniffing script can be very complex or very simple. For simple IE detection on Java, one line is enough:
request.getHeader('User-Agent').toLowerCase().indexOf('msie') == -1