blakeembrey / free-style

Make CSS easier and more maintainable by using JavaScript
MIT License
707 stars 29 forks source link

Interpolation question with deep nest #72

Closed wclr closed 5 years ago

wclr commented 5 years ago

Was asked originally at https://github.com/typestyle/typestyle/issues/292

For such style definition:

Style.registerStyle({
    '& .c1, & .c2': {      
        '&:hover,&:active': {
            '& .nested': {
              opacity: '0.15'
            }
        },
    },
})

The generated CSS:

.generated .c1, 
.generated .c2:hover,
.generated .c1, 
.generated .c2:active .nested {
    opacity:0.15
}

I wonder in't it more reasonable to get:

.generated .c1:hover .nested, 
.generated .c1:active .nested, 
.generated .c2:hover .nested, 
.generated .c2:active .nested {
    opacity:0.15
}

Currently & .c1, & .c2 is applied to generated, then &:hover,&:active applied to the last of interpolated parts, and so on? Is it how it should be in your view? What would be unlogical if it would apply interpolation to all previous interpolated parts (like most of the things like LESS/SASS do)?

blakeembrey commented 5 years ago

@whitecolor It's definitely more reasonable to get that output, but unfortunately it won't be supported. This isn't really a problem with nested styles, but with the fact that this module does not parse any CSS selectors. It doesn't actually know or care that & .c1, & .c2 are two different rules.

Here's the previous issue on the topic: https://github.com/blakeembrey/free-style/issues/69. Most likely just using a utility to do this is the better approach and avoids having to introduce any CSS selector parsing.

blakeembrey commented 5 years ago

@whitecolor I'd definitely be interested to hear if you have a better idea or approach here though. It's just relatively easy to work around but I haven't a better built-in idea based on the object module right now.

Here's one possibly wacky idea. Based on the IS_UNIQUE tag, we could have a REPEAT_BLOCK tag that you attach to the object. When used, it'd repeat the previous declaration. Your example with this:

Style.registerStyle({
    '& .c1': {      
        '&:hover': {
            '& .nested': {
              opacity: '0.15'
            }
        },
        '&:active': {
            [REPEAT_BLOCK]: true
        }
    },
    '& .c2': {
        [REPEAT_BLOCK]: true
    }
})

Reference: https://github.com/blakeembrey/free-style/blob/d085b4c8c1ef629d2b0978bd493cf20e3bfce0c9/src/free-style.ts#L26

wclr commented 5 years ago

@blakeembrey Ah I just got that free-style just replaces & with previous value =) I thought it does something more intelligent. So then maybe there is no big reason to complicate things... but..

Idea with tags definitely doesn't feel good to me =)

So there could be helper something like this:

const multiStyle = (selectors: string[], style: FreeStyle.Styles) =>
  selectors.reduce((p, sel) => ({ ...p, [sel]: style }), {} as FreeStyle.Styles)

Then styles could be composed like:

const Style = FreeStyle.create()
Style.registerStyle({
  ...multiStyle(['& .c1', '& .c2'],
    multiStyle(['&:hover', '&:active'], {
      '& .nested': {
        opacity: '0.15'
      }
    })
  )  
})

So this could be even embed in the library, but really correct parsing can be a problem, because if I'm not mistaken potentially comma can be not only delimter between selectors, but also be inside text content (i.e. attribute names), which is always in quotes anyway should be easy to parse, but still will require some efforts.

@basarat Please read my thoughts here.

In typestyle, as there already present special style params ($nest, $unique,..), there could be introduced some new param somehting like $multi to make things happen automatically:

style({
  $nest: {
    '& .c1, & .c2': {
      $multi: true,
      $nest: {
        '&:hover,&:active': {
          $multi: true,
          $nest: {
            '& .nested': {
              opacity: '0.15'
            }
          }
        },
      }
    },
  }
})

So in case if styles have $multi param defined it would just parse selector (without complicating things, just split by commas) and apply multiStyle helper function to it, like shown above. I seem to quite easy solution, and may be useful in some cases.

blakeembrey commented 5 years ago

@whitecolor That definitely sounds reasonable, it's not a huge utility to build into the library (or external libraries).

blakeembrey commented 5 years ago

Published under https://github.com/blakeembrey/style-helper#multi for now 😄