Can CSS identifiers begin with two hyphens?

3.7k views Asked by At

CSS 2.1 defines identifiers as

In CSS, identifiers can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+00A0 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit. Identifiers can also contain escaped characters and any ISO 10646 character as a numeric code.

Therefore, -- should be an invalid identifier, and thus #-- shouldn't select the element with id="--":

body { color: black }
#-- { color: red }
<p id="--">I should be black.</p>

In attribute selectors,

Attribute values must be identifiers or strings.

But -- seems to work as an identifier for attribute values, at least on Firefox:

body { color: black }
[id=--] { color: red }
<p id="--">I am red on Firefox.</p>

Moreover, CSS.escape has been modified to allow --:

Minor changes has been made in Firefox 32, to match the spec and the evolution of the CSS syntax. The identifier now can begin with -- and the second dash must not be escaped.

According to the Wayback Machine, the change happened between 19 and 30 Apr 2014:

  • 6 Feb 2014 editor's draft, stored on 19 Apr 2014.

    If the character is the second character and is "-" (U+002D) and the first character is "-" as well, then the escaped character.

  • 30 Apr 2014 editor's draft, stored on 4 May 2014.

    If the character [...] is "-" (U+002D) [...], then the character itself.

So, has some new CSS3 module changed the definition of identifiers, so that they can sometimes begin with --, or what exactly is happening here?

2

There are 2 answers

1
Oriol On BEST ANSWER

Standard

Effectively, a change in CSS Syntax Module now allows identifiers to start with two hyphens:

4.3.9. Check if three code points would start an identifier

Look at the first code point:

  • U+002D HYPHEN-MINUS

    If the second code point is a name-start code point or a U+002D HYPHEN-MINUS, or the second and third code points are a valid escape, return true. Otherwise, return false.

This change appeared in the 21 Mar 2014 Editor's Draft (changelog), and is still not present in the current Candidate Recomendation, which is the 20 Feb 2014 CR.

It's also described in Changes:

11.1. Changes from the 20 February 2014 Candidate Recommendation

  • Change the definition of ident-like tokens to allow "--" to start an ident.

Reason

In www-style, the ...let's change the syntax thread proposed to change the syntax of CSS Variables:

  1. Tab Atkins Jr. proposed changing the syntax of Custom Properties to "any ident starting with/containing an underscore".
  2. Chris Eppstein disagreed because _property is a common IE6 hack.
  3. Brian Kardell proposed --.
  4. Zack Weinberg warned:

    Unfortunately, "--" would require a change to Syntax. IDENT isn't allowed to start with two dashes in a row.

  5. There was a long discussion of what should be done.

  6. Tab Atkins Jr. informed they resolved to use a -- prefix to indicate custom properties and other custom things.

So the following day he commited the change of CSS Syntax to github (he is an editor of the spec).

Implementations

Firefox

Firefox allows identifiers to start with -- since Nightly 31 2014-04-03 (pushlog). The behavior was changed in bug 985838:

Bug 985838 - change var- prefix of CSS Variables to --

Recently decided changes to the CSS Variables spec:

  • the prefix of the custom property change from var- to --
  • inside the var() you use the full custom property name (i.e. with the -- prefix)
  • a custom property consisting only of the prefix, --, is allowed
  • idents in the CSS parser now allow things like -- and --0

The change landed on Firefox 31.0. Since then, [id=--] works.

However, #-- still does not work even on latest Nightly 41. I filed bug 1175192 in order to fix that.

Chrome

Chromium built a new CSS parser:

We now allow idents to start with --

It was shipped in this commit, which was part of this commitlist, which was rolled in this commit. So it was finally implemented in build 44.0.2370.0 325166 (pushlog since 325152).

Since then, Chromium allows both [id=--] and #--.

5
BoltClock On

I'm not sure if there's a definitive answer to this, but for what it's worth, double hyphens make an appearance in the CSS Variables module, which defines custom properties. Here's an example (currently only works in Firefox, which may be the reason why it works there in CSS.escape()):

:root { --color: red; }
p { color: var(--color); }
<p>I am red on Firefox.</p>

See CanIUse.com for current status of browser support of this feature: CSS Variables (Custom Properties).

While css-syntax-3's definition of identifiers seems consistent with that of CSS2.1, it does make a number of references to css-variables itself. None of those references seem to address the -- prefix used by custom property names, however.

css-variables itself says:

A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo. The <custom-property-name> production corresponds to this: it’s defined as any valid identifier that starts with two dashes.

The last statement is particularly interesting, because the only way to interpret it that doesn't conflict with the definitions given in CSS2.1 and css-syntax-3 is one of vagueness: "identifier that starts with two dashes" can either mean that the two dashes are not part of the identifier, i.e.:

<custom-property-name> = '-' '-' <ident>

Or they are, which would mean that custom properties are exempt from the general definition of an identifier. It doesn't help that the grammar for the <custom-property-name> production is nowhere to be found, not in css-syntax, nor in css-variables, nor in CSSOM; that prosaic statement is the only definition available.

Of course, none of this explains why #-- is recognized as valid by Chrome, especially since Chrome doesn't have a working implementation of custom properties.