POSTD PRODUCED BY NIJIBOX
POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

2015年8月13日

あまり知られていないCSSの12の事実(続編)

Louis Lazaris

本記事は、原著者の許諾のもとに翻訳・掲載しております。

1年以上前に、私は最初の 12 Little-known CSS Facts(あまり知られていないCSSの12の事実) を発表しました。SitePointで最も人気の高い記事となりました。この記事を書いた後も、私はCSSのアドバイスやちょっとした情報の収集を続けました。だって、大ヒット映画も必ず続編を制作するじゃないですか。

12 Little-known CSS Facts: The Sequel
注釈
SitePoint/ Natalia Balska によるイラスト

それでは、早速今年も開発のヒントになる12の事実について話しましょう。もちろん、中にはもうすでにご存じのこともあると思いますが、この中で初めて知ったという事実がありましたら、コメントでお知らせください。

1. border-radius プロパティに”スラッシュ”シンタックスを使用できる事実

このプロパティについてはSitePointに4年以上 前に書いた のですが、この機能が存在することを、未だに多くの初心者や経験の豊富な開発者でさえ知らない人がいます。

まさかと思うでしょうが、次の border-radius コードは有効です。

.box {
  border-radius: 35px 25px 30px 20px / 35px 25px 15px 30px;
}

初めて見る方には分かりにくいと思いますので、仕様書の説明を確認してみましょう。

スラッシュで区切られて値が与えられた場合、スラッシュの前の値は縦半径を設定し、スラッシュの後の値は横半径を設定します。スラッシュがない場合は、両者の半径を等しく設定する。

次の画像も仕様書に掲載されています。

Multiple Radii on each corner with border-radius
画像の説明には、” border-top-left-radius: 55pt 25pt の2つの値は隅の曲率を定義する”と記載されています。

このようにスラッシュで値を区切ることで、非対称的に隅を丸めることができます。さらに詳しく知りたい場合は、先ほど紹介した記事を読むか、できたら、 handy little interactive demo from MDN(役に立つMDNによるちょっとしたインタラクティブデモ) をお読みください。

ほとんどの border-radius ジェネレータでは、このような値の設定はできません。私の知る限り、MDNの border-radius ジェネレータでのみ設定が可能です。

2. 相対指定でも font-weight プロパティに有効という事実

通常、 font-weight プロパティの定義では、値は normalbold のどちらかです。時には100 を一段階とする変数で指定されているのを目にすることがあると思います。 100200 など、最高で 900 までが指定できます。

しかし、忘れられがちな値が2つあります。 bolderlighter です。

仕様書によると、この2つは継承値よりboldなウェイトあるいはlightなウェイトを指定します。ただの”bold”よりboldだったり、普通のテキストよりlightだったりする複数のウェイトを持つフォントを使用した時に最も効果を発揮します。

100を一段階とする値で、”bold”は 700 をマッピングし、”normal”は 400 をマッピングします。もし、ウェイトが 300 のフォントだった場合、継承値が 400 であれば、”lighter”の値は 300 を作ります。よりlightなウェイトがない(例えば、 400 が最もlightなウェイトだった)場合、値は 400 のままで、”lighter”の値は無効になります。

次のCodePenデモをご覧ください。

この例では、18種のスタイルを指定することが可能な Exo 2 という、フォントを使用しています。私のデモは、100を一段階としたウェイトにしているので、斜体ではないスタイルだけを埋め込んでいます。

デモに、”bolder”や”lighter”など、異なる font-weight の値を持つネスト化した”box”要素が12個含まれていること分かると思います。これで、異なる継承値を持つテキストのウェイトにどう影響するか分かると思います。次はCSSコードの例になります。コード内の構文にお気づきたと思いますが、後続の”box”は前の要素にネスト化されていることを覚えておいてください。

.box {
  font-weight: 100;
}

.box-2 {
  font-weight: bolder; /* maps to 400 */
}

.box-3 {
  font-weight: bolder; /* maps to 700 */
}

.box-4 {
  font-weight: 400;
}

.box-5 {
  font-weight: bolder; /* maps to 700 */
}

.box-6 {
  font-weight: bolder; /* maps to 900 */
}

.box-7 {
  font-weight: 700;
}

.box-8 {
  font-weight: bolder; /* maps to 900 */
}

.box-9 {
  font-weight: bolder; /* maps to 900 */
}

.box-10 {
  font-weight: lighter; /* maps to 700 */
}

.box-11 {
  font-weight: lighter; /* maps to 400 */
}

.box-12 {
  font-weight: lighter; /* maps to 100 */
}

ここでは、”bolder”と”lighter”のキーワードは、 100400700900 の値にのみマッピングします。9種の異なるスタイルを持つこのキーワードでは、 200300500600800 の値にマッピングすることはありません。

このような結果になる理由は、ブラウザにコードの中から次に”bold”あるいは”light”なフォントを選択するよう指示しているからです。つまり、次に最もboldあるいは最もlightなフォントを選択しているのではなく、単に継承値よりboldあるいはよりlightなフォントを選択しているのです。しかしながら、最もlightなフォントの値が(例えば Open Sans みたいに) 300 で、継承値が 400 の場合は、”lighter”の値は 300 をマッピングします。

初めは分かりにくいでしょうが、デモをいじっていくうちにこれらのキーワードの効果が分かってくると思います。

3. outline-offset プロパティがある事実

デバッギングに役に立つため、 outline プロパティは広く知られています( ページの実行に影響はありません )。しかしながら、 outline-offset プロパティが仕様書に追加されていることはどうでしょう。名前どおりの動きをするこのプロパティは、要素からどれだけアウトラインをオフセットするのか定義します。

先ほどのデモでは、数値の範囲をスライダーで左右に動かせばアウトラインオフセットの変化を見ることができます。この例で使用している数値の範囲は 0px から 30px ですが、CSSでは好きなだけ大きくすることが可能です。CSSの outline プロパティはショートハンドプロパティですが、 outline-offset プロパティは含まれていないため、別途 outline-offset を定義する必要があることに注意してください。

唯一の大きなデメリットは、 outline-offset プロパティがInternet Explorerで (IE 11でさえ)サポートされていないことです。他のブラウザではサポートされています。

4. table-layout プロパティがある事実

いまさら、と思っているかもしれません。 display: table のことは全て知っている、簡単にページ中央を垂直にする方法 、とも思っているかもしれません。しかし、私が話しているのはこのことではありません。私が話しているのは、 table-layout プロパティで display プロパティではありません。

table-layout プロパティはCSS機能の中でも説明が難しいので、まずは、仕様を見てから例を見てみましょう。仕様書の説明は次のとおりです。

この(高速)アルゴリズムを用いたテーブルの水平方向のレイアウトは、セルのコンテンツに依存しない。水平方向のレイアウトはテーブルの幅、列の幅、ボーダーおよびセルの間隔にのみ依存する。

恐らく、今までのW3C(World Wide Web Consortium)の仕様の中で初めて理解しにくいのではないでしょうか。冗談です(笑)。

しかし、いつものように例を見た方が分かりやすいのは本当です。次のデモでは、CSSで table-layout: fixed をテーブルに追加しています。トグルボタンをクリックして、トグルを無効にしたり有効にしたりして比べてみてください。

この例を見れば、デフォルト設定に auto を使用する代わりに、 table-layout: fixed を使用するメリットが分かると思います。必ずしも、最適な選択とは限りませんし、必要ないかもしれません。しかし、幅の異なるセルが混在する表を使用する上で覚えておくと便利だと思います。

昨年、Chris Coyierが このプロパティについて 分かりやすく説明していますので、さらに詳しく理解したい場合は、ぜひお読みください。

5. vertical-align プロパティはテーブルのセルと他の要素とでは作用が異なる事実

2000年代中盤もしくはそれ以前からWebサイトのコーディングを行っていたり、HTMLメールを多く手掛けていたりすれば、おそらくどこかの時点で vertical-align プロパティが従来の HTML4のvalign属性 に対する標準的なアップグレードに相当していることにお気づきでしょう。これは現在の HTML5では廃止され、非準拠の機能 とされています。

