CSS to bring unwieldy lists under control
In this article, I'll demonstrate how to use CSS to bring unwieldy lists
under control. It’s time for you to tell lists how to behave, instead
of letting them run wild on your web page.
Setting the stage
For purposes of this article, I am using unordered lists. The same
CSS can be applied, with similar results, to ordered lists as well.
Unless otherwise defined, all of the examples in this article use the
following code for the lists.
<ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> <li>Item 4</li> <li>Item 5 we'll make a bit longer so that it will wrap</li> </ul>
Each list is simply placed inside a different DIV, and the CSS is
written so that the list’s behavior is determined by the DIV it is in.
Each DIV has a base rule:
#base { border: 1px solid #000; margin: 2em; width: 10em; padding: 5px; }
Without any additional styles applied, the list is rendered in this way in the base DIV:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
Positioning
Sometimes the default indent of a list is too much for the design you are working on. But simply changing the margin or the padding of the UL doesn’t work for all browsers. To make the list flush left, e.g., you need to change both
the margin and the padding. This is because Internet Explorer and Opera
opted to create the indent with the left margin, while Mozilla/Netscape
use padding.
In the following example both the
margin-left
and padding-left
of the UL in the DIV are set to zero:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
Note that the markers lie outside of the DIV. If the containing box were
the BODY of the HTML document, the markers might be rendered outside of
the browser window, in effect vanishing. If you want the markers to
line up inside the DIV, but along its left side, set either the left
padding or margin to one em:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
Markers
Maybe you've had a project that required a custom bullet. If so you
might have marked it up in a table with one column containing the GIF
image bullets aligned top and right, and the other column containing the
content of what should have been LIs. With CSS it is possible to use an
image as a bullet. If the browser doesn’t support this part of CSS (or
doesn’t do images), the default bullet will be used (or you can specify a
different HTML bullet if you wish).
The rule looks something like this:
ul { list-style-image: url(bullet.gif); }
And the browser renders it:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
To specify an HTML bullet to use if the browser doesn’t support this part of CSS, add:
list-style-type: disc;
to your rule. Depending on the image that you choose you may find that
it doesn’t align itself with the list items the way that you intend. In
that case you may choose to have the image be placed within the list
item block (rather than outside the block). Adding the following:
list-style-position: inside;
to your rule will make that change. These three declarations can be
combined into a single, shorthand declaration as illustrated in the
following rule:
ul { list-style: disc url(bullet.gif) inside; }
which is the equivalent of:
ul { list-style-type: disc; list-style-image: url(bullet.gif); list-style-position: inside; }
This is what it looks like in the web page:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
There may be times when you have a list, but you don’t want any bullets,
or you want to use some other character in place of the bullet. Again,
CSS provides a straightforward solution. Simply add
list-style: none;
to your rule and force the LIs to display with hanging indents. The rule will look something like this:
ul { list-style: none; margin-left: 0; padding-left: 1em; text-indent: -1em; }
Either the padding or the margin needs to be set to zero, with the other
one set to 1em. Depending on the “bullet” that you choose, you may need
to modify this value. The negative
text-indent
causes the first line to be moved to the left by that amount, creating a hanging indent.
The HTML will contain our standard UL, but with whatever character or
HTML entity that you want to use in place of the bullet preceding the
content of the list item. In our case we'll be using
»
, the right double angle quote: ».
» Item 1
» Item 2
» Item 3
» Item 4
» Item 5
.tips ul {
list-style: none;
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
}
.tips li, .tips li {
list-style-image: none;
list-style-type: none;
line-height:25px;
}
.tips ul {
list-style: none;
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
}
.tips ul li:before {
content: "\00BB \0020";
}
list-style: none;
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
}
.tips li, .tips li {
list-style-image: none;
list-style-type: none;
line-height:25px;
}
.tips ul {
list-style: none;
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
}
.tips ul li:before {
content: "\00BB \0020";
}
we'll make a bit longer so that it will wrap
I should point out that Netscape6/7/Mozilla (and other Gecko-based browsers) and Opera can create generated content via the CSS2
:before
pseudo-element. We can take advantage of this to automatically generate
the » character (or any other character) for the bullets. This allows
us to leave the content of the UL alone. If you are using Opera or a
Gecko-based browser, the following CSS will create the same list as
above, but using the standard UL with no extra content:
#custom-gen ul li:before { content: "\00BB \0020"; }
The
content
property may contain strings, URIs and more, including special characters. When using these characters, like », it is necessary to encode them as their escaped HEX equivalents.
For the right double angle quote, we use \00BB (the other character,
\0020, is a space). The final result (remember, the character will only
be visible in Opera or Mozilla/Netscape): - Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
Truly inline lists
Who says a list needs to be vertically aligned with bullets hanging off
the left side of each item? Perhaps you want the structure of an ordered
list of links, but visually you want it to look like a vertical
navigation bar on the web page. Or maybe you want your list of links to
align themselves horizontally across the top of your page.
This applies to more than just lists of links. There are times when you
might need to list several items in the midst of a paragraph, maybe a
list of books that you want to read. Structurally it makes sense to mark
this up as a list, but presentationally you might not want break the
flow of the paragraph. CSS to the rescue again!
Since this list will not be separate and unto itself, I won’t put it
into the base DIV that the previous lists have inhabited. This time the
markup will be a paragraph, followed by the same list, followed by
another paragraph.
I hear you crying,“FOUL! I thought you were going to put a list inside of a paragraph, not between two paragraphs.”
To which I reply,“Well, yes. But (X)HTML does not allow a list to appear
inside of a paragraph. However, with the help of our style sheet, that
is how it will look in the web page.”
Here’s what the styles look like:
#inline-list { border: 1px solid #000; margin: 2em; width: 80%; padding: 5px; font-family: Verdana, sans-serif; } #inline-list p { display: inline; } #inline-list ul, #inline-list li { display: inline; margin: 0; padding: 0; color: #339; font-weight: bold; }
The markup consists of a
<div id="inline-list">
. This
DIV contains a paragraph followed by our standard UL, and a followup
paragraph. The UL list has been modified so that each list item has a
comma after it, with the last item followed by a period.
The results are below (list appears bold and blue):
A bit of text before the list appears. Perhaps the context is
something about a husband getting a call from his wife to pick up a few
things on the way home from work. It doesn’t really matter, for our
purposes we just need some preceding text before the list:
- Item 1,
- Item 2,
- Item 3,
- Item 4,
- Item 5
we'll make a bit longer so that it will wrap.
And then there is the text that follows the list in the paragraph. One or two sentences is sufficient for the example.
As in the custom bullet example above, we could use CSS to automatically
generate the commas and period that follow each list item. If all you
had to worry about were Opera and Gecko powered browsers, that is. This
time the styles would look like:
#inline-list-gen ul li:after { content: ", "; } #inline-list-gen ul li.last:after { content: ". "; }
Here we use the
:after
pseudo-element to add a comma after each list item, and a period after a list item with class="last"
, resulting in the following (remember, it will only be visible in Opera or Mozilla/Netscape):
A bit of text before the list appears. Perhaps the context is
something about a husband getting a call from his wife to pick up a few
things on the way home from work. It doesn’t really matter, for our
purposes we just need some preceding text before the list:
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5 we'll make a bit longer so that it will wrap
Navigation
As I mentioned previously, the menus of links that appear on nearly
every site should really be marked up as lists, since that is what they
are. Since we usually don’t want the default list style to apply to
these links, we can use CSS to change the way they appear on the page.
As we saw above, lists can be forced to display horizontally (inline)
rather than stacked vertically (the default behavior). When you do this
the bullet goes away and you have many choices about how to separate the
list items.
These examples of horizontal lists will all use the same base DIV with the following styles:
#h-contain { padding: 5px; border: 1px solid #000; margin-bottom: 25px; }
The next two examples use the same UL as in the previous examples, but
without the final list item with its extra text. They also include an
additional class that sets apart one of the LIs in the list.
Borders
A pipe character, |, is often used to differentiate between choices. It
is an obvious separating character, and can be emulated by adding
borders to list items:
#pipe ul { margin-left: 0; padding-left: 0; display: inline; } #pipe ul li { margin-left: 0; padding: 3px 15px; border-left: 1px solid #000; list-style: none; display: inline; } #pipe ul li.first { margin-left: 0; border-left: none; list-style: none; display: inline; }
Here we add
class="first"
to the first LI so that it does not end up with a border on its left side.
- Item 1 | Item 2 | Item 3 |Item 4
You can modify these styles to create a tabbed navigation effect:
#tabs ul { margin-left: 0; padding-left: 0; display: inline; } #tabs ul li { margin-left: 0; margin-bottom: 0; padding: 2px 15px 5px; border: 1px solid #000; list-style: none; display: inline; } #tabs ul li.here { border-bottom: 1px solid #ffc; list-style: none; display: inline; }
- Item 1 | Item 2 | Item 3 |Item 4
In this example adding
class="here"
to an LI creates a bottom border that matches the background color to indicate that the tab refers to the current page.
Breadcrumb trails
Another list of links that typically has a horizontal orientation on a
web page is what has become known as breadcrumbing. Breadcrumbs show you
where you are in the hierarchy of a site, starting with the home page
and drilling down to the current section or page. If you really want to
make the markup meaningful, you would want to create a series of nested
lists, since each new section is part of the section before it:
<div id="bread"> <ul> <li class="first">Home <ul> <li>» Products <ul> <li>» Computers <ul> <li>» Software</li> </ul></li> </ul></li> </ul></li> </ul> </div>
creates the following:
- Home
- » Products
- » Computers
- » Software
- » Computers
- » Products
Adding the following rules to the style sheet for the page:
#bread { color: #ccc; background-color: #006; padding: 3px; margin-bottom: 25px; } #bread ul { margin-left: 0; padding-left: 0; display: inline; border: none; } #bread ul li { margin-left: 0; padding-left: 2px; border: none; list-style: none; display: inline; }
creates this:
- Home» Products» Computers» Software
Again, we can generate the » character (or any other character you might want to use as a separator) with the
:before
pseudo-element, combined with a class="first" so that the first LI doesn’t generate a separator:
#bread-gen ul li:before { content: "\0020 \0020 \0020 \00BB \0020"; color: #ff9; } #bread-gen ul li.first:before { content: " "; }
And the end result:
- Home» Products» Computers» Software
In the Real World
I'd like to end with a real world application of some of the techniques
that have been discussed here. We'll use a standard UL containing links
to create a dynamic menu with hover effects. In order to obtain the
hover effects we'll let the UL provide the structure, and the anchor
styles will provide most of the visual effects.
we came up with a style sheet that works on this markup:
<div id="button"> <ul> <li><a href="#">Home</a></li> <li><a href="#">Hidden Cameras</a></li> <li><a href="#">CCTV Cameras</a></li> <li><a href="#">Employee Theft</a></li> <li><a href="#">Helpful Hints</a></li> <li><a href="#">F.A.Q</a></li> <li><a href="#">About Us</a></li> <li><a href="#">Contact Us</a></li> </ul> </div>
Let’s look at the style sheet rule by rule, and I'll explain why each rule is constructed the way that it is.
#button { width: 12em; border-right: 1px solid #000; padding: 0 0 1em 0; margin-bottom: 1em; font-family: 'Trebuchet MS', 'Lucida Grande', Verdana, Lucida, Geneva, Helvetica, Arial, sans-serif; background-color: #90bade; color: #333; }
The first rule is for the
#button
DIV. It defines the space
that the menu will occupy, and provides a context for the menu so that
we can define the way the list and links will behave inside the DIV. I
chose to make the menu fluid, based on the browser’s font size
preferences, so (almost) all units are in ems. This includes the width
of the menu. The solid black border on the right was based on the
original design from Michael. The bottom padding is there to extend the
DIV down beyond the menu of links so that you can see the background of
the DIV. Again, this follows the original design. The bottom margin is
to separate the DIV from what follows it. The colors came from the
original design.
#button ul { list-style: none; margin: 0; padding: 0; border: none; } #button li { border-bottom: 1px solid #90bade; margin: 0; }
Next I defined what the list will look like. Since all the list items
were to be links, and the rollover functionality would be built into the
CSS for the links, I essentially removed all styling from the lists. I
did add a single pixel border on the bottom of each link that matched
the background of the
#button
DIV, to work as the separator. In the original design, this was an image.
#button li a { display: block; padding: 5px 5px 5px 0.5em; border-left: 10px solid #1958b7; border-right: 10px solid #508fc4; background-color: #2175bc; color: #fff; text-decoration: none; width: 100%; } html>body #button li a { width: auto; } #button li a:hover { border-left: 10px solid #1c64d1; border-right: 10px solid #5ba3e0; background-color: #2586d7; color: #fff; }
Finally, I defined the links. The original design has 10 pixel borders
on the right and left. These borders, along with the background, change
color on the rollover. This is a relatively simple thing to control with
the
:hover
pseudo-class in CSS, so I put this style into the anchor styles.
There is one workaround in this part of the style sheet. To make the links active for the full width of the DIV, I made them
display: block;
.
This works for everything but IE/Windows. If you give the block an
explicit width of 100%, then IE/Windows plays along. But doing this
creates problems with IE5/Mac and Netscape/Mozilla. So I used the child
selector“>” to redefine the width to auto. Since IE/Windows doesn’t
understand child selectors, it ignores the rule. IE5/Mac, Opera and
Netscape/Mozilla follow the rule, and everyone is happy.
The rule for the
:hover
pseudo-class creates the color changes on the backgrounds and borders when the links are moused over.
The style and list markup (about 1K worth) replaced about 5K of
JavaScript and TABLE markup, not to mention another 8K or so of images
for the rollover effects. It also made the markup more readable, and
easier to update, since you no longer need to create new images if a
link name changes. Now you simply modify some text.
No comments: