代表的な面接トピック

フロントエンド面接:CSS Anchor Positioningモデルとプリミティブ

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

CSS Anchor Positioningのレイアウトモデルを説明してください。カードに付加された注釈(annotation)の例を用いて、アンカーの命名、位置の関連付け、位置・サイズ関数、およびスコープ設定を説明してください。

1. 設問とユースケース

価格の注釈(annotation)が製品カードに追従します。そのインライン方向の先頭(inline start)はカードのコンテンツの先頭に揃い、インラインサイズはカードと一致します。この関係は、左横書き(LTR)、右横書き(RTL)、縦書きの各書字方向(writing mode)に対応する必要があります。あるリストでは多数のカードと注釈のペアが描画され、別のビューでは2つの独立した端点間に範囲マーカーが引き伸ばされます。

この質問は、CSS Anchor Positioningレイアウトモデルに関するものです。要素がどのようにアンカーになるか、絶対配置された要素がどのようにデフォルトアンカーを選択するか、位置およびサイズ関数がどのように解決されるか、そして名前スコープと包含ブロックがどのようにその関係を制約するかを扱います。ビューポートとの衝突やフローティングウィジェットのインタラクションは、別の実装トピックとなります。

2. 面接官がテストしていること

  • anchor-nameがアンカーを公開し、position-anchorが配置要素のデフォルトアンカーを選択することを理解しているか。
  • 位置を解決するanchor()と、長さを解決するanchor-size()を区別できているか。
  • すべてをleftrightに固定するのではなく、書字方向サポートのために論理インセット(logical insets)と論理サイズを使用しているか。
  • ソース順、ツリースコープ、およびanchor-scopeが同名マッチングに影響を与えることを知っているか。
  • アンカーの値は依然として通常の配置計算に関与するため、配置要素の包含ブロックを確認しているか。

3. 最初に確認すべき明確化のための質問

  1. ターゲットはposition: absoluteですか、それともposition: fixedですか?また、どの要素がその包含ブロックを確立しますか?
  2. その関係は1対1、複数ターゲット対1アンカー、それとも1ターゲット対複数アンカーのどれですか?
  3. ページ上で同じ名前を持つコンポーネントが繰り返されたり、Shadow DOMなどのツリー境界を越えたりしますか?
  4. コンポーネントは右横書きや縦書きの書字方向をサポートする必要がありますか?物理的な幅と論理的なインラインサイズのどちらをコピーすべきですか?
  5. 名前付きアンカーが見つからない、または現在のコンテキストで受け入れられない場合、どのような通常の配置値を適用すべきですか?

4. 30秒の回答フレームワーク

anchor-nameを使用してソース要素にダッシュ付き識別子(dashed identifier)を付与し、ターゲットをabsoluteまたはfixedにしてposition-anchorでそのソースを選択します。アンカーの端(edge)を読み取るにはanchor()を使用し、物理的または論理的な寸法を読み取るにはanchor-size()を使用します。ターゲットのインセットは依然として自身の包含ブロック内で解決されます。書字方向をサポートするにはinset-inline-*inset-block-*inline-sizeを使用します。重複する名前はanchor-scopeでスコープを設定し、1つのターゲットが複数のソースに依存する場合は関数内でアンカーを明示的に指定します。

5. アンカーから最終レイアウトまで

ステップ1: 明示的なアンカー関係を確立する

ソースはanchor-nameを介して1つ以上の名前を公開します。position-anchorがデフォルトアンカーを選択できるようにするには、ターゲットが絶対配置または固定配置されている必要があります。

css
.product-card {
  anchor-name: --product-card;
}

.price-note {
  position: absolute;
  position-anchor: --product-card;
}

position-anchorはデフォルトの参照のみを確立します。インセットを設定したり、ターゲットの包含ブロックを置き換えたりすることはありません。名前を受け入れ可能なアンカーに解決できない場合、依存する関数はフォールバックを使用するか無効になるため、通常の配置フォールバックでも予測可能な結果が必要です。

ステップ2: anchor()で位置を読み取る

anchor()はinsetプロパティの長さを算出し、ターゲットの端とアンカーの端を接続します。論理インセットを使用することで、同じルールで異なる書字方向に対応できます。

css
.price-note {
  position: absolute;
  position-anchor: --product-card;
  inset-block-start: anchor(end);
  inset-inline-start: anchor(start);
}

ここで、startendは該当する軸上で解決されます。デフォルトアンカー以外のものを参照するには、anchor(--sidebar end)のように関数内に直接その名前を指定します。明示的な名前を使用することで、1つのターゲット上の異なるプロパティを異なるソースに依存させることができます。

ステップ3: anchor-size()でサイズ関係を構築する

anchor-size()は座標ではなく、物理的または論理的な寸法を返します。注釈は次のようにカードの論理幅をコピーできます。

css
.price-note {
  inline-size: anchor-size(inline);
  max-inline-size: min(32rem, anchor-size(inline));
}

widthheightは明示的に物理的な関係に適しており、inlineblockは書字方向に追従するコンポーネントに適しています。この関数はcalc()に参加することもでき、例えばアンカーのインラインサイズの一部から間隔を導出する場合などに利用できます。これは長さのみを提供し、要素の位置は依然として絶対配置のルールに従います。

ステップ4: 複数のアンカーを参照する

