CSS の新機能

作成日:2018年10月9日

CSS カスタムプロパティ / CSS 変数 -- と var()

CSS カスタムプロパティは、CSS のユーザー定義変数で、CSS 変数と呼ばれることもあります。公式には「CSS Custom Properties for Cascading Variables」と呼ばれています。

CSS Custom Properties for Cascading Variables Module Level 1 (W3C)

CSS 変数が使えると、とても便利になりますが、残念ながら IE11 は対応していません。

caniuse CSS Variables(ブラウザ対応状況)

Sass などのプリプロセッサーでは変数が使えますが、プリプロセッサーの変数は CSS にコンパイルしなければなりません。対して、CSS 変数は、ブラウザーで実行する動的な CSS プロパティです。また、CSS 変数は、JavaScript コードから直接アクセスすることができます。

変数の宣言と使用

CSS 変数(カスタムプロパティ)は先頭にハイフンを2つ (--) 付けて任意の名前を記述し、通常の CSS プロパティと同じようにコロンで区切って値を記述します(変数名は大文字小文字を区別します)。

.foo {
  /* カスタムプロパティ名はハイフン2つで始める */
  --my-bg-color: yellow;  
}   

定義した CSS 変数を呼び出すには、使用したい箇所で var() 関数にカスタムプロパティ名を指定します。

.foo {
  --my-bg-color: yellow;
  /* var() にカスタムプロパティ名を指定して呼び出し */
  background-color: var(--my-bg-color);
}

但し、CSS 変数のスコープ(適用範囲)は CSS セレクタ内になります。文書全体で適用できるようにするには :root 擬似クラスに定義します。

.foo {
  --my-bg-color: yellow;
  background-color: var(--my-bg-color);
}

.bar {
  /* この場合、以下は適用されない */
  color: var(--my-bg-color);
}

:root 擬似クラスに定義

CSS 変数のスコープは CSS セレクタ内になりますが、:root 擬似クラスに定義してグローバル変数のように使用することができます。

つまり、:root 擬似クラスで変数を宣言することで、変数を CSS 内のどこでも使用することが可能です(html 要素内の全てに適用することができます)。

:root {
  --main-bg-color: #efefef;
  --main-text-color: #999;
}
  
.foo {
  background-color: var(--main-bg-color);
  color: var(--main-text-color);
  width: 100px;
  height: 50px;
}
  
.bar {
  background-color: var(--main-bg-color);
  color: var(--main-text-color);
  width: 200px;
  height: 100px;
}

スコープを設定

:root 擬似クラスに CSS 変数を定義すればグローバル変数のようにすべての要素で呼び出すことができます。また、セレクタ内で定義した CSS 変数の定期用範囲はそのブロック内になります。

そのため、:root 擬似クラスでグローバルな値を定義して、特定のセレクタのブロック内で局所的にその値を再定義(上書き)することでローカルのスコープを設定することができます。

:root{
  /* グローバルな値を定義 */
  --primary-color: green;
}

body {
  color: var(--primary-color);
}

.foo {
  /* ブロックのスコープ内で再定義(上書き) */
  --primary-color: blue;
  color: var(--primary-color);
}

.bar {
  /* ブロックのスコープ内で再定義(上書き) */
  --primary-color: red;
  color: var(--primary-color);
}

注意点

  • CSS 変数(カスタムプロパティ)の名前は大文字と小文字が区別されます。
  • プロパティ名を変数にすることはできません(値のみが変数として利用可能)。
  • CSS 変数はカスケードの対象となります。

CSS 変数の中で CSS 変数を設定

CSS 変数の値に別の CSS 変数を設定することもできます。

以下は、グラデーションを CSS 変数を使って設定する例です。グラデーションの CSS 変数の値には、開始色(--start-color)と終了色(--end-color)の CSS 変数を使っています。

:root {
  --start-color: #C7F9D8;
  --end-color: #9FB0F2;
  --bg-gradient: linear-gradient(var(--start-color), var(--end-color));
}
.foo {
  background: var(--bg-gradient);
  width: 100px;
  height: 100px;
}
<div class="foo"></div>

