CSS Spatial Layout Module Level 1

Unofficial Proposal Draft,

More details about this document
This version:
TBD
Issue Tracking:
CSSWG Issues Repository
Inline In Spec
Editors:
(Apple)
Elika J. Etemad / fantasai (Apple)
Suggest an Edit for this Spec:
GitHub Editor

Abstract

This module introduces three-dimensional layout to the CSS rendering model.

CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc.

Status of this document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C standards and drafts index.

Please send feedback by filing issues in GitHub (preferred), including the spec code “css-spatial” in the title, like this: “[css-spatial] …summary of comment…”. All issues and comments are archived. Alternately, feedback can be sent to the (archived) public mailing list www-style@w3.org.

This document is governed by the 18 August 2025 W3C Process Document.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent that the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

1. Introduction

This section is not normative.

This module introduces spatial (3-dimensional) layout to CSS, creating spatial contexts and extending relative and absolute positioning to handle the z-axis (perpendicular to the canvas).

Note: The z-index property is unrelated to spatial positioning, it merely controls the stacking order at a given z-axis position.

1.1. Value Definitions

This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3]. Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3]. Combination with other CSS modules may expand the definitions of these value types.

In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.

2. Establishing Spatial Contexts

2.1. Establishing Spatial Contexts and Portals: the spatial property

Name: spatial
Value: none | page | portal
Initial: none
Applies to: all elements except internal table elements, internal ruby elements, and inline boxes
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

The spatial property establishes a spatial container, generating one or more spatial contexts allowing 3-dimensional layout within the context.

This property was previously named portal, since it would establish a shared spatial context for all the boxes within it. For a spatial: portal value (previously portal: full), it’s clear when you need to create a shared context vs not. But for page contexts, it’s less clear. Do we want to encourage authors to set up shared contexts, or do we prefer them setting up independent ones for each thing they want to shift in the z axis? This may change whether we want to require shifted content to be contained by a spatial: page box or whether we want to allow such boxes to be able to shift their own z-axis position (by having the border box create an implicit spatial container). Relatedly, in the former case we might want to restrict this to apply to boxes that establish formatting contexts, an in the latter we’d want to allow them potentially even for inline boxes.

Values have the following meanings:

none
No effect.
page
Establishes a limited front spatial context, which allows spatial positioning in front of the element’s plane. (However, see front-limit for limits on this area.)
portal
Establishes two spatial contexts connected by a portal coinciding with the border box of the spatial container:

All content that is not positioned using absolute spatial positioning into one of these spatial contexts is drawn and laid out normally in 2D—​effectively on the “glass” of the portal (the portal plane)—​and affects / is affected by the size of the element’s box as usual. Relative positioning can be used to shift content in the z-axis relative to the “glass”, see § 3.1 Relative Spatial Positioning.

When the element establishes a back spatial context, the page is cut out behind its principal border box: neither its background nor any content behind the box in the normal page context is drawn. Instead, the user views the contents of the back spatial context.

When spatial is not none, the box establishes a containing volume for all descendant content not otherwise contained by a containing volume (excepting content whose containing block would be an ancestor of this element, such as fixed-positioned boxes).

Nesting a portal inside an existing portal does not create a new visual context or spatial “world”, it merely creates a new coordinate system within the existing back spatial context, establishing a new containing volume and portal transform only.

For example, nesting a model element (which establishes its own spatial container) inside an existing portal sizes and positions the model to its spatial container, but the model co-exists with the other content of the parent spatial container rather than creating a new pocket of reality with its own backdrop and lighting conditions.

2.2. Spatial Contexts and the Spatial Layout Model

A spatial context contains 3-dimensional layout, establishing a coordinate system in all three axes as well 3D rendering context for its content. It defines:

The page allowed volume limits the rendering of objects, and thus clips the allowed volume of any front spatial contexts on the page. It must include the entire viewport in the X and Y axes, and in front of the page it includes the volume between the page canvas and the UA-defined front limit plane. The UA may, however, extend the page allowed volume beyond the viewport.

Note: The page allowed volume can change during the lifetime of the page. For example, the UA might increase or decrease it depending on the placement of the browser window in relation to the user and/or other UI, the level of trust of the website, whether an immersive viewing mode has been enabled, whether the page has focus, etc.

2.2.1. The Front Spatial Context

The front spatial context of a spatial container establishes a 3-dimensional coordinate system relative to the spatial container itself. In the z-axis, the 3-dimensional box created by the spatial container’s border edges, portal plane, and front limit plane defines the front spatial context volume.

For absolute spatial positioning, the containing volume is the front spatial context volume. For relative spatial positioning, the containing volume is the front spatial context volume flattened to coincide with the portal plane.

The allowed volume of the front spatial context is the page allowed volume clipped by the spatial container’s front limit plane in front, clipped by the overflow clip edge on the sides (if overflow clipping applies), and in the case of a spatial: portal spatial container, extending infinitely into the back spatial context behind the portal.

An illustration of the front spatial context: the model and text have been raised off the page. The pencil model is shown at it’s true size in CSS units. The grid layout has been kept and is usable.

2.2.2. The Back Spatial Context

The back spatial context of a spatial container establishes a 3-dimensional coordinate system independent of the spatial container and its page. Content positioned into this context effectively exists inside its own 3D “world”, connected to the world of the page through the portal of the spatial container, which coincides with its border box. The allowed volume of the back spatial context is infinite and unlimited; however it is not rendered except behind the portal plane.