範囲マーカーは、開始地点をデフォルトアンカーとして関連付けつつ、別のインセットで終了地点を明示的に読み取ることができます。

css
.range-start { anchor-name: --range-start; }
.range-end { anchor-name: --range-end; }

.range-band {
  position: absolute;
  position-anchor: --range-start;
  inset-inline-start: anchor(--range-start center);
  inset-inline-end: anchor(--range-end center);
}

配置要素は依然としてposition-anchorを介した1つの関連付けられたデフォルトアンカーしか持ちませんが、他の名前もanchor()を介して位置計算に参加できます。そのデフォルトの関連付けは、スクロール追従(scroll attachment)などの動作にも影響します。両方のインセット値がターゲットの包含ブロック方程式に入るため、欠落したアンカー、交差したアンカー、または受け入れられないアンカーに対するフォールバックを設計で定義する必要があります。

ステップ5: 重複するアンカー名のスコープを設定する

繰り返されるすべてのコンポーネントが--itemを宣言している場合、スコープが設定されていない配置要素は、ソース順で最後の受け入れ可能なアンカーに一致する可能性があります。anchor-scopeは、同じ名前をコンポーネントのサブツリー内に制限します。

css
.result-row {
  anchor-scope: --item;
}

.result-row__value { anchor-name: --item; }
.result-row__note {
  position: absolute;
  position-anchor: --item;
  inset-inline-start: anchor(end);
}

これにより名前の可視性が制御されます。ツリースコープを越えて参照を強制することはできず、包含ブロックを確立することもありません。コンポーネントには依然として意図的に配置された親要素が必要です。

ステップ6: 包含ブロックをモデルに含める

anchor()によって返された位置はターゲットのインセットで使用可能な長さに変換されますが、ターゲットは依然として自身の絶対配置包含ブロックによってレイアウトされます。absolutefixedの切り替え、DOM内でのターゲットの移動、または親要素への新しい位置コンテキストの確立によって、結果が変わる可能性があります。配置が正しくない場合は、アンカーの一致をデバッグする前に、ターゲットの包含ブロックを確認してください。

6. 質の高い回答例

Anchor Positioningは、まずソース要素と配置ターゲットを関連付けます。ソースはanchor-nameで名前を宣言し、absoluteまたはfixedターゲットはposition-anchorでデフォルトのソースを選択します。anchor()はインセットに位置を提供し、anchor-size()は物理的または論理的な寸法を提供します。右横書きや縦書きには論理インセットとinline-sizeを使用します。2つのソースを必要とするターゲットは、1つのデフォルト関連付けを選択し、該当するanchor()内で各ソースを指定します。繰り返されるリストコンポーネントではanchor-scopeを使用して同名を分離します。また、アンカー関数は絶対配置モデルを迂回しないため、ターゲットの包含ブロックも確認します。

7. よくある間違い

  • anchor-nameのみを宣言して移動を期待する → まだ配置関係が存在していません → ターゲットをabsoluteまたはfixedにして名前を参照してください。
  • anchor()をサイズ設定関数として扱う → 位置と寸法の役割が混同されます → 端にはanchor()を使用し、寸法にはanchor-size()を使用してください。
  • leftwidthをハードコードする → 右横書きや縦書きで意図した関係が反転します → 論理インセットと論理サイズを選択してください。
  • スコープを設定せずにリストアイテム間で1つの名前を再利用する → ターゲットが最後に現れた同名アンカーに追従してしまいます → 各コンポーネントコンテナにanchor-scopeを設定してください。
  • anchor-scopeを包含ブロックとして扱う → マッチングは正しくても座標が依然としてずれます → 配置された祖先要素を個別に確認してください。
  • 複数アンカーのターゲットに1つのデフォルトposition-anchorのみを設定する → 他のプロパティが依然として誤ったソースを読み取ってしまいます → 各ソースをそれぞれのanchor()で明示的に指定してください。

8. フォローアップ質問と回答

フォローアップ1: anchor()とposition-areaの違いは何ですか?

anchor()は特定のアンカーの端をインセット用の長さに変換するため、正確なエッジ間の関係設定に役立ちます。position-areaは、アンカーを中心としたエリアグリッド内にターゲットを配置するため、より広範な配置領域を表現します。両者は同じモデルに属しますが、解決するレイヤーが異なります。

フォローアップ2: anchor-size()を使用すると、ターゲットはアンカーの動きに追従しますか?

いいえ。これは長さを計算するだけです。位置の追従は、anchor()position-area、または別の配置ルールによって行われます。inline-size: anchor-size(inline)のみを設定すると論理サイズが同期されますが、位置は同期されません。

フォローアップ3: 複数の要素が1つのanchor-nameを共有するとどうなりますか?

ターゲットはマッチングルールに従って受け入れ可能なアンカーを選択するため、繰り返されるコンポーネントがすべて後から現れる同名ソースに解決される可能性があります。anchor-scopeを使用して各コンポーネント内で名前のスコープを設定するか、インスタンスに明示的な個別の名前を付けてください。

フォローアップ4: アンカーが正しいのにターゲットがずれてしまうのはなぜですか?

そのインセットが依然として絶対配置に参加しているためです。包含ブロック、書字方向、マージン、ターゲットサイズがすべて最終結果に影響します。まずターゲットの包含ブロックを確認し、次にそのデフォルトアンカーと各関数の解決値を確認してください。

公開情報ソース

関連する質問