Is there a way to prepend a selector directly to the current selector in scss. Consider the following:
.object-fit {
object-fit: cover;
height: 100%;
width: 100%;
overflow: hidden;
}
is there a way to prepend img so that the output is img.object-fit?
The only way I have seen for prepending is to add & after like so:
.object-fit {
img & {
}
}
but this would turn it into a parent child selector: img .object-fit
The normal way would be just to append the second selector with &img but as this has no dot before the selector, that ends up with a different class name: .object-fitimg
So basically the question is is there anyway inside the object fit class to prepend a bare element selector?
If you use #at-root and & with interpolation syntax:
.object-fit {
#at-root img#{&} {
color: blue;
}
}
Your output will be:
img.object-fit{
color: blue;
}
Complete answer here is #at-root selector-append(), which will also work for multiple parent selectors.
Source: https://github.com/sass/sass/issues/2059#issuecomment-218862922
.object-fit,
.non-fit {
#at-root #{selector-append("img", &)} {
color: blue;
}
}
Output
img.object-fit, img.non-fit {
color: blue;
}
In the following code example I generate two squares that ideally should turn red.
The first div .with-root currently stays blue, the second div .without-root turns red. I expect this behaviour, but don't see a proper solution to turn the .with-root div red as well.
Note the difference in the scss file: the first div works with a fixed parent selector, the second one doesn't have a parent. For CSS specificity I need to work with the .with-root {} wrapper.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
background: red;
}
}
}
}
.without-root {
&__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red & {
&__item {
display: block;
width: 5rem;
height: 5rem;
background: red;
}
}
}
}
The codepen can be found here: https://codepen.io/studiotwist/pen/OzMOmr
Well now that I hopefully understood your question I deleted my wrong idea before and the following solution should work.
Maybe there could be a logic erorr. You have actually three class definitions of .with-root__element and two of them are extended with --red and __item, but the 3rd one is however an extra class which comes in conflict with the other two. You're basically concatenating the endings --red and __item with the parent selector *__element. Also, the --red class is nested inside the *__element one without ending in your CSS but in HTML it is not. *__element and *__element--red are attached in the same HTML tag.
DEBUG
Only showing the first DIV.
.with-root {
.with-root__element {
display: block;
width: 5rem;
height: 5rem;
background: blue;
&--red {
//#error &; // this reference contains the entire document root including the root element .with-root which is wrong
#{&} &__item {
//#error #{&} &__item; // this is a wrong concatenation plus it takes the entire root with it
background: red; // thus, this won't render
}
}
}
}
Debug in action # Sassmeister
POSSIBLE FIX
#mixin bg($bg) {
width: 5rem;
height: 5rem;
background: $bg;
}
.with-root__element {
#include bg(blue);
$this: &;
#at-root {
.with-root {
#{$this}--red #{$this}__item {
#include bg(red);
}
}
}
}
.without-root {
&__element {
#include bg(blue);
&--red &__item {
#include bg(red);
}
}
}
Fork
#at-root is a directive which is useful for your issue as it basically crops the nesting level of the selector and styles can be defined inside the root-body by referencing the parent selector instead of the entire root. So I added a variable $this which will cache the reference. display: block is not needed as div elements have it by default. Sorry about the mixin, it's a habit. --red and __item have now the refence selector *__element.
#at-root Documentation
I have a chunk of CSS that I want to "scope" to a specific block of HTML. I'm generating a unique ID and then setting it on the block of HTML and then would like to wrap the chunk of CSS with the same ID so that those selectors can't match sibling or parent elements. I don't know the contents of the chunk of CSS. Given a chunk of CSS:
.container {
background-color: black;
}
.container .title {
color: white;
}
.container .description {
color: grey;
}
I need it to come out like this:
.theme0 .container, .theme0.container {
background-color: black;
}
.theme0 .container .title, .theme0.container .title {
color: white;
}
.theme0 .container .description, .theme0.container .description {
color: grey;
}
Is there any way to do this with LESS? The first selector is easy, just wrap the CSS chunk with '.theme0 {' + cssChunk + '}'. But I haven't been able to figure out a way to prepend '.theme0' to all of the selectors without the space.
EDIT:
So I should clarify that our intentions are to build such a system into our build process / dependency system. We're attempting to scope a chunk of css to a react component. We have a couple different approaches we're trying out, this is just one of them. Point is, the CSS and HTML we're trying to scope could be anything, we have no control or knowledge of it. The first pattern can easily be achieved by prepending .uniqueID { and appending }. This gives .uniqueID .someSelector {}. I'm wondering if it's possible to do a similar thing but get .uniqueID.someSelector {}? Ideally without having to write the original chunk of CSS with knowledge of our scoping system.
Assuming the component styles are in a separate CSS file, i.e.:
// component.css
.container {
background-color: black;
}
.container .title {
color: white;
}
.container .description {
color: grey;
}
The wrapper code could be:
.theme0 {
#import (less) "component.css";
&.container:extend(.theme0 .container all) {}
}
in less you can nest selectors for selecting inside that element like:
.theme {
color: black;
.container {
color: blue;
}
}
This wil generate:
.theme {
color:black;
}
.theme .container {
color:blue;
}
Creating elements that are connected is easy enof:
.test#badge will select a class test width an id badge
In less this is dont with the & symbol. (this selects the starting property)
.test {
color: blue;
&#badge {
color:black;
}
}
Compiles to:
.test {
color: blue;
}
.test#badge {
color: black;
}
And for the final selector:
To get the output of .test, .container use the function: .test:extends(.container);
.test {
color: black;
&:extends(.conatiner);
}
.container {
color: pink;
}
Compiles to:
.test {
color: black;
}
.test, .container {
color: pink;
}
You can even extend multiple ones in a single line:
.test:extends(.oclas, .tclss);
and its wil work as abose only for both classes. So outputed selectors would be .test, .oclass and .test, .tclass
I have an element lets say it could have .foo or .bar or both or none as a class:
<div class="foo bar">green</div>
<div class="foo">green</div>
<div class="bar">green</div>
<div class="something-else">red</div>
How can I test that the element doesn't have either class.
I tried something like this, but it doesn't work as expected:
div {
background: green;
&:not(.foo) and &:not(.bar) {
background: red;
}
}
I don't see many examples using less and not(), only 1 in their docs.
To say not to multiple conditions in a CSS selector, you need to chain them together: div:not(.foo):not(.bar)
div {
background: green;
&:not(.foo):not(.bar) {
background: red;
}
}
I was able to do it by nesting two &:not() rules, but I'm not sure this is the best way.
div {
background: green;
&:not(.foo) {
&:not(.bar) {
background: red;
}
}
}
There is always the pure css alternative.
div { background: red; }
div.foo, div.bar { background: green; }
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
}
}
}