CSS 変数の継承

通常の CSS プロパティと同じように、CSS 変数は継承されます。

class="three" の div 要素は、親要素(class="bar")の値を継承します。

<div class="bar">
  <p>abcdefg</p>
  <div class="one">
    <p>abcdefg</p>
  </div>
  <div class="two">
    <p>abcdefg</p>
  </div>
  <div class="three">
    <p>abcdefg</p>
  </div>
</div>
:root {
  --main-color: blue;
}
.bar p {
  color: var(--main-color);
}
.one {
  --main-color: red;
}
.two {
  --main-color: green;
}

abcdefg

abcdefg

abcdefg

abcdefg

メディアクエリ内で CSS 変数を使う

Sass の変数はメディアクエリ内で動作しませんが、CSS 変数は機能します。

:root {
  --my-bg: #EEE;
}
@media (max-width: 580px) {
  :root {
    --my-bg: green;
  }
}
.buz {
  background: var(--my-bg);
  height: 50px;
  border: 1px solid #CCC;
  line-height: 50px;
}

以下の div 要素は、幅が580px以下で背景色が緑色に変わります。

class="buz"

参考サイト:僕がネイティブな CSS 変数にわくわくする理由

フォールバックの指定

CSS 変数にはフォールバック(代替値)の値を1つ以上設定することができます。

var() 関数を使用して、指定された変数が定義されていない場合や無効な場合の代替値をカンマで区切って複数指定することができます。

以下の場合、--my-var が定義されていなければ green が適用されます。

.foo {
  color: var(--my-var, green); 
}

以下の場合、 --my-var が定義されていなければ --my-var2 が適用されます。

.foox {
  color: var(--my-var, var(--my-var2)); 
  
  /* 以下は NG。CSS 変数を指定する場合、var() 関数を使用する必要があります。 */
  color: var(--my-var, --my-var2); /* ※--my-var2 は適用されない */
}

以下の場合、 --my-var が定義されていなければ --my-var2 が適用され、 my-var 及び --my-var2 が定義されていなければ red が適用されます。

.foo {
  color: var(--my-var, var(--my-var2, red)); 
}

但し、フォールバックの指定は、パフォーマンスに影響があるようです。以下は「MDN CSS のカスタムプロパティ (変数) の利用」からの引用です。

このテクニックはパフォーマンスの問題を起こすことがあります。変数を解釈するのにより時間が掛かるため、ブラウザーがページを表示するのが通常よりも遅くなります。

JavaScript で CSS 変数を使用

CSS 変数は、JavaScript から直接アクセスすることができます。

.foo クラスの CSS 変数 --bg-gradient が以下のように設定されている場合、JavaScript で --bg-gradient の値を取得できます。

:root {
 --start-color: #C7F9D8;
 --end-color: #9FB0F2;
 --bg-gradient: linear-gradient(var(--start-color), var(--end-color));
}
#foo {
  background: var(--bg-gradient);
  width: 100px;
  height: 100px;
  margin: 20px 0;
}

#foo の計算済みスタイルを getComputedStyle() で取得して、getPropertyValue() で変数の値を取得します。

// #foo の計算済みスタイルを取得 
const fooStyle = window.getComputedStyle(document.getElementById("foo"));

// CSS 変数 --bg-gradient の値の取得 
const fooVal = fooStyle.getPropertyValue('--bg-gradient'); 

console.log(fooVal); //出力:linear-gradient( #C7F9D8,  #9FB0F2)

JavaScript で CSS 変数の値を変更する場合は setProperty() を使います。

document.getElementById("foo").style.setProperty('--bg-gradient', 'linear-gradient( #CCCCCC,  #333333)'); 

hsl() と CSS 変数で色を定義

数値で色を扱う場合、HSL は RGB に比べて直感的に色を指定できます。

成分 説明
Hue(色相) 0〜359の数値で指定。赤は0(360)、 緑は120、青は240 など
Saturation(彩度) 0%~100%で指定。0%は灰色で、100%に向かって純色になります。
Lightness(輝度) 0%~100%で指定。0%は黒、100%は白となり、純色を表現したい場合は50%を指定。

また、補色は、指定した Hue の値に 180 を足すことで求められます。

以下は hsl() 関数と CSS 変数を使って色を定義する一例です。

:root に基本的な色の値を設定して、hsl() 関数と calc() 関数を使って値を調整しています。

この例では色相(Hue)を一定にして彩度(Saturation)と輝度(Lightness)の値を変化させて色を定義していますが、どの値を変化させるかでいろいろな色のパターンを作成することができます。

以下の場合、:root で定義している --hue-base の値を変えれば全体の色を変更できます。

ホバー時には hsl() 関数に指定する最初のパラメータの --hue-base の値を変更して色を変更しています。

:root {
  --hue-base: 180; /* Hue(180 は水色)  */
  --str-base: 100%; /* 純色 */
  --lgt-base: 50%; /* 純色 */ 
  --hue-hover: calc(var(--hue-base) - 20); /* ホバー時の Hue(薄緑色) */
  --hue-comp: calc(var(--hue-base) + 180);   /* 補色の Hue   */
  --hue-hover-comp: calc(var(--hue-comp) - 20); /* 補色のホバー時の Hue */
}
.color1{
  background-color: hsl(var(--hue-base), var(--str-base), var(--lgt-base));
}
.color2{
  background-color: hsl(var(--hue-base), calc(var(--str-base) * 0.9), calc(var(--lgt-base) * 0.9));
}
.color3{
  background-color: hsl(var(--hue-base), calc(var(--str-base) * 0.8), calc(var(--lgt-base) * 0.8));
}
.color4{
  background-color: hsl(var(--hue-base), calc(var(--str-base) * 0.7), calc(var(--lgt-base) * 0.7));
}
.color5{
  background-color: hsl(var(--hue-base), calc(var(--str-base) * 0.6), calc(var(--lgt-base) * 0.6));
}
.color6{
  background-color: hsl(var(--hue-base), calc(var(--str-base) * 0.5), calc(var(--lgt-base) * 0.5));
}

.color1:hover,.color2:hover, .color3:hover, .color4:hover, .color5:hover, .color6:hover {
  --hue-base: var(--hue-hover);
}

/* 補色 */
.comp1{
  background-color: hsl(var(--hue-comp), var(--str-base), var(--lgt-base));
}
.comp2{
  background-color: hsl(var(--hue-comp), calc(var(--str-base) * 0.9), calc(var(--lgt-base) * 0.9));
}
.comp3{
  background-color: hsl(var(--hue-comp), calc(var(--str-base) * 0.8), calc(var(--lgt-base) * 0.8));
}
.comp4{
  background-color: hsl(var(--hue-comp), calc(var(--str-base) * 0.7), calc(var(--lgt-base) * 0.7));
}
.comp5{
  background-color: hsl(var(--hue-comp), calc(var(--str-base) * 0.6), calc(var(--lgt-base) * 0.6));
}
.comp6{
  background-color: hsl(var(--hue-comp), calc(var(--str-base) * 0.5), calc(var(--lgt-base) * 0.5));
}

.comp1:hover,.comp2:hover, .comp3:hover, .comp4:hover, .comp5:hover, .comp6:hover {
  --hue-comp: var(--hue-hover-comp);
}

.color-wrapper {
  display: flex;
}
.color-wrapper div {
  min-width: 50px;
  height: 100px;
  cursor: pointer;
}
HTML
<div class="color-wrapper">
  <div class="color1"></div>
  <div class="color2"></div>
  <div class="color3"></div>
  <div class="color4"></div>
  <div class="color5"></div>
  <div class="color6"></div>
</div>
<div class="color-wrapper">
  <div class="comp1"></div>
  <div class="comp2"></div>
  <div class="comp3"></div>
  <div class="comp4"></div>
  <div class="comp5"></div>
  <div class="comp6"></div>
</div>

