吹き出しやリストマーカーに使える三角形を CSS で作る

しばらく CSS ネタ的なの書いていませんでしたのでデザイン内に出てくることの多い三角形のあれを簡単に CSS で作る方法について書いてみたいと思います。汎用性を考慮した mixin も同時に。

特に目新しい方法ではないですが、デザイン内に出てくることの多い三角形のあれ (謎) を簡単に CSS で作る方法について。

しばらく CSS ネタ的なの書いていませんでしたので書いてみようと思います。

三角形のあれというのは、よくリスト型メニューにアイコン的に使われていたり、メインメニューでアクティブな項目に付けたり、Tips などを表示する際に吹き出し的な見た目を作る場合に使われたりする、下記のような部分のことです。

デザインでよく使用される三角形のあれ

これを CSS のみで簡単に再現する方法を解説してみます。CSS は class の付け方でカラーやサイズなどを簡単に適用できるようにしてみましょう。また、今どきっぽく Sass 向けに mixin (ミックスイン) のサンプルも紹介しておきますので参考まで。

CSS による基本的な三角形の作り方

いまさら詳しく解説するまでもないですが、下記のように border プロパティを使用して三角形を作ります。また、::before::after でもいいけど) 疑似要素を使用しています。

ちなみに class 名はとりあえずで付けてますが、汎用性の高い感じにしておくと使い回しするときに便利です。

.module-triangle-right::before {
  content: "";
  display: inline-block;
  border: 8px solid transparent;
  border-left-color: #000;
}

で、下記のような HTML があったとして、

<div>
  右側に三角系がある吹き出しっぽい見た目にしてみる
</div>

これに上の class を付与すると、下記のような感じになりますね。

<div class="module-triangle-right">
  右側に三角形がある吹き出しっぽい見た目にしてみる
</div>

これを実際にブラウザで表示してみると下記のようになります。

三角形の描写まで完了した例

三角形が描写されているのがわかります。ただこのままでは見た目がおかしいので、三角形を配置しやすいように下記のように CSS を書き足してみましょう。

.module-triangle-right {
  display: inline-block;
  position: relative;
}
.module-triangle-right::before {
  content: "";
  display: inline-block;
  border: 8px solid transparent;
  border-left-color: #000;
  position: absolute;
  right: -16px;
  top: 50%;
  margin-top: -8px;
}

ここまでやると、下記のような見た目になります。

三角形の表示位置を調整

right: -16px; としているのは、 border プロパティで指定したサイズが 8px なので、左右の border 分ということで、×2 して 16px に。これで div 要素のボックスに対して右側の Border Edge 上に三角形が配置されます。

縦方向に関しては、top: 50%;margin-top: -8px; を同時に指定することで、div 要素のたてサイズに対して上下中央に配置します。この辺は単なる計算の問題なので詳しい説明も不要かと。

あとは、適当にスタイルを調整すれば、下記のような吹き出しができあがります。今回はサンプルなので色が黒ベタになっていますが、border のカラーやサイズなどを調整すれば様々なタイプの三角形を再現することができますね。

スタイルを調整して吹き出しっぽくした例

なんで三角形になるのか

border プロパティを使った三角形の描写について簡単に理屈だけ解説しておきます。

まず、4辺にそれぞれ異なるカラーの border を設定してみるのが一番わかりやすいんですが、仮に span 要素に下記のようなスタイルを当ててみましょう。

span {
  display: inline-block;
  border: 20px solid;
  border-top-color: #f00;
  border-right-color: #000;
  border-bottom-color: #0f0;
  border-left-color: #00f;
}

4辺に異なるカラーの border を設定した例すると、左図のような状態になると思います。この時点でもう理解できると思いますが、下向きの三角形を作りたければ、border-top-color だけを残して、他を transparent にしてあげれば、この図でいうところの赤い border だけが残り、下向きの矢印になると。

右向き三角形なら border-left-color だけ、上向き三角形なら border-bottom-color だけ、左向き三角形なら border-right-color だけという風に変えてあげればそれぞれの向きの三角形が描写できますし、あとは border-width の値を調整することで三角形のサイズも変えることができます。

リストのアイコンで使うなら border-width は 4px 程度とか、吹き出しで使うならもう少し大きめにとかですね。

再利用しやすい CSS の記述を考えてみる

あとは色々なところで使いやすいようにスタイルを記述しておくと class の付与だけで三角形を表示したりできて楽です。書き方は好みがあると思いますが、例えば上下左右、各方向への吹き出しを作りたいとかなら下記のような感じとか。

[class^="module-triangle-"] {
  position: relative;
}
[class^="module-triangle-"]::before {
  content: "";
  border: solid transparent;
  display: inline-block;
  position: absolute;
}
.module-triangle-top::before {
  border-width: 8px;
  border-bottom-color: #000;
  margin-left: -8px;
  left: 50%;
  top: -16px;
}
.module-triangle-right::before {
  border-width: 8px;
  border-left-color: #000;
  margin-top: -8px;
  right: -16px;
  top: 50%;
}
.module-triangle-bottom::before {
  border-width: 8px;
  border-top-color: #000;
  margin-left: -8px;
  left: 50%;
  bottom: -16px;
}
.module-triangle-left::before {
  border-width: 8px;
  border-right-color: #000;
  margin-top: -8px;
  left: -16px;
  top: 50%;
}

カラーやサイズ、三角形の表示位置に関してはデザインに応じて変更する必要がありますけども、上記のように書いておけば、要素に付与する class 名を変更するだけで、簡単に吹き出しのような見た目のベースを作ることができます。

mixin 書いてみる

で、これだとカラーの変更などに対応するのが面倒なので、下記のような mixin にしておくと楽かも。これも好みによって書き方は調整すればよいと思いますが参考まで。もうちょっとスマートな書き方ができそうだけど・・・・・・

@mixin module-triangle($size: 8px, $color: #000, $side: top, $position: 50%, $direction: regular) {
  position: relative;
  &::before {
    content: "";
    border: solid transparent;
    display: inline-block;
    position: absolute;
    border-width: $size;
    @if $direction == reverse {
      @if $side == top {
        border-bottom-color: $color;
        margin-left: -$size;
        left: $position;
        bottom: -($size * 2);
      }
      @elseif $side == right {
        border-left-color: $color;
        margin-top: -$size;
        left: -($size * 2);
        top: $position;
      }
      @elseif $side == bottom {
        border-top-color: $color;
        margin-left: -$size;
        left: $position;
        top: -($size * 2);
      }
      @else {
        border-right-color: $color;
        margin-top: -$size;
        right: -($size * 2);
        top: $position;
      }
    }
    @else {
      @if $side == top {
        border-bottom-color: $color;
        margin-left: -$size;
        left: $position;
        top: -($size * 2);
      }
      @elseif $side == right {
        border-left-color: $color;
        margin-top: -$size;
        right: -($size * 2);
        top: $position;
      }
      @elseif $side == bottom {
        border-top-color: $color;
        margin-left: -$size;
        left: $position;
        bottom: -($size * 2);
      }
      @else {
        border-right-color: $color;
        margin-top: -$size;
        left: -($size * 2);
        top: $position;
      }
    }
  }
}

初期値とかは適当に変更してください。

あとは下記のように読み込めば OK。

.sample {
  @include module-triangle(4px, #f00, left);
}

コンパイルすれば下記のようになります。

.sample {
  position: relative;
}
.sample::before {
  content: "";
  border: solid transparent;
  display: inline-block;
  position: absolute;
  border-width: 4px;
  border-right-color: red;
  margin-top: -4px;
  left: -8px;
  top: 50%;
}

最初に挙げた黒ベタの吹き出しを作るなら下記のような感じでしょうか。

.sample {
  border-radius: 5px;
  background: #000;
  color: #fff;
  display: inline-block;
  padding: 10px;
  @include module-triangle(8px, #000, right);
}

上の例だと吹き出しっぽく、ボックスの外側に向かって三角形の頂点が向く感じになりますので、リストのマーカーなどで使う (三角形の頂点が内側に向いていた方がいい) 場合は、下記のように、$directionreverse を引数として渡してください。

ul li {
  @include module-triangle(4px, #000, right, 50%, reverse);
}

下記のようにコンパイルされると思います。

ul li {
  position: relative;
}
ul li::before {
  content: "";
  border: solid transparent;
  display: inline-block;
  position: absolute;
  border-width: 4px;
  border-left-color: black;
  margin-top: -4px;
  left: -8px;
  top: 50%;
}

なるべく汎用的にしようとしている結果、上のサンプルでは mixin が冗長な感じになっているかと思いますが、この辺は実際の利用状況にあわせて変えればいいんじゃないでしょうか。

関連エントリー

記事をここまで御覧頂きありがとうございます。
この記事が気に入ったらサポートしてみませんか?