テレビのアニメはどこから来る? / 第66話

第66話:イトは、全部の文字を32pxにしなかった|CSSは誰に何を当てる?

ピコルート第66話。イトが全要素への大きなCSSを止め、セレクタ・宣言・カスケードを読み、PCとスマホのはみ出しを両方直します。

CSSのセレクタプロパティと値カスケードと適用ruleviewport別のレスポンシブ確認11分更新

第65話で、イトは六つのopenを一括置換しなかった。

現在の状態に属する四つだけをclosedへ変え、次に開けるactionは残した。

HTMLの親子関係も、attributeも、link先も合っている。

見学者用screenへの切替にも間に合った。

けれど、廊下から画面を見ると、状態とactionの境目がほとんど分からない。

白い背景。

同じくらいの黒い文字。

狭い余白。

閉じた屋根の画像と二つのactionが、縦に押し合っていた。

最初の見学groupがラボへ来るまで、あと四分。

入口のQR codeから、同じpageをスマートフォンで開く予定にもなっていた。

全部を大きくすれば読める?

イトはCSS editorを開いた。

「小さいから読みにくいんだ。全部、大きくすればいい」

最初のruleを入れる。

* {
  font-size: 32px;
  min-width: 640px;
}

*は、ここではすべてのelementへmatchするuniversal selectorとして働く。

font-sizeはproperty。

32pxはvalue。

min-widthにも640pxを指定した。

幅960pxのpreviewでは、文字が大きくなった。

状態も目立つ。

actionも横一列に並んだ。

イト

ほら、desktopなら一気に読みやすくなったよ

ユイ

入口ではスマートフォンでも開きます。そちらのpreviewは見ましたか

イト

同じCSSなんだから、文字は同じように大きくなるはずだよ

イトは幅360pxのpreviewを開いた。

pageの右側が、screenの外へ続いていた。

640pxより細くなれないelementが、360pxのviewportへ押し込まれている。

横方向のoverflowは280px。

現在状態は見えたが、右側のopen roofとrefresh statusはscreen外だった。

指で横へ送らなければ、actionの存在さえ分からない。

desktop snapshotの成功は、mobileの成功ではなかった。

三分前の三択

残り三分十二秒。

CSS previewに三つの案が並んだ。

A. desktopが読めるので、そのまま採用する

ラボの大きなscreenだけなら整って見える。

QRから開く360px pageは、actionがviewport外へ残る。

B. 競合するruleへ!importantを足し、全部の大きさを押し通す

別ruleに負けにくくはなる。

しかし、全elementを640px以上にする判断そのものは変わらず、mobile overflowも消えない。

C. global ruleを閉じ、役割ごとにselectorを分けて二つのviewportを試す

どのelementへ、何のpropertyを当てるかを選び直す必要がある。

競合するruleはcascadeで勝ったdeclarationを確認し、960pxと360pxの両方を再計測する。

ユイはpreview幅を二つ並べただけで、案を選ばなかった。

ピコもCを光らせなかった。

イトはdesktopだけの大きな文字と、mobileで消えたactionを見比べた。

「Cにする。全部に当てたruleを止めて、誰に何を当てるか分ける」

イトは*のruleを削除した。

selectorは対象を選ぶ

HTMLの骨組みは変えなかった。

状態cardには、見出し、状態text、画像、action groupがある。

イトはCSS側から、その役割へmatchするselectorを置いた。

.status-card {
  display: grid;
  gap: 16px;
  padding: 24px;
  max-width: 720px;
}

.status-card strong {
  color: #164fb8;
  font-size: 1.5rem;
}

.status-card .actions {
  display: flex;
  gap: 12px;
  min-width: 0;
}

.status-cardは、同じclassを持つelementへmatchする。

.status-card strongは、そのcardの中にあるstrong elementへ対象を絞る。

.status-card .actionsは、action groupへ対象を絞る。

selectorは見た目そのものではない。

どのelementへruleを結びつけるかを決める条件だった。

状態を目立たせるruleを、見出しやlink先へまで広げない。

actionを並べるruleを、画像へまで当てない。

HTMLの意味を変えず、presentationの対象を選べる。

declarationをpropertyとvalueに分ける

一つのblockの中には、declarationが並ぶ。

たとえばgap: 16px。

何を指定するかがgapというproperty。

どの値を使うかが16pxというvalue。

padding: 24pxなら、cardの内側に取る間隔を指定する。

max-width: 720pxなら、広いscreenでもcardが際限なく横へ伸びるのを抑える。

