CSS Anchor Positioning Polyfill

Anchoring Elements Using CSS

The CSS anchor positioning specification defines anchor positioning, "where a positioned element can size and position itself relative to one or more 'anchor elements' elsewhere on the page." This CSS Anchor Positioning Polyfill supports and is based on this specification.

The proposed anchor() and anchor-size() functions add flexibility to how absolutely positioned elements can be placed within a layout. Instead of being sized and positioned based solely on the position of their containing block, the proposed new functions allow absolutely positioned elements to be placed relative to one or more author-defined anchor elements.

Note: This polyfill was implemented against an early version of the spec, and updates were paused to allow the syntax to solidify. Now that browsers are working on implementation, we are in the process of bringing it up to date, and welcome code contributions and financial support to make that happen.

Positioning with anchor() [stylesheet]

Target
Anchor

With polyfill applied: Target and Anchor's right edges line up. Target's top edge lines up with the bottom edge of the Anchor.

#my-anchor-positioning {
  anchor-name: --my-anchor-positioning;
}

#my-target-positioning {
  position: absolute;
  top: anchor(--my-anchor-positioning bottom);
  right: anchor(--my-anchor-positioning right, 50px);
}

Positioning with anchor() [<style> tag]

Target
Anchor

With polyfill applied: Target is positioned at the top left corner of the Anchor.

/* a <style> tag */

#my-anchor-style-tag {
  anchor-name: --my-anchor-style-tag;
}

#my-target-style-tag {
  position: absolute;
  bottom: anchor(--my-anchor-style-tag start);
  right: anchor(--my-anchor-style-tag left);
}

Positioning with anchor() [inline styles]

Target
Anchor

With polyfill applied: Target and Anchor's right edges line up. Target's top edge lines up with the bottom edge of the Anchor.

/* inline <style> attributes */

/* anchor */

  style="anchor-name: --my-anchor-in-line"

/* target */

  style="
    position: absolute;
    top: anchor(--my-anchor-in-line bottom);
    right: anchor(--my-anchor-in-line right);
  "

Positioning with anchor() [implicit anchor attribute]

Anchor
Target

With polyfill applied: Target is positioned at the top left corner of the Anchor.

<div id="my-implicit-anchor">Anchor</div>
<div id="my-implicit-target" anchor="my-implicit-anchor">Target</div>

#my-implicit-target {
  position: absolute;
  right: anchor(implicit left);
  bottom: anchor(top);
}

Positioning with anchor() [ position-anchor property]

Anchor A
Target A
Anchor B
Target B

With polyfill applied: Targets are positioned at the top right corner of their respective Anchors.

<div id="my-position-anchor-a" class="anchor">Anchor A</div>
<div id="my-position-target-a" class="target">Target A</div>
<div id="my-position-anchor-b" class="anchor">Anchor B</div>
<div id="my-position-target-b" class="target">Target B</div>

#my-position-target-a {
  position-anchor: --my-position-anchor-a;
}

#my-position-target-b {
  position-anchor: --my-position-anchor-b;
}

.target {
  position: absolute;
  bottom: anchor(top);
  left: anchor(right);
}

#my-position-anchor-a {
  anchor-name: --my-position-anchor-a;
}

#my-position-anchor-b {
  anchor-name: --my-position-anchor-b;
}

Anchoring a popover

Popover (Target)

With polyfill applied: Target is positioned at the bottom right corner of the Anchor.

#my-anchor-popover {
  position: absolute;
  left: 200px;
  anchor-name: --my-anchor-popover;
}

#my-target-popover {
  position: absolute;
  left: anchor(--my-anchor-popover right);
  top: anchor(--my-anchor-popover bottom);
}

Positioning with anchor() [anchor name set as CSS custom property]

Anchor
Target

With polyfill applied: Target is positioned at the top left corner of the Anchor.

#my-anchor-name-prop {
  anchor-name: --my-anchor-name-prop;
}

#my-target-name-prop {
  --anchor-var: --my-anchor-name-prop;

  position: absolute;
  right: anchor(var(--anchor-var) left);
  bottom: anchor(var(--anchor-var) top);
}

Fallbacks with position-try-fallbacks

position-try-fallbacks Anchor
Target
position-try-fallbacks Anchor 2
Target 2

With polyfill applied, the following positions are attempted in order:

  1. Align the bottom, left edge of the target with the top, right edge of its anchor.
  2. Apply flip-block, flipping across the inline axis, and align the top, left edge of the target with the bottom, right edge of its anchor.
  3. Apply flip-inline, flipping across the block axis, and align the bottom, right edge of the target with the top, left edge of its anchor.
  4. Apply flip-start, flipping across a diagonal line from upper left to lower right, and align the top, right edge of the target with the bottom, left edge of its anchor.
  5. When every position overflows, revert to the last successful fallback position if there is one, otherwise revert to the initial base styles.

Note: Early Chromium implementations used position-try-options instead of position-try-fallbacks. We recommend adding both properties, or using the position-try shorthand.

#my-anchor-try-tactics {
  anchor-name: --my-anchor-try-tactics;
}

#my-anchor-try-tactics-2 {
  anchor-name: --my-anchor-try-tactics-2;
}

#my-target-try-tactics {
  position-anchor: --my-anchor-try-tactics;
}

#my-target-try-tactics-2 {
  position-anchor: --my-anchor-try-tactics-2;
}

#my-target-try-tactics,
#my-target-try-tactics-2 {
  position: absolute;
  bottom: anchor(top);
  left: anchor(right);
  position-try-fallbacks: flip-block, flip-inline, flip-start;
}

Fallbacks with position-try-fallbacks and @position-try

@position-try Anchor
Target
@position-try Anchor 2
Target 2

With polyfill applied, the following positions are attempted in order:

  1. Align the bottom, left edge of the target with the top, left edge of its anchor.
  2. Align the top, left edge of the target with the bottom, left edge of its anchor.
  3. Align the bottom, right edge of the target with the top, right edge of its anchor.
  4. Align the top, right edge of the target with the bottom, right edge of its anchor, and set the height and width of the target to 100px.
  5. When every position overflows, revert to the last successful fallback position if there is one, otherwise revert to the initial base styles.
#my-anchor-fallback {
  anchor-name: --my-anchor-fallback;
}

#my-anchor-fallback-2 {
  anchor-name: --my-anchor-fallback-2;
}

#my-target-fallback {
  position-anchor: --my-anchor-fallback;
}

#my-target-fallback-2 {
  position-anchor: --my-anchor-fallback-2;
}

#my-target-fallback,
#my-target-fallback-2 {
  position: absolute;

  /* First try to align the bottom, left edge of the target
     with the top, left edge of the anchor. */
  bottom: anchor(top);
  left: anchor(left);
  width: anchor-size(width);
  position-try-fallbacks: --bottom-left, --top-right, --bottom-right;
}

@position-try --bottom-left {
  /* Next try to align the top, left edge of the target
     with the bottom, left edge of the anchor. */
  top: anchor(bottom);
  left: anchor(left);
  /* Values set by the initial styles will still be applied
     unless they are explicitly reverted. */
  bottom: revert;
}

@position-try --top-right {
  /* Next try to align the bottom, right edge of the target
     with the top, right edge of the anchor. */
  bottom: anchor(top);
  right: anchor(right);
  left: revert;
  width: revert;
}

@position-try --bottom-right {
  /* Finally, try to align the top, right edge of the target
     with the bottom, right edge of the anchor. */
  top: anchor(bottom);
  right: anchor(right);
  width: 100px;
  height: 100px;
  bottom: revert;
  left: revert;
}

Positioning with combined try tactics

position-try Anchor
Target

With polyfill applied, the following positions are attempted in order:

  1. Align the bottom, left edge of the target with the top, right edge of the anchor.
  2. Apply flip-block flip-inline, flipping across both the block and inline axis, and align the top, right edge of the target with the bottom, left edge of the anchor.
  3. Apply flip-inline --bottom-left-combined, flipping across the block axis, and align the top, right edge of the target with the bottom, right edge of the anchor.
  4. When every position overflows, revert to the last successful fallback position if there is one, otherwise revert to the initial base styles.
#my-anchor-try-tactics-combined {
  anchor-name: --my-anchor-try-tactics-combined;
}

#my-target-try-tactics-combined {
  position: absolute;
  position-anchor: --my-anchor-try-tactics-combined;
  bottom: anchor(top);
  left: anchor(right);
  position-try: flip-block flip-inline, flip-inline --bottom-left-combined;
}

@position-try --bottom-left-combined {
  top: anchor(bottom);
  left: anchor(left);
  bottom: revert;
}

Positioning with anchor() [scrolling elements]

Anchor
Inner-anchored Target
Outer-anchored Target

With polyfill applied: The Inner-anchored Target is positioned at the top right corner of the Anchor. The Outer-anchored Target is positioned at the bottom left corner of the Anchor.

#scroll-anchor {
  anchor-name: --scroll-anchor;
}

#inner-anchored {
  position: absolute;
  bottom: anchor(--scroll-anchor top);
  left: anchor(--scroll-anchor end);
}

#outer-anchored {
  position: absolute;
  top: anchor(--scroll-anchor bottom);
  right: anchor(--scroll-anchor start);
}

Positioning with anchor() [used in calc() function]

Anchor
Target

With polyfill applied: Target's left edge is 50px left of the Anchor's right edge). The top edge of the Target is 50px above the bottom edge of the Anchor.

#my-anchor-math {
  anchor-name: --my-anchor-math;
}

#my-target-math {
  --full-math: anchor(--my-anchor-math 100%);
  --full-minus-math: calc(anchor(--my-anchor-math 100%) - 50px);

  position: absolute;
  top: calc(var(--full-math) - 50px);
  left: var(--full-minus-math);
}

Positioning with anchor() [passed through CSS custom property]

Anchor
Target

With polyfill applied: Target's top edge is positioned at 50% of the height of the Anchor. The right edge of the Target lines up with 100% of the width of the Anchor (i.e. the Anchor's right edge).

#my-anchor {
  anchor-name: --my-anchor;
}

#my-target {
  --center: anchor(--my-anchor 50%);
  --full: anchor(--my-anchor 100%);

  position: absolute;
  top: var(--center);
  right: var(--full);
}

Positioning with anchor() [passed through multiple CSS custom properties]

Anchor
Target

With polyfill applied: Target's left edge is positioned in Anchor's horizontal center. Target's bottom edge is 10% above the bottom edge of Anchor.

#my-anchor-props {
  anchor-name: --my-anchor-props;
}

#my-target-props {
  --half: anchor(--my-anchor-props 50%);
  --quarter: calc(var(--half) / 2);
  --tenth: calc(var(--quarter) / 2.5);

  position: absolute;
  left: var(--half);
  bottom: var(--tenth);
}

Positioning with anchor() [with duplicate CSS custom properties]

Anchor
Target

With polyfill applied: Target's top left corner is positioned in the center (vertically and horizontally) of the Anchor.

#anchor-duplicate-custom-props {
  anchor-name: --anchor-duplicate-custom-props;
}

#target-duplicate-custom-props {
  --center: anchor(--anchor-duplicate-custom-props 50%);

  position: absolute;
  top: var(--center);
  left: var(--center);
}

#other {
  --center: anchor(--anchor-duplicate-custom-props 100%);
}

Sizing with anchor-size()

Anchor
Target

With polyfill applied: Target has the same width as the Anchor.

#my-anchor-size {
  anchor-name: --my-anchor;
  width: 5em;
}

#my-target-size {
  width: anchor-size(--my-anchor width);
}

Dynamically update anchors

Anchor
Target

With polyfill applied: Target and Anchor's right edges line up. Target's top edge lines up with the bottom edge of the Anchor.

When Anchor width is changed dynamically, Target position updates accordingly.

#my-anchor-update {
  anchor-name: --my-anchor-update;
}

#my-target-update {
  position: absolute;
  right: anchor(--my-anchor-update right);
  top: anchor(--my-anchor-update bottom);
}

Use a list of anchor names

