diff --git a/files/en-us/web/api/viewport/index.md b/files/en-us/web/api/viewport/index.md new file mode 100644 index 000000000000000..c93166121b0f7a6 --- /dev/null +++ b/files/en-us/web/api/viewport/index.md @@ -0,0 +1,35 @@ +--- +title: Viewport +slug: Web/API/Viewport +page-type: web-api-interface +status: + - experimental +browser-compat: api.Viewport +--- + +{{APIRef("Viewport Segments API")}}{{SeeCompatTable}} + +The **`Viewport`** interface of the {{domxref("Viewport Segments API", "Viewport Segments API", "", "nocode")}} represents the device's {{glossary("viewport")}}. + +{{InheritanceDiagram}} + +## Instance properties + +- {{domxref("Viewport.segments", "segments")}} {{ReadOnlyInline}} {{Experimental_Inline}} + - : Returns an array of {{domxref("DOMRect")}} objects representing the position and dimensions of each viewport segment within the overall display. + +## Specifications + +{{Specifications}} + +## Browser compatibility + +{{Compat}} + +## See also + +- [Using the Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API/Using) +- [Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API) +- [Visual Viewport API](/en-US/docs/Web/API/Visual_Viewport_API) +- [Device Posture API](/en-US/docs/Web/API/Device_Posture_API) +- [Origin trial for Foldable APIs](https://developer.chrome.com/blog/foldable-apis-ot) via developer.chrome.com (2024) diff --git a/files/en-us/web/api/viewport/segments/index.md b/files/en-us/web/api/viewport/segments/index.md new file mode 100644 index 000000000000000..fc60c696bd3ccd0 --- /dev/null +++ b/files/en-us/web/api/viewport/segments/index.md @@ -0,0 +1,47 @@ +--- +title: "Viewport: segments property" +short-title: segments +slug: Web/API/Viewport/segments +page-type: web-api-instance-property +status: + - experimental +browser-compat: api.Viewport.segments +--- + +{{APIRef("Viewport Segments API")}}{{SeeCompatTable}} + +The **`segments`** read-only property of the {{domxref("Viewport")}} interface returns an array of {{domxref("DOMRect")}} objects representing the position and dimensions of each viewport segment within the overall display. + +## Value + +An array of {{domxref("DOMRect")}} objects. + +## Examples + +### Basic `Viewport.segments` usage + +This snippet will loop through each segment in the viewport, and log a string to the console detailing the index number, width, and height. + +```js +const segments = window.viewport.segments; + +segments.forEach((segment) => + console.log( + `Segment ${segments.indexOf(segment)} is ${segment.width}px x ${segment.height}px`, + ), +); +``` + +## Specifications + +{{Specifications}} + +## Browser compatibility + +{{Compat}} + +## See also + +- [Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API) +- [Visual viewport API](/en-US/docs/Web/API/Visual_Viewport_API) +- [Origin trial for Foldable APIs](https://developer.chrome.com/blog/foldable-apis-ot) via developer.chrome.com (2024) diff --git a/files/en-us/web/api/viewport_segments_api/index.md b/files/en-us/web/api/viewport_segments_api/index.md new file mode 100644 index 000000000000000..3f116abfbcfb6fa --- /dev/null +++ b/files/en-us/web/api/viewport_segments_api/index.md @@ -0,0 +1,61 @@ +--- +title: Viewport Segments API +slug: Web/API/Viewport_segments_API +page-type: web-api-overview +status: + - experimental +browser-compat: api.Viewport.segments +--- + +{{DefaultAPISidebar("Viewport segments API")}}{{seecompattable}} + +The **Viewport Segments API** allows developers to access the position and dimensions of logically separate viewport segments using CSS and JavaScript. _Viewport segments_ are created when the viewport is split by one or more hardware features such as a fold or a hinge between separate displays. With the Viewport Segments API, developers can create responsive designs optimized for different viewport segment sizes and arrangements. + +## Concepts and usage + +Devices with multiple displays that are intended to act as different segments of the same display surface (think foldable or hinged screen smartphones) present some unique design challenges to developers. You can optimize your layout for the display as a single entity, but how can you ensure that design elements fit snugly on the different segments and are not cut into two pieces? And how can you prevent content from being hidden by the physical fold/join? + +It may be important to know whether a user's device screen has a fold or join, what size the different segments are, whether they are the same size, and the orientation of the segments (whether they are side-by-side or top-to-bottom). The Viewport Segments API enables accessing the user's segmented device information with CSS and JavaScript features that provide access to the position and dimensions of each viewport segment within a display, including {{cssxref("@media")}} features to detect different horizontal and vertical region layouts. + +For an explanation of how the API works, see [Using the Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API/Using). + +## Interfaces + +- {{domxref("Viewport")}} + - : Represents the device's viewport. Provides access to the {{domxref("Viewport.segments")}} property, which returns an array of {{domxref("DOMRect")}} objects representing the position and dimensions of each viewport segment within a segmented display. + +### Extensions to other interfaces + +- {{domxref("Window.viewport")}} + - : Returns a `Viewport` object instance, which provides information about the current state of the device's viewport. + +## CSS features + +- {{cssxref("@media/horizontal-viewport-segments", "horizontal-viewport-segments")}} `@media` feature + - : Detects whether the device has a specified number of viewport segments laid out horizontally. +- {{cssxref("@media/vertical-viewport-segments", "vertical-viewport-segments")}} `@media` feature + - : Detects whether the device has a specified number of viewport segments laid out vertically. +- [Viewport segment environment variables](/en-US/docs/Web/CSS/env#viewport-segment-width) + - : These [environment variables](/en-US/docs/Web/CSS/CSS_environment_variables/Using_environment_variables) provide access to the edge coordinates and dimensions of each viewport segment. + +## Examples + +You can find a complete example demonstrating usage of the above features in the [Viewport segment API demo](https://mdn.github.io/dom-examples/viewport-segment-api/). + +If possible, you should view the demo on a foldable device. Current browser developer tools enable emulating foldable devices, but don't include emulation of the different physical segments. + +## Specifications + +{{Specifications}} + +## Browser compatibility + +{{Compat}} + +## See also + +- [Using the Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API/Using) +- [Viewport API](/en-US/docs/Web/API/Viewport_API) +- [Device Posture API](/en-US/docs/Web/API/Device_Posture_API) +- [CSS environment variables](/en-US/docs/Web/CSS/CSS_environment_variables) module +- [Origin trial for Foldable APIs](https://developer.chrome.com/blog/foldable-apis-ot) via developer.chrome.com (2024) diff --git a/files/en-us/web/api/viewport_segments_api/using/env-var-indices.png b/files/en-us/web/api/viewport_segments_api/using/env-var-indices.png new file mode 100644 index 000000000000000..ddb1aea08da7dde Binary files /dev/null and b/files/en-us/web/api/viewport_segments_api/using/env-var-indices.png differ diff --git a/files/en-us/web/api/viewport_segments_api/using/index.md b/files/en-us/web/api/viewport_segments_api/using/index.md new file mode 100644 index 000000000000000..33db1df88b3167e --- /dev/null +++ b/files/en-us/web/api/viewport_segments_api/using/index.md @@ -0,0 +1,340 @@ +--- +title: Using the Viewport Segments API +slug: Web/API/Viewport_segments_API/Using +page-type: guide +--- + +{{DefaultAPISidebar("Viewport segments API")}} + +This article explains how to use the [Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API) to create responsive designs optimized for different viewport segment sizes and arrangements. + +## The problem with foldable devices + +Foldable devices include smartphones, tablets, and laptops. Some fold inward, with the display folding into the interior of the device, and some fold outward, with the display wrapping around the device. Foldable devices come in a variety of forms: some have an actual folding screen, whereas some have separate screens with a physical hinge in the middle. They may be used in landscape orientation, with two screens side-by-side, and portrait orientation, with a top and a bottom screen. + +Whatever the case, foldable device displays are intended to act as different segments of the same display surface. While one person's foldable device may appear seemless and be used fully flat, similar to a single-segmented viewport, another may have an apparent seam be used at an angle that is less than a fully open, flat screen. This presents some unique challenges. You can optimize your layout for the display as a single entity, but how can you ensure that design elements fit snugly on the different segments and are not cut into two pieces? And how can you prevent content from being hidden by the physical fold/join? + +The viewport segments API provides features that allow you to detect (in CSS and JavaScript) whether the user's device screen has a fold or join, what size the different segments are, whether they are the same size, and what orientation they are in (side-by-side or top-to-bottom). We'll introduce you to these features in the following sections, then walk through a complete example to show them in action. + +## Viewport segment media features + +Two [media query](/en-US/docs/Web/CSS/CSS_media_queries) features are available that enable testing whether a device has a specific number of viewport segments laid out horizontally or vertically. These look like so: + +```css +/* Segments are laid out horizontally. */ +@media (horizontal-viewport-segments: 2) { + .wrapper { + flex-direction: row; + } + + /* ... */ +} + +/* Segments are laid out vertically. */ +@media (vertical-viewport-segments: 2) { + .wrapper { + flex-direction: column; + } + + /* ... */ +} +``` + +The {{cssxref("@media/horizontal-viewport-segments")}} media feature detects whether the device has a specified number of viewport segments laid out horizontally, whereas the {{cssxref("@media/vertical-viewport-segments")}} media feature detects whether the device has a specified number of viewport segments laid out vertically. + +## Viewport segment environment variables + +To be fit a layout precisely into the available viewport segments, the [viewport segment environment variables](/en-US/docs/Web/CSS/env#viewport-segment-width) provide access to each segments dimensions and placement inside the overall viewport. The browser provides [environment variables] enabling access to each segment's width and height and the offset positions of its top, right, bottom, and left edges: + +- `viewport-segment-width` +- `viewport-segment-height` +- `viewport-segment-top` +- `viewport-segment-right` +- `viewport-segment-bottom` +- `viewport-segment-left` + +The {{cssxref("env()")}} function is used to access these variables, with the name of the variable and two integers representing the indices of the segment to return the value for. For example: + +```css +/* Return the width of the top/left segment */ +env(viewport-segment-width 0 0) + +/* Return the width of the right segment */ +env(viewport-segment-width 1 0) + +/* Return the width of the bottom segment */ +env(viewport-segment-width 0 1) +``` + +The indices are both integers of `0` or greater. The first value represents the horizontal index value of the segment, and the second value represents the vertical index value of the segment: + + + +- In a horizontal side-by-side layout, the left segment is represented by `0 0`, and the right segment is represented by `1 0`. +- In a vertical top to bottom layout, the top segment is represented by `0 0`, and the bottom segment is represented by `0 1`. + +In a layout, you can use these variables to set your containers to fit neatly into the available segments. For example: + +```css +@media (horizontal-viewport-segments: 2) { + .wrapper { + display: grid; + grid-template: "left fold right"; + grid-columns: env(viewport-segment-width 0 0) + env(viewport-segment-width 1 0); + } + .firstSection { + grid-area: left; + } + .secondSection { + grid-area: right; + } +} + +@media (vertical-viewport-segments: 2) { + .wrapper { + display: grid; + grid-template: + "top" + "bottom"; + grid-rows: env(viewport-segment-height 0 1) env(viewport-segment-width 0 0); + } + .firstSection { + grid-area: top; + } + .secondSection { + grid-area: bottom; + } +} +``` + +Here, we are setting the outer wrapper to be either a horizontal or vertical grid layout, based on whether the viewport segments are laid out horizontally or vertically. We then set the left and top cells to be the first segments, and place the second section in the right or bottom grid cells. + +We could add an empty middle "fold" cell to stop content being obscured by the fold. We could calculate its thickness by subtracting the combined widths or heights of the two sides from the full viewport size, or set the middle cell to `1fr`. + +```css +@media (horizontal-viewport-segments: 2) { + .wrapper { + grid-template: "left fold right"; + grid-columns: + env(viewport-segment-width 0 0) + calc(100vw - (env(viewport-segment-width 0 0) + env(viewport-segment-width 1 0)) + env(viewport-segment-width 1 0); + } +} + +@media (vertical-viewport-segments: 2) { + .wrapper { + grid-template: + "top" + "fold" + "bottom"; + grid-rows: env(viewport-segment-height 0 1) 1fr env(viewport-segment-width 0 0); + + } +} +``` + +## Accessing segment information in JavaScript + +You can access segment information in JavaScript using the {{domxref("Viewport.segments", "window.viewport.segments")}} property, which returns an array of {{domxref("DOMRect")}} objects providing access to each segment's `x` and `y` coordinates inside the overall viewport, and their `width` and `height`. + +For example, this snippet will loop through each segment in the viewport and log a string to the console detailing the index number, width, and height. + +```js +const segments = window.viewport.segments; + +segments.forEach((segment) => + console.log( + `Segment ${segments.indexOf(segment)} is ${segment.width}px x ${segment.height}px`, + ), +); +``` + +## A complete example + +Let's look at the Viewport Segment API features in action in a real example. You can see our example running live at [Viewport segment API demo](https://mdn.github.io/dom-examples/viewport-segment-api/) (see the full [source code](https://github.com/mdn/dom-examples/tree/main/viewport-segment-api) also). If possible, view the demo on a real foldable device. Browser developer tools that enable visually emulating the multiple segments of foldable devices generally don't include emulation of the physical segmentation. + +> [!NOTE] +> This example is adapted from [Origin trial for Foldable APIs](https://developer.chrome.com/blog/foldable-apis-ot) by Alexis Menard and Thomas Steiner, originally published on `developer.chrome.com` in 2024 under the [Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/). + +We'll run through the source code in the following sections. + +### HTML structure + +The markup features a wrapper {{htmlelement("div")}} containing two {{htmlelement("section")}} elements representing a basic list view and detail view, and a fold `
${segment.width}px x ${segment.height}px
`; +} +``` + +Next, we define a `reportSegments()` function which removes any previously appended segment output `@@ -147,6 +174,10 @@ The below example makes use of the optional second parameter of `env()`, which a
``` +#### CSS + +We set a {{cssxref("width")}} of `300px` and a {{cssxref("border")}}. We then add {{cssxref("padding")}}, using the `env()` function with a fallback for the size of the padding on each side. We intentionally set an invalid value for the left padding (remember, environment variable names are case-sensitive), to demonstrate the use of the fallback value. + ```css p { width: 300px; @@ -156,32 +187,13 @@ p { } ``` -{{EmbedLiveSample("Using_the_fallback_value", "350px", "250px")}} - -### Example values - -```css -/* zero for all rectangular user agents */ -padding: env(safe-area-inset-bottom, 50px); +#### Results -/* 50px because UA properties are case sensitive */ -padding: env(Safe-area-inset-bottom, 50px); - -/* as if padding: '50px 20px' were set because x is not a valid environment variable */ -padding: env(x, 50px 20px); - -/* ignored because '50px, 20px' is not a valid padding value and x is not a valid environment variable */ -padding: env(x, 50px, 20px); -``` - -The syntax of the fallback, like that of custom properties, allows commas. But, if the property value doesn't support commas, the value is not valid. - -> [!NOTE] -> User agent properties are not reset by the [all](/en-US/docs/Web/CSS/all) property. +{{EmbedLiveSample("Using_the_fallback_value", "350px", "250px")}} ### Using env() to ensure content is not obscured by window control buttons in desktop PWAs -In the following example `env()` ensures that content displayed in a desktop Progressive Web App that uses the [Window Controls Overlay API](/en-US/docs/Web/API/Window_Controls_Overlay_API) is not obscured by the operating system's window control buttons. The `titlebar-area-*` values define a rectangle where the title bar would normally have been displayed. On devices that do not support the Window Controls Overlay feature, such as mobile devices, the fallback values are used. +In the following example, `env()` ensures that content displayed in a desktop Progressive Web App that uses the [Window Controls Overlay API](/en-US/docs/Web/API/Window_Controls_Overlay_API) is not obscured by the operating system's window control buttons. The `titlebar-area-*` values define a rectangle where the title bar would normally have been displayed. On devices that do not support the Window Controls Overlay feature, such as mobile devices, the fallback values are used. Here is what a PWA installed on a desktop device normally looks like: @@ -213,6 +225,10 @@ main { > [!NOTE] > Using `position:fixed` makes sure the header does not scroll with the rest of the content, and instead stays aligned with the window control buttons, even on device/browsers that support elastic overscroll (also known as rubber banding). +### Viewport segments + +The [Viewport segment API demo](https://mdn.github.io/dom-examples/viewport-segment-api/) and [Using the Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API/Using) guide provides a demonstration and explanation of using the `env()` function with the `viewport-segments-*` environment variables. + ## Specifications {{Specifications}} @@ -229,6 +245,7 @@ main { - [CSS custom properties for cascading variables](/en-US/docs/Web/CSS/CSS_cascading_variables) module - [Custom properties (`--*`): CSS variables](/en-US/docs/Web/CSS/--*) - [Using CSS custom properties (variables)](/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties) +- [Viewport Segments API](/en-US/docs/Web/API/Viewport_segments_API) - [Customize the window controls overlay of your PWA's title bar](https://web.dev/articles/window-controls-overlay) - [Display content in the title bar](https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps/how-to/window-controls-overlay) - [Breaking Out of the Box](https://alistapart.com/article/breaking-out-of-the-box/) diff --git a/files/jsondata/GroupData.json b/files/jsondata/GroupData.json index 0040b7b4ae576f8..5462bd5b2394dfd 100644 --- a/files/jsondata/GroupData.json +++ b/files/jsondata/GroupData.json @@ -1857,6 +1857,14 @@ "properties": [], "events": [] }, + "Viewport Segments API": { + "overview": ["Viewport Segments API"], + "guides": ["/docs/Web/API/Viewport_segments_API/Using"], + "interfaces": ["Viewport"], + "methods": ["Window.viewport"], + "properties": [], + "events": [] + }, "View Transition API": { "overview": ["View Transition API"], "guides": ["/docs/Web/API/View_Transition_API/Using"],