This portal itself is opaque to the page: through it, the user can only see the contents of the back spatial context, not any content placed behind it. By default, the backdrop color of the back spatial context is taken from the background-color property. If this color is not opaque, it is first composited on top of Canvas. The background-color is not otherwise painted, however all other background image layers are painted as usual on the portal plane, as if drawn on the “glass” of the portal.

Should this respond to color-scheme? How exactly?

An illustration of the back spatial context, the entity-transform of the portal scales the chair model down to fit the portal.

2.2.3. Full Portals: Connecting the Front and Back Spatial Contexts

A spatial: portal spatial container connects the front and back spatial contexts through its portal, as if it were a window into the back spatial context and the contents of the front spatial context were attached to its window “glass”.

The portal thus connects two coordinate systems: the page coordinate system (which has its own idea of <length> values) and the back spatial context coordinate system (which has its own idea of <length> values, see portal-transform). Objects in the front spatial context are always positioned using the page coordinate system, even when they are physically positioned inside the volume of the back spatial context. (In other words, the front spatial context and back spatial context can coincide in physical rendering space while maintaining their independent coordinate systems.)

An illustration of the front and back spatial context together, the pencil has been rotated so that it overlaps the back spatial context. It is unaffected by the back spatial context’s entity-transform.

2.3. Configuring the Back Spatial Context

The back spatial context can have an entity transform that differs from the page. The portal-transform and portal-action properties control this this transform, called the portal transform.

The containing volume of the back spatial context defaults to containing its contents; however it can be explicitly configured with the portal-stage property.

2.3.1. Defining Stage: the portal-stage property

Name: portal-stage
Value: [ auto | <length> ]{1,4} [ / [ auto | <length> ]{1,2} ]?
Initial: auto
Applies to: spatial containers establishing a back spatial context
Inherited: no
Percentages: n/a
Computed value: as specified, but with lengths made absolute
Canonical order: per grammar
Animation type: by computed value

The portal-stage property defines the containing volume of the back spatial context. Values prior to the slash (/) are assigned to each side as for margin; values after the slash (/) are assigned to the back and front, respectively.

Note: The allowed volume of the back spatial context is infinite. The containing volume is only providing the origin and a reference container for portal-transform: auto and absolute spatial positioning.

Values have the following meanings:

<length>
The stage expands from the origin by the specified length towards the indicated side. Values can be negative.
auto
The stage expands from the origin to contain the contents of the back spatial context on the indicated side.

Note: This means that portal-stage: auto will always include the world origin.

Negative values (including those calculated from auto) are clamped to ≤ max(0, opposite side) in order to ensure a non-negative containing volume.

The combination of portal-stage: auto (which sizes the stage based on its contents) and percentage inset values (which position the content based on the size of the stage) can give weird results.

Note: This property is analogous to the object-view-box property which applies to 2D objects.

2.3.1.1. Calculating an Automatic Containing Volume

For the purpose of portal-stage: auto, the contents of the back spatial context are measured:

For example, if a back spatial context with portal-stage: auto contained two 10″ cubes whose centers coincided with their origins:
  1. If they were not otherwise positioned, their origins would coincide at the world origin, and their bounding boxes would each be a 10" cube centered on the world origin, resulting in a containing volume that’s the same.

  2. If one cube were positioned left: 10in (which shifts it 10in to the right), then the containing volume would be from -5in on the left to 15in on the right, with this cube placed between 5in and 15in.

  3. If that cube instead had left: 10in; right: -1in; margin: -0.5in, then the containing volume would be instead from -5in on the left to 13in on the right, with this cube splaced between 4.5in and 14.5in.

  4. If that cube instead had left: 20%; right: 0, then we would resolve 20% to zero for the purpose of resolving the containing volume, thus yielding a containing volume from -5in to 5in. However the cube would then be centered between -2in and 5in (because overconstrained objects are centered, see § 3.2 Absolute Spatial Positioning: the spatial-absolute keyword for position), and thus actually placed between -3.5in and 6.5in.

2.3.2. Transforming the Stage: the portal-transform property

Name: portal-transform
Value: none | auto | auto? <transform-list> | <transform-list> auto <transform-list>?
Initial: auto
Applies to: spatial containers establishing a back spatial context
Inherited: no
Percentages: n/a
Computed value: as specified, but with lengths made absolute
Canonical order: per grammar
Animation type: Discrete if the presence of the none or auto keyword differs; otherwise interpolate each <transform-list> independently, see CSS Transforms 1 § 9 Interpolation of Transforms.

This property transforms the back spatial context, i.e. it sets the portal transform (sometimes called the “entity transform”). Transforms are applied in order from first to last; therefore transforms listed before auto affect the used value of auto, whereas transforms listed after it are applied after auto-fitting.

none
The origin of the back spatial context is the center of its border box on the portal plane, and the portal transform ensures that <length> values correspond between the page and the back spatial context (i.e. 1cm in the back spatial context is 1cm on the page.)
auto
The matrix required to fit the containing volume (or bounding sphere of the containing volume, in the case of portal-action: orbit) of the back spatial context into an axis-aligned rectangle whose sides align with content edges of the spatial container, and front aligns with the portal plane. If a <transform-list> is specified before auto, then it is applied to the containing volume prior to this calculation.
<transform-list>
The back spatial context is transformed as specified.

Note: This property has no effect on the contents of the front spatial context, even if they intersect with the physical space of the back spatial context.

