Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
96b9905
Add Soft Navigation docs
tunetheweb Aug 7, 2026
5c06fea
Fix linting
tunetheweb Aug 7, 2026
e977ec4
Update files/en-us/web/api/interactioncontentfulpaint/index.md
tunetheweb Aug 10, 2026
ddcd1dd
Apply suggestions from code review
tunetheweb Aug 10, 2026
06d69ad
Review feedback
tunetheweb Aug 10, 2026
f837fcd
Merge branch 'soft-navs' of github.com:tunetheweb/content into soft-navs
tunetheweb Aug 10, 2026
357b435
Update files/en-us/web/api/interactioncontentfulpaint/index.md
tunetheweb Aug 10, 2026
a9316b5
Update files/en-us/web/api/interactioncontentfulpaint/index.md
tunetheweb Aug 10, 2026
ddbe14c
Apply suggestions from code review
tunetheweb Aug 10, 2026
948273d
Apply suggestions from code review
tunetheweb Aug 10, 2026
47a0ca4
Apply suggestions from code review
tunetheweb Aug 10, 2026
173597a
Add contentful paint and update other entries
tunetheweb Aug 10, 2026
085f834
Update files/en-us/web/api/interactioncontentfulpaint/interactionid/i…
tunetheweb Aug 10, 2026
04ba3f1
Merge branch 'soft-navs' of github.com:tunetheweb/content into soft-navs
tunetheweb Aug 10, 2026
e6c68aa
Review feedback
tunetheweb Aug 10, 2026
391121e
Merge branch 'main' into soft-navs
tunetheweb Aug 10, 2026
54722e3
Remove brackets from property
tunetheweb Aug 10, 2026
948f9d6
tweaks
tunetheweb Aug 10, 2026
4d47b3f
Tweaks to glossary
tunetheweb Aug 10, 2026
db55b4b
More tweaks
tunetheweb Aug 10, 2026
6564baa
More tweaks
tunetheweb Aug 10, 2026
161467b
Tweaky, tweaky, tweaky
tunetheweb Aug 10, 2026
4c81831
More tweaks
tunetheweb Aug 10, 2026
591621e
Review feedback
tunetheweb Aug 10, 2026
a5efd19
Apply suggestions from code review
tunetheweb Aug 11, 2026
bb9c31d
Review feedback
tunetheweb Aug 11, 2026
3dbc817
Merge branch 'soft-navs' of github.com:tunetheweb/content into soft-navs
tunetheweb Aug 11, 2026
d090da6
Apply suggestion from @tunetheweb
tunetheweb Aug 11, 2026
9b4ada6
Update files/en-us/web/api/interactioncontentfulpaint/largestcontentf…
tunetheweb Aug 12, 2026
9332fb4
Update files/en-us/web/api/performancesoftnavigation/getlargestintera…
tunetheweb Aug 12, 2026
e157dc6
Merge branch 'main' into soft-navs
tunetheweb Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions files/en-us/glossary/contentful_paint/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Contentful paint
slug: Glossary/Contentful_paint
page-type: glossary-definition
sidebar: glossarysidebar
---

Several performance metrics such as {{Glossary("First_contentful_paint", "First Contentful Paint (FCP)")}} and {{Glossary("Largest_contentful_paint", "Largest Contentful Paint (LCP)")}} use the concept of a "contentful paint".

Contentful paints are paint operations that render significant content and are therefore significant for performance measurements. These stand apart from less important paints that contain no useful content for the user, such as background color paints. For example, a page that loads, sets the background color, but then shows no actual content for a long period afterwards is not as useful as one that shows text, images, or other content.

What exactly counts as "content" is heuristic-based to some extent and may not perfectly align with user perception or developer intent. This means paints drawn to screen in different ways may count as "contentful" or not, even if they appear the same to the user. For example, using a {{htmlelement("div")}} with a background color would not be contentful, while using an {{htmlelement("img")}} or {{htmlelement("svg")}} for that color may be considered contentful.

The types of paints considered contentful also differ slightly between metrics:

- FCP is intended to measure when the page starts to load. It therefore [includes most elements that paint](https://w3c.github.io/paint-timing/#contentful) including {{htmlelement("canvas")}} and `<svg>` elements. Skeleton screens using only `<div>` elements would not count as contentful.
- LCP is intended to measure when the page is mostly loaded and ready for the user, so it tries to ensure there is meaningful content before triggering. It has stricter criteria than FCP, only counting [timing-eligible](https://w3c.github.io/paint-timing/#timing-eligible) elements as contentful. It therefore doesn't include `<canvas>` or `<svg>` elements, since those are more difficult to measure in terms of when (or even if) they are representative of the largest content drawn on the page. Furthermore, LCP also excludes low-entropy images.
- Other metrics like {{domxref("InteractionContentfulPaint", "Interaction Contentful Paint")}}, {{domxref("PerformanceSoftNavigation", "soft navigations")}}, {{domxref("PerformanceElementTiming", "Element Timing")}}, and the upcoming [Container Timing](https://github.com/WICG/container-timing) are closer to the LCP model with some small differences like low-entropy images not being excluded.

## See also

- Related glossary terms:
- {{Glossary("First Contentful Paint")}}
- {{Glossary("Largest Contentful Paint")}}
- Performance APIs
- {{domxref("InteractionContentfulPaint")}}
- {{domxref("LargestContentfulPaint")}}
- {{domxref("PerformanceElementTiming")}}
- {{domxref("PerformancePaintTiming")}}
- {{domxref("PerformanceSoftNavigation")}}
7 changes: 4 additions & 3 deletions files/en-us/glossary/first_contentful_paint/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ page-type: glossary-definition
sidebar: glossarysidebar
---

**First Contentful Paint** (FCP) is when the browser renders the first content from the DOM, providing feedback to the user that the page is loading. Completing the first contentful paint answers the question "Is it happening?"
**First Contentful Paint** (FCP) is a measure of when the browser renders its first DOM content, providing user feedback that the page is loading. Completing the first {{Glossary("Contentful paint")}} answers the question "Is anything happening?"

_The First Contentful Paint_ timestamp is when the browser first rendered any text, image (including background images), video, canvas that had been drawn into, or non-empty SVG. This excludes any content of iframes, but includes text with pending webfonts. This is the first time users could start consuming page content.
_The First Contentful Paint_ timestamp indicates when the browser first rendered any text, image (including background images), video, canvas that had been drawn into, or non-empty SVG. This excludes any iframe content, but includes text with pending webfonts. This is the first time users could start consuming page content.

## See also

- [`PerformancePaintTiming`](/en-US/docs/Web/API/PerformancePaintTiming)
- Related glossary terms:
- {{Glossary("Contentful paint")}}
- {{Glossary("First Paint")}}
- {{Glossary("Largest Contentful Paint")}}
- {{Glossary("First Meaningful Paint")}}
- [First Contentful Paint](https://web.dev/articles/fcp) at web.dev
- [First Contentful Paint](https://web.dev/articles/fcp) on web.dev (2023)
3 changes: 2 additions & 1 deletion files/en-us/glossary/largest_contentful_paint/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar: glossarysidebar

The **Largest Contentful Paint** (LCP) performance metric provides the render time of the largest image or text block visible within the viewport, recorded from when the page first begins to load.

The following elements are considered when determining the LCP:
The following elements are considered {{Glossary("Contentful paint", "contentful")}} when determining the LCP:

- {{HTMLElement("img")}} elements.
- {{svgelement("image")}} elements inside an SVG.
Expand All @@ -19,5 +19,6 @@ The following elements are considered when determining the LCP:

- [`LargestContentfulPaint`](/en-US/docs/Web/API/LargestContentfulPaint)
- Related glossary terms:
- {{Glossary("Contentful Paint")}}
- {{Glossary("First Contentful Paint")}}
- {{Glossary("First Paint")}}
27 changes: 27 additions & 0 deletions files/en-us/glossary/soft_navigation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: Soft navigation
slug: Glossary/Soft_navigation
page-type: glossary-definition
sidebar: glossarysidebar
---

A soft navigation is a client-side routing navigation used by {{Glossary("SPA", "Single Page Applications (SPAs)")}} to load new content.

Typically, clicking a link on a webpage loads a completely new page (a "hard navigation"), which can result in considerable unnecessary work, and loss of state when navigating between similar pages on a site.

Modern JavaScript frameworks use client-side routing to intercept link clicks, update the content on the current page (perhaps after making [Fetch](/en-US/docs/Web/API/Fetch_API) calls), and then update the URL in the address bar while pushing a history state entry for the resulting navigation.

The net effect appears to be a navigation to the user (but hopefully faster than a hard navigation), which the browser will see as the same page.

The {{domxref("PerformanceSoftNavigation")}} and {{domxref("InteractionContentfulPaint")}} APIs allow measurement of soft navigations outside of a framework for performance metrics.

## See also

- [Single-page application](https://en.wikipedia.org/wiki/Single-page_application) (Wikipedia)
- [Understanding client-side JavaScript frameworks](/en-US/docs/Learn_web_development/Core/Frameworks_libraries)
- Related glossary terms:
- {{Glossary("SPA")}}
- Popular SPA frameworks:
- [React](https://react.dev/)
- [Angular](https://angular.dev/)
- [Vue.JS](https://vuejs.org/)
96 changes: 96 additions & 0 deletions files/en-us/web/api/interactioncontentfulpaint/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
title: InteractionContentfulPaint
slug: Web/API/InteractionContentfulPaint
page-type: web-api-interface
browser-compat: api.InteractionContentfulPaint
---

{{APIRef("Performance API")}}

The `InteractionContentfulPaint` interface provides timing information about {{Glossary("Contentful paint", "contentful paints")}} attributable to an interaction.

## Instance properties

This interface directly defines the following properties:

- {{domxref("InteractionContentfulPaint.interactionId")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : The id of the interaction that resulted in the paint.
- {{domxref("InteractionContentfulPaint.largestContentfulPaint")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns details of the largest {{domxref("LargestContentfulPaint")}} for the interaction. This can remain the same between two `InteractionContentfulPaint` entries for the same interaction if a new contentful paint is smaller than the current largest contentful paint for that interaction.
- {{domxref("InteractionContentfulPaint.navigationId")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : The id of the navigation this paint is attributable to.
- {{domxref("InteractionContentfulPaint.paintTime")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns the {{domxref("DOMHighResTimeStamp","timestamp")}} of when the first rendering phase ended and the paint phase started.
- {{domxref("InteractionContentfulPaint.presentationTime")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns the {{domxref("DOMHighResTimeStamp","timestamp")}} of when the first painted pixels were actually drawn on the screen.

It also extends the following {{domxref("PerformanceEntry")}} properties, qualifying and constraining them as described:

- {{domxref("PerformanceEntry.entryType")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns `"interaction-contentful-paint"`.
- {{domxref("PerformanceEntry.duration")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns the result of {{domxref("InteractionContentfulPaint.presentationTime")}} - {{domxref("PerformanceEntry.startTime")}}.
- {{domxref("PerformanceEntry.name")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Always returns an empty string.
- {{domxref("PerformanceEntry.startTime")}} {{ReadOnlyInline}} {{Experimental_Inline}}
- : Returns the {{domxref("DOMHighResTimeStamp","timestamp")}} of the interaction that resulted in the soft navigation.

## Description

The `InteractionContentfulPaint` provides a stream of paint updates attributable to an interaction.

At present this is scoped to increasing paint sizes, so it can be used to measure {{Glossary("Largest Contentful Paint", "Largest Contentful Paint (LCP)")}} for {{glossary("Soft Navigation", "Soft Navigations")}}, but the API has been designed to allow for all paints relevant to an interaction to be emitted.

`InteractionContentfulPaint` is needed instead of using the {{domxref("LargestContentfulPaint")}} API as that is only emitted per full page load and is finalized upon interaction (which is a necessary start to a soft navigation).

### Relationship with Event Timing and INP

The {{domxref("PerformanceEventTiming", "Event Timing API")}} API provides details about UIEvents — scheduling and processing durations, and total duration to next paint — but does not actually directly track the effects of those events, nor any future paints those effects might cause. It is intended to measure the responsiveness time during which a user receives no feedback, which should be kept to a minimum and forms the basis for metrics such as {{Glossary("Interaction to Next Paint", "Interaction to Next Paint (INP)")}}.

`InteractionContentfulPaint`, despite being similarly named to Interaction to Next Paint, serves a different purpose. `InteractionContentfulPaint` excludes non-contentful paints which do count for Event Timing and INP but also measures additional paints beyond the first paint. It allows enables measurement of a more complete understanding of the effects and content updates directly attributable to an interaction.

## Examples

### Observing interaction contentful paints

In the following example, a {{domxref("PerformanceObserver")}} is registered to get the soft navigations. The `buffered` flag is used to access data from before observer creation.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log("Interaction Contentful Paints:", entry.startTime, entry);
}
});
observer.observe({ type: "interaction-contentful-paints", buffered: true });
```

### Observing interaction contentful paints specific to a soft navigation

One of the key uses of the `InteractionContentfulPaint` interface is to measure all contentful paints related to a {{domxref("PerformanceSoftNavigation", "soft navigation")}} to calculate the {{Glossary("Largest Contentful Paint", "Largest Contentful Paint (LCP)")}} for that soft navigation.

To do this, it is recommended to use the {{domxref("PerformanceSoftNavigation.interactionId")}} rather than the {{domxref("PerformanceSoftNavigation.navigationId")}}, since some LCP candidates can happen before the soft navigation is defined (for paints, before the URL is updated) and will therefore have the old `navigationId`.

```js
let currentNavigationInteractionId = 1045; // hardcoded in this example

const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.InteractionId === currentNavigationInteractionId) {
console.log("Soft LCP candidate:", entry.startTime, entry);
}
}
});
observer.observe({ type: "interaction-contentful-paints", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- [Measuring soft navigations](https://developer.chrome.com/docs/web-platform/soft-navigations) on developer.chrome.com (2026)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: "InteractionContentfulPaint: interactionId property"
short-title: interactionId
slug: Web/API/InteractionContentfulPaint/interactionId
page-type: web-api-instance-property
browser-compat: api.InteractionContentfulPaint.interactionId
---

{{APIRef("Performance API")}}

The **`interactionId`** read-only property of the {{domxref("InteractionContentfulPaint")}} interface returns the {{domxref("PerformanceEventTiming.interactionId", "interactionId")}} of the {{domxref("PerformanceEventTiming")}} entry representing the interaction that resulted in the paint.

## Value

An integer matching the {{domxref("PerformanceEventTiming.interactionId", "interactionId")}} of the interaction that resulted in the paint.

## Examples

### Logging the `interactionId` of `InteractionContentfulPaint`

This example uses a {{domxref("PerformanceObserver")}} to log new `interaction-contentful-paint` performance entries as they are recorded in the browser's performance timeline. The `buffered` option is used to access entries from before the observer creation.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(
"Interaction Contentful Paint:",
entry.startTime,
entry.interactionId,
);
}
});
observer.observe({ type: "interaction-contentful-paint", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: "InteractionContentfulPaint: largestContentfulPaint property"
short-title: largestContentfulPaint
slug: Web/API/InteractionContentfulPaint/largestContentfulPaint
page-type: web-api-instance-property
browser-compat: api.InteractionContentfulPaint.largestContentfulPaint
---

{{APIRef("Performance API")}}

The **`largestContentfulPaint`** read-only property of the {{domxref("PerformanceSoftNavigation")}} interface returns the current largest {{domxref("LargestContentfulPaint")}} relevant to this soft navigation.

This can remain the same between two `InteractionContentfulPaint` entries for the same interaction if a new contentful paint is smaller than the current largest contentful paint for that interaction. When using this API to measure {{Glossary("Largest Contentful Paint", "Largest Contentful Paint (LCP)")}} for soft navigations you may only be interested in the largest paint.
Comment thread
tunetheweb marked this conversation as resolved.

Note that, in current implementations, `InteractionContentfulPaint` is scoped to increasing paint sizes, so it can be used to measure {{Glossary("Largest Contentful Paint", "Largest Contentful Paint (LCP)")}} for {{glossary("Soft Navigation", "Soft Navigations")}}; the `largestContentfulPaint` value should change each time. However, the API has been designed to allow for all paints relevant to an interaction to be emitted, so this is liable to change.

## Value

A {{domxref("LargestContentfulPaint")}} object representing the largest contentful paint (LCP) for this interaction contentful paint.

## Examples

### Logging the largest contentful paint of `InteractionContentfulPaint`

In this example, calling `entry.largestContentfulPaint` returns a {{domxref("LargestContentfulPaint")}} object.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(
"Interaction Contentful Paint:",
entry.startTime,
entry.largestContentfulPaint,
);
}
});
observer.observe({ type: "interaction-contentful-paint", buffered: true });
```

To get a JSON string, you can use [`JSON.stringify(entry)`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) directly; it will call `toJSON()` automatically.

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("LargestContentfulPaint")}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: "InteractionContentfulPaint: navigationId property"
short-title: navigationId
slug: Web/API/InteractionContentfulPaint/navigationId
page-type: web-api-instance-property
browser-compat: api.InteractionContentfulPaint.navigationId
---

{{APIRef("Performance API")}}

The **`navigationId`** read-only property of the {{domxref("InteractionContentfulPaint")}} interface returns the id of the navigation that this paint is happened under.

Note that, for {{glossary("Soft Navigation", "Soft Navigations")}}, paints happening before the URL is updated may wish to considered for the {{Glossary("Largest Contentful Paint", "Largest Contentful Paint (LCP)")}} of the soft navigation that is in flight. For the LCP case, {{domxref("PerformanceSoftNavigation.getLargestInteractionContentfulPaint()")}} and {{domxref("InteractionContentfulPaint.interactionId")}} are more effective for calculating that metric to consider all relevant paint regardless of the `navigationId`.

## Value

An integer matching either a {{domxref("PerformanceNavigationTiming")}} or a {{domxref("PerformanceSoftNavigation")}} entry.

## Examples

### Logging the `navigationId` of `InteractionContentfulPaint`

This example uses a {{domxref("PerformanceObserver")}} to log new `interaction-contentful-paint` performance entries as they are recorded in the browser's performance timeline. The `buffered` option is used to access entries from before the observer creation.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(
"Interaction Contentful Paint:",
entry.startTime,
entry.navigationId,
);
}
});
observer.observe({ type: "interaction-contentful-paint", buffered: true });
```

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: "InteractionContentfulPaint: paintTime property"
short-title: paintTime
slug: Web/API/InteractionContentfulPaint/paintTime
page-type: web-api-instance-property
browser-compat: api.InteractionContentfulPaint.paintTime
---

{{APIRef("Performance API")}}

The **`paintTime`** read-only property of the {{domxref("InteractionContentfulPaint")}} interface returns the {{domxref("DOMHighResTimeStamp","timestamp")}} of when the rendering phase ended and the paint phase started.

The `paintTime` is broadly interoperable: The value should be the same across different implementations.

## Value

A {{domxref("DOMHighResTimeStamp")}}.

## Examples

This example uses a {{domxref("PerformanceObserver")}} to log new `interaction-contentful-paint` performance entries as they are recorded in the browser's performance timeline. The `buffered` option is used to access entries from before the observer creation.

```js
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Interaction Contentful Paint:', entry.startTime, entry.paintTime);
}
});
observer.observe({ type: "interaction-contentful-paint", buffered: true });

## Specifications

Comment on lines +27 to +32

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, malformed Markdown in a few files (has been fixed)

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- {{domxref("LargestContentfulPaint.presentationTime")}}
```
Loading