W3C home > Mailing lists > Public > www-style@w3.org > April 2015

CSS selector to better match label elements

From: Manuel Strehl <svg@manuel-strehl.de>
Date: Thu, 30 Apr 2015 00:13:52 +0200
Message-ID: <554157A0.8040801@manuel-strehl.de>
To: www-style@w3.org

I've been pondering a while over the following suggestion for a new
selector and, to my surprise, still think it is a necessary tool that is

<label> elements are notoriously hard to select with CSS in any
non-trivial mark-up structure, when the state of their control is to be
taken into account. Consider these frequent cases, where it is not
possible to reach the <label> element with CSS selectors, when the
corresponding <input> state is relevant:

1. <label><input> Input inside label</label>

2. <tr>
    <td><label>Good ol' tables</label></td>

3. <label>Label before input</label><input>

If you want to make the label react to the state of its associated
control, you need to rely on JavaScript to generate classes to "listen"
to. Only example (1) would be addressed by a parent selector
<http://www.w3.org/TR/2011/WD-selectors4-20110929/#subject>, e.g.,
`$label input`. Reference combinators
<http://www.w3.org/TR/selectors4/#idref-combinators> only work the other
way round, to address the control from its label: `label /for/ input`.
Case (3) is the inverse of `input + label`.

A clunky way to address a label from its input is to use :has()

    :root:has(input#foo:invalid) label[for="foo"]

But it relies on the knowledge of the ID of the <input> element, if it
has any. Case (1) above is then ruled out.

In many cases, the control element is partially or fully hidden, and the
label acts as proxy to interact with the control, loved by designers for
its independence from OS styles and ability to use `::before` and
`::after`. In certain constellations, e.g.,
handling this situation is already possible today with CSS only. Matt
Mastracci played a bit with the thought of using CSS Extensions to
expand on this:
However, robust solutions would most probably still rely on JavaScript.

So I come to... ***drumroll*** ...the proposal:

A pseudo-class :for() can provide better control over addressing <label>
elements. Its content must be a single selector or selector list
(mirroring :not()), that is matched against label.control. The
specificity of :for() is the specificity of its arguments. Examples:

    label:for([type="text"])             /* all labels with
label.control.type === "text" */
    label:for(:invalid)                  /* all labels with
label.control.invalid === true */
    label:for(:focus)                    /* label.control ==
document.activeElement */
    label:for(:disabled):for(read-only)  /* chaining :for(): labels for
disabled read-only inputs */
    label:for(#foo)                  /* label, that refers to the input
#foo */

If a <label> has no corresponding `control`, it will never match a
:for() selector. It could be matched by :not(:for(*)).

The :for() selector does not rely on any ID reference but on the
algorithm to find the control for a label outlined in
<http://www.w3.org/TR/html5/forms.html>. In later specifications it
could be extended to also match references defined in another way, e.g.,
"reverse lookup" via aria-labelledby or a[href^="#"].

I assume there will be headaches for implementors, since calculating the
relation might not be cheap. However, the gain in possibilities
justifies IMHO to at least consider, what concrete drawbacks an
implementation would yield. The :for() is initially scoped to <label>
attributes only, which should be an advantage in implementations.

What do you think?

Received on Wednesday, 29 April 2015 22:14:16 UTC

This archive was generated by hypermail 2.4.0 : Friday, 25 March 2022 10:08:53 UTC