CSS nesting specification


#1

Is there an interest into dragging this into the limelight again? (I feel I raised this on here before but can’t find where)

The spec written by @tabatkins http://tabatkins.github.io/specs/css-nesting/

I thought it would be worth bringing up again as there is interest in implementing this into CSSNext:

I think the biggest change would be around formatting, example 5 uses:

  div {{
    .keyword {color: red;}
    &:hover {background-color: rgb(200, 255, 255);}
    section > & {border: 2px solid gray;}
  }}

Which nowhere else is made clear that multiple nested selections can be used within the block, so I would advocate changing examples to this style formatting:

table.colortable {
  {
    td {
      text-align:center;

      {
        &.c { text-transform:uppercase }
        &:first-child, &:first-child + td { border:1px solid black }
      }
    }
    th {
      text-align:center;
      background:black;
      color:white;
    }
  }
}

I understand the reasoning behind the nesting selectors however would a different character be possible? What that might be I’m not sure as the following are all likely out: []<>()*@+"': backticks perhaps or slashes?

a {
 \
   span {
     color: red;
   }
 \
}

#2

Well, backslash is out, as that’s used for character escapes. :slight_smile:


#3

It was the first char that looked remotely decent that looked alright in the list, it was just to kick the discussion off :smiley: .


#4

Square brackets look good to me; since it’s essentially an array of objects (looking at the JavaScript syntax). On the other, curly brackets look more natural in CSS.

table {
  [
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  ]
}

Or with curly brackets:

table {
  {
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  }
}

I think the square brackets stand out more and thus make it more readable; it triggers the brain with a “Hey this is new” sort of vibe. Not sure if this would play nice with the attribute selector, maybe @tabatkins can enlighten us?


#5

I did not realize this had not been written.

If it’s not already done, I’ll see if I can write one.

/* with nesting */

a, b {
	color: red;

	{
		x &:hover y {
			color: blue;
		}
	}
}
/* after flattening */

a, b {
	color: red;
}

x a:hover y, x b:hover y {
	color: blue;
}

#6

No problem; this isn’t in a context that already expects selectors, so there’s no clash syntactically. (That said, I still prefer curlies, for consistency.


#7

I’m with you on the consistency thing. Here is a somewhat more complex example which I converted from SCSS to CSS with both curlies and squares.

article {
  position: relative;
  padding: 0 15px 30px;
  
  [
    header {
      margin: 0 -15px;
      padding: 10px;
      font-size: 18px;
      font-weight: 400;
      line-height: 20px;
      text-align: center;
      background-color: #bfda23;
    }
    
    <!-- Product title -->
    h2 {
      font-size: 26px;
      line-height: 30px;
      text-align: center;
    }
    
    <!-- Product description -->
    h2 + p {
      font-size: 13px;
      line-height: 22px;
    }
  
    <!-- Product image -->
    figure {
      position: relative;
      margin: 30px 0;
      
      [
        img[src*="product-images/"] {
          max-width: 100%;
          
          <!-- Default label attributes-->
          [
            & + img {
              position: absolute;
              width: 169px;
            }
          ]
        }
      ]
    }
  ]
}
article {
  position: relative;
  padding: 0 15px 30px;
  
  {
    header {
      margin: 0 -15px;
      padding: 10px;
      font-size: 18px;
      font-weight: 400;
      line-height: 20px;
      text-align: center;
      background-color: #bfda23;
    }
    
    <!-- Product title -->
    h2 {
      font-size: 26px;
      line-height: 30px;
      text-align: center;
    }
    
    <!-- Product description -->
    h2 + p {
      font-size: 13px;
      line-height: 22px;
    }
  
    <!-- Product image -->
    figure {
      position: relative;
      margin: 30px 0;
      
      {
        img[src*="product-images/"] {
          max-width: 100%;
          
          <!-- Default label attributes-->
          {
            & + img {
              position: absolute;
              width: 169px;
            }
          }
        }
      }
    }
  }
}

I still think the square brackets look better; you can clearly see where nested blocks end.

As Huey Lewis & The News say: “It’s hip to be square” :wink:


#8

I smell lots of spaghetti CSS if this is over abused.

I for one if something starts to get really deep won’t mind writing a new selector and it’s body.

html {
   body {
       main {
           section {
              div {

              }
           }
       }
   }
}

Over:

html body main section div {

}

Obviously this is not the best example hopefully nobody actually does that in real life.


#9

Yes, the same debate is had in every preprocessor that supports nesting. In general, the advice seems to be to follow the Inception rule - four layers is too deep.

Eh, that’s what bracket-matching in your IDE is for. :slight_smile:


#10

I think the different sigil makes a lot of sense as it doesn’t really mean the same thing does it. I thought you were not happy with using the same characters again to keep CSS parsers simple.

These all should be possible right (Not that I think I like any of the options or square more for that matter)?

table {
  (
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  )
}
table {
  <
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  >
}
table {
  `
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  `
}
table {
  |
    td {
      color: pineapple;
    }

    th {
     color: seaweed; 
    }
  |
}

I still stand by a syntax should look easy to read printed out.


#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?