SASS CSS: Target Parent Class from Child

CssSass

Css Problem Overview


I am using SASS and found an inconvenience. This is an example of what I am trying to do:

.message-error {
    background-color: red;

    p& {
        background-color: yellow
     }
  }

Expected CSS:

.message-error {
    background-color: red;
}
p.message-error {
    background-color: yellow ;
}

The idea: all elements with .message-error will be red, except if it is p.message-error. This is not real-life situation, just to show an example.

SASS is not able to compile this, I even tried string concatenation. Is there some plugin that will do exactly the same?

NOTE: I know I can put another CSS definition like:

p.message-error{....}

...under, but I would like to avoid that and use one place for all .message-error definitions.

Thanks.

Css Solutions


Solution 1 - Css

As of Sass 3.4, this is now supported. The syntax looks like this:

.message-error {
	background-color: red;

	@at-root p#{&} {
		background-color: yellow
	}
}

Note the @at-root directive and the interpolation syntax on the ampersand. Failure to include the @at-root directive will result in a selector like .message-error p.message-error rather than p.message-error.

Solution 2 - Css

You can assign the current selector to a variable and then use it at any depth:

.Parent {
  $p: &;

  &-Child {
    #{$p}:focus & {
      border: 1px solid red;
    }

    #{$p}--disabled & {
      background-color: grey;
    }
  }
}

Solution 3 - Css

Natalie Weizenbaum (the lead designer and developer of Sass) says it will never be supported:

> Currently, & is syntactically the same as an element selector, so it > can't appear alongside one. I think this helps clarify where it can be > used; for example, foo&bar would never be a valid selector (or would > perhaps be equivalent to foo& bar or foo &bar). I don't think this use > case is strong enough to warrant changing that.

Source: #282 – Element.parent selector

To my knowledge, there is no possible workaround.

Solution 4 - Css

The best thing to do would be probably this (assuming you have a little more in your .message-error class than just background color.

.message-error {
  background-color: red;
}

p.message-error {
  @extend .message-error;
  background-color: yellow
}

This approach doesn't offer that close grouping, but you can just keep them close to each other.

Solution 5 - Css

I had the same problem so I made a mixin for that.

@mixin tag($tag) {
  $ampersand: & + '';
  $selectors: simple-selectors(str-replace($ampersand, ' ', ''));
  
  $main-selector: nth($selectors, -1);
  $previous-selectors: str-replace($ampersand, $main-selector, '');

  @at-root {
     #{$previous-selectors}#{$tag}#{$main-selector} {
      @content;
    }
  }
}

To make it work, you will need a string replacement function as well (from Hugo Giraudel):

@function str-replace($string, $search, $replace: '') {
  $index: str-index($string, $search);
  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
  }
  @return $string;
}

How it works:

SCSS

.foo {
  color: blue;

  @include tag(p) {
    color: red;
  }
}

Output

.foo {
  color: blue;
}

p.foo {
  color: red;
}

> Use case
This method works with nested selectors but not whit compound ones.

Solution 6 - Css

@Zeljko It is no possible to do what you want via SASS.

See Nex3 comment: https://github.com/nex3/sass/issues/286#issuecomment-7496412

The key is the space before the '&':

.message-error {
    background-color: red;

    p & {
        background-color: yellow
     }
  }

instead of:

.message-error {
    background-color: red;

    p& {
        background-color: yellow
     }
  }

Solution 7 - Css

I think if you want to keep them grouped by parent selector, you might need to add a common parent:

body {
    & .message-error {background-color: red;}
    & p.message-error {background-color: yellow}
}

Of course, body could be replaced with some other common parent, such as #Content or another div name that will contain all the error messages.

UPDATE (Another Idea)

