Category Archive CSS・デザイン

CSSだけでApple Vision Pro風スクロールアニメーションを再現する高度なテクニック

CSSだけでApple Vision Pro風スクロールアニメーションを再現する高度なテクニック

Appleの製品ページで多用される、スクロールに連動したダイナミックなアニメーションは、多くのWeb制作者にインスピレーションを与えてきた。特にVision Proの紹介ページで見られる、デバイスが分解されながら迫ってくるような演出は、技術的にも非常に洗練されている。

これまでこうした演出の多くはJavaScriptを用いて制御されていたが、最新のCSS機能を駆使することで、スクリプトなしでの再現が可能になりつつある。CSS-Tricksの記事では、スクロール駆動アニメーション(Scroll-driven Animations)を活用し、Apple風の演出をCSSだけで構築する手法が提案された。

本記事では、その実装の核心となる「パーツの分解」と「デバイスの反転」という2つのステージを、最新のCSSプロパティを用いてどのように制御するのかを深掘りしていく。パフォーマンスとレスポンシブ対応を両立させるための、具体的な計算式や構造の設計についても詳しく見ていこう。

Appleのスクロール演出を構成する2つのステージ

Appleのスクロール演出を構成する2つのステージ

Vision Proのアニメーションを再現するためには、まずその動きを論理的に分解する必要がある。CSS-Tricksの分析によれば、この演出は大きく分けて2つの段階で構成されているという。

ステージ1 ハードウェアの分解表示

最初の段階では、デバイスの底部から3つの主要な電子部品が順番に浮き上がってくる。それぞれのコンポーネントは、他の部品を挟み込むように配置された2枚の画像で構成されている。これにより、部品が重なり合いながらも奥行きを感じさせる、立体的な「爆発図」のような効果が生まれる。

この視覚効果のポイントは、透明な領域を含む複数のレイヤーが、スクロールに合わせて異なる速度やタイミングで移動することだ。最前面と最後面に配置された画像が、中間にある部品を包み込むように動くことで、単なる平面の移動ではない3D的な深みが表現されている。

ステージ2 接眼レンズへのフリップアップ

部品の分解が終わると、次にデバイス全体が滑らかに回転し、接眼レンズ(アイピース)が見える状態へと変化する。Appleの公式サイトでは、この部分はJavaScriptで動画の再生位置をスクロール量に合わせて制御することで実現されている。

これをCSSだけで再現する場合、動画ファイルの代わりに大量の静止画を高速で切り替える手法が検討される。スクロールというユーザーの入力に対して、パラパラ漫画のように画像を差し替えていくことで、動画と同等の滑らかな回転アニメーションを作り出すアプローチだ。

Gridレイアウトによる要素の重ね合わせと配置

Gridレイアウトによる要素の重ね合わせと配置

アニメーションを実装する前の準備として、複数の部品画像を正確に重ね合わせる必要がある。従来は position: absolute を多用していたが、これでは要素が通常の文書フローから外れてしまい、レスポンシブ対応やスクロール位置の管理が複雑になるという課題があった。

CSS-Tricksの筆者は、この問題の解決策として display: grid の活用を挙げている。親要素を1カラム・1行のグリッドに設定し、すべての部品画像を同じグリッドエリア(grid-area: 1 / 1 / 2 / 2)に割り当てることで、文書フローを維持したまま完璧な重ね合わせを実現できる。

Gridによる重ね合わせの概念図
背面パーツ
中央パーツ
前面パーツ
各要素が同じエリアに重なりつつ、個別に移動可能

このデモのように、グリッドを使うことで要素の順序(z-index)を保ちながら、個々のパーツに自由なアニメーションを適用できる土台が整う。また、各画像に background-size: cover を適用することで、アスペクト比を維持したまま画面幅に合わせることも容易になる。

StickyとView Timelineによるスクロール制御

StickyとView Timelineによるスクロール制御

スクロールに応じてアニメーションを動かす際、最も重要なのが「要素が画面内の特定の場所に留まり続けること」と「要素の表示状態を検知すること」の2点だ。これを実現するのが position: stickyview-timeline プロパティである。

要素を画面に固定するStickyの役割

アニメーションが実行されている間、対象のデバイスが画面外に流れていってしまっては意味がない。そこで、アニメーション全体を包むコンテナ要素に十分な高さを設定し、中のデバイス要素に position: sticky; top: 0; を指定する。これにより、ユーザーがスクロールしている間、デバイスは画面上部に固定され、アニメーションの変化だけが視覚的に伝わるようになる。

View Timelineによる実行タイミングの最適化

従来、スクロールアニメーションの開始位置を特定するには、JavaScriptでスクロール量を監視し、要素のオフセットを計算する必要があった。しかし、最新のCSSでは view-timeline-name を定義するだけで、その要素がビューポート(画面)に入ってきたことをトリガーにアニメーションを開始できる。

CSS-Tricksの記事では、scroll-timeline ではなく view-timeline を選択した理由として、レスポンシブ性の向上を挙げている。ページの総高さに依存する scroll-timeline よりも、要素自体の表示状態に基づく view-timeline の方が、画面サイズが変わっても正確なタイミングでアニメーションを開始できるからだ。

レスポンシブ対応のための動的な高さ計算

レスポンシブ対応のための動的な高さ計算

アニメーションの移動量を固定値(px)で指定すると、画面サイズが小さいデバイスではパーツが画面外に飛び出したり、逆に移動が足りなかったりする問題が発生する。これを防ぐために、数学的なアプローチが必要となる。

デバイスの画像サイズが 960px × 608px である場合、現在の表示幅に基づいた動的な高さを calc() 関数で算出できる。具体的には、以下の計算式を用いることで、画像の比率を維持した高さを取得し、それを移動量の基準にする手法だ。

:root {
  --stage2-height: calc(min(100vw, 960px) * 608 / 960);
}

@media screen and (max-height: 608px) {
  :root {
    --stage2-height: 100vh;
  }
}

この計算式により、ブラウザの幅が狭いときは 100vw に基づいた高さが計算され、画面の高さが極端に低い場合は 100vh が優先される。こうして得られた --stage2-height 変数を translate プロパティに適用することで、どのような画面サイズでもパーツが適切な位置まで移動し、重なりを維持できるようになる。

パラパラ漫画方式による「動画風」アニメーション

パラパラ漫画方式による「動画風」アニメーション

前述の通り、CSSだけで動画のフレームを制御することはできない。そこで、ステージ2のフリップアップ演出では、背景画像を高速で切り替える手法が採用された。これは、キーフレームアニメーションの中で background-image を順番に指定していく方法だ。

具体的には、0%から100%までの進行度に合わせて、数十枚の静止画(00011.jpg、00013.jpg…)を切り替えていく。この際、パフォーマンスを向上させるために、HTMLの <link rel="preload" as="image"> タグを使用して、すべての画像を事前に読み込んでおくことが推奨されている。これにより、スクロール時の画像のチラつきや遅延を防ぐことができる。

画像切り替えによる回転演出(Before / After)
スクロール開始(画像001)
スクロール中(画像060:回転完了)
● ●
※スクロール位置に応じて背景画像が差し替わり、擬似的に3D回転を表現する

この手法のデメリットは、1つの動画ファイルの代わりに大量の静止画をダウンロードする必要がある点だ。CSS-Tricksの筆者は、フレーム数を半分に間引くことでファイル数を削減しつつ、視覚的な滑らかさを維持する工夫を凝らしている。実運用では、画像の最適化やスプライト画像化などのさらなる対策が有効だろう。

Animation Rangeによる精緻なタイミング調整

Animation Rangeによる精緻なタイミング調整

view-timeline を使うだけでは、アニメーションが開始・終了するタイミングを細かく制御できない場合がある。そこで役立つのが animation-range プロパティだ。これは、要素がビューポートのどの位置に来たときにアニメーションを開始し、どこで終了するかを定義するものだ。

例えば、部品の分解アニメーション(ステージ1)では animation-range: contain cover; が使用された。これは、要素が完全に画面内に入ってから(contain)アニメーションを開始し、画面から消え去るまで(cover)継続することを意味する。一方、回転アニメーション(ステージ2)では、画面から消える前に動きを完結させる必要があるため、animation-range: cover 10% contain; のような指定で調整が行われている。

このように、スクロール量という「時間軸」に対して、アニメーションの「区間」を定義することで、JavaScriptを使わずとも極めて精度の高い演出制御が可能になる。これは、現代のCSSにおける大きな進化の一つだ。

独自の分析:CSS主導のアニメーションがもたらす変化

独自の分析:CSS主導のアニメーションがもたらす変化

今回紹介した手法は、単に「Appleの真似ができる」という以上の意味を持っている。最大の利点は、ブラウザのメインスレッドをJavaScriptの計算から解放できることにある。スクロール駆動アニメーションは、ブラウザのコンポジタースレッドで処理されるため、ページの読み込みや他の処理が重い状況でも、カクつきの少ないスムーズな動きを提供できる。

一方で、実務上の課題も残されている。大量の画像を切り替える手法は、LCP(Largest Contentful Paint)などのパフォーマンス指標に悪影響を与える可能性があるからだ。また、現時点ではFirefoxがこのCSS機能に完全対応していないため、フォールバック(代替表示)の用意が欠かせない。

しかし、これまで「実装コストが高すぎる」と諦めていた高度な演出が、CSS数行で記述できるようになった意義は大きい。今後は、動画ファイルとCSSアニメーションをより高度に組み合わせた、ハイブリッドな実装が主流になっていくのではないかと推測される。

この記事のポイント

  • Apple風のスクロール演出は、部品の分解と回転という2つのステージに分けて考える
  • display: grid を使うことで、要素の重ね合わせとレスポンシブな配置を両立できる
  • view-timelineposition: sticky の組み合わせが、スクロール連動の鍵となる
  • 大量の画像をCSSで切り替える際は、preload による事前読み込みが不可欠だ
  • animation-range を活用することで、JSなしでも精緻な実行タイミングの制御が可能になる
AstroでMarkdownを強化するMDX活用術!コンポーネントを自由自在に配置する

AstroでMarkdownを強化するMDX活用術!コンポーネントを自由自在に配置する

静的サイトジェネレーターとして人気を集めるAstroは、標準でMarkdownをサポートしている。しかし、より高度なカスタマイズやインタラクティブな要素を記事内に取り入れたい場合、標準のMarkdownだけでは限界を感じることがあるだろう。

そこで活用したいのがMDXだ。MDXはMarkdownの簡潔さと、JSXによるコンポーネントの柔軟性を兼ね備えた強力なツールとして知られている。AstroにMDXを導入することで、ドキュメントの記述効率は劇的に向上する。

この記事では、CSS-Tricksの記事を基に、AstroでMDXを使用するメリットや具体的な実装方法、そして運用上の注意点を詳しく解説していく。技術的な背景を知る同僚から教わるような感覚で、その可能性を探っていこう。

MDXがAstroの開発体験を劇的に変える理由

MDXがAstroの開発体験を劇的に変える理由

MDXとは、Markdownの中でReactやSvelte、Astroといったフレームワークのコンポーネントを直接使えるようにする拡張仕様だ。通常のMarkdownはテキストの装飾には優れているが、複雑なUIパーツを配置するにはHTMLを直接記述しなければならず、管理が煩雑になりやすい。

例えば、記事の中に「補足説明用のカード」や「インタラクティブなグラフ」を置きたい場合を考えてほしい。標準のMarkdownでは、複雑な div タグの階層を書く必要がある。しかしMDXなら、あらかじめ定義したコンポーネントを1行書くだけで済む。

CSS-Tricksの記事でも指摘されている通り、MDXの最大の利点は「Markdownの書きやすさを維持したまま、HTMLの表現力を手に入れられること」にある。これは、コンテンツ制作のスピードと品質を両立させる上で極めて重要な要素だ。

HTML記述の苦痛から解放される

MDXを使用すると、複雑なレイアウトをMarkdownの記法だけで構築できるようになる。例えば、クラス名を持った div で囲まれた見出しやリストを作成する場合、MDXならHTMLタグを最小限に抑えることが可能だ。

<div class="card">
  ### カードのタイトル

  ここにはコンテンツが入る。

  - リスト項目1
  - リスト項目2
</div>

上記のコードは、Astroによって適切なHTMLへと自動変換される。見出しは h3 タグになり、リストは ulli になる。これをすべてHTMLで書く手間を考えれば、MDXがいかに効率的かがわかるだろう。

従来のMarkdown(Before)
<div class=”card”>
  <h3>タイトル</h3>
  <p>説明文</p>
</div>
MDXでの記述(After)
<div class=”card”>
### タイトル
説明文
</div>

このデモは、MDXを使うことでHTMLタグの記述量をどれだけ削減できるかを視覚化したものだ。構造が複雑になるほど、この恩恵は大きくなる。

AstroでMDXを使いこなす3つのアプローチ

AstroでMDXを使いこなす3つのアプローチ

AstroでMDXを利用するには、まず公式のインテグレーションをインストールする必要がある。準備が整えば、主に3つの方法でコンテンツを管理できるようになる。それぞれの特徴を理解し、プロジェクトに最適な手法を選ぼう。

1. コンポーネントとして直接インポートする

最もシンプルな方法は、MDXファイルを他のAstroコンポーネントと同じようにインポートして使うことだ。特定のページの一部として、固定のコンテンツを表示したい場合に適している。

---
import MyContent from '../components/MyContent.mdx';
---

<MyContent />

この方法を使えば、MDXファイルを「再利用可能なパーツ」として扱える。複数のページで同じ説明文を使い回したいときなどに便利だ。ただし、大量のブログ記事を管理するような用途には向いていない。

2. Content Collectionsで一括管理する

Astroの強力な機能である「Content Collections(コンテンツコレクション)」を利用する方法だ。これは、特定のディレクトリ内にあるMarkdownやMDXファイルを一元管理し、型安全なデータとして取り出す仕組みを指す。

src/content/config.js でコレクションを定義する際、読み込むファイルのパターンに .mdx を含めるだけで準備は完了する。記事のメタデータ(フロントマター)を活用して、一覧ページや詳細ページを動的に生成できるのが強みだ。

また、この方法では <Content components={{ Image }} /> のように、すべての記事で共通して使いたいコンポーネントを一括で渡すことができる。各MDXファイルで毎回インポートを書く手間が省けるため、大規模なサイト運用では必須の手法と言える。

3. Layoutフロントマターで共通のデザインを適用する

MDXファイルのフロントマターに layout プロパティを指定することで、その記事を特定のデザイン枠組み(レイアウト)の中に埋め込むことができる。これは、記事ごとに異なるレイアウトを適用したい場合に有効だ。

---
title: 私のブログ記事
layout: ../layouts/BlogPostLayout.astro
---

指定されたレイアウトファイル側では、Astro.props を通じて記事のタイトルや公開日などの情報を受け取り、<slot /> タグを使ってMDXの本文をレンダリングする。デザインとコンテンツの分離が明確になり、メンテナンス性が向上するだろう。

実装前に知っておきたいMDXの注意点と対策

実装前に知っておきたいMDXの注意点と対策

MDXは非常に便利だが、導入にあたってはいくつかの課題も存在する。開発をスムーズに進めるために、あらかじめこれらの注意点を把握しておこう。特にツール周りの挙動については、事前の設定が重要になる。

リンターとフォーマッターの限界

現時点では、ESLintやPrettierといったコード整形ツールがMDXファイルを完璧にサポートしているとは言い難い。特に、Markdown記法とJSXが入り混じった複雑な構造では、自動整形が意図しない結果を招くことがある。

CSS-Tricksの著者であるZell Liew氏も、複雑なマークアップをMDXで行う際は手動でのインデント調整が必要になる場合があると述べている。もしマークアップが非常に重くなるのであれば、MDXではなく別のコンポーネント化手法を検討するのも一つの手だ。

RSSフィード生成の工夫

Astroの標準的なRSSインテグレーションは、デフォルトではMDXファイルをそのまま処理できない。RSSは純粋なXML形式を求めるが、MDXにはJavaScriptのロジックやコンポーネントが含まれている可能性があるからだ。

この問題を解決するには、Astroの「Container API」などを使用して、MDXを静的なHTMLにレンダリングしてからRSSに渡す処理が必要になる。ブログサイトでRSS配信を重視している場合は、実装の初期段階でこのワークフローを確認しておくべきだ。

独自の分析:AstroとMDXがもたらす「コンテンツ管理の未来」

独自の分析:AstroとMDXがもたらす「コンテンツ管理の未来」

AstroとMDXの組み合わせは、単なる「便利な記法」以上の価値を提供している。それは、エンジニアがコードを書く感覚で、ライターが質の高いコンテンツを制作できる環境の構築だ。これを実現しているのが、Astroの「アイランドアーキテクチャ」との親和性である。

アイランドアーキテクチャとは、ページ全体を静的なHTMLとして出力しつつ、必要な部分だけを動的なコンポーネント(アイランド)として動作させる仕組みだ。MDXを使えば、記事の本文という「静的な海」の中に、複雑な機能を持つ「動的な島」を簡単かつ安全に配置できる。

また、Content Collectionsによる型定義は、コンテンツの品質管理にも寄与する。例えば「すべての記事にサムネイル画像と著者情報が必須」というルールをコードレベルで強制できる。これにより、多人数での運用でもサイトの整合性が保たれやすくなるのだ。

筆者の見解としては、今後のWeb制作において「コンテンツのデータ化」はさらに加速するだろう。その際、MDXのような「構造化しやすいドキュメント形式」を採用していることは、将来的なプラットフォームの移行や再利用において大きなアドバンテージとなるはずだ。

この記事のポイント

  • MDXはMarkdown内でコンポーネントを使用可能にし、HTML記述の手間を大幅に削減する
  • Astroでは、直接インポート、Content Collections、Layoutフロントマターの3つの方法でMDXを活用できる
  • Content Collectionsを使えば、共通コンポーネントを全記事に一括で提供でき、管理が効率化される
  • フォーマッターの挙動やRSS対応など、一部のツールチェーンには工夫が必要な点に注意する
  • AstroのアイランドアーキテクチャとMDXの相性は抜群であり、静的サイトの表現力を最大化させる
CSS最新動向まとめ:clip-pathのジグソーパズル、ビュートランジション、名前付きコンテナ

CSS最新動向まとめ:clip-pathのジグソーパズル、ビュートランジション、名前付きコンテナ

CSSの進化は止まらない。毎週のように新たな機能や実装が追加され、開発者の表現の幅を広げている。CSS-Tricksの最新レポート「What’s !important #9」では、実用的なclip-pathの応用から、管理が楽になるビュートランジションツールキット、そして長らく待たれたsubgridの本質まで、押さえておくべきトピックがまとめられている。

この記事では、同レポートで紹介された主要なCSS機能とその背景にある動向を解説する。各機能がどのような問題を解決し、実際のプロジェクトでどう活かせるのか、具体例を交えて見ていく。

clip-pathで作るジグソーパズルと角丸ポリゴン

clip-pathで作るジグソーパズルと角丸ポリゴン

要素の表示領域を自由な形に切り抜くCSSプロパティclip-pathの応用例が注目を集めている。Amit Sheen氏は、このプロパティだけで完全なジグソーパズルを作成する方法を紹介した。パズルそのものが必要になる場面は稀だが、このチュートリアルはclip-pathの可能性とその構文を学ぶ絶好の機会だ。

進化を続けるclip-pathの仕様

clip-pathは当初、基本的な図形の切り抜きしかできなかった。しかし現在はpolygon()関数で複雑な多角形を定義できる。さらに仕様は進化を続けており、Chrome Canaryでは先週、polygon()関数にroundキーワードを追加して角を丸める機能が実装された。

開発者のyisibl氏は、この機能の実装に携わっていると述べている。また、bevel(面取り)のような他の角形状キーワードの実装についても議論が進んでいる。これらが実用化されれば、より滑らかでデザイン性の高いクリッピングが可能になる。

clip-pathアニメーションの実例

Karl Koch氏は、clip-pathを使った印象的なアニメーションのデモを公開している。形状を連続的に変化させることで、モーフィングのような視覚効果をCSSのみで実現できる。JavaScriptを使わないためパフォーマンスに優れ、ユーザーインタラクションへの応答も滑らかだ。

シンプルな四角形
星形にクリップ
clip-pathプロパティで形状を定義

このデモは、clip-pathの値が四角形から星形へ変化する様子を概念的に示している。実際のアニメーションでは、この変化が連続的に行われる。

ビュートランジションを効率化するツールキット

ビュートランジションを効率化するツールキット

ページや要素が切り替わる際のトランジション効果を簡単に実装できる「ビュートランジションAPI」。Chrome DevRelチームは、このAPIの利用を支援する「ビュートランジションツールキット」を公開した。

要素スコープのビュートランジション

このツールキットが公開された背景には、技術の急速な普及がある。Chromeは先月、ページ全体ではなく特定の要素だけにトランジション効果を適用する「要素スコープのビュートランジション」を正式に実装した。これにより、ページの一部だけを滑らかに更新するといった、より細かい制御が可能になった。

ツールキットには、この新機能を活用したデモも含まれている。開発者は複雑なJavaScriptコードを書かずに、CSSとわずかなマークアップで高度な画面遷移を実現できる。

ツールキットが解決する課題

ビュートランジションAPIは強力だが、適切なタイミングでstartViewTransition()を呼び出し、DOMの更新と連携させる必要がある。ツールキットはこうしたボイラープレートコードを抽象化し、一般的なユースケースを簡単に実装できるユーティリティを提供する。特にReactやVueなどのフレームワークと組み合わせる際の手間を大幅に削減できる見込みだ。

名前付きコンテナと@scopeによるスタイルのスコープ管理

名前付きコンテナと@scopeによるスタイルのスコープ管理

大規模なプロジェクトでは、CSSのスタイルが意図しない要素に影響を与える「スタイルの漏れ」が問題になる。この問題を解決するためのアプローチとして、「名前付きコンテナ」と@scopeルールが注目されている。Chris Coyier氏は両者を比較し、その使い分けについて論じている。

名前付きコンテナの仕組み

名前付きコンテナは、container-nameプロパティでコンテナに名前を付け、@containerルール内でその名前を参照してスタイルを適用する手法だ。これにより、特定のコンテナ内の要素にのみスタイルを限定できる。

.component {
  container-name: my-component;
}

@container my-component (min-width: 400px) {
  .component .button {
    background-color: blue;
  }
}

このコードでは、.componentというコンテナ内にあり、かつコンテナの幅が400px以上の場合にのみ、ボタンの背景色が青になる。コンテナクエリに近い考え方でスコープを制限する方法だ。

@scopeルールとの比較

一方、@scopeルールは、スタイルの適用範囲を親要素によって直接定義する。

@scope (.component) {
  .button {
    background-color: blue;
  }
}

Coyier氏は当初、名前付きコンテナのアプローチを評価していたが、現在はHTMLを汚さず、より直感的にスコープを定義できる@scopeを好む傾向にあると述べている。@scopeを使えば、クラス名を増やすことなく、スタイルの影響範囲を明確にできる利点がある。

従来のクラス指定
<div class=”component”>
  <button class=”component__button”>送信</button>
</div>
※クラス名でスコープを表現。BEMなどの命名規則が必要。
@scopeを使用
<div class=”component”>
  <button>送信</button>
</div>
※HTMLはシンプル。スコープはCSSの@scopeルールで定義。
従来のクラス指定  @scopeを使用

どちらの手法を選ぶかはプロジェクトの構造やチームの好みによる。コンテナクエリと連携させたい場合は名前付きコンテナが、純粋にスタイルのカプセル化を目的とする場合は@scopeが適している場合がある。

subgridの本質とその実践的価値

subgridの本質とその実践的価値

CSS Gridの強力な機能である「subgrid」は、約2年半前に主要ブラウザで利用可能になった。当時はレイアウトの革命とまで言われたが、実際の採用は緩やかだ。David Bushell氏は、subgridの核心をシンプルに解説し、その真価を再評価している。

subgridが解決するレイアウト課題

従来、親グリッドと子要素のグリッドを連動させたい場合、ネストされた<div>要素(いわゆる「ラッパー地獄」)や負のマージンといったハックが必要だった。subgridを使えば、子グリッドが親グリッドのトラック(行や列)を直接継承できる。これにより、マークアップを複雑にすることなく、深い階層の要素も親グリッドにきれいに整列させられる。

subgridなし
親グリッドアイテムA
親グリッドアイテムB
子要素。親の列と連動しない。
※子要素内のコンテンツが、親グリッドの列に揃わない。
subgrid使用
親グリッドアイテムA
親グリッドアイテムB (subgrid)
子要素。親の列を継承して整列。
※子要素が親グリッドの列ラインを継承し、レイアウトが整合する。
subgridなし  subgrid使用

このデモは概念を示したものだ。実際のsubgridでは、grid-template-columns: subgridを指定した子アイテムが、親の列トラックをそのまま借用する。

採用が進まない理由と今後

subgridの採用が思ったほど進んでいない理由の一つは、学習コストにある。Grid自体が豊富な概念を持つため、その上位機能であるsubgridの必要性や利点を理解するハードルが高い。Bushell氏の解説は、このハードルを下げ、具体的なメリットを視覚的に示す良いきっかけになる。

カードレイアウトや複雑なフォーム、編集可能なダッシュボードなど、内部構造が異なるコンポーネントを共通のグリッドに揃えたい場面で、subgridの真価が発揮される。Flexboxや従来のGridでは実現が難しかった、高度な整列が可能になる。

CSSの拡張と「JavaScript不要」の潮流

CSSの拡張と「JavaScript不要」の潮流

Pavel Laptev氏は「The Great CSS Expansion」と題した記事で、かつてJavaScriptライブラリに頼っていた機能の多くが、現代のCSSで代替可能になっている点を指摘している。これは「You Might Not Need jQuery」の現代版とも言える潮流だ。

例えば、ツールチップやドロップダウンメニューの位置決めには、JavaScriptライブラリが使われてきた。しかし現在では、CSSのanchor()関数やanchor-positionプロパティを用いて、相対的な位置を純粋なCSSで計算できる。同様に、スムーズスクロールやタブインターフェース、アコーディオンなども、scroll-behavior:target疑似クラス、<details>要素など、CSSとHTMLの組み合わせで実現できるケースが増えている。

この変化の背景には、ブラウザベンダーによるCSS仕様の積極的な拡張がある。開発者は、軽量でパフォーマンスに優れ、ブラウザにネイティブに統合されたCSSソリューションを選択肢として持つようになった。プロジェクトによっては、依存ライブラリを削減し、バンドルサイズを縮小できる可能性がある。

この記事のポイント

  • clip-pathは角丸ポリゴンなどでさらに進化しており、複雑な形状の作成とアニメーションが可能になった。
  • Chromeのビュートランジションツールキットは、要素スコープのトランジションなど、最新APIの実装を効率化する。
  • スタイルのスコープ管理には@scopeルールが有力な選択肢で、HTMLをシンプルに保ちながらカプセル化を実現できる。
  • subgridは親グリッドの構造を子要素が継承する機能で、ネストしたラッパーやハックなしで深い整列を実現する。
  • かつてJavaScriptが必要だった多くのUI機能が、現代のCSSとHTMLで代替可能になりつつある。
JavaScriptモジュール設計がアプリの命運を分ける!ESM時代のアーキテクチャ入門

JavaScriptモジュール設計がアプリの命運を分ける!ESM時代のアーキテクチャ入門

JavaScriptで大規模なアプリケーションを構築する際、モジュールシステムをどう設計するかは、プロジェクト全体の命運を分ける最初の大きな決断となる。かつてのJavaScriptにはグローバルスコープしか存在せず、複数のスクリプトが互いの変数を上書きしてしまうリスクが常に付きまとっていた。

現代のモジュールシステムは、単にコードを複数のファイルに分割するための仕組みではない。それはシステムの各パーツの間に「境界線」を引き、依存関係の流れを制御するためのアーキテクチャそのものだ。適切な設計がなされていないモジュール構造は、プロジェクトが成長するにつれてメンテナンスを困難にし、変更のたびに予期せぬ場所でバグを発生させる原因となる。

この記事では、現代の標準であるESM(ECMAScript Modules)の特性を理解し、クリーンアーキテクチャの原則をモジュール設計にどう適用すべきかを詳しく解説していく。技術に詳しい同僚からアドバイスを受けるような感覚で、保守性の高いコードベースを構築するためのヒントを掴んでほしい。

モジュールシステムは「境界線」のデザインだ

モジュールシステムは「境界線」のデザインだ

JavaScriptのモジュールシステムには、主にCommonJS(CJS)とECMAScript Modules(ESM)の2つの流れがある。CommonJSはNode.jsの誕生とともに普及したサーバーサイド向けの仕組みであり、ESMはブラウザでもネイティブに動作するよう設計された現在の標準規格だ。これら2つは単に構文が異なるだけでなく、根本的な設計思想に大きな違いがある。

ESMがCommonJSから引き継がなかった「柔軟性」の正体

CommonJSの最大の特徴は、require()が通常の関数として実行される点にある。これにより、if文の中やループの中で動的にモジュールを読み込むことが可能だった。一方でESMは、import文を必ずファイルの先頭(トップレベル)に記述しなければならず、パスも静的な文字列である必要がある。この制約は一見不便に思えるが、実は「静的解析」を可能にするための意図的な設計だ。

ESMの制約のおかげで、ビルドツールはコードを実行することなく、どのモジュールがどこで使われているかを完全に把握できる。これが「Tree-shaking(ツリーシェイキング)」と呼ばれる、不要なコードを自動的に削除してバンドルサイズを削減する技術を支えている。CommonJSでは実行時まで依存関係が確定しないため、ツールは安全のために「使われていないかもしれないコード」もすべて含めざるを得ない。ESMは柔軟性を犠牲にすることで、パフォーマンスの最適化を手に入れたのだ。

従来の方式(CommonJS)
関数のため、どこでも実行可能
実行時まで依存関係がわからない
不要なコードが残りやすい(低速)
現代の方式(ESM)
トップレベルでの宣言が必須
ビルド時に依存関係をすべて把握可能
不要なコードを自動削除(高速)
柔軟だが不透明  制約があるが最適化可能

このデモは、モジュールシステムの進化によって、どのように解析のしやすさが向上したかを視覚化したものだ。

クリーンアーキテクチャに学ぶ依存関係のルール

クリーンアーキテクチャに学ぶ依存関係のルール

プロジェクトの規模が大きくなると、どのファイルがどのファイルを参照しているかが複雑に絡み合い、いわゆる「スパゲッティコード」になりがちだ。これを防ぐための有力な指針として、ロバート・マーチン氏が提唱した「クリーンアーキテクチャ」がある。すべてのプロジェクトに導入すべき銀の弾丸ではないが、モジュールの境界線を引く際の強力な土台となる。

依存の方向は常に「内側」へ

クリーンアーキテクチャの核心は「依存性のルール」にある。これは、システムの各パーツを同心円状のレイヤーに分け、依存の方向を一方向に限定するというルールだ。円の内側にはビジネスロジック(システムの核心となるルール)を配置し、外側にはUI、データベース、フレームワークなどの技術的な詳細を配置する。

重要なのは、内側のレイヤーは外側のレイヤーについて何も知らないという点だ。たとえば、ユーザー登録のルール(ビジネスロジック)を記述したモジュールの中で、Reactの特定の関数や、特定のデータベース操作用のライブラリを直接インポートしてはいけない。なぜなら、技術スタックはビジネスルールよりも頻繁に変更されるからだ。技術に依存しない「コア」を保つことで、フレームワークの乗り換えやライブラリのアップデートに強いシステムが構築できる。

ビジネスロジック(中心)
ユースケース層
UI・外部API・DB(外側)
依存の方向(外側から内側へ)

このデモは、依存関係が常にシステムの核心(ビジネスロジック)に向かって流れるべきであることを示している。外側の層を変更しても、内側の層には影響が及ばない構造が理想的だ。

モジュールグラフで健康状態をチェックする

モジュールグラフで健康状態をチェックする

自分のプロジェクトが健全な依存関係を保てているかどうかを確認するには、「モジュールグラフ」を可視化するのが効果的だ。モジュールグラフとは、ファイル同士のインポート関係を線で結んだネットワーク図のことである。MadgeやDependency Cruiserといったツールを使えば、現在のコードベースから自動的にこのグラフを生成できる。

循環参照と「やりすぎた共通ユーティリティ」の罠

不健全なグラフには、いくつかの共通した特徴がある。その筆頭が「循環参照」だ。モジュールAがBをインポートし、BがCをインポートし、さらにCがAをインポートしているような状態を指す。これはモジュールの再利用を困難にするだけでなく、ビルドエラーや予期せぬ実行時の不具合を引き起こす温床となる。

また、utils.jsのような汎用的なファイルに何でも詰め込みすぎるのも危険だ。あらゆる場所から参照される巨大なユーティリティファイルは、その一部を修正しただけでシステム全体に影響が及ぶ「爆発半径」の大きな部品になってしまう。これを解決するには、単一責任の原則に基づき、ユーティリティを機能ごとに細かく分割し、特定の文脈に閉じた場所に配置し直す必要がある。高レベルのモジュールが低レベルのモジュールに依存するという原則を、グラフを通じて常に監視することが重要だ。

バレルファイル(index.js)の使用は慎重に

バレルファイル(index.js)の使用は慎重に

JavaScript開発でよく使われる手法に「バレルファイル」がある。これは、ディレクトリ内の複数のモジュールを一つのindex.js(またはindex.ts)でまとめて再エクスポートする仕組みだ。インポート側の記述がシンプルになり、ディレクトリ構造を隠蔽できるため、コードの見た目は非常に美しくなる。

見た目の美しさとパフォーマンスのトレードオフ

しかし、バレルファイルには無視できないデメリットがある。それは、ビルドパフォーマンスの低下とTree-shakingの阻害だ。バレルファイル経由で一つの関数だけをインポートしたつもりでも、ビルドツールはそのディレクトリ内のすべてのファイルを解析対象として読み込んでしまうことがある。

大規模なプロジェクトでは、この影響が顕著に現れる。実際にAtlassianのエンジニアリングチームは、Jiraのフロントエンドからバレルファイルを削除することで、ビルド時間を75%も短縮し、バンドルサイズの削減にも成功したと報告している。小規模なプロジェクトであれば利便性が勝るが、規模が拡大してきたら「インポートの美しさ」のために「実行性能」を犠牲にしていないか、立ち止まって考える必要があるだろう。

バレルファイルあり(index.js)
import { login } from './auth';
※auth内の全ファイルが解析されるリスクあり
対比
直接インポート
import { login } from './auth/login';
※必要なファイルのみが最小限に解析される

このデモで示しているように、バレルファイルは記述を簡潔にする一方で、裏側での解析コストを増大させる可能性がある。パフォーマンスが求められる現場では、直接的なインポートが推奨される場合も多い。

結合度をコントロールし保守性を高める

結合度をコントロールし保守性を高める

モジュール間の関係性を考える上で避けて通れないのが「結合度」という概念だ。結合度とは、あるモジュールが別のモジュールの内部実装にどれほど依存しているかを示す指標である。保守性の高いシステムを目指すなら、可能な限り「疎結合」な状態を保つことが求められる。

特に注意すべきは「密結合」と「暗黙的な結合」だ。密結合は、相手のモジュールの内部の仕組みを知りすぎている状態であり、一方を修正するともう一方も修正しなければならない「変更の連鎖」を引き起こす。一方、暗黙的な結合は、グローバルな状態(シングルトンやグローバル変数)を介して、目に見えない形で依存している状態だ。これらはデバッグを困難にし、コードの予測可能性を著しく低下させる。依存関係は常に明示的(Explicit)にし、モジュールが公開する「インターフェース」のみを通じてやり取りを行うのが、長期的な運用における鉄則だ。

この記事のポイント

  • ESMは静的解析を可能にする制約を設けることで、Tree-shakingによる最適化を実現している
  • クリーンアーキテクチャの原則に従い、依存の方向を常に「外側から内側のビジネスロジック」へ向ける
  • モジュールグラフを可視化し、循環参照や肥大化したユーティリティファイルを早期に発見・解消する
  • バレルファイルは小規模では便利だが、大規模開発ではビルド時間やバンドルサイズに悪影響を与える可能性がある
  • 結合度を低く保ち、モジュール間のやり取りを明示的なインターフェースに限定することで保守性を高める
CSSだけで多階層の状態を管理するラジオボタン・ステートマシンの実装手法

CSSだけで多階層の状態を管理するラジオボタン・ステートマシンの実装手法

Web制作における状態管理は、多くの場合JavaScriptの役割だと考えられている。しかし、純粋に視覚的なUIの変化であれば、CSSだけで完結させるアプローチが非常に有効な場合がある。

パネルの開閉やアイコンの形態変化、カードの反転といった「表示上の状態」をCSSで管理することで、JavaScriptのオーバーヘッドを削減し、プレゼンテーション層に近い場所でロジックを保持できる。この記事では、従来のチェックボックスハックを進化させた「ラジオボタン・ステートマシン」という手法について詳しく解説する。

この手法をマスターすると、複雑な多段階のUI遷移もHTMLとCSSのみで堅牢に実装できるようになる。技術に詳しい同僚が教えるような感覚で、具体的なコード例を交えながらその仕組みを紐解いていこう。

CSSによる状態管理の新しいアプローチ

CSSによる状態管理の新しいアプローチ

Webサイトのインタラクションにおいて、すべての状態変化にJavaScriptが必要なわけではない。ビジネスロジックやデータの永続化が絡まない、純粋な表示の切り替えであれば、CSSの機能を活用したほうがスマートな解決策になることが多い。

JavaScriptを使わない選択肢

JavaScriptは強力だが、依存しすぎるとコードの複雑さが増し、パフォーマンスにも影響を与える。例えば、ダークモードの切り替えやタブメニューの制御をCSSで行うと、ページの読み込み直後から即座に反応し、スクリプトの実行待ちによる遅延が発生しない。これは、ユーザー体験の向上に直結する重要なポイントだ。

従来のチェックボックスハックとその限界

CSSで状態を管理する古典的な手法として「チェックボックスハック」がある。これは、非表示にしたチェックボックスの :checked 擬似クラスを利用し、隣接する要素のスタイルを変更するテクニックだ。しかし、この方法には「オンかオフか」という2つの状態しか持てないという明確な限界がある。3つ以上の状態を切り替えたい場合には、別の工夫が必要になる。

ラジオボタン・ステートマシンの基本構造

ラジオボタン・ステートマシンの基本構造

2つの状態しか持てないチェックボックスに対し、複数の選択肢から1つだけを選べるラジオボタンを利用するのが「ラジオボタン・ステートマシン」の核心だ。ラジオボタンは同じ name 属性を持つグループ内で排他的に動作するため、これを「現在の状態」として利用する。

相互排他的な状態を作る仕組み

まず、複数のラジオボタンを用意し、それぞれに異なる状態を割り当てる。例えば「状態A」「状態B」「状態C」の3つがある場合、HTML構造は以下のようになる。ここで重要なのは、ラジオボタンを display: none で消すのではなく、アクセシビリティを考慮した方法で隠すことだ。

<div class="state-container">
  <input type="radio" name="ui-state" id="state-1" checked>
  <input type="radio" name="ui-state" id="state-2">
  <input type="radio" name="ui-state" id="state-3">
  
  <div class="content">
    <!-- ここに状態に応じて変化する要素を配置 -->
  </div>
</div>

ボタンの見た目をカスタマイズする

ラジオボタンそのものをUIのボタンとして機能させるには、appearance: none を使用してデフォルトのスタイルを解除する。これにより、ラジオボタンをあたかも普通のボタンやタブのようにスタイリングできるようになる。疑似要素の ::after などを使ってラベルテキストを表示すれば、HTMLタグを最小限に抑えたまま、インタラクティブな要素が完成する。

状態切り替えデモ(簡易版)

状態1(選択中)
状態2
状態3
現在は「状態1」のコンテンツが表示されています
選択されている状態  選択されていない状態

このデモはラジオボタンの排他的な性質を利用した状態遷移を視覚化したものだ。実際の実装では、クリックするたびに :checked が移動し、それに応じて下のコンテンツが切り替わる仕組みになる。

循環型と非循環型のフロー制御

循環型と非循環型のフロー制御

ステートマシンを構築する際、ユーザーがどのように状態間を移動するかを設計する必要がある。すべての状態をループさせる「循環型」と、最初から最後まで順番に進む「非循環型(リニア型)」の2パターンが主に使われる。

次の状態へ進むシーケンシャルな遷移

例えば、クリックするたびに「進む」だけのUIを作る場合、現在の状態の「次」にあるラジオボタンだけを表示させるテクニックが使える。CSSの隣接兄弟結合子 + を活用し、input:checked + input というセレクタを使えば、現在選択されている要素の直後にある要素だけにスタイルを適用できる。

input[name="state"] {
  position: fixed;
  opacity: 0;
  pointer-events: none;
}

/* 現在チェックされているものの次にあるボタンだけを表示する */
input[name="state"]:checked + input[name="state"] {
  position: relative;
  opacity: 1;
  pointer-events: all;
  appearance: none;
  /* ボタンとしてのスタイル */
}

前に戻る双方向フローの実装

「戻る」ボタンも実装したい場合は、最新のCSS擬似クラスである :has() が威力を発揮する。input:has(+ input:checked) というセレクタを使えば、「次にチェックされている要素がある場合の、自分自身」をターゲットにできる。これにより、進むボタンと戻るボタンの両方をCSSだけで制御可能になる。

カスタムプロパティと計算式の活用

カスタムプロパティと計算式の活用

ラジオボタン・ステートマシンの真価は、CSSカスタムプロパティ(変数)と組み合わせたときに発揮される。各状態に対して直接スタイルを記述するのではなく、変数の値だけを書き換える手法だ。

状態を変数として一括管理する

例えば、状態ごとに要素の位置や色を変えたい場合、各状態の :checked 時に --state-index のような変数の値を変更する。これにより、各コンポーネント側ではその変数を参照するだけで済み、コードの重複を劇的に減らすことができる。

.container:has(#state-1:checked) { --index: 0; --color: #e91e63; }
.container:has(#state-2:checked) { --index: 1; --color: #2196f3; }
.container:has(#state-3:checked) { --index: 2; --color: #4caf50; }

.indicator {
  background-color: var(--color);
  transform: translateX(calc(var(--index) * 100%));
}

calc関数による動的なスタイル適用

変数を数値として扱うことで、calc() 関数を用いた高度なレイアウト計算が可能になる。例えば、スライダーの移動距離や、要素の不透明度、あるいは hsl() 関数を使った色の変化などを、状態のインデックス番号から動的に算出できる。これは、まるでJavaScriptで計算しているかのような柔軟性をCSSにもたらす。

数値を変化させるシミュレーション
1
2
3

※状態変数 –index の値によってゲージの幅や色を計算

現在のアクティブな状態(変数: 1)  待機中の状態(変数: 2, 3)

このデモは、内部的な変数値の変化がどのように視覚的なゲージやインジケーターに反映されるかを示している。CSSの計算機能を使うことで、滑らかなアニメーションを伴う状態遷移が実現する。

実用性とアクセシビリティの考慮点

実用性とアクセシビリティの考慮点

CSSステートマシンは非常に強力だが、実務で導入する際にはアクセシビリティへの配慮が欠かせない。単に「動く」だけでなく、すべてのユーザーが利用できる形でなければならない。

フォームコントロールとしての特性を活かす

ラジオボタンは本来、フォームの入力要素だ。そのため、キーボード操作(Tabキーでの移動や矢印キーでの選択)に標準で対応している。この特性を壊さないようにスタイリングすることが重要だ。display: none を使ってしまうとフォーカスが当たらなくなるため、視覚的に隠しつつもスクリーンリーダーやキーボードからは認識できる状態を維持する必要がある。

視覚的な変化とセマンティクスのバランス

CSSステートマシンが適しているのは、あくまで「視覚的なバリエーション」や「ローカルなUI操作」だ。データの保存が必要なフォーム送信や、複雑なバリデーションが絡む場合は、おとなしくJavaScriptを使用すべきだ。Kinstaの著者Carlo Daniele氏も指摘するように、CSSはプレゼンテーション層に責任を持ち、アプリケーションのロジックはスクリプト層が持つという役割分担を忘れてはならない。

この記事のポイント

  • ラジオボタンの「1つだけ選択できる」特性を利用して、3つ以上のUI状態をCSSで管理できる
  • :has() や隣接兄弟結合子を駆使することで、進む・戻る・循環といった複雑なフローを制御可能だ
  • カスタムプロパティと calc() を組み合わせれば、状態に応じた動的なレイアウト計算がCSSのみで行える
  • アクセシビリティを損なわないよう、appearance: none を活用し、キーボード操作性を維持することが重要だ
  • 純粋な表示上の状態管理にはCSSを使い、ビジネスロジックにはJavaScriptを使うという適切な使い分けが求められる
2026年3月のBaselineアップデート!最新Web技術の互換性と実務への活用法

2026年3月のBaselineアップデート!最新Web技術の互換性と実務への活用法

Webプラットフォームの進化が加速している。2026年3月、主要なブラウザエンジンすべてで相互運用が可能になった機能を示す指標「Baseline」において、多くの強力な機能が新たに「利用可能(Newly available)」な状態となった。

同時に、登場から30ヶ月が経過し、もはやポリフィルなしで安心して本番環境に投入できる「広く普及(Widely available)」の段階に達した技術も大量に増えている。レイアウト制御の高度化から、低遅延なネットワーク通信、洗練されたストリーミング機能まで、Webの可能性はさらに広がった。

この記事では、2026年3月のアップデート内容を整理し、それぞれの技術が実務にどのようなメリットをもたらすのかを詳しく掘り下げていく。Web制作の現場で「今、どの技術を使うべきか」を判断する材料として役立ててほしい。

最新機能とタイポグラフィの進化

最新機能とタイポグラフィの進化

今回のアップデートでは、CSSのタイポグラフィ制御に関する機能がいくつかBaselineの「Newly available」となった。これにより、これまで実現が難しかった高度なテキストレイアウトが、標準的なCSSのみで完結するようになる。

数式表示とテキストインデントの自由度

まず注目したいのが、font-family プロパティに新しく追加された math という値だ。これは数式コンテンツ(MathMLなど)をレンダリングするために特別に設計されたフォントセットを指定するものだ。技術文書や教育サイトにおいて、複雑な数式を美しく、かつ正確な間隔で表示するために不可欠な機能となる。

また、text-indent プロパティも大幅に強化された。新しく追加された each-line キーワードを使えば、ブロックの最初の行だけでなく、<br /> による強制改行の後のすべての行にインデントを適用できる。さらに hanging キーワードを使えば、1行目はそのままに、2行目以降をインデントさせる「ぶら下げインデント」が簡単に実現可能だ。

/* ぶら下げインデントの指定例 */
.bibliography {
  text-indent: 2em hanging;
}
通常のインデント(Before)

これは通常のテキスト配置だ。1行目の先頭だけが空くのが一般的だが、参考文献リストなどでは2行目以降を下げたい場合がある。

ぶら下げインデント(After: hanging)

参考文献:Web技術の進化に関する考察。この行は1行目だが、2行目以降は左側に余白が作られ、項目名が際立つようになる。

※このデモはCSSの概念を視覚化したイメージだ。実際の text-indent: hanging の動作は対応ブラウザで確認してほしい。

このコードを適用すると、参考文献リストや箇条書きのような、特定のデザインルールが求められるレイアウトを非常にシンプルに記述できるようになる。従来のようにネガティブマージンとパディングを組み合わせるハックは不要だ。

JavaScriptの反復処理を簡略化する新メソッド

スクリプト面では、Iterator.concat() が全ての主要ブラウザでサポートされた。これは、複数の反復可能なオブジェクト(配列やセットなど)を一つのイテレータに結合する静的メソッドだ。途中で中間的な配列を作成することなく、複数のデータソースを連続して処理できるため、メモリ効率の向上とコードの簡略化に寄与する。

データ通信とパフォーマンスの最適化

データ通信とパフォーマンスの最適化

Webアプリケーションの「体感速度」を左右する通信技術やストリーミング機能も、Baselineの新たなステージへと進んだ。特にリアルタイム性が求められるサービスにおいて、これらの技術は大きな武器になる。

WebTransportによる低遅延通信

WebTransport は、HTTP/3をベースにした現代的な通信APIだ。クライアントとサーバー間での双方向通信を可能にし、従来のWebSocketよりも効率的で低遅延なデータのやり取りを実現する。信頼性の高いデータ転送と、信頼性は低いが高速な「データグラム」の両方をサポートしている点が特徴だ。

例えば、オンラインゲームやライブストリーミングなど、一分一秒の遅延が許されないアプリケーションにおいて、WebTransport は理想的な選択肢となる。HTTP/3のメリットである「ヘッドオブラインブロッキング(一つのパケット損失が全体の通信を止める現象)」の解消を享受できるため、不安定なネットワーク環境下でもパフォーマンスが安定しやすい。

バイナリデータの効率的なストリーミング

Streams APIにおける「読み取り可能なバイトストリーム(Readable byte streams)」のフルサポートも重要な進展だ。これはバイナリデータの処理に最適化されており、開発者が用意したバッファに直接データを読み込むことができる。これにより、巨大なファイルのアップロードやダウンロード、動画の動的処理などにおけるメモリ管理が劇的に効率化される。

さらに、ブラウザレベルでのエラーやポリシー違反を通知する「Reporting API」も共通の基盤となった。コンテンツセキュリティポリシー(CSP)の違反や、非推奨機能の使用、ブラウザのクラッシュレポートなどを特定の終端(エンドポイント)へ送信し、集中的に監視することが可能になる。これは大規模なWebサービスの運用保守において、問題の早期発見に大きく貢献するはずだ。

「広く普及」した技術:CSS subgridと安定したレイアウト

「広く普及」した技術:CSS subgridと安定したレイアウト

2026年3月には、多くの技術が「Widely available(広く普及)」へと移行した。これは登場から30ヶ月が経過し、もはや「最新技術」というリスクを負うことなく、あらゆるプロジェクトで標準的に採用できることを意味している。

CSS subgridによるグリッドレイアウトの完成

中でも最大の影響力を持つのが CSS subgrid だ。これは、子要素が親要素のグリッド定義(列や行のサイズ)をそのまま継承できる機能だ。これまでは、異なる階層にある要素同士を正確に整列させるために複雑な計算やHTML構造の妥協が必要だったが、subgridを使えばDOM構造を美しく保ったまま、完璧な整列が実現できる。

従来のグリッド(Before)
カード1:タイトル
カード1:中身が長い
カード2:タイトル
カード2:短い
※カード内の中身の高さがバラバラになり、横で揃わない。
Subgridによる整列(After)
カード1:タイトル
カード1:共通の高さ
カード2:タイトル
カード2:共通の高さ
※親のグリッドを継承し、中身が短くても高さが自動的に揃う。
従来の課題  Subgridの解決

このデモが示すように、カード型レイアウト内のタイトルや本文の高さが、隣のカードと完全に一致するように制御できるのが subgrid の強みだ。もはや、JavaScriptで高さを揃える処理(いわゆるmatchHeightのようなもの)を書く必要はない。

表示の最適化とデバイス対応

また、image-set() 関数も普及段階に入った。これは <img> タグの srcset 属性に近い機能をCSSの background-image などで実現するものだ。ユーザーのデバイス解像度(DPI)に応じて、ブラウザが最適な画像ファイルを自動的に選択してダウンロードする。無駄な帯域を消費せず、Retinaディスプレイなどでは鮮明な画像を表示できる。

さらに、update メディアクエリも広く利用可能になった。これはデバイスの画面がどの程度の頻度で更新されるかを判定するものだ。スマートフォンのような高速リフレッシュレートを持つ画面と、電子書籍リーダー(e-ink)のような低速な画面を区別し、それぞれに最適なアニメーションや装飾を出し分けることができる。

実務での技術選定:Baselineをどう活用するか

実務での技術選定:Baselineをどう活用するか

Web技術がこれほど速く進化する中で、エンジニアやディレクターは「いつ、どの技術を実務に導入するか」という難しい判断を迫られる。GoogleのRachel Andrew氏は、自身の講演の中で、この課題に対する現実的なアプローチを提示している。

「安全」と「最新」のバランスを取る戦略

Andrew氏によると、Baselineのステータスを単なる「安全な機能のリスト」として見るのではなく、プロジェクトのリリース日に合わせてターゲットを設定することが重要だという。例えば、開発開始時点では「Newly available(最新)」であっても、プロジェクトの公開日が数ヶ月先であれば、その頃にはユーザーのブラウザ更新が進み、安全に使えるようになっている可能性がある。

一方で、特定のブラウザバージョンをサポートしなければならない制約がある場合、Baselineの「Widely available(広く普及)」に達している機能を選ぶのが最も堅実だ。この区分に入っている技術は、主要なブラウザすべてで安定して動作することが30ヶ月にわたって証明されている。ポリフィルによるパフォーマンス低下や、予期せぬバグのリスクを最小限に抑えつつ、モダンな開発体験を享受できる基準と言える。

コミュニティでの実装例と可視化

開発者コミュニティでも、このBaselineの考え方を積極的に取り入れる動きが出ている。Stu Robson氏は、自身のサイトに「Baseline status」を表示するWebコンポーネントを導入した事例を紹介している。特定の技術について解説する記事の冒頭に、その技術が現在のブラウザでどの程度サポートされているかをリアルタイムで表示する仕組みだ。

このような取り組みは、読者(またはクライアント)に対して、その技術が「今すぐ使えるものなのか」を即座に伝えるための優れた方法だ。Webコンポーネント自体はオープンソースで公開されており、Eleventyなどの静的サイトジェネレーターに限らず、WordPressなどあらゆるフレームワークで利用可能となっている。

この記事のポイント

  • 2026年3月のアップデートで、WebTransporttext-indent: hanging などが主要ブラウザで利用可能になった。
  • CSS subgridimage-set() などの強力な機能が「広く普及」の段階に達し、本番環境で安心して使えるようになった。
  • math フォントファミリーや Iterator.concat() により、数式表示やデータ処理のコードがよりシンプルになる。
  • Baselineのステータスを基準にすることで、プロジェクトのリリース時期に合わせた最適な技術選定が可能になる。
View Transitions APIでサイト体験を劇的に変える!CSSだけで実現する7つのページ遷移レシピ

View Transitions APIでサイト体験を劇的に変える!CSSだけで実現する7つのページ遷移レシピ

Webサイトのページを切り替える際、画面が瞬時にパッと切り替わるのではなく、モバイルアプリのような滑らかなアニメーションを伴う手法が注目されている。これを実現するのが「View Transitions API」だ。複雑なJavaScriptライブラリを使わずに、ブラウザの標準機能とCSSだけで高度な遷移エフェクトを実装できる。

View Transitions APIは主要なブラウザでのサポートが進み、実用的な段階に入った。特にマルチページアプリケーション(MPA)でも、ページ間の連続性を保った演出が可能になった点は大きい。ユーザー体験を向上させるための強力な武器になるだろう。

本記事では、CSS-Tricksの記事を基に、すぐに試せる7つのアニメーションレシピを紹介する。基本的なセットアップから、ぼかしや3D回転を組み合わせた応用例まで、その仕組みを詳しく解説していく。技術的なハードルは低いため、最新のWeb制作トレンドを取り入れたいエンジニアやデザイナーにとって有益な情報となるはずだ。

View Transitions APIの基本設定と導入のポイント

View Transitions APIの基本設定と導入のポイント

View Transitions APIを利用するには、まずブラウザに対して「このサイトでページ遷移のアニメーションを有効にする」という宣言が必要だ。これを「オプトイン(利用選択)」と呼ぶ。CSSの @view-transition アットルールを使い、遷移元と遷移先の両方のページで設定を行う必要がある。

@view-transitionルールでのオプトイン

最も基本的な設定は、CSSに数行のコードを追加するだけで完了する。共通のCSSファイルに記述しておくことで、サイト全体に適用できる。 navigation: auto を指定すると、通常のリンク移動時にブラウザが自動的にトランジションを実行するようになる。

@view-transition {
  navigation: auto;
}

この設定だけで、ブラウザのデフォルトである「クロスフェード(前の画面が消えながら次の画面が浮き上がる)」が適用される。さらに特定の名前(タイプ)を付けることで、ページの種類ごとに異なるアニメーションを使い分けることも可能だ。

ユーザーの好みに配慮したアクセシビリティ対応

アニメーションを実装する上で忘れてはならないのが、アクセシビリティへの配慮だ。OSの設定で「視覚効果を減らす」を選択しているユーザーに対しては、激しい動きを控えるべきだ。これを判定するのが prefers-reduced-motion メディアクエリである。

「動きを減らす」設定が無効(no-preference)の場合のみアニメーションを有効にする記述が推奨される。これにより、すべてのユーザーが快適にサイトを閲覧できる環境を整えられる。技術的な新しさを追求するだけでなく、こうした配慮をセットで行うのがプロの仕事だ。

@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
    types: my-transition;
  }
}

視覚効果で魅せるフェードとワイプのレシピ

視覚効果で魅せるフェードとワイプのレシピ

ここからは具体的なアニメーションレシピを見ていこう。まずは定番のフェード効果をアレンジしたものや、画面を拭き取るようなワイプ効果だ。これらは汎用性が高く、どんなジャンルのサイトにも馴染みやすい。

ぼかしを活用したPixelate dissolve

単なるフェードではなく、画面全体をぼかしながら切り替えるのが「Pixelate dissolve(ピクセレート・ディゾルブ)」だ。CSSの filter: blur() プロパティを使用する。古いページがぼやけて消えていき、新しいページがぼやけた状態から鮮明に現れる演出だ。

t=0% (開始・古いページ)
元のコンテンツ(鮮明)
t=50% (ぼかしながらクロスフェード中)
遷移中(ぼやけて溶け合う)
t=100% (完了・新しいページ)
新しいコンテンツ(鮮明)

このアニメーションは実際には約1.4秒かけて連続的に行われる。中間状態の filter:blur(6px)opacity:0.6 がスムーズに変化することで、画面全体が一度ぼやけてから鮮明な新画面が立ち上がる演出になる。短く設定すればキビキビとしたモダンな操作感、長くすればゆったりとした高級感のある印象を与えられる。

clip-pathで実現する上下左右のWipe効果

「Wipe(ワイプ)」は、画面をスライドさせて覆い隠すような効果だ。これには clip-path プロパティの inset() 関数を利用する。 inset() は要素の表示領域を上下左右からの距離で指定する仕組みで、この数値を 0% から 100% へ動かすことで、コンテンツを削り取るような動きを作れる。

例えば「Wipe up(上方向へのワイプ)」なら、古いページの表示領域を下から上へ 100% 削り、新しいページを上から下へ 0% に戻していく。 clip-path を使うメリットは、実際のレイアウトを崩さずに表示領域だけを制御できる点にある。非常にパフォーマンスが良く、滑らかな動きを実現できる。

ダイナミックな動きを作る回転とプッシュの演出

ダイナミックな動きを作る回転とプッシュの演出

次に、より動きの大きいダイナミックな演出を紹介する。これらはユーザーの目を引きやすいため、ポートフォリオサイトやキャンペーンページなど、個性を出したい場面で有効だ。 transform プロパティを駆使して、空間的な広がりを演出する。

遊び心のあるRotate in-out

「Rotate in-out(回転イン・アウト)」は、ページが回転しながら縮小して消え、新しいページが逆回転しながら拡大して現れるエフェクトだ。 scale(0)rotate(180deg) を組み合わせる。実用性は限られるかもしれないが、View Transitionsの表現力の高さを示す良い例だ。

t=0% (開始・元ページ)
元コンテンツ
t=50% (切替の瞬間・縮小して回転中)
回転中
t=100% (完了・新ページ)
新コンテンツ

このアニメーションは実際には約1秒かけて連続的に行われる。中間状態では transform:scale(0.3) rotate(90deg)opacity:0.4 によって元ページが縮小しながら回転して消え、その直後に新ページが逆方向から拡大して現れる。transform-origin: center を指定して画面中央を軸に回転させるのがポイントだ。また、回転角度を大きくしすぎるとユーザーが酔ってしまう可能性があるため、180度程度に抑えておくのが無難だ。

画面の隅から現れるDiagonal push

「Diagonal push(斜めプッシュ)」は、古いページを斜め方向に押し出し、新しいページを逆の斜め方向から滑り込ませる演出だ。 translate(-100%, -100%) のように X軸 と Y軸 の両方を同時に動かすことで斜めの移動を実現する。

この演出は、スライド資料を切り替えるような感覚をユーザーに与える。移動の軌跡に合わせて opacity (不透明度)を変化させると、より自然で洗練された印象になる。 ease (緩急)の指定を工夫することで、重厚感のある動きから軽快な動きまで調整可能だ。

形状と奥行きを活かした高度なトランジション

形状と奥行きを活かした高度なトランジション

最後に、より高度な視覚効果を紹介する。これらは clip-path の応用や 3D変形 を使用しており、実装には少しコツが必要だが、その分インパクトは非常に大きい。ブラウザが自動的に生成するスナップショットをどのように加工するかが鍵となる。

円形に広がるCircle wipe-out

「Circle wipe-out(サークル・ワイプ)」は、画面中央から円形に新しいページが広がっていく演出だ。映画のシーン切り替えなどで見かける手法である。 clip-path: circle() を使い、半径を 0% から 150% まで拡大させることで、画面全体を覆い尽くす動きを作る。

このレシピの面白い点は、背景色が同じページ間での遷移だ。背景が変わらずにコンテンツだけが円形に浮き上がってくるように見えるため、非常にシームレスな体験を提供できる。中心点は at 50% 50% だけでなく、クリックした位置に合わせて動的に変更するような応用も考えられる。

幕が開くようなCurtain reveal

「Curtain reveal(カーテン・リビール)」は、舞台の幕が左右に開くような動きだ。これも clip-path: inset() を使用するが、左右の値を 50% から 0% へと変化させる点が特徴だ。画面中央から左右に向かって新しいページが露出していく様子は、新しい体験の始まりを予感させる。

t=0% (幕が完全に閉じている)
舞台の中身
t=50% (幕が左右に開いて中身が半分見える)
舞台の中身
t=100% (幕が完全に開いて全体が見える)
舞台の中身がフル表示
幕(左右から覆う部分)  新ページのコンテンツ

このアニメーションは実際には約0.8秒かけて連続的に行われる。clip-path: inset(0 50% 0 50%) から inset(0 0 0 0) へと値が変化することで、左右から幕が引かれて中央のコンテンツが露出していく。実際のView Transitionsでは、::view-transition-new(root) に対してこのクリッピングアニメーションを適用することで、滑らかなカーテン効果が実現する。

3D空間でカードがめくれる3D flip

最もインパクトがあるのが「3D flip(3Dフリップ)」だ。ページ全体を一枚のカードに見立て、 Y軸 を中心に回転させて裏返すような演出を行う。 rotateY(90deg) でページを真横に向け、その瞬間に新しいページと入れ替えて 0deg に戻していく。

この演出を成功させるには、 perspective (遠近感)の設定が重要だ。奥行きを感じさせる数値を指定することで、平面的な画面の中に立体的な空間が生まれる。ただし、非常に目立つエフェクトなので、使いどころを慎重に選ぶ必要があるだろう。

実務でView Transitionsを導入する際の注意点

実務でView Transitionsを導入する際の注意点

View Transitions APIは非常に強力だが、実務に導入する際にはいくつか考慮すべき点がある。単にコードをコピーするだけでなく、プロジェクトの要件に合わせた最適化が必要だ。ここでは、技術的な側面とユーザー体験の両面から、筆者の見解を交えて解説する。

ブラウザサポートとフォールバックの考え方

View Transitions APIは現在、ChromeやEdgeなどのChromium系ブラウザで先行して実装され、SafariやFirefoxでも順次対応が進んでいる。しかし、すべてのユーザーが最新ブラウザを使っているわけではない。そのため、「アニメーションが動かなくてもコンテンツは正しく表示される」というプログレッシブ・エンハンスメントの考え方が不可欠だ。

幸いなことに、View Transitions APIは「対応していないブラウザでは単にアニメーションが無視されるだけ」という特性を持っている。特別なJavaScriptによる条件分岐を書かなくても、基本的には安全に導入できる。ただし、アニメーションがあることを前提とした複雑なUI設計は避けるべきだ。

パフォーマンスへの影響と最適化

トランジション実行中、ブラウザは画面のスナップショット(画像のようなもの)を作成し、それをアニメーションさせている。そのため、非常に高解像度な画像が大量にあるページや、複雑なDOM構造を持つページでは、一瞬の動作の重さを感じることがあるかもしれない。

対策としては、 will-change プロパティを適切に使ってブラウザに最適化を促すことや、アニメーションさせる要素を view-transition-name で限定することが有効だ。画面全体(root)を動かすのではなく、ヘッダーやロゴなどの共通要素を固定し、中身のコンテンツだけを動かすようにすると、より軽快で自然な遷移になる。

この記事のポイント

  • View Transitions APIはCSSだけでモバイルアプリのような滑らかなページ遷移を実現する
  • @view-transition ルールの navigation: auto 設定でMPAでも簡単に導入できる
  • clip-pathfilter を組み合わせることで、ぼかしやワイプなど多様な演出が可能になる
  • prefers-reduced-motion を使い、動きを好まないユーザーへの配慮を忘れない
  • 対応ブラウザ以外では通常の遷移になるため、プログレッシブ・エンハンスメントとして導入しやすい
CSS段組みレイアウトの革命!column-wrapで横スクロール問題を解消する

CSS段組みレイアウトの革命!column-wrapで横スクロール問題を解消する

CSSのMulti-column Layout(マルチカラムレイアウト)は、長い文章を新聞のように複数の列に分割して表示する仕組みだ。これまでWeb制作の現場では、コンテンツが溢れた際に強制的に横スクロールが発生してしまうという致命的な課題があり、利用シーンが限られていた。しかし、Chrome 145から導入された新しいプロパティによって、この状況が劇的に変わろうとしている。

最新のアップデートでは、column-wrap(カラム・ラップ)とcolumn-height(カラム・ハイト)という2つのプロパティが追加された。これにより、指定した高さを超えたコンテンツを次の「行」へと折り返して表示する、いわゆる「2Dフロー」が可能になった。これはWebにおけるテキスト表現の幅を大きく広げる重要な進化といえる。

本記事では、CSS-Tricksが報じた最新情報を基に、新しい段組みレイアウトの仕組みや具体的な活用方法、そして既存のCSS GridやFlexboxとの使い分けについて詳しく解説する。新しいプロパティがどのようにWebのユーザー体験を改善するのか、その全容を紐解いていこう。

従来のCSS段組みレイアウトが抱えていた大きな課題

従来のCSS段組みレイアウトが抱えていた大きな課題

CSSの段組みレイアウトは、古くから存在する仕様でありながら、現代のWebデザインでは主役になりきれなかった。その最大の理由は、コンテンツの量が増えたときの挙動がWebの閲覧スタイルに合っていなかったからだ。ここでは、なぜ従来の段組みが使いにくかったのかを振り返る。

横スクロールというUX上の壁

従来の段組みレイアウトでは、column-count(列の数)やcolumn-width(列の幅)を指定して文章を流し込む。しかし、親要素に高さを設定している場合、テキストがその高さを超えると、ブラウザは右側に新しい列を勝手に追加していく。その結果、ユーザーはページを横にスクロールしなければ最後まで読めないという状態に陥る。

Webサイトの基本は垂直(縦)スクロールだ。スマートフォンの普及により、縦に指を動かす操作が標準となった現代において、突如として現れる横スクロールはユーザーに混乱を与える。これが「UX(ユーザーエクスペリエンス)上の禁じ手」とみなされ、多くのデザイナーが段組みの使用を避ける原因となっていた。

レスポンシブ対応の難しさ

また、従来の段組みは「1次元的」な流れしか持っていなかった。コンテンツは常に左から右へと流れるだけで、画面の下に回り込むことはない。画面幅が狭いモバイル端末では、列を1つにするなどの調整が必要だが、高さの制限がある中でコンテンツを適切に収めるには、複雑な計算やJavaScriptによる制御が不可欠だった。CSSだけで完結できない点が、開発のハードルを上げていたのだ。

Chrome 145で登場した「column-wrap」と「column-height」

Chrome 145で登場した「column-wrap」と「column-height」

2026年4月にリリースされたChrome 145では、これらの問題を一挙に解決する新機能が実装された。それがcolumn-wrapプロパティだ。このプロパティの登場により、段組みレイアウトは「横に伸び続ける」仕組みから「縦に折り返す」仕組みへと進化した。

2Dフローを実現する新しい仕組み

新しく導入されたcolumn-wrap: wrapを指定すると、コンテンツが指定された高さを超えた際、右に新しい列を作るのではなく、下に新しい「段組みの行」を作成する。これにより、コンテンツ全体を縦スクロールの中で完結させることができるようになる。これは、Flexboxがflex-wrap: wrapで要素を次の行に送る挙動に近いが、段組みレイアウト独自の「テキストの分割」機能を保持している点が異なる。

具体的なコードの書き方と挙動の変化

新しいプロパティを使用する場合、基本的には対象の要素にcolumn-countcolumn-wrap、そして基準となる高さを指定する。以下のコード例を見てほしい。column-wrap: wrapを加えるだけで、横への溢れが解消される。

.article {
  column-count: 3;
  column-gap: 20px;
  column-wrap: wrap; /* 新プロパティ */
  height: 400px;
}
従来の段組み(Before / column-wrap なし)
列 1
列 2
列 3
列 4 (横溢れ)
※コンテンツが横に突き抜け、横スクロールが必要になる。
新しい段組み(After / column-wrap: wrap)
列 1
列 2
列 3
列 4 (折り返し)
※高さを超えた分が下の行に回り込み、縦スクロールで閲覧可能。
通常の列  横に溢れた列  下に折り返した列

上記のデモが示すように、column-wrap: wrapを適用することで、コンテンツは親要素の幅の中で適切に折り返される。これは単なる見た目の変化ではなく、Webサイト全体のアクセシビリティとユーザビリティを向上させる大きな一歩だ。

新しい段組みプロパティが活躍する3つの具体的な場面

新しい段組みプロパティが活躍する3つの具体的な場面

この新機能は、どのようなWebサイトで威力を発揮するのだろうか。CSS-Tricksの記事では、いくつかの実用的なユースケースが紹介されている。特に「固定の高さ」を扱うデザインにおいて、そのメリットは顕著だ。

高さが決まっているカード型レイアウト

もっとも身近な例は、ブログの記一覧や製品紹介などのカード型レイアウトだ。各カードの最大高さが決まっている場合、段組みレイアウトを使うことで、要素を美しく並べることができる。column-wrap: wrapを使えば、カードの数が増えてもレイアウトが崩れず、シームレスに次の行へと流れていく。Flexboxでも同様のことは可能だが、段組みレイアウトは「要素の途中で改行させない」といった制御(break-inside: avoidなど)が容易であるため、より洗練されたカード配置が可能になる。

雑誌や新聞のような本格的なマガジン形式

オンラインマガジンやニュースサイトにおいて、新聞のような多段組みデザインを採用したいケースは多い。これまでは、画面サイズに合わせて手動でコンテンツを分割するか、横スクロールを許容するしかなかった。新しいプロパティを使えば、デバイスの高さに合わせて自動的に段を折り返すことができるため、どの端末で見ても「読みやすい新聞スタイル」を維持できる。これは、コンテンツの連続性を保ちつつ、視覚的なリズムを生み出すのに最適だ。

垂直スクロールを活用したフルスクリーン・カルーセル

個人的に興味深い活用法として挙げられているのが、垂直方向のページめくり体験だ。column-heightをビューポート(画面の表示領域)いっぱいの高さ(100dvhなど)に設定し、CSSのscroll-snap-typeと組み合わせる。すると、コンテンツが画面の高さに合わせて自動的に「ページ」として分割され、ユーザーは縦にフリックするだけで雑誌をめくるように記事を読み進めることができる。JavaScriptを使わずに、CSSだけでこのような高度なインタラクションが実現できるのは驚きだ。

既存のCSSレイアウト手法と新機能の使い分け

既存のCSSレイアウト手法と新機能の使い分け

新しい段組みレイアウトが登場したからといって、CSS GridやFlexboxが不要になるわけではない。むしろ、それぞれの特性を理解し、適切に使い分けることが重要だ。ここでは、それぞれの設計思想の違いを整理する。

CSS GridやFlexboxとの決定的な違い

CSS GridやFlexboxは、基本的に「個別の要素(子要素)」をどのように配置するかを管理するシステムだ。対して、段組みレイアウト(Multi-column)は「単一の連続したコンテンツ」をどのように分割するかを管理する。この違いは大きい。

例えば、1つの長い長文を途中で切り離すことなく複数の列に流し込みたい場合、GridやFlexboxでは文章を物理的に分割して複数のHTML要素に分ける必要がある。しかし、段組みレイアウトなら1つの<p>タグの中身をそのまま分割できる。構造を壊さずにレイアウトを変更できるのは、段組みレイアウトだけの特権だ。

注目が集まるCSS Masonryとの比較

現在、CSSの仕様策定が進んでいる「Masonry(メーソンリー)レイアウト」とも比較されることが多い。Masonryは高さの異なる要素を隙間なく敷き詰める手法だが、段組みレイアウトもcolumn-countを使えば似たような見た目を作ることができる。ただし、Masonryが「要素の順序」を重視するのに対し、段組みレイアウトはあくまで「コンテンツの流れ」を重視する。情報の優先順位が重要なニュース記事などでは段組みが適しており、ビジュアル重視のギャラリーサイトなどではMasonryが適しているといえるだろう。

導入時に注意すべき制限事項とブラウザ対応状況

導入時に注意すべき制限事項とブラウザ対応状況

非常に便利な新機能だが、実務で採用する際にはいくつか注意点がある。まず、2026年4月時点でのブラウザ対応状況だ。このプロパティは現在、Chrome 145以降でのみサポートされている。FirefoxやSafari、Edgeではまだ利用できないため、現時点では「プログレッシブ・エンハンスメント」の考え方で導入するのが現実的だ。

プログレッシブ・エンハンスメントとは、基本の機能はすべてのブラウザで提供しつつ、最新ブラウザではより良い体験を提供する設計手法を指す。未対応ブラウザでは従来の1カラム表示やシンプルな段組みにし、Chromeユーザーには進化した2Dフローを提供するという構成が望ましい。

また、動的なコンテンツへの対応も課題だ。ユーザーが投稿するコメントやCMSから配信される記事など、高さが予測できないコンテンツの場合、column-heightを固定してしまうと、不自然な余白ができたり、意図しない場所で折り返されたりする可能性がある。完全にレスポンシブな設計にするには、依然としてメディアクエリを駆使して、画面サイズごとに最適な列数や高さを微調整する作業が必要になるだろう。

この記事のポイント

  • Chrome 145で導入されたcolumn-wrap: wrapにより、段組みの横スクロール問題が解消された。
  • コンテンツが高さを超えた際に「下の行」へ折り返す2Dフローが実現可能になった。
  • 固定高のカードレイアウトや、新聞スタイルのデザイン、垂直カルーセルなどで特に威力を発揮する。
  • GridやFlexboxが「要素の配置」を得意とするのに対し、段組みは「単一コンテンツの分割」に特化している。
  • 現時点ではブラウザ対応が限定的なため、未対応環境へのフォールバックを考慮した設計が不可欠だ。
CSSで日付範囲を選択する::nth-child(n of selector)を活用したスマートなUI実装術

CSSで日付範囲を選択する::nth-child(n of selector)を活用したスマートなUI実装術

Webサイトでホテルの予約や航空券の検索を行う際、カレンダーから「開始日」と「終了日」を選ぶUIは欠かせない要素だ。この「日付範囲の選択」を実装する場合、従来はJavaScriptを駆使して、選択された期間内のすべての要素に特定のクラスを付与する手法が一般的だった。

しかし、最新のCSSセレクタを活用すれば、JavaScriptの役割を最小限に抑えつつ、高度な範囲指定のスタイリングが可能になる。特に「:nth-child(n of selector)」という構文は、複雑な要素選択を劇的に簡素化する力を持っている。

この記事では、CSS-Tricksで紹介された手法を基に、最新のCSSセレクタを組み合わせてスマートな日付範囲セレクターを構築する方法を詳しく解説する。コードの保守性を高め、ブラウザの負荷を軽減する新しい実装アプローチを見ていこう。

:nth-child(n of selector) の基礎知識

:nth-child(n of selector) の基礎知識

まず、今回の実装の核となる「:nth-child(n of selector)」について理解を深めておこう。これはCSSの「擬似クラス」と呼ばれる機能の一つで、特定の条件に合う要素の中から、さらに順番を指定して選択できる強力なツールだ。

従来の :nth-child との違い

従来の :nth-child(n) は、「親要素から見て何番目の子要素か」を基準に判定していた。例えば .item:nth-child(2) と書いた場合、「2番目の子要素であり、かつ .item クラスを持っている要素」にスタイルが適用される。もし2番目の要素が別のクラスだった場合、何も選択されないという問題があった。

一方で、新しい :nth-child(n of .selector) 構文は、まず指定したセレクタ(この場合は .selector)に一致する要素だけをフィルタリングし、その抽出されたリストの中からn番目を選択する。これにより、間に別の要素が挟まっていても、特定のクラスを持つ要素だけを正確にカウントできるようになった。

フィルタリング機能の仕組み

この構文の最大のメリットは、動的に変化する状態に対しても柔軟に対応できる点だ。例えば、ユーザーがチェックを入れた要素だけを対象に「1番目のチェック済み要素」や「2番目のチェック済み要素」を指定できる。これは、日付範囲の開始点と終了点を特定する際に非常に役立つ仕組みだ。

通常の :nth-child(2) の場合
1. 項目(対象外)
2. 広告(2番目だがクラスが違うため不適合)
3. 項目(3番目なので不適合)
:nth-child(2 of .item) の場合
1. 項目(1番目)
2. 広告(無視される)
3. 項目(.item の中で2番目なのでヒット!)

このデモのように、特定の要素群(この場合は「項目」)だけを対象にして順番を数えられるのが、このセレクタの革新的な点だ。

カレンダーの基本レイアウトを作成する

カレンダーの基本レイアウトを作成する

日付範囲選択を実装するために、まずは土台となるカレンダーのレイアウトを準備する。CSS Grid(グリッドレイアウト)を使えば、カレンダーのような格子状の配置は驚くほど簡単に記述できる。

Grid Layoutによる7列配置

カレンダーは1週間が7日であるため、7つの列を持つグリッドを作成する。grid-template-columns:repeat(7, 1fr) と指定することで、親要素の幅を均等に7分割した列が自動的に生成される。これにより、日付の数字を順番に並べるだけで、自動的に適切な位置で改行されるようになる。

HTML構造の設計

HTML側では、各日付をリスト要素(<li>)として配置する。各日付の中には、チェック状態を管理するための <input type="checkbox"> を隠し要素として入れておく。ユーザーが日付をクリックした際に、このチェックボックスが切り替わる仕組みだ。

<ul id="calendar">
  <!-- 曜日の表示 -->
  <li class="day">月</li>
  <li class="day">火</li>
  <!-- ...土日まで -->

  <!-- 日付の表示 -->
  <li class="date">01<input type="checkbox" value="01"></li>
  <li class="date">02<input type="checkbox" value="02"></li>
  <!-- ...31日まで -->
</ul>

CSSでは、この #calendar に対して display:grid を適用し、曜日と日付が綺麗に整列するように調整する。各日付(.date)は、ユーザーがクリックしやすいように十分なサイズと適切なパディングを持たせておくことが重要だ。

JavaScriptとCSSの役割分担

JavaScriptとCSSの役割分担

日付範囲の選択において、すべての処理をCSSだけで完結させることは現在の仕様では難しい。チェックボックスの「2つまでしか選択させない」といったロジックや、3つ目が選ばれた際の挙動制御にはJavaScriptが必要となる。しかし、ここで大切なのは「役割の最適化」だ。

チェック状態の制御ロジック

JavaScriptの主な仕事は、ユーザーのクリックに応じて checked 属性を適切に操作することだ。CSS-Tricksの記事で紹介されているロジックでは、新しく日付がクリックされた際、既存の選択範囲との位置関係を判定し、開始日または終了日を更新する処理を行っている。

ここで :nth-child(n of selector) がJS内でも威力を発揮する。querySelector メソッドでこのセレクタを使うことで、「現在チェックされている要素のうち、1番目のもの」を :nth-child(1 of :has(:checked)) として直接取得できるのだ。わざわざループを回してインデックスを探す手間が省ける。

CSSセレクタによる要素の特定

JS側で「範囲が選択された」と判断した際、親要素であるカレンダーに isRangeSelected といったクラスを付与する。これ以降の「範囲内の要素を青く塗る」といったビジュアル面の処理は、すべてCSSの領分となる。JSは状態(State)を管理し、CSSは見た目(View)を制御するという理想的な分離が実現できる。

この手法により、JSのコード量は大幅に削減される。DOMの書き換え(クラスの付け外し)を最小限に抑えられるため、ブラウザの再描画コストも低減され、結果としてパフォーマンスの向上につながるのだ。

範囲スタイリングの魔法

範囲スタイリングの魔法

さて、いよいよ本題である「範囲内のスタイリング」について解説する。クラスを一つずつ付与することなく、CSSだけで「開始日と終了日の間」を特定するには、高度なセレクタの組み合わせが必要だ。

兄弟要素セレクタ(~)との組み合わせ

範囲を指定するための第一歩は、後続兄弟結合子(~)を使うことだ。これは「ある要素より後ろにある兄弟要素」をすべて選択する記号だ。:nth-child(1 of :has(:checked)) ~ .date と記述すれば、1番目にチェックされた日付より後ろにあるすべての日付を選択できる。

否定擬似クラス(:not)による制御

しかし、これだけでは「終了日より後ろの要素」まで選択されてしまう。そこで :not セレクタを組み合わせて、範囲を制限する。具体的には、「2番目にチェックされた要素より後ろにある要素ではないもの」という条件を加えるのだ。

.isRangeSelected :nth-child(1 of :has(:checked)) ~ :not(:nth-child(2 of :has(:checked)) ~ .date) {
  background-color:rgb(228 239 253);
}

この一見複雑なコードを分解すると、「1番目のチェック要素より後にある要素」の中から、「2番目のチェック要素より後にある要素」を除外していることになる。結果として、1番目と2番目の間にある要素だけが綺麗に抽出されるという仕組みだ。

ステップ1:1番目のチェック以降をすべて選択(~)
1234567
チェック済み  ~で選択された範囲  対象外
ステップ2:2番目のチェック以降を除外(:not)→ 範囲が確定
1234567
開始日・終了日  選択範囲(2〜5の間)  対象外

※このデモはCSSの概念を視覚化したイメージだ。実際の動作はブラウザのデベロッパーツール等で確認してほしい。

実務におけるメリットと独自の分析

実務におけるメリットと独自の分析

この新しいアプローチには、単に「コードが短くなる」以上の価値がある。Web制作の実務において、どのようなインパクトをもたらすのかを考察してみよう。

コードの保守性とパフォーマンス

最大のメリットは、JavaScriptがDOMの状態を過剰に意識しなくて済むようになることだ。従来の手法では、日付がクリックされるたびに、範囲内の全要素をループで回して .is-in-range といったクラスを付け替える必要があった。要素数が多い場合、この処理は無視できない負荷になる。

一方、今回の手法では、JSが行うのは「どのチェックボックスをオンにするか」という最小限の状態変更のみだ。見た目の更新はブラウザのCSSエンジンがネイティブで高速に処理するため、ユーザー体験はより滑らかになる。また、スタイルの変更が必要になった際も、JSを触ることなくCSSの修正だけで完結する保守のしやすさがある。

アクセシビリティへの配慮

この実装は、アクセシビリティ(利用しやすさ)の観点からも優れている。ネイティブのチェックボックスをベースにしているため、スクリーンリーダーなどの支援技術に対しても「どの項目が選択されているか」という情報を標準的な方法で伝えることができる。見た目だけでなく、情報の構造としても正しい状態を保ちやすいのだ。

ただし、注意点もある。:nth-child(n of selector) は比較的新しい機能であるため、古いブラウザ(特に数年前のスマートフォンなど)では動作しない可能性がある。実務で導入する際は、対象となるユーザーのブラウザ利用状況を確認し、必要に応じて基本的な背景色のみを適用するようなフォールバック(代替処理)を用意するのが賢明だろう。

この記事のポイント

  • :nth-child(n of selector) は特定の条件に合う要素の中だけで順番を数えられる
  • JavaScriptは状態管理に専念し、複雑な範囲スタイリングはCSSに任せるのが現代流
  • 兄弟要素セレクタ(~)と否定擬似クラス(:not)を組み合わせることで範囲を特定できる
  • DOM操作の削減により、コードの保守性とパフォーマンスの両立が可能になる
MDNのフロントエンド刷新の裏側:ReactからWeb ComponentsとRspackへの移行

MDNのフロントエンド刷新の裏側:ReactからWeb ComponentsとRspackへの移行

世界中のエンジニアが頼りにする技術ドキュメントサイト「MDN Web Docs」が、フロントエンドのアーキテクチャを根本から作り直した。今回の刷新は単なるデザインの変更ではなく、長年抱えていた技術的な課題を解決するための大規模な再設計となっている。

MDNのチームは、これまで利用していたReactベースのSPA(Single Page Application / シングルページアプリケーション)から脱却し、Web Componentsと独自のサーバーサイドレンダリング(SSR)を組み合わせた新しい仕組みへ移行した。さらに、ビルドツールをWebpackからRust製のRspackに切り替えることで、開発環境の起動時間を2分から2秒へと劇的に短縮している。

なぜMDNのような巨大なサイトがReactを離れ、ネイティブに近い技術を選んだのか。その背景には、ドキュメントサイト特有の課題と、最新のWeb標準技術への信頼があった。この記事では、MDNの新しいフロントエンドがどのような思想で構築されたのか、その詳細を解説する。

なぜMDNはフロントエンドを根本から作り直したのか

なぜMDNはフロントエンドを根本から作り直したのか

MDNのフロントエンド刷新の最大の動機は、旧システム「yari」が抱えていた深刻な技術負債の解消だ。yariはCreate React Appをベースに構築されていたが、MDNのような静的コンテンツが主体のサイトには不向きな部分が多く、場当たり的な修正が積み重なっていた。

React SPAが抱えていた「ラッパー」という限界

旧システムにおいて、Reactアプリは静的なHTMLコンテンツを包む「ラッパー」に過ぎなかった。MDNのドキュメントの大部分はMarkdownから生成された静的なテキストだが、Reactはこのコンテンツの内容を直接把握することができなかった。

そのため、ドキュメント内に「コードのコピーボタン」のようなインタラクティブな要素を追加する場合、Reactの枠組みの外で標準的なDOM API(Document Object Model API / ブラウザがHTMLを操作するための仕組み)を直接操作する必要があった。これにより、サイトの一部はReactで書かれ、別の部分は直接的なDOM操作で書かれるという、管理しにくい二重構造が生まれていた。

複雑化しすぎたビルド設定とCSSの管理

ビルド環境も限界に達していた。Create React Appのデフォルト設定では対応できない要件が増えた結果、設定を「eject(イジェクト / ツールによる自動管理を解除して手動管理に移行すること)」せざるを得なくなり、Webpackの設定が複雑怪奇なものになっていた。

CSSについても、Sass(サス / CSSを効率的に書くための拡張言語)とモダンなCSS変数が混在し、スコープ(影響範囲)の管理が不十分だった。あるコンポーネントのスタイルを変更すると、予期せぬ場所のデザインが崩れるといった問題が頻発していた。また、CSSを適切に分割する仕組みがなかったため、ユーザーは常に巨大なCSSファイルをダウンロードさせられていた。

Web ComponentsとLitがもたらした相互運用の柔軟性

Web ComponentsとLitがもたらした相互運用の柔軟性

技術負債を解消するための切り札として選ばれたのが、Web Components(ウェブコンポーネント)だ。Web Componentsとは、HTMLの新しいタグを自分で定義できるブラウザ標準の機能だ。MDNチームは、このコンポーネント開発を効率化するために「Lit(リット)」という軽量なライブラリを採用した。

コンテンツ内にインタラクティブ要素を直接埋め込む

Web Componentsの最大の利点は、どんなHTML環境でも「カスタムタグ」として機能することだ。Reactのような特定のフレームワークに依存せず、Markdownから生成されたHTMLの中に <mdn-copy-button> のようなタグを直接配置するだけで動作する。

これにより、ドキュメント本文という「静的な世界」と、UIコンポーネントという「動的な世界」の境界線が消えた。MDNの著者は、複雑なJavaScriptの知識がなくても、特定のタグを記述するだけで高度な機能を記事に追加できるようになった。

Scrimbaの事例で見えた「ネイティブに近い」開発

Web Componentsの有効性を証明したのが、学習プラットフォーム「Scrimba」との連携だ。MDNのカリキュラムページでは、インタラクティブな学習環境を埋め込む必要があった。これをWeb Componentsで実装することで、ユーザーがクリックするまで重い <iframe> を読み込まない、といった制御が非常に簡潔に記述できるようになった。

Litを使用することで、ReactのJSX(JavaScript内にHTML風の構文を書く手法)に近い感覚で開発できつつ、コンパイル不要な標準のJavaScriptとして動作する。これにより、開発のしやすさと実行時のパフォーマンスを両立させた。

SPAを脱却し「アイランド・アーキテクチャ」へ

SPAを脱却し「アイランド・アーキテクチャ」へ

MDNは今回の刷新で、サイト全体を一つの巨大なアプリとして動かすSPAを完全にやめた。代わりに採用したのが、静的なHTMLをベースにしつつ、必要な部分だけを独立したコンポーネントとして動かす「アイランド・アーキテクチャ」に近い考え方だ。

必要な場所だけで動くWeb Components

新しいMDNでは、ページが読み込まれた後にDOM全体をスキャンし、mdn- で始まるカスタムタグを探す仕組みを導入している。特定のコンポーネントがページ内に存在する場合のみ、そのコンポーネントに必要なJavaScriptを非同期で読み込む。

このアプローチにより、ユーザーは自分が閲覧しているページに関係のないJavaScriptをダウンロードする必要がなくなった。トップページのナビゲーション、検索モーダル、記事内のインタラクティブな例など、それぞれが独立した「島」として機能する。

旧SPA方式
全機能のJSを同梱
読み込みが遅い
全体が1つの塊
新アイランド方式
必要なJSのみ読込
表示が爆速
機能ごとに独立

このデモは、ページ全体のJSを一度に読み込むSPAと、必要な部品だけを読み込む新方式の違いを視覚化したものだ。

Litを活用した独自のサーバーコンポーネント

MDNのチームは、クライアントサイドだけでなくサーバーサイドのレンダリングにもLitの仕組みを応用した。独自の「ServerComponent」クラスを作成し、Node.js上でHTMLを組み立てている。

特筆すべきは、CSSの最適化だ。サーバー側でどのコンポーネントが使われたかを追跡し、そのページに必要なCSSだけを <link> タグとして書き出す。これにより、未使用のスタイルシートが読み込まれることを防ぎ、レンダリングの高速化に成功している。

徹底したパフォーマンス最適化と開発体験の向上

徹底したパフォーマンス最適化と開発体験の向上

アーキテクチャの変更に加え、MDNは開発ツールやブラウザ互換性の判断基準も刷新した。これにより、エンドユーザーだけでなく、サイトを維持管理するエンジニアの生産性も向上している。

Rspackの採用で起動時間を2分から2秒へ

開発環境の劇的な改善をもたらしたのは、ビルドツール「Rspack」への移行だ。Rspackは、広く使われているWebpackと互換性を持ちながら、コア部分がRust(ラスト / 高速なシステム開発向け言語)で書かれているため、非常に高速に動作する。

以前の環境では、開発サーバーを立ち上げるだけで約2分かかっていた。ちょっとした修正を確認するために数分待つ必要があり、開発者の大きなストレスとなっていた。Rspackの導入により、この待ち時間はわずか2秒にまで短縮された。開発体験の向上は、結果としてサイトの更新頻度や品質の向上に直結する。

Baselineに基づいたモダン機能の積極採用

MDNは「どの技術がどのブラウザで使えるか」を定義する「Baseline(ベースライン)」プロジェクトを推進している。自サイトの開発においても、このBaselineの基準を厳格に適用している。

「Baseline Widely Available(主要ブラウザで広く利用可能)」な技術は積極的に使い、比較的新しい技術についてはポリフィル(古いブラウザで新しい機能をエミュレートするコード)を最小限に抑えつつ、段階的な機能拡張(Progressive Enhancement)として実装している。これにより、最新ブラウザの性能を最大限に引き出しつつ、古い環境でも情報を損なわない設計を実現した。

独自の分析:静的サイトの未来とMDNの選択

独自の分析:静的サイトの未来とMDNの選択

今回のMDNの決断は、近年のWeb開発トレンドにおける重要な転換点を示している。一時期、あらゆるサイトをReactなどのSPAで構築するのが正解とされた時期があった。しかし、MDNの事例は「コンテンツ主体のサイトには、HTMLネイティブに近い構成が最適である」という原点回帰の正当性を証明している。

特筆すべきは、Web Componentsという「標準技術」への信頼だ。特定のフレームワークの流行り廃りに左右されず、ブラウザが直接理解できる形式でコンポーネントを構築することは、MDNのような「Webの辞書」としての永続性が求められるサイトにとって、最も合理的な選択と言える。

また、RspackのようなRust製ツールの台頭も無視できない。JavaScriptで書かれたツールチェーンの限界を、低レイヤーの言語で書かれたツールが打破していく流れは、今後さらに加速するだろう。MDNの刷新は、最新のWeb標準と高速なビルドツールが組み合わさることで、いかに強力なプラットフォームが構築できるかを示す、最高の手本となっている。

この記事のポイント

  • MDNはReact SPAからWeb Componentsベースの新アーキテクチャへ移行した。
  • Litを採用し、静的なMarkdownコンテンツ内に動的な要素を直接埋め込める柔軟性を確保した。
  • アイランド・アーキテクチャにより、必要なJavaScriptだけを非同期で読み込む高速な表示を実現した。
  • Rspackの導入により、開発環境の起動時間を2分から2秒へと劇的に短縮した。
  • Baseline基準を採用し、モダンなWeb標準技術を最大限に活用しつつ互換性を維持している。