くっつかない理由を祖先に探す
目標
包含ブロック・スクロールコンテナー・スタッキングコンテキストを、祖先をたどって上がりながら自分で計算します。ブラウザーはありませんが、この3つはマークアップとCSSだけで答えが決まります。
なぜ重要なのか
fixedが固定されずstickyがついてこないバグは、症状が原因をまったく指し示しません。その要素のCSSは完璧に正しく、開発者ツールでその要素だけを見ても、何も問題がありません。見るべき場所は祖先です。
祖先にtransformが1つあると、fixedの基準がビューポートからその祖先に変わります。祖先にoverflow: hiddenが1つあると、そのボックスがスクロールコンテナーになり、stickyが固定される場面がなくなります。カードのホバー効果とモーダルは無関係に見えるため、ルールを知らないと、原因を見つけるまでに何時間もかかります。
このラボの文書
採点ツールと計算ツールが、次の構造を想定します。HTMLは書きません。
div.page
├ header.topbar
└ div.shell
├ aside.side
│ └ nav.toc
└ main.content
└ div.card
├ span.badge
└ div.overlay
作るもの
/root/work/pos/layout.cssの1つだけです。
計算ツール
cd /root/work/pos
python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css
python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css 500
要素ごとに、position・包含ブロック・stickyの判定・z-indexの範囲を出力します。2つ目の引数として画面幅を渡すと、その幅で再計算します。
ステップ
.badgeを.card基準に(絶対位置の包含ブロック).overlayをビューポート基準に(fixedの標準の包含ブロック)transformをどこに置くか:fixedが閉じ込められる落とし穴.topbarをstickyに(しきい値).tocが実際に固定されるように(スクロールコンテナーを選ぶ)z-indexがルートで競うように(スタッキングコンテキスト)- 計算ツールの結果を
07-boxes.txtに - 狭い画面では目次をフローに(順序の落とし穴をもう一度)
- まとめ →
09-notes.md
参考
- ステップ3は、先に
.cardにtransformを入れて計算ツールを実行し、閉じ込められるのを自分で見たあとで、その属性を.badgeに移してください。 inset: 0の1行は、4つの方向をすべて0にするという意味です。- 固定される区間の長さは、親ボックスの高さによるため、計算ツールではわかりません。それは、ブラウザーで見る必要があります。
絶対位置の基準を作る
/root/work/pos/layout.cssを作成し、.badgeをposition: absoluteにして、方向を2つ以上(例: topとright)指定してください。その包含ブロックが.cardになる必要があります。
mkdir -p /root/work/posを実行します。absoluteの包含ブロックは、最も近い位置指定された祖先です。relativeは、自分の位置をそのままにしつつ基準だけを作ってくれるため、この用途で最もよく使われます。
オーバーレイをビューポートに固定する
.overlayをposition: fixedにして、4つの方向をすべて決めてください(inset: 0の1行でも構いません)。z-indexも指定してください。包含ブロックがビューポートである必要があります。
fixedの標準の包含ブロックはビューポートです。そのため、スクロールしてもついてこず、画面に固定されています。4つの方向がすべて決まって初めて、画面全体を覆います。
transformをどこに置くか
.badgeにtransformを指定して、角の外へ少しはみ出させてください。.cardには、transform・filter・perspective・will-change・containのどれもあってはいけません。そして、.overlayの包含ブロックは、引き続きビューポートである必要があります。
まず.cardにtransform: translateZ(0)を入れて、計算ツールを実行してみてください。.overlayの包含ブロックが.cardに変わります。それが、このラボが見せようとしている落とし穴です。確認できたら、その属性を、実際に動かす必要のある要素に移してください。
ヘッダーバーを固定する
.topbarをposition: stickyにして、topを指定してください。計算ツールが「固定される」と判定する必要があります。
しきい値(top・right・bottom・leftのうち1つ以上)がないと、固定される線がないので、そのまま流れていきます。エラーも警告も出ないという点が、このバグを長く生き残らせます。
スクロールコンテナーを選ぶ
.tocをposition: sticky; top: 64pxにし、.pageにoverflow-y: autoを指定して、.pageがスクロールコンテナーになるようにしてください。.shellと.sideには、visibleではないoverflowがあってはいけません。
stickyは、最も近いスクロールコンテナーの中でだけ固定されます。途中のコンテナーにoverflow: hiddenが1つあると、それがスクロールコンテナーになり、ユーザーはそのボックスをスクロールしないため、固定される場面が生まれません。overflow-x: hiddenも、同じ結果になります。
z-indexが通用する範囲
.topbarにz-indexを指定し、.overlayのz-indexをそれより大きくしてください。.page・.shell・.content・.cardのどれも、スタッキングコンテキストを作ってはいけません。
祖先がスタッキングコンテキストを作ると、その中のz-indexは、どれだけ大きくても外へ出られません。opacity: 0.99やtransformのような何気ない1行が、それを作ります。計算ツールが、「z-indexがどのボックスの中で通用するか」も一緒に出力します。
計算ツールで確認する
boxes.pyを実行して、結果を/root/work/pos/07-boxes.txtに残してください。出力に「固定されない」がなく、要素の行が切れずにすべて入っている必要があります。
python3 /opt/lab/checks/css-pos-lab/boxes.py layout.css | tee 07-boxes.txtを実行します。包含ブロックの横のかっこが、なぜそう決まったのかを教えてくれます。祖先の名前と、その属性です。
狭い画面ではフローにする
@media (max-width: 700px)で、.tocをposition: staticに戻してください。広い画面(1280px)では、引き続き固定される必要があります。メディアクエリは、基本ルールの下に置く必要があります。
詳細度が同じなら、あとに書いたものが勝ちます。メディアクエリの中にあるからといって、より強いわけではありません。基本ルールをメディアクエリの下に置くと、メディアクエリは何もできなくなります。boxes.py layout.css 500で、狭い画面を確認してください。
診断の順番を書き留める
/root/work/pos/09-notes.mdに3行以上(120文字以上)書いてください。本文に담음 블록、transform、overflowが含まれている必要があります(韓国語で「包含ブロック」を意味する語を含みます)。シートも、引き続き前の段階の基準を通過する必要があります。
核心は1つです。原因は常に祖先にあります。次に同じ症状に出会ったとき、祖先をたどって上がりながら何を探すのかを、自分の言葉で書き留めておいてください。