A partial archive of discourse.wicg.io as of Saturday February 24, 2024.

CSS nesting specification

jonathank
2015-04-29

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;
   }
 \
}
tabatkins
2015-05-15

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

jonathank
2015-05-15

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

Michiel
2015-06-16

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?

jonathantneal
2015-06-16

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;
}
tabatkins
2015-06-16

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.

Michiel
2015-06-16

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:

Edwin_Reynoso
2015-06-16

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.

tabatkins
2015-06-16

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:

jonathank
2015-06-17

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.

Edwin_Reynoso
2015-06-17

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;
}
Michiel
2015-06-17

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?

jonathantneal
2015-06-17

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

jonathank
2015-06-17

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.

MoOx
2015-06-17

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

jonathank
2015-06-17

@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

tabatkins
2015-06-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;
  }
}
jonathank
2015-06-17

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

jonathank
2015-06-17

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.

Edwin_Reynoso
2015-06-18

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?