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

Make <label> elements reflect CSS pseudoclasses on associated form element #1632

Open
AmeliaBR opened this Issue Aug 5, 2016 · 20 comments

Comments

10 participants
@AmeliaBR

AmeliaBR commented Aug 5, 2016

There are a number of CSS pseudoclasses that reflect state of labelable elements. This includes the standard interactive pseudoclasses available on all elements (:hover, :active, :focus) and also form-specific pseudoclasses (:required, :optional, :invalid, :valid, :enabled, :disabled, :checked, :selected, :indeterminate, :default, :placeholder-shown, :in-range, :out-of-range, :read-only, :read-write).

The purpose of these pseudoclasses is to make it easy for the visual presentation to reflect the state. However, because there are only limited styling options available on most form elements, it is often desirable to convey the state by styling the label, instead, possibly using generated text content. For example, it is common to indicate a required form field with a red asterisk after the label.

Currently, in order to reflect the form element's state in the label with CSS, you must re-arrange the DOM order so that the label is a subsequent sibling in the tree (because CSS selectors only operate in tree order). For example, to create the "required" asterisk, you would need:

<input required id="t" />
<label for="t">Important info</label>
input:required + label::after {
  content: " *";
  color: red;
}

The HTML spec already requires that activation and hover interactions on the label should trigger the :active or :hover state on the control. However, the reverse effect only happens if the control is a descendent of the label.

In November 2014, the CSS WG resolved:

Both :hover and :active should propagate from the labeled control to the label, in addition to the other direction.

and also

reuse the hook from :active on :focus [meaning that :focus would also reflect the same relationships].

Florian Rivoal reports that this resolution stalled at the WHATWG and nothing went forward.

I would strongly urge the HTML spec to require that implementations reflect all pseudoclasses from a labelled input onto the associated label element, for the following reasons:

  • It allows markup and styles to be independent; the DOM structure does not need to be constrained to allow styling.
  • It encourages correct use of the <label> element, enhancing accessibility.
  • It encourages correct use of form element attributes to indicate state, enhancing accessibility.
  • It would reduce the use of sibling CSS selectors, which are problematic for performance; in some "checkbox hack" designs, it would reduce the number of total selectors considerably (because you wouldn't need a separate selector for each input/label pair).
  • It would make developers lives much easier (once browser support catches up).

There may be reasons to make limited exceptions for certain pseudoclasses, such as :defined, where it could have a valid but distinct meaning on the label versus the control. But for the most part, the semantics of the label are the semantics of the element it describes.

Edit: On re-reading Florian's email, it seems he did not mention anything about W3C HTML WG, so I'm not sure why I was thinking he did. False claims removed!

@bkardell

This comment has been minimized.

bkardell commented Aug 5, 2016

From what I could find, the most compelling argument against came from @bzbarsky . I find that convincing and I understand the concern, but this is about hover and things that might be spammy. This is what led to my suggestion on www-style that excluding the potentially spammy things and re-proposing a simpler starting point - things like :focused and :checked specifically seem to be considerably rarer and easy to deal with and have the advantage of opening a host of new potential patterns for developers.

@beattyml1

This comment has been minimized.

beattyml1 commented Aug 6, 2016

This seems extremely useful and very naturally expressive on the one hand but seems to go against predictable structure on the other. I think most of the value would be from things like required/optional, valid/invalid, read-only. The things like hover, focus, and active both seem the least useful an the most likely to cause ambiguity. Perhaps it could only apply to psuedo-classes that a label itself cannot have? This would give most of the value without the ambiguity problem but introduces a slight consistency issue.

@AmeliaBR

This comment has been minimized.

AmeliaBR commented Aug 6, 2016

I agree that for :hover the use-case versus implementation-hassle ratio may be worth excluding it, although the fact that browsers already handle the reverse relationship suggests that it's not insurmountable.

:active and :focus would be useful for cases where the actual input element is offscreen and the styled label is being used as the visible widget (e.g., a checkbox being displayed as a toggle button); you still want keyboard focus & activation to be reflected in the styles.

@domenic

This comment has been minimized.

Member

domenic commented Aug 6, 2016

I don't have a strong feeling for or against this. (I guess as a web developer, I think a better solution would be for :has() to work in both selector profiles, but I've been told many times that is not something implementers are interested in.)

