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 would like to bring 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-fallback
Note: The @position-fallback
syntax has
been replaced in the spec, and will be deprecated in the next version
of this Polyfill.
With polyfill applied, the following positions are attempted in order:
#my-anchor-fallback {
anchor-name: --my-anchor-fallback;
}
#my-target-fallback {
position: absolute;
position-fallback: --fallback1;
}
@position-fallback --fallback1 {
/* First try to align the bottom, left edge of the target
with the top, left edge of the anchor. */
@try {
bottom: anchor(--my-anchor-fallback top);
left: anchor(--my-anchor-fallback left);
}
/* Next try to align the top, left edge of the target
with the bottom, left edge of the anchor. */
@try {
top: anchor(--my-anchor-fallback bottom);
left: anchor(--my-anchor-fallback left);
}
/* Next try to align the bottom, right edge of the target
with the top, right edge of the anchor. */
@try {
bottom: anchor(--my-anchor-fallback top);
right: anchor(--my-anchor-fallback right);
}
/* Finally, try to align the top, right edge of the target
with the bottom, right edge of the anchor. */
@try {
top: anchor(--my-anchor-fallback bottom);
right: anchor(--my-anchor-fallback right);
width: 100px;
height: 100px;
}
}
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);
}
With polyfill applied: Target and original Anchor's right edges line up.
Target's top edge lines up with the top edge of the original Anchor.
When another valid Anchor is added earlier in the DOM order and the
polyfill is re-applied, Target position updates relative to the added
Anchor.
#my-anchor-added-new {
anchor-name: --my-anchor-added;
margin-bottom: 1rem;
}
#my-anchor-added-original {
anchor-name: --my-anchor-added;
}
#my-target-added {
position: absolute;
left: anchor(--my-anchor-added right);
top: anchor(--my-anchor-added top);
}
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