2024年08月01日
こんにちは! 今日もコーディングを楽しんでいますか?
さて、以前の記事で:
近ごろのCSSは、想像を超えるほど高機能になってきていて、「CSSだけで、ここまでできるの?!」と驚くばかりです。
と書きましたが、具体的にどんなふうに進化したのか? 気になるところですよね。こんかいは、「JavaScriptなしのCSSだけで、ここまでできる!」という例を、いくつか紹介してみたいと思います。
注意:
当記事で取り上げるCSSは、ChromeおよびSafariの最新版であれば、デスクトップ版・スマートフォン版をとわず動作します。
本記事ではSafariの最低バージョンを16.5と想定していますが、Safariのバージョンは、お使いのOSバージョンに依存しています。そのため、OSバージョンがそれ未満だと、コード例が動作しません。もしOSが最新版ではない、あるいはアップグレードしたくない場合は、Firefoxブラウザ(無料)を導入してみてください。
チェックボックス
フォーム部品の「四角いやつ」でおなじみの要素です。マークアップは全部同じなのに、CSSによってこんなにも面白い表現ができるようになりました。
See the Pen CSS2024の例1 by isobeact (@rmzpacpm-the-builder) on CodePen.
特にトグルスイッチのような見せ方や、記事を掘り下げていくドリルダウンのような見せ方が、CSSだけでできるだなんて信じられませんよね!
ラジオボタン
もともとの「複数のうちひとつしかチェックできない」という性質を利用して、こんな見せ方ができるようになりました。
See the Pen CSS2024の例2 by isobeact (@rmzpacpm-the-builder) on CodePen.
念のために、このタブのUI自体も、ラジオボタンでできています!
スナップスクロールとスナップ合わせ位置
手動でのスクロール操作で、子要素の位置に磁石で吸い付くように「カチッ」と止める(スナップする)事ができるようになりました(これは Safari 11 から対応しているので、心配ないでしょう)。
スナップの強さは、mandatory
(必須=強い)かproximity
(おおよそ=弱い)かを選べます。例で言えば、縦長の写真の閲覧中、強制スナップがじゃまになってしまう場合などに、proximity
が役立ちます。
また子要素に対しては、スナップの「合わせ位置」をどこにするかも、start
(先頭)、center
(中央)、end
(末端)を選べます。
See the Pen CSS2024の例3 by isobeact (@rmzpacpm-the-builder) on CodePen.
ではここからは、上記の例で、CSSの進化した点を説明していきます。
擬似クラス:has()
という福音
上記の例のCSSを確認してみてください。セレクタの中に、やたらと:has()
という構文が現れているのがわかると思います。これはCSS4で実装された「関係擬似クラス(Reletional Pseudo-class)」と呼ばれるもののひとつです。
ここでいう関係とは、中学英語の授業で習う「関係代名詞」(Relative pronoun)の「関係」(Relation)と同じです。この構文は、
(同じセレクタでも)特定の文脈に関係付けられたセレクタ
を表現するために生み出されました。
基本の使い方
実例をあげましょう。このようなマークアップを想定してみます。
<label class="button-like">
<input type="checkbox">
まるでボタンみたい!
</label>
このとき、CSSがこうであったとすると、どのように解釈されるでしょうか?
.button-like:has(input) {
background: #aaa;
}
これは、英語調に書くと 、
a
.button-like
class that hasinput
element.
すなわち
input
要素を子孫に持つ、.button-like
クラス。
と解釈されます。
重要なこととして、セレクタはあくまでも.button-like
であり、input
要素の記載は、あくまでも.button-like
の絞り込みの条件に過ぎません。
:has()
の中には「セレクタ」を書く
この:has()
の中に書く「絞り込み条件」には、左側に書かれたセレクタから見た、セレクタが入ります。正しいセレクタ記法であれば、より複雑な指定も可能です。たとえば、
.button-like:has(+ h3) {
background: #8f8;
}
は、「h3
要素を直後の妹要素として持つ、.button-like
」と解釈されます。
.button-like:has(+ h3 > input) {
background: #8f8;
}
は、「input
要素を直接の子として持つh3
要素を、直後の妹要素として持つ、.button-like
」と解釈されます。
.button-like:has(+ h3 > input:checked) {
background: #8f8;
}
は、「:checked
であるinput
要素を、直接の子として持つh3
要素を、直後の妹要素として持つ、.button-like
」と解釈されます。
.button-like:has(+ h3 > input[value="4"]:checked) {
background: #8f8;
}
は、「:checked
であり、かつ、value
属性が"4"
であるinput
要素を、直接の子として持つh3
要素を、直後の妹要素として持つ、.button-like
」と解釈されます。
「特定の条件を満たしたセレクタ」からのセレクタが定義できる
さて、こうした関係疑似クラスは、これまでわれわれが夢見て、それでも不可能だった、以下の表現を可能にします!
.button-like:has(input[value="4"]:checked) .grid {
grid-template-rows: 1fr;
}
『
:checked
であり、かつ、value
属性が"4"
であるinput
要素を子孫として持つ、.button-like
』の子孫である、.grid
どうですか、この持って回ったような言い回し! まさに中学英語で習った「関係代名詞」そっくりですね!
しかし、これによって「特定の条件を持ったセレクタに対する子孫、兄弟要素」に対して、スタイルを当てる事が可能となったのです。これはCSSにおける一大革命と言えます!
CSS定義の入れ子
ついに通常のCSSも、SCSS(SASS)同様に、入れ子が可能となりました! ただし、Safariではバージョンによりどこまでが許可されるのかが変わるので、目印をつけておきます。
&
(入れ子セレクタ)によるCSS定義の入れ子
先の例のCSSには、:has()
よりももっと多くの&
記号が出現しています。SCSS(SASS)をご存知であればわかりやすいと思いますが、この記号は「親セレクタの…」を意味します。違いは、SCSSでは{ }
の中に別のセレクタ名称として「親セレクタの名称」を使い回せるのに対し、CSSではそれが禁止されていることです。
/* SCSS の場合 */
.wrapper {
color: black;
/* いわゆるBEM記法を実現する書き方 */
&__content {
/* `.wapper__content` と解釈される */
color: red;
}
}
/* CSS入れ子記法 の場合 */
.wrapper {
color: black;
/* これは無効なCSSとなる! */
&__content {
color: red;
}
}
それ以外であれば、&
を省略しない限り、SCSSと同様の書き方が可能です。
/* SCSS の場合 */
.wrapper {
color: black;
/* そのセレクタへの擬似クラスや疑似要素の場合、 `&` をつける */
&:hover {
/* `.wapper:hover` と解釈される */
color: red;
}
/* 単純な子孫セレクタの場合、 `&` はいらない */
.main {
/* `.wapper .main` と解釈される */
}
/* その他のセレクタでも、 `&` はいらない */
+ footer {
/* `.wrapper + footer` と解釈される */
color: gray;
}
/* 親セレクタ名を後ろに繰り返すことは可能。その際は `&` で親セレクタを明示する */
.main & {
/* `.wapper .main .wrapper` と解釈される */
}
/* 連結セレクタを表現する場合は、&を前にして書く */
&.main {
/* `.wrapper.main` と解釈される */
}
/* `&` とセレクタを連結子なしでつなげると、セレクタの名前が繰り返される */
&header {
/* `.wrapperheader` と解釈される */
}
}
/* CSS入れ子記法 の場合 */
.wrapper {
color: black;
/* SCSSと同じ使い方 */
&:hover {
/* `.wapper:hover` と解釈される */
color: red;
}
/* もし `&` をつけないと… */
:hover {
/* `*:hover` と解釈されてしまう! */
color: red;
}
/* 親セレクタの明示として `&` が必須! */
& .main {
/* `.wapper .main` と解釈される */
}
/* 親セレクタの明示として `&` が必須! */
& + footer {
/* `.wrapper + footer` と解釈される */
color: gray;
}
/* 親セレクタ名を後ろに繰り返すことは可能。その際は `&` で親セレクタを明示する */
& .main & {
/* `.wapper .main .wrapper` と解釈される */
}
/* 連結セレクタを表現する場合は、&を前にして書く */
&.main {
/* `.wrapper.main` と解釈される */
}
/* `&` とセレクタを連結子なしでつなげることは、 `&要素名` だけが認められる */
&header {
/* 要素名を先頭にした `header.wrapper` と解釈される */
}
}
&
を省略したCSS定義の入れ子
SCSSと比べ、先のCSSは、どうしても&
が目ざわりですね。一般的なモダンブラウザ、および Safari 17.2 以降であれば、もっと単純に記載することができます!
/* CSSモダン入れ子記法 の場合 */
.wrapper {
color: black;
/* 親セレクタの明示は不要! */
.main {
/* `.wapper .main` と解釈される */
}
/* 親セレクタの明示は不要! */
+ footer {
/* `.wrapper + footer` と解釈される */
color: gray;
}
/* 親セレクタ名を後ろに繰り返すことは可能ですが、その際は `&` を省略できません */
.main & {
/* `.wapper .main .wrapper` と解釈される */
}
/* 連結セレクタを表現する場合も、残念ながら `&` 省略できません */
&.main {
/* `.wrapper.main` と解釈される */
}
/* `&` とセレクタを連結子なしでつなげることは、 `&要素名` だけが認められるのは同じです */
&header {
/* 要素名を先頭にした `header.wrapper` と解釈される */
}
}
SCSSの便利さとスタイル詳細度のコントロールを熟知している方は、&
記号の使い方に気をつけつつ、入れ子でCSSを書くことに挑戦してみましょう!
まとめ
いかがでしたか? 本記事では、もはやJavaScriptを使うことなく、CSSだけで様々なUIを実現できるようになった事例の、ほんの一部をご紹介しました。他にもFlexレイアウトの進化形である「Gridレイアウト」、段組みによる文字の流し込みを実現する「カラムレイアウト」、ヘッダーやフッターをスクロール部分に固定する「粘着ポジション」など、紹介しきれなかった項目はまだまだあります! それらはいずれ、機会がありましたらご紹介しましょう。
こんかいの記事は、以上です。それでは、よきコーディングライフを!