Using the content edge for auto means that the padding of the spatial container insets auto-fitted content. If we want to allow different amounts of padding on portal content vs glass content, then we can introduce a new portal-padding property—​taking the same syntax as padding but with an initial value of auto that just copies from the regular padding to provide an easy default.

Unit Transforms There are use cases for units that aren’t transformed together with portal-transform inside the portal. For example to maintain consistently-sized readable labels of differently-sized objects. As an illustration, here are three different ways we could define the px unit:

This question needs more consideration, and might prompt the addition of new units or the re-interpretation of some existing ones (e.g. transforming the physical units together with the stage, but resolving px and relative units against the portal glass). See CSS Values 4 § 6 Distance Units: the <length> type for more information on CSS length units.

2.3.3. Interacting with the Stage: the portal-action property

Name: portal-action
Value: none | orbit || [ pan | pan-inline || pan-block || pan-x || pan-y || pan-z ]
Initial: none
Applies to: spatial containers establishing a back spatial context
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

This property indicates what kinds of motions the back spatial context can accept. The methods of providing the interaction are left to the user agent, and may include gestures, keyboard input, context menus, etc.

Values have the following meanings:

none
The contents of the back spatial context cannot be repositioned or rotated by the user.
orbit
The contents of the back spatial context can be rotated around the center of the containing volume by the user.

Note: This value also causes portal-transform: auto to use the bounding sphere instead of the bounding box.

pan
The contents of the back spatial context can be translated (in any direction) by the user.
pan-inline
pan-block
pan-x
pan-y
pan-z
The contents of the back spatial context can be translated (in the specified page-aligned axis) by the user.

The transform matrix introduced by these user interactions is applied after any transforms applied by portal-transform.

Better name for this property?

Note: For user gestures involving touch, the touch-action property can limit what sorts of gestures are propagated from the element receiving the action to the portal.

2.4. Front Limits: how far content can be raised on the page

The front limit of a spatial container defines how far forward content is allowed to be rendered. The resulting front limit plane is used to clamp the z-axis position of relatively positioned boxes and to clip content that is transformed too far forward towards the user.

The UA is responsible for choosing a comfortable front limit for the user (and defining the page allowed volume accordingly), in consideration of the window context, user preferences, etc. This limit may be dynamically updated during the life of the page.

The author can control the front limit effect with the the front-limit property; query the computed front-limit of a given spatial container using getComputedStyle(); and query the UA-dictated front limit for a page using the front-limit media query.

2.4.1. Controlling the Front Limit: the front-limit property

Name: front-limit
Value: <length [0,∞]> | max
Initial: TBD
Applies to: spatial containers
Inherited: yes
Percentages: n/a
Computed value: a computed <length>
Canonical order: per grammar
Animation type: by computed value type

This property defines the front limit of the spatial container. Its computed value is the specified length computed and clamped by the distance from the portal plane to the front plane of the page allowed volume.

Matching the front-limit media query, the computed value for front-limit from getComputedStyle() is not required to reflect temporary UA adjustments to the page allowed volume.

In this example we set front-limit on the root element allowing it to inherit to all the spatial containers on the page.
:root {
  front-limit: 5cm;
}
What should be the initial value? It probably shouldn’t be max, because that will likely set us up for compat pain. And zero wouldn’t be useful. Some possibilities:

2.4.2. Querying the Front Limit: the front-limit media query

Name: front-limit
For: @media
Value: <length [0,∞]>
Type: range

The front-limit media feature describes the front limit allowed by the UA for this page. This allows authors to define style rules that depend on whether and by how much objects can be placed in front of the page canvas plane.

The UA may limit the responsiveness of front-limit to temporary changes to the page allowed volume, to avoid exposing information about the external state of the user agent. In this case, its value should correspond to the effective page allowed volume when the page is in focus and under typical active use.

The front-limit media feature allows for alternative design decisions when there is not enough space available.

In this case when there is enough space the images sit in front of the page. Otherwise a portal will be added for them to have space.

.stack {
  position: relative;
  spatial: portal;
  front-limit: 5cm;
}

.stack img {
  rotate: calc(sibling-index() * 10deg);
  transform-origin: -10% 110%;

  position: absolute;
  inset-back: calc(100% - sibling-index() * 1cm);
}

@media (front-limit >= 5cm) {
  .stack {
    spatial: page;
  }
}
<div class="stack">
  <img src="photo-1.jpg" alt="First photo">
  <img src="photo-2.jpg" alt="Second photo">
  <img src="photo-3.jpg" alt="Third photo">
</div>

front-limit is false in the negative range.

2.5. Color Compositing in Spatial Contexts

Although by default colors are composited in sRGB in CSS, elements positioned in a spatial context are composited in a UA-chosen linear color space. Thus, for example, if a section within a spatial context is raised above its container using `back: 5mm`, although its children will composit with its own background using the usual rules applicable to flat pages, the result will composite with its container and other parts of the page in the linear color space used by the UA for spatial rendering.

Note: This may result in spatial elements having slight color discrepancies compared with otherwise-similar non-spatial elements.

3. Spatial Positioning

Within a containing volume, boxes can use spatial positioning to position themselves in 3D within their spatial context.

The front of the spatial context (towards the user) is considered to be the start side of the z-axis.

3.1. Relative Spatial Positioning

