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
Related
What's the best way to accomplish the ability to use class prefixes using a mixin with placeholder selectors.
To elaborate, say I have a box class that has 3 sizes but I would like to have the option of having it a different color.
My base classes would be:
.box
.box-md
.box-sm
If I wanted any of the base class boxes to be green, I would like to be able to specify as such:
.box-green
.box-md-green
.box-sm-green
How would I be able to do so in as DRY a method as possible?
Similar to this answer but using mixins AND placeholder extends: SCSS, how to #extend Nested ampersand "prefix"?
Here's what I put together so far (which doesn't work)
HTML:
<div class="box"></div>
<div class="box-green"></div>
<div class="box-sm"></div>
<div class="box-sm-green"></div>
CSS (SCSS):
// Main style placholder as mixin
#mixin box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
&-green {
background-color: green;
}
}
// Placeholders
%box {
#include box;
}
%small-box {
#include box;
width: 100px;
height: 100px;
}
// Class Definition
.box { #extend %box; }
.box-sm { #extend %small-box; }
Pen: https://codepen.io/Aricha_MW/pen/xxKZWbV
This isn't a complete answer but does pose as a solution to the problem:
Our champions are the #at-root directive and interpolation here. The solution requires both the use of mixins and placeholder selectors and is a little messy.
If we set up our placeholder selector styles:
%box {
height: 300px;
width: 300px;
margin: 20px;
display: inline-block;
background-color: blue;
}
%small-box {
#extend %box;
width: 100px;
height: 100px;
}
Then we can let mixins do the rest of the work for us.
First we define our mixin for the variation we want:
#mixin green-bg($selector, $root) {
// Takes element out of any nesting
// Then we interpolate our argument variables
#at-root{
#{$root}-green {
// We can't set a placeholder as an argument so we'll just borrow the string and append the placeholder definer '%'
#extend %#{$selector};
background-color: green;
}
}
}
Then we define our mixins that will help us define our classes
#mixin box($parent) {
#extend %box;
#include green-bg(box, #{$parent});
}
#mixin small-box($parent) {
#extend %small-box;
#include green-bg(small-box, #{$parent});
}
When we define our classes they'll look clean like so:
.box { #include box(&); }
.box-sm { #include small-box(&); }
Here's what the final product looks like in action: https://codepen.io/Aricha_MW/pen/oNvxjEw
Edit: 08/15/2019 - Much cleaner version here:
https://codepen.io/Aricha_MW/pen/mdbPVXY
I inherited some CSS code, which is making use of the & character prior to the id name to style it. It looks something like this:
&#my-id {
// Content and attributes
}
There are also other instances of it, such as:
&:before {
// content and attributes
}
and
&:hover {
// content and attributes
}
What do those mean? I can't find a good way to express this in a search, so I can't find anything. My apologies if this is a duplicate.
It refers to the parent selector.
Input:
.parent {
&.child {
color: red;
}
}
Output:
.parent.child { color: red }
It's really helpful if you're writing CSS in BEM format, something like:
.block {
&__element {
width: 100px;
height: 100px;
&--modifier {
width: 200px;
}
}
}
.block__element { width: 100px; height: 100px;}
.block__element--modifier { width: 200px;}
<div class="block__element"></div>
<div class="block__element block__element--modifier"></div>
And finally, all examples I've shared have been concatenating the class names, but you can also use it as a reference, like:
.parent {
& .child {
color: red;
}
}
.parent {
.child & {
color: blue;
}
}
.parent .child { color: red }
.child .parent { color: blue }
Additional references:
http://lesscss.org/features/#parent-selectors-feature
https://blog.slaks.net/2013-09-29/less-css-secrets-of-the-ampersand/
Using the ampersand (SASS parent selector) inside nested selectors
It's a built-in feature of Sass: https://css-tricks.com/the-sass-ampersand/
You can use it when you're nesting selectors and you need a more specific selector, like an element that has both of two classes:
If your CSS looks like this:
.some-class.another-class { }
And you wanted to nest, the Sass equivalent is:
.some-class {
&.another-class {}
}
Presentation
I'm trying to build a web site available in multiple cultures, with different reading direction.
To do so, I simply add the dir="rtl" attribute on my root HTML element.
My issue is that I have some CSS rules that are specific to one direction or the other (margins or paddings, most of the times).
Unsuccessful try with attribute selector
I though that I could simply use the attribute selector but the dir attribute is only set on the root element, so this wouldn't work :
selector {
&[dir="ltr"] {
// LTR specific
}
&[dir="rtl"] {
// RTL specific
}
}
For instance, on this demo, the title should have a margin of 5px on the right if the application is in rtl or on the left if it's in standard ltr.
Other idea
I've noticed that the direction is rightfully set at rtl, is there a way to use that rule within a CSS or Sass selector ?
Edit and precisions
It seems that I've forgotten an important point. I'm building the web site using Vue.js, the dir attribute is bind in the main component (App) and the RTL/LTR specific CSS rules can be in the same component or in other self-contained component.
Following your css code you could do this with SASS at-root directive DEMO. So this:
#app {
width: 300px;
height: 100px;
border: 1px solid red;
h1 {
#at-root {
[dir="rtl"]#{&} {color: green}
}
#at-root {
[dir="ltr"]#{&} {color: red}
}
}
}
It will compile to this css.
#app {
width: 300px;
height: 100px;
border: 1px solid red;
}
[dir="rtl"]#app h1 {
color: green;
}
[dir="ltr"]#app h1 {
color: red;
}
You could style everything LTR, and only adjust some elements styling for RTL. Might this work for you?
[dir="rtl"] {
&selector {
// RTL specific
}
&selectorN {
// RTL specific
}
}
Use below scss to get expected output
#app {
width: 300px;
height: 300px;
background: red;
&[dir="ltr"] h1{
margin-left: 10px;
}
&[dir="rtl"] h1 {
margin-right: 10px;
}
}
Probably you are going a little in the wrong direction.
Most of the time, you can achieve this automatically, no need for specific selectors.
Margin, for instance:
Just set it both for left and right margin. The browser will choose the correct one for you
#app {
width: 300px;
background: tomato;
margin: 10px;
}
h1 {
margin-left: 15px;
margin-right: 5px;
}
<div id="app" dir="ltr">
<h1>
margin left 15
</h1>
</div><div id="app" dir="rtl">
<h1>
margin right 5
</h1>
</div>
I have a code that I can't change:
item.left,
item.centre,
item.right{
.MIXIN();
}
.MIXIN(){
width: 100px;
}
I need to apply width only to .right element. I can only change contents of MIXIN(). I was thinking of using &but it will result either in .right item.right or item.right .right which is not what I want. Is there a way to apply styling only for .right element using contents of MIXIN()?
You can use the negation CSS pseudo-class :not().
item.left,
item.centre,
item.right{
width: 20px;
&:not(.left):not(.centre) {
width: 100px;
}
}
Fiddle: https://jsfiddle.net/e0nd7pk4
You can not do it. The only way is to override the first declaration.
item.left,
item.centre {
width: inherit;
}
How about & but without the space:
.MIXIN() {
width: 100px;
&.right { color: red; }
}
It compiles down to item.right.right which is a bit weird but won't match left and center.
Fiddle: http://jsfiddle.net/c0634wg2/
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