参考サイト:Creating Color Themes With Custom Properties, HSL, and a Little calc()


pointer-events

pointer-events を使うと、ポインターイベント (クリック、ドラッグ、ホバー等) の発生を制御することができます。別の言い方をすると、特定の要素がポインターイベントの対象になるかどうかを制御することができます。

caniuse: CSS pointer-events (for HTML)

HTML 要素に適用できる値は、auto または none です。SVG には他の値(visiblePainted や visibleFill など)を適用することができます(省略)。

/* キーワード値 */
pointer-events: auto;
pointer-events: none;

/* グローバル値 */
pointer-events: inherit;
pointer-events: initial;
pointer-events: unset;
MDN pointer-events より
説明
auto 要素は、 pointer-events プロパティが指定されていないときと同様にふるまいます。
none 要素がポインターイベントの対象になることはありません。しかし、子孫要素が pointer-events の別の値をセットされていた場合は、その子孫要素自体はポインターイベントのターゲットとなりえます。その場合、ポインターイベントはイベントキャプチャ/バブリングの過程で必要に応じて親要素のイベントリスナーをトリガーします。

例えば、以下のような CSS が設定してある場合、

.nolink {
  pointer-events: none;
}
.linkOn {
  pointer-events: auto;
}

以下のリンク(a 要素)のポインターイベントは無効になり、クリックすることができません。

<p><a class="nolink" href="http://example.com">example.com</a></p>   

example.com

以下の場合は、1つ目のリンクのポインターイベントは無効になりますが、2つ目のポインターイベントは pointer-events が指定されていないのと同様にクリックすることができます。

<ul class="nolink">
  <li><a href="http://example.com">example.com</a></li>
  <li><a class="linkOn" href="http://example.com">example.com</a></li>
</ul>

重なり順(レイヤー)が下の要素をクリック

例えば、絶対配置を使って要素を重ねた場合、下になった領域(レイヤー)にクリックさせたい要素があっても、通常ではクリックすることができません。

以下は絶対配置と z-index を使って半透明の領域(.over_layer)を前面(上)に重ねているので、下にあるリンクをクリックすることができません。

<div class="sample_wrapper">
  <div class="over_layer"></div><!-- 上のレイヤー  -->
  <p class="under_layer"><a href="#">Click</a></p><!-- 下のレイヤー  -->
</div>

.under_layer のスタイルは絶対配置で上下左右中央寄せするためのもので、この話題とは関係ありません。

.sample_wrapper {
  position: relative; 
  width: 100px; 
  height: 50px;
}  
.over_layer {
  background-color:rgba(0,0,0,0.1); 
  position: absolute; 
  top: 0; 
  width: 100%; 
  height: 100%; 
  z-index: 10; /* z-index を指定して前面に */
}
.under_layer {
  position: absolute; 
  top: 50%; 
  left:50%; 
  transform:translate(-50%, -50%);
}

上(前面)のレイヤーの要素(.over_layer )に pointer-events: none; を指定するとクリックやタッチなどの操作が無効になり、下のレイヤーの要素をクリックすることができます。

.over_layer {
  background-color:rgba(0,0,0,0.1); 
  position: absolute; 
  top: 0; 
  width: 100%; 
  height: 100%; 
  z-index: 10;
  pointer-events: none; /* ポインターイベントの対象としない */
}

疑似要素のみをクリックできるようにする

以下は button 要素の疑似要素で表示している部分のみをクリックできるようにする例です。

通常はボタン要素全体がポインターイベントの対象ですが、以下ではボタン要素に pointer-events: none; を設定し、疑似要素に pointer-events: auto; を設定しています。

<button class="btn-pe">ボタン</button>
.btn-pe {
  pointer-events: none;
  border: 1px solid #ccc;
  background-color: #CADFFA;
  border-radius: 4px;
  padding: 5px 10px;
  
}
.btn-pe::after {
  content: ' +';
  color: #F5060A;
  pointer-events: auto;
}

以下のサイトを参考にさせていただきました。

Stuff you can do with pointer-events