When a box is relatively positioned within a portal, it gains the ability to be positioned in the z-axis using the inset-front and inset-back properties (relative spatial positioning) in addition to positioning in the x-axis and y-axis using the other inset properties. These position the box relative to their containing block’s plane, allowing the content to shift forward and backward with respect to the page canvas plane.

Raising some text of the page by moving it 1cm from the back plane.
h1 {
  spatial: page;
  > span {
    position: relative;
    inset-back: 1cm;
  }
}
<h1><span>My Website</span></h1>

A relatively positioned box that is contained by a spatial container and whose inset-front or inset-back property is not auto is spatially positioned and rendered using spatial compositing. The box is otherwise composited in 2D with its containing block as normal.

3.1.1. Clamping Relative Spatial Positioning to the Front Limit

If the inset-front and inset-back properties would position the box beyond the front limit plane of its portal, then their used values are clamped to stay within the front limit.

In the case of spatial: page if the inset-front and inset-back properties would position the box below the portal plane, then their used values are clamped to stay in the plane of the page.

When elements' positions are clamped to the front limit plane or the portal plane, they are stacked and composited relative to other elements as if the z-axis distance between them were compressed to an infinitesimal amount (rather than flattening them into a single 2D composition).

Note: This means they will continue to be composited in the linear color space used for spatial compositing. See § 2.5 Color Compositing in Spatial Contexts.

3.2. Absolute Spatial Positioning: the spatial-absolute keyword for position

Name: position
New values: spatial-absolute

This module introduces the spatial-absolute keyword to the position property. When contained by a spatial container, this value takes the now spatially positioned box out of flow and positions it into the containing volume of the back spatial context (in the case of spatial: portal), or the front spatial context (in the case of spatial: page) of its spatial container.

This style of positioning is called absolute spatial positioning, and it uses the inset properties (including the new inset-back and inset-front properties) very similarly to absolute positioning. Since it works in 3D, in place of the containing block, this style of positioning uses a containing volume.

In all cases, percentage sizes (including sizing properties, inset properties, margins, and padding) that are defined to resolve against the containing block are resolved against the containing volume, see portal-stage.

Unlike regular absolute positioning, the normal value for self-alignment (the initial value) behaves as center, causing the box to use fit-content sizing for the automatic size, and centering the object when constrained on both sides by the inset-modified containing volume.

What should happen if there’s no containing portal?

3.3. Z-axis Insets: the inset-front and inset-back properties

Name: inset-back, inset-front
Value: <top>
Initial: auto
Applies to: boxes contained by a spatial container
Inherited: no
Percentages: for relative spatial positioning, relative to the distance from the containing block to the front limit plane; for absolute spatial positioning, relative to the z-axis size of the containing volume.
Computed value: as specified
Canonical order: per grammar
Animation type: by computed value type

These inset sub-properties control the z-axis in spatial positioning.

Values have the following meanings:

auto
If both insets in this axis are auto, then the box is positioned at its default z-axis position.

Otherwise, this value defers positioning to the opposite inset, see below.

<length-percentage>
The box’s position is inset by the specified amount from the front (for inset-front) or back (for inset-back).

When relative positioning, if inset-back is auto, then it computes to the negative of the computed value of inset-front; otherwise inset-front computes to the negative of the computed value of inset-back.

In case of overconstraint, inset-front is the weaker inset.

Need a diagram!

3.3.1. Inset Aliases: the inset-top, inset-right, inset-bottom, and inset-left properties

Name: inset-top, inset-right, inset-bottom, inset-left
Value: <top>
Initial: see individual properties
Applies to: see individual properties
Inherited: see individual properties
Percentages: see individual properties
Computed value: see individual properties
Animation type: see individual properties
Canonical order: per grammar

As a convenience to authors, this module introduces inset- prefixed legacy name aliases for the top, right, bottom, and left inset properties for consistency with the existing inset-block-start, inset-inline-start, inset-block-end, inset-inline-end properties and the newly introduced inset-front and inset-back properties. See CSS Positioned Layout 3 § 3 Positioning Coordinates and CSS Cascading 4 § 3.1 Property Aliasing.

3.3.2. Inset Shorthand: the / <inset-back> <inset-front>? extension to inset

Name: inset
New values: <top>{1, 4} / <inset-back> <inset-front>?

This module extends the inset shorthand property to include inset-back and inset-front as longhand properties, and introduces the ability to set these by specifying their values after a slash (/). Omitted values for inset-front or inset-back are set to auto.

3.4. Anchor-based Positioning

Absolute spatial positioning can also be used together with CSS Anchor Positioning. This module extends position-area and the anchor() functions to reference the z-axis and to reference the anchor’s origin in all three axes.

To handle origin-matching between two elements, we:

Alternatives considered include:

3.4.1. Anchor-based Coordinates: the position-context property

Name: position-context
Value: container | anchor
Initial: container
Applies to: absolutely positioned boxes
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

This property controls whether the box is oriented in the coordinate system of its containing block or if it is transformed together with its default anchor box.

Values have the following meanings:

container
The anchor box of a 3-dimensional object is its axis-aligned bounding box in the coordinate system of the positioned box’s containing volume, and the position-area grid is likewise oriented in the coordinate system of its containing volume.
anchor
The positioned box is transformed together with its default anchor box, and the used anchor box of any 3-dimensional object is the axis-aligned bounding box of the default anchor box’s coordinate system, however that has been transformed. Consequently, the positioned box moves, rotates, and scales together with the default anchor box, and the position-area grid is oriented in the default anchor box’s coordinate system.