However , we had two implementers with objections in the linked thread, @bzbarsky and @rniwa. We can't put something in the standard with such implementer objections standing; we in fact need at least two implementers who are prepared to concretely commit to implementing it, and no strong objections.

So, if people think this is a valuable change, the best thing they can do is to try to overcome the expressed implementer concerns, and furthermore to convince implementers to put it on their implementation roadmap. It sounds like some implementer representatives (from the CSSWG) in other parts of those engines would like this feature, so maybe there are some backchannel discussions to have there. Or people can use this bug to try to present their arguments, if they feel that's a productive use of their time.

In any case, I'll tag this appropriately, and we'll see where the discussion goes. But I want everyone to be realistic about the chances of changing things here, and how the process works; we don't just standardize things and then the implementers must implement it :)

@frivoal

This comment has been minimized.

frivoal commented Aug 8, 2016

However , we had two implementers with objections in the linked thread, @bzbarsky and @rniwa.

Yes, they did push back. What makes this murky is that the same proposal had support from representatives of the same companies in the CSSWG.

@bzbarsky's argument was based on the performance problems. While it is true that runtime cost of doing this from the button to the label is higher than in the other direction, this isn't overwhelming:

  • Internet Explorer 10 and 11 (not Edge) implement it already (on :active and :hover), and performs just fine even in artificially heavy scenarios (see here http://jsbin.com/jogita/edit?html,css,output), proving it can be done effectively
  • Multiple people (and I'll join them) have stated that :hover is not the most compelling of all the pseudo classes that could be supported, and if that's the blocker due to perf, we could drop this one and keep the rest.

As for @rniwa's argument, I don't think it holds up, because he bases his rejection on “[...] this use case can be solved by the proposed :has() [...]”, which isn't true, since :has() is explicitly specified not to be available in “dynamic browser CSS selector matching”.

@frivoal

This comment has been minimized.

frivoal commented Aug 8, 2016

[...] convince implementers to put it on their implementation roadmap. It sounds like some implementer representatives (from the CSSWG) in other parts of those engines would like this feature

@gregwhitworth: MS implemented button->label(s) matching of the :hover and :active states in IE 10 and 11, and during the discussion in the CSSWG, supported making that the standard behavior. As far as I can tell, this has been removed from Edge, though. Do you have any insight on why? Did you do that to align with the spec since the spec wouldn't align with you, or did you find some independent reason why this was problematic?

@LeaVerou

This comment has been minimized.

LeaVerou commented Aug 8, 2016

The author part of me wants to shout "OMG yes, :checked on <label> would enable me to do so many things!!"
And there might indeed be some use cases of legitimately doing that. After all, :checked must be used in conjunction with something, since checkboxes and radios can't be styled much.
However, most of it is making the checkbox hack easier. So, it might be better to start discussing toggleable controls more seriously? Although, toggleability should be reflected in the HTML somehow, so perhaps the checkbox hack is not a hack after all?

@craigfrancis

This comment has been minimized.

craigfrancis commented Aug 8, 2016

:hover might be useful for accessibility reasons... e.g. someone who needs 200x zoom and/or has poor motor control, who is trying to click on a tiny check box (if the website visually changes the input and label on hover, that feedback lets the user know that clicking will check/uncheck the field).

@BigBadaboom

This comment has been minimized.

BigBadaboom commented Aug 8, 2016

Would it be a feasible alternative to instead have a new combinator that works no matter how the label is applied? Eg. something like

input:checked @ label {
  font-weight: bold;
}
@LeaVerou

This comment has been minimized.

LeaVerou commented Aug 8, 2016

@BigBadaboom We used to have a combinator for this, but it was dropped for reasons I don't remember.

@bkardell

This comment has been minimized.

bkardell commented Aug 8, 2016

@LeaVerou I think you are thinking of 'friend' or the 'idref combinator' - discussions started in like '98 on that one and it changed names a few times. It's complicated, but I don't think that it would really have done exactly this.

@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Aug 8, 2016

Sorry in advance for the long and somewhat side-issue comment, but one part of the discussion here raises a question that seems to generally be under-considered in working group deliberations...

What makes this murky is that the same proposal had support from representatives of the same companies in the CSSWG.

Layout engines and performance are complicated; none of us are infallible. If @dbaron thinks this can be done reasonably, there's a good chance he's right. In fact, I expect it can be done reasonably, for some values of "reasonably".

The basic performance cost here, in addition to the issues of propagating the change and dealing with non-local :hover changes, which are mostly a matter of exploding rendering engine complexity to keep performance sane, is slowing down all DOM mutations (maybe only in DOMs that involve <label> elements) somewhat to introduce this feature. That's because the only sane way to do this feature is for the control to keep track of its labels as the DOM mutates; anything else is just ridiculously slow. One could argue that the HTML spec has already taken that plunge with the .labels attribute on various interfaces, which is not actually universally implemented in UAs, note. Also note that this is a cost that is generally not fatal in any one given case but the failure mode is that we make mutation complicated and slow enough that people start building things like React to run a huge chunk of script just to avoid DOM mutations because they can't trust those to be fast.

This is a general problem in the design of web specifications. There are lots of neat features we can add to the web platform (this one, display: run-in, etc). The benefits are pretty clear to the people advocating for them. The cost, in almost all cases, is some incremental performance degradation and some incremental complexity of implementation. If you do this for one feature, it's all fine. If you do this for 100 features, especially interacting ones, you get horrible performance and super-complex implementations that make it very difficult to add anything else to them. Then people complain that implementation of seemingly simple things (that just happen to interact with a few hundred other seemingly simple things) takes too long.

So really, this is a question about tradeoffs. Which other things are you willing to give up, either for a few years due to slower implementation or permanently because no one can figure out how to make them work, to get a feature which increases complexity? How much performance degradation is OK before people just give up on the DOM entirely? Unfortunately, isolated working groups, and even individual rendering engine developers, are not typically well-positioned to think about these tradeoffs in the large. So we all stumble along, until a few years later we look back on the state of the world and wonder why everything is so slow and messy.

None of these problems are specific to the Web, by the way; any complex software system will suffer from these sorts of problems. The Web somewhat exacerbates them by virtue of size and explicitly distributed decision-making.

Note that it's not clear to me that suggested replacements for the proposed functionality, like :has or the idref combinator are better on the whole "added complexity" front. I only see two ways of avoiding the problems of extra complexity and incremental performance degradation:

  1. Build your thing on top of already existing bits of complexity. From this point of view, for example, the existing .labels attribute may be a great boon, because the cost of maintaining the control-to-labels association is a sunk cost anyway. But even there it's not obvious in terms of the performance angle: I can see UAs computing .labels lazily only if someone has poked it once or something, which is almost certainly not good enough for the feature we're talking about here, since that would poke it for all sorts of inputs as you move your mouse.
  2. Come up with a setup where the cost of niche features is borne only by their consumers. This is the premise of the Houdini work, and has its own complications: the complexity of the whole Houdini system, and the fact that the bar for making use of a feature suddenly becomes much higher; instead of being built in, it is at best a library you have to hunt down and at worst something you have to implement from scratch yourself.

No easy answers here, I'm afraid.

@bkardell

This comment has been minimized.

bkardell commented Aug 8, 2016

@bzbarsky does my suggestion from www-style which launched this discussion that we just do it for the rare event things like 'checked' and 'focus' help at all in this regard? it definitely seems simpler/less involved than things that could happen frequently or at any time like 'hover'?

@bzbarsky

This comment has been minimized.

Collaborator

bzbarsky commented Aug 8, 2016

It's hard to say; it depends on whether someone then writes a benchmark that toggles checked state on thousands of checkboxes really fast, thus forcing UAs to optimize this case anyway. :(

@frivoal

This comment has been minimized.

frivoal commented Aug 9, 2016

Layout engines and performance are complicated; none of us are infallible. If @dbaron thinks this can be done reasonably, there's a good chance he's right. In fact, I expect it can be done reasonably, for some values of "reasonably".

If my recollection is right, @dbaron did not show strong interest for this feature, but he did participate in the discussion, thought implementation would not be particularly problematic, and was persuaded (even if not enthusiastically so) that this was worth having.

Note that it's not clear to me that suggested replacements for the proposed functionality, like :has or the idref combinator are better on the whole "added complexity" front.

As far as I understand, :has() at least is significantly worse on the performance front. I haven't heard any specific argument about the idref combinator, but it seems unlikely to do better than this proposal if we craft it so that it can solve the same use cases.

[paraphrase: it's a trade-off between expressiveness and performance&complexity / performance&complexity needs to be protected from death by a thousand paper cuts]

True. I'll openly admit a personal bias in favor of expressiveness over performance (within reason), but I understand different people are at different points along that spectrum.

All in all, I think it is a case of "can be done, is it worth the cost?", rather than a case of "cannot be done".

@frivoal

This comment has been minimized.

frivoal commented Aug 17, 2017

The css-wg has (re)discussed this recently, and still think bidirectional propagation between labeled form controls and the corresponding label would be good.

To the two reasons advanced against it by the whatwg:

  • :has() is not a viable alternative. It is explicitly specified not to be supported in css.
  • The performance problem raised by @bzbarsky mostly affects :hover, since it is the only one of these selectors which is likely to change quickly. This is also the pseudo for which this bidirectional propagation is considered least useful, so the csswg would be ok with having bidirectional propagation on other pseudos but not :hover. Also, as mentioned in #1632 (comment), the complexity cost is a sunk cost.

So, all in all, while we realize the ball is in the whatwg camp, and that this is for you rather than us to define, our collective input remains that supporting this would be a good idea.

@domenic

This comment has been minimized.

Member

domenic commented Aug 17, 2017

Thanks for the update @frivoal. After refreshing myself on this issue, it seems like the main TODO is still in the camp of various CSSWG representatives from Apple and Mozilla to convince @rniwa and @bzbarsky to withdraw their implementer objections. Was there any progress on that front?

Per #1632 (comment), I think we're generally happy to do this if there is implementer interest.

@frivoal

This comment has been minimized.

frivoal commented Aug 17, 2017

I would like to hear back from @rniwa. My (extremely summarized) understanding of his argument was "we do not need this because we have :has()". However, we do not have :has() in CSS. So either I am misunderstanding something about @rniwa's point, or his objection seems to stand on shaky ground.

As for @dbaron (or other mozillians) trying to convince @bzbarsky, I do not know if this discussion has occurred.

@LeaVerou

This comment has been minimized.

LeaVerou commented Aug 17, 2017

Even if we did have :has() in CSS, it doesn't cover all cases of labels. :has() wouldn't help with
labels linked via for, only the ones linked via nesting.

@rniwa

This comment has been minimized.

Collaborator

rniwa commented Aug 17, 2017

@bzbarsky raises many good points.

The only reason I mentioned :has() was because I thought :has() was coming to CSS, and it would address some use cases. Even if :has() wasn't added to CSS, the extra implementation complexity required to implement this feature without degrading the performance, if at all possible, doesn't change.

The biggest question is if this feature really provides enough benefits to justify the extra complexity in browser engines as well as relevant specifications.

For example, this feature would require the style engine to be able to check the state of another element in order to figure out whether a given pseudo element applies, and that each label element be able to efficiently find its associated form control element. Neither exists in WebKit, and implementing them would require a serious engineering effort. That's the engineering resource that could be diverted to implement other features and fix bugs elsewhere.

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