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.
anchor()
[stylesheet]
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);
}
anchor()
[<style> tag]
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);
}
anchor()
[inline styles]
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);
"
anchor()
[implicit
anchor
attribute]
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); }
anchor()
[
position-anchor
property]
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; }
popover
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);
}
anchor()
[anchor name set as CSS custom
property]
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);
}
position-try-fallbacks
With polyfill applied, the following positions are attempted in order:
flip-block
, flipping across the inline axis, and
align the top, left edge of the target with the bottom, right edge
of its anchor.
flip-inline
, flipping across the block axis, and
align the bottom, right edge of the target with the top, left edge
of its anchor.
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.
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;
}
position-try-fallbacks
and
@position-try
With polyfill applied, the following positions are attempted in order:
#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;
}
With polyfill applied, the following positions are attempted in order:
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.
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.
#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;
}
anchor()
[scrolling elements]
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);
}
anchor()
[used in
calc()
function]
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);
}
anchor()
[passed through CSS custom
property]
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);
}
anchor()
[passed through multiple CSS
custom properties]
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);
}
anchor()
[with duplicate CSS custom
properties]
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%);
}
anchor-size()
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
<style>
)
<link>
)
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>
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);
}
At OddBird, we love contributing to the languages & tools developers rely on. We're currently working on polyfills for new Popover & Anchor Positioning functionality, as well as CSS specifications for functions, mixins, and responsive typography. Help us keep this work sustainable and centered on your needs as a developer! We display sponsor logos and avatars on our website.
Sponsor OddBird's OSS Work