3.4.2. Extensions to position-area: 3-dimensional areas

Name: position-area
New values: [ front | center | back | span-front | span-back | span-all ]?

This module extends the <position-area> syntax to append the following to all 2-keyword values:

[/ [ front | center | back | span-front | span-back | span-all ]]?

This creates a 3-axis syntax that describes a 27-sector 3-dimensional grid, essentially extruding the existing 9-sector 2-dimensional grid by adding areas corresponding to the front, center, and back of the anchor box. The z-axis positioning value is specified after a slash (/) following the 2D positioning values.

If only one keyword is specified, and it is a valid z-axis keyword, then it is repeated for all three axes. Otherwise, the z-axis value defaults to center.

3.4.3. Extensions to position-area: the origin area

Name: position-area
New values: origin

This module also extends the <position-area> syntax to allow the origin keyword everywhere that center is currently allowed. This value represents a (zero-sized) track that coincides with the anchor’s origin, and its area-specific default alignment is anchor-origin.

3.4.4. Extensions to anchor() and anchor-size()

This module extends the <anchor()> syntax to add front and back to <anchor-side>. Together with the existing start/end keywords, this allows the inset-front and inset-back inset properties to resolve against the corresponding sides of the anchor box.

Similarly, for sizing purposes, the <anchor-size()> syntax gains a depth keyword to represent the z-axis size of the anchor box.

It also adds an origin keyword to <anchor-side>, which resolves against the origin of the anchor box.

3.4.5. Extensions to Box Alignment: the z-align-self and z-align-items properties

Name: z-align-self
Value: <align-self>
Initial: normal
Applies to: spatially positioned boxes
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete
Name: z-align-items
Value: <align-items>
Initial: auto
Applies to: all elements
Inherited: no
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

These properties are analogous to align-self and align-items, except they operate in the z-axis.

They are sub-properties of the place-self and place-items shorthand properties, respectively, which are themselves extended as follows:

Name: place-self
Value: <align-self> <justify-self>? [ / <z-align-self> ]?
Initial: see individual properties
Applies to: see individual properties
Inherited: see individual properties
Percentages: see individual properties
Computed value: see individual properties
Animation type: see individual properties
Canonical order: per grammar
Name: place-items
Value: <align-items> <justify-items>? [ / <z-align-items> ]?
Initial: see individual properties
Applies to: see individual properties
Inherited: see individual properties
Percentages: see individual properties
Computed value: see individual properties
Animation type: see individual properties
Canonical order: per grammar

This module extends the place-self and place-items shorthand properties to also set <z-align-self> and <z-align-items> (respectively). If only one value is specified, then the shorthand sets all three axes to the same value. If only the z-axis value is omitted, then it is set to its initial value.

3.4.6. Extensions to Anchor Alignment: the anchor-origin value

Name: justify-self, align-self, z-align-self, justify-items, align-items, z-align-items
New values: anchor-origin

This module also introduces the new anchor-origin value, which centers the origin of the alignment subject against the origin of its default anchor box. When anchor-origin is specified, the inset properties behave differently in the affected axis: instead of adjusting the inset-modified containing block, they shift the origin of the alignment subject—​exactly as if it were a zero-sized containing block and the effective origin were the resulting inset-modified containing block.

For example, if the box’s horizontal origin is at 3cm, and we apply right: 1cm (leaving left as auto) then the resulting origin used for alignment is the point at 2cm. If we then alsoapply left: 0.5cm, then the result is overconstrained, and we ignore the weaker inset (the right inset in direction: ltr), resulting in an origin point at 3.5cm instead. If we instead apply left: -3cm, then we end up with an origin in the range [0,2cm], the center of which is 1cm, which gets matched with the center of the origin of the anchor box.

Like anchor-center, this value falls back to center when the box is not absolutely positioned or does not have a default anchor box.

3.4.7. Defining the Origin

For 3-dimensional objects with an origin coordinate, the origin is that coordinate. Otherwise, the origin of a box is the origin provided by the offset-anchor property.

Work out the interaction among these a little better. E.g. Maybe transform-origin should default to a coordinate-defined object’s actual origin rather than to 50% 50%.

4. Spatial Transforms

Transforms applied to spatially positioned objects—​whether direct children of the spatial container or not—​inherently participate in the 3D rendering context of the spatial context into which they are positioned. transform-style: preserve-3d can be used to extend this 3D rendering context further to nested elements that are not themselves spatially transformed.

Because it uses the actual perpsective of the user, the 3D rendering context of a spatial container ignores the perspective and perspective-origin properties.

We could also replace the perspective() function with the identity transform; or we can let it do whatever weird thing it ends up doing once it’s converted to a matrix anyway.

Note: Transforms that do not use preserve-3d are flattened as usual.

5. Interactions with Spatial Content

Elements which are contained in a portal continue to be part of the accessibility tree and to be interactive with all ways on the web content is.

For the purposes of pointer interactions, it is up to the user agent to identify the user’s target. However, to limit the amount of information shared about the user’s location, other than firing events on the correct target, the UA must not transmit precise coordinates for the interaction.

Note: The restriction on coordinates is to reduce the page’s ability to reconstruct the ray used for interactions.

6. Environment Maps

Environment maps describe the lighting surrounding the spatial context.

6.1. Defining Environment Maps: the @environment-map rule

