I would like to make the following code more maintainable (but less understandable is okay) by creating some sort of looping function that would create the CSS code using my LESS parser;
.box {
&.ebay {
background-color:#ebay-color;
h2, p{
color: lighten(#ebay-color, 15%);
}
}
&.google-shopping {
background-color:#google-shopping-color;
h2, p{
color: lighten(#google-shopping-color, 15%);
}
}
&.website {
background-color:#website-color;
h2, p{
color: lighten(#website-color, 15%);
}
}
&.feed {
background-color: #feed-color;
h2, p{
color: lighten(#feed-color, 15%);
}
}
&.twitter {
background-color: #twitter-color;
h2, p{
color: lighten(#twitter-color, 15%);
}
}
&.facebook {
background-color:#facebook-color;
h2, p{
color: lighten(#facebook-color, 15%);
}
}
}
So wouldn't it be great if I had a function that would A) loop over all the colors (ebay,google-shopping etc) and then another function that would output the style with the right colors.
Question
Is there a way to store my colours in an array and have a function that accepted the style for one of the above 6 blocks and then produced the same style (with the parameter for the color) in each block? and, what is this way?
I guess you could just do this:
.fn(#color) {
background-color: #color;
h2, p{
color: lighten(#color, 15%);
}
}
.box {
&.ebay {
.fn(#ebay-color);
}
&.google-shopping {
.fn(#google-shopping-color);
}
&.website {
.fn(#website-color);
}
&.feed {
.fn(#feed-color);
}
&.twitter {
.fn(#twitter-color);
}
&.facebook {
.fn(#facebook-color);
}
}
Change .fn to whatever you want.
There is a Bug that Prevents the Perfect Approach
This is using LESS 1.4+. The following achieves it, but the lighten() has to be predone because of a bug currently in LESS (see explanation following solution).
LESS
#numAssociations: 6;
#Associations: ebay google-shopping website feed twitter facebook;
#ebay-color: #ff0000;
#ebay-color-contrast: lighten(#ebay-color, 15%);
#google-shopping-color: #0000ff;
#google-shopping-color-contrast: lighten(#google-shopping-color, 15%);
#website-color: #ffff00;
#website-color-contrast: lighten(#website-color, 15%);
#feed-color: #ffffff;
#feed-color-contrast: lighten(#feed-color, 15%);
#twitter-color: #ffc0cb;
#twitter-color-contrast: lighten(#twitter-color, 15%);
#facebook-color: #ffa500;
#facebook-color-contrast: lighten(#facebook-color, 15%);
//loop code
.buildClassColorAssociations(#i) when (#i =< #numAssociations) {
#className: extract(#Associations, #i);
#bkgColor: ~'#{#{className}-color}';
#color: ~'#{#{className}-color-contrast}';
&.#{className} {
background-color: #bkgColor;
h2, p {
color: #color;
}
}
.buildClassColorAssociations(#i + 1);
}
//end the loop
.buildClassColorAssociations(#i) when (#i = (#numAssociations + 1)) {}
//call the loop
.buildClassColorAssociations(1);
CSS Output
.ebay {
background-color: #ff0000;
}
.ebay h2,
.ebay p {
color: #ff4d4d;
}
.google-shopping {
background-color: #0000ff;
}
.google-shopping h2,
.google-shopping p {
color: #4d4dff;
}
.website {
background-color: #ffff00;
}
.website h2,
.website p {
color: #ffff4d;
}
.feed {
background-color: #ffffff;
}
.feed h2,
.feed p {
color: #ffffff;
}
.twitter {
background-color: #ffc0cb;
}
.twitter h2,
.twitter p {
color: #ffffff;
}
.facebook {
background-color: #ffa500;
}
.facebook h2,
.facebook p {
color: #ffc04d;
}
This can, of course, be nested inside any class, so just call the mixin from within
.box { .buildClassColorAssociations(1); }
to get your original, desired output for that class.
BUG EXPLANATION:
Ideally, the code would be something like this:
LESS
#numAssociations: 6;
#Associations: ebay google-shopping website feed twitter facebook;
#ebay-color: #ff0000;
#google-shopping-color: #0000ff;
#website-color: #ffff00;
#feed-color: #ffffff;
#twitter-color: #ffc0cb;
#facebook-color: #ffa500;
//loop code
.buildClassColorAssociations(#i) when (#i =< #numAssociations) {
#className: extract(#Associations, #i);
#color: color(~'#{#{className}-color}'); //<-- color conversion fails, yet is
needed for lighten() to work so...
&.#{className} {
background-color: #color;
h2, p {
color: lighten(#color,15%); //<-- ... lighten fails, making a compile error
}
}
.buildClassColorAssociations(#i + 1);
}
//end the loop
.buildClassColorAssociations(#i) when (#i = (#numAssociations + 1)) {}
//call the loop
.buildClassColorAssociations(1);
BUT, the CSS Output shows that LESS is not handling the color conversion
properly, so the lighten() function fails (it makes a compile error), because the #color is producing this CSS output:
.ebay {
background-color: #NaNeebbaaNaNNaNccNaNNaNNaNNaNNaN;
}
.ebay h2,
.ebay p {
/* color: lighten(#color,15%); yields an error */
}
.google-shopping {
background-color: #NaNNaNNaNNaNNaNNaNeeNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNccNaNNaNNaNNaNNaN;
}
.google-shopping h2,
.google-shopping p {
/* color: lighten(#color,15%); yields an error */
}
.website {
background-color: #NaNNaNeebbNaNNaNNaNeeNaNccNaNNaNNaNNaNNaN;
}
.website h2,
.website p {
/* color: lighten(#color,15%); yields an error */
}
.feed {
background-color: #NaNffeeeeddNaNccNaNNaNNaNNaNNaN;
}
.feed h2,
.feed p {
/* color: lighten(#color,15%); yields an error */
}
.twitter {
background-color: #NaNNaNNaNNaNNaNNaNeeNaNNaNccNaNNaNNaNNaNNaN;
}
.twitter h2,
.twitter p {
/* color: lighten(#color,15%); yields an error */
}
.facebook {
background-color: #NaNffaacceebbNaNNaNNaNNaNccNaNNaNNaNNaNNaN;
}
.facebook h2,
.facebook p {
/* color: lighten(#color,15%); yields an error */
}
Related
Is there a way to add a class to the first element of a nested selector with .scss?
Imagine the example below:
body p {
color: black;
}
body.other-mode p {
color: red;
}
Would it be possible to express it like this (or in a similar way, since this won't compile)
body p {
& {
color: black;
}
.other-mode& {
color: red;
}
}
I'm aware this can be written like this, but that's just a different way of "solving" an example of the issue.
body {
& p {
color: black;
}
&.other-mode p {
color: red;
}
}
I also tried using some scss selectors but they don't work quite as expected, in the case below the selectors ends up being body p body.other-mode p instead of body p
body p {
& {
color: black;
}
#{selector-replace(&, "body", "body.other-mode")} {
color: red;
}
}
To make your last solution work, you can use #at-root.
body p {
& {
color: black;
}
#at-root #{selector-replace(&, "body", "body.other-mode")} {
color: red;
}
}
compiles to
body p {
color: black;
}
body.other-mode p {
color: red;
}
But personally, I find your original solution the most readable.
body { &.other-mode p {color: red;} }
I find the split body and p more convenient in SCSS.
I'm trying to create a helper mixin in my SCSS file for easily styling form input placeholder texts. For a while, I only needed to change the text's color, so I had this mixin:
#mixin input-placeholder($color, $opacity, $focusColor: null, $focusOpacity: null) {
&:-moz-placeholder {
color: $color;
opacity: $opacity;
}
&::-moz-placeholder {
color: $color;
opacity: $opacity;
}
&:-ms-input-placeholder {
color: $color;
opacity: $opacity;
}
&::-webkit-input-placeholder {
color: $color;
opacity: $opacity;
}
&:placeholder {
color: $color;
opacity: $opacity;
}
&:invalid {
color: $color;
}
&:focus {
#if($focusColor==null) {
$focusColor: transparent;
}
#if($focusOpacity==null) {
$focusOpacity: 0;
}
&::-webkit-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:-moz-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&::-moz-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:-ms-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&::-webkit-input-placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
&:placeholder {
color: $focusColor !important;
opacity: $focusOpacity !important;
}
}
}
This purposely adds each selector separately and not in a comma separated list due to the fact that some browsers will ignore the entire entry if one is invalid.
I wanted to use #extend so that I could create a mixin like this:
#mixin style-input-placeholder($module) {
&:-moz-placeholder {
#extend #{$module};
}
&::-moz-placeholder {
#extend #{$module};
}
&:-ms-input-placeholder {
#extend #{$module};
}
&::-webkit-input-placeholder {
#extend #{$module};
}
&:placeholder {
#extend #{$module};
}
&:invalid {
#extend #{$module};
}
}
Where $module is the selector I pass to the mixin to extend the styles of, and I'd be able to use it like this:
.some-special-placeholder-styles {
color: purple;
opacity: 0;
text-transform: uppercase;
font-family: 'Open Sans', Arial, sans-serif;
}
input {
#include style-input-placeholder('.some-special-placeholder-styles');
}
It would allow me to modify more than just the color and opacity without having to annoyingly specify each attribute as a mixin parameter. But because of the nature of #extend, it combines all of those selectors into one comma separated list. So is there something else I can do or any workarounds that you've come across?
You can use #content directive to pass arbitrary content inside mixins (was added into sass 3.2). So your code may look like:
#mixin style-input-placeholder() {
&:-moz-placeholder {
#content;
}
&::-moz-placeholder {
#content;
}
&:-ms-input-placeholder {
#content;
}
&::-webkit-input-placeholder {
#content;
}
&:placeholder {
#content;
}
&:invalid {
#content;
}
}
input {
#include style-input-placeholder() {
color: purple;
opacity: 0;
text-transform: uppercase;
font-family: 'Open Sans', Arial, sans-serif;
}
}
Also you can try to wire your sass compilation with PostCSS and use excellent Autoprefixer plugin that will free you from defining all these vendor-specific prefixes.
Given this scss
.root {
color: red;
&-child {
color: blue;
small & {
font-size: 80%;
}
}
}
This is the CSS I get:
.root {
color: red;
}
.root-child {
color: blue;
}
small .root-child {
font-size: 80%;
}
I want to style .root-child on small differently so the rule I need is:
small.root-child {
font-size: 80%;
}
(Notice no whitespace after small)
How can I do that?
You need to use #at-root and that will remove the white space in your selector, as well as it will be a valid syntax so no issues while you try to compile.
.root {
color: red;
&-child {
color: blue;
#at-root small#{&} {
font-size: 80%;
}
}
}
You can use #at-root like this:
SCSS
.root {
color: red;
&-child {
color: blue;
#at-root {
small#{&} {
font-size: 80%;
}
}
}
}
Compiled:
.root {
color: red;
}
.root-child {
color: blue;
}
small.root-child {
font-size: 80%;
}
Just when I thought Sass was the coolest thing since sliced bread, it had to go and let me down. I'm trying to use the ampersand to select a parent of a nested item. It's a complex selection and its returning some unexpected results...
My sass:
.page--about-us {
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
Outputted CSS:
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
a .page--about-us .fa-stack:hover .fa-circle-thin {
color: red;
}
a .page--about-us .fa-stack:hover .fa-twitter {
color: blue;
}
Expected Output (Note the placement of the a tag):
.page--about-us a {
text-decoration: none;
}
.page--about-us .fa-stack .fa {
color: pink;
}
.page--about-us a .fa-stack:hover .fa-circle-thin {
color: red;
}
.page--about-us a .fa-stack:hover .fa-twitter {
color: blue;
}
Demo:
http://sassmeister.com/gist/8ed68bbe811bc9526f15
You can store the parent selector in a variable!
Take the following BEM-like SASS:
.content-block {
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
&__heading {
font-size: 4em;
font-weight: bold;
}
}
}
The selector inside of .content-block--featured is going to be .content-block--featured .content-block--featured__heading which might not be what you're after.
It's not as elegant as the single ampersand but you can stash the parent selector into a variable! So to get what you might be after from the above example without hard-coding the parent selector:
.content-block {
$p: &; // store parent selector for nested use
&__heading {
font-size: 2em;
}
&__body {
font-size: 1em;
}
&--featured {
#{$p}__heading {
font-size: 4em;
font-weight: bold;
}
}
}
So, OP, in your case you might try something like this:
.page--about-us {
$about: &; // store about us selector
a {
text-decoration:none;
}
.fa-stack {
.fa {
color:pink;
}
#{$about} a & {
&:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
}
}
This is the normal behavior, as described in Sass documentation (link):
& will be replaced with the parent selector as it appears in the CSS. This means that if you have a deeply nested rule, the parent selector will be fully resolved before the & is replaced.
Meaning:
.foo {
.bar {
.baz & {
color: red;
}
}
}
Will render as:
.baz .foo .bar {
color: red;
}
And not:
.baz .bar {
color: red;
}
The right way to get your expected result is this one:
.page--about-us {
a {
text-decoration:none;
.fa-stack:hover {
.fa-circle-thin {
color:red;
}
.fa-twitter {
color:blue;
}
}
}
.fa-stack {
.fa {
color:pink;
}
}
}
Say I've got:
.apple {
color: red;
}
Now, let's say I've also got:
.big {
.apple {
font-size: 1.25em;
}
}
Is there a way I can put the .big selector inside the rule for .apple? In psuedocode, something like:
.apple {
color: red;
&:[WHEN INSIDE `.big`] {
font-size: 1.25em;
}
}
You place the & at the end:
.apple {
color: red;
.big & {
font-size: 1.25em;
}
}