-
指摘をする際は、必ず「理由」と「解決策」をセットにする(「この書き方はダメです」だけではレビューにならない)。
-
[MUST]は、仕様・コーディング規約に反するもの/バグが起きているもの/将来的に深刻な問題になりそうなものを優先して扱う。次点として「改善したほうが良いもの」を挙げる。 -
教義的になりすぎない。趣味嗜好が入りうる指摘は
IMOとして扱う。- 私は flex と grid の使い分けについて普段発信しているが、レビューでは挙動に問題がなければ基本的にコメントしない。
margin-inlineなどの論理プロパティを「左右ショートハンドのように扱う」ことに否定的な意見を見かけるが、私は許容している。- 前提として、通常フロー/flex/grid/コンテナクエリなど主要なレイアウト手法は、論理的な指定を前提として成立している。flex は縦書き(
writing-mode: vertical-lr)になれば主軸が変わり、grid-templateは RTL 環境ではセル順が逆転する。一方でメディアクエリ、translate、background-positionなどは物理指定しか存在しない。このような環境下でmarginやpaddingだけを取り上げて物理・論理を議論してもあまり意味がない。 - これまで
justify-content: flex-endを「右揃え」と呼んでも問題視されにくかったのに、marginやpaddingの論理プロパティに対して突然強く批判するのは不自然に感じる。もっと言うのであればjustify-contentにはrightのような物理指定も存在するが、使用されているのを私は見たことがない。物理・論理に拘るのであればそのあたりにも言及するべきではないだろうか。 margin-inline: autoのような使い方であれば RTL 環境でも同じ結果になりやすく、問題化しにくい。縦書きは基本的にデザイン都合でしか発生しないため、考慮対象ではない。むしろ、ブロック軸のmarginを不要に上書きしてしまう潜在的不具合を抑えられるなら多少の認識差があっても採用する価値がある。- なお、私自身は発信上のコードでは
topをinset-block-startとするなど、論理プロパティをなるべく優先して使っている。ただしこれは多言語対応のためというより、主要なレイアウト手法と整合させたいという私的な理由が大きく、他者に強制する意図はない。実務では他者にとって馴染みが深い物理プロパティを優先して使うこともある。 - CSS の有識者が多く関わっている Tailwind でさえ、 v4 から
mxがmargin-inline、pyがpadding-blockに置き換わっている。[https://tailwindcss.com/docs/margin]
- 前提として、通常フロー/flex/grid/コンテナクエリなど主要なレイアウト手法は、論理的な指定を前提として成立している。flex は縦書き(
-
意図が読み取れない記述には
[Q]を付けて、質問としてコメントする。 -
レビュイーが書いた CSS は、レビューを通した時点でレビュアーも責任を負うことを自覚する。
- プロジェクトでコーディングガイドラインが定められている場合は、それを厳守できているかを最優先で確認する。
- タイポグラフィ、カラーパレット、ボーダー、エレベーション、アニメーションイージング、各種サイズ類などのデザイントークンで定められている定数は
tokens(Sass 系の CSS 設計法ではsettingにあたる)ディレクトリにジャンル毎にファイルを作成してカスタムプロパティに保存する。なるべくそれを指定するように勧める。tokensディレクトリは CSS から参照できるデザインガイドとして活用する
line-height: 1.8のような指定があった場合は、トークンとして定義されているものであればそれを使用するように指摘する。一方でデザイントークンとして定義されていない特異的なものであれば直書きを推奨する。
- 本来この種の整形はフォーマッター/リンターの責務であり、人間が手作業で揃える仕事ではない。ただし、何らかの事情で自動化が機能していない場合は指摘する。
- プロパティ順は「統一されていること」自体が重要であり、方式そのものはプロジェクトの合意が取れていれば基本的に何でもよい。
- 私は自作の stylelint order ルールを npm に公開している:https://github.com/tak-dcxi/taks-stylelint-order
- 例として「Safari は最新メジャーバージョンの 2 つ前の最新版までを動作対象とする」という前提があるなら、現時点で Safari 17.5 で未サポートの CSS を使っていないかを確認する。
- ただし、プログレッシブ・エンハンスメントの範囲で利用でき、品質チェックでも問題になっていないのであれば指摘しない。
- プログレッシブ・エンハンスメントとして利用可能なもの
- 未サポート環境では従来どおりに表示され、サポート環境ではより良い体験を提供できるもの
- 例:View Transition API、
text-wrap: pretty、word-break: auto-phrase、アニメーション目的の::details-content、field-sizingなど
- プログレッシブ・エンハンスメントとして利用しづらいもの
- 未サポート環境で指定自体が無視されやすいアットルール、または表示崩れが大きくなりやすいレイアウト関連の機能
- 例:スタイルクエリ、Anchor Positioning、
position: absolute / fixedに対するplace-self、sibling-index()/sibling-count()関数など
- プログレッシブ・エンハンスメントとして利用可能なもの
- さらに、Chrome が先行実装しがちな「既存プロパティの新仕様」にも注意する。Chrome 最新版でのみ確認していると、意図せず差分にハマる可能性がある。
display: blockに対するjustify-items/justify-selfは現時点で Chrome のみ。レスポンシブでgrid→blockにロールバックする場合、justify-items/justify-selfの定義が残ったままになっていないか確認する。calc(40px / 1280px * 100vw)のような「単位付きの除算」は typed arithmetic と呼ばれ、近年各ブラウザでサポートが進みつつある。- ただし現時点では Firefox が未サポートで、Safari も 18.2 以降でのサポートに留まるため扱いづらい。利用は控える。
calc()周りの仕様に詳しくない初学者が意図せず使ってしまうケースも積み上げポストなどで見かけるため、レビューでは特に注意する。
- Chrome 145 から条件下で
vwがスクロールバーを含まずに計算される挙動に変わった。:https://x.com/tak_dcxi/status/2012336570293248061- 一方で Safari / Firefox では従来どおり横スクロールが発生し得るため、
width: 100vwの使用は避ける。 - そもそも
width: 100vwは多くの場合「不要」または「別手段で置き換え可能」なので、全コアブラウザでスクロールバーを含まずに計算される挙動に置き換わったとしてもコードスメルを減らす目的で指摘対象になり得る。
- 一方で Safari / Firefox では従来どおり横スクロールが発生し得るため、
- class やカスタムプロパティの命名に一貫性があるかを確認する。
- ケバブケース/スネークケース/キャメルケースが混在していないか。
- BEM とそれ以外の命名規則が混在していないか。
.titleのような汎用的なエレメント名は Scoped CSS によってスコープされているか確認する。- コンポーネントの値を操作する API 的カスタムプロパティは、衝突防止と異なる API 的カスタムプロパティが混在した際に「どのコンポーネントのものか」が判別できるよう、
--foregroundのような汎用名ではなく--component-name--foregroundのように対象をプレフィックスで表現する方針を勧める。 - すべてがグローバル定義になる
@keyframesも、衝突防止のためコンポーネント固有であれば同様にプレフィックスを付与することを勧める。- レビューで指摘しないが、最近は
@keyframesなど「ユーザーが命名する識別子」には--を付けるように提案している。- 近年の CSS では、ユーザー定義の名前を明確に区別するために
--を強制する流れがある(例:anchor-name、animation-timelineなど)。 - 「そのプロパティでは
--が必須か?」を個別に覚える必要が減る。 - 将来 CSS 標準側のキーワードが増えても、衝突リスクを下げられる。
- 自分が定義した名前だと即座に判別できる。
- 近年の CSS では、ユーザー定義の名前を明確に区別するために
- ただし、これは値として扱われる名前に関する話であり、
@layerのように値に関係しないものであれば付与する必要はない。
- レビューで指摘しないが、最近は
- 余計な指定があるほど、別セレクタ(ベースCSS/レイアウトプリミティブ/コンポーネント内の別ルール)とぶつかった際に「どれが勝つか」「どの値が残るか」が状況依存になり、期待通りに動かないケースが増える。
- 不要な指定をすると、それを打ち消すための上書きに詳細度バトルが発生して修正コストが上がる可能性がある。
- 「なんとなく指定してみる」でCSSを書くと、設計判断が共有されず、再現性のない実装になりがち。宣言ひとつひとつに「なぜそれが必要か」を説明できる状態にすること。
- 特に初心者の書く CSS にありがちなのが「とりあえず
width: 100%をぶっこんでみる」というもの。 - 原則として、
<div>や<p>のように UA スタイルシートでdisplay: blockが適用される要素は、初期値(auto)のままで親要素の幅に自然に追従するため、多くのケースでwidth: 100%は不要である。 position: absolute/fixedが適用された要素は置換要素などを除きinset: 0を指定した時点で利用可能幅いっぱいに広がるため、width: 100%を重ねて書く必要は<img>などを除き基本的にない。height: 100%は無限ループ抑止の都合で「親要素の高さが明示されている」場合にしか期待通りに動作しない。min-heightのような提案値では成立しない。- 後述するが、明示的な高さ指定そのものがアンチパターンになり得るため、安易な
height固定は避ける必要がある。 - flex / grid アイテムは stretch が効いていれば
height: 100%を書かなくても高さいっぱいになり得るため、不要な指定になりやすい。
- 後述するが、明示的な高さ指定そのものがアンチパターンになり得るため、安易な
width: 100%/height: 100%は無意味なだけではなく、ブラウザ差分を踏んで意図しないバグを誘発することがある。- 過去にあった事例:Safari では
list-style-typeが有効な<summary>の子要素でwidth: 100%を指定をすると不自然な改行が起こる、Firefox ではheight: 100%が適用された subgrid 要素で不自然な詰めが起こる可能性がある、など。
- 過去にあった事例:Safari では
width: 100%は、外部表示形式をblockにしても自然には広がらない置換要素(例:<img>)や、フォームパーツ(例:<button>)など「必要性が明確な対象」に限定して使う。inline-blockinline-flexのような外部表示形式がinlineの要素にwidth: 100%を指定する場合は、外部表示形式をblockに変更することも検討する。この場合はvertical-alignの初期値baselineによって生じる下部の空白の発生を抑えられるメリットもある。
- 子要素を親要素の高さに揃えたいだけであれば、親要素に
display: gridを指定するだけでstretchが働き達成できることが多い。- この方法なら
min-heightでも揃えられるため、「高さ固定」というアンチパターンを回避しやすい。
- この方法なら
- Tailwind を使っている場合、何にでも
w-full/h-fullを付与していないかを確認する。特に生成 AI の出力は乱用されがち。
- ショートハンドは便利な一方で、ロングハンドで積み上げてきた設定が後からショートハンド指定によって意図せずリセットされることがあるため注意する。
- 例:
background: whiteはbackground-colorだけでなく、背景に関する他のサブプロパティも既定値へ戻し得る。結果として、別箇所で設定していた背景画像・サイズ等を壊し、事故につながる可能性がある。 - 横方向の中央寄せとして
margin: 0 auto/margin: autoを学んだ人は多いだろうが、これらは「本来触る必要のない上下のマージン」まで0/autoに上書きするのと同等である。- 特に Every Layout で紹介される flow レイアウトとの相性が悪く、flow 側で意図していた
marginを打ち消してしまう。 - 代替として、先述したインライン軸だけを中央寄せする
margin-inline: autoを提案できる。
- 特に Every Layout で紹介される flow レイアウトとの相性が悪く、flow 側で意図していた
- すべてのショートハンドが NG というわけではない。
insetやborderのように「すべての辺に任意の数値を与える」用途では問題になりにくい。- 指摘対象は、継承や複合的な既定値が絡み、リセット事故を招きやすいもの(例:
background、fontなど)に絞る。
- 特に
fontは最悪のプロパティであり、リセット目的以外では使わない方針を伝える。
- ここでいう「デフォルト」とは、CSS の初期値/UA スタイルシートの値/リセット CSS の値/ベース CSS の値を総称したもの。
- 例:
margin: 0や<div>に対するdisplay: blockなどを場当たり的に書いていないかを確認する。意図が薄い場合は設計の一貫性が崩れているサインになりやすい。- 「最初の要素だけ上マージンを打ち消す」目的なら、
:not(:first-child)やフクロウセレクタなどを使い、「最初の要素にだけmarginを適用しない」という設計を提案する。
- 「最初の要素だけ上マージンを打ち消す」目的なら、
- ブレイクポイント下で値をデフォルトへロールバックしたい場合など、デフォルト値を明示する必要がある場合は IMO として
unset/initial/revert/revert-layerを使い、どの次元へ帰りたいのかを明示するよう勧める。- ただし、
displayの CSS 上の初期値は<div>だろうが<p>だろうが一律inlineであるなど、基礎知識が前提になるため、レビュイーの習熟度に応じて指摘の強度は調整する。
- ただし、
:root/bodyに定義されているベースのタイポグラフィは、原則として継承を利用する。局所での再指定は必要最小限に抑える。
- ここでいう「重い指定」はパフォーマンスの重さではなく、影響範囲が大きく、結果として保守性の治安を悪化させやすい指定を指す。
- ベースレイヤーで定義するタイプセレクタの詳細度が高いと、後からの上書きが難しくなり、破綻の原因になりやすい。
- 例として、ベースレイヤーに次のような指定があるケースがある。
a:link, a:visited { color: var(--foreground-link); }
- この指定の問題は、詳細度が高い点にある。
a(タイプセレクタ)+:link/:visited(擬似クラス)で詳細度は 0.1.1 になり、class 1つでは上書きできない。- その結果として、リンク色を変えるだけのために、 意味の薄いセレクタの組み合わせで詳細度を上げたり、最悪
!importantを付けるといった対応が増え、治安悪化を招きやすい。
- その結果として、リンク色を変えるだけのために、 意味の薄いセレクタの組み合わせで詳細度を上げたり、最悪
- 新規プロジェクトでは、まずカスケードレイヤー(
@layer)の導入を検討する。- レイヤー設計が明確なら、上記のようなセレクタでも詳細度バトルが起きにくくなる。
- カスケードレイヤーの導入が難しい場合は、ベースのタイプセレクタを原則
:where()で包み、詳細度を 0 に落とすことを推奨する。:where(a:any-link) { color: var(--foreground-link); }
- ただし
:where()には落とし穴もあり、サードパーティ製リセットCSS(例:destyle.css)が:where()を使っておらず、タイプセレクタの詳細度が残っている場合、リセット側が優先されてベースの指定が打ち消される可能性がある。そのため、リセットCSSは可能な限り kiso.css のように:where()によって詳細度が下げられているものを選定する。
display/position/marginなど、レイアウトに関わるプロパティは非常に繊細で、色や文字周りのプロパティと比べて破壊的になりやすい。- 極端な例として、次のような発想は明確に危険だと理解できるだろう。
flexを多用するからと言って、div全部にdisplay: flexを付けるposition: absoluteの相対先の指定が面倒だからと言って、全称セレクタにposition: relativeを付ける
- 上記のような実装はさすがに見かけたことは無いものの、実際には次のような指定が紛れ込むことがある。
aをすべてinline-blockにするpにmax-widthを指定する
- レイアウト系プロパティはコンテキストによって何を指定するか変動するのを覚えておく。ベースのスタイルでレイアウト系の定義が行われている場合、異なるコンテキストでは
unset/revertが頻発して手間が増えたり、意図しない不具合を引き起こすといったリスクが高い。 - レイアウト系プロパティは、リセットCSSで最低限の前提を整えた上で、さらに平坦化が必要な場合にのみ慎重に定義する。ベースでの特異な指定は避けるように伝える。
- 典型例は、リンクやインタラクティブ要素への
outline: none。アウトラインを消すと、キーボード操作時にフォーカス位置が見えなくなり、アクセシビリティを阻害するためアンチパターンである。- 近年のUAスタイルではフォーカスリングは主に
:focus-visibleのときだけ表示されるため、クリック時に常時アウトラインが出る状況は基本的に起きにくい。
- 近年のUAスタイルではフォーカスリングは主に
- 「コーディングしやすいから」という理由で、デフォルトの
line-heightを1にするアプローチも見かけるが、これも擁護できないレベルのアンチパターンである。- デザイン上では1行でも、コンテンツ変動やレスポンシブで改行が発生しうる。フォント次第では行が重なり、読めなくなる可能性がある。
- ベースでは、アクセシビリティの観点から
line-height: 1.5以上を基準に指定し、継承させるように進言する。 - デザイン上どうしても
line-height: 1が必要な箇所があっても、text-box-trimやcalc((1lh - 1em) / 2)でハーフレディングを算出して詰めるなどの代替手段は存在する。 - レビュー時は
line-height: 1の指定は MUST で指摘する。
- ダークモード対応が行われていないのに
color-scheme: light darkを指定しているサイトも見かける。- 背景色やテキスト色の指定が不足していると、ダークモード時にシステムカラーが適用され、文字色と背景色が同化するなどの事故が起こりうる。
- 海外製のリセットCSSやフレームワーク由来の可能性もあるが、ダークモード非対応なら
color-scheme: light darkは取り除くよう指摘する。 - 暗い背景のサイトであれば、
<meta name="color-scheme" content="dark">を設定するとよい。ユーザーの外観モードに左右されず、スクロールバーなども暗い見た目に寄り、体験の一貫性が上がる。 - 明るい背景のサイトでは、
color-schemeについては特に何も指定しない方針で問題ない。