Environment maps are named and defined using the @environment-map at-rule, which contains a list of environment map descriptor declarations. Its syntax is as follows:

<@environment-map> = @environment-map {
  <declaration-list>
}

It accepts the name, format, and src descriptors; all other declarations are ignored. As with @font-face rules, when a given descriptor is declared multiple times within an @environment-map rule the last one wins, and if multiple @environment-map rules are declared with the same name then then last one wins.

An @environment-map rule without a name is invalid; and although it appears in the [CSSOM], it is effectively ignored.

6.1.1. Naming an Environment Map: the name descriptor

Name: name
For: @environment-map
Value: <string>
Initial: N/A

The name descriptor declares the name of an environment map, allowing it to be referenced by the environment property.

6.1.2. Environment Map Formats: the format descriptor

Name: format
For: @environment-map
Value: equirectangular | cube
Initial: equirectangular

The format descriptor declares the format of an environment map, which defines how the source image is mapped into three dimensions.

Values have the following meanings:

equirectangular
The rectangular image is mapped onto the interior of a sphere (similar to a Mercator projection), with the top of the image coinciding with the y-most point on the sphere, the bottom of the image coinciding with the y-least point on the sphere, and the center of the image at the z-least point on the sphere (directly in front of the user).

If more than one source image is provided, the first one is used and all others are ignored.

cube
The six images provided in the src descriptor are mapped onto the interior of an axis-aligned cube, see § 6.1.2.1 Mapping Images Onto a Cube.

Make sure we’re happy with which side of these images faces forward.

6.1.2.1. Mapping Images Onto a Cube

The six source images for a cube mapping are assigned in the following order:

  1. Top (y-most)

  2. Ahead (z-least, i.e. in front of the the user, facing backward)

  3. Right (x-most)

  4. Behind (z-most, i.e. in back of the user, facing forward)

  5. Left (x-least)

  6. Bottom (y-least)

For the top and bottom sides, the top of the source image is attached to the ahead edge. For the right, left, ahead, and behind images, the top of the source image is attached to the top.

Omitted values are handled as follows:

  1. If only 5 images, the left is considered omitted and is mirrored from the right.

  2. If only 4 images, the behind is also considered omitted and is mirrored from the ahead.

  3. If only 3 images, the middle image is stretched to wrap around all four vertical sides, centered straight ahead.

  4. If only 2 images, the second is ignored; see 1 image.

  5. If only 1 image, it is divided into 4 equal columns and 3 equal rows, with the region in [2,2] used ahead and the others folded onto the cube (ignoring the regions that don’t fit).

  TT
  TT  
LLAARRBB
LLAARRBB
  BB
  BB
Diagram of the single-image cube fold

6.1.3. Environment Map Images: the src descriptor

Name: src
For: @environment-map
Value: <image>{1,6}
Initial: N/A

The src descriptor declares the sources of an environment map, which are images mapped on to the geometry of the environment map in accordance with the specified format.

6.2. Applying Environment Maps

Environment maps are applied to a portal’s back spatial context using the environment sub-properties, which form a coordinating list property group with environment-map as the coordinating list base property.

What about the front spatial context?

6.2.1. Referencing an Environment Map: the environment-map property

Name: environment-map
Value: auto | none | <string>#
Initial: auto
Applies to: elements participating in a back spatial context
Inherited: yes
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: discrete

This property applies an environment map to the back spatial context of a portal element.

Values have the following meanings:

none
The environment map is opaque black in all directions.
auto
The environment map used is the user agent’s default environment map.
<string>
The environment map is defined by the @environment-map rule matching the specified name (if any; otherwise it is opaque black).

When one or more environment maps are specified, each environment map adds its own contribution: each map’s color value is first multiplied by its corresponding environment-intensity and the results are summed.

Note: These calculations should be performed in a linear color space such as srgb-linear or display-p3-linear in order to be physically correct. A gamma-corrected color space will produce inaccurate results.

6.2.2. Brightening and Dimming the Environment: the environment-intensity property

Name: environment-intensity
Value: <number [0,∞]>#
Initial: 1
Applies to: elements participating in a back spatial context
Inherited: yes
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: by computed value type

This property specifies the intensity factor used when calculating the total lighting provided by the environment maps (see § 6.2.1 Referencing an Environment Map: the environment-map property).

6.2.3. Rotating the Environment: the environment-rotate property

Name: environment-rotate
Value: <angle>#
Initial: 0deg
Applies to: elements participating in a back spatial context
Inherited: yes
Percentages: n/a
Computed value: as specified
Canonical order: per grammar
Animation type: by computed value type

This property rotates the environment map specified by environment-map around the y-axis by the specified amount.

6.2.4. Declaring the Environment: the environment shorthand

Name: environment
Value: [ <environment-map> || <environment-intensity> || <environment-rotate> ]#
Initial: see individual properties
Applies to: see individual properties
Inherited: see individual properties
Percentages: see individual properties
Computed value: see individual properties
Animation type: see individual properties
Canonical order: per grammar

This shorthand property specifies environment-map, environment-intensity, and environment-rotate in a single declaration.

Appendix A: Default UA Stylesheet

This appendix provides CSS rules for implementing the behaviors of proposed spatial HTML elements.

model, spatialfigure {
  spatial: portal;
  front-limit: 0 !important;
}

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Advisements are normative sections styled to evoke special attention and are set apart from other normative text with <strong class="advisement">, like this: UAs MUST provide an accessible alternative.

Conformance classes

Conformance to this specification is defined for three conformance classes:

style sheet
A CSS style sheet.
renderer
A UA that interprets the semantics of a style sheet and renders documents that use them.
authoring tool
A UA that writes a style sheet.

A style sheet is conformant to this specification if all of its statements that use syntax defined in this module are valid according to the generic CSS grammar and the individual grammars of each feature defined in this module.

A renderer is conformant to this specification if, in addition to interpreting the style sheet as defined by the appropriate specifications, it supports all the features defined by this specification by parsing them correctly and rendering the document accordingly. However, the inability of a UA to correctly render a document due to limitations of the device does not make the UA non-conformant. (For example, a UA is not required to render color on a monochrome monitor.)

An authoring tool is conformant to this specification if it writes style sheets that are syntactically correct according to the generic CSS grammar and the individual grammars of each feature in this module, and meet all other conformance requirements of style sheets as described in this module.

Partial implementations

So that authors can exploit the forward-compatible parsing rules to assign fallback values, CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support. In particular, user agents must not selectively ignore unsupported component values and honor supported values in a single multi-value property declaration: if any value is considered invalid (as unsupported values must be), CSS requires that the entire declaration be ignored.

Implementations of Unstable and Proprietary Features

To avoid clashes with future stable CSS features, the CSSWG recommends following best practices for the implementation of unstable features and proprietary extensions to CSS.

Non-experimental implementations

Once a specification reaches the Candidate Recommendation stage, non-experimental implementations are possible, and implementors should release an unprefixed implementation of any CR-level feature they can demonstrate to be correctly implemented according to spec.

To establish and maintain the interoperability of CSS across implementations, the CSS Working Group requests that non-experimental CSS renderers submit an implementation report (and, if necessary, the testcases used for that implementation report) to the W3C before releasing an unprefixed implementation of any CSS features. Testcases submitted to W3C are subject to review and correction by the CSS Working Group.

