Taming Lists


リストを使いこなす

A List Apart: Articles: CSS Design: Taming Listsの日本語訳です。

1999年7月もの昔、私はメーリングリストでスタイルシートの長所について述べていた。決して変わらないことはあるものだ。

変わったのは、私のCSSに対する見方と、スタイルシートが適用される(X)HTMLの基本的な構造だ。たとえば、ウェブ上のほとんどのページはナビゲーションエリアにリンクのメニューがある。これらはよくリンクの羅列としてマークアップされ、またバラバラのDIVやパラグラフとしてマークアップされることも多い。しかしながら、構造上は、メニューはリンクのリストであり、そのようにマークアップされるべきだ。

もちろん、そのようにマークアップしない理由は、ナビゲーションエリアのどのリンクにもバレットがあるのが気に入らないからだ。私は前の記事で、CSSを使ってウェブページをレイアウトするいくつかのテクニックを概説した。その中には、リストを垂直ではなく水平に並べて表示するテクニックがあった。

この記事では、CSSを使って扱いにくいリストを制御する方法を実演する。あなたはこれから、リストをウェブページで好き勝手に振舞わせないで、振舞い方を指示することができるようになるだろう。

準備

この記事に私は順不同リストを使っている。順序つきリストにも同じCSSは適用できて、同じ結果は得られる。特に断りのない限りこの記事のどの事例にもリストには次のコードを使う。

<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>

それぞれのリストは別々のDIVの中に置いているだけである。CSSはリストの表示がDIVでで決まるように書かれている。それぞれのDIVには基本的なCSSルールがある。

#base {
	border: 1px solid #000;
	margin: 2em;
	width: 10em;
	padding: 5px;
	}

追加スタイルの適用がない限り、リストは元のDIVではこのように表示される:

  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5 we'll make a bit longer so that it will wrap

ポジショニング

リストのデフォルトインデントは多すぎることがある。だが、ULのmarginやpaddingを変えるだけではすべてのブラウザーではうまく動作しない。たとえばリストを左に詰めるようなことをするためには、marginとpaddingの両方を変える必要がある。なぜなら、Internet ExplorerとOperaは左marginでインデントを作るが、Mozilla/Netscapeはpaddingを使うからだ。これについて詳しく知るには、DevEdge?の記事、Consistent List Indentationを読んでほしい。

次の例では、DIV内にあるULのmargin-leftとpadding-leftの両方が0に設定されている。

  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5 we'll make a bit longer so that it will wrap

マーカーがDIVの外側にあることに注目してほしい。もし包含ボックスがHTML文書のBODYならば、マーカーはブラウザーのウィンドウの外に描画され、消えているかもしれない。マーカーをDIVの内側に並べたいのなら、左paddingかmarginを1emに設定しよう:

  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5 we'll make a bit longer so that it will wrap

マーカー

おそらく、カスタムのバレットが必要なプロジェクトに出くわしたことがあるだろう。もしそうなら、tableでマークアップしたことがあるかもしれない。topにvertical-alignされたGIFイメージのバレットを含む列と、LIとなるべきだったコンテンツを含む列の。CSSを使うと、バレットとしてイメージを使うことができる。もしブラウザーがこのCSSの記述部分をサポートしていない(もしくは画像を表示しない)場合、デフォルトのバレットが使われる(あるいはもし望むのならHTMLのバレットを指定することもできる)。

そのルールはこんなものだ:

ul {
	list-style-image: url(bullet.gif);
	}

ブラウザーのレンダリングは:

Item 1 Item 2 Item 3 Item 4 Item 5 we'll make a bit longer so that it will wrap

このCSSの記述部分をブラウザーがサポートしていない場合に、HTMLバレットを使うように指定するには、次の記述を追加する。

	list-style-type: disc;

バレットとして選択したイメージによっては、望んだようにリストアイテムが配置されないことに気づくかもしれない。その場合、画像をリストアイテムのブロックの(外側ではなく)内側に置くことをお選ぶかもしれない。次の記述

	list-style-position: inside;

をCSSルールに追加すればそのように変わる。これらの3つの宣言は1つの短い宣言にまとめることができる。それには次のようにまとめればよい。

ul {
	list-style: disc url(bullet.gif) inside;
	}

これは次と等価である:

ul {
	list-style-type: disc;
	list-style-image: url(bullet.gif);
	list-style-position: inside;
	}

これはウェブページで次のように表示される:

Item 1 Item 2 Item 3 Item 4 Item 5 we'll make a bit longer so that it will wrap

時には、リストがあって、バレットを表示させたくないときや、バレットの位置に文字を表示したくないときもあるかもしれない。この場合にも、CSSはストレートな解決方法を提供する。list-style:none;を追加して、LIにインデントなしで表示させればよい。そのCSSルールは次のようなものだ。

ul {
	list-style: none;
	margin-left: 0;
	padding-left: 1em;
	text-indent: -1em;
	}

paddingやmarginのどちらかは0に、もう一方は1emに設定する必要がある。バレットに応じて、値を変える必要があるかもしれない。text-indentの値を負に設定することで、1行目(の開始位置)がそれだけ左に移動し、hanging indentを生み出す。

問題のHTMLは冒頭で述べた標準のULを含んでいるが、バレットの位置に使いたい文字や文字参照がリストアイテムのコンテンツの前にある。今回は»を使う。&raquo;だ。

» Item 1 » Item 2 » Item 3 » Item 4 » Item 5 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

真のインラインリスト

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 And then there is the text that follows the list in the paragraph. One or two sentences is sufficient for the example.

ナビゲーション

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.


ボーダー

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.

Note: This technique was first proffered by Randal Rust, and then riffed on by many on the css-discuss email list

パンくず

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>&#187; Products
		<ul>
			<li>&#187; Computers
			<ul>
				<li>&#187; Software</li>
			</ul></li>
		</ul></li>
 	</ul></li>
</ul>
</div>

creates the following:

Home » Products » Computers » Software 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

現実世界

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.

Home Hidden Cameras CCTV Cameras Employee Theft Helpful Hints F.A.Q About Us Contact Us

This menu of links is actually a solution to a question posed by Michael Efford on the css-discuss list. Michael had created this exact effect using a table, images, and JavaScript?. He asked the list if it could be done with CSS. I took the challenge, and with the help of several other members who tracked down some browser specific issues, 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. You can view the final result in the context of the site on the Asset Surveillance web site.

Tip of the Iceberg

Believe it or not, we have just scratched the surface of what can be done to modify lists with style sheets. I won’t claim that all of the techniques presented here are the most practical CSS that you'll come across, but I do hope they make you think about how using CSS can release you to use more structured markup.