Skip to content

[css-conditional-4] Need a way in CSS to test for support of HTML features #9746

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
jensimmons opened this issue Dec 21, 2023 · 19 comments
Open

Comments

@jensimmons
Copy link
Contributor

There should be a way to feature detect support for an HTML attribute, HTML element and other things in HTML.

For instance, the switch attribute is new to HTML. It turns a checkbox form control into a native switch, in a progressively enhanced fashion.

Authors will likely want to style their switch with custom styles, while still supporting the checkbox fallback. If they apply styles without writing conditional code, the checkbox is ruined.

This is one idea of how it could work...

@supports selector(input[switch]) {
 /* Would like to put custom switch styles here, so they only apply conditionally. */
}

...but that doesn't work... because it always returns true. The selector is understood by CSS, even when it's not supported in HTML.

How can we provide a mechanism to CSS for testing support of HTML features?

Here's a demo of switch, with custom styles that should be applied conditionally.
https://codepen.io/jensimmons/pen/eYXYZWr?editors=1100

@jensimmons jensimmons changed the title Need a way in CSS to test for support of HTML features [css-conditional-4] Need a way in CSS to test for support of HTML features Dec 21, 2023
@hober
Copy link
Member

hober commented Dec 21, 2023

Put another way, the reason you want feature detection from CSS is so that you can style the thing on one way instead of in another way. Concretely:

  • Write styles that apply to checkboxes but not switches in browsers that support just checkboxes as well as browsers that support both
  • Write styles that apply to switches in browsers that support them and don't get applied at all in browsers that don't support switches)

The first one is just input:not([switch]), I think.
The second is really interesting. @nt1m tells me :has() doesn't support pseudo-elements, which is a real bummer, because input[type=checkbox][switch]:has(::track) would be the obvious and nice solution to this if it worked.

@fantasai
Copy link
Collaborator

Yeah, I think this would be a good idea. Could even re-use selector syntax, but it would be a different function, e.g. html() or markup() or something. E.g.

  • HTML(rb) indicates the UA supports the rb element
  • HTML([dir]) indicates some basic level of support for the dir attribute (HTML4-level support would be adequate to return true, because auto hadn't been invented yet)
  • HTML([dir=auto]) indicates support for dir=auto
  • HTML(input[type=color]) indicates support for <input type=color>

CSS tracks whether features are supported by whether they're successfully parsed, and you probably can't do that here, though so it would require the UA maintaining some kind of registry of what it supports.

@SebastianZ
Copy link
Contributor

I really like @fantasai's proposal. I'd just note to think of SVG and MathML, as well. So either call the function markup() and let it cover all types of markup. Or, to avoid name clashes and allow more precise checks, let the markup() function take keywords to differenciate them or introduce separate functions html(), svg(), and mathml() for them.

Sebastian

@nt1m
Copy link
Member

nt1m commented Dec 22, 2023

The problem with the above proposal is that a registry of features for each language could go stale, and there's no good way from code to enforce that registry from being updated.

@annevk suggested linked with IDL, which I think is a much better idea, since IDL needs to be updated in order to expose a new web feature.

@supports idl(HTMLInputElement.prototype.switch) or such. (syntax to be bikeshed)

@SebastianZ
Copy link
Contributor

While IDL is good in regard of requiring to be updated, not all elements have an IDL associated to them, like e.g. the <ruby> element.

Therefore, I'd vote for introducing a similar registry for markup, as that would not only allow CSS to check whether something is supported but also have other benefits. E.g. having such a registry would allow DevTools to add autocompletion for tag and attribute names without having to maintain them by themselves. And it also makes markup feature detection generally easier or even possible, which is useful to authors and presumably also for WPT.

Though if such a registry is not possible for some reason and we have to base the checks on IDL, the syntax should made more CSSy and also not include the abbreviation "IDL". It could actually still be mapped to what @fantasai and I proposed earlier, because all IDLs follow a specific naming syntax. The names have the type as prefix, followed by their name, followed by the suffix "Element". And the IDLs also define the attributes. So you could internally translate html(input[switch]) to HTMLInputElement.prototype.switch or svg(clipPath) to SVGClipPathElement.
But again, it would be weird if you can test for html(details) but not for html(summary) just because there's no HTMLSummaryElement interface for it.

Sebastian

@nt1m
Copy link
Member

nt1m commented Dec 23, 2023

You mentioned DevTools are an example of registry, I do want to say this is a counter example, this isn't something WebKit does a great job at updating well.

I agree the syntax should be as friendly as possible, but not having to maintain a separate registry is as equally as important IMO.

@nt1m
Copy link
Member

nt1m commented Dec 23, 2023

I guess engines have an atom string registry for both tagnames / attributes, which can be re-used, but I don't think it's well standardized or consistent across browsers (whereas IDL is better standardized)

@SebastianZ
Copy link
Contributor

You mentioned DevTools are an example of registry, I do want to say this is a counter example, this isn't something WebKit does a great job at updating well.

No, what I meant is that DevTools currently have to maintain their own registry for that. I advocate for a central registry of HTML elements and attributes in the engine. So DevTools could make use of that instead. And that central registry would also cater the feature detection used in CSS, which is discussed here.

I guess engines have an atom string registry for both tagnames / attributes, which can be re-used, but I don't think it's well standardized or consistent across browsers (whereas IDL is better standardized)

That is my point. To allow CSS to detect HTML features, the engines need to have some kind of registry it can use and check against. Additional standardization across user agents don't seem to be necessary, engines only need to ensure their registries are consistent and kept up-to-date.

Sebastian

@Loirooriol
Copy link
Contributor

I share Tim's concern about a registry. It's basically the same problem as in #3559 (comment), so I guess @tabatkins is also opposed to a registry.

@nt1m
Copy link
Member

nt1m commented Dec 24, 2023

That is my point. To allow CSS to detect HTML features, the engines need to have some kind of registry it can use and check against. Additional standardization across user agents don't seem to be necessary, engines only need to ensure their registries are consistent and kept up-to-date.

If engines already had a registry that they are already using I would be happy to leverage it, but the current registries that are available are not great. I still stand by my point that any mechanism used here should be required to be kept up to date by design.

Unfortunately, currently available atom string registries of attribute names / tag names are too vague. E.g. once switch gets added to the attribute registry, then h1[switch] will also return true. This is why I still think IDL is the best mechanism to leverage even if not extensive.

@SebastianZ
Copy link
Contributor

That is my point. To allow CSS to detect HTML features, the engines need to have some kind of registry it can use and check against. Additional standardization across user agents don't seem to be necessary, engines only need to ensure their registries are consistent and kept up-to-date.

If engines already had a registry that they are already using I would be happy to leverage it, but the current registries that are available are not great.

I obviously don't understand enough about the internals of the engines. Though I am wondering why engines e.g. have a consistent registry for CSS properties but not for HTML elements and attributes they can handle.

And a way to test for those features would pave the way for WPTs to test for them. And by having tests for all the different HTML features, we avoid the issue of implementations becoming outdated and push them towards introducing proper registries, so they don't have to manually maintain them.

I still stand by my point that any mechanism used here should be required to be kept up to date by design.

@nt1m Which other mechanism than the above could you imagine that allows for a comprehensive solution?
Should the HTML spec. maintain machine readable definitions for all HTML elements and attributes?

Sebastian

@SebastianZ
Copy link
Contributor

I share Tim's concern about a registry. It's basically the same problem as in #3559 (comment), so I guess @tabatkins is also opposed to a registry.

It's not exactly the same. There, the issue is that implementations would have to maintain contextual information, i.e. whether one property is interpreted in combination with another.
Here, the check aligns with the idea of "Does it parse?", as Tab expressed it. For @supports (grid-template-columns: subgrid) the engine needs to be able to parse the property and the given value. So the property name and its syntax need to be registered. For @supports html(input[switch]) it's similar.
And if it's not close enough to the "Does it parse?" principle yet, how about the following syntax?

@supports html(<input type="checkbox" switch>) { … }

Sebastian

@nt1m
Copy link
Member

nt1m commented Dec 25, 2023

I obviously don't understand enough about the internals of the engines. Though I am wondering why engines e.g. have a consistent registry for CSS properties but not for HTML elements and attributes they can handle.

This is because the HTML parser allows arbitrary name / value pairs of attributes, unlike CSS where invalid props won't actually parse.

WebKit has:

  1. a list of attributes it handles: https://searchfox.org/wubkat/source/Source/WebCore/html/HTMLAttributeNames.in
  2. a list of tagnames it handles: https://searchfox.org/wubkat/source/Source/WebCore/html/HTMLTagNames.in

(These can go stale if one gets removed, but accidentally stay in the list)

Potentially a mix of 2 + IDL lookup could work, but then any attributes that don't have an IDL equivalent might not work (and sometimes the IDL name doesn't match the attribute name too).

And a way to test for those features would pave the way for WPTs to test for them. And by having tests for all the different HTML features, we avoid the issue of implementations becoming outdated and push them towards introducing proper registries, so they don't have to manually maintain them.

I wouldn't fully rely on WPT to ensure this is up to date. It would really have to be something that's mandatory to update when you add a new feature.

@Loirooriol
Copy link
Contributor

@SebastianZ There is no registry for the supported CSS properties, browsers just check whether the parser accepts that. There is no promise of functional support, e.g. @supports (gap: 1px) doesn't imply that flex layout will use it, and @supports (content: "+") doesn't imply that it will work on a ::marker.

But in this case the HTML parser accepts arbitrary elements and attributes, so it's not as easy as checking whether the parser accepts it.

While IDL is good in regard of requiring to be updated, not all elements have an IDL associated to them, like e.g. the <ruby> element.

Not a big deal, the interface is HTMLElement, "support" for elements could be defined as getting an interface different than HTMLUnknownElement. Attribute "support" is trickier, since validation typically happens on IDL attributes, not content attributes.

Here, the check aligns with the idea of "Does it parse?"

The registry idea does not align with that. And <input type="checkbox" switch> parses perfectly fine even without support for switch.

@Crissov
Copy link
Contributor

Crissov commented Dec 26, 2023

Don’t authors usually just want to check whether the browser’s internal stylesheet for HTML (or SVG or MathML) contains any rule for the element or attribute they are querying?

@Loirooriol
Copy link
Contributor

@Crissov Not necessarily, lots of things are supported with internal magic instead of CSS, e.g. I'm not finding any CSS targeting the attribute maxlength on Gecko, Blink nor WebKit, but they support it on <input>.

@tabatkins
Copy link
Member

@nt1m tells me :has() doesn't support pseudo-elements

Not quite; the spec allows for :has() to take certain pseudo-elements (the :has-allowed pseudo-elements), but doesn't define any of the existing ones to be in that set.

The basic requirement for a pseudo to be :has-allowed is just that it's not dependent on styling, so you can't introduce cycles with it. Would you be able to shut off the switch pseudos, with appearance or something? If so, they can't be :has-allowed; if not, then they could be.

(The hidden extra requirement is that they're not "always there" either, like ::before, because testing for them would be useless in that case.)


Returning to the main topic, yeah, as much as possible we don't want to maintain registries, and want to instead rely on parsing to give us a correct answer automatically. We can probably pull that off with tagnames (based on IDL class, as @Loirooriol said), and likely with input types (they have their own behavior validator that recognizes valid names), but not with attributes in general. Every attribute we add would need special handling.

@annevk
Copy link
Member

annevk commented Dec 30, 2023

You could base it off IDL members, but I'm somewhat hesitant to build IDL testing into CSS.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-conditional-4] Need a way in CSS to test for support of HTML features, and agreed to the following:

  • RESOLVED: Add html function for testing both elements and attribute support
The full IRC log of that discussion <frances_> Alan: next is testing support for html features
<florian> q+
<frances_> Jen: apple recently put together along with the html group, switch input type, won't use checkbox by default for the switch, common for developers to want to use native switch control and use custom styling on it. Great for developers to use conditional @support statements. Some work arounds for pseudo-elements, is hacky and not long term. Make form controls more stylable and test support for html elements and attributes and put them in support s[CUT]
<TabAtkins> q+
<fantasai> proposed syntax - https://github.com//issues/9746#issuecomment-1867929357
<frances_> Jen: Mostly the issue is how can it be done technically. fantasai has more information on the syntax. What in the browser engine could css look at to get accurate results for this kind of a test?
<astearns> ack florian
<frances_> florian: implied in what was suggested, is very valuable important design consideration. Went directly through css syntax to not get out of sync. How to test in a way to not be out of sync in support?
<astearns> ack TabAtkins
<iank_> Fallback on non-supporting browsers will just be an input vs. a checkbox if an author writes `<input type="switch">` I believe. (fallback doesn't work on attributes)
<frances_> tab: same that florian said, use case is valid, would require registries, might not be workable. Input types have a handling path to recognize input types, element names are testable in this way, for outside explicit is fine, a :input[switch] pseudo-class
<astearns> ack florian
<frances_> tab: can do a handful of automatic protections. For broader case, targeted pseudo class to target exposure. Might not reasonably be able to do it.
<astearns> ack fantasai
<fantasai> HTMLInputElement.prototype.switch
<frances_> fantasai: what was mentioned for element support, specific subclasses, could check whether the prototype exists like html.prototype.switch exists for internal processing for the attributes. Might only take valid attributes, browser doesn't know right now,
<frances_> Ian: trouble with many attributes, not a simple registry of possible values. Alot of internal processing in the attributes.
<frances_> Alan: could be based off of ideal numbers.
<astearns> s/ideal/IDL/
<frances_> Jen: hoping to create reusable pseudo-elements, not one-offs
<TabAtkins> I mean I'd *like* to have a generic mechanism. Just not seeing a way to do that that's not registry-based.
<frances_> Alan: possibly work for all attributes, compelling use case for the attribute. Start with the case and then see if we can come up with a more generic attribute solution in the future.
<frances_> Alan: any other ideas for testing attribute support generally?
<frances_> florian: don't know enough about it internally, html parses everything. Plug in at right level of the parser, will accept anything to parse, similar to the html. Is there a layer in the parser that is currently not exposed?
<frances_> florian: syntax edit
<frances_> fantasai: happening at level above the parser about the elements and attributes even if they are unknown. Won't know correct DOM attributes if you don't recognize them. Recognition is reasonable.
<TabAtkins> Not all valid and processed attributes are reflected as IDL properties.
<khush> q+
<frances_> tab: Don't have an example, not all attributes are reflected as ideal properties, anything can go in the element attribute set, ideal testing will often work, but there is a bit of a confusion like input-value. There is going to be some things that don't work anyway.
<TabAtkins> s/ideal/IDL/
<frances_> Alan: if we go with this, people might find it useful where elements are not reflected as IDL properties, specified browser with attributes.
<TabAtkins> I *would* expect it to work all the time, yeah.
<frances_> Jen: @supports add a switch, test popover, do we need to test and see if it works all the time?
<frances_> Alan: put it in place for people to use in set of reflected properties possibly
<fantasai> https://github.com//issues/9746#issuecomment-1867929357
<frances_> Ian: Are all objects defined in the IDL?
<frances_> fantasai: The backing and question of use of supports hooked up to IDL level or not. Possibly not have IDL function and stuff in there. More natural for authors for input values to map.
<TabAtkins> Tho remember the request here isn't for "is this attribute supported" but rather "is this attribute *value* supported", and that's Ian's larger concern that many attributes make that more complicated
<frances_> Ian: possibly test for webGPU device
<fantasai> html(rb)
<fantasai> html([dir])
<fantasai> html(input[type])
<fantasai> html(input[type=color])
<frances_> fantasai: scoped to elements, some kind of a function like markup and a tag name and reflective syntax for it, and test.
<frances_> fantasai: It is the direction I'd like to go in and be limited to the support for each attribute in the elements.
<TabAtkins> Like, `<input type="foo">` is a *supported* value. It's just treated identically to "text".
<frances_> florian: Go through ideal later possibly to see if it is matched.
<khush> q-
<frances_> Jen: alot of the behavior is not reflected in just HTML, still need some conditional ability to style and it needs to move into CSS, not going to use webGPU without Javascript
<frances_> Ian:good to investigate in custom elements as well, create some custom element with API attributes, might not capture some magical attributes not in the prototype
<fantasai> TabAtkins: but that returns 'text' if you get it
<fantasai> https://www.software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%0A%3Cinput%20type%3Dfoo%20id%3Dtest%3E%0A%0A%3Cbutton%20onclick%3D%22w(document.getElementById(%27test%27).type)%22%3Eclick%3C%2Fbutton%3E
<frances_> Alan: any other comments?
<fantasai> s/TabAtkins:/TabAtkins,/
<TabAtkins> Sure, but if your test is "does this round-trip identically", then other types of canonicalization of *understood* values won't work either
<frances_> PROPOSAL: A straightforward way to check for html element support to specify elements.
<frances_> for specified elements*
<frances_> Alan: break into resolvable checks, break into a resolution, and do elements today, see if we can resolve on attributes as well later
<frances_> Jen: If we do elements now as a step along the process for the attributes, yes let's do it, if there is a chance for it to break, no use case for the elements
<frances_> PROPOSAL: Check if HTML elements are supported
<frances_> tab: there is no reasonable use case for attribute values
<frances_> florian: what do we mean by html elements supported? such as you must parse, what about deprecated values and elements?
<TabAtkins> s/there is no reasonable use case for attribute values/weakly objecting, no use-case has been presented for testing for element names yet/
<frances_> Alan: need to resolve, any objections?
<TabAtkins> happy for "try to work it out in an Ed"
<frances_> tab: if we can figure out a way to do it
<frances_> RESOLVED: Add html function for testing both elements and attribute support

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

10 participants