5

I have set up a mixin for a button using display:inline-block. I am trying to get to the parent of whatever class that will eventually end up using the mixim, so I can add the font-size: 0px line there to make sure that I don't need to make adjustments to my HTML to avoid unwanted space between each button.

Here's an example... I want the. parent class to receive the font-size: 0px line.

@mixin button() {
    display:inline-block;
    font-size: 1em;
    //other stuff to make a pretty button
    && { font-size: 0px; }
}

.parent{
    .child {
        @include button();
    }
}
MacBryce
  • 123
  • 1
  • 3
  • 14
  • Setting the font-size to 0px is an unreliable way of removing undesirable spaces. If the user has forced a minimum font-size in their browser, there's nothing you can do about it. – cimmanon Apr 22 '13 at 15:33
  • I've seen it a couple of times to make sure that the inline-blocks do not get extra spaces in between them when there are line breaks in the HTML. What would be a better solution for that problem then? – MacBryce Apr 22 '13 at 16:43
  • I comment out the whitespace in the HTML itself. The only reliable way to do it with CSS is for the element to not be inline (either via float, table-cell, as a flex element via Flexbox, etc). – cimmanon Apr 22 '13 at 16:53

4 Answers4

13

As of Sass 3.4 this is now possible.

@mixin parent {

  @each $selector in & {

    $l: length($selector);

    @if ($l == 1) {
      @error "Used parent mixin on a top-level selector";
    } @else {

      $parent: nth($selector,1);
      @for $i from 2 to $l {
        $parent: append($parent,nth($selector,$i));
      }

      @at-root #{$parent} {
        @content;
      }

    }
  }
}

// Use
.grandparent {
  .parent{
      .child {
        font-size: 1em;
          @include parent {
            font-size: 0px;
          }
      }
  }
}

// Result
.grandparent .parent .child {
  font-size: 1em;
}
.grandparent .parent {
  font-size: 0px;
}

// Errors:
.root {
  @include parent {
    content: "Won't work";
  }
}
.grandparent .parent, .root {
  @include parent {
    content: "Also won't work";
  }
}
  • 1
    Is all of this necessary to get the parent class to have a font size of 1px? If not, would you give a simpler example of just that? – Marc Jul 19 '18 at 11:34
7

No, this is not possible. You could do something like this, though:

@mixin button($child: '.child') {
    font-size: 0px;
    //other stuff to make a pretty button

    #{$child} {
        display:inline-block;
        font-size: 1em;
    }
}

.parent{
    @include button();
}

Output:

.parent {
  font-size: 0px;
}
.parent .child {
  display: inline-block;
  font-size: 1em;
}
cimmanon
  • 62,582
  • 13
  • 151
  • 162
2

There is a XXX! selector in the draft for the CSS 4 spec, which will act as the way you like. It announces the subject of the CSS style declarations, if the selectors match

So if you have this selector

.a > .b! > .c

It will match e.g. for this

<div class="a">
    <div class="b">
        <div class="c">
        </div>
    </div>
</div>

but the style declarations will not take effect on .c, but on .b, because I announced by the exclamation mark, that this element should be the subject of the style http://dev.w3.org/csswg/selectors4/#subject

You cannot use it right now out of the box. But there is one jQuery plugin, that is a polyfill for that. http://dev.w3.org/csswg/selectors4/

See also this stack: Is there a CSS parent selector?

How to apply? Well, I don't know exactly in SASS, but in LESS it would be

*! > & {
    /* ... */
}
Community
  • 1
  • 1
yunzen
  • 30,001
  • 10
  • 64
  • 93
0

While Karol's answer is near perfect, it doesn't take into account pseudo-elements or pseudo-selectors. Furthermore, code is duplicated if using more than one complex selector. I came up with a simplified version:

@mixin parent {
    $parents: ();
    $parent: '';

    @each $selector in & {
        $length: length($selector);
        $index: 0;
        $last-selector: nth($selector, $length);

        @if ($length == 1) {
            @error "Used parent mixin on a top-level selector";
        } @else {
            $index: str-index($last-selector, '::');

            @if ($index) {
                $last-selector: str-slice($last-selector, 1, $index - 1);
            } @else {
                $last-selector: null;
            }
            // Inspect allows us to combine two selectors in one block.
            $parent: inspect(set-nth($selector, $length, #{$last-selector}));
            $parents: join($parents, $parent, comma);
        }
    }
    @at-root #{$parents} {
        @content;
    }
}

There's a first loop to iterate over the selector list (selectors with commas at the end). Because complex selectors are also treated as a list, we just need to remove the last element of the list. There's no loop to iterate over the compound or simple selectors since we only need to discard the last one.

There's no function in Sass to remove an element of a list, but we can set the value of an element with set-nth. By making the last element as an empty string and unquoting it, we can remove the last element from the printed representation (string) of the list. Since selectors can be strings, we simply use the new string as a selector.

When using the following:

.grandmother,
.grandfather {
  .parent {
      .child {
        font-size: 10em;
          @include parent {
            font-size: 5em;
          }

          &::after {
            font-size: 1px;
            @include parent {
              font-weight: bold;
            }
          }
      }
  }
}

We get the following:

.grandmother .parent .child,
.grandfather .parent .child {
  font-size: 10em;
}
.grandmother .parent,
.grandfather .parent {
  font-size: 5em;
}
.grandmother .parent .child::after,
.grandfather .parent .child::after {
  font-size: 1px;
}
.grandmother .parent .child,
.grandfather .parent .child {
  font-weight: bold;
}

Note: pseudo-elements and pseudo-selectors are not children of an element but are attached to it and have therefore no parents in themselves. I assumed parents would mean the parent in the sense of Sass nesting.

notetienne
  • 71
  • 1
  • 4