If you leverage @for and lists then it seems like this should work (what I don't know for sure is if the list will allow the . (period).

@for $i from 1 to 3 {
  nth(. p. ul., #{$i})message-error {
    background-color: nth(red yellow cyan, #{$i}));
  }
}

Should compile to something like:

.message-error {
   background-color: red;}
p.message-error {
   background-color: yellow;}
ul.message-error {
   background-color: cyan;}

Solution 8 - Css

I made a mixin that solves this problem.

Github: https://github.com/imkremen/sass-parent-append

Example: https://codepen.io/imkremen/pen/RMVBvq


Usage (scss):

.ancestor {
  display: inline-flex;
  
  .grandparent {
    padding: 32px;
    background-color: lightgreen;
    
    .parent {
      padding: 32px;
      background-color: blue;
      
      .elem {
        padding: 16px;
        background-color: white;
        
        @include parent-append(":focus", 3) {
          box-shadow: inset 0 0 0 8px aqua;
        }
        
        @include parent-append(":hover") {
          background-color: fuchsia;
        }
        
        @include parent-append("p", 0, true) {
          background-color: green;
        }
      }
    }
  }
}

Result (css):

.ancestor {
  display: inline-flex;
}
.ancestor .grandparent {
  padding: 32px;
  background-color: lightgreen;
}
.ancestor .grandparent .parent {
  padding: 32px;
  background-color: blue;
}
.ancestor .grandparent .parent .elem {
  padding: 16px;
  background-color: white;
}
.ancestor:focus .grandparent .parent .elem {
  box-shadow: inset 0 0 0 8px aqua;
}
.ancestor .grandparent .parent:hover .elem {
  background-color: fuchsia;
}
.ancestor .grandparent .parent p.elem {
  background-color: green;
}

Solution 9 - Css

I created package/mixin with a similar solution :) (Maybe it will help U)

https://github.com/Darex1991/BEM-parent-selector

so writing:

.calendar-container--theme-second-2 {
  .calendar-reservation {
    @include BEM-parent-selector('&__checkout-wrapper:not(&--modifier):before') {
      content: 'abc';
    }
  }
}

This mixin will add selector only for the last parent:

.calendar-container--theme-second-2 .calendar-reservation__checkout-wrapper:not(.calendar-reservation--modifier):before {
   content: 'abc';
 }

More info on the repo.

Solution 10 - Css

I have ran into this before as well. Bootstrap 3 handles this using a parent selector hack. I've tweaked it slightly for my own purposes...

@mixin message-error() {
  $class: '.message-error';
  #{$class} {
    background-color: red;
  }
  p#{$class} {
    background-color: yellow;
  }
}
@include message-error();

wheresrhys uses a similar approach above, but with some sass errors. The code above allows you to manage it as one block and collapse it in your editor. Nesting the variable also makes it local so you can reuse $class for all instances where you need to apply this hack. See below for a working sample...

http://sassmeister.com/gist/318dce458a9eb3991b13

Solution 11 - Css

This cheat might work

 {
     $and: .message-error;
     #{$and} {
        background-color: red;
     }

     p#{$and} {
        background-color: yellow
     }
  }

You may even be able to use $& as your variable name but I'm not 100% sure it won't throw an error.

And SASS has inbuilt scoping, which removes having to worry about the value of $and leaking out to the rest of your stylesheet

> Variables are only available within the level of nested selectors > where they’re defined. If they’re defined outside of any nested > selectors, they’re available everywhere.

Solution 12 - Css

In the Current Release: Selective Steve (3.4.14) this is now possible, just need to update a little bit your code:

.message-error {
    background-color: red;
    p &{
        background-color: yellow
     }
 }

this only works if you are one level nested, for instance it does not work if you have something like this:

.messages{
    .message-error {
        background-color: red;
        p &{
            background-color: yellow
         }
     }
 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZeljkoView Question on Stackoverflow
Solution 1 - CsscimmanonView Answer on Stackoverflow
Solution 2 - CssDominicView Answer on Stackoverflow
Solution 3 - CsspiouPiouMView Answer on Stackoverflow
Solution 4 - CsspyronaurView Answer on Stackoverflow
Solution 5 - CssQuentin VeronView Answer on Stackoverflow
Solution 6 - CssTianView Answer on Stackoverflow
Solution 7 - CssScottSView Answer on Stackoverflow
Solution 8 - CssimkremenView Answer on Stackoverflow
Solution 9 - CssDarex1991View Answer on Stackoverflow
Solution 10 - CssAdam YoungersView Answer on Stackoverflow
Solution 11 - CsswheresrhysView Answer on Stackoverflow
Solution 12 - CssFelipe SalazarView Answer on Stackoverflow