しかし、実はCSSの vertical-align は異なる動きをします。テーブルの場合はまた異なります。おかしな話ですが、このプロパティがテーブルに対してまったく無効となるよりは、納得できる話です。

ではこのプロパティが通常の要素に適用される際、テーブルセルに適用される場合とはどのように異なるのでしょうか。

テーブルセルに適用されない場合、 vertical-align プロパティは以下の基本的なルールに準じます。

  • インライン要素、またはインラインブロック要素にのみ適用できる。
  • 要素の中身には影響しないが、代わりに、ほかのインライン要素またはインラインブロック要素との関連で要素自体の整列位置が変更される。
  • line-heightなどのテキスト/フォント設定や、隣接するインライン要素またはインラインブロック要素のサイズの影響を受ける。

次のデモをご確認ください。

vertical-align プロパティは input 要素で定義されます。どれかのボタンを押すと、ボタンに表示されている値に変更できます。いずれも input の位置を変更するものであることはお分かりですね。

これは、このプロパティと値を確認する非常に基本的な例です。さらに詳しく見てみたい方は Christopher Aueの2014年の記事 を参照してください。

さて、これがテーブルに適用されるとなると、 vertical-align はまったく異なる動きをします。プロパティ/値を、1つまたは幾つかのテーブルセルに適用した場合、テーブルセルの中身は、選択された整列位置の影響を受けます。

今のデモでお見せしたように、テーブルセルに対しては4つの値のみが適用できます。 baseline を押した場合にのみ、分割されたセルにも影響がありますが、基本的には vertical-align を適用したセルの中身の配置のみが影響を受けます。

6. ::first-letter 疑似要素は思っている以上に賢い事実

::first-letter 疑似要素を使うと、指定した要素の第一文字目のスタイルを変更でき、印刷業界では長年常識とされている ドロップキャップ 効果が得られます。

これを使用する利点は、要素の”一文字目”を構成するものに対して、ブラウザが適切な水準を備えているということです。このことは、 Matt Andrewsのツイート で初めて見ました。しかし、彼はこれを喜ばしいとは思っていないようでした。彼が挙げた例を以下のCodePenで見てみましょう。

私が見たところ、4大ブラウザはどれも同様の扱いをするようなので、これは正しい挙動だと考えられますし、素晴らしいです。開き括弧が”1文字目”として認識されているのはいささか妙ではありますが。”1キャラクタ目”と言った方がふさわしいかもしれませんし、これ自体まったく新しい疑似クラスになり得ると思います。

7. HTMLクラスリストに区切り文字として無効文字を使用できる事実

このコンセプトは Ben Everardによって2013年に 論じられましたが、私は更に詳しく追求する意義があると考えます。

Benの投稿は、HTMLクラスをグループに分割する際にスラッシュ(”/”)を使用することで、コードを読みやすくし、可読性を上げるための方策でした。彼が指摘しているように、エスケープ処理されていないスラッシュは無効文字ではありますが、ブラウザはそれを問題として扱うことなく、単に無視します。

次のHTMLの例を見てみましょう。

<div class="col col-4 col-8 c-list bx bx--rounded bx--transparent">

スラッシュを入れて以下のような記述にします。

<div class="col col-4 col-8 / c-list / bx bx--rounded bx--transparent">

無効文字であるかどうかに関わらず、どのような文字を挿入した場合でも同じ結果を得られます。

<div class="col col-4 col-8 ** c-list ** bx bx--rounded bx--transparent">

<div class="col col-4 col-8 || c-list || bx bx--rounded bx--transparent">

<div class="col col-4 col-8 && c-list && bx bx--rounded bx--transparent">

いずれのコード記述でも正常に作用するようです。以下の例で試してみてください。

もちろん、これらの区切り文字はスタイルシートでクラスとしては使用できません。そういう意味で私は”無効”としています。従って次のような記述はルールに準じておらず、特定のスタイルには適用できないでしょう。