同じ32pxを全員へ配るのではなく、対象とpropertyを組み合わせる。

すると、見出し、状態、画像、actionの差をHTMLの外側から見せられた。

イト

selectorが誰に、propertyが何を、valueがどれくらいを決めるんだ

ピコ

その三つを分けると、直したい見た目と巻き込んだ場所を比べられるね

書いたruleではなく、勝ったruleを見る

action linkの色だけが変わらなかった。

イトは新しいblueを指定したはずだった。

しかしcomputed styleにはgrayが出ている。

matched rulesを開くと、二つのdeclarationが同じcolorを求めていた。

a {
  color: gray;
}

.status-card a {
  color: #164fb8;
}

CSSでは、同じelementの同じpropertyへ複数のdeclarationが届くことがある。

どの値を使うかを決める過程がcascadeだった。

originやimportance、selectorのspecificity、同条件なら現れる順序などが判断に関わる。

だから「後に書いたから必ず勝つ」と一つだけで決めない。

イトは、どのselectorがmatchし、どのdeclarationが取り消し線になり、computed valueへ何が残ったかを見た。

fixtureでは.status-card aのdeclarationが勝ち、action linkはblueになった。

!importantを足して見えなくするのではなく、競合の理由を記録した。

viewportが細いときだけ並びを変える

desktopのcardは整った。

けれど、360px previewではaction二つが窮屈だった。

イトはdevice名ではなく、pageが使えるviewport幅を条件にした。

@media (max-width: 640px) {
  .status-card {
    padding: 16px;
  }

  .status-card .actions {
    flex-direction: column;
  }
}

幅が640px以下なら、cardの内側を少し狭くする。

action groupは横並びから縦並びへ変える。

HTMLの順序は変えない。

状態の意味も、hrefも変えない。

同じdocumentへ、利用できる幅に合うpresentationを選ぶ。

イトが全要素へ広がる大きなviolet rule curtainを閉じ、中央の同じHTML treeから状態、画像、actionへ三本のcyan selector pathを分け、右側のwide previewとnarrow previewでactionがどちらも収まるよう照合している
全員へ同じ大きさを強制せず、役割ごとにruleを当て、wideとnarrowの両方で結果を見る。

二つの結果を同じ表へ残す

見学groupが扉の前へ来た。

イトはdesktopのきれいな一枚だけを保存しなかった。

同じHTML、同じCSS、同じ状態cardを、二つのviewportで測った。

確認960px360px
horizontal overflow0px0px
status visible1 / 11 / 1
open roof visible1 / 11 / 1
refresh status visible1 / 11 / 1
hidden action00

別の欄には、変更境界も出た。

matched target groups: 3
unintended element matches: 0
html mutations: 0
forced important declarations: 0
tested viewports: 2

960pxでは、状態とactionが横へ無理なく並んだ。

360pxでは、actionが縦に並び、横scrollなしで二つとも見えた。

pageを大きく見せることが、読みやすさではない。

一つのscreenで整って見えることが、CSSの完了でもない。

誰へruleを当てたか。

どのdeclarationが勝ったか。

使える幅が変わっても、必要な情報とactionが残るか。

結果まで一組にして初めて、見た目の変更を判断できた。

最初にする一手

CSSを直すときは、まず崩れているelementを一つ選び、どのselectorがmatchしているか、どのpropertyのcomputed valueが勝っているかを見る。

global selectorや!importantを足す前に、対象外へ広がっていないかを止めて確認する。

通常幅で整ったら終わりにせず、少なくともwideとnarrowの二条件でoverflowと必要なactionを確かめる。

HTMLの意味やaction先まで変えないと直せないなら、CSSだけの問題と決めず、HTMLやbehaviorの担当へ戻る。

次回:押せそうなのに、何も起きない

最初の見学者が、360pxのscreenでpageを開いた。

状態は読める。

actionも二つ見える。

青く整ったrefresh status buttonへ、イトが指を置いた。

押せる形になっている。

hoverした色も変わる。

けれど、押してもstatusは更新されなかった。

CSSはbuttonの見た目を整えた。

押した後に何をするかまでは、まだ結びついていない。

ピコが、buttonから伸びていない一本の空のsignal lineを照らした。

次回、ピコルート第67話。

イトは、二度押す前にeventの行き先を探す。


参考にした一次資料

この物語を、解説で深める

第66話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

CSSとは|Webページの見た目を整えるスタイル言語を読む