年末だからというわけでもないのですが、いつものサイト作りに使う CSS リセットについて見直してみました。今までもちょっとずつ手を入れてはいたのですが、今回はかなり大きく修正しています。というのも、Nicolas Gallagher と Jonathan Neal の両氏による normalize.css を知り、大きく影響を受けたからです。
Normalize.css は「新手の CSS リセット」ではありません。CSS を「リセット」するのではなく「ノーマライズ」する、という新しい考え方です。CSS リセットとノーマライズはどちらも、ブラウザ間で CSS の実装に差異があることを前提にそれらを吸収しようとする、という同じ目的を持っています。ただ、リセットはすべてをまっさらな「さら地」にしようとするのに対し、ノーマライズは使える部分は残しつつ手を入れる必要のある部分だけを整える、という違いがあります。「ブラウザ間の CSS 実装に差異がある」とは言っても、それぞれのブラウザが勝手にてんでバラバラの方向を向いているわけではないので、歩調のあってるとこまでわざわざ殺さず、活かせるものはそのまま活かして、という発想です。
Normalize.css によって具体的にどんなスタイルになるかは デモ をご覧いただくのが手っ取り早いと思います。シンプルなサイトなら、ここにちょっとしたレイアウトのスタイルを追加すればそのまま完成するような、汎用性の高いスタイルです。
とは言え、長いことリセットという手法が一般的であったため、たとえばブロックレベル要素のマージンやリストのマーカーがデフォルトのままだったりすると扱いづらいという場合も多いんじゃないでしょうか。一から自分でコントロールできる新規プロジェクトならいいですが、チームでメンテナンスしていたり、運用・管理を他の人に移譲するものの場合、ある程度はコモンセンスというか浸透している手法でないと採用が難しい、というところもあると思います。とくにリセットもノーマライズもスタイルの基盤を作るもので、その後の CSS 設計のすべてに大きく影響するものなのでなおさらです。
というわけで、ノーマライズの考え方を取り入れつつも影響の大きそうな部分はリセットしておくという、新しいリセットのアプローチを模索しています。以下はそうやって出来た「俺の CSS リセット: 2011 冬」です。Normalize.css については GitHub の Wiki に詳しい解説があるのでそちらも参照してみてください。そこには先人たちの様々な CSS テクニックが盛り込まれており、そのまま採用しないにしても読むだけで得られるものは少なくないと思います。
ソースコード
/*!
* CSS Reset 2011-12-25
* https://gist.github.com/1360380
*
* Author: Takeru Suzuki, https://terkel.jp/
* License: Public domain
*
* Inspired by Normalize.css: http://necolas.github.com/normalize.css/
*/
/* HTML5 display definitions */
section, nav, article, aside, hgroup,
header, footer, figure, figcaption, details {
display: block;
}
video, audio, canvas {
display: inline-block;
*display: inline;
*zoom: 1;
}
audio:not([controls]) {
display: none;
}
[hidden] {
display: none;
}
/* The root element */
html {
font-size: 100%;
overflow-y: scroll;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
/* Sections */
body {
font-family: sans-serif;
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
font-size: 1em;
margin: 0;
}
/* Grouping content */
p, blockquote, dl, dd, figure {
margin: 0;
}
hr {
color: inherit;
height: auto;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
pre {
font-family: monospace, sans-serif;
white-space: pre-wrap;
word-wrap: break-word;
margin: 0;
}
ol, ul {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
/* Text-level semantics */
a:focus {
outline: thin dotted;
}
a:hover, a:active {
outline: 0;
}
strong, b {
font-weight: bold;
}
small {
font-size: 0.83em;
}
q {
quotes: none;
}
abbr[title] {
border-bottom: 1px dotted;
}
code, samp, kbd {
font-family: monospace, sans-serif;
}
mark {
color: black;
background-color: yellow;
}
sub, sup {
font-size: 0.83em;
line-height: 0;
vertical-align: baseline;
position: relative;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
br {
letter-spacing: 0;
}
/* Embedded content */
img {
border: 0;
-ms-interpolation-mode: bicubic;
}
svg:not(:root) {
overflow: hidden;
}
/* Tabular data */
table {
border-collapse: collapse;
border-spacing: 0;
}
caption {
padding: 0;
text-align: left;
}
th, td {
text-align: left;
vertical-align: baseline;
padding: 0;
}
/* Forms */
form {
margin: 0;
}
fieldset {
border: 0;
padding: 0;
margin: 0;
}
legend {
border: 0;
*margin-left: -7px;
}
input, button, select, textarea {
font-family: inherit;
font-size: 1em;
color: inherit;
margin: 0;
}
input, button {
line-height: normal;
vertical-align: inherit;
*vertical-align: middle;
}
input::-moz-focus-inner,
button::-moz-focus-inner {
border: 0;
padding: 0;
}
input[type="search"] {
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
input[type="search"]:focus {
outline-offset: -2px;
}
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box;
padding: 0;
}
input[type="submit"],
input[type="reset"],
input[type="button"],
button {
cursor: pointer;
-webkit-appearance: button;
*overflow: visible;
}
select {
background-color: inherit;
line-height: normal;
}
textarea {
vertical-align: top;
overflow: auto;
*font-family: sans-serif;
}
長い… 要素の分類と記述順は HTML5 の仕様書 を参考にしました。以下、それぞれのパートごとにできる限り解説してみます。
HTML5 display definitions
section, nav, article, aside, hgroup,
header, footer, figure, figcaption, details {
display: block;
}
video, audio, canvas {
display: inline-block;
*display: inline;
*zoom: 1;
}
audio:not([controls]) {
display: none;
}
[hidden] {
display: none;
}
まずは HTML5 の新仕様関連で、normalize.css 丸写しです。HTML5 以前の古いブラウザ向けですね。video
や audio
などマルチメディア系の要素は個人的に扱った経験がないため詳しくはわからないんですが、とりあえずは display
まわりを揃える程度なので、このままいってよさそうです。将来的に実装が進んだときはもうちょっと複雑な記述が必要になるのかもしれません。hidden
属性 は HTML5 で新しく定義されたグローバル属性です。属性セレクタによる指定のため IE6 では非表示になりませんが。
The root element
html {
font-size: 100%;
overflow-y: scroll;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
次はドキュメントのルートであるところの html
への指定で、ここも normalize.css まんまです。
font-size: 100%
は、フォントサイズを em 単位で指定した場合に「文字サイズの変更」をおこなうとサイズが極端に変動するという IE6/7 のバグへの対応です。これはいずれかの祖先要素のフォントサイズを % 単位で指定しておくと回避できるので、こうしておけば body
以下で自由に em が使えます。
overflow-y: scroll
は IE6/7 以外のブラウザ向けで、ドキュメントの高さがウィンドウより小さくても縦のスクロールバー領域を確保するというものです。
次の -webkit-text-size-adjust: 100%
と -ms-text-size-adjust: 100%
は iOS の文字サイズ自動アジャスト機能に対するもののようです。が、手元の iPhone で軽く検証したりググってみたりしたものの、正直よくわかっていません… とりあえず、この値を none
にしてしまうのはどうもよくないらしいとか、IE Mobile がなぜか -webkit-text-size-adjust
をサポートしてた (けど直った?) とか、iOS だけじゃなくてデスクトップ版 Safari にも影響があったとか、なんかややこしいらしいってことだけはわかりました。どなたかご教授いただけると嬉しいです! ここでは参考になりそうなリンクだけ並べてお茶を濁します:
- Safari Web Content Guide: Customizing Style Sheets
- MSDN - Windows Phone 用の Web 開発
- iPhone向けWebアプリを作ろう(4/4) - @IT
- Beware of -webkit-text-size-adjust:none | 456 Berea Street
- Controlling text size in Safari for iOS without disabling user zoom | 456 Berea Street
- WebKit Bugzilla - Bug 56543 - CSS property "-webkit-text-size-adjust" means different things in Safari and iOS
- -webkit-text-size-adjustがiOS以外でも効いてしまうらしい - vantguarde - web:g
- #28: Delete -ms-text-size-adjust - Issues - necolas/normalize.css - GitHub
Sections
body {
font-family: sans-serif;
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
font-size: 1em;
margin: 0;
}
body
の font-family
はとりあえずざっくりと指定しておいて、あとはプロジェクトごとに細かくやる (あるいはやらない) という感じ。margin
はリセット。ちなみに body
のマージンはデフォルトだと IE6/7 でやや大きいです。
hn
は normalize.css だと HTML5 のセクショニング要素の対応として h1 { font-size: 2em; }
しか指定してない (#41: Normalize headings - Issues - necolas/normalize.css - GitHub) ですが、ここでは h1
から h6
まで font-size
と margin
をリセットしてます。
Grouping content
p, blockquote, dl, dd, figure {
margin: 0;
}
hr {
color: inherit;
height: auto;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
pre {
font-family: monospace, sans-serif;
white-space: pre-wrap;
word-wrap: break-word;
margin: 0;
}
ol, ul {
padding: 0;
margin: 0;
}
li {
list-style: none;
}
段落やリストなど、実際にサイトでよく使うとこですね。このへん、normalize.css ではあまりいじってないんですが、ここではけっこうリセットしてます。
まず p
などブロックレベル要素の margin
はリセット。ul
と ol
は padding
も 0
に、li
のマーカーもなし。normalize.css では nav
要素の子孫である場合にだけマーカーなしですが、実際問題としていわゆる普通のサイトではマーカーとして独自の画像を用意することが多いと思うので。
hr
は Firefox のスタイルがちょっと例外的 なので他のブラウザに合わせる方向。IE6/7 以外は上下に 0.5em の margin
を持っていますが、IE6/7 では上下の余白がコントロールできないため、きっちり見た目を揃えるのが難しいです。なのであえてリセットせずそのまま残してます。
pre
はまず Safari と Chrome でフォントサイズが揃わない問題 への対応として font-family: monospace, sans-serif
を指定。あと行が長くなったときに折り返すようにしています。
Text-level semantics
a:focus {
outline: thin dotted;
}
a:hover, a:active {
outline: 0;
}
strong, b {
font-weight: bold;
}
small {
font-size: 0.83em;
}
q {
quotes: none;
}
abbr[title] {
border-bottom: 1px dotted;
}
code, samp, kbd {
font-family: monospace, sans-serif;
}
mark {
color: black;
background-color: yellow;
}
sub, sup {
font-size: 0.83em;
line-height: 0;
vertical-align: baseline;
position: relative;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
br {
*letter-spacing: 0;
}
いわゆるインライン要素。このあたりは基本的にデフォルトの font-weight
や font-style
をそのまま活かします。
a
の outline
プロパティは、Eric Meyer 氏のリセットの古い版 で「ちゃんとあとから自分で定義し直してね!」っていう注釈つきで :focus { outline: 0; }
となってた (のちに修正された) のがそのまま広まってしまった感があります。が、それではアクセシビリティ上の問題があるので、そのあたりを考慮したものになってます。以下、参考資料:
- CSS outline property - outline: none and outline: 0
- 24 ways: Don't Lose Your :focus
- Better CSS outline suppression
- フォーカス時の輪郭線(アウトライン)の役割をご存知ですか? - White Stage
strong
と b
は Firefox や WebKit のデフォルトでは bold
ではなく bolder
という相対値です。つまり、<h1><b>Steve Jobs</b>: 1955–2011</h1>
というマークアップで h1
の font-weight
が bold
(700
) の場合、その中の b
は 900
となるわけです (CSS Fonts Module Level 3 - 3.2 Font weight: the font-weight property)。このままでもいいんですが、予期したウェイトよりも太くなってしまうってことがけっこうありそうなので bold
に揃えよう、ってことだと思いますたぶん。ウェイトがノーマルかボールドしか使えない場合がほとんどの和文フォントではあまり気にする機会がありませんが。
sub
と sup
は行の高さと縦位置の調整です (CSS for and — Gist)。
br
は 親要素に letter-spacing
が指定されているときに連続して改行しても認識されない という IE6/7 のバグへの対応。
Embedded content
img {
border: 0;
-ms-interpolation-mode: bicubic;
}
svg:not(:root) {
overflow: hidden;
}
img
はリンク時のボーダーを消して、IE7 で拡大縮小したときに綺麗にする、っていういつものやつです。svg
は IE9 向けの指定みたいです。
Tabular data
table {
border-collapse: collapse;
border-spacing: 0;
}
caption {
padding: 0;
text-align: left;
}
th, td {
text-align: left;
vertical-align: baseline;
padding: 0;
}
テーブル関連はセル間の隙間をなくすのに加えて、キャプションとセルの文字方向とパディングをリセットしてます。
Forms
form {
margin: 0;
}
fieldset {
border: 0;
padding: 0;
margin: 0;
}
legend {
border: 0;
*margin-left: -7px;
}
input, button, select, textarea {
font-family: inherit;
font-size: 1em;
color: inherit;
margin: 0;
}
input, button {
line-height: normal;
vertical-align: inherit;
*vertical-align: middle;
}
input::-moz-focus-inner,
button::-moz-focus-inner {
border: 0;
padding: 0;
}
input[type="search"] {
-webkit-appearance: textfield;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
input[type="search"]:focus {
outline-offset: -2px;
}
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box;
padding: 0;
}
input[type="submit"],
input[type="reset"],
input[type="button"],
button {
cursor: pointer;
-webkit-appearance: button;
*overflow: visible;
}
select {
background-color: inherit;
line-height: normal;
}
textarea {
vertical-align: top;
overflow: auto;
*font-family: sans-serif;
}
そして最後はフォーム関連。やはりここがもっともややこしいですね。基本的に normalize.css まんまなので詳しくはそちらを参照していただきたいんですが、おもに以下のような指定を追加してます:
fieldset
の余白とボーダーはリセット。- Chrome は
select
にbackground-color
を指定しないとフォント系プロパティを継承しないので指定。
HTML5 の新しいコントロール型として search
型にけっこうな手間をかけてますが、これからほかの新しい型の実装も進んだとき、今までのようにすべてリセットしてクロスブラウザでピクセルパーフェクトなルックを目指すというのはいよいよ現実的ではないと思います。そういった意味で、フォームこそ CSS ノーマライズという考え方の真価が発揮されるところかもしれません。
メンテナンス
さて、この手のコードは、チームや個人で一度これと決めてしまうとあまり中身を見直されることなくそのまま使い回されるということになりがちです。その結果として、すでに不要になった古い (バッド) ノウハウが残り続け、よくわからないまま「おまじない」として次世代に受け継がれていく… という、あまり好ましくない事態が想定されます。とくに今後、要らないベンダー接頭辞が残り続けているという事態はちょくちょく目にすることになりそうです。ですから、ブラウザの実装の進歩やシェアの変動を踏まえつつ、折を見て内容を吟味して、更新し続けることが重要だと考えます。
というわけで、上記 CSS のソースコードは Gist に置いてます。気がついたときにちょくちょく手を入れると思うので、本稿での記述とは違っていることがあるかもしれません。あと現時点でのデモページも用意しました。HTML の中身は normalize.css のをそのまま拝借しています:
参考
最後に参考にしたリソースのリストを。まずはブラウザのデフォルトのスタイルシートを確認できるサイトです。IE のは公式じゃないし、Opera のは見つけられませんでした:
- mozilla-central mozilla/layout/style/html.css
- mozilla-central mozilla/layout/style/forms.css
- /trunk/Source/WebCore/css/html.css – WebKit
- Internet Explorer UA Style Sheets
- CSS2.1 User Agent Style Sheet Defaults
あと、メジャーな CSS リセットをいくつか:
- CSS Tools: Reset CSS: Eric Meyer’s CSS Reset
- HTML5 Reset Stylesheet | HTML5 Doctor
- CSS Reset - YUI Library
メリー・クリスマス!