1:導入
例の「CSS完全に理解した」Tシャツ。
色々な方が話している通り、あれを意図的にやろうとすると実はなかなか難しい。
むしろ、「CSS完全に理解した」を再現するコードを書くたびに我々はCSSの完全理解に近づけるんじゃ無いだろうか?
CSS完全に理解したでCSS完全に理解すればもっとCSS完全に理解したできるんじゃ無いか
とにかく何かよくわからない追求心にとらわれた我々(約1名)は「CSS完全に理解した」を理解するために色々な方法でこれを再現することにしました。
以下に「CSS完全に理解した」するための数々の方法について書いていこうと思います。
2:前提
前提条件が限定されていないと無限の「CSS完全に理解した」状態を作り出せることになってしまうので、
今回は以下の画像のように、まず、「CSS完全に理解したわけではない」状態を再現し、ここに手を加えることで「CSS完全に理解した」状態を作り出すことにしました。
この状態でのHTML、CSSは以下の通りです。
つまり、テキストが入ったP要素を、CSSで枠を指定したDIVに入れたシンプルなものです。
HTMLはいじらず、CSSだけいじることでCSS完全に理解する状態にしていきます。
<div class="box">
<p class="box__text">
CSS<br>
完全に理解した
</p>
</div>
.box {
font-size: 16px;
position: relative;
border: 1px solid #000;
border-radius: 5px;
width: 140px;
height: 62px;
}
.box__text {
margin: 10px;
}
※以下のCSSコードでは、付け足す部分のみを記述し、上に書かれた根幹の部分は省略します。
3:実践
基本:P要素の左側に単純な隙間を空けるパターン
まず単純にテキスト部分の左側に隙間を開けることを考えてみます。
これは、padding-left
やmargin-left
使えば可能です。
(※padding-inline-start
やmargin-inline-start
も同様)
.box__text {
margin-left: 60px;
}
.box__text {
padding-left: 50px;
}
しかし、ただ隙間を空けただけだと下の画像のように勝手に文字が途中で改行されてしまいます。
また、position:absolute
とleft
を使用して右側にテキストを移動した場合も、途中で改行されてしまいます。
.box__text {
position: absolute;
left: 50px;
}
これは親要素であるDIVの幅が固定であるためで、それに引っ張られて下層のPタグも幅が制限され、改行されてしまうということです。
そこで、このテキストの自動折り返しをキャンセルする方法を考えなければなりません。
折り返しキャンセル方法1:white-space: nowrap;
文字通り改行を禁止するために使うCSSなので一番ふさわしいと思われます。line-height
などをいじればwhite-space: pre;
でも不可能ではないが、今回は再現性が低いので割愛します。
また、white-space: nowrap;
の代わりにword-break: keep-all;
でも同じ結果を得られます。
折り返しキャンセル方法2:ネガティブマージン
左側に隙間を空けた分だけの幅をmargin-right
のマイナス値で指定すれば、結果全体の幅は保たれるので、自動折り返しをキャンセルすることができます。
折り返しキャンセル方法3:幅を指定する
親の幅に引っ張られているので、自身が幅を持てばそちらを優先することができ、結果折り返しが起こらなくなります。
width
の代わりにinline-size
を利用しても同じ効果を得られます。
(もちろんmin-width
やmin-inline-size
も同様)
以上、「左側に単純な隙間を空ける方法」と「折り返しをキャンセルする方法」の組み合わせでここまでで9種類の「CSS完全に理解した」ができます。
応用:折り返しをキャンセルする必要がないパターン
position:relative
を使う
.box__text {
position: relative;
left: 50px;
}
position: relative;
は元の高さや幅を保ったまま見た目を移動させるため、left
(あるいはinset-inline-start
)を使用しても自動折り返しも起こりません。
position: absolute;
とright
を使用する
position: absolute
の時にleft
を使うと上記のように折り返しが発生しますが、right
(あるいはinset-inline-end
)をマイナス値で指定する方法ならばそれは起こりません。ただ基準がテキストの幅(フォントの種類)に依存するため、正確な位置合わせは難しくなります。
上記のサンプルでmargin-left
とmargin-right
を同時に指定したように、left: 50px;
とright: -50px
を同時に指定したり、あるいはinset-inline: 50px -50px;
を指定したりすればテキストの幅に対する依存は無くなります(そこまでする必要があるかどうかは別として)。
.box__text {
position: absolute;
right: -42px;
}
transform
を使う
transform
は「移動」ではなく「変形」なので、transformでの変形結果は幅や位置などに(見た目以上の)影響を与ええません。このためもちろん自動折り返しも起こりません。
.box__text {
transform: translateX(50px);
}
/* あるいは */
.box__text {
transform: translate(50px, 0);
}
/* またあるいは */
.box__text {
transform: translate3d(50px, 0, 0);
}
ちなみに上記は下記と同義となります。
.box__text {
transform: matrix(1, 0, 0, 1, 50, 0);
}
/* あるいは */
.box__text {
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 50, 0, 0, 1);
}
また、translateXやmatrix以外のtransformでも、下記のように無理やり右に50px移動させることができます。
/* Y軸回転→Z軸移動→Y軸回転 */
.box__text {
transform: rotateY(90deg) translateZ(50px) rotateY(-90deg);
}
/* Z軸回転→Y軸移動→Z軸回転 */
.box__text {
transform: rotateZ(90deg) translateY(-50px) rotateZ(-90deg);
}
応用:flexを無理やり使う。
DIVにdisplay: flex;
を指定しP要素をフレックスアイテムにした場合も自動折り返しが働きますが、この場合は上にあげた方法に加えてflex-shrink: 0;
でも折り返しをキャンセルすることができます。
.box {
display: flex;
}
.box__text {
padding-left: 50px;
flex-shrink: 0;
}
変態:常識にとらわれないパターン
ボックスを一旦消したあと書き直してCSS完全に理解する
とりあえず、枠の幅をautoに指定し、P要素の左に隙間を空けてみましょう。
.box {
width: auto;
}
.box__text {
margin-left: 60px;
}
当然P要素の指定に引っ張られて枠の長さが伸びてしまいます。
そこで、伸びた枠を背景と同じ色に指定した擬似要素で塗りつぶし、新しい枠を作ってしまいます。
その上でP要素が一番上に来るようにz-index
で調整します。
枠を上書きして塗りつぶす以外にborder-color: transparent;
で枠を透明にする方法もあります。
.box {
width: auto;
}
.box::after {
content: '';
position: absolute;
top: -1px;
left: -1px;
background-color: #fff;
width: 221px;
height: 64px;
z-index: 1;
}
.box::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
border: 1px solid #000;
border-radius: 5px;
width: 140px;
height: 62px;
z-index: 2;
}
.box__text {
position: relative;
margin-left: 60px;
z-index: 3;
}
インデントを二重に指定してCSS完全に理解する
親のDIV要素にtext-indent
を指定し、P要素をインラインブロック要素にすると、親要素のtext-indentの影響をブロック全体で受けるようになるため、P要素はブロックごと右にずれることになります。しかしtext-indentはP要素の中にまで継承されるので、このままでは一行目の「CSS」がさらに右へずれてしまいます。そこでP要素でtext-indent
をゼロに指定してこれを打ち消せば、思惑通りの見た目を得ることができます。
.box {
text-indent: 50px;
}
.box__text {
text-indent: 0;
display: inline-block;
}
影文字で見た目を騙してCSS完全に理解する
本来の文字をcolor: transparent;
で透明にし、text-shadowを使ってぼかしの無い影文字を50px右に表示させることにより、スマートに枠からはみ出した文字を再現することができます。
.box__text {
color: transparent;
text-shadow: 50px 0 0 #000;
}
transformのscaleで幅を突破しCSS完全に理解する
前述したようにtransformの変形結果は幅や位置などに影響を与えません。
そこでまずは、font-size
を半分のサイズにすることで、折り返しが起こらない状況を作ります。
こうした上で、transform: scale(2);
で大きさを戻すことで折り返しを回避することができます。
.box__text {
font-size: 8px;
padding-left: 25px;
transform-origin: 0 0;
transform: scale(2);
}
この時transform-origin
で始点を左上にしてもpadding-left
はその範囲に入るので、移動させる距離である50pxの半分である25pxを指定します。
transformのrotateYで2回反転しCSS完全に理解する
まず枠全体を180度Y軸回転させます。
当然文字が裏返りますが、この状態で中のP要素のみを中心軸を変えてもう一回反転させれば、文字の裏返りが直り、なおかつ枠の外にはみ出させることができます。
.box {
transform: rotateY(180deg);
}
.box__text {
transform-origin: 35px 0;
transform: rotateY(180deg);
}
裏返っていることでtransform-origin: 0 0;
は右上をさすことになり、幅いっぱい(120px)分右にずれるので、ここから動かす50pxを引いた上での半分((120-50)/2)つまり35pxを指定すればちょうどの位置にずらすことができます。
グリッドシステム完全に理解したからCCSも完全に理解する
せっかくなのでグリッドシステムを利用した方法も考えてみました。
グリッドセルを異なるグリッドアイテムで共有できることを利用した方法です。
親の枠を消し、before疑似要素と.box__text
要素をグリッドアイテムとします。
縦方向のグリッドは
grid-template-rows: 11px auto 11px;
となります。11px
は枠線の太さと枠線と.box__text
要素間の隙間を足した数値です。
横方向のグリッドは
grid-template-columns: 11px 50px 68px 11px auto;
となります。50px
が右にずらす数値、68px
は余り、auto
ははみ出す部分です。
こうして作ったグリッドにbefore疑似要素と.box__text
要素を指定の位置に当て込めば完成です。
.box {
width: auto;
display: grid;
grid-template: 11px auto 11px / 11px 50px 68px 11px auto;
border: 0;
}
.box::before {
content: '';
grid-area: 1 / 1 / 4 / 5;
border: 1px solid #000;
border-radius: 5px;
}
.box__text {
margin: 0;
grid-area: 2 / 3 / 3 / 6;
}
鬼畜の所業!SVGをCSSに入れ込んでCSS?完全に理解する
まず、「CSS完全に理解した」と全く同じ見た目になるようにSVGを作ります。
<svg xmlns="http://www.w3.org/2000/svg" width="202" height="64" viewbox="0 0 202 64">
<style>
text {
font-family: sans-serif;
}
</style>
<rect x="0.5" y="0.5" width="141" height="63" rx="4.5" ry="4.5" stroke="#000" stroke-width="1" fill="transparent"/>
<text x="0" y="50" font-size="16">
<tspan x="61" y="24">CSS</tspan>
<tspan x="61" y="43">完全に理解した</tspan>
</text>
</svg>
cssのborderとsvgのstrokeでは線の中央位置が違うのでうまく調整しないと同じ見た目になりません。
SVGができたら、これをbase64に変換し、疑似要素のcontentとして指定します。
あとは元からあった枠を透明に、P要素は非表示にすれば完成!
.box::before {
content: url(data:image/svg;base64,iVBORw0KGgoAAAANSUhEUgAAAMoAAABACAYAAABbTugWAAAJ5UlEQVR4nO2d74tVxxnHP+muqa6/1iZp6tpYWw2bWlswtWkwIbUmSMR1lbQSpRo0/ZXUBG1rE7c10SJptaghkY24aLaxSFHCUoIsEpDAIiws+KIv8kLKEoKYsG/vP3D74jvDmXv2nL337ln3XveZDwx7z5k5c+a593nm9zwLCZ3AB8CXQDmGGILwBdAPPIxxVgL/A34CfKPBZYk0H4uBdcAoqlBN0omMJBKphc+A5Y0uRCP4ALUkkUgtrAfeb3QhGsGXxO5WpHY6gNuNLkQjKDe6AJG7DpM6Y1LoSCFM6oxJoSOFMKkzJoWOFMKkzpgUOlIIkzpjUuhIIUzqjEmhA2YD24C/AmeBPwFP5KRtAba4tH3An4E1OWnnAS8Cx4EzwMvAsqkqdIMxqTMmhXb8ELiJvoP/AgNom0YZuIiU3TMfGHFxY8CVIO0/gHuCtKuAkov7FLgaXG+7Y9JMHyZ1xqTQyAhuIUNZmYrbjb6Xfwb33nL31pMYRQtw1N3/WZB2xOX7neBeOzKYMvDNKSh/IzGpMyaFRl2iMvCtnPg3XXyHux4C/pORrhW4AfzBXc92z+3LSLsKtVzrJ1fkpsGkzpgUGnWfeieInwssCK4voBZobo15X6KyOzaTMKkzFoVuR3LvquOZJ9wzo6hrtoJ8Q/i9S3sV6Gbm7aWzqDMmhV6D5K531/QaNIj3B5pKwHvAdzPSPo+6ZD7tKOrOfW1yRW4qLOqMSaFXILm3TPL5NuBJND18y+W1Oift/cAm4G2X7hYwZ5LvbRYs6oxJoVuR3AeqpGmrIa/7kPKfrCHtY+693TWkbWYs6oxNoYFh1B2alRP/NPpu1gFLgOvA9py0/0LrJQDPoi7Xipy0JeBU/cVtKkzqjEmhkQGU0Sp7elDejgyphFqVVvf5GuMNqw0Z3IC7ftTlezjjnZ0ubm/h0jcWkzpjUmjHGST/FWAn8BTwElL8EpXrHXtdWt+yrAMOoXWRcirth+5eP+pmbUStyJjLd/Edkme6MKkzJoUO2EGi7H57ymWyHShsQ4YSuvIZBjak0rUCfyHZ4uLDZeCRKZdg+jGpMyaFzmAWGpjXQhvwANrCUo2FzIwp4RCTOmNS6EghTOqMSaEjhTCpMyaFjhTCpM6YFDpSCJM6Y1LoCdjL+FmselgL/GCKyjIdnKJ+59t3Qme+egfynFKioVQyDLw2yWd3kUwFr60hfStwGi1S1sMq99xJ4IT724uUfhCtC30UhCvu/pFUPj92ZX0ZTZOvBh5HOwuGUmEY7YqGSp15DB1BKLI25M/wNPXpT6uG8gY6iPVhKpTQdpT0/QEm3nri93H9EXgXrcdU8/z+PfdM3rn7PB5B6zSn3fOvAweRUZxBi5y/JdlX1o222VxI5TOI1oX6XNo+YBHaVb3J3duMDOpj9x6o1Bmf7oE6ZQhZ5vJo6pbYqqFcRAuAO2sM54FPcvLyZ1X60XaYWcgIS+Q7qgDVoGXUsgD0oG37fwf+BryDlPsGMuw0q907PAPAb9xnv0Pa0+PK5+l2z7aTGOzSIH4fOs7suQ78wn0O893iruvtOi1w7+0Afu7y2Aq8glrJpsOyofSgE4tt6IfOCm3ofP1OxhtKK6ply6h2b0nFnXNxbwD3ZpThCKqpPbuQQV5yoR+1YgeAH2U8n2UoQ+iYs3/3cReukxjKShc3gva6jaAd0CfQNh5vwF2ohfyVu/YtZKgzPm29pzk3UrlrwZ/vuY4qiabDqqHsRzVYP+N/sHQ4icYcbwXP/xQp2Bj60fN4zqX51H0OjWkAtRqTJctQLqEJiSMkGzD3onGKN5TFqNs1hgyhTLJvzW/R2e3S9qOWN9zIGerMTianQy2om3cfqizOpeJWoP1zW9EhuK5JvGNKsWoonkWo+7EgJyxCropCnkff2zk0JhhCrU1WGEItV6975s0gnxEmt5O4g6QG9n/PUV/Xaz3q0uGeX4ha2T1ozNSL5M46lBbmu4dKY60Xfzbol+7a77AO99750FCsGspGNBDdWGPYRFKrtaDZIVBL8xyq+baiWrk/uN6O/IfhnlkYlOEmSb/f0w08WKXsX0FTuhvQ7/cQMp6P0NhoP4l7pf0uDFI5mPfnbbxfM7+B8wXUnSqRuG06iJz57XHPhjrzEtWV+OsTxD3u8vMuo+ahCYSmc+lk1VAGUY3upz9LLgyR7Ca+HsT7tNW4SjKNWo0xKo8j++nadTU+n+567UdduVMkXcpTLryLZsI8G5Ccy0hmnK4g47iGWkOfxwCaNTvtng115hU0vpmIPiq7ViE96Htoeo81Vg0lzUXk8RGkgGVq2x3sa9rdLoyicYK/9nFZjKEW5Aby1HKZfIXKIm0oIemuV5ouZPzzXB5L3PvPuGu/47nM+NOaYb77mdhQZiE5j+XEDyFD8mxAjgebznCiocjZQwkpPdRnKBdQjZu3DjOAFDCLUWQYN4PrnjrK7cvZ5fI5jwbGV0kG5Vdd+JhKB347yJ64OIsM3bdEftr7bZf/96nUmReZeIzyKpVdq5ClLu5Zd30PMrpDVeRuCNFQVCuWkccUyDeUg1SOMbKop+vl3Rl5JxdnkdI9QzJOaUXeLHdQOanQiVpAP5h/FY07NqPx1K9d3CYXNqMxk+c1Kgf3oFb1OJpwOEAyI3bCXfeQDLY9fsFxKePxU8ATdbtKJFPn95MskjYd1g1lO/oOwv57lqG0uHvVTijWYygXqVyR9gPyvGlqn66LZB1kF9nuj6p1vS5QOQPny/NC6l61rtdc1LX6BLUas9Ei6xGX7hqaVUwzxz2XXly8iVqVfcjwn0ILksfId+4xLVg0lFZU23mlfJ3x3uj9Yt3v0ID1srs334VLqG/dmwpjqKVI3+8D/o0UyXPE5ZlejHwQLTBuQPvAlqTSzGf8lO1hVEMfdMH7ETsYhENoomA+qsn95s9H3fuG0LpISJnxmybTOrOWxL9ZuHh4mGTXQZo9VC5iehaj72s4yGs0p2zTikVDaUcKPUgyzRvSipxun0e1rO+jP+ni56BZpKOoVq4lHEWzRqGvsIdIpo6L8h7qih2fIJxCXbDlyJi9R5ljSBmvUfk/XPwaR7qMWTozC7W2z7g8qg3G7+Uuc1Zu0VCgNmfbM5laJipANXy6xTOpMyaFjhTCpM6YFDpSCJM6Y1LoSCFM6oxJoSOFMKkzJoWOFMKkzpgUOlIIkzpjUuhIIUzqjEmhI4UwqTMmhY4UwqTOmBQ6UgiTOmNS6EghTOqMSaEjhTCpMyaFjhTCpM6YFDpSCJM68wV3/z/fjEwfHcDtRheiEfRTu3ucSORp6vMUM2N4GJ1ui0Rq4XPg240uRKPoBD5DRzM7GlyWSPPRgVqSzxnvaMIcy4H3Uf8zzwtIDDbDbdTdMtuS/B82JMKF/qPrCwAAAABJRU5ErkJggg==);
position: absolute;
top: -1px;
left: -1px;
border: none;
}
.box__text {
display: none;
}
これはCSSと言えるのか。。。。。?
4: まとめ
以下に上記で解説した、数々の「CSS完全に理解した」解法をのせました。
See the Pen CSS完全に理解したでCSS完全理解する by ichimonzi (@ichimonzi) on CodePen.
おそらくこれ以上の変態の解法もあるものと思われますので、変態の皆さんはよろしければ教えてください。
皆さんのさらなるCSSの完全理解をお祈りしています。