./ {
  color: blue;
}

もし、CSS内でこれらの無効文字をHTMLクラスで使用し、ターゲットにしなければならない場合は、 こちらのツールを使ってスラッシュを挿入し、エスケープすることができます 。先ほどの例でいうと、CSSは次のような記述になります。

.¥/ {
  color: blue;
}

さらに言うと、Unicode文字はまったくエスケープ処理される必要がないので、以下のようなことさえもできるのです。

<div class="♥ ★"></div>

そして、CSSでは次のような記述になるでしょう。

.♥ {
  color: hotpink;
}

.★ {
  color: yellow;
}

あるいは、これらの文字も、直接挿入せずに、先ほどと同様にエスケープできます。以下の例は、上のコードブロックと同じことを意味します。

. \2665 {
  color: hotpink;
}

. \2605 {
  color: yellow;
} 

8. アニメーションの繰り返し再生が分数値で指定できる事実

CSSでキーフレームのアニメーションを記述する際、 animation-iteration-count プロパティを使用してアニメーションの再生回数を指定することは、おそらくご存知でしょう。

.example {
  animation-iteration-count: 3;
}

この例では、アニメーションをフルタイムで3回実行することが整数値で指定されています。しかし、分数値を指定できることはご存知ないのではないでしょうか。

.example {
  animation-iteration-count: .5;
}

この場合、アニメーションは半数回実行されます(つまり、最初の再生の途中で停止します)。以下の例では、ページ上で2つのボールが動作します。上のボールは再生回数を”1″に指定していますが、下のボールの再生回数は”0.5″と指定しています。

追記:コメントで指摘をされましたが、これらのデモはデスクトップ版およびモバイル版のSafariでは正常に動作しません。これはfill modeに関係するバグのためで、既に こちらに報告し 、修正されました。次の更新版で反映されるはずです。

この例で興味深いのは、再生間隔が描画のプロパティ/値に基づいているわけではないという点です。つまり、何かを100pxで描画する場合、中間点は必ずしも50pxではないのです。例えば、先ほどのアニメーションは linear というタイミング関数を使用しているので、下のボールが横軸上の中間で止まったように見えます。

以下は、同じ2つのボールのアニメーションですが、 ease というタイミング関数を使用した例です。

今度は下のボールが中間点を超えてから停止していますね。繰り返しになりますが、これは異なるタイミング関数を使用したことによって起きています。

タイミング関数を理解していれば、 ease-in-out で値を指定した場合も linear を使用したときと同じボールの位置になることにお気づきでしょう。いろいろな結果が得られますので分数値とタイミング関数で試して遊んでみてください。

9. アニメーション名がショートハンドを壊してしまう事実

これは数人の開発者が偶然見つけた事象で、仕様で注意喚起されています。例えば次のようなアニメーションコードがあるとします。

@keyframes reverse {
  from {
    left: 0;
  }

  to {
    left: 300px;
  }
}

.example {
  animation: reverse 2s 1s;
}

ここでは reverse というアニメーション名を使ってみました。一見問題なさそうですが、実際にこのコードを動かしてみるとどのような結果になるのか確認してみましょう。

アニメーションは動きません。なぜなら、”reverse”という値が、 animation-direction プロパティの値にも指定できるキーワードに相当しているからです。ショートハンドシンタックスで有効なキーワード値とアニメーション名が一致する場合には、常に同様の事象が起こります。ロングハンドの場合には問題ありません。

このようにショートハンドシンタックスを壊すアニメーション名としては、例えば infinitealternaterunningpaused といった タイミング関数のキーワード も含まれます。

10. 要素が範囲選択できる事実

誰が最初にこの方法を使い始めたのかは分かりませんが、私が最初にこのやり方を見たのは Gunnar Bittersmann による こちらのデモ です。20個の要素を持つ順リストがあり、このうち7番目から14番目までの要素を全て選択したい、としましょう。この場合次のように、セレクタ1つで実行することができます。

