2023 年は主要ブラウザでの CSS のサポート状況が大幅に向上し、ウェブデザインの可能性が大きく広がりました。この記事では、それらの新たに使用できるようになった CSS の機能を踏まえたうえで、2023 年 12 月時点での個人的な CSS の書き方や方針をまとめます。

なお、2023 年の CSS の包括的なまとめとしては、Chrome for Developers の以下の記事が、デモも充実していて読み応えがあります。

この記事における CSS の書き方や考え方は、新規プロジェクトにおける起点にはなりますが、原則として、そのプロジェクトの前提条件・制約条件が優先されます。

例えば、ウェブサイトのガイドラインに相当するものが存在すればそちらを優先しますし、想定されるユーザの閲覧環境に応じて CSS の書き方を変化させることもあります。

Baseline は、W3C の WebDX Community Group が主導する、ウェブプラットフォームにおける特定の機能のブラウザでのサポート状況(互換性)を把握しやすくするための取り組みです。

2023 年 5 月に開始し、12 月からは以下の 3 つの分類にアップデートされました。

Baseline Widely available、Baseline 2023 Newly available、Limited availability の 3 つのウィジェットのスクリーンショット
  • Widely available: すべての対象ブラウザでサポート(30 ヶ月以上)
  • Newly available: すべての対象ブラウザでサポート(30 ヶ月未満)
  • Limited availability: 限定的なサポート

対象ブラウザは以下が該当します。

  • Safari(macOS、iOS)
  • Firefox(デスクトップ、Android)
  • Google Chrome(デスクトップ、Android)
  • Microsoft Edge(デスクトップ)

これらのウィジェット(バッジ)は、MDN や Can I use… で表示されますが、すべてのページに含まれているわけではありません。また、Baseline に定義されていたとしても、特定の環境ではアクセシビリティ上の問題などが発生する可能性があります。

結局のところケースバイケースなので、Baseline は参考程度にとどめます。

プロパティの記述順については、先日のブログ記事で CSS 仕様のモジュールごとにグループ化してから並べるルールを作りました。

モジュールの並び順
  1. Cascading and Inheritance
    all
  2. Generated Content
    contentquotes
  3. Positioned Layout
    positioninset など
  4. Anchor Positioning
    anchor-nameposition-anchor など
  5. Containment Module
    conatinercontain など
  6. Display
    displayordervisibility
  7. Grid Layout
    gridgrid-comumn など
  8. Flexible Box
    flexflex-flow など
  9. Box Alignment Module
    gapalign-contentjusitify-content など
  10. Float
    floatclear
  11. Multi-column Layout
    conatinercontain など
  12. Overflow
    overlfowtext-overflow など
  13. Overscroll Behavior
    overscroll-behavior など
  14. Masking
    clipmask など
  15. Box Sizing
    box-sizinginline-sizeblock-size など
  16. Box Model
    marginpadding など
  17. Backgrounds and Borders (border)
    borderborder-radius など
  18. Shapes
    shape-outside など
  19. Images
    object-fitimage-rendering など
  20. Lists and Counters
    list-stylecounter-reset など
  21. Tables
    table-layoutborder-collapse など
  22. Color Adjustment
    color-scheme など
  23. Backgrounds and Borders (background)
    backgroundbackground-color など
  24. Color
    coloropacity など
  25. Compositing and Blending
    mix-blend-modebackground-blend-mode など
  26. Fragmentation
    box-decoration-break など
  27. Writing Modes
    writing-modedirection など
  28. Fonts
    font-familyfont-size など
  29. Inline Layouts
    line-heightvertical-align など
  30. Text Module
    text-alignwhite-space など
  31. Text Decoration
    text-decorationtext-shadow など
  32. Ruby Annotation Layout
    ruby-position など
  33. Filter Effects
    filter など
  34. SVG's Presentation
    fillstroke など
  35. Scroll Snap
    scroll-snap-typescroll-snap-align など
  36. Scrollbars Styling
    scrollbar-colorscrollbar-width
  37. Basic User Interface
    outlineresizepointer-events など
  38. Motion Path
    offset-path など
  39. Viewport
    zoom
  40. Transforms
    transformtranslate など
  41. Will Change
    will-change
  42. Transitions
    transition など
  43. Animations
    animation など
  44. Scroll-driven Animations
    animation-rangescroll-timeline など
  45. View Transitions Module
    view-transition-name

基本的にはこのルールを踏襲しますが、ウェブサイトのガイドラインの有無や、CSS ファイルを共同編集するかどうかといった条件に応じて、ルールの必要性や内容を都度検討します。

プロパティの記述順の詳細やルールを徹底する方法については、以下の記事で説明しています。

従来の物理プロパティではなく、論理プロパティを優先させます。

具体的には、margin-topwidth のような物理プロパティではなく、margin-block-startinline-size を優先させます。

物理プロパティではなく論理プロパティを優先させる
/* 🙁 Not Recommended */
.foo {
  max-width: 600px;
  max-height: 20em;
  margin-top: 40px;
  padding-right: 20px;
  padding-left: 20px;
}

/* 👍 OK */
.foo {
  max-inline-size: 600px;
  max-block-size: 20em;
  margin-block-start: 40px;
  padding-inline: 20px;
}

理由としては、書字方向が異なる状況でもなるべく情報を得られるようにすることと、CSS Grid Layout や Flexbox、コンテナクエリのように、比較的新しい CSS の機能は論理的な指定が前提となっているためです。

CSS Grid Layout やコンテナクエリでは論理的な指定がベースになっている
.foo {
  container-type: inline-size;
  display: grid;
  align-items: start;
  justify-items: end;
}

例外として、overflow-blockoverflow-inline のように現時点ではブラウザのサポートが十分ではない論理プロパティについては、代わりに物理プロパティを使用するか、@supports によるフォールバック指定で対応します。

また、意図的に物理プロパティを使用するケースも考えられるので、無条件に論理プロパティを強制するわけではありません。

繰り返しになりますが、この記事での CSS の書き方や方針は、あくまでも現時点での個人的な考え方になりますので、広く推奨するものではありません。

論理プロパティ同様に、物理値ではなく論理値を優先させますが、実際に使用するプロパティは多くありません。

物理値ではなく論理値を優先させる
/* 🙁 Not Recommended */
.foo {
  text-align: right;
}

/* 👍 OK */
.foo {
  text-align: end;
}

論理プロパティと値については、以下のページを参考にしました。

CSS スコープの管理方法は導入するフレームワークによって変わります。

フレームワークやライブラリの機能によって、コンポーネント単位でスコープが確保される場合には、セレクタ名について深く考える必要はありませんが1、それ以外の場合には、従来の BEM や FLOCSS をベースとした命名規則を採用します。

ただ、各種ブラウザで @scope 規則のサポートが十分になれば、将来的には @scope をベースとした書き方に変わっていくかもしれません。

@scope 規則については、以下の記事が参考になります。

