Skip to content

Baseline status mismatch for accent-color across web-features and compute-baseline #2880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
rviscomi opened this issue Apr 17, 2025 · 5 comments

Comments

@rviscomi
Copy link
Contributor

rviscomi commented Apr 17, 2025

Should Baseline tools like ESLint warn on accent-color? The feature is not considered Baseline, but the individual BCD key is fully supported across all major browsers now that the accent-color.maintains_contrast subfeature key absorbs all of the a11y issues. See #2874 and mdn/browser-compat-data#26073

Baseline tools look up the Baseline status of individual BCD keys using compute-baseline, which now says that css.properties.accent-color itself is Baseline. As a result, the tools WILL NOT warn when they see the property.

Is that the intended behavior?

@rviscomi
Copy link
Contributor Author

The compat table on MDN shows the issue well:

Image

accent-color is green across the board, indicating that the property is fully implemented. But there's a separate row for implementation issues related to color contrast.

@ddbeck
Copy link
Collaborator

ddbeck commented Apr 23, 2025

If you're computing from a single key, then yes this is expected behavior. In general, a per-key status will tell you whether that key and all of its ancestors are supported or not (unless you go to some extra lengths to call computeBaseline() with checkAncestors set to false). A per-key status doesn't (yet?) have any additional context from other compat keys.

Possible alternatives you might consider:

  • Use a web-features headline status (i.e., the status given via the JSON package, seen here at the top of https://github.com/web-platform-dx/web-features/blob/main/features/accent-color.yml.dist). In other words, trust us to group BCD keys sensibly. You might discover that we don't have the right level of granularity for you. I'd like to know if this is the case.

  • Take both the web-features headline status and the per-key status and clamp to the "worse" result. For example, accent-color's status in web-features and the compute-baseline result for BCD's css.properties.accent-color, and take the less-supporting result.

  • Check more keys. For example, you might want to check whether accent-color is supported by writing some logic in ESLint that says, accent-color (the property) is actually css.properties.accent-color + css.properties.accent-color.maintains_contrast. This will be VERY precise—you'll cover only the slimmest part of BCD that matters to you. However, BCD churns a bit; you might find yourself having to do a lot of maintenance if, for example, BCD renames or moves a compat key to a different part of the tree.

All that said: for accent-color specifically, I think BCD structured this data poorly. I've sent mdn/browser-compat-data#26605 to correct it, though it remains to be seen whether it's accepted.

Does that answer some or all of your questions?

@rviscomi
Copy link
Contributor Author

Thanks @ddbeck!

To give us some more concrete examples to play with, I wrote a script to extract BCD keys whose status differs from their parent WebDX feature, limited to the types of keys Baseline tools tend to look at.

Results
## html.elements
┌─────────┬──────────────────────────┬───────────────────────────────────────────────────┬───────────────┬───────────┐
│ (index) │ featureId                │ bcdKey                                            │ featureStatus │ keyStatus │
├─────────┼──────────────────────────┼───────────────────────────────────────────────────┼───────────────┼───────────┤
│ 0       │ 'audio'                  │ 'html.elements.audio.autoplay'                    │ 'high'        │ false     │
│ 1       │ 'form'                   │ 'html.elements.form.rel'                          │ 'high'        │ 'low'     │
│ 2       │ 'csp'                    │ 'html.elements.iframe.csp'                        │ 'high'        │ false     │
│ 3       │ 'loading-lazy'           │ 'html.elements.img.loading'                       │ 'low'         │ 'high'    │
│ 4       │ 'input'                  │ 'html.elements.input.alpha'                       │ 'high'        │ undefined │
│ 5       │ 'input'                  │ 'html.elements.input.colorspace'                  │ 'high'        │ undefined │
│ 6       │ 'select'                 │ 'html.elements.select.size'                       │ 'high'        │ false     │
│ 7       │ 'source'                 │ 'html.elements.source.height'                     │ 'high'        │ 'low'     │
│ 8       │ 'source'                 │ 'html.elements.source.width'                      │ 'high'        │ 'low'     │
│ 9       │ 'declarative-shadow-dom' │ 'html.elements.template.shadowrootclonable'       │ 'low'         │ false     │
│ 10      │ 'declarative-shadow-dom' │ 'html.elements.template.shadowrootdelegatesfocus' │ 'low'         │ false     │
│ 11      │ 'declarative-shadow-dom' │ 'html.elements.template.shadowrootserializable'   │ 'low'         │ false     │
└─────────┴──────────────────────────┴───────────────────────────────────────────────────┴───────────────┴───────────┘
## html.global_attributes
┌─────────┬─────────────┬────────────────────────────────────┬───────────────┬───────────┐
│ (index) │ featureId   │ bcdKey                             │ featureStatus │ keyStatus │
├─────────┼─────────────┼────────────────────────────────────┼───────────────┼───────────┤
│ 0       │ 'autofocus' │ 'html.global_attributes.autofocus' │ 'high'        │ 'low'     │
└─────────┴─────────────┴────────────────────────────────────┴───────────────┴───────────┘
## css.at-rules
┌─────────┐
│ (index) │
├─────────┤
└─────────┘
## css.properties
┌─────────┬───────────────────────────┬────────────────────────────────────────────────────────────┬───────────────┬───────────┐
│ (index) │ featureId                 │ bcdKey                                                     │ featureStatus │ keyStatus │
├─────────┼───────────────────────────┼────────────────────────────────────────────────────────────┼───────────────┼───────────┤
│ 0       │ 'accent-color'            │ 'css.properties.accent-color'                              │ false         │ 'high'    │
│ 1       │ 'accent-color'            │ 'css.properties.accent-color.auto'                         │ false         │ 'high'    │
│ 2       │ 'animations-css'          │ 'css.properties.animation-duration.auto'                   │ 'high'        │ false     │
│ 3       │ 'page-breaks'             │ 'css.properties.break-after.always'                        │ 'high'        │ false     │
│ 4       │ 'page-breaks'             │ 'css.properties.break-after.avoid-page'                    │ 'high'        │ false     │
│ 5       │ 'page-breaks'             │ 'css.properties.break-after.recto'                         │ 'high'        │ false     │
│ 6       │ 'page-breaks'             │ 'css.properties.break-after.verso'                         │ 'high'        │ false     │
│ 7       │ 'page-breaks'             │ 'css.properties.break-before.always'                       │ 'high'        │ false     │
│ 8       │ 'page-breaks'             │ 'css.properties.break-before.avoid-page'                   │ 'high'        │ false     │
│ 9       │ 'page-breaks'             │ 'css.properties.break-before.recto'                        │ 'high'        │ false     │
│ 10      │ 'page-breaks'             │ 'css.properties.break-before.verso'                        │ 'high'        │ false     │
│ 11      │ 'column-breaks'           │ 'css.properties.break-inside.avoid-column'                 │ false         │ 'high'    │
│ 12      │ 'table'                   │ 'css.properties.caption-side.bottom-outside'               │ 'high'        │ false     │
│ 13      │ 'table'                   │ 'css.properties.caption-side.top-outside'                  │ 'high'        │ false     │
│ 14      │ 'logical-properties'      │ 'css.properties.clear.inline-end'                          │ 'high'        │ 'low'     │
│ 15      │ 'logical-properties'      │ 'css.properties.clear.inline-start'                        │ 'high'        │ 'low'     │
│ 16      │ 'svg'                     │ 'css.properties.color-interpolation.linearGradient'        │ 'high'        │ false     │
│ 17      │ 'content'                 │ 'css.properties.content.gradient'                          │ 'high'        │ false     │
│ 18      │ 'content-visibility'      │ 'css.properties.content-visibility'                        │ false         │ 'low'     │
│ 19      │ 'content-visibility'      │ 'css.properties.content-visibility.hidden'                 │ false         │ 'low'     │
│ 20      │ 'content-visibility'      │ 'css.properties.content-visibility.visible'                │ false         │ 'low'     │
│ 21      │ 'counters'                │ 'css.properties.counter-reset.list-item'                   │ 'high'        │ false     │
│ 22      │ 'cursor'                  │ 'css.properties.cursor'                                    │ false         │ 'high'    │
│ 23      │ 'cursor'                  │ 'css.properties.cursor.auto'                               │ false         │ 'high'    │
│ 24      │ 'cursor'                  │ 'css.properties.cursor.cell'                               │ false         │ 'high'    │
│ 25      │ 'cursor'                  │ 'css.properties.cursor.text'                               │ false         │ 'high'    │
│ 26      │ 'cursor'                  │ 'css.properties.cursor.url'                                │ false         │ 'high'    │
│ 27      │ 'svg'                     │ 'css.properties.d'                                         │ 'high'        │ false     │
│ 28      │ 'display-contents'        │ 'css.properties.display.contents'                          │ false         │ 'high'    │
│ 29      │ 'mathml'                  │ 'css.properties.display.math'                              │ 'low'         │ false     │
│ 30      │ 'logical-properties'      │ 'css.properties.float.inline-end'                          │ 'high'        │ 'low'     │
│ 31      │ 'logical-properties'      │ 'css.properties.float.inline-start'                        │ 'high'        │ 'low'     │
│ 32      │ 'mathml'                  │ 'css.properties.font-size.math'                            │ 'low'         │ false     │
│ 33      │ 'font-size'               │ 'css.properties.font-size.xxx-large'                       │ 'high'        │ 'low'     │
│ 34      │ 'font-synthesis'          │ 'css.properties.font-synthesis.position'                   │ 'high'        │ false     │
│ 35      │ 'font-variant'            │ 'css.properties.font-variant.historical-forms'             │ 'high'        │ 'low'     │
│ 36      │ 'font-variant'            │ 'css.properties.font-variant.sub'                          │ 'high'        │ 'low'     │
│ 37      │ 'font-variant'            │ 'css.properties.font-variant.super'                        │ 'high'        │ 'low'     │
│ 38      │ 'forced-colors'           │ 'css.properties.forced-color-adjust'                       │ 'high'        │ false     │
│ 39      │ 'forced-colors'           │ 'css.properties.forced-color-adjust.auto'                  │ 'high'        │ false     │
│ 40      │ 'forced-colors'           │ 'css.properties.forced-color-adjust.none'                  │ 'high'        │ false     │
│ 41      │ 'forced-colors'           │ 'css.properties.forced-color-adjust.preserve-parent-color' │ 'high'        │ false     │
│ 42      │ 'list-style'              │ 'css.properties.list-style.symbols'                        │ 'high'        │ false     │
│ 43      │ 'list-style'              │ 'css.properties.list-style-type.symbols'                   │ 'high'        │ false     │
│ 44      │ 'masks'                   │ 'css.properties.mask-clip.border'                          │ 'low'         │ false     │
│ 45      │ 'masks'                   │ 'css.properties.mask-clip.content'                         │ 'low'         │ false     │
│ 46      │ 'masks'                   │ 'css.properties.mask-clip.padding'                         │ 'low'         │ false     │
│ 47      │ 'masks'                   │ 'css.properties.mask-clip.text'                            │ 'low'         │ false     │
│ 48      │ 'masks'                   │ 'css.properties.mask-origin.border'                        │ 'low'         │ false     │
│ 49      │ 'masks'                   │ 'css.properties.mask-origin.content'                       │ 'low'         │ false     │
│ 50      │ 'masks'                   │ 'css.properties.mask-origin.fill-box'                      │ 'low'         │ false     │
│ 51      │ 'masks'                   │ 'css.properties.mask-origin.padding'                       │ 'low'         │ false     │
│ 52      │ 'masks'                   │ 'css.properties.mask-origin.stroke-box'                    │ 'low'         │ false     │
│ 53      │ 'masks'                   │ 'css.properties.mask-origin.view-box'                      │ 'low'         │ false     │
│ 54      │ 'mathml'                  │ 'css.properties.math-depth'                                │ 'low'         │ false     │
│ 55      │ 'mathml'                  │ 'css.properties.math-shift'                                │ 'low'         │ false     │
│ 56      │ 'mix-blend-mode'          │ 'css.properties.mix-blend-mode.plus-darker'                │ 'high'        │ false     │
│ 57      │ 'motion-path'             │ 'css.properties.offset-anchor'                             │ 'high'        │ 'low'     │
│ 58      │ 'motion-path'             │ 'css.properties.offset-anchor.auto'                        │ 'high'        │ 'low'     │
│ 59      │ 'motion-path'             │ 'css.properties.offset-anchor.bottom'                      │ 'high'        │ 'low'     │
│ 60      │ 'motion-path'             │ 'css.properties.offset-anchor.center'                      │ 'high'        │ 'low'     │
│ 61      │ 'motion-path'             │ 'css.properties.offset-anchor.left'                        │ 'high'        │ 'low'     │
│ 62      │ 'motion-path'             │ 'css.properties.offset-anchor.right'                       │ 'high'        │ 'low'     │
│ 63      │ 'motion-path'             │ 'css.properties.offset-anchor.top'                         │ 'high'        │ 'low'     │
│ 64      │ 'motion-path'             │ 'css.properties.offset-path.border-box'                    │ 'high'        │ 'low'     │
│ 65      │ 'motion-path'             │ 'css.properties.offset-path.content-box'                   │ 'high'        │ 'low'     │
│ 66      │ 'motion-path'             │ 'css.properties.offset-path.fill-box'                      │ 'high'        │ 'low'     │
│ 67      │ 'motion-path'             │ 'css.properties.offset-path.margin-box'                    │ 'high'        │ false     │
│ 68      │ 'motion-path'             │ 'css.properties.offset-path.none'                          │ 'high'        │ 'low'     │
│ 69      │ 'motion-path'             │ 'css.properties.offset-path.padding-box'                   │ 'high'        │ 'low'     │
│ 70      │ 'motion-path'             │ 'css.properties.offset-path.ray'                           │ 'high'        │ 'low'     │
│ 71      │ 'motion-path'             │ 'css.properties.offset-path.stroke-box'                    │ 'high'        │ 'low'     │
│ 72      │ 'motion-path'             │ 'css.properties.offset-path.url'                           │ 'high'        │ 'low'     │
│ 73      │ 'motion-path'             │ 'css.properties.offset-path.view-box'                      │ 'high'        │ 'low'     │
│ 74      │ 'motion-path'             │ 'css.properties.offset-position'                           │ 'high'        │ 'low'     │
│ 75      │ 'motion-path'             │ 'css.properties.offset-position.auto'                      │ 'high'        │ 'low'     │
│ 76      │ 'motion-path'             │ 'css.properties.offset-position.bottom'                    │ 'high'        │ 'low'     │
│ 77      │ 'motion-path'             │ 'css.properties.offset-position.center'                    │ 'high'        │ 'low'     │
│ 78      │ 'motion-path'             │ 'css.properties.offset-position.left'                      │ 'high'        │ 'low'     │
│ 79      │ 'motion-path'             │ 'css.properties.offset-position.normal'                    │ 'high'        │ 'low'     │
│ 80      │ 'motion-path'             │ 'css.properties.offset-position.right'                     │ 'high'        │ 'low'     │
│ 81      │ 'motion-path'             │ 'css.properties.offset-position.top'                       │ 'high'        │ 'low'     │
│ 82      │ 'logical-properties'      │ 'css.properties.overflow-block'                            │ 'high'        │ false     │
│ 83      │ 'logical-properties'      │ 'css.properties.overflow-block.overlay'                    │ 'high'        │ false     │
│ 84      │ 'logical-properties'      │ 'css.properties.overflow-inline'                           │ 'high'        │ false     │
│ 85      │ 'logical-properties'      │ 'css.properties.overflow-inline.overlay'                   │ 'high'        │ false     │
│ 86      │ 'page-selectors'          │ 'css.properties.page'                                      │ false         │ 'low'     │
│ 87      │ 'paint-order'             │ 'css.properties.paint-order'                               │ false         │ 'low'     │
│ 88      │ 'ruby-position'           │ 'css.properties.ruby-position.alternate'                   │ 'low'         │ false     │
│ 89      │ 'ruby-position'           │ 'css.properties.ruby-position.inter-character'             │ 'low'         │ false     │
│ 90      │ 'svg'                     │ 'css.properties.rx'                                        │ 'high'        │ false     │
│ 91      │ 'svg'                     │ 'css.properties.ry'                                        │ 'high'        │ false     │
│ 92      │ 'shape-outside'           │ 'css.properties.shape-image-threshold.percentages'         │ 'high'        │ false     │
│ 93      │ 'shape-outside'           │ 'css.properties.shape-outside.path'                        │ 'high'        │ false     │
│ 94      │ 'svg'                     │ 'css.properties.stroke-color'                              │ 'high'        │ false     │
│ 95      │ 'text-align'              │ 'css.properties.text-align.match-parent'                   │ 'high'        │ false     │
│ 96      │ 'text-decoration'         │ 'css.properties.text-decoration-line.grammar-error'        │ 'high'        │ false     │
│ 97      │ 'text-decoration'         │ 'css.properties.text-decoration-line.spelling-error'       │ 'high'        │ false     │
│ 98      │ 'text-decoration'         │ 'css.properties.text-decoration-skip'                      │ 'high'        │ false     │
│ 99      │ 'text-decoration'         │ 'css.properties.text-decoration-skip.auto'                 │ 'high'        │ false     │
│ 100     │ 'text-decoration'         │ 'css.properties.text-decoration-skip.none'                 │ 'high'        │ false     │
│ 101     │ 'text-decoration'         │ 'css.properties.text-decoration-skip-ink.all'              │ 'high'        │ false     │
│ 102     │ 'text-decoration'         │ 'css.properties.text-decoration-thickness.percentage'      │ 'high'        │ 'low'     │
│ 103     │ 'text-emphasis'           │ 'css.properties.text-emphasis-position.auto'               │ 'high'        │ false     │
│ 104     │ 'text-emphasis'           │ 'css.properties.text-emphasis-position.over'               │ 'high'        │ 'low'     │
│ 105     │ 'text-emphasis'           │ 'css.properties.text-emphasis-position.under'              │ 'high'        │ 'low'     │
│ 106     │ 'text-transform'          │ 'css.properties.text-transform.full-size-kana'             │ 'high'        │ false     │
│ 107     │ 'text-transform'          │ 'css.properties.text-transform.full-width'                 │ 'high'        │ false     │
│ 108     │ 'mathml'                  │ 'css.properties.text-transform.math-auto'                  │ 'low'         │ false     │
│ 109     │ 'text-underline-offset'   │ 'css.properties.text-underline-offset.percentage'          │ 'high'        │ 'low'     │
│ 110     │ 'text-underline-position' │ 'css.properties.text-underline-position.left'              │ 'high'        │ 'low'     │
│ 111     │ 'text-underline-position' │ 'css.properties.text-underline-position.right'             │ 'high'        │ 'low'     │
│ 112     │ 'text-wrap-style'         │ 'css.properties.text-wrap-style'                           │ false         │ 'low'     │
│ 113     │ 'text-wrap-style'         │ 'css.properties.text-wrap-style.auto'                      │ false         │ 'low'     │
│ 114     │ 'text-wrap-style'         │ 'css.properties.text-wrap-style.balance'                   │ false         │ 'low'     │
│ 115     │ 'text-wrap-style'         │ 'css.properties.text-wrap-style.stable'                    │ false         │ 'low'     │
│ 116     │ 'touch-action'            │ 'css.properties.touch-action.pan-down'                     │ 'high'        │ false     │
│ 117     │ 'touch-action'            │ 'css.properties.touch-action.pan-left'                     │ 'high'        │ false     │
│ 118     │ 'touch-action'            │ 'css.properties.touch-action.pan-right'                    │ 'high'        │ false     │
│ 119     │ 'touch-action'            │ 'css.properties.touch-action.pan-up'                       │ 'high'        │ false     │
│ 120     │ 'transform-box'           │ 'css.properties.transform-box'                             │ 'low'         │ 'high'    │
│ 121     │ 'transform-box'           │ 'css.properties.transform-box.fill-box'                    │ 'low'         │ 'high'    │
│ 122     │ 'transform-box'           │ 'css.properties.transform-box.view-box'                    │ 'low'         │ 'high'    │
│ 123     │ 'white-space-collapse'    │ 'css.properties.white-space-collapse.preserve-spaces'      │ 'low'         │ false     │
│ 124     │ 'writing-mode'            │ 'css.properties.writing-mode.sideways-lr'                  │ 'high'        │ 'low'     │
│ 125     │ 'writing-mode'            │ 'css.properties.writing-mode.sideways-rl'                  │ 'high'        │ 'low'     │
└─────────┴───────────────────────────┴────────────────────────────────────────────────────────────┴───────────────┴───────────┘
## css.selectors
┌─────────┬──────────────────┬───────────────────────┬───────────────┬───────────┐
│ (index) │ featureId        │ bcdKey                │ featureStatus │ keyStatus │
├─────────┼──────────────────┼───────────────────────┼───────────────┼───────────┤
│ 0       │ 'page-selectors' │ 'css.selectors.first' │ false         │ 'low'     │
└─────────┴──────────────────┴───────────────────────┴───────────────┴───────────┘
## css.types
┌─────────┬───────────────┬─────────────────┬───────────────┬───────────┐
│ (index) │ featureId     │ bcdKey          │ featureStatus │ keyStatus │
├─────────┼───────────────┼─────────────────┼───────────────┼───────────┤
│ 0       │ 'motion-path' │ 'css.types.ray' │ 'high'        │ 'low'     │
└─────────┴───────────────┴─────────────────┴───────────────┴───────────┘

Use a web-features headline status (i.e., the status given via the JSON package, seen here at the top of https://github.com/web-platform-dx/web-features/blob/main/features/accent-color.yml.dist). In other words, trust us to group BCD keys sensibly. You might discover that we don't have the right level of granularity for you. I'd like to know if this is the case.

One example where the granularity may be too coarse for an approach like this is html.elements.img.loading, which belongs to the loading-lazy feature.

This specific BCD key has a Baseline status of "high" and the loading-lazy feature is "low". From a tooling perspective though, there's no reason to discourage developers from using <img loading> just because <iframe loading> isn't as well-supported.

There are also lots of examples where a BCD key's status may be lower than its parent feature. Here are a few of them:

  • html.elements.audio.autoplay (false) < audio
  • html.elements.iframe.csp (false) < csp (high)
  • css.properties.animation-duration.auto (false) < animations-css (high)
  • css.properties.clear.inline-start (low) < logical-properties (high)
  • css.properties.forced-color-adjust (false) < forced-colors (high)

In cases like these, I would expect Baseline tools to warn developers about using an HTML attribute or CSS property value that isn't widely available.

Take both the web-features headline status and the per-key status and clamp to the "worse" result. For example, accent-color's status in web-features and the compute-baseline result for BCD's css.properties.accent-color, and take the less-supporting result.

Interesting idea. This would completely address the second case above where the BCD key has a status that is lower than the parent feature. But I don't think it would help the loading-lazy case. Maybe that's a good argument for splitting up that particular feature?

There are a few other cases like loading-lazy:

And one more case that seems similar to accent-color, in which maybe the solution is to mark the BCD key itself as partial:

But in the other cases above, I don't necessarily see a reason to hold back from using the features, as long as they meet my Baseline targets. For example, transform-box: fill-box is widely available and so is the value view-box. Tools probably shouldn't warn when those values are used just because other possible values exist like content-box and stroke-box, which are not widely available.

Check more keys. For example, you might want to check whether accent-color is supported by writing some logic in ESLint that says, accent-color (the property) is actually css.properties.accent-color + css.properties.accent-color.maintains_contrast. This will be VERY precise—you'll cover only the slimmest part of BCD that matters to you. However, BCD churns a bit; you might find yourself having to do a lot of maintenance if, for example, BCD renames or moves a compat key to a different part of the tree.

Yeah this seems like a lot of maintenance overhead on the part of the tooling author, and it would have to be duplicated across every tool that checks that feature.

If anything, that association between keys should be centralized in web-features, sort of like what you were hinting at here:

A per-key status doesn't (yet?) have any additional context from other compat keys.

And compute-baseline could handle the logic to take a single key and combine it with any other relevant keys to get the true status. There are so few of these edge cases that maybe this is a viable solution.

@ddbeck
Copy link
Collaborator

ddbeck commented Apr 24, 2025

Thank you so much for the breakdown here! Clamping seems like a good area to investigate more closely.

Interesting idea. This would completely address the second case above where the BCD key has a status that is lower than the parent feature. But I don't think it would help the loading-lazy case. Maybe that's a good argument for splitting up that particular feature?

I'm seeing a few different ways to handle a case like loading-lazy, if clamping is on the table. Not every feature would get the same treatment, so we'd probably need to do a mix of these:

  1. Split the feature.

    In the lazy-loading case, caniuse lumps it together, so we did too. We could break with caniuse, if there's a strong story for it. It's at least plausible in this case: lazy-loading an image is something developers would be very likely to do independently of iframes.

    (I think there's going to be a lot of enthusiasm for this approach once we implement some feature evolution mechanisms. It'd be much nicer for a feature's "later additions" to exist as separate, related features that eventually merge into a root feature, after they're well-established. Feature evolution is already on my agenda, with Consider handling feature identifier changes or aliases #91 being the most central part of it.)

  2. Use an editorial override on the feature's headline status, setting a compute_from annotation in the authored YAML files.

    In the lazy-loading case, we'd pin the headline status to lazy image support. This would play nicely with the clamping approach: the headline status would line up with lazy-loading images (which I suspect is the much more common use of the attribute), while the per-key status would be worse for iframes. This is a little… impure, but we do it often enough anyway and makes clamping make sense for your application.

  3. Override BCD on a key-by-key basis.

    In the css.properties.accent-color + css.properties.accent-color.maintains_contrast scenario (supposing Roll up css.properties.accent-color.maintains_contrast into parent mdn/browser-compat-data#26605 is not accepted, though I think it will be), we might just make a special case judgement that the two keys cannot be computed separately.

    (We need this option eventually anyway. Sometimes BCD shows a partial implementation that is more restrictive than the Baseline definition. Override individual BCD keys #1690)

If all that sounds plausible then…

And compute-baseline could handle the logic to take a single key and combine it with any other relevant keys to get the true status. There are so few of these edge cases that maybe this is a viable solution.

Yes, I could see getStatus() being the way clamping and exceptional case handling happens. I kinda imagined something like this for the method, but we never actually got to implementing it (because, for the longest time, no one was using compute-baseline!).

/**
* Calculate a Baseline status for specific browser compat data keys within a
* web-features feature, in the style of a web-feature's `status` key. Use this
* method to calculate fine-grained support statuses. This is the only method
* approved to compute Baseline statuses not otherwise published in the
* `web-features` package.
*
* For example, suppose you want to show a Baseline status for a specific method
* in a feature, which might've been supported earlier or later than the broader
* feature overall. Then you'd call `getStatus('example-feature',
* 'api.ExampleManager.doExample')`.
*/
export function getStatus(
featureId: string,
compatKey: string,
compat: Compat = defaultCompat,
): SupportStatus {
// TODO: actually check that featureId is a valid feature
// TODO: actually check that compatKey is tagged as featureId in BCD _or_ listed in web-features
return JSON.parse(
computeBaseline(
{ compatKeys: [compatKey], checkAncestors: true },
compat,
).toJSON(),
);
}

@ddbeck
Copy link
Collaborator

ddbeck commented Apr 24, 2025

After meeting with MDN today, I think #2891 might also be of interest here. It would maybe complicate some clamping scenarios.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants