はじめに
スマホ用のメニューバーの作成です。
ボタンを押すと、メニューが現れる、何処にでも良くありそうなものです。
それを勉強の為に、0からSCSSで作ってみたのですが、上手く行かない部分があったため、解決策をメモ書きです。
メニューバー(改良前)
非常にテキトーなMenubarです。実際の動作はこちら。
「■」を押すと、メニューバーが表示されます。
一応ソースコードも載せて置きます。
<nav>
<div class="menubar">
<div>Logo</div>
<label class="viewbutton" for="nav-visible">■</label>
</div>
<input class="open" type="checkbox" id="nav-visible">
<ul>
<li>Menu1</li>
<li>Menu2</li>
<li>Menu3</li>
<li>Menu4</li>
</ul>
</nav>
nav {
display: flex;
flex-direction: column;
}
.menubar {
display: flex;
flex-direction: row;
justify-content: space-between;
height: 3rem;
line-height: 3rem;
background-color: aqua;
.viewbutton {
display: block;
font-size: 2rem;
height: 3rem;
width: 3rem;
text-align: center;
}
}
.open {
display: none;
}
ul {
display: flex;
flex-direction: column;
$li_height: 2rem;
background-color: red;
margin: 0;
height: 0;
overflow-y: hidden;
li {
height: $li_height;
line-height: $li_height;
list-style: none;
}
.open:checked ~ & {
height: $li_height * 4;
}
}
このサンプルのポイントは、下記のコードの部分です。
.open:checked ~ & {
height: $li_height * 4;
}
Menu部分(Menu1~Menu4が表示される部分)の高さは、ul要素で決めています。
これはli要素の個数を、scssに直接書き込まなければならないので、決して良いコードとは思えませんでした。
出来る限り、依存性は無くしたい所です。
li要素でheightを決める
li要素でheightを決めれば良いのでは?、というツッコミがあるかと思います。
実際に作ってみました。transition部分の細かい動作(メニューの非表示時に、文字が潰れた様な表現になる)には気になりますが、これでも良い様には思います。
最大のメリットは、li要素を幾ら増やしても、scssの修正が不要である事だと思います。
ul {
display: flex;
flex-direction: column;
background-color: red;
margin: 0;
li {
$li_height: 2rem;
height: 0;
line-height: 0;
list-style: none;
overflow-y: hidden;
.open:checked ~ & {
height: $li_height;
line-height: $li_height;
}
transition: height, 0.5s;
}
}
ですが、諸事情からどうしても親要素でheightを決めたかった、と言うことです。
メニューの非表示時に、文字が潰れた様な表現になるのも気になりますし……。
javascriptを使えば解決するのでしょうが、ここではそれ以外の解決策を探ってみる事にしました。
解決案
data-*(カスタムデータ属性)をul属性につける事で、liの個数をscssに伝える様にしました。
html上に個数を指定しないとならないのは面倒ですが、scssに直接個数を書くよりはマシな様な気がします。
<ul data-items="5">
<li>Menu1</li>
<li>Menu2</li>
<li>Menu3</li>
<li>Menu4</li>
<li>Menu5</li>
</ul>
ul {
display: flex;
flex-direction: column;
$li_height: 2rem;
background-color: red;
margin: 0;
height: 0;
overflow-y: hidden;
li {
height: $li_height;
line-height: $li_height;
list-style: none;
}
@for $i from 1 through 12 {
.open:checked ~ &[data-items="#{$i}"] {
height: $li_height * $i;
}
}
transition: height 0.5s;
}
@for
の部分がポイントです。
このコードによって、下記コードを出力します。
.open:checked ~ &[data-items="1"] {
height: $li_height * 1;
}
.open:checked ~ &[data-items="2"] {
height: $li_height * 2;
}
.open:checked ~ &[data-items="3"] {
height: $li_height * 3;
}
// 以下省略
そして、data-itemsが一致した部分で、heightを決めています。
このコードでは、data-items="5"
としたので、height: $li_height * 5
となります。
要するに、5行分のheightを設定する、という事ですね。
最後に
素直にjavascript使おうよ、というツッコミはあると思います。
全く持ってその通りです。
ただ、出来る限りデザインはcssで完結させたいというのが、私の考えなんですよね……。
私はプロのhtmlデザイナーでも無ければ、Webプログラマーでも無い、タダの素人なので、この考えが正しいかどうかは解りませんが……。
今回は、scssでこの様なコードを書けば良いのでは、と言うことで記事を書いてみました。
もしかたら、他に良い方法があるのかも知れませんが、私の頭ではこれが精一杯です。
おまけ
css4からは:has
疑似要素と言うのがあるそうで、これを使えば、子要素の条件に基づいて、親要素を読む事が可能だとか。
これを使えば、data-itemsを使わなくともいけそうな気がするのですが、残念ながら、まだどのBrowserも未サポートとの事。