カスケードレイヤー(@layer

見出し「カスケードレイヤー(」

@layer 規則を使用したカスケードレイヤーでは、レイヤーごとにグループ化することで、スタイルの優先順位を制御しやすくなります。

ただ、基本的にはレイヤー化されていないルールセットと、@layer で囲われているレイヤー化されたルールセットとがあった場合、前者のレイヤー化されていないセレクタが優先されます。

レイヤー化されていないルールが適用される
.foo {
  padding: 0;
  background-color: lightgreen;
}

@layer example {
  .foo {
    padding: 40px;
    background-color: lightblue;
  }
}

つまり、CSS リセットなどのベースとなるスタイルがレイヤー化されていなければ、スタイルを上に積み上げること(カスケードを管理すること)が難しくなります。

そのため、カスケードレイヤーが設定されていない既存サイトに、@layer 規則を使用する可能性は低いですが、新規サイトや大幅なリニューアルを実施する場合には、CSS 設計の方針に応じてカスケードレイヤーの導入を検討します。

@layer 規則については、以下の記事が参考になりますが、@layer のみならず !important を使ったときのカスケードのルールも理解することができます。

ネスト記法(CSS Nesting)については、Safari でサポートされたのが 16.5 からと比較的最近であり、個人的には導入するモチベーションも高くないため、積極的に使うことはありません。

Sass を使用する場合でも、以下の理由よりネスト記法には消極的でした。

  • ネストが深くなるとコードの可読性が悪くなる
  • セレクタの検索がしづらくなる
  • CSS の詳細度が高くなる(複雑になる)

ただ、要素の状態を表したり、@media 規則のような at-rules を記述するケースでは、ネスト記法を使用することでコードが整理することができるので有効だと考えています。

ネスト記法の使用例
/* 🙁 Not Recommended */
.foo {
  /* ... */
  .bar {
    /* ... */
    .baz {
      /* ... */
    }
  }
}

/* 👍 OK */
.foo {
  &:hover {
    /* ... */
  }
  &[aria-pressed="true"] {
    /* ... */
  }
  @media (min-width: 600px) {
    /* ... */
  }
}

この方向性を具体化するものとして、前述の「スコープの管理」の関連リンクで挙げた Miriam Suzanne 氏の記事の、@scope は BEM における block-element の関係で、ネスト記法は element--modifier の関係を表現するのに優れているという説明がしっくりきました。

While @scope is excellent at expressing the block-element relationship, nesting is still the clear choice for the element--modifier relationships, like pseudo-classes.

CSS @scope | 12 Days of Web

いずれにしても、ネスト記法を実案件で使用するのは控えます。

2024/01/15 追記

上記のように考えていたのですが、HTML Web Components を使用するときには、ネスト記法で管理するのも有効な方法だと考えを改めています。

メディアクエリの比較演算子

見出し「メディアクエリの比較演算子」

Media Queries Level 4 より、メディアクエリで比較演算子(=<<=>>=)を使用できるようになりました。

以下は従来の min-max- 接頭辞を使用した例です。

従来の min-、max- 接頭辞を使用した例
@media (max-width: 600px) {
  /* 横幅 600px 以下 */
}
@media (min-width: 601px) {
  /* 横幅 601px 以上 */
}
@media (min-width: 601px) and (max-width: 800px) {
  /* 横幅 601px 以上 800px 以下 */
}

同様の指定を比較演算子に置き換えた例が以下です2

比較演算子を使用した例
@media (width <= 600px) {
  /* 横幅 600px 以下 */
}
@media (width > 600px) {
  /* 横幅が 600px より大きい */
}
@media (600px < width <= 800px) {
  /* 横幅が 600px より大きく 800px 以下 */
}

このように比較演算子を使用したほうが簡潔に記述できるのですが、Safari でサポートされたのが 16.4 からと比較的最近なのと、ほとんど書き方の違いでしかないので、当分は従来の min-max- 接頭辞を使用します。

コンテナクエリ(@container

見出し「コンテナクエリ(」

コンテナクエリの導入によって、コンポーネントのコンテナ幅ごとにスタイルを切り替えるボトムアップのアプローチが可能になりました。

メディアクエリとコンテナクエリの比較の図。メディアクエリは、ビューポート幅ごとにスタイルを切り替えるトップダウンのアプローチで、コンテナクエリは、コンテナ幅ごとにスタイルを切り替えるボトムアップのアプローチ
メディアクエリとコンテナクエリの比較

しかし、コンテナクエリを使用する場合、以下の点に留意する必要があります。

  • デザイン担当者とのすり合わせが必要
  • 検証方法を考える必要がある
  • 同じ要素にコンテナクエリとメディアクエリを混在させない(一貫性を持たせる)
  • DOM の構造が深くなる(基準となるコンテナ要素が必要)
  • window.matchMedia に相当するメソッドがない
  • レスポンスイメージに非対応

なお、コンテナクエリには、親要素や祖先要素のサイズに応じて子孫要素のスタイルを切り替える「Size Queries」と、スタイルに応じて子孫要素のスタイルを切り替える「Style Queries」がありますが、後者は現時点ではブラウザのサポートが十分ではありません。

コンテナクエリについては、以下の 3 つの記事で取り上げました。

CSS Subgird によって、親グリッドのトラックに紐づけることができるようになりました。特に、横並びのカードコンポーネントなどで活用できます。

CSS Subgrid でカードコンポーネントを実装する例
親グリッドのトラックに収めることができるので、内容量に関わらずラインがそろう

CSS Subgrid は主要なブラウザでサポートされており、実案件で使用できる段階にありますが、まだ新しい機能ではあるので、ウェブサイトのユーザ環境のシェアや特性に応じて、フォールバックにも配慮しながら対応していきます。

CSS Subgrid については、以下の記事で実例を取り上げながら特徴や使い方を説明しました。

:has() 擬似クラス

この見出しのリンク

:has() 擬似クラスは、Firefox 121 でサポートされたことで、主要なブラウザすべてで使用できる状態となりました。

こちらも Subgrid 同様に、まだ新しい機能であることを考慮し、フォールバックが可能な場合には使用する、非対応環境でも最低限の情報の取得を確保する、といった方針で考えています。

:has() 擬似クラスについては、以下の記事で 10 個のサンプルを取り上げながら、その仕組みや使い方を説明しました。

2023 年は CSS カラー指定に関する選択肢が大幅に増えました。

  • LCH や Oklch などの HD(高解像度)カラースペース
  • color-mix() 関数による混色
  • 相対的なカラー指定(Relative Color Syntax)

相対的なカラー指定(Relative Color Syntax)は、現時点では Firefox が非対応です。

ただ、どのように指定するかはデザイン次第であるので、ウェブサイトのデザインデータの作り方や、表現したいデザインの内容によります。

基本的にはデザインデータ内のカラー指定のまま CSS に反映しますが、状況に応じて記述方法を変更します。例えば以下のようなケースが考えられます。

  • アルファを指定するときにカラー関数を使用する(例: hsl(180 100% 23% / 10%)
  • デザインシステムの配色設計で、スケールしやすい HSL、LCH、Oklch を使用する
  • 鮮やかなグラデーションを表現するために HD カラースペースを使用する

CSS Color については、以下の記事にて、「CSS Color Level 4」で導入された新しいカラースペースの特徴について、色彩の理論もまじえつつ紹介しました。

transform プロパティの値として指定できる translate()rotate()scale() は、独立したプロパティである translaterotatescale を使用します。

独立した transform プロパティを使用する
/* 🙁 Not Recommended */
.foo {
  transform: translateX(10%) rotate(45deg) scale(1.2);
}

/* 👍 OK */
.foo {
  translate: 10% 0;
  rotate: 45deg;
  scale: 1.2;
}

それぞれの値を管理しやすくなることに加え、transition プロパティによるアニメーションを個別に指定しやすくなり、パフォーマンスの観点からも優れているというメリットがあります。

独立した transform プロパティについては、以下の記事が参考になります。

ライトモード・ダークモードのスタイルを切り替える prefers-color-scheme や、ユーザの設定に応じてアニメーションを停止させたり緩和させる prefers-reduced-motion などの、ユーザの設定に応じてスタイルを切り替える指定については、基本的にはプロジェクトの要件に従います。

ただ、一部のユーザが利用したときに、めまい、吐き気、頭痛などの症状を引き起こす可能性があるアニメーションの場合には、prefers-reduced-motion によるアニメーションの無効化や緩和策の提案に加え、そもそもアニメーションが必要不可欠なのかを確認します。

それ以外の CSS に関連するアクセシビリティとしては、キーボード操作でフォーカスインジケータが隠れないようにする、また、リンクやボタンなどのインタラクティブな要素のターゲットサイズを確保するといった点に配慮します。

prefers-reduced-motion に対する考え方は、以下の記事を参考にしています。

この記事では、2023 年末時点の個人的な CSS の書き方や方針をまとめました。1 年前と比較すると確実に変わってきているのを感じています。

ちなみに、ここまで主要ブラウザでの CSS のサポートが進んだのは、ウェブブラウザの相互運用性を向上させるためのプロジェクトである Interop による功績が大きいでしょう。

すでに Interop 2024 も提案されており、2024 年 1 月には一般公開される予定です。

Interop の範囲は CSS にとどまりませんが、これらの動向をフォローしつつ、引き続き CSS の書き方をアップデートしていきたいと考えています。

脚注

  1. 例えば、本サイトでも導入している Astro であれば、コンポーネント単位で CSS のスコープがカプセル化されます(CSS とスタイル | Astro)。

  2. ただし、ブラウザの解釈によってビューポートサイズに小数点が生じると両者の結果が異なる場合があります(2.4.4. Using “min-” and “max-” Prefixes On Range Features | Media Queries Level 4)。