Repository navigation
[css-spatial] Lots of review feedback from the spec #134
Copy link
Copy link
Open
Description
Apologies, while reviewing the spec I ended up with lots of small bits of feedback and questions. I'll post them all as one big issue here, but I'm happy to break things out into their own issues if they deserve it; just let me know.
Environment maps:
- why is the name a string? Should be a dashed-ident. Let's not repeat the @Keyframes mistake.
- environment rotate defaulting to a y-axis rotation is fine, but should allow an arbitrary axis using the same syntax as
rotate. (this conflicts in the shorthand, tho) - all "applies to" are wrong, it's spatial containers with a back spatial context, not elements participating in one (that's all the children)
- position-context - is this meant to apply to all elements? text implies 3d only.
- position-area:origin - this is independent of the existing 3x3 grid, right? you can't do any spans, just position yourself "in" the zero-sized box? how is this intended to work? this would mean you're automatically overflowing, and thus this can't be used with position-try at all? (i kinda suspect this might just be for centering on the origin? If so, should do it with an origin-center alignment keyword a la anchor-center, instead. The other alignments can be done via
anchor()where referring to a singular line in an axis makes more sense.)- what even is the origin? The defining text isn't very clear; I feel like it's referring to some 3d model concepts that the writer assumed were obvious.
- it says for other elements we use offset-anchor, but that presumably needs to be extended into 3d as well, probably by extending the
<position>grammar.
- it says for other elements we use offset-anchor, but that presumably needs to be extended into 3d as well, probably by extending the
- what even is the origin? The defining text isn't very clear; I feel like it's referring to some 3d model concepts that the writer assumed were obvious.
- position:spatial-absolute - single-auto should instead be defined as "auto inset behaves as 0,
normalalignment is toward the non-auto side" - portal-action should expose the resultant transform - perhaps with another transform property, which is set on the element's style a la 'resize'? (Probably not altering 'portal-transform')
- 'inset-front' and 'inset-back' seem to be assuming that they're only used for shifting something. What's the 0 inset for each represent? (Remember that 'z-align-self' can make it not stretch to fill the z-axis; it can align to start/end.)
- z-align-items: is 'start' toward the front or back?
- anchor-size(depth) - I thought the z-axis size was still always 0 for now (elements don't have a thickness)? What size is this referring to? (Does it refer to front stage size?)
- general q: it seems there are two entirely unrelated 3d contexts here, the front (from
spatial:pagebut alsoportal) and back (just fromspatial:portal), and a descendant element chooses which one it's positioning itself into according to whether it usesposition: spatial-absolute(back space) or not (front space).- front space is a pretty straightforward extension of normal CSS concepts into the z axis. You can relpos or abspos yourself in or out of the page, you can anchor in the z-axis, align in the z-axis, etc.
- I think I heard Ada talking about 3d transforms, mentioning an element lifted off the page via
inset-backand then 3d-rotated to partially go behind the page. How do those interact with the front space and its limits? If you don't have a front space, are 3d transforms still always composited back into your flat page plane? (I'm not familiar with how 3d-rendering environments currently treat 3d transforms on the page) If transforms do interact with the front space, how do you handle elements being transformed out of the volume (front-wise or side-wise)?
- I think I heard Ada talking about 3d transforms, mentioning an element lifted off the page via
- back space is a new space, more akin to scrolling, which is infinite in extent but is only viewable through the viewport of the establishing element. elements living in the back space can't fly around the page or anything, just like elements in a scroller can go all over the place but are still only visible via the scrollport. So if you positioned something living in the backspace forward, higher than the viewport, it just wouldn't be visible because it's behind the "camera" of the viewport.
- Since we shorthand "scroll container's viewport" to "scrollport", clearly a spatial container's back-space viewport must be called the "spaceport".
- front space can push back "into" the page, but it doesn't actually extend out the back "physically" like it does in the front - if you were interacting with the page in a 3d environment and looked at the window from the side, stuff pushed forward visibly hovers off the page, but stuff pushed backwards isn't visible (it extends into its own space a la the back-space). right?
- front space's "back" overlaps with the back space but is its own coord system/etc (not affected by the various portal-* properties). Ideally stuff in the front and back spaces should z-interact.
- are all back-space portals created by non-descendants independent spaces? or are they shared?
- I see that if a parent container establishes a backspace, then children that also establish a backspace just live in that shared space (they just get their own coords overlaid for their purposes). This is akin to 3d transform containers sharing a context. Does this mean the grandchildren can effectively fly around and be visible frontwards of their their parent container (because they live in the grandparent's space alongside their parents, rather than being trapped in their parent's space)?
- How are the normal, non-positioned contents of a backspace box rendered? I just see that the backspace box doesn't render its own background, but I'm not finding rendering details otherwise.
- front space is a pretty straightforward extension of normal CSS concepts into the z axis. You can relpos or abspos yourself in or out of the page, you can anchor in the z-axis, align in the z-axis, etc.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
cabanier commentedon May 27, 2026
I'm worried about the complexity of this spec.
I believe the original proposal of simply having a true 3d CSS transform is a much simpler way to enable spatial content:
The original proposal didn't enable portals but we can likely add that separately.
tabatkins commentedon Jun 1, 2026
I'm sorry, if you have further issues I would appreciate you raising them in a separate thread. I did not, for example, question why there are front and back contexts.
AdaRoseCannon commentedon Jul 20, 2026
Sorry for the slow reply, I wanted to sit down with time to answer properly, and after I wrapped up all the post-travel chaos I kept rewriting it trying to get it just right, I hope this answers some of your questions. Sorry there are a couple which are probably best answered by Fantasai because they fall more strongly into her areas of expertise.
This is good feedback, thank you. I'll take a look at updating it.
Environment map images, when stored as the typical equirectangular format are free to rotate about the Y-axis, but very expensive to rotate around any other axes. This could potentially be performed at the shader level rather than the texture level, but I doubt this capability is widely available.
It doesn't actually affect the portal itself at all.
The environment map defines the light that is used by any surfaces which receive light within the portal.
Right now this would just be
<model>elements with physically based rendering materials, but if this proposal is one that is adopted, I think probably the next thing that would follow would be simulated physical materials for DOM elements, which would also be affected by this lighting.It can apply to all elements. If the element is attached to a named anchor in
<model>, then it would be transformed with the anchor. E.g. if the element was anchored to the front of a locomotive animated to be moving around a circular track, it will act like it is attached directly to the locomotive pointing forwards: it will always point forward relative to the locomotive, rather than always at the same angle relative to the portal/user. I'll look into updating the text to better reflect that.Yes, this is correct. It's not just for always being center-aligned; it may also be to put any corner/edge against the origin. But yes, the origin is a single point.
You are correct, this is a 3D model concept.
3D models have 3D bounding boxes that typically are not centered to the 0,0,0 coordinate (the origin).
This is partly a side effect of the way they are constructed but it is frequently a useful reference point in the model.
It's useful as a way to place a model: 3D models typically use the origin so that when the model is positioned using its origin, it is placed on a surface and looks correct.
It is also a useful location to place other things: for example, the origin is also used to combine multiple 3D models which all share the same origin, so when they are all rendered together they look correct.
I will add a note about this to the document, to make sure the need is adequately explained.
(Elika would be better placed to respond to this than I)
(I think this is another one where Elika would be better placed to respond to this than I)
I think that is a good idea.
Elika and I have talked about it being one of the properties in portal-transform so it can be stacked with the other transform properties. Currently, on the
<model>element, we expose it via the entityTransform property.For relative positioning, 0 is the plane upon which the element is already placed, so it does nothing.
For absolute-spatial positioning, 0 is the plane named in the inset; e.g. if you do
inset-front: 0;you are placed at the front-plane of the box used for absolute spatial positioning and increasing it moves you towards the back plane.inset-back: 0;is the back-plane of the box used for absolute spatial positioning and increasing it moves you towards the front-plane.This should be made clear, and it has me thinking — would it be weird if it were in the direction where there is the most space? E.g. for
spatial: portal;, start is at the surface and end is towards the portal. Forspatial: page;, start is at the surface and end is towards the user?Model elements have a depth related to the bounding box of their content.
That's correct. They have a distinction because they have different coordinate systems: one is page-space and page-scale, and the other can vary depending on the contents and can make use of the portal-transform and portal-action properties.
Elements which are transformed out of the volume to the side or front get clipped. It's not ideal, but it's the best that can be done to protect users whilst respecting the defined transforms. It's the primary reason we have all of the extra layout bits, so that developers have a way to perform layout that will always safely be able to be displayed which can't be guaranteed with transforms.
Correct.
I looooove this, 🚀 spaceport 🪐 💖
Correct, it disappears into the hole, which is flat on the page. Which feels very much like an acme portable hole
Yes, and they should. They have different coordinate spaces, but they share the 3D volume and should behave as such. So that if they were to intersect they should clip each other as one would expect because to a user there is no distinction.
They are independent spaces, so you can't have two adjacent
spatial: portalelements and have one object go in one and come out the other.In spatial contexts, they share a view-frustum because they are all rendered from the same point of view: the user's point of view (their eyes). Something that is open for discussion is what should happen in a 2D situation. I can see arguments for each of:
I heavily lean towards 3, because especially at the edges of a window there ends up being a big visual discrepancy between the untracked monoscopic experience and the head tracked stereoscopic experience.
I think it's up for debate whether the parent's front-limit is just used for layout or is also used for clipping. I think if it's just layout, that would be my expectation, but I am not set in stone about it.
The only way to be positioned in the backspace is by using
position: spatial-absolute;. The default position for them when not otherwise placed is to put them at the origin (the center of the total bounding box of its contents, or portal-stage if it is set).tabatkins commentedon Jul 24, 2026
Interesting, that limitation must be for some subtle reasons, as it seems like running a rotate transform on any vectors trying to find an environment map value would be trivial, at least no more complicated than running a Y rotation. Maybe the only way to do do the rotation is by changing the input image itself, not the light-mapper? That would explain why an equirectangular can only rotate in Y, as that's just shifting the pixels sideways with wraparound.
Is this true for cube as well?
...huh, that's extremely unclear from the descriptions. Do you mean that every element can use an entirely different environment map?
Note that the current description of the concept is just "Environment maps describe the lighting surrounding the spatial context.", which pretty directly states that it's something that affects the spatial context itself. ^_^ That should be fixed/expanded to give an accurate idea of how this functionality works.
Yeah, wording could use some updating to not imply it only does something to "3-dimensional objects" (an undefined term in the spec, thus my wrong inference).
I think this should use a more specific name than
-context, which is super generic and might be useful for more general position properties in the future. Maybe justposition-3d-contextor similar?tabatkins commentedon Jul 24, 2026
Whoops, accidentally hit enter too early. More responses incoming.
tabatkins commentedon Jul 24, 2026
Okay, then as I said, I think this isn't the right way to do this. Having an IMCB that is guaranteed to be zero-size and thus automatically putting an element into an overflow situation means it's hostile to the
position-tryfallback machinery. The functionality seems fine and useful, it just needs to be done in a different way. Either:origin-start/end/centeras alignment keywords would do the job in a way that fits better with the alignment models (and, for example, still interoperate with the existing position-area so we can do adjustment to keep it in the desired space, trigger overflow, etc)origin-centeras an alignment keyword, then just new spanningposition-areakeywords (origin-left,origin-bottom, etc) which give you a positioning area based on the origin point that's not zero-sized.Cool, figured, there seems to be a lot of assumptions about the (unspecified, unlinked)
<model>and even more mysterious<spatialfigure>elements.<model>is only mentioned in passing in an example in 2.1, and then in the default UA stylesheet. If some of these concepts depend on<model>/etc, we'll need more description of what they are and how they work, at least at a high level.Ooh, that's a good idea; a keyword that represents "whatever transform the portal-action is applying", and which is part of the initial value of 'portal-transform'. Then you can observe it just by asking 'portal-transform'.
Okay, that sounds good, but the abspos details are missing from the spec. The behavior of the inset properties for relpos and abspos are just 100% unrelated in CSS, they're a terrible design. You have to describe the two cases completely independently, along with stickypos (which in this case would be "no effect" since you can't be scrollable in the z-axis)
I think that's a bad idea. ^_^ The axis directions should be as consistent as possible in different situations. (We screwed up in Flexbox, for example, by basing alignment off of flex-axis directions rather than the standard block/inline directions; it's too much indirection and confusion.)
Cool, this just needs a gloss in the spec, like for "origin", since I have no idea what a
<model>is or what properties and interactions it has, and then an explicit reference to that in this definition, with a note that "normal" elements always have a depth of 0.Yup, I think I could just have used a little more explicitness that these are two independent and unrelated contexts that an element can have. I could infer it after reading a bit, it just wasn't clear to me in the intro, and having that knowledge up-front would probably have reduced some early confusion. (Initially I thought they were linked more, and the terms just referred to the part that extended "above" the page and "below" it.) Probably just moving some of the text out of the definition of 'spatial: portal' into the intro instead, with some illustrations/explanation? Then you can simplify the 'page' and 'portal' values to just say they create a front context, or both front and back.
Ah, searching for the word, I now find that 2.2's definition of "allowed volume" does define this, but very abbreviatedly and in what looks like a gloss. This could just use a little expansion, and maybe repetition in 2.2.1 defining the front spatial context (+ maybe a small example?)
Cool, that needs specifying.
Tho, hm, actually is it like the back volume? Or does the container elements (and other elements on the page, including the page canvas itself) obscure it? Is something pushed backwards in the front volume only visible if you have a back volume, or literally everything in the page underneath it is transparent? Or is it always clipped at the page if you don't have a back volume? (I think, re-reading the definition of "page-allowed volumn", that the answer is it clips unless there's a back volume for it to extend into.)
Cool, this could use a little bit of clarification.
Similarly, could just use a little bit of clarification. Likening it to overflow spaces might be a helpful analogy.
Hm, I'm not quite sure what this means. If an element has
spatial: portal; display: flex;, are its non-position: spatial-absolutechildren still laid out normally as flex children, just living in the element's plane as normal?AdaRoseCannon commentedon Jul 27, 2026
(tried to reply a little more timely this time)
You are right that it is something that could be done in code. I don't think it would be super expensive; my only hesitation is that not every browser might have access to the code that is doing the 3D rendering, whereas applying an offset to the texture itself can be done easily.
Having free rotation would allow for some fun effects with multiple environment maps. I am not totally anti-free-rotation, perhaps I am being overly cautious.
Rotations that are multiples of 90deg for sure, but I think otherwise it would need the same shader code as free rotations for equirectangular images.
This conversation makes me think that, in that case, we should either have free rotation or none at all.
Yeah, this is useful when you want minute control over the lighting of every element of the 3D content.
Will do
👍
I'll try to think of something potentially more fitting.
I'll go through this with Fantasai. This may take more time than the other issues raised; I will look into it, but it might not be in the PR that fixes some of the other issues raised.
The Immersive Web CG requested that the
<model>element explainer be focused on before writing a specification for it. My hope is that when the IWCG starts workingon the specification for
<model>, it references the Spatial CSS spec. But you are right, there will need to be some concepts which are<model>-specific that we will need to bring over. I wonder if it's worth having a section on "elements with depth" which right now is only<model>but could be reused if thickness or curvature are added later.😊
One of the reasons we have a separate
position: spatial-absolute;is so thatposition: absolute;can maintain the same behaviour from 2D and is not 3D aware, but you are correct that we left outposition: absolute's z-positioning, which is an oversight on my part. I think it should gain the same z inset behavior asposition: relative, but I would like to think through the consequences of that.Thanks for the context!
I'll look into drafting an "elements with depth" section
I'll take a look to see how I can improve this
👍
Yes, it would clip if you don't have a back volume and you tried to transform it into the page. But if you use inset properties to move it back then they are clamped so won't move below the page.
I'll look into putting together a mockup of 2 models each in the front and back spaces which have been transformed to intersect to show it visually.
👍
Yes correct! It was important to me that
spatial, when used on its own, changed as little as possible to help with back compat, and in that case allow a user to keep their original markup but still be able to raise or lower a card by adding spatial to the parent and one of the Z inset properties to move it.