スマホアプリの下のタブバーを作り直していたときの話です。真ん中に大きい「+」ボタンが乗っている、よくあるやつですね。
「+をもう少し上に出したい」と言われたので、margin-top の値を振って3パターン並べて見せました。そうしたら3つとも同じに見えると言われて、実際に自分で見比べても同じでした。
原因が分かってみると、CSSの仕様どおりの動きでした。同じところで悩む人がいそうなので残しておきます。
こういうHTMLでした
<nav class="tabbar">
<a class="tab"><svg>…</svg><span>フィード</span></a>
<div class="fab-tab"><button class="fab">+</button><span>投稿</span></div>
<a class="tab"><svg>…</svg><span>マイページ</span></a>
</nav>
.tabbar {
display: flex;
align-items: flex-end; /* ← ここが大事 */
justify-content: space-between;
}
.fab-tab {
display: flex;
flex-direction: column;
justify-content: flex-end; /* ← ここも */
}
.fab {
width: 64px; height: 64px;
margin-top: -26px; /* ← これを -30px, -34px …と振ってみた */
}
margin-top をマイナスにすれば上にはみ出す、という発想です。これが効きませんでした。
なぜ動かないのか
justify-content: flex-end は「中身を下端に寄せて詰める」という指定です。
このとき、要素は下から順に積まれます。.fab-tab の中は下から「ラベル」「+ボタン」の順です。
ここで margin-top が何をするかというと、**「+ボタンより上の余白」**です。つまり:
┌─────────────┐ ← コンテナの上端
│ │
│ (余った空間) │ ← margin-top をマイナスにすると、ここが増える
│ │
│ +ボタン │
│ ラベル │
└─────────────┘ ← コンテナの下端(ここに寄せられている)
下端に寄っているので、上に余白が増えても下の位置は変わりません。 +ボタンとラベルの距離も変わりません。
じゃあ margin-top は何をしていたのかというと、「この要素が場所を取る高さ」を縮めていました。中身の位置ではなく、外側の箱のサイズが変わっていたわけです。
だから「タブバー全体が少し低くなる」という変化は起きていて、でも「+の位置」は1pxも動いていなかった、というのが正体でした。
動かしたいときは position
.fab {
position: relative;
top: -26px; /* これは実際に動く */
}
position: relative の top は描画位置だけをずらす指定で、レイアウトの計算には影響しません。まさに「見た目だけ上にずらしたい」ときのためのものです。
transform: translateY(-26px) でも同じことができます。こちらのほうが再描画のコストが低いので、アニメーションさせるなら transform のほうが向いています。
この性質、じつは使えます
「動かない」と分かってガッカリしたのですが、そのあとすぐ役に立ちました。
次に言われたのが「+ボタンを大きくしたい。でもタブバーの高さは変えないで」でした。素直に width と height を大きくすると、その分タブバーも一緒に伸びてしまいます。
ここで margin-top の出番です。
.fab {
width: 64px;
height: 64px;
margin-top: calc(38px - 64px); /* = -26px。アイコンと同じ高さぶんしか場所を取らせない */
}
「場所を取る高さだけを縮める」という性質そのものなので、+の直径をいくら変えてもタブバーの高さは変わらず、はみ出す量だけが増えます。左右のアイコンが38pxなので、そこに揃えているわけですね。
まとめ
| やりたいこと | 使うもの |
|---|---|
| 要素を動かしたい |
position: relative + top / transform: translateY()
|
| 要素が場所を取る高さを変えたい |
margin(負の値) |
下揃え(flex-end)や右揃え(justify-content: flex-end)のときは、この2つがはっきり別物になります。
「値を変えているのに見た目が変わらない」と思ったら、そのプロパティが本当に位置を動かすものなのかを疑ってみるといいかもしれません。自分は3回くらい値を振り直してから、ようやく気づきました。