Further information on submitting testcases and implementation reports can be found from on the CSS Working Group’s website at http://www.w3.org/Style/CSS/Test/. Questions should be directed to the public-css-testsuite@w3.org mailing list.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-2023]
Chris Lilley; et al. CSS Snapshot 2023. URL: https://drafts.csswg.org/css-2023/
[CSS-ALIGN-3]
Elika Etemad; Tab Atkins Jr.. CSS Box Alignment Module Level 3. URL: https://drafts.csswg.org/css-align/
[CSS-ANCHOR-POSITION-1]
Tab Atkins Jr.; Elika Etemad; Ian Kilpatrick. CSS Anchor Positioning Module Level 1. URL: https://drafts.csswg.org/css-anchor-position-1/
[CSS-BACKGROUNDS-3]
Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. URL: https://drafts.csswg.org/css-backgrounds/
[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. URL: https://drafts.csswg.org/css-box-4/
[CSS-CASCADE-4]
Elika Etemad; Tab Atkins Jr.. CSS Cascading and Inheritance Level 4. URL: https://drafts.csswg.org/css-cascade-4/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. URL: https://drafts.csswg.org/css-cascade-5/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. URL: https://drafts.csswg.org/css-color-4/
[CSS-COLOR-ADJUST-1]
Elika Etemad; et al. CSS Color Adjustment Module Level 1. URL: https://drafts.csswg.org/css-color-adjust-1/
[CSS-CONDITIONAL-3]
Chris Lilley; David Baron; Elika Etemad. CSS Conditional Rules Module Level 3. URL: https://drafts.csswg.org/css-conditional-3/
[CSS-DISPLAY-4]
Elika Etemad; Tab Atkins Jr.. CSS Display Module Level 4. URL: https://drafts.csswg.org/css-display-4/
[CSS-FONTS-5]
Chris Lilley. CSS Fonts Module Level 5. URL: https://drafts.csswg.org/css-fonts-5/
[CSS-IMAGES-3]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS Images Module Level 3. URL: https://drafts.csswg.org/css-images-3/
[CSS-LOGICAL-1]
Elika Etemad; Rossen Atanassov. CSS Logical Properties and Values Module Level 1. URL: https://drafts.csswg.org/css-logical-1/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. URL: https://drafts.csswg.org/css-overflow-3/
[CSS-OVERFLOW-4]
David Baron; Florian Rivoal; Elika Etemad. CSS Overflow Module Level 4. URL: https://drafts.csswg.org/css-overflow-4/
[CSS-POSITION-3]
Elika Etemad; Tab Atkins Jr.. CSS Positioned Layout Module Level 3. URL: https://drafts.csswg.org/css-position-3/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Box Sizing Module Level 3. URL: https://drafts.csswg.org/css-sizing-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://drafts.csswg.org/css-syntax/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. URL: https://drafts.csswg.org/css-transforms/
[CSS-TRANSFORMS-2]
Tab Atkins Jr.; et al. CSS Transforms Module Level 2. URL: https://drafts.csswg.org/css-transforms-2/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. URL: https://drafts.csswg.org/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. URL: https://drafts.csswg.org/css-writing-modes-4/
[CSS2]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. URL: https://drafts.csswg.org/css2/
[CSSOM]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[MEDIAQUERIES-5]
Tab Atkins Jr.; et al. Media Queries Level 5. URL: https://drafts.csswg.org/mediaqueries-5/
[MODEL-ELEMENT]
The <model> element. Draft Community Group Report. URL: https://immersive-web.github.io/model-element/
[MOTION-1]
Tab Atkins Jr.; Dirk Schulze; Jihye Hong. Motion Path Module Level 1. URL: https://drafts.csswg.org/motion-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SVG2]
Amelia Bellamy-Royds; et al. Scalable Vector Graphics (SVG) 2. URL: https://w3c.github.io/svgwg/svg2-draft/

Informative References

[COMPAT]
Mike Taylor. Compatibility Standard. Living Standard. URL: https://compat.spec.whatwg.org/
[CSS-IMAGES-5]
CSS Images Module Level 5. Editor's Draft. URL: https://drafts.csswg.org/css-images-5/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 3. URL: https://drafts.csswg.org/css-writing-modes-3/

Property Index

Name Value Initial Applies to Inh. %ages Anim­ation type Canonical order Com­puted value
environment [ <environment-map> || <environment-intensity> || <environment-rotate> ]# see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
environment-intensity <number [0,∞]># 1 elements participating in a back spatial context yes n/a by computed value type per grammar as specified
environment-map auto | none | <string># auto elements participating in a back spatial context yes n/a discrete per grammar as specified
environment-rotate <angle># 0deg elements participating in a back spatial context yes n/a by computed value type per grammar as specified
front-limit <length [0,∞]> | max TBD spatial containers yes n/a by computed value type per grammar a computed <length>
inset-back <top> auto boxes contained by a spatial container no for relative spatial positioning, relative to the distance from the containing block to the front limit plane; for absolute spatial positioning, relative to the z-axis size of the containing volume. by computed value type per grammar as specified
inset-bottom <top> see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
inset-front <top> auto boxes contained by a spatial container no for relative spatial positioning, relative to the distance from the containing block to the front limit plane; for absolute spatial positioning, relative to the z-axis size of the containing volume. by computed value type per grammar as specified
inset-left <top> see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
inset-right <top> see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
inset-top <top> see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
place-items <align-items> <justify-items>? [ / <z-align-items> ]? see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
place-self <align-self> <justify-self>? [ / <z-align-self> ]? see individual properties see individual properties see individual properties see individual properties see individual properties per grammar see individual properties
portal-action none | orbit || [ pan | pan-inline || pan-block || pan-x || pan-y || pan-z ] none spatial containers establishing a back spatial context no n/a discrete per grammar as specified
portal-stage [ auto | <length> ]{1,4} [ / [ auto | <length> ]{1,2} ]? auto spatial containers establishing a back spatial context no n/a by computed value per grammar as specified, but with lengths made absolute
portal-transform none | auto | auto? <transform-list> | <transform-list> auto <transform-list>? auto spatial containers establishing a back spatial context no n/a Discrete if the presence of the none or auto keyword differs; otherwise interpolate each <transform-list> independently, see CSS Transforms 1 § 9 Interpolation of Transforms. per grammar as specified, but with lengths made absolute
position-context container | anchor container absolutely positioned boxes no n/a discrete per grammar as specified
spatial none | page | portal none all elements except internal table elements, internal ruby elements, and inline boxes no n/a discrete per grammar as specified
z-align-items <align-items> auto all elements no n/a discrete per grammar as specified
z-align-self <align-self> normal spatially positioned boxes no n/a discrete per grammar as specified

@environment-map Descriptors

Name Value Initial
format equirectangular | cube equirectangular
name <string> N/A
src <image>{1,6} N/A

@media Descriptors

Name Value Initial Type
front-limit <length [0,∞]> range

Issues Index

This property was previously named portal, since it would establish a shared spatial context for all the boxes within it. For a spatial: portal value (previously portal: full), it’s clear when you need to create a shared context vs not. But for page contexts, it’s less clear. Do we want to encourage authors to set up shared contexts, or do we prefer them setting up independent ones for each thing they want to shift in the z axis? This may change whether we want to require shifted content to be contained by a spatial: page box or whether we want to allow such boxes to be able to shift their own z-axis position (by having the border box create an implicit spatial container). Relatedly, in the former case we might want to restrict this to apply to boxes that establish formatting contexts, an in the latter we’d want to allow them potentially even for inline boxes.
Should this respond to color-scheme? How exactly?
Using the content edge for auto means that the padding of the spatial container insets auto-fitted content. If we want to allow different amounts of padding on portal content vs glass content, then we can introduce a new portal-padding property—​taking the same syntax as padding but with an initial value of auto that just copies from the regular padding to provide an easy default.
Unit Transforms There are use cases for units that aren’t transformed together with portal-transform inside the portal. For example to maintain consistently-sized readable labels of differently-sized objects. As an illustration, here are three different ways we could define the px unit:

This question needs more consideration, and might prompt the addition of new units or the re-interpretation of some existing ones (e.g. transforming the physical units together with the stage, but resolving px and relative units against the portal glass). See CSS Values 4 § 6 Distance Units: the <length> type for more information on CSS length units.

Better name for this property?
What should be the initial value? It probably shouldn’t be max, because that will likely set us up for compat pain. And zero wouldn’t be useful. Some possibilities:
What should happen if there’s no containing portal?
Need a diagram!
Work out the interaction among these a little better. E.g. Maybe transform-origin should default to a coordinate-defined object’s actual origin rather than to 50% 50%.
We could also replace the perspective() function with the identity transform; or we can let it do whatever weird thing it ends up doing once it’s converted to a matrix anyway.
Make sure we’re happy with which side of these images faces forward.
What about the front spatial context?