Anchor
Target A
Target B

With polyfill applied: Target A is positioned at Anchor's top left corner. Target B is positioned at Anchor's bottom right corner.

#my-anchor-name-list {
  anchor-name: --my-anchor-name-a, --my-anchor-name-b;
}

#my-target-name-list-a {
  position: absolute;
  right: anchor(--my-anchor-name-a left);
  bottom: anchor(--my-anchor-name-a top);
}

#my-target-name-list-b {
  position: absolute;
  left: anchor(--my-anchor-name-b right);
  top: anchor(--my-anchor-name-b bottom);
}

Absolutely positioned anchor

Anchor one
Target one
Anchor two
Target two

With polyfill applied: Target one is positioned at the bottom right corner of Anchor one. Target two is positioned at the bottom right corner of Anchor two.

#my-anchor-absolute-one,
#my-anchor-absolute-two {
  position: absolute;
  anchor-name: --my-anchor-absolute;
}

#my-target-absolute-one,
#my-target-absolute-two {
  position: absolute;
  top: anchor(--my-anchor-absolute bottom);
  left: anchor(--my-anchor-absolute right);
}

Pseudo-element anchor

Anchor
Target

Target is positioned at the bottom right corner of the Anchor's ::before pseudo-element.

#my-anchor-pseudo-element::before {
  content: '::before';
  anchor-name: --my-anchor-pseudo-element;
}

#my-target-pseudo-element {
  position: absolute;
  position-anchor: --my-anchor-pseudo-element;
  top: anchor(bottom);
  left: anchor(right);
}

Anchor declared in media query

Screen Anchor
Print Anchor
Target

With polyfill applied: Target and Screen Anchor's right and top edges line up.

@media print {
  #my-print-anchor-media-query {
    anchor-name: --my-anchor-media-query;
  }
}

#my-anchor-media-query {
  anchor-name: --my-anchor-media-query;
}

#my-target-media-query {
  position: absolute;
  top: anchor(--my-anchor-media-query top);
  right: anchor(--my-anchor-media-query right);
}

Manually apply polyfill to specific styles

Anchor
Target 1 (with <style>)
Target 3 (with inline style)

With polyfill applied: Target 1, 2, and 3 are positioned at Anchor’s top-left, top-right, and bottom-right corners respectively.

<style id="my-style-manual-anchor">
  #my-anchor-manual {
    anchor-name: --my-anchor-manual;
  }
</style>
<style id="my-style-manual-style-el">
  #my-target-manual-style-el {
    position: absolute;
    bottom: anchor(--my-anchor-manual top);
    right: anchor(--my-anchor-manual left);
  }
</style>
<link rel="stylesheet" href="/anchor-manual.css" id="my-style-manual-link-el" />
<!--
CSS inside the anchor-manual.css file:

#my-target-manual-link-el {
  position: absolute;
  bottom: anchor(--my-anchor-manual top);
  left: anchor(--my-anchor-manual right);
}
-->

<div id="my-anchor-manual" class="anchor">...</div>
<div id="my-target-manual-style-el" class="target">...</div>
<div id="my-target-manual-link-el" class="target">...</div>
<div id="my-target-manual-inline-style" class="target"
  style="position: absolute;
    top: anchor(--my-anchor-manual bottom);
    left: anchor(--my-anchor-manual right);"
>...</div>

<script>
  polyfill({
    elements: [
      // The <style> element for anchor
      document.getElementById('my-style-manual-anchor'),
      // The <style> element
      document.getElementById('my-style-manual-style-el'),
      // The <link> element
      document.getElementById('my-style-manual-link-el'),
      // The target element with inline styles
      document.getElementById('my-target-manual-inline-style'),
    ],
  });
</script>

Scope an anchor name

With polyfill applied: Targets are positioned to the right of their corresponding item's arrow.

#anchor-scope li {
  anchor-scope: --anchor-scope;
}

#anchor-scope .arrow {
  anchor-name: --anchor-scope;
}

#anchor-scope .target {
  position: absolute;
  position-anchor: --anchor-scope;
  left: anchor(right);
  top: anchor(center);
}