Representative interview topic

Frontend Interview: CSS Anchor Positioning Model and Primitives

FrontendHard
Offer.cc Editorial TeamPublished Updated

Question

Explain the CSS Anchor Positioning layout model. Use an annotation attached to a card to show anchor naming, position association, position and size functions, and scoping.

1. Prompt and Use Case

A price annotation follows a product card. Its inline start aligns with the card's content start, and its inline size matches the card. The relationship must survive left-to-right, right-to-left, and vertical writing modes. A list renders many card-and-annotation pairs, while another view stretches a range marker between two separate endpoints.

This question is about the CSS Anchor Positioning layout model: how an element becomes an anchor, how an absolutely positioned element selects a default anchor, how position and size functions resolve, and how name scope and containing blocks constrain the relationship. Viewport collision and floating-widget interaction are separate implementation topics.

2. What the Interviewer Is Testing

  • Whether you understand that anchor-name exposes an anchor and position-anchor selects a default anchor for a positioned element.
  • Whether you distinguish anchor(), which resolves a position, from anchor-size(), which resolves a length.
  • Whether you use logical insets and logical sizes for writing-mode support instead of fixing everything to left and right.
  • Whether you know that source order, tree scope, and anchor-scope affect same-name matching.
  • Whether you inspect the positioned element's containing block because anchor values still participate in normal positioning calculations.

3. Clarifying Questions to Ask First

  1. Is the target position: absolute or position: fixed, and which element establishes its containing block?
  2. Is the relationship one-to-one, many targets to one anchor, or one target to several anchors?
  3. Does the page repeat components with the same names or cross a tree boundary such as Shadow DOM?
  4. Must the component support right-to-left and vertical writing modes? Should it copy a physical width or a logical inline size?
  5. What ordinary positioning value should apply if the named anchor is missing or unacceptable in the current context?

4. A 30-Second Answer Framework

Give the source element a dashed identifier with anchor-name, then make the target absolute or fixed and select that source with position-anchor. Use anchor() to read an anchor edge and anchor-size() to read a physical or logical dimension; the target's insets still resolve within its own containing block. Use inset-inline-*, inset-block-*, and inline-size for writing-mode support. Scope repeated names with anchor-scope, and name anchors explicitly inside functions when one target depends on several sources.

5. From an Anchor to Final Layout

Step 1: Establish an Explicit Anchor Relationship

The source exposes one or more names through anchor-name. The target must be absolutely or fixed positioned before position-anchor can select its default anchor:

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

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

position-anchor establishes only the default reference. It does not set an inset or replace the target's containing block. If the name cannot resolve to an acceptable anchor, dependent functions use their fallback or become invalid, so the ordinary positioning fallback still needs a predictable result.

Step 2: Read Position with anchor()

anchor() yields a length for an inset property, connecting one target edge to an anchor edge. Logical insets let the same rule follow different writing modes:

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

Here, start and end resolve on the relevant axis. To reference something other than the default anchor, put its name directly in the function, such as anchor(--sidebar end). Explicit names let different properties on one target depend on different sources.

Step 3: Build Size Relationships with anchor-size()

anchor-size() returns a physical or logical dimension, not a coordinate. An annotation can copy the card's logical width like this:

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

width and height fit explicitly physical relationships; inline and block fit components that follow writing mode. The function can also participate in calc(), for example to derive spacing from part of the anchor's inline size. It supplies a length only; the element's position still follows absolute-positioning rules.

Step 4: Reference More Than One Anchor

A range marker can associate with the start as its default anchor while explicitly reading the end in another inset:

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);
}

The positioned element still has only one associated default anchor through position-anchor; other names can participate in position calculations through anchor(). That default association also affects behavior such as scroll attachment. Both inset values enter the target's containing-block equation, so the design must define fallbacks for missing, crossed, or unacceptable anchors.

Step 5: Scope Repeated Anchor Names

If every repeated component declares --item, an unscoped positioned element may match the last acceptable anchor in source order. anchor-scope limits the same name to a component subtree:

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);
}

This controls name visibility. It cannot force a reference across tree scopes, and it does not establish a containing block. The component still needs a deliberate positioned ancestor.

Step 6: Include the Containing Block in the Model

The position returned by anchor() is converted into a length usable by the target inset, but the target is still laid out by its own absolute-positioning containing block. Switching between absolute and fixed, moving the target in the DOM, or establishing a new positioning context on an ancestor can change the result. When placement is wrong, inspect the target's containing block before debugging anchor matching.

6. High-Quality Sample Answer

Anchor Positioning first relates a source element to a positioned target. The source declares a name with anchor-name; an absolute or fixed target selects its default source with position-anchor. anchor() supplies a position to an inset, while anchor-size() supplies a physical or logical dimension. I use logical insets and inline-size for right-to-left or vertical writing. A target that needs two sources selects one default association and names each source inside the relevant anchor(); repeated list components use anchor-scope to isolate the same name. I also verify the target's containing block, because anchor functions do not bypass the absolute-positioning model.

7. Common Mistakes

  • Declare only anchor-name and expect movement → no positioned relationship exists yet → make the target absolute or fixed and reference the name.
  • Treat anchor() as a sizing function → position and dimension responsibilities become confused → use anchor() for edges and anchor-size() for dimensions.
  • Hard-code left and width → right-to-left or vertical writing reverses the intended relationship → choose logical insets and logical sizes.
  • Reuse one name across list items without scope → targets follow the last same-name anchor → set anchor-scope on each component container.
  • Treat anchor-scope as a containing block → matching is correct but coordinates are still wrong → inspect the positioned ancestor separately.
  • Set one default position-anchor for a multi-anchor target → other properties still read the wrong source → name each source explicitly in its anchor().

8. Follow-up Questions and Responses

Follow-up 1: How does anchor() differ from position-area?

anchor() turns a specific anchor edge into a length for an inset, which is useful for precise edge-to-edge relationships. position-area places the target in an area grid centered on the anchor, which expresses a broader placement region. They belong to the same model but solve at different levels.

Follow-up 2: Does anchor-size() make the target follow anchor movement?

No. It calculates a length. Position tracking comes from anchor(), position-area, or another positioning rule. Setting only inline-size: anchor-size(inline) synchronizes logical size, not location.

Follow-up 3: What happens when several elements share one anchor-name?

The target selects an acceptable anchor according to the matching rules, so repeated components can all resolve to a later same-name source. Scope the name inside each component with anchor-scope, or give instances explicit distinct names.

Follow-up 4: Why can the target be offset when the anchor is correct?

Its insets still participate in absolute positioning. The containing block, writing mode, margins, and target size all affect the final result. Inspect the target's containing block first, then verify its default anchor and each function's resolved value.

Public sources

Related questions