From 34f003a928983a6175e1b0e8a4a3aaa58c6ad776 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 28 Jul 2025 16:55:49 +0200 Subject: [PATCH 01/14] Add Baseline regression notes --- features/content-visibility.yml | 14 ++++++++------ features/font-variant-position.yml | 15 +++++++++------ features/link-rel-dns-prefetch.yml | 14 ++++++++------ features/popover.yml | 15 +++++++++------ features/streams.yml | 6 ------ 5 files changed, 34 insertions(+), 30 deletions(-) diff --git a/features/content-visibility.yml b/features/content-visibility.yml index 34da9be944f..b479a9d9a97 100644 --- a/features/content-visibility.yml +++ b/features/content-visibility.yml @@ -3,12 +3,14 @@ description: The `content-visibility` CSS property delays rendering an element, spec: https://drafts.csswg.org/css-contain-2/#content-visibility group: css caniuse: css-content-visibility -# TODO: https://github.com/web-platform-dx/web-features/issues/1971 -# Status changed: https://github.com/web-platform-dx/web-features/pull/2591 -# 2025-01-30 — low → false — Safari hides text behind `content-visibility: auto` from "Find…" in the page. -# References: -# - https://github.com/mdn/browser-compat-data/pull/25781 -# - https://bugs.webkit.org/show_bug.cgi?id=283846 +notes: + - category: baseline-regression + date: 2025-01-30 + message: > + The `content-visibility` feature regressed from newly available to limited availability. + Upstream data reported that Safari prevented text in `content-visibility: auto` elements from being found with browser's "Find…" user interface. + citations: + - https://bugs.webkit.org/show_bug.cgi?id=283846 compat_features: - api.ContentVisibilityAutoStateChangeEvent - api.ContentVisibilityAutoStateChangeEvent.ContentVisibilityAutoStateChangeEvent diff --git a/features/font-variant-position.yml b/features/font-variant-position.yml index 7bfa1284d0e..990abc1a065 100644 --- a/features/font-variant-position.yml +++ b/features/font-variant-position.yml @@ -2,9 +2,12 @@ name: font-variant-position description: The `font-variant-position` CSS property sets whether to use alternate glyphs for subscript and superscript text. spec: https://drafts.csswg.org/css-fonts-4/#font-variant-position-prop group: font-features -# TODO: https://github.com/web-platform-dx/web-features/issues/1971 -# Status changed: https://github.com/web-platform-dx/web-features/pull/1958 -# 2024-10-15 — low → false — Chrome, Edge, and Safari do not implement font synthesis for missing superscript or subscript glyphs. -# References: -# - https://issues.chromium.org/issues/352218916 -# - https://bugs.webkit.org/show_bug.cgi?id=151471 +notes: + - date: 2024-10-15 + category: baseline-regression + message: > + The `font-variant-position` feature regressed from newly available to limited availability. + Upstream data reported that Chrome, Edge, and Safari did not implement font synthesis for missing superscript or subscript glyphs. + citations: + - https://issues.chromium.org/issues/352218916 + - https://bugs.webkit.org/show_bug.cgi?id=151471 diff --git a/features/link-rel-dns-prefetch.yml b/features/link-rel-dns-prefetch.yml index 6363055406c..0a5a6dc8442 100644 --- a/features/link-rel-dns-prefetch.yml +++ b/features/link-rel-dns-prefetch.yml @@ -2,11 +2,13 @@ name: '' description: The `rel="dns-prefetch"` attribute for the `` HTML element is a hint to the browser that the page or user is likely to request resources from another domain, so the browser should preemptively resolve DNS for the `href` value's domain. spec: https://html.spec.whatwg.org/multipage/links.html#link-type-dns-prefetch caniuse: link-rel-dns-prefetch +notes: + - date: 2025-06-23 + category: baseline-regression + message: > + The `` feature regressed from newly available to limited availability. + Upstream data corrected an earlier report that Safari on iOS supported this feature when it did not. + citations: + - https://github.com/mdn/browser-compat-data/pull/27057 compat_features: - html.elements.link.rel.dns-prefetch -# TODO: https://github.com/web-platform-dx/web-features/issues/1971 -# Status changed: https://github.com/web-platform-dx/web-features/pull/3074/ -# 2025-06-23 — low → false — On iOS, it was erroneously reported that this feature was supported. -# References: -# - https://developer.apple.com/documentation/safari-release-notes/safari-26-release-notes#Networking -# - https://github.com/mdn/browser-compat-data/pull/27057 diff --git a/features/popover.yml b/features/popover.yml index 41e8afbb4a6..80ab607e3aa 100644 --- a/features/popover.yml +++ b/features/popover.yml @@ -2,12 +2,15 @@ name: Popover description: The `popover` HTML attribute creates an overlay to display content on top of other page content. Popovers can be shown declaratively using HTML, or using the `showPopover()` method. spec: https://html.spec.whatwg.org/multipage/popover.html group: html -# TODO: https://github.com/web-platform-dx/web-features/issues/1971 -# Status changed: https://github.com/web-platform-dx/web-features/pull/1797 -# 2024-09-18 — low → false — Safari on iOS has a bug that prevents dismissing popovers by touch. -# References: -# - https://github.com/mdn/browser-compat-data/issues/22927 -# - https://bugs.webkit.org/show_bug.cgi?id=267688 +notes: + - date: 2024-09-18 + category: baseline-regression + message: > + The popover feature regressed from newly available to limited availability. + Upstream data reported that Safari on iOS had a bug that prevented light dismiss (tapping outside the element to close it). + citations: + - https://bugs.webkit.org/show_bug.cgi?id=267688 + - https://github.com/mdn/browser-compat-data/issues/22927 status: compute_from: - api.HTMLElement.popover diff --git a/features/streams.yml b/features/streams.yml index a1a70e31c20..bd6be3b72ea 100644 --- a/features/streams.yml +++ b/features/streams.yml @@ -2,12 +2,6 @@ name: Streams description: The streams API creates, composes, and consumes continuously generated data. spec: https://streams.spec.whatwg.org/ group: streams -# TODO: https://github.com/web-platform-dx/web-features/issues/1971 -# Status changed: https://github.com/web-platform-dx/web-features/pull/2358, https://github.com/web-platform-dx/web-features/pull/2491 -# 2024-12-19 — low → false — Regressed status to match Caniuse, which considers support beginning at BYOB shipping. -# 2025-01-30 — false → high — Split BYOB into a separate "readable-byte-streams" feature. Linked that one to Caniuse. -# References: -# - https://caniuse.com/streams status: compute_from: - api.ReadableStream From fccb850f91de192b09f8f39cbba87242a8b9f4a5 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 11 Aug 2025 17:19:16 +0200 Subject: [PATCH 02/14] Structure the Baseline value change --- features/content-visibility.yml | 3 ++- features/font-variant-position.yml | 3 ++- features/link-rel-dns-prefetch.yml | 3 ++- features/popover.yml | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/features/content-visibility.yml b/features/content-visibility.yml index b479a9d9a97..8701ef786f2 100644 --- a/features/content-visibility.yml +++ b/features/content-visibility.yml @@ -6,8 +6,9 @@ caniuse: css-content-visibility notes: - category: baseline-regression date: 2025-01-30 + old_baseline_value: low + new_baseline_value: false message: > - The `content-visibility` feature regressed from newly available to limited availability. Upstream data reported that Safari prevented text in `content-visibility: auto` elements from being found with browser's "Find…" user interface. citations: - https://bugs.webkit.org/show_bug.cgi?id=283846 diff --git a/features/font-variant-position.yml b/features/font-variant-position.yml index 990abc1a065..f1355665bed 100644 --- a/features/font-variant-position.yml +++ b/features/font-variant-position.yml @@ -5,8 +5,9 @@ group: font-features notes: - date: 2024-10-15 category: baseline-regression + old_baseline_value: low + new_baseline_value: false message: > - The `font-variant-position` feature regressed from newly available to limited availability. Upstream data reported that Chrome, Edge, and Safari did not implement font synthesis for missing superscript or subscript glyphs. citations: - https://issues.chromium.org/issues/352218916 diff --git a/features/link-rel-dns-prefetch.yml b/features/link-rel-dns-prefetch.yml index 0a5a6dc8442..f2ebc18b8d2 100644 --- a/features/link-rel-dns-prefetch.yml +++ b/features/link-rel-dns-prefetch.yml @@ -5,8 +5,9 @@ caniuse: link-rel-dns-prefetch notes: - date: 2025-06-23 category: baseline-regression + old_baseline_value: low + new_baseline_value: false message: > - The `` feature regressed from newly available to limited availability. Upstream data corrected an earlier report that Safari on iOS supported this feature when it did not. citations: - https://github.com/mdn/browser-compat-data/pull/27057 diff --git a/features/popover.yml b/features/popover.yml index 80ab607e3aa..198c3b2f4e6 100644 --- a/features/popover.yml +++ b/features/popover.yml @@ -5,8 +5,9 @@ group: html notes: - date: 2024-09-18 category: baseline-regression + old_baseline_value: low + new_baseline_value: false message: > - The popover feature regressed from newly available to limited availability. Upstream data reported that Safari on iOS had a bug that prevented light dismiss (tapping outside the element to close it). citations: - https://bugs.webkit.org/show_bug.cgi?id=267688 From 241e4e578efce899b44ccf09717b6723fa0d589c Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 11 Aug 2025 17:19:45 +0200 Subject: [PATCH 03/14] Add regression note for `createimagebitmap` --- features/createimagebitmap.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/features/createimagebitmap.yml b/features/createimagebitmap.yml index bd56d7cc6c2..da08b41d264 100644 --- a/features/createimagebitmap.yml +++ b/features/createimagebitmap.yml @@ -2,6 +2,15 @@ name: createImageBitmap description: The `createImageBitmap()` global method creates an `ImageBitmap` object from a source such as an image, SVG, blob, or canvas. An `ImageBitmap` object represents pixel data that can be drawn to a canvas with lower latency than other types, such as `ImageData`. spec: https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#imagebitmap caniuse: createimagebitmap +notes: + - date: 2025-08-11 + category: baseline-regression + old_baseline_value: high + new_baseline_value: low + message: > + This feature's status was recalculated to be more consistent with caniuse. + citations: + - https://github.com/web-platform-dx/web-features/pull/3173 status: compute_from: - api.createImageBitmap From a96e279ab4b8f4e4de4cb42721a1ebf04fe9bcb0 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Sat, 30 Aug 2025 20:15:10 +0200 Subject: [PATCH 04/14] Add Baseline regression notes to the schema --- features/popover.yml | 20 ++++++------- index.ts | 16 ++++++++++ schemas/data.schema.json | 63 ++++++++++++++++++++++++++++++++++++++++ types.ts | 23 +++++++++++++++ 4 files changed, 112 insertions(+), 10 deletions(-) diff --git a/features/popover.yml b/features/popover.yml index 198c3b2f4e6..009151ecf74 100644 --- a/features/popover.yml +++ b/features/popover.yml @@ -2,16 +2,16 @@ name: Popover description: The `popover` HTML attribute creates an overlay to display content on top of other page content. Popovers can be shown declaratively using HTML, or using the `showPopover()` method. spec: https://html.spec.whatwg.org/multipage/popover.html group: html -notes: - - date: 2024-09-18 - category: baseline-regression - old_baseline_value: low - new_baseline_value: false - message: > - Upstream data reported that Safari on iOS had a bug that prevented light dismiss (tapping outside the element to close it). - citations: - - https://bugs.webkit.org/show_bug.cgi?id=267688 - - https://github.com/mdn/browser-compat-data/issues/22927 +# notes: +# - date: 2024-09-18 +# category: baseline-regression +# old_baseline_value: low +# new_baseline_value: false +# message: > +# Upstream data reported that Safari on iOS had a bug that prevented light dismiss (tapping outside the element to close it). +# citations: +# - https://bugs.webkit.org/show_bug.cgi?id=267688 +# - https://github.com/mdn/browser-compat-data/issues/22927 status: compute_from: - api.HTMLElement.popover diff --git a/index.ts b/index.ts index f9193492782..f0896f54b63 100644 --- a/index.ts +++ b/index.ts @@ -134,6 +134,13 @@ for (const [key, data] of yamlEntries('features')) { data.description = text; data.description_html = html; } + if (Array.isArray(data.notes)) { + for (const note of data.notes as FeatureData["notes"]) { + const { text, html } = convertMarkdown(data.description); + note.message = text; + note.message_html = html; + } + } // Compute Baseline high date from low date. if (data.status?.baseline === 'high') { @@ -163,6 +170,15 @@ for (const [key, data] of yamlEntries('features')) { } } + // Ensure regression notes are still relevant + if (Array.isArray(data.notes)) { + for (const [index, note] of (data.notes as FeatureData["notes"]).entries()) { + if (note.new_baseline_value !== data.status.baseline) { + throw new Error(`regression note ${index} on ${key}.yml no longer applies (status is ${data.status.baseline}, note is ${note.new_baseline_value}). Delete this note.`); + } + } + } + if (data.compat_features) { // Sort compat_features so that grouping and ordering in dist files has // no effect on what web-features users see. diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 2d34661eed7..b6090cd7e4e 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -100,6 +100,69 @@ "description": "Short name", "type": "string" }, + "notes": { + "description": "Notes about this feature", + "items": { + "additionalProperties": false, + "description": "A note describing a Baseline status regression. For example, a feature that has moved from Baseline low to not Baseline.", + "properties": { + "category": { + "const": "baseline-regression", + "description": "The topic of this note. This field is also a discriminator for any future note types", + "type": "string" + }, + "citations": { + "description": "One or more URLs, such as bugs, used to justify the regression", + "items": { + "type": "string" + }, + "type": "array" + }, + "date": { + "description": "The date that the regression was added to web-features data", + "type": "string" + }, + "message": { + "description": "A short description of the cause of the regression as a plain text", + "type": "string" + }, + "message_html": { + "description": "A short description of the cause of the regression as HTML", + "type": "string" + }, + "new_baseline_value": { + "description": "The `baseline` status value after the regression", + "enum": [ + "low", + false + ], + "type": [ + "string", + "boolean" + ] + }, + "old_baseline_value": { + "description": "The `baseline` status value before the regression", + "enum": [ + "high", + "low" + ], + "type": "string" + } + }, + "required": [ + "category", + "date", + "message", + "message_html", + "citations", + "old_baseline_value", + "new_baseline_value" + ], + "type": "object" + }, + "type": "array" + }, "snapshot": { "anyOf": [ { diff --git a/types.ts b/types.ts index d1492262104..95b94b60a59 100644 --- a/types.ts +++ b/types.ts @@ -47,6 +47,8 @@ export interface FeatureData { compat_features?: string[]; /** Whether developers are formally discouraged from using this feature */ discouraged?: Discouraged; + /** Notes about this feature */ + notes?: BaselineRegressionNote[]; } type BrowserIdentifier = "chrome" | "chrome_android" | "edge" | "firefox" | "firefox_android" | "safari" | "safari_ios"; @@ -82,6 +84,27 @@ interface Discouraged { // https://github.com/web-platform-dx/web-features/issues/2722 is resolved. } +/** + * A note describing a Baseline status regression. + * For example, a feature that has moved from Baseline low to not Baseline. + */ +interface BaselineRegressionNote { + /** The topic of this note. This field is also a discriminator for any future note types */ + category: "baseline-regression"; + /** The date that the regression was added to web-features data */ + date: string; + /** A short description of the cause of the regression as a plain text */ + message: string; + /** A short description of the cause of the regression as HTML */ + message_html: string; + /** One or more URLs, such as bugs, used to justify the regression */ + citations: string[]; + /** The `baseline` status value before the regression */ + old_baseline_value: "high" | "low"; + /** The `baseline` status value after the regression */ + new_baseline_value: "low" | false; +} + export interface GroupData { /** Short name */ name: string; From f6324603d9a74284518fc6c6ee53615a3f719096 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Sat, 30 Aug 2025 20:15:24 +0200 Subject: [PATCH 05/14] Add Baseline regression note data guidelines --- docs/guidelines.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/guidelines.md b/docs/guidelines.md index 181feb12705..d71e8fdfc15 100644 --- a/docs/guidelines.md +++ b/docs/guidelines.md @@ -348,3 +348,20 @@ When you set a `discouraged` block in a feature file, do: - Set one or more (optional) `alternative` feature IDs that are whole or partial substitutes for the discouraged feature. An alternative doesn't have to be a narrow drop-in replacement for the discouraged feature but it must handle some use case of the discouraged feature. Guide developers to the most relevant features that would help them stop using the discouraged feature. + +## Notes + +Features may have notes. +Presently, there is one type of note, a Baseline regression note. + +### Baseline regression note + +Use a note with a `category: baseline-regression` whenever the Baseline status goes backwards (such as from `"high"` to `"low"`). +This note type applies to any regression, whether it was caused by changes in upstream data or an editorial override. + +In the `message` field, explain the cause of the regression. +If the cause is a newly-discovered or reported bug, then briefly describe the nature of the bug. +If the cause is a correction, then briefly describe the nature and origin of the correction (usually, upstream data). + +In the `citations` field, include the URLs that are most important to understanding the nature and origin of the change. +For example, if BCD marked a feature as a `partial_implementation` due to a browser bug, include the URL for the browser bug and the BCD pull request or issue where the `partial_implementation` status was agreed. From e877da5f61f3b7dec59a8103f39ff860e3c5b241 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Sat, 30 Aug 2025 20:24:01 +0200 Subject: [PATCH 06/14] Revise regression notes to follow the new guidelines --- docs/guidelines.md | 5 +++-- features/content-visibility.yml | 2 +- features/createimagebitmap.yml | 2 +- features/font-variant-position.yml | 2 +- features/link-rel-dns-prefetch.yml | 2 +- features/popover.yml | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/guidelines.md b/docs/guidelines.md index d71e8fdfc15..c504c1f3386 100644 --- a/docs/guidelines.md +++ b/docs/guidelines.md @@ -360,8 +360,9 @@ Use a note with a `category: baseline-regression` whenever the Baseline status g This note type applies to any regression, whether it was caused by changes in upstream data or an editorial override. In the `message` field, explain the cause of the regression. -If the cause is a newly-discovered or reported bug, then briefly describe the nature of the bug. -If the cause is a correction, then briefly describe the nature and origin of the correction (usually, upstream data). +Write the message to developers, to help them understand whether the regression applies to their use case. +If the cause is a newly-discovered or reported bug then briefly describe the nature of the bug. +If the cause is a correction then briefly describe the nature and origin of the correction (usually, upstream data). In the `citations` field, include the URLs that are most important to understanding the nature and origin of the change. For example, if BCD marked a feature as a `partial_implementation` due to a browser bug, include the URL for the browser bug and the BCD pull request or issue where the `partial_implementation` status was agreed. diff --git a/features/content-visibility.yml b/features/content-visibility.yml index 8701ef786f2..d2ddae99bd2 100644 --- a/features/content-visibility.yml +++ b/features/content-visibility.yml @@ -9,7 +9,7 @@ notes: old_baseline_value: low new_baseline_value: false message: > - Upstream data reported that Safari prevented text in `content-visibility: auto` elements from being found with browser's "Find…" user interface. + Safari prevents text in `content-visibility: auto` elements from being found with browser's "Find…" user interface. citations: - https://bugs.webkit.org/show_bug.cgi?id=283846 compat_features: diff --git a/features/createimagebitmap.yml b/features/createimagebitmap.yml index da08b41d264..37e0d141fe8 100644 --- a/features/createimagebitmap.yml +++ b/features/createimagebitmap.yml @@ -8,7 +8,7 @@ notes: old_baseline_value: high new_baseline_value: low message: > - This feature's status was recalculated to be more consistent with caniuse. + This feature's status was recalculated to be more consistent with caniuse's criteria for full support, requiring `SVGImageElement` as a supported image source. citations: - https://github.com/web-platform-dx/web-features/pull/3173 status: diff --git a/features/font-variant-position.yml b/features/font-variant-position.yml index f1355665bed..63448778d3f 100644 --- a/features/font-variant-position.yml +++ b/features/font-variant-position.yml @@ -8,7 +8,7 @@ notes: old_baseline_value: low new_baseline_value: false message: > - Upstream data reported that Chrome, Edge, and Safari did not implement font synthesis for missing superscript or subscript glyphs. + Chrome, Edge, and Safari do not implement font synthesis for missing superscript or subscript glyphs. citations: - https://issues.chromium.org/issues/352218916 - https://bugs.webkit.org/show_bug.cgi?id=151471 diff --git a/features/link-rel-dns-prefetch.yml b/features/link-rel-dns-prefetch.yml index f2ebc18b8d2..3e1b0ea3c59 100644 --- a/features/link-rel-dns-prefetch.yml +++ b/features/link-rel-dns-prefetch.yml @@ -8,7 +8,7 @@ notes: old_baseline_value: low new_baseline_value: false message: > - Upstream data corrected an earlier report that Safari on iOS supported this feature when it did not. + Safari on iOS does not support this feature. Upstream data corrected an earlier report that it did. citations: - https://github.com/mdn/browser-compat-data/pull/27057 compat_features: diff --git a/features/popover.yml b/features/popover.yml index 009151ecf74..e253180f9f4 100644 --- a/features/popover.yml +++ b/features/popover.yml @@ -8,7 +8,7 @@ group: html # old_baseline_value: low # new_baseline_value: false # message: > -# Upstream data reported that Safari on iOS had a bug that prevented light dismiss (tapping outside the element to close it). +# Safari on iOS has a bug that prevents light dismiss (tapping outside the element to close it). # citations: # - https://bugs.webkit.org/show_bug.cgi?id=267688 # - https://github.com/mdn/browser-compat-data/issues/22927 From 55513e5b224b2e85999bf7b31544f687fa309030 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Wed, 26 Nov 2025 18:50:00 +0100 Subject: [PATCH 07/14] Fix description duplication from @rviscomi Co-authored-by: Rick Viscomi --- index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 3faef18c69e..9cfa0de7ac2 100644 --- a/index.ts +++ b/index.ts @@ -167,7 +167,7 @@ for (const [key, data] of yamlEntries('features')) { } if (Array.isArray(data.notes)) { for (const note of data.notes as FeatureData["notes"]) { - const { text, html } = convertMarkdown(data.description); + const { text, html } = convertMarkdown(note.message); note.message = text; note.message_html = html; } From 778c3dcdf982b9ed43f6b027d3ecf0a43c1363bf Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Wed, 3 Dec 2025 17:39:05 +0100 Subject: [PATCH 08/14] Consolidate Baseline values into one subschema This turns `"high" | "low" | false` into a named definition that's reused in a couple places. --- schemas/data.schema.json | 14 ++++++++------ types.quicktype.ts | 19 +++++++++---------- types.ts | 14 +++++++++----- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 5551be02503..2b3b7f3982d 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -76,6 +76,10 @@ "required": ["browsers", "features", "groups", "snapshots"], "additionalProperties": false, "definitions": { + "BaselineValue": { + "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", + "enum": ["high", "low", false] + }, "Discouraged": { "type": "object", "properties": { @@ -168,13 +172,11 @@ }, "new_baseline_value": { "description": "The `baseline` status value after the regression", - "enum": ["low", false], - "type": ["string", "boolean"] + "$ref": "#/definitions/BaselineValue" }, "old_baseline_value": { "description": "The `baseline` status value before the regression", - "enum": ["high", "low"], - "type": "string" + "$ref": "#/definitions/BaselineValue" } }, "required": [ @@ -301,8 +303,8 @@ "type": "object", "properties": { "baseline": { - "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", - "enum": ["high", "low", false] + "description": "Whether the feature is not Baseline, Baseline newly available, or Baseline widely available", + "$ref": "#/definitions/BaselineValue" }, "baseline_high_date": { "description": "Date the feature achieved Baseline high status", diff --git a/types.quicktype.ts b/types.quicktype.ts index d5082f21f40..cca9ce6eb0f 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -167,17 +167,14 @@ export interface Note { /** * The `baseline` status value after the regression */ - new_baseline_value: boolean | "low"; + new_baseline_value: boolean | BaselineValueEnum; /** * The `baseline` status value before the regression */ - old_baseline_value: OldBaselineValue; + old_baseline_value: boolean | BaselineValueEnum; } -/** - * The `baseline` status value before the regression - */ -export type OldBaselineValue = "high" | "low"; +export type BaselineValueEnum = "high" | "low"; /** * Whether a feature is considered a "Baseline" web platform feature and when it achieved @@ -185,9 +182,10 @@ export type OldBaselineValue = "high" | "low"; */ export interface StatusHeadline { /** - * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) + * Whether the feature is not Baseline, Baseline newly available, or Baseline widely + * available */ - baseline: boolean | OldBaselineValue; + baseline: boolean | BaselineValueEnum; /** * Date the feature achieved Baseline high status */ @@ -208,9 +206,10 @@ export interface StatusHeadline { export interface Status { /** - * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) + * Whether the feature is not Baseline, Baseline newly available, or Baseline widely + * available */ - baseline: boolean | OldBaselineValue; + baseline: boolean | BaselineValueEnum; /** * Date the feature achieved Baseline high status */ diff --git a/types.ts b/types.ts index 844acf95dba..303104724e8 100644 --- a/types.ts +++ b/types.ts @@ -5,13 +5,13 @@ // nicer to work with in TypeScript. import type { - OldBaselineValue as BaselineHighLow, BrowserData, Browsers, Discouraged, GroupData, Kind, FeatureData as QuicktypeMonolithicFeatureData, + Note as QuicktypeNote, Status as QuicktypeStatus, StatusHeadline as QuicktypeStatusHeadline, WebFeaturesData as QuicktypeWebFeaturesData, @@ -22,7 +22,6 @@ import type { // Passthrough types export type { - BaselineHighLow, BrowserData, Browsers, Discouraged, @@ -32,12 +31,17 @@ export type { Support, }; +// Quicktype interprets the schema's `baseline: false | "high" | "low"` as +// meaning `baseline: boolean | "high" | "low"`. `BaselineValue` patches it. +export type BaselineValue = "high" | "low" | false; export interface Status extends QuicktypeStatus { - baseline: false | BaselineHighLow; + baseline: BaselineValue; } - export interface SupportStatus extends QuicktypeStatusHeadline { - baseline: false | BaselineHighLow; + baseline: BaselineValue; +} +export interface RegressionNote extends QuicktypeNote { + old_baseline_value: BaselineValue; } // These are "tests" for our type definitions. From 6aea90bf728594a0255f6deab28ab7b951293127 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Wed, 3 Dec 2025 17:42:12 +0100 Subject: [PATCH 09/14] Drop stale regression notes --- features/content-visibility.yml | 9 --------- features/link-rel-dns-prefetch.yml | 9 --------- 2 files changed, 18 deletions(-) diff --git a/features/content-visibility.yml b/features/content-visibility.yml index d2ddae99bd2..df7bd09d71b 100644 --- a/features/content-visibility.yml +++ b/features/content-visibility.yml @@ -3,15 +3,6 @@ description: The `content-visibility` CSS property delays rendering an element, spec: https://drafts.csswg.org/css-contain-2/#content-visibility group: css caniuse: css-content-visibility -notes: - - category: baseline-regression - date: 2025-01-30 - old_baseline_value: low - new_baseline_value: false - message: > - Safari prevents text in `content-visibility: auto` elements from being found with browser's "Find…" user interface. - citations: - - https://bugs.webkit.org/show_bug.cgi?id=283846 compat_features: - api.ContentVisibilityAutoStateChangeEvent - api.ContentVisibilityAutoStateChangeEvent.ContentVisibilityAutoStateChangeEvent diff --git a/features/link-rel-dns-prefetch.yml b/features/link-rel-dns-prefetch.yml index 3e1b0ea3c59..23c2a3f060a 100644 --- a/features/link-rel-dns-prefetch.yml +++ b/features/link-rel-dns-prefetch.yml @@ -2,14 +2,5 @@ name: '' description: The `rel="dns-prefetch"` attribute for the `` HTML element is a hint to the browser that the page or user is likely to request resources from another domain, so the browser should preemptively resolve DNS for the `href` value's domain. spec: https://html.spec.whatwg.org/multipage/links.html#link-type-dns-prefetch caniuse: link-rel-dns-prefetch -notes: - - date: 2025-06-23 - category: baseline-regression - old_baseline_value: low - new_baseline_value: false - message: > - Safari on iOS does not support this feature. Upstream data corrected an earlier report that it did. - citations: - - https://github.com/mdn/browser-compat-data/pull/27057 compat_features: - html.elements.link.rel.dns-prefetch From 7b6fdf292359e1bf54a6b47e7fddd16a9664a465 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 4 Dec 2025 14:59:36 +0100 Subject: [PATCH 10/14] Test stale note check --- assertions.test.ts | 114 ++++++++++++++++++++++++++++++++++++++++++++- assertions.ts | 30 ++++++++++++ index.ts | 12 +---- types.ts | 7 ++- 4 files changed, 151 insertions(+), 12 deletions(-) diff --git a/assertions.test.ts b/assertions.test.ts index 2ff81193779..87522cc2b4c 100644 --- a/assertions.test.ts +++ b/assertions.test.ts @@ -1,5 +1,9 @@ import assert from "node:assert/strict"; -import { assertValidFeatureReference } from "./assertions"; +import { + assertFreshRegressionNotes, + assertValidFeatureReference, +} from "./assertions"; +import { FeatureData } from "./types"; describe("assertValidReference()", function () { it("throws if target ID is a move", function () { @@ -34,3 +38,111 @@ describe("assertValidReference()", function () { }); }); }); + +describe("assertFreshRegressionNotes", function () { + it("throws when the current status is the same as the previous status", function () { + const f = { + kind: "feature", + status: { baseline: "low" }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: "low", + }, + ], + } as Partial as FeatureData; + assert.throws(() => { + assertFreshRegressionNotes("a", f); + }); + }); + + it("throws when the current status is better than the previous status", function () { + const highLow = { + kind: "feature", + status: { baseline: "high" }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: "low", + }, + ], + } as Partial as FeatureData; + assert.throws(() => { + assertFreshRegressionNotes("a", highLow); + }); + + const lowFalse = { + kind: "feature", + status: { baseline: "low" }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: false, + }, + ], + } as Partial as FeatureData; + assert.throws(() => { + assertFreshRegressionNotes("a", lowFalse); + }); + + const highFalse = { + kind: "feature", + status: { baseline: "high" }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: false, + }, + ], + } as Partial as FeatureData; + assert.throws(() => { + assertFreshRegressionNotes("a", highFalse); + }); + }); + + it("does not throw when the current status is lower than the previous status", function () { + const lowHigh = { + kind: "feature", + status: { baseline: "low" }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: "high", + }, + ], + } as Partial as FeatureData; + assertFreshRegressionNotes("a", lowHigh); + + const falseLow = { + kind: "feature", + status: { baseline: false }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: "low", + }, + ], + } as Partial as FeatureData; + assertFreshRegressionNotes("a", falseLow); + + const falseHigh = { + kind: "feature", + status: { baseline: false }, + notes: [ + { + category: "baseline-regression", + old_baseline_value: "high", + }, + ], + } as Partial as FeatureData; + assertFreshRegressionNotes("a", falseHigh); + }); + + it("does not throw without a regression note", function () { + const noRegressionNotes = { + kind: "feature", + status: { baseline: "high" }, + } as Partial as FeatureData; + assertFreshRegressionNotes("a", noRegressionNotes); + }); +}); diff --git a/assertions.ts b/assertions.ts index 90d7a546f16..6cbc0554f2a 100644 --- a/assertions.ts +++ b/assertions.ts @@ -1,4 +1,5 @@ import { isOrdinaryFeatureData } from "./type-guards"; +import { BaselineValue, FeatureData } from "./types"; import { WebFeaturesData } from "./types.quicktype"; /** @@ -26,4 +27,33 @@ export function assertValidFeatureReference( } } +export function assertFreshRegressionNotes( + id: string, + data: FeatureData, +): void { + if (!isOrdinaryFeatureData(data)) { + return; + } + + const { baseline } = data.status; + const notes = data.notes ? data.notes : []; + + for (const [index, note] of notes.entries()) { + if (compareBaselineValue(note.old_baseline_value, baseline) <= 0) { + throw new Error( + `regression note ${index} on ${id}.yml no longer applies (status is ${baseline}, was ${note.old_baseline_value}). Delete this note.`, + ); + } + } +} + +function compareBaselineValue(a: BaselineValue, b: BaselineValue): number { + const statusToNumber = new Map([ + ["high", 2], + ["low", 1], + [false, 0], + ]); + return statusToNumber.get(a) - statusToNumber.get(b); +} + // TODO: assertValidSnapshotReference diff --git a/index.ts b/index.ts index 9cfa0de7ac2..87abba43f44 100644 --- a/index.ts +++ b/index.ts @@ -7,7 +7,7 @@ import YAML from 'yaml'; import { BASELINE_LOW_TO_HIGH_DURATION, coreBrowserSet, getStatus, parseRangedDateString } from 'compute-baseline'; import { Compat } from 'compute-baseline/browser-compat-data'; -import { assertValidFeatureReference } from './assertions'; +import { assertFreshRegressionNotes, assertValidFeatureReference } from './assertions'; import { convertMarkdown } from "./text"; import { isMoved, isSplit } from './type-guards'; import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types'; @@ -201,15 +201,6 @@ for (const [key, data] of yamlEntries('features')) { } } - // Ensure regression notes are still relevant - if (Array.isArray(data.notes)) { - for (const [index, note] of (data.notes as FeatureData["notes"]).entries()) { - if (note.new_baseline_value !== data.status.baseline) { - throw new Error(`regression note ${index} on ${key}.yml no longer applies (status is ${data.status.baseline}, note is ${note.new_baseline_value}). Delete this note.`); - } - } - } - if (data.compat_features) { // Sort compat_features so that grouping and ordering in dist files has // no effect on what web-features users see. @@ -242,6 +233,7 @@ for (const [id, feature] of Object.entries(features)) { const { kind } = feature; switch (kind) { case "feature": + assertFreshRegressionNotes(id, feature); for (const alternative of feature.discouraged?.alternatives ?? []) { assertValidFeatureReference(id, alternative, features) } diff --git a/types.ts b/types.ts index 303104724e8..77a0dbc6e19 100644 --- a/types.ts +++ b/types.ts @@ -76,7 +76,7 @@ export interface WebFeaturesData }; } -export type FeatureData = { kind: "feature" } & Required< +type IntermediateFeatureData = { kind: "feature" } & Required< Pick< QuicktypeMonolithicFeatureData, "description_html" | "description" | "name" | "spec" | "status" @@ -94,6 +94,11 @@ export type FeatureData = { kind: "feature" } & Required< > >; +export interface FeatureData extends IntermediateFeatureData { + status: Status; + notes?: RegressionNote[]; +} + const goodFeatureData: FeatureData = { kind: "feature", name: "Test", From 37f5bb211159059f49f8e6ba1db613652f5570d1 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 4 Dec 2025 15:41:34 +0100 Subject: [PATCH 11/14] Drop `new_baseline_value` from the schema `old_baseline_value` is always worse than `new_baseline_value`. Since we only have three stops, this means that only the status pairs [high, low], [high, false], and [low, false] can be represented as a regression. But that also means there's only one situation where a regressed feature could advance but not back to the level of the status before the regression (from high to false, then false to low). But at that point, the feature has had some time to regress, so that regression note is likely stale anyway. --- assertions.test.ts | 14 +++++++------- assertions.ts | 4 ++-- features/createimagebitmap.yml | 3 +-- features/font-variant-position.yml | 3 +-- features/popover.yml | 3 +-- schemas/data.schema.json | 9 ++------- types.quicktype.ts | 6 +----- types.ts | 2 +- 8 files changed, 16 insertions(+), 28 deletions(-) diff --git a/assertions.test.ts b/assertions.test.ts index 87522cc2b4c..506609fbf72 100644 --- a/assertions.test.ts +++ b/assertions.test.ts @@ -47,7 +47,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: "low", + previous_baseline_value: "low", }, ], } as Partial as FeatureData; @@ -63,7 +63,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: "low", + previous_baseline_value: "low", }, ], } as Partial as FeatureData; @@ -77,7 +77,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: false, + previous_baseline_value: false, }, ], } as Partial as FeatureData; @@ -91,7 +91,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: false, + previous_baseline_value: false, }, ], } as Partial as FeatureData; @@ -107,7 +107,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: "high", + previous_baseline_value: "high", }, ], } as Partial as FeatureData; @@ -119,7 +119,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: "low", + previous_baseline_value: "low", }, ], } as Partial as FeatureData; @@ -131,7 +131,7 @@ describe("assertFreshRegressionNotes", function () { notes: [ { category: "baseline-regression", - old_baseline_value: "high", + previous_baseline_value: "high", }, ], } as Partial as FeatureData; diff --git a/assertions.ts b/assertions.ts index 6cbc0554f2a..6a048c22994 100644 --- a/assertions.ts +++ b/assertions.ts @@ -39,9 +39,9 @@ export function assertFreshRegressionNotes( const notes = data.notes ? data.notes : []; for (const [index, note] of notes.entries()) { - if (compareBaselineValue(note.old_baseline_value, baseline) <= 0) { + if (compareBaselineValue(note.previous_baseline_value, baseline) <= 0) { throw new Error( - `regression note ${index} on ${id}.yml no longer applies (status is ${baseline}, was ${note.old_baseline_value}). Delete this note.`, + `regression note ${index} on ${id}.yml no longer applies (status is ${baseline}, was ${note.previous_baseline_value}). Delete this note.`, ); } } diff --git a/features/createimagebitmap.yml b/features/createimagebitmap.yml index 37e0d141fe8..bfa4275b540 100644 --- a/features/createimagebitmap.yml +++ b/features/createimagebitmap.yml @@ -5,8 +5,7 @@ caniuse: createimagebitmap notes: - date: 2025-08-11 category: baseline-regression - old_baseline_value: high - new_baseline_value: low + previous_baseline_value: high message: > This feature's status was recalculated to be more consistent with caniuse's criteria for full support, requiring `SVGImageElement` as a supported image source. citations: diff --git a/features/font-variant-position.yml b/features/font-variant-position.yml index 63448778d3f..226d6725bdf 100644 --- a/features/font-variant-position.yml +++ b/features/font-variant-position.yml @@ -5,8 +5,7 @@ group: font-features notes: - date: 2024-10-15 category: baseline-regression - old_baseline_value: low - new_baseline_value: false + previous_baseline_value: low message: > Chrome, Edge, and Safari do not implement font synthesis for missing superscript or subscript glyphs. citations: diff --git a/features/popover.yml b/features/popover.yml index e253180f9f4..d3180933427 100644 --- a/features/popover.yml +++ b/features/popover.yml @@ -5,8 +5,7 @@ group: html # notes: # - date: 2024-09-18 # category: baseline-regression -# old_baseline_value: low -# new_baseline_value: false +# previous_baseline_value: low # message: > # Safari on iOS has a bug that prevents light dismiss (tapping outside the element to close it). # citations: diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 2b3b7f3982d..4025da132aa 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -170,11 +170,7 @@ "description": "A short description of the cause of the regression as HTML", "type": "string" }, - "new_baseline_value": { - "description": "The `baseline` status value after the regression", - "$ref": "#/definitions/BaselineValue" - }, - "old_baseline_value": { + "previous_baseline_value": { "description": "The `baseline` status value before the regression", "$ref": "#/definitions/BaselineValue" } @@ -185,8 +181,7 @@ "message", "message_html", "citations", - "old_baseline_value", - "new_baseline_value" + "previous_baseline_value" ], "type": "object" }, diff --git a/types.quicktype.ts b/types.quicktype.ts index cca9ce6eb0f..732dee8d019 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -164,14 +164,10 @@ export interface Note { * A short description of the cause of the regression as HTML */ message_html: string; - /** - * The `baseline` status value after the regression - */ - new_baseline_value: boolean | BaselineValueEnum; /** * The `baseline` status value before the regression */ - old_baseline_value: boolean | BaselineValueEnum; + previous_baseline_value: boolean | BaselineValueEnum; } export type BaselineValueEnum = "high" | "low"; diff --git a/types.ts b/types.ts index 77a0dbc6e19..9cb99a9ac5a 100644 --- a/types.ts +++ b/types.ts @@ -41,7 +41,7 @@ export interface SupportStatus extends QuicktypeStatusHeadline { baseline: BaselineValue; } export interface RegressionNote extends QuicktypeNote { - old_baseline_value: BaselineValue; + previous_baseline_value: BaselineValue; } // These are "tests" for our type definitions. From 4654301ec6a8f452a92da2f9645c6e2d11c3525f Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 15 Jan 2026 12:57:43 +0100 Subject: [PATCH 12/14] FIx missing punctuation --- schemas/data.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 26c9415f7b4..f3e8aabfac1 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -161,7 +161,7 @@ "properties": { "category": { "const": "baseline-regression", - "description": "The topic of this note. This field is also a discriminator for any future note types", + "description": "The topic of this note. This field is also a discriminator for any future note types.", "type": "string" }, "citations": { From 9177cfddb82473f1c932f9470c4c207a688e6758 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 15 Jan 2026 12:57:55 +0100 Subject: [PATCH 13/14] Add missing docstring --- assertions.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/assertions.ts b/assertions.ts index c7e30ceba73..363e7e12e8c 100644 --- a/assertions.ts +++ b/assertions.ts @@ -27,6 +27,20 @@ export function assertValidFeatureReference( } } +/** + * Assert that a regression note is still relevant. + * + * A fresh regression note must represent a status change where the + * `previous_baseline_value` value is better than the current `status.baseline` + * value. A regression note must represent a change in status from high to low, + * high to not Baseline, or low to not Baseline. A regression note must not + * represent a status change that has aged, such that the current + * `status.baseline` value has progressed back to `previous_baseline_value`. + * + * @export + * @param {string} id The ID of the feature to be checked + * @param {FeatureData} data The ordinary feature data to be checked + */ export function assertFreshRegressionNotes( id: string, data: FeatureData, From 2614b110aba93f60dbda85ab0b5f2950cd869493 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 15 Jan 2026 13:01:31 +0100 Subject: [PATCH 14/14] Remove spurious edits --- index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/index.ts b/index.ts index f046279db2d..00786692085 100644 --- a/index.ts +++ b/index.ts @@ -159,7 +159,6 @@ for (const [key, data] of yamlEntries('features')) { } } - // XXX clean me up if (isOrdinaryFeatureData(data)) { // Convert Markdown fields const description = data.description as unknown; @@ -189,7 +188,6 @@ for (const [key, data] of yamlEntries('features')) { } } - // Compute Baseline high date from low date. if (data.status?.baseline === 'high') { const [date, ranged] = parseRangedDateString(data.status.baseline_low_date);