Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with @extend and interpolation #2808

Closed
filip-jk opened this issue Jan 10, 2020 · 5 comments
Closed

Issue with @extend and interpolation #2808

filip-jk opened this issue Jan 10, 2020 · 5 comments
Labels
needs info Blocked on user response

Comments

@filip-jk
Copy link

filip-jk commented Jan 10, 2020

Hello there!

I apologize for the previous post, I took a completely wrong approach. I had already posted it on StackOverflow before writing here, without any success. I rewrote my original message to a proper bug report and posting it now.

I encountered a very strange behavior when using @extend, placeholder class, and interpolation.

I have a CSS declaration of a regular and a placeholder class, like the following:

%class-name,
.class-name {
  $this: &;

  content: "class-name";

  &__child {
     content: "__child";
  }

  &__optional-element {
    content: "optional-element";
  }

  &--modifier { // I want this modifier class to be applied to the parent element as it may affect more than one children element
    #{$this}__child {
      content: "__child--modified";
    }
  }
}

I keep the regular class in the declaration above because I want it to be available as-is, without a need to extend it i.e. for very basic cases.

Then, I extend the placeholder class with the following code:

.new {
  @extend %class-name;

  &__child {
    @extend %class-name__child;
  }
}

However, the compiled CSS output comes with a problematic bit:

.class-name--modifier .new,
.class-name--modifier .class-name__content {
  content: "__child--modified";
}

The first line seems to be completely wrong to me. I'd like to know:

  1. Is this a bug or is there some hidden, strange logic behind how the @extend works in this case?
  2. Even if there is, why does #{$this}__child turn into #{$this} in the compiled code?

Link to a Gist with the above code:

https://www.sassmeister.com/gist/0fe1e18ede2d842c4946bb0a0d243a4b

Update:

I was tweaking the above Gist and it turns out that separating the placeholder class from the regular one (a thing I wanted to avoid) does not resolve the issue at all. The updated Gist:

https://www.sassmeister.com/gist/de49ad160f03973efb91e4e170e6dd25

Full code:

%class-name {
  $this: &;

  content: "class-name";

  &__child {
     content: "__child";
  }

  &__optional-element {
    content: "optional-element";
  }

  &--modifier {
    #{$this}__child {
      content: "__child--modified";
    }
  }
}

.class-name {
  @extend %class-name;
  
  &__child {
    @extend %class-name__child;
  }
  
  &__optional-element {
    @extend %class-name__optional-element;
  }
  
  &--modifier {
    @extend %class-name--modifier;
  }
}

.new {
  @extend %class-name;

  &__child {
    @extend %class-name__child;
  }
}

Part of the compiled CSS code:

.class-name--modifier .class-name__child, 
.class-name--modifier .new__child {
  content: "__child--modified";
}

The second line is what bothers me...

Thank you!

@nex3
Copy link
Contributor

nex3 commented Jan 10, 2020

There's a lot of extra CSS in your examples which makes it hard to figure out exactly what you're trying to express. Can you provide a minimal example of the behavior you don't understand?

@nex3 nex3 added the needs info Blocked on user response label Jan 10, 2020
@filip-jk
Copy link
Author

Thank you for your response!

I think the first part of my message comes with the minimal example required to reproduce the issue:

I have a CSS declaration of a regular and a placeholder class, like the following:

%class-name,
.class-name {
  $this: &;

  content: "class-name";

  &__child {
     content: "__child";
  }

  &__optional-element {
    content: "optional-element";
  }

  &--modifier { // I want this modifier class to be applied to the parent element as it may affect more than one children element
    #{$this}__child {
      content: "__child--modified";
    }
  }
}

I keep the regular class in the declaration above because I want it to be available as-is, without a need to extend it i.e. for very basic cases.

Then, I extend the placeholder class with the following code:

.new {
  @extend %class-name;

  &__child {
    @extend %class-name__child;
  }
}

However, the compiled CSS output comes with a problematic bit:

.class-name--modifier .new,
.class-name--modifier .class-name__content {
  content: "__child--modified";
}

@nex3
Copy link
Contributor

nex3 commented Jan 14, 2020

To be clear, when I say "a minimal example", I mean without any extra classes or variables that aren't relevant to the specific issue you're having. When you include content like your &__optional-element styles it makes it more difficult for me to figure out what the core of the issue is.

Please provide me:

  • An input stylesheet that doesn't include any nesting, variables, or selectors that could be deleted while still reproducing the issue.
  • The output you're getting that you don't like.
  • The output you expect to get instead.

@filip-jk
Copy link
Author

filip-jk commented Jan 14, 2020

Hi @nex3

I apologize for the confusion. I left that bit you just mentioned for purpose, just to show that it doesn't get extended. But okay, I'm removing it now. However, I cannot remove nesting as it's crucial for this case.

Input stylesheet

%class-name,
.class-name {
  $this: &;

  content: "class-name";

  &--modifier { // I want this modifier class to be applied to the parent element as it may affect more than one children element
    #{$this}__child {
      content: "__child--modified";
    }
  }
}

.new {
  @extend %class-name;
}

Output:

.new,
.class-name {
  content: "class-name";
}
.class-name--modifier .new,
.class-name--modifier .class-name__child {
  content: "__child--modified";
}

Expected output:

.new,
.class-name {
  content: "class-name";
}
.class-name--modifier .class-name__child {
  content: "__child--modified";
}

Thank you!

@nex3
Copy link
Contributor

nex3 commented Jan 14, 2020

So if I'm understanding correctly, when you write something like:

%class-name,
.class-name {
  $this: &;
  #{$this}__child {x: y}
}

it compiles to:

%class-name %class-name,
%class-name .class-name__child,
.class-name %class-name,
.class-name .class-name__child {
  x: y;
}

but you expect it to be:

%class-name %class-name__child,
%class-name .class-name__child,
.class-name %class-name__child,
.class-name .class-name__child {
  x: y;
}

(For reference, this ⬆️ is the sort of thing I was looking for from a minimal example.)

This is happening because $this gets assigned to the SassScript value %class-name, .class-name (as a list of lists of unquoted strings), which then gets injected into the child selector as a plain string. See the "Heads up!" in the Parent Selector > In SassScript > Advanced Nesting section of the documentation for details.

We recommend using the selector.append() function to add suffixes to selectors in SassScript:

@use "sass:selector";

%class-name,
.class-name {
  $this: &;

  content: "class-name";

  &--modifier { // I want this modifier class to be applied to the parent element as it may affect more than one children element
    #{selector.append($this, "__child")} {
      content: "__child--modified";
    }
  }
}

.new {
  @extend %class-name;
}

@nex3 nex3 closed this as completed Jan 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs info Blocked on user response
Projects
None yet
Development

No branches or pull requests

2 participants