I'm looking for a way to make an equivalent to .bloc + .bloc or .article + .article but directly from a mixin bloc-article() :
#mixin bloc-article() {
margin-top: 20px;
margin-top: 50px;
& + "bloc-article()" { // is there a "$this"-like to make an equivalent to `.bloc + .bloc` or `.article + .article` here ?
border-top: 1px solid red;
}
}
.bloc {
#include bloc-article();
}
.article {
#include bloc-article();
}
is there a "$this"-like to make an equivalent to .bloc + .bloc or .article + .article directly from the mixin ?
You can write your mixin with an & + & selector:
#mixin bloc-article() {
// …
& + & {
border-top: 1px solid red;
}
}
Which compiles from:
.article {
#include bloc-article();
}
to:
.article + .article {
border-top: 1px solid red;
}
Here, the Sass parent selector is the $this you are looking for.
From the docs:
The parent selector, &, is a special selector invented by Sass that’s used in nested selectors to refer to the outer selector. It makes it possible to re-use the outer selector in more complex ways, like adding a pseudo-class or adding a selector before the parent.
I'm looking at the following selector from a .css file:
.tab {
flex: 1 0 auto;
height: 52px;
& + & {
border-left: 1px solid;
}
}
I'm not familiar with the syntax of & + & {} - what does it mean?
This is not CSS but some file meant to be compiled to CSS. It's probably SCSS or Less.
In SCSS and Less, the & is just a repetition of the enclosing selector.
So
& + & {
border-left: 1px solid;
}
would be translated as
.tab + .tab {
border-left: 1px solid;
}
This construct is common when you need to add a border between items: you add it to the left of any items which follows another one.
introduction to the sass/less ampersand
Has anyone had issues with LESS compiling CSS that utilizes plus signs [+] and/or greater than [>] symbols, whereas the inclusion of these causes compilation errors/fails? (we're using the standard lessc compiler). The LESS below is identical to what we're trying to compile - the first plus after banana.keynote is what sets the compiler error off:
.banana.keynote {
+ {
label:before {
border: 2px solid #CC6633 !important;
}
}
}
.banana.chunga {
+ {
label:before {
border: 2px solid #ffb5ff !important;
}
}
}
.banana.gordita {
+ {
label:before {
border: 2px solid #4067ea !important;
}
}
}
NOTE: admittedly, I'm using an online converter to go from CSS to LESS - could this be producing weird artifacts in code?
Thanks.
Combinators in Less must always be followed by an element identifier, i.e. you can't use + alone on its own. Rewrite your code to:
.banana.keynote {
+ label:before {
border: 2px solid #CC6633;
}
}
If you really need to get it "alone" for some reason you may hack it through selector interpolaton:
#plus: ~'+';
.banana.keynote {
#{plus} {
label:before {
border: 2px solid #CC6633;
}
}
}
---
P.S. Additionally note that the following code:
.banana.keynote + {
label:before {
border: 2px solid #CC6633;
}
}
while compiled w/o any errors, does not produce the proper CSS (invalid + is just silently omitted).
How can I simplify this SASS so that I only write .question-sector-answer the once? I need to apply different styles to the .question-sector-answer if the parent div has a class of both .question-row and .question-review. This currently seems unwieldy and I'm sure could be simplified and made more elegant:
.question-row {
border-bottom: 1px solid #ddd;
&.question-review {
.question-sector-answer {
padding-top: 30px;
}
}
.question-sector-answer {
padding: 15px;
}
}
I don't see how you can simplify it. You need to use 2 different styles for .question-sector-answer under different parents. Since it's impossible in css to access parent selector, you have no choice but do what you did (well, in SASS you kind of can - see below). Although my personal preference to always put more generic selectors on top and more specific ones to the bottom like so:
.question-row {
border-bottom: 1px solid #ddd;
.question-sector-answer {
padding: 15px;
}
&.question-review {
.question-sector-answer {
padding-top: 30px;
}
}
}
So in SASS you can access parent selector with & using it in certain way, but I don't think you can recreate your styles with it, the best I could come up with was this but it looks uglier than your original way of doing it, but you're welcome to play with it:
.question-row {
border-bottom: 1px solid #ddd;
}
.question-sector-answer
{
.question-row & {
padding-top: 15px;
}
.question-row.question-review &
{
padding: 30px;
}
}
You can read more about accessing parent selectors with & here
.question-row {
border-bottom: 1px solid #ddd;
}
.question-sector-answer {
padding: 15px;
.question-review & {
padding-top: 30px;
}
}
De-nesting here does two things: (1) creates terser, more flexible CSS and (2) allows the parent & selector. To compensate for the decrease in OOP, we slightly indent to imply subjugation. But in SASS you want to avoid the temptation to nest when not totally necessary, because nesting for OOP's sake tends to create more problems than it solves.
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.
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.
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;
}
}
}
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.
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.
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.
#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
}
}
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;}
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;
}
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.
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
I use an #mixin function like this, when i need change some element in middle
of a sass big tree.
The first parameters is the parent element, the target, and the second the class that should have.
SASS
#mixin parentClass($parentTarget, $aditionalCLass) {
#at-root #{selector-replace(&, $parentTarget, $parentTarget + $aditionalCLass)} {
#content;
}
}
Sample,
like i need to improve font size in a strong tag, when .txt-target had .txt-strong too
HTML
<section class="sample">
<h1 class="txt-target txt-bold">Sample<strong>Bold</strong>Text</h1>
</section>
SASS
section{
.txt-target{
strong{
#include parentClass('.txt-target','.txt-bold'){
font-weight:bold;
font-size:30px;
}
}
}
}
Font:
https://sass-lang.com/documentation/at-rules/at-root
Here you can see a function called #mixin unify-parent($child) that looks like this
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.
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
}
}
}