-
Notifications
You must be signed in to change notification settings - Fork 709
[css-anchor-1] More declarative syntax for simple cases #7757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Agenda+ to discuss adding the That is, Also want to discuss the possible constraints of the proposed |
The CSS Working Group just discussed The full IRC log of that discussion<TabAtkins> Topic: Declarative anchor fallback positioning<TabAtkins> github: https://github.com//issues/7757 <fantasai> TabAtkins: At previous meeting when introduced anchor positioning <fantasai> TabAtkins: Emilio brought up that XUL had similar functionality, and had a simple declarative way to express fallbacks <fantasai> TabAtkins: This is good, current fallback is very complex <fantasai> TabAtkins: I have a proposal in this thread for solving that <fantasai> TabAtkins: Can still use full complex positioning if necessary, but this should solve common cases <fantasai> TabAtkins: Not looking for resolution right now, just wanted to get attention for review and comment <fantasai> iank_: Can you briefly describe this mode? <fantasai> TabAtkins: The way you opt into it, rather than declaring a side to align to in the anchor <fantasai> TabAtkins: Instead you say "auto", and it will automatically choose the opposite side of the property you're setting <fantasai> TabAtkins: but if you would trigger fallback positioning, we flip around <fantasai> TabAtkins: and align your bototm edge to the top edge <fantasai> TabAtkins: so we can do this positoining without affecting layout <fantasai> TabAtkins: then if neither works, we go to normal fallback rules <fantasai> iank_: idk that you need auto on opposite side <fantasai> iank_: how does this interact with maximum number of fallbacks? <fantasai> TabAtkins: need auto on other side because if not, there's nothing to fall back to <fantasai> TabAtkins: If you specify this for your top property, we can't switch to using bottom if your bottom is zero <fantasai> iank_: ok <fantasai> TabAtkins: wrt maximums, this will be part of that list <fantasai> TabAtkins: effectively position fallback will have an extra entry, so this will be factored into that maximum <fremy> q+ <fantasai> iank_: does this mean you need to set on both insets? <fantasai> TabAtkins: no, set one to anchor and the other to auto <fantasai> TabAtkins: it will be naturally sized <fantasai> TabAtkins: we'll swap the properties if we need to for fallback reasons <fantasai> [note that auto is the initial value] <fantasai> iank_: You can embed an anchor inside of a calc() expression <fantasai> iank_: so if you're not using the ?? anchor, what does that calc expression resolve to? <fantasai> TabAtkins: after doing the flip? <fantasai> TabAtkins: it resolves to the appropriate other edge <fantasai> TabAtkins: Luckily because insets are specified in opposite directions <Rossen_> ack dbaron <fantasai> TabAtkins: if you use calc() to put you 10px from bottom edge, that same calc will work against the top <fantasai> iank_: so you're taking the whole calc() and putting it in the other property, and the other property becomes auto? <fantasai> TabAtkins: yes, we resolve exactly as if you'd done it on the other side <fantasai> TabAtkins: just as position fallback already works, but we make it automatic for obvious placement <fantasai> iank_: sounds fine, just concerned about magical swapping of computed values at layout time <fantasai> TabAtkins: This is exactly position fallback, the same feature, we're just giving you one for free <fantasai> iank_: not exactly, because you're taking e.g. top is auto, bottom is calc expression with anchor <fantasai> iank_: the position fallback then is like the top becomes that calc function and bottom becomes auto <fantasai> iank_: oh, and I see, you're saying that this will automatically populate a fallback position entry with those two things <fantasai> iank_: okay, that makes sense <fantasai> iank_: if you can write that as a note, this is how it should be implemented, that would be helpful <fantasai> fremy: Small question <fantasai> fremy: because I believe this would be common <fantasai> fremy: what happens if you do it for top and left, and if so what happens? <fantasai> TabAtkins: what exactly will happen is a great question, which I have not thought through the implications of! <fantasai> TabAtkins: I don't have an answer for you yet. <fantasai> TabAtkins: I don't think it's hugely important what the answer is... worst case maybe generate 3 fallbacks, generating vertical flip, horizontal flip, or both <fantasai> TabAtkins: will think about it <iank_> likely want the flips in the logical space vs the physical? <fantasai> fremy: I guess maybe we can check what tooltips do on iOS/MacOS/Windows <iank_> block flip vs. vertical flip <fantasai> +1 iank <Rossen_> ack fremy <fantasai> TabAtkins: just wanted to get feedback, this was helpful <TabAtkins> well, doesn't matter, point is you flip to the opposite side of wherever it's specified |
All right, |
I don't see major issues with 3a60ac6, but some minor ones:
I think the correct behavior is to insert some flipped fallback position entries immediately after the current entry.
I think it should not. It doesn't make much sense when the base style is
.foo {
position: absolute;
top: calc(.5em + anchor(--foo auto));
} .foo {
position: absolute;
top: calc(.5em + anchor(--foo bottom));
position-fallback: --flip;
}
@position-fallback --flip {
@try {
top: auto;
bottom: calc(.5em + anchor(--foo top));
}
} In the second CSS, we'll always end up with the fallback position. To make this example work, if Alternatively, we can change the fallback position application algorithm into: try to use the base style first, and then if it overflows, try each fallback position. Not sure if it's a good idea... |
I defined it in a later commit - it just selects the appropriate side as normal, but doesn't insert any entries. This seemed like the simplest behavior, and if you're already writing a fallback list, you can just write your fallback cases; the point of this is to let you avoid writing a fallback list entirely. But I'd be willing to make a change if necessary.
It shouldn't, yeah. I meant the current text to express that, but I see I didn't properly hook things to the validity of the automatic anchor positioning. If you don't use the value correctly none of the effects should trigger, and it'll just be an invalid anchor function. I'll tweak to fix.
...huh, I thought that I wrote it to start with the base styles and only do the position-fallback if it fails from the start. But reading it again, you're right, we do indeed just jump straight into the fallback list. And on consideration, that's almost certainly the most sensible behavior anyway. So yeah, I'll need to add the base styles in. And also I probably want to change how this interacts with an existing fallback list - most likely I want to change it to only add entries if there is no list (for the same reason we don't add the base styles to the fallback list already). So you can either use auto positioning or use position-fallback, not both. That then also makes me feel better about doing what you suggested about using it in a fallback list, where it adds 1-3 extra entries after itself. That way you can do some simple flipping followed by something more complicated as a last resort. So in summary:
|
There's still value if we allow For example, the current position fallback list of @position-fallback -internal-selectmenu-listbox-default-fallbacks {
/* Below the anchor, left-aligned, no vertical scrolling */
@try {
inset-block-start: anchor(self-end);
inset-inline-start: anchor(self-start);
}
/* Below the anchor, right-aligned, no vertical scrolling */
@try {
inset-block-start: anchor(self-end);
inset-inline-end: anchor(self-end);
}
/* Over the anchor, left-aligned, no vertical scrolling */
@try {
inset-block-end: anchor(self-start);
inset-inline-start: anchor(self-start);
}
/* Over the anchor, right-aligned, no vertical scrolling */
@try {
inset-block-end: anchor(self-start);
inset-inline-end: anchor(self-end);
}
/* Below the anchor, left-aligned, fill up the available vertical space */
@try {
inset-block-start: anchor(self-end);
block-size: -webkit-fill-available;
inset-inline-start: anchor(self-start);
}
/* Below the anchor, right-aligned, fill up the available vertical space */
@try {
inset-block-start: anchor(self-end);
block-size: -webkit-fill-available;
inset-inline-end: anchor(self-end);
}
/* Over the anchor, left-aligned, fill up the available vertical space */
@try {
inset-block-end: anchor(self-start);
block-size: -webkit-fill-available;
inset-inline-start: anchor(self-start);
}
/* Over the anchor, right-aligned, fill up the available vertical space */
@try {
inset-block-end: anchor(self-start);
block-size: -webkit-fill-available;
inset-inline-end: anchor(self-end);
}
} With auto positioning, we can shrink its size in half (with some difference that shouldn't matter): @position-fallback -internal-selectmenu-listbox-default-fallbacks {
/* Below/above the anchor, left-aligned, no vertical scrolling */
@try {
inset-block-start: anchor(auto);
inset-inline-start: anchor(self-start);
}
/* Below/above the anchor, right-aligned, no vertical scrolling */
@try {
inset-block-start: anchor(auto);
inset-inline-end: anchor(self-end);
}
/* Below/above the anchor, left-aligned, fill up the available vertical space */
@try {
inset-block-start: anchor(auto);
block-size: -webkit-fill-available;
inset-inline-start: anchor(self-start);
}
/* Below/above the anchor, right-aligned, fill up the available vertical space */
@try {
inset-block-start: anchor(auto);
block-size: -webkit-fill-available;
inset-inline-end: anchor(self-end);
}
} (this also means that in the generated fallback positions, there should also be sizing and box alignment property values copied from the base style or the base Besides, the What if we want to use the same side with automatic flipping? For example, what if we have @position-fallback -internal-selectmenu-listbox-default-fallbacks {
/* Below/above the anchor, left/right-aligned, no vertical scrolling */
@try {
inset-block-start: anchor(auto);
inset-inline-start: anchor(auto-same-side);
}
/* Below/above the anchor, left/right-aligned, fill up the available vertical space */
@try {
inset-block-start: anchor(auto);
inset-inline-start: anchor(auto-same-side);
block-size: -webkit-fill-available;
}
} |
Right, that's why I said I should edit to allow it and expand in-place. ^_^
Oooh, I didn't even consider that "auto same side" could be useful, but you're right, it totally could be! I'll have to think of a better name, but consider it in the spec. |
What happens if both axes use #foo {
left: anchor(auto); /* invalid use of `auto` */
right: 0;
top: anchor(auto); /* valid use of `auto` */
bottom: auto;
} While the usage on the x-axis is invalid, I think it still makes sense to expand it on the y-axis. |
Yes, that follows from the (2) edit I said I needed to make:
|
…rides base styles, rather than using base styles as the first entry. #7757
All right, landed a few commits today:
|
Thanks for the edits! One more issue: what happens when the element's base style has both auto position fallback? The current version has only specified two cases:
|
I can't quite parse your question, but I assume it's "what happens if you use auto in base styles and also in a |
Are you suggesting that using auto in both base style with a position fallback doesn't make much sense? I was think of something else, but now I agree with that (the sentence above).
So how about making the restriction even stronger: if the base style has .target {
left: anchor(auto);
position-fallback: --pf;
}
@position-fallback --pf {
@try { top: ... }
@try { bottom: ... }
} Note that we may still need to evaluate a valid |
Per your earlier comment, base styles are ignored when using position fallback, so I'm matching that behavior.
That's what the current spec already does. |
Yeah, and it's what confused me. Maybe worths adding a note? |
…llback prevents anchor(auto) from creating fallback entries. #7757
I think this can be closed now? |
In the minutes of the CSSWG discussion, @emilio brought up that handling fallback completely and correctly can require a decent amount of code, but in most cases the desired fallback behavior is very simple and straightforward. He suggests having a more declarative version of the fallback specification, which will let authors get this "basic" position fallback without having to think thru a lot of cases (or copy-paste from a good example).
This sounds reasonable to me. The current API is intentionally somewhat low-level because, as Ian said in the discussion, there are a lot of cases that want the complexity. But it's also true that a large fraction, possibly a majority, of cases want a very simple behavior that can be described in a common fashion.
Here's my suggestion: we add a
auto
side keyword toanchor()
:anchor(--foo auto)
. This only works if the opposite property in the axis isauto
(so the positioning is guaranteed to not have an effect on the element's size); if this isn't true it resolves to 0.By default, it positions the specified edge of the element against the opposite edge of the anchor;
top: anchor(--foo auto)
puts your top edge against the bottom edge of the --foo anchor. It automatically falls back to the opposite set of edges (for both elements) if it needs to. (This doesn't change what property it's affecting - in the preceding example it would resolvetop
to a value that happens to place the element's bottom edge against the anchor's top edge, so 'transition: top 1s` still works to make it non-abrupt.) This applies to each axis independently; the other axis can also be auto-anchored, against the same or a different element, or be a fixed value or a non-auto anchor, whatever.This would resolve the "lots of small popups, all need a separate @position-fallback rule" problem.
Emilio also mentioned a "sticky"-like behavior, where the element defaults to sticking to an edge like above, but if it would slightly overflow it just slides enough to avoid that, rather than flipping. We could do this as well with a
sticky
keyword that's subject to the same constraints asauto
. I'm curious exactly what the constraints of this are, tho - does it stop sliding when the opposite edges are aligned? This would occur when the anchor is just about to slip off-screen entirely - is that what's desired?Interaction between these and position-fallback need to be defined. Maybe we consider these as effectively a first fallback entry, and then use the actual position-fallback only if they still overflow in either position?
The text was updated successfully, but these errors were encountered: