角の丸い吹き出しを作る
今日はCSSのみで角の丸い吹き出しを作ります。
一般的にCSSで作成される角が丸い吹き出しというと文字を囲む箱部分の角だけが丸い物が多いのですが、今回はさらに飛び出し部分の角も丸く
なおかつ、ボーダーとシャドウにも対応させてみましょう。
一般的な吹き出しを作る
吹き出しを作る
まずは飛び出し部分の角が尖っていない一般的な吹き出しを作ってみます。
HTMLは次のとおりです。
このHTMLの balloon
部分を吹き出しにします。
<!DOCTYPE html>
<html>
<head>
<title>Hello CSS balloon</title>
<link rel="stylesheet" href="css.css" />
<meta charset="UTF-8" />
</head>
<body>
<h1>Hello! balloon</h1>
<div class="balloon">
Hello! CSS balloon.
</div>
</body>
</html>
吹き出しの矩形部分は background
と border
を使用して作ります。
border-radius
を使用することで角を丸めます。
body{
-webkit-text-size-adjust: 100%;
color: black;
background-color: white;
}
.balloon{
background-color: #F0F0D0;
display: inline-block;
border-radius: 8px;
padding: 32px;
border: 2px solid #666;
position: absolute;
box-shadow: 0 4px 4px #000;
}
飛び出し部分を作る
吹き出しの飛び出し部分の三角を取得するためには、after要素を使って要素を作り次のように高さと幅がゼロの要素に太さのある罫線をつけると各辺がそれぞれ二等辺三角形となるので、
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: red green blue orange;
border-width: 40px;
}
3辺を透明(transparent
)にして一辺を使用する方法が一般的です。
bottom にborder-widthの倍のサイズを指定することで飛び出しの位置が吹き出しの下に一致します。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: red transparent transparent transparent;
border-width: 40px;
bottom: -80px;
}
この方法の問題点
これで色を調整すれば一般的な吹き出しを作ることができますがこの方法は問題があります。
それは、飛び出し部分を丸めることができないのと、飛び出し部分に影がつかないことです。
試しに飛び出し部分に border-radius
と box-shadow
を適用してみましょう。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: red transparent transparent transparent;
border-width: 40px;
bottom: -80px;
border-radius: 10px;
box-shadow: 0 4px 4px #000;
}
このように、影があらぬところにつけられてしまい、飛び出しの角もまるまっていません。
よく見ると底角の左右が丸まってしまっています。 border-radius
や box-shadow
が元の矩形に対して適用されてその一辺を利用しているために発生する現象です。
飛び出しを丸めた吹き出しを作る
頂角が丸まった二等辺三角形を作る。
ということで、ここからが本題です。先程の一辺を使う方法だと角が中心部分となってしまい飛び出し部分を丸めることができませんでした。
そこで、2辺を使うことで矩形の角を頂角として、頂角がまるまった二等辺三角形を用意します。
そのためにまずは、transparent
の位置を変更して右辺と底辺を残すようにします。
また影を右下に落ちるようにX座標にもY座標と同じだけ影を移動します。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent green blue transparent;
border-width: 40px;
bottom: -80px;
border-radius: 10px;
box-shadow: 4px 4px 4px #000;
}
等辺側に影がついているのも確認できます。
底角は尖っていてほしいので border-radius
を右下だけに適用します。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent green blue transparent;
border-width: 40px;
bottom: -80px;
border-radius: 0px 0px 10px 0px;
box-shadow: 4px 4px 4px #000;
}
これで頂角だけが丸まった二等辺三角形を得ることができました。
めでたしめでたし
いや、めでたくありません
位置を合わせる
まだ吹き出しの位置がおかしく、向きもあらぬ方向を向いています。
これを調整するためにCSSの transform
を使用します。
transform
を使用することで、要素を変形したり回転させることができます。
今回は45度回転させたいので rotate(45deg);
を指定します。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent green blue transparent;
border-width: 40px;
bottom: -80px;
border-radius: 0 0 10px 0;
box-shadow: 4px 4px 4px #000;
transform: rotate(45deg);
}
位置は上辺と右辺が表示されない分上向きに座標を動かして上げる必要があります。
bottom
の数値を border-width
分だけ加算します。
もともと bottom
の値は border-width
* -2 の値だったのでちょうど boder-width
* -1 の値となります。
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent green blue transparent;
border-width: 40px;
bottom: -40px;
border-radius: 0 0 10px 0;
box-shadow: 4px 4px 4px #000;
transform: rotate(45deg);
}
飛び出しを細くする
これで角の丸まった飛び出しを作ることができましたが、飛び出し部分をもっとスリムにしたいこともあると思います。
これも transform
で実現可能です。
scaleX()
を使用することで左右の幅を調整できます
左右幅を半分にしたければ 0.5 を渡して
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent green blue transparent;
border-width: 40px;
bottom: -40px;
border-radius: 0 0 10px 0;
box-shadow: 4px 4px 4px #000;
transform: scaleX(0.5) rotate(45deg);
}
飛び出し部分を細くすることが出来ます。
飛び出し部分に色を付ける
ここまでは分かりやすいように青と緑の色を付けてきましたが、ちゃんと吹き出しの一部になるように色を付けます。
矩形側の background
と同じ色を 飛び出しの border-color
のうち右と下につけるので
.balloon {
background-color: #F0F0D0;
display: inline-block;
border-radius: 8px;
padding: 32px;
border: 2px solid #666;
position: absolute;
box-shadow: 0 4px 4px #000;
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent #F0F0D0 #F0F0D0 transparent;
border-width: 40px;
bottom: -40px;
border-radius: 0 0 10px 0;
box-shadow: 4px 4px 4px #000;
transform: scaleX(0.5) rotate(45deg);
}
としてもいいのですが、この方法だと #F0F0D0
を3箇所に記載する必要があり、吹き出しの色を変えたくなったときに面倒です。
飛び出し部分に色を付けた。 うっすらと線が残っている部分はこの後で消えます。
css ではvar関数を使うことで変数のように値に別名をつけることができるので、それを利用します。
--background-color: #F0F0D0; /* 吹き出しの色 */
のように指定するとその他の場所ではvar関数を使って
background-color: var(--background-color);
のように値を取得することが出来ます。
.balloon {
--background-color: #F0F0D0; /* 吹き出しの色 */
background-color: var(--background-color);
display: inline-block;
border-radius: 8px;
padding: 32px;
border: 2px solid #666;
position: absolute;
box-shadow: 0 4px 4px #000;
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
border-width: 40px;
bottom: -40px;
border-radius: 0 0 10px 0;
box-shadow: 4px 4px 4px #000;
transform: scaleX(0.5) rotate(45deg);
}
飛び出しのサイズも指定しておきます
--pick-size: 40px; /* 飛び出しのサイズ */
同様に、同じような指定をしている場所を見ていくと、影の色と大きさも同じなので、それぞれ次のように指定します。
--shadow-size:4px; /* 影の長さ */
--shadow-color: #000; /* 影の色 */
飛び出しの border-width
と bottom
も正負が逆なだけの同じ値です。
このような場合には calc()
関数を使用して calc(var(--pick-size)*-1);
とすることで値を計算することが出来ます。
.balloon {
--background-color: #F0F0D0; /* 吹き出しの色 */
--shadow-size:4px; /* 影の長さ */
--shadow-color: #000; /* 影の色 */
--pick-size: 40px; /* 飛び出しのサイズ */
background-color: var(--background-color);
display: inline-block;
border-radius: 8px;
padding: 32px;
border: 2px solid #666;
position: absolute;
box-shadow: 0 var(--shadow-size) var(--shadow-size) var(--shadow-color);
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 10px 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(0.5) rotate(45deg);
}
飛び出し部分にボーダーをつける
今回では吹き出しにボーダーがついていますが、飛び出し部分にはボーダーがついていません。
分かりやすいようにボーダーの色を赤にしてみます。
ボーダーの色は飛び出し部分でも使うためこちらも
--border-color
と --border-width
として値を設定しておきます。
.balloon {
--background-color: #F0F0D0; /* 吹き出しの色 */
--shadow-size: 4px; /* 影の長さ */
--shadow-color: #000; /* 影の色 */
--border-width: 2px; /*線の太さ */
--border-color: red; /*線の色 */
--pick-size: 40px; /* 飛び出しのサイズ */
background-color: var(--background-color);
display: inline-block;
border-radius: 8px;
padding: 32px;
border: var(--border-width) solid var(--border-color);
position: absolute;
box-shadow: 0 var(--shadow-size) var(--shadow-size) var(--shadow-color);
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 10px 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(0.5) rotate(45deg);
}
飛び出し部分にボーダーがついていないですし、よく見るとなんだか薄っすらと上方向にも影が漏れてしまっていますね
これも合わせて修正します。
CSSでボーダーをつけるにはborder
を使うのですが、こんかいはそもそも飛び出し部分自体が border
で実装されているためこれにさらなる border
を追加することが出来ません。
そこで、また疑似要素をもう一つ用意します。
::after
をコピーして ::before
を作り、こちらで罫線と影の処理を行います。
.balloon::after {
--pick-size: 40px; /* 飛び出しのサイズ */
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 10px 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(0.5) rotate(45deg);
}
.balloon::before{
--pick-size: 40px; /* 飛び出しのサイズ */
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 10px 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(0.5) rotate(45deg);
}
重複している部分のうち、::before
の --pick-size
指定は削除します。
また、影は罫線がついたサイズに対して当てたいため ::after
側の box-shadow
も削除します
飛び出し部分の丸めと幅はbeforeとafterでそれぞれ記載したくないため、こちらも var()
で pick-radius
と pkck-thin
としてまとめます
すると次のように出来ます。
.balloon {
--background-color: #F0F0D0; /* 吹き出しの色 */
--shadow-size: 4px; /* 影の長さ */
--shadow-color: #000; /* 影の色 */
--border-width: 2px; /*線の太さ */
--border-color: red; /*線の色 */
--pick-size: 40px; /* 飛び出しのサイズ */
--pick-radius: 10px; /* 飛び出しの丸め */
--pick-thin: 0.5; /* 飛び出しの幅 */
background-color: var(--background-color);
display: inline-block;
border-radius: 8px;
padding: 32px;
border: var(--border-width) solid var(--border-color);
position: absolute;
box-shadow: 0 var(--shadow-size) var(--shadow-size) var(--shadow-color);
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 var(--pick-radius) 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
.balloon::before {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 var(--pick-radius) 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
before の色をボーダーの色に合わせて影を作ります。
.balloon::before {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--border-color) var(--border-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 var(--pick-radius) 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
::before
のサイズを border-width
の2倍分大きくします
.balloon::before {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--border-color) var(--border-color) transparent;
bottom: calc(var(--pick-size)*-1);
border-width: calc(var(--pick-size) + var(--border-width) * 2);
border-radius: 0 0 var(--pick-radius) 0;
box-shadow: var(--shadow-size) var(--shadow-size) var(--shadow-size) var(--shadow-color);
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
ボーダーの位置が右上に発生してしまっているため ::border
を真下に来るように left
と bottom
の位置を調整します。
left
については飛び出しの位置を調整できるようにこれも var()
で pick-left
として定義します。
.balloon {
--pick-left: 10px; /* 飛び出しの位置 */
}
.balloon::after {
left: var(--pick-left);
}
.balloon::before {
bottom: calc( (var(--pick-size) + var(--border-width) * 2) * -1);
left: calc(var(--pick-left) - var(--border-width)*2);
}
細部を整える
これでひとまず、飛び出しにボーダーをつけることが出来ましたが、
よく見ると飛び出し部分の影が矩形部分より存在感が薄くなっています。
また、飛び出しの矩形から少し上側にも影が漏れていたり、飛び出しの先端でボーダーが太くなっていたりして不格好です。
影は、斜めに展開しているため、ルート2倍に影が伸びたのに対して横幅を半分にしているため、おおよそ1.4倍影の位置を下げます。
.balloon::before{
box-shadow: calc(var(--shadow-size) * 1.4) calc(var(--shadow-size) * 1.4) var(--shadow-size) var(--shadow-color);
}
角のトガリを抑えるには .balloon::before
の border-radius
を大きくしてあげればOKです。
.balloon::before{
border-radius: 0px 0px calc(var(--pick-radius)*1.5) 0px;
これで角を自然に丸めることができるようになりました。
影の位置がずらしたことで上への影漏れも目立たなくなっています。
完成
最後に細かな色や影を整えて出来上がり
body {
-webkit-text-size-adjust: 100%;
color: black;
background-color: white;
}
.balloon {
--background-color: #F0F0D0; /* 吹き出しの色 */
--shadow-size: 2px; /* 影の長さ */
--shadow-color: rgba(0, 0, 0, 0.3); /* 影の色 */
--border-width: 2px; /*線の太さ */
--border-color: #808040; /*線の色 */
--pick-size: 40px; /* 飛び出しのサイズ */
--pick-radius: 10px; /* 飛び出しの丸め */
--pick-thin: 0.5; /* 飛び出しの幅 */
--pick-left: 10px; /* 飛び出しの位置 */
background-color: var(--background-color);
display: inline-block;
border-radius: 8px;
padding: 32px;
border: var(--border-width) solid var(--border-color);
position: absolute;
box-shadow: 0 var(--shadow-size) var(--shadow-size) var(--shadow-color);
}
.balloon::after {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--background-color) var(--background-color) transparent;
left: var(--pick-left);
bottom: calc(var(--pick-size)*-1);
border-width: var(--pick-size);
border-radius: 0 0 var(--pick-radius) 0;
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
.balloon::before {
content: '';
position: absolute;
width: 0;
height: 0;
display: block;
border-style: solid;
border-color: transparent var(--border-color) var(--border-color) transparent;
bottom: calc( (var(--pick-size) + var(--border-width) * 2) * -1);
left: calc(var(--pick-left) - var(--border-width)*2);
border-width: calc(var(--pick-size) + var(--border-width) * 2);
border-radius: 0px 0px calc(var(--pick-radius)*1.5) 0px;
box-shadow: calc(var(--shadow-size) * 1.4) calc(var(--shadow-size) * 1.4) var(--shadow-size) var(--shadow-color);
transform: scaleX(var(--pick-thin)) rotate(45deg);
}
ということで、今回はCSSを使って吹き出しを作ってみました。
本来のCSSの使い方とは異なるため程々に楽しむのがいいかなと思います。