CSS nesting specification


#11

I’m feeling this one most:

table {
  <
    td {
      color: pineapple;
    }

    th {
      color: seaweed;
    }
  >
}

And IDK why. But I really don’t mind the curly braces.

Here’s another idea:

Perhaps we could have something that declares a part of the stylesheet to allow this but it’ll will have to be done by whitespace (Which most people will probably not like, because of minification):

@deepSelecting {
  table
    td
      color: pineapple;
    th
      color: seaweed;
}

#12

Sure, but I feel CSS should be readable in plain text format (it always has been). Why rely on an IDE if a different character achieves the same thing?


#13

Here goes for the polyfill: https://github.com/jonathantneal/postcss-nesting


#14

After experimenting today lots, I think I am still seeing the { syntax as the easiest to read. I use zero formatting and highlighting in vim and as @leaverou said I’m a ‘extreme outlier on this’; but if I can cope most people can.

I would potentially suggest making it an @rule too (However this adds another type of @rule).

So:

.thing {
  @matches {
    &:hover {
      color: blue;
    }
    .thing {
      color: red;
    }
  }
}

Would be:

.thing:hover {
  color: blue;
}
.thing .thing {
  color: red;
}

@jonathantneal Fancy pollyfilling @tabatkins’s @extend syntax too :stuck_out_tongue: I looked at how feasible it was today and certainly seems easy enough.

I wanted it so I could easily use nested custom properties within a fake shadow DOM so the risks of it not cascading went away.

If not I was going to have a go tonight anyway :smiley:

PS: The smilies here are still creepy.


#15

I think current @extend proposal isn’t doable per tabatkins comment on this thread https://github.com/postcss/postcss/issues/321


#16

@MoOx reading the spec it seems to be doable in certain circumstances but perhaps I don’t understand. For example #id's would need to be turned into [id=...].

However for the tiny use case I want it for it works: Custom property selectors


#17

Taking this in a slightly different direction, here’s an alternate syntax proposal I’ve been mulling over and think I like.

The only reason we need anything special is because the grammar for declarations and selectors are ambiguous; specifically, a selector starting with a type selector and a pseudo-class looks just like a declaration, and you can’t tell which it is until you hit a curly or a semicolon.

If you can avoid that case, you’re golden. And the common case for nesting is to start the selector with the nesting selector. So:

  1. As long as you’re starting the selector with the nesting selector, you can nest it directly inside of the parent rule. No Nesting Block or anything else needed. (Note: if you use a comma-separated list of selectors, all of them will have to start with the nesting selector, even though disambiguation has already technically been achieved by the first selector in the list. If you don’t have this restriction, it’s a refactoring hazard; re-arranging selectors can break code.)

  2. If you want to use the nesting selector anywhere else inside of a selector (like .foo & to add extra styles only when it’s within a .foo ancestor), you have to add an @nest rule inside of the parent style rule, which then contains style rules with no restriction on & placement.

So in other words, these would be the two allowed syntaxes:

.foo {
  color: red;
  & .bar { color: blue; }
}

/* equivalent to
   .foo { color: red; }
   .foo .bar { color: blue; }
*/

.foo {
  color: red;
  @nest {
    .bar & { color: blue; }
  }
}

/* equivalent to
   .foo { color: red; }
   .bar .foo { color: blue; }
*/

But something like this is illegal:

.foo {
  color: red;
  .bar & {
    color: blue;
  }
}

#18

@nest is very intuitive as that was actually my goto for @matches above but I thought that would keep it in keeping.


#19

The only slight problem with @nest is that it doesn’t convey descendants.

So:

.foo {
  color: red;
  @nest {
    .bar { color: blue; }
  }
}

Would equal:

.foo { color: red; }
.foo .bar { color: blue; }

or:

.foo { color: red; }
.foo.bar { color: blue; }

Which the convenience of multiple lines would be out there wouldn’t it.


#20

Only two I can think of:

@decendants, @children

@jonathank I’m assuming you want @something { .bar {} } more than & .bar?

I’m not sure (I’m asking) is it easier to implement than & .bar or being easier is irrelevant. If it’s irrelevant & .bar is less code so?


#21

You’d still be required to use the nesting selector inside of @nest, for precisely the lack-of-clarity issues you raise. Your example would thus be invalid. You’d have to write either & .bar or &.bar.


#22

@Edwin_Reynoso unless the & .thing case @tabatkins just mentioned could be removed then I don’t see it as possible.

.thing {
  @nest {
     .thing {...}
  }
}

Could mean:

.thing .thing {...}

And:

.thing {
  @nest {
     &.thing {...}
  }
}

Could mean:

.thing.thing {...}

Which if possible I would probably prefer @nest for multiple selectors, if not then the use-case is pretty slim in my opinion.


#23

Ampersand syntax looks nice, but how will it handle nested @rules? Or are nested @rules—like @media—not supported?

I do however feel that the ampersand syntax creates inconsistencies. Depending on whether you want to pre- or suffix your selector, you’ll have to use the @nest rule. A nesting block prevents this, be it curlies or another character.


#24

Ampersand syntax looks nice, but how will it handle nested @rules? Or are nested @rules—like @media—not supported?

Nothing special needs to be done for nested at-rules. As I said before, the sole grammatical ambiguity is that a selector starting with a tagname followed by a pseudo-class looks like a declaration. Anything else is fine, including at-rules, which start with an at-keyword token. (To handle nested at-rules just needs some additions to the CSSOM, to add a .childRules property to the CSSStyleRule interface.)

I do however feel that the ampersand syntax creates inconsistencies. Depending on whether you want to pre- or suffix your selector, you’ll have to use the @nest rule. A nesting block prevents this, be it curlies or another character.

The @nest rule will allow you to use any nested rule, whether the & appears at the start or elsewhere, so if you want a simpler model, feel free to use it all the time. (It’s identical to the Nesting Block, after all, just with the five extra characters of “@nest”.) The idea is just that, in the common case where you start with &, you don’t need to pay the extra cost of the wrapping block.


#25

From what I am reading @tabatkins my example above is matching your thinking.

So:

  • .thing { @nest { .thing {
    • is .thing .thing {
  • .thing { @nest { &.thing {
    • is .thing.thing {
  • .thing { @nest { &:hover {
    • is .thing:hover {
  • .thing { &:hover {
    • is .thing:hover {

If that is the case, I’m very very happy.


#26

No, I’ve explicitly contradicted that:

You’d still be required to use the nesting selector inside of @nest, for precisely the lack-of-clarity issues you raise. Your example would thus be invalid. You’d have to write either & .bar or &.bar.

So your first example is invalid, and needs to be written .thing { @nest { & .thing {. But the rest are valid and correct per my intentions, yes.


#27

I thought you had just wanted to confirm as I thought you were saying the opposite with your last comment.

The &space could be implied when in @nest, this is how LESS works which seems fairly intuitive and only really required for pseudo and more specificity.

Thanks


#28

You argued in http://discourse.wicg.io/t/css-nesting-specification/839/22 that that very syntax was confusing, because it wasn’t clear which the author meant. I agree with you; I’m unsure why you’ve reversed your position or what your new reasoning for it is.

Nesting without an & is confusing, because it’s not immediately clear whether you mean to extend the last compound selector or start a new one. It’s also bad because it defaults to the descendant selector, which is relatively slow; we shouldn’t be making that the easiest case.


#29

Yeah perhaps I didn’t really make my point clear at all, I was stating the decisions needed with the syntax rather than it was actually a show stopper.

If it was defined in the spec as it soon wouldn’t become confusing I feel.

I’m happy for either case to be a default, it seems that a descendent selector would be more common however I get the hesitation to do neither also.

It seems that besides when nesting a selector within a new parent selector @nest has limited use otherwise.

My use case personally is for defining custom elements and all the behaviour and elements within them. So the @nest with descendant selectors seems a very common case in the files to me.


#30
thing { &:hover {
// equal
.thing:hover { 

it seems weird to accept that and reject

thing { .parent:hover  & {
// equal
.parent:hover & .thing{ 

@tabatkins I get your argument about syntax issues, so I would go for only supporting @nest + mandatory &. KISS.

For media queries can you confirm my expectations:

.selector {
  @media whatever {
    @nest {
      &:hover {
        ...
      }
    }
  }
}
// equal
@media whatever {
  .selector:hover {
    ...
  }
}