ol li:nth-child(n+7):nth-child(-n+14) {
  background: lightpink;
}

追記: コメント欄でのご指摘の通り、Safariにはこのテクニックの実行を妨げるバグがあります。幸いなことに、Matt Pomaskiの提案方法でこの問題を解決することができるようです。コードチェーンの順番を次のように単純に入れ変えてみてください。 ol li:nth-child(-n+14):nth-child(n+7) となります。この方法で今のところWebKitではバグは出てきていませんので、Safari上でも問題なくこの方法を使うことができる、と言えるでしょう。

このコードには、構造疑似クラスが連鎖して使われています。少し分かりづらい式になっていますが、選択したい範囲が式の中の数値で示されているのが確認できます。

どのように実行しているか正確に見てみましょう。連鎖の最初にある式には、”7番目の要素を選び、その後に続く全ての要素も選択する”と書かれています。次のパートには、”14番目の要素を選び、その前にある全ての要素も選択する”と書かれています。2つのセレクタは連鎖しているので、各パートはそれぞれのスコープを限定します。すなわちこの場合、2番目に出てきたパートは、最初のパートが14番目より先にある要素を選択することを許容しません。また最初のパートは、2番目のパートが7番目より前にある要素を選択することを許容しません。

こういったタイプのセレクタや式に関する詳細は、 以前こちらの記事 にまとめましたので、ご確認ください。

11. 疑似要素を空要素に適用できる事実

もしかしたら皆さんも私のように、疑似要素をイメージやフォームのインプットに当ててみようと考える時があるかもしれません。しかし疑似要素とは置換要素に対しては機能しないものなので、この試みはうまく動きません。そこで開発者の皆さんの多くは、全ての空要素(終了タグを持たない要素)には疑似要素が適用できない、と想定されるかと思います。しかしそれは正解ではありません。

置換要素ではない いくつかの空要素 には、疑似要素が適用できるものもあるのです。次のデモで見られる hr 要素もこのケースの1つです。

この例における色付きエリアは、水平方向のルール( hr 要素)に従っています。そしてこの要素には、 ::before::after という疑似要素が適用できています。でも面白いことに、同じく置換要素ではない空要素である br 要素を使った場合には、適用可能という結果は得られません。

メタタグと link 要素に対しても疑似要素を加えることができます。そのためには次のデモのように、これらの要素を display: block になんとかうまく変換することが必要です。

12. セレクタ内で大文字・小文字の区別が不要な属性値があるという事実

最後に取り上げるこの事実は、改めて特筆するべきものではないかもしれません。次に挙げるHTMLがあるとしましょう。

<div class="box"></div>
<input type="email">

属性セレクタを使えば、次のようにこれらの要素をスタイリングすることができます。

div[class="box"] {
  color: blue;
}

input[type="email"] {
  border: solid 1px red;
}

これはうまく動くようですが、では次のコードではどうでしょう?

div[class="BOX"] {
  color: blue;
}

input[type="EMAIL"] {
  border: solid 1px red;
}

2番目の例では、属性の値はどちらも大文字で書かれています。この場合、 .box 要素にはスタイルが適用されません。なぜなら class の属性は大文字と小文字を区別する、いわゆるケース・センシティブなものだからです。一方 type の属性値はケース・センシティブではないので、emailのフィールドには正しくスタイルが適用されます。この場で取り上げる必要はなかったかもしれませんが、でももしかしたら皆さんが気づいていなかったかもしれない事実です。

以上です、皆さん

これで幕は下りました。この続編が皆さんにとっては、そんなに安っぽいものではなかったことを祈ります。私は毎週のように、このようなちょっとしたユニークなCSS豆知識を学んできました。この記事の中から少しでも目新しいものを、多くの皆さんが見つけることを願っています。CSSのあいまいなトリックにテクニック、皆さんのお気に入りの情報はどれだったのか、気になっています。あまりご存知なかったけれども、実際ブラウザはサポートしていた素敵なプロパティや機能が見つかったかどうか、教えて下さい。コメントをお待ちしています。