Anchor Positioning Syntax Workshop
[OpenUI] #357
Goal: Assess Syntax for Anchor Positioning
If you have an idea for how you would like the developer experience for anchor positioning to look, please leave your proposed syntax after the image slides:
Problems to resolve:
Assumptions to make
Demo A: Menubar
Use case: Style a popup based on its popup anchor
Keep menubar anchor on screen, inset 10px to the right of the menu button
#menu-popup
#menu-button
Proposed Syntax: Declarative Anchor Una
#menu-popup {
/* anchor: <parent pos> / <child pos> */
anchor: bottom right / top left;
margin-right: 10px;
}
The menu popup is always anchored to the bottom right of the menu button at the top right point of the anchored menu element, with a 10px right margin (you can also add margin-top / margin-block-start to give it vertical space away from the menu button).
anchor: <parent pos> / <child pos>
Proposed Syntax - Declarative Anchor #2 Rob
#menu-popup {
/* anchor: <anchor edge> <anchor align> */
anchor: bottom right;
margin-right: 10px;
}
The menu popup is always anchored to the bottom right of the menu button, with a 10px right margin (you can also add margin-top / margin-block-start to give it vertical space away from the menu button).
anchor: <anchor edge> <anchor align>
Proposed Syntax: anchor() function + @-rules Melanie/MS
#menu-popup {� position: fixed;� top: anchor(bottom);� right: calc(anchor(right) - 10px);� max-width: min(20rem, anchor(right));� max-height: calc(100vh - anchor(bottom));� overflow: auto;�}
Proposed Syntax Andrico
#menu-popup {
anchor: bottom right;
anchor-offset: 0 -10px 0 0;
}
Explain:
I’m treating anchor-offset like top, right, bottom, and left to adjust the browser’s default placement of the popup, without needing to change the position attribute.
Proposed Syntax <proposer name>
// Code
Explain:
Demo B: Select
Use case: adjust where the popup is based on viewport position
If there is enough screen real estate for the select to drop down, open it underneath the input bar. If not, open it above to keep it on the page
Choose your own constraints:
#select-open
#select-open
#select-popup
#select-popup
Proposed Syntax: Declarative Anchor Una
#select-popup {
anchor: center / center;
optimizeVisibility: viewport 1rem;
}
optimizeVisibility (name tbd) is a property that tells the browser to optimize for keeping the popup in the viewport. The 1rem value is the inset from the viewport.
optimizeVisibility values:
Proposed Syntax: Declarative Anchor #2 Rob
#select-popup {
anchor: bottom left, top left;
}
Proposed Syntax: anchor() function + @-rules Melanie/MS
#select-popup {� position: fixed;� position-set: largestHeight(selectPos);� left: anchor(left);� width: calc(anchor(right) - anchor(left));� overflow: auto;�}��@position-set(selectPos) {� 1 {� top: anchor(bottom);� max-height: calc(100vh - anchor(bottom));� }� 2 {� bottom: anchor(top);� max-height: anchor(top);� }�}
Proposed Syntax: anchor() + position-set() Melanie/MS
#select-popup {� position: fixed;� top: largestHeight(position-set(anchor(bottom), auto));� bottom: largestHeight(position-set(auto, anchor(top)); � left: anchor(left);� width: calc(anchor(right) - anchor(left));� max-height: largestHeight(position-set(calc(100vh - anchor(bottom), anchor(top));� overflow: auto;�}
Proposed Syntax: anchor() + position-set() - alt Melanie/MS
#select-popup {� position: fixed;� position-optimize: largestHeight;� top: position-set(anchor(bottom), auto));� bottom: position-set(auto, anchor(top)); � left: anchor(left);� width: calc(anchor(right) - anchor(left));� max-height: position-set(calc(100vh - anchor(bottom), � anchor(top));� overflow: auto;�}
Proposed Syntax: anchor() function + magic properties Melanie
#select-popup {� position: fixed;� reposition: flip-v;� reposition-optimize: � min-overflow, largest-height;� top: anchor(bottom);� left: anchor(left);� width: calc(anchor(right) - anchor(left));� max-height: calc(100vh - anchor(bottom));� overflow: auto;�}
Proposed Syntax Andrico
#select-popup {
anchor: bottom, top;
anchor-arrow: hidden;
width: anchor(width);
min-height: 80px;
height: 120px;
}
Anchor takes comma-separated list of anchor positions in order of preference
Anchor-arrow specified the appearance of the arrow, in this case we hide it. This removes any gutter space between the popup and its parent.
Used melanie’s anchor function to specify the width
Height is the preferred height, but if height is less than 80px, the next anchor position will be used.
Proposed Syntax <proposer name>
// Code
Explain:
Demo C: Cards
Use case: Style an anchored element based on an ideal location but make adjustments for the viewport
Center the tooltip above the card when hovered/focused, but keep it within the viewport
#tooltip-popup
#tooltip-popup
#card
#card
Demo C: Cards
Use case: Style an anchored element based on an ideal location but make adjustments for the viewport
Center the tooltip above the card when hovered/focused, but keep it within the viewport
#tooltip-popup
#tooltip-popup
#card
#card
#tooltip-popup
#tooltip-popup
Proposed Syntax: Declarative Anchor Una
#tooltip-popup {
anchor: top center / bottom center;� optimizeVisibility: viewport 1rem;
}
#tooltip-popup:after {
content: ‘’; /* rest of tooltip triangle styling here ...*/� anchor: top center / bottom center;� optimizeVisibility: none; /* default so technically not needed */� /* might need a position: absolute too */
}
optimizeVisibility (name tbd) is a primitive that tells the browser to optimize for keeping the popup in the viewport
The 1rem value is the optimizeVisibilityOffset (offset from the viewport).
Anchor shorthand
Proposed Syntax: pseudo class Robert
#select-popup {
anchor: bottom left, top left;
}
#select-popup:anchor(0) {
anchor: none; /* bad*/
}
#select-popup:anchor(0) > .callout {
… /* bottom left style */
}
Proposed Syntax: anchor() function + @-rules Melanie/MS
#tooltip-popup {� position: fixed;� position-set: tooltipPos;� top: calc(anchor(top) - .5rem);� max-width: min(10rem, calc(100vw / 2));�}��@position-set(tooltipPos) {� 1 {� --anchorMidpoint: calc(anchor(left) +� ((anchor(right)-anchor(left)) / 2);� left: var(--anchorMidpoint);� transform: translateX(-50%);� }� 2 {left: anchor(left);}� 3 {right: anchor(right);}�}
Proposed Syntax: anchor() + position-set() - alt Melanie/MS
#tooltip-popup {� --anchorMidpoint: calc(anchor(left) +� ((anchor(right)-anchor(left)) / 2);
position: fixed;� top: calc(anchor(top) - .5rem);� left: position-set(var(--anchorMidpoint),� anchor(left), anchor(right));� transform: position-set(translateX(-50%),� auto); � max-width: min(10rem, calc(100vw / 2));�}
Proposed Syntax: anchor() function + magic properties Melanie
#tooltip-popup {� --anchorMidpoint: calc(anchor(left) +� ((anchor(right)-anchor(left)) / 2);
position: fixed;� reposition: align-edges-h, resize-h;� reposition-optimize: � min-overflow, largest-width;� top: calc(anchor(top) - .5rem);� left: var(--anchorMidpoint);� transform: translateX(-50%);� max-width: min(10rem, calc(100vw / 2));� min-width: 8em;�}
Proposed Syntax Andrico
#tooltip-popup {
anchor: top center;
}
Would there be any scenario where the tooltip overflowing outside of the viewport is the default behaviour?
Should the browser try and keep the popup within the viewport until it literally can’t?
If so, what could the inverse syntax be, when we don’t mind if the popup overflows?
Proposed Syntax <proposer name>
// Code
Explain:
SEPARATOR FOR ADDITIONAL EXAMPLES
Demo Use case D: Facebook like menu
Use case: <use case>
Above and left aligned (to edge of button) by default
Below and left aligned (to edge of button) when scroller meets edge of container.
Demo D: <Name>
Use case: <use case>
Explain Use Case
Demo E: Teaching UI
Use case: position teaching UI (with variable-length content) relative to the anchor element it describes. The teaching popup is repositioned both horizontally and vertically according to real-estate; the diagrams show the order in which you prefer to display the popup.
Assume that your stylesheet doesn’t know what element .teaching-popup will be anchored to.
Choose your own constraints:
#anchor-button
.teaching-popup
#anchor-button
.teaching-popup
#anchor-button
.teaching-popup
#anchor-button
.teaching-popup
#anchor-button
.teaching-popup
#anchor-button
.teaching-popup
#1
#2
#3
#4
#5
#6
Proposed Syntax: anchor() function + @-rules Melanie/MS
.teaching-popup {� --midpointH: calc(anchor(left) + ((anchor(right)-anchor(left)) / 2);� --midpointV: calc(anchor(top) + ((anchor(bottom) - anchor(top)) / 2));� --spaceRight: calc(100vw - anchor(right) - 1rem); � --spaceLeft: calc(anchor(left) - 1rem);� --spaceHMiddle: calc(min(midPointH, calc(100vw - midpointH)) - 1rem);� --spaceAbove: calc(anchor(top) - 2.5rem);� --spaceTop: calc(100vw - anchor(top));� --spaceVMiddle: calc(min(midpointV, calc(100vw - midpointV)) - 1rem);� position: fixed;� position-set: teachingPos;�}��@position-set(teachingPos) {� /* Set horizontal positions */� 1, 2 {left: calc(anchor(right) + .5rem); max-width: var(--spaceRight);}� 3, 4 {right: calc(anchor(left) - .5rem); max-width: var(--spaceLeft);}� 5, 6 {left: var(--midpointH); transform: translateX(-50%); � max-width: var(--spaceHMiddle);}�� /* Set vertical positions */� 1, 3 {top: var(--midpointV); transform: translateY(-50%); � max-height: var(--spaceVMiddle);}� 2, 4 {top: calc(anchor(top) - .5rem); max-height: var(--spaceTop);}� 5, 6 {top: calc(anchor(top) - 2rem); max-height: var(--spaceAbove);}�}
Proposed Syntax: anchor() + position-set() - alt Melanie/MS
.teaching-popup {� /* Assume same custom props as previous */� position: fixed;�� left: position-set((calc(anchor(right) + .5rem), calc(anchor(right) + � .5rem), auto, auto, var(--midpointH));� right: position-set(auto, auto, calc(anchor(left) - .5rem), � calc(anchor(left) - .5rem), auto);� max-width: position-set(var(--spaceRight), var(--spaceRight), � var(--spaceLeft), var(--spaceLeft), var(--spaceHMiddle));� transform: position-set(translateY(-50%), auto, translateY(-50%), auto, � translateX(-50%));�� top: position-set(var(--midpointV), calc(anchor(top) - .5rem), � var(--midpointV), calc(anchor(top) - .5rem), calc(anchor(top) - 2rem));� max-height: position-set(var(--spaceVMiddle), var(--spaceTop),� var(--spaceVMiddle), var(--spaceTop), var(--spaceAbove));�}
Proposed Syntax: anchor() function + magic properties Melanie
.teaching-popup {� --midpointV: calc(anchor(top) + ((anchor(bottom) - anchor(top)) / 2));� --spaceRight: calc(100vw - anchor(right) - 1rem); � --spaceVMiddle: calc(min(midpointV, calc(100vw - midpointV)) - 1rem);�� position: fixed;� top: var(--midpointV);� left: anchor(right);� margin: .5rem;� max-width: var(--spaceRight);� max-height: var(--spaceVMiddle);� transform: translateY(-50%);�� reposition: align-edges-right, align-edges-left, center-h /� align-edges-top, center-v;� reposition-optimize: min-overflow, largest-height, largest-width;�}
Proposed Syntax 1 <proposer name>
// Code
Explain:
Proposed Syntax 2 <proposer name>
// Code
Explain:
Demo F: sidenotes
Use case: center the sidenote of variable content length to the right of the element it describes. If the space to the right of the element is “too narrow” or “too short” (you decide when that is), the sidenote should be in a fixed position at the top-left corner of the document.
Assume that your stylesheet doesn’t know what element .teaching-popup will be anchored to.
#anchor-el
.sidenote
#anchor-el
.sidenote
Proposed Syntax: anchor() function + @-rules Melanie/MS
.sidenote {� --midpointV: calc(anchor(top) + ((anchor(bottom) - anchor(top)) / 2));� --spacevMiddle: calc(min(midpointV, calc(100vw - midpointV)) - 1rem);� --spaceRight: calc(100vw - anchor(right) - 1rem);� position: fixed;� position-set: sidenotePos;�}��@position-set(sidenotePos) {� 1 {� left: calc(anchor(right) + .5rem);� top: var(--midpointV);� transform: translateY(-50%);� max-height: var(--spacevMiddle);� max-width: var(--spaceRight);� min-width: 10em;� min-height: 10em;� }� � 2 {� top: .5rem;� left: .5rem;� width: calc(100% - 1rem);� width: height(100% - 1rem);� }�}
Proposed Syntax 1 <proposer name>
// Code
Explain:
Proposed Syntax 2 <proposer name>
// Code
Explain: