ドキュメントにフルページパターンを表示するための基盤整備

shoelace-style/webawesome

WebAwesomeのドキュメントシステムに、フルページパターンを適切に提示するための複数の改善が加えられました。コード例のトランスフォーマー拡張、CSSのロジカルプロパティ化、コンテナクエリへの移行、そしてファイルエクスプローラーUIスタイルの追加が主な変更点です。

背景

これまでのドキュメントシステムは、フルページパターン(アプリ・ブログ・ECサイトなどの実際の画面を模したレイアウト)を効果的に提示する手段が不足していました。<wa-zoomable-frame> を使ったコード例の表示機能はあったものの、<wa-include> のトップレベルでの直接利用や、プレビューなしのコード表示、ネストした <wa-include> の展開制御といった機能が欠けていました。

サイドバーのナビゲーション構造も見直され、Layouts エントリが一覧の下部から上部へ移動し、AppBlogEcommerce のアンカーリンクを持つ構成に再編されています。これに伴い、/docs/patterns/layouts/ecommerce//docs/patterns/layouts/blog/ といった個別パスは廃止されました。

技術的な変更

code-examples.js の拡張

コード例処理の中核となる関数が getFrameSource から getElementSource へリファクタリングされ、<wa-zoomable-frame> だけでなく <wa-include> のトップレベル要素も同様に処理できるようになりました。

変更前:

const getFrameSource = frame => {
  const selectSrc = frame?.hasAttribute('data-select-src');
  if (!selectSrc) return;
  let src = frame.getAttribute('src');
  let source = frame.getAttribute('srcdoc');
  if (!source && src) {
    src += src.match(/\.html/) ? '' : `${src.endsWith('/') ? '' : '/'}index.html`;
    src = src.split('?')[0].split('#')[0];
    source = readFileSync(path.join(baseDir, src), 'utf8');
  }
  const selectors = frame?.getAttribute('data-select-src');
  if (selectors) {
    const sourceNode = parse(source, { comment: true, voidTag: { closingSlash: true } });
    sourceNode.querySelectorAll('wa-include').forEach(e => replaceIncludeWithSource(e));
    // ...
  }
};

変更後:

const getElementSource = element => {
  const selectSrc = element?.hasAttribute('data-select-src');
  const expandIncludes = element?.hasAttribute('data-expand-includes');
  if (!selectSrc) return;
  let src = element.getAttribute('src');
  let source = element.getAttribute('srcdoc');
  const isInclude = element.tagName?.toLowerCase() === 'wa-include';

  if (!source && src) {
    // For wa-include, read the source file directly
    // For frames, normalize src for file path resolution
    if (!isInclude) {
      src += src.match(/\.html/) ? '' : `${src.endsWith('/') ? '' : '/'}index.html`;
      src = src.split('?')[0].split('#')[0];
    }
    source = readFileSync(path.join(baseDir, src), 'utf8');
  }
  const selectors = element?.getAttribute('data-select-src');
  if (selectors) {
    const sourceNode = parse(source, { comment: true, voidTag: { closingSlash: true } });
    if (expandIncludes) {
      sourceNode.querySelectorAll('wa-include').forEach(e => replaceIncludeWithSource(e));
    }
    // ...
  }
};

主な変更点は3つです。まず、isInclude フラグによる要素種別の判定で、<wa-include> の場合はパスの正規化(index.html 付与やクエリ文字列除去)をスキップし、ファイルを直接読み込みます。次に、data-expand-includes 属性によるネスト展開の明示的な制御で、以前は <wa-zoomable-frame> で非boolean値の data-select-src を指定した場合に自動的に展開されていた動作が、オプトイン方式に変更されました。さらに、no-preview クラスの追加により、<wa-zoomable-frame><wa-include> のパース結果を描画せずコードのみを表示できるようになりました。

code-examples.css のロジカルプロパティ化

code-examples.css では、物理プロパティからロジカルプロパティへの移行が行われました。具体的には、border-top-left-radius / border-top-right-radiusborder-start-start-radius / border-start-end-radius に、top / left / right / bottominset-block-start / inset-inline-start 等に置き換えられています。また、境界線のスタイリング値が CSS カスタムプロパティ(--code-example-border-color--code-example-border-radius--code-example-border-width)にまとめられ、一元管理されるようになっています。

margin-block-end の付与条件も変わっています。従来は .code-example に常に適用されていた下マージンが、:has(+ *) セレクタを使って後続要素が存在する場合のみ適用されるよう改善されました。これにより、コードブロックが最後の要素である場合の余分な余白がなくなります。

docs.css のコンテナクエリへの移行

docs.css の検索インデックス(.search-list)において、メディアクエリがコンテナクエリに置き換えられました。

変更前:

@media screen and (max-width: 1470px) {
  grid-template-columns: repeat(3, 1fr);
}
@media screen and (max-width: 960px) {
  grid-template-columns: repeat(2, 1fr);
}
@media screen and (max-width: 500px) {
  grid-template-columns: repeat(1, 1fr);
}

変更後:

.search-list {
  container-type: inline-size;
}

@container (width < 768px) {
  grid-template-columns: repeat(3, 1fr);
}
@container (width < 576px) {
  grid-template-columns: repeat(2, 1fr);
}
@container (width < 384px) {
  grid-template-columns: repeat(1, 1fr);
}

あわせて、2カラムレイアウトを基準とした .large グリッドバリアントが追加されています。これはフルページパターンのサムネイルギャラリーなど、より大きなカードを表示したい場面での使用を想定しています。また、ヘッダースタイルの適用対象が &::part(header) から &[with-header]::part(header) に絞り込まれ、with-header 属性を持つ要素のみにヘッダースタイルが適用されるようになっています。

utils.css へのファイルエクスプローラーUIの追加

utils.css.file-explorer コンポーネントのスタイルが追加されました。これはフルページパターンの説明ページで、パターンを構成する複数のファイルをツリー形式で表示し、選択したファイルのコードをパネルに表示するUIを実現します。

レイアウトは grid-template-columns: 30ch 1fr の2カラムグリッドで、左ペインに <wa-tree> によるファイルツリー、右ペインにコードキャンバスを配置します。ファイルツリーは position: stickyoverflow-y: auto で独立スクロール可能にされており、高さは block-size: 60vh に固定されています。選択中のアイテムは &:state(selected)::part(item) でスタイリングされており、Web Componentsの CSS Part と Custom State に対応した実装です。また、wa-zoomable-frame:has(+ .file-explorer) セレクタにより、ファイルエクスプローラーの直前に置かれた <wa-zoomable-frame> には自動的に枠線とmin-heightが適用されます。

設計判断

data-expand-includes による明示的なオプトインという設計が採用された点が注目されます。以前は <wa-zoomable-frame> で非boolean値の data-select-src を指定すると <wa-include> が自動展開されていましたが、この動作が暗黙的すぎるとして、属性による明示的な制御に変更されています。自動展開が常に望ましいわけではなく(<wa-include> の参照を展開せずそのままコードとして示したい場合など)、利用側が展開の有無を選択できる柔軟性が確保されています。

コンテナクエリへの移行も重要な設計判断です。メディアクエリはビューポート幅に依存するため、サイドバーの開閉状態などコンテナの実際の幅が変化する文脈では正確に機能しません。container-type: inline-size.search-list 自身に持たせることで、コンポーネントが埋め込まれる場所の幅に応じたレスポンシブ対応が可能になっています。

まとめ

本PRは、フルページパターンのドキュメント化という具体的なユースケースに対応するため、コード例処理の汎化(フレームからインクルードへの対応拡大)、CSSのモダン化(ロジカルプロパティ・コンテナクエリ)、そしてファイルエクスプローラーUIの追加という複数の変更を一体的に実施しています。各変更は独立した改善でありながら、フルページパターンの提示という共通目的のもとに整合しており、ドキュメントの表現力を大幅に高める基盤となっています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
f2ea279a

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→各セクション(各論)→まとめ(結論)の構成が明確で、必須要素である「背景」「技術的な変更」に加え、任意要素の「設計判断」も含まれており、非常に質の高い構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)とGitHubのPRリンク記法([PR #2109](URL))が、ガイドライン通りに正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

「ロジカルプロパティ」「コンテナクエリ」「Web Componentsの CSS Part と Custom State」といった専門用語を前提としており、対象読者である専門知識を持つエンジニアに適した技術レベルと表現です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクション、各パラグラフが「総論→各論」の構造で書かれ、トピックセンテンスが先頭に配置されています。1段落1トピックの原則も守られており、非常に明快で読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiff情報(`code-examples.js`, `docs.css`など)と正確に一致しており、改変や省略なく引用されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

PR情報で使用されている「container queries」「logical properties」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

`getElementSource`関数のリファクタリング内容や、コンテナクエリ移行の技術的メリットなど、Diffに基づいた説明が技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(`data-expand-includes`の導入理由、サイドバーのナビゲーション変更など)は、PRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2109)、関数名(`getElementSource`)、CSSクラス名(`.file-explorer`)などの数値・固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトル「ドキュメントにフルページパターンを表示するための基盤整備」は、PRのタイトル「Support showing full-page patterns in the docs」の内容を的確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に記載のないバージョン情報やリリース日程などの外部知識の捏造はなく、すべての記述が提供された情報源に基づいています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「以前は...自動的に展開されていた」という記述が、PR Descriptionの「this previously happened automatically」と一致しており、時間表現は正確です。