Layout Instability API

Draft Community Group Report,

This version:
https://wicg.github.io/layout-instability
Issue Tracking:
GitHub
Editors:
Steve Kobes (Google)
Nicolás Peña Moreno (Google)
Emily Hanley (Google)

Abstract

This document defines an API that provides web page authors with insights into the stability of their pages based on movements of the elements on the page.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

This section is non-normative.

The shifting of DOM elements on a webpage detracts from the user’s experience, and occurs frequently on the web today. This shifting is often due to content loading asynchronously and displacing other elements on the page.

The layout Instability API identifies these unstable pages by reporting a value (the "layout shift") for each animation frame in the user’s session. This specification presents a method for a user agent to compute the layout shift value.

The layout shift value is expected to have a general correspondence to the severity of layout instability at a particular time. The method of computing it considers both the area of the region impacted by instability and the distance by which elements on the page are shifted.

The developer can use the layout shift values that are reported by this API to compute a cumulative score (the "cumulative layout shift score").

The cumulative layout shift score is expected to have a general correspondence to the severity of layout instability for the lifetime of a page.

1.1. End of session signal

This section is non-normative.

The developer can compute a cumulative layout shift score by summing the layout shift values as they are reported to the observer.

A "final" score for the user’s session can be reported by listening to the visibilitychange event, and taking the value of the cumulative layout shift score at that time.

This strategy is illustrated in the usage example.

1.2. Usage example

This section is non-normative.

// Stores the current layout shift score for the page.
let cumulativeLayoutShiftScore = 0;

// Detects new layout shift occurrences and updates the
// `cumulativeLayoutShiftScore` variable.
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // Only count layout shifts without recent user input.
    if (!entry.hadRecentInput) {
      cumulativeLayoutShiftScore += entry.value;
    }
  }
});

observer.observe({type: 'layout-shift', buffered: true});

// Sends the final score to your analytics back end once
// the page’s lifecycle state becomes hidden.
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    // Force any pending records to be dispatched.
    observer.takeRecords();

    // Send the final score to your analytics back end
    // (assumes `sendToAnalytics` is defined elsewhere).
    sendToAnalytics({cumulativeLayoutShiftScore});
  }
});

The layout shift score is only one signal, which correlates in an approximate manner with the user experience of "jumpiness".

Developers should avoid relying on the precise value of the layout shift score, as the metric might evolve over time, and user agents might compromise precision in the interest of calculation efficiency.

2. Terminology

2.1. Basic Concepts

The starting point of a Node N in a coordinate space C is defined as follows:

The visual representation of a Node N is defined as follows:

A condition holds in the previous frame if it was true at the point in time immediately after the most recently completed invocation of the report the layout shift algorithm.

The previous frame starting point of a Node N in a coordinate space C is the point which, in the previous frame, was the starting point of N in C.

The previous frame visual representation of a Node N is the set which, in the previous frame, was the visual representation of N.

2.2. Unstable Nodes

A Node N has shifted in a coordinate space C if the starting point of N in C differs from the previous frame starting point of N in C by 3 or more pixel units in either the horizontal or vertical direction.

Otherwise, N has not shifted in C.

A Node N is unstable if:

The unstable node set of a Document D is the set containing every unstable shadow-including descendant of D.

NOTE: In the first frame, the previous frame starting point does not exist for any node, and therefore the unstable node set is empty.

2.3. Layout Shift Value

The viewport base distance is the greater of the visual viewport width or the visual viewport height.

The move vector of a Node N is the two-dimensional offset in pixel units from

The move distance of a Node N is the greater of

The maximum move distance of a Document D is the greatest move distance of any Node in the unstable node set of D, or 0 if the unstable node set of D is empty.

The distance fraction of a Document D is the lesser of

The impact region of a Document D is the set containing

The impact fraction of a Document D is the area of the impact region divided by the area of the viewport.

The layout shift value of a Document D is the impact fraction of D multiplied by the distance fraction of D.

NOTE: The layout shift value takes into account both the fraction of the viewport that has been impacted by layout instability as well as the greatest distance by which any given element has moved. This recognizes that a large element which moves only a small distance can have a low impact on the perceived instability of the page.

2.4. Input Exclusion

An excluding input is any event from an input device which signals a user’s active interaction with the document, or any event which directly changes the size of the viewport.

Excluding inputs generally include mousedown, keydown, and pointerdown. However, an event whose only effect is to begin or update a fling or scroll gesture is not an excluding input.

The user agent may delay the reporting of layout shifts after a pointerdown event until such time as it is known that the event does not begin a fling or scroll gesture.

The mousemove and pointermove events are also not excluding inputs.

3. LayoutShift interface

[Exposed=Window]
interface LayoutShift : PerformanceEntry {
  readonly attribute long value;
  readonly attribute boolean hadRecentInput;
  readonly attribute DOMHighResTimeStamp lastInputTime;
  [Default] object toJSON();
};

All attributes have the values which are assigned to them by the steps to report the layout shift.

A user agent implementing the Layout Instability API MUST include "layout-shift" in supportedEntryTypes for Window contexts. This allows developers to detect support for the Layout Instability API.

4. Processing model

Within the update the rendering step of the event loop processing model, a user agent implementing the Layout Instability API MUST perform the following step after the step that invokes the mark paint timing algorithm:

  1. For each fully active Document in docs, invoke the algorithm to report the layout shift for that Document.

4.1. Report the layout shift

When asked to report the layout shift for an active Document D, run the following steps:
  1. If the current layout shift value of D is not 0:

    1. Create a new LayoutShift object newEntry.

    2. Set newEntry’s name attribute to "layout-shift".

    3. Set newEntry’s entryType attribute to "layout-shift".

    4. Set newEntry’s startTime attribute to current high resolution time.

    5. Set newEntry’s duration attribute to 0.

    6. Set newEntry’s value attribute to the current layout shift value of D.

    7. Set newEntry’s lastInputTime attribute to the time of the most recent excluding input, or 0 if no excluding input has occurred during the browsing session.

    8. Set newEntry’s hadRecentInput attribute to true if lastInputTime is less than 500 milliseconds in the past, and false otherwise.

    9. Queue the PerformanceEntry newEntry object.

5. Security & privacy considerations

Layout instability bears an indirect relationship to resource timing, as slow resources could cause intermediate layouts that would not otherwise be performed. Resource timing information can be used by malicious websites for statistical fingerprinting. The layout instability API only reports instability in the current browsing context. It does not directly provide any aggregation of instability scores across multiple browsing contexts. Developers can implement such aggregation manually, but browsing contexts with different origins would need to cooperate to share instability scores.

Conformance

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.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-BREAK-4]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 4. 18 December 2018. WD. URL: https://www.w3.org/TR/css-break-4/
[CSS-DISPLAY-3]
Tab Atkins Jr.; Elika Etemad. CSS Display Module Level 3. 11 July 2019. CR. URL: https://www.w3.org/TR/css-display-3/
[CSS-OVERFLOW-3]
David Baron; Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 31 July 2018. WD. URL: https://www.w3.org/TR/css-overflow-3/
[CSS-TEXT-3]
Elika Etemad; Koji Ishii; Florian Rivoal. CSS Text Module Level 3. 12 December 2018. WD. URL: https://www.w3.org/TR/css-text-3/
[CSS-TRANSFORMS-1]
Simon Fraser; et al. CSS Transforms Module Level 1. 14 February 2019. CR. URL: https://www.w3.org/TR/css-transforms-1/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 31 January 2019. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. 30 July 2019. CR. URL: https://www.w3.org/TR/css-writing-modes-4/
[CSS2]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 7 June 2011. REC. URL: https://www.w3.org/TR/CSS2/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM View Module. 17 March 2016. WD. URL: https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[HR-TIME-2]
Ilya Grigorik; James Simonsen; Jatinder Mann. High Resolution Time Level 2. 26 June 2019. CR. URL: https://www.w3.org/TR/hr-time-2/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[PAINT-TIMING]
Shubhie Panicker. Paint Timing 1. 7 September 2017. WD. URL: https://www.w3.org/TR/paint-timing/
[PERFORMANCE-TIMELINE-2]
Ilya Grigorik. Performance Timeline Level 2. 17 September 2019. WD. URL: https://www.w3.org/TR/performance-timeline-2/
[RESOURCE-TIMING-1]
Arvind Jain; et al. Resource Timing Level 1. 30 March 2017. CR. URL: https://www.w3.org/TR/resource-timing-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[VISUAL-VIEWPORT]
Visual Viewport API. cg-draft. URL: https://wicg.github.io/visual-viewport/
[WebIDL]
Boris Zbarsky. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

IDL Index

[Exposed=Window]
interface LayoutShift : PerformanceEntry {
  readonly attribute long value;
  readonly attribute boolean hadRecentInput;
  readonly attribute DOMHighResTimeStamp lastInputTime;
  [Default] object toJSON();
};