refactoring repetitive sass to modify based on immediate parent's class - css

tldr: how to avoid repetition of ".well" selector in below example.
I am using bootstrap and sass to display a "well" div with a shape and with a gradient fill. This may not be a proper use of wells and I'd welcome other suggestions as to how to draw circular/rectangular divs with X% shaded (ideally where X is any integer. [0, 100]) but, for now, I am most interested in whether it's possible in SASS to get rid of the repetition of ".well". I tried using "&" but it would reverse .some_container too and I only wanted to reverse the immediate .inner_container parent to apply there (e.g. .inner_container.round). [There is one outer_container and multiple inner_containers. Each inner_container has one well.]
.outer_container {
.inner_container {
&.round .well {
border-radius: 50%;
}
&.barely_filled .well {
#include gradient-horizontal(sienna, $well-bg, 0%, 25%);
}
&.half_filled .well {
#include gradient-horizontal(sienna, $well-bg, 0%, 50%);
}
&.fairly_filled .well {
#include gradient-horizontal(sienna, $well-bg, 0%, 75%);
}
&.mostly_filled .well {
background-color: sienna;
}
}
}

The most terse way to write it would be like this:
#mixin well($sel) {
&#{$sel} .well {
#content;
}
}
.outer_container {
.inner_container {
#include well('.round') {
border-radius: 50%;
}
#include well('.barely_filled') {
test: 1;
}
#include well('.half_filled') {
test: 2;
}
#include well('.fairly_filled') {
test: 3;
}
#include well('.mostly_filled') {
background-color: sienna;
}
}
}
However, in addition to being more verbose, I feel that this decreases readability over what you currently have.

Related

Exclude elements from group in SCSS

Imagine a set of rules like the ones shown below:
span, div { color: red; }
span { background: white; }
div { background: black; }
Is it possible to wrap them under 1 SCSS rule? Something in the form of:
span, div {
& { color: red; }
&:not(div) { background: white;}
&:not(span) { background: black; }
}
Unfortunately an approach like this could very easily get quite large. So I'm hoping for an SCSS implementation of the code shown at the top but without the use of :not(<every other selector>).
Preferably something looking like (invalid code):
span, div {
& { color: red; }
&(span) { background: white;}
&(span) { background: black; }
}
I don't think that it is possible to do what you want this way (but I may be wrong).
The code below achieve the result you are looking for but uses a map, a #mixin and #extend instead of a single selector. Maybe it's a bit too complex for want you want to achieve but I hope it can help:
#mixin setSelectors($elements) {
%commonProperties {
#content;
}
#each $selector, $properties in $elements {
#{$selector} {
#extend %commonProperties;
#each $property, $value in $properties {
#{$property}: #{$value};
}
}
}
}
#include setSelectors((
span: (background: white),
div: (background: black)
)) {
color: red; // Common properties
}
Will return:
div, span { color: red; }
span { background: white; }
div { background: black; }
The first argument is a map containing all your selectors and their specific properties. The #content of the #mixin contains shared properties.
If you need to add a selector that doesn't have any specific property, you can add it to the map with null as key. Such as:
#include setSelectors((
span: (background: white),
div: (background: black),
i: null
)) {
color: red;
}
However, this solution doesn't allow nested selectors so I believe that separating the selectors is the best way to go.

How to add URL image as scss variable

I have following scss style. I have added bg color as variable & it is working fine. I need to add 'icons.png' also as variable.
.home {
#include themify($themes) {
background: url(images/icons.png) themed('bgcolor');
}
}
How to add "icons.png" as veritable? like
background: url(images/VARIABLENAME) themed('bgcolor');
You can try this.
$image: 'icons.png';
.home {
#include themify($themes) {
background: url(images/${$image}) themed('bgcolor');
}
}

Sass - #extend mixin, change an argument

I'm new to Sass, so if this isn't the best way of doing this type of thing, I apologise!
So, I've got a mixin for button style, like so:
#mixin button ($bg, $color, $padding, $display: inline, $radius: 0, $transition: 0.2s) {
background: $bg;
#if $radius > 0 {
#include border-radius($radius)
}
color: $color;
#if $display != inline {
display: $display;
}
#if $padding > 0 {
padding: $padding;
}
text-decoration: none;
#include transition(all, $transition, linear);
&:hover {
#if lightness($bg) > 50% {
background: darken($bg, 10%);
} #else {
background: lighten($bg, 10%);
}
}
}
and a button, like so:
.btn {
#include button(#095d94, #fff, 10px, inline-block);
}
But, now I need another button with a different background colour. So what I'm wondering is: is there a way to extend a class, and also just change an argument of the mixin that that class includes, without having to do this:
.btn2 {
#extend .btn;
background: #bad78d;
&:hover {
background: darken(#bad78d, 10%);
}
}
Is it possible to feed in another background colour? Something like,
.btn2 {
$bg: #bad78d; //i know this doesn't work
#extend .btn;
}
or like,
.btn2 ($bg: #bad78d) {
#extend .btn; //this one doesn't even make sense, but I think I'm explaining what I need... ish.
}
I think you have two options here.
Also you try to keep it dry, there is nothing too wrong about repading sometimes. So if your mixin is not too huge it'll be ok to this:
.btn {
#include button(#095d94, #fff, 10px, inline-block);
}
.btn2 {
#include button(#bad78d, #fff, 10px, inline-block);
}
But This will only be required if the difference between .btn and .btn2 is big.
If you just want to change certain properties, you may also just use the classig cascading.
.btn,.btn2 {
#include button(#095d94, #fff, 10px, inline-block);
}
.btn2 {
background-color:#bad78d;
...
}

SASS/Compass better organizing

everyone.
I start to learn how to use SASS and Compass and whant to ask advice in better organizing this snippet of code
.main-link.first-item {
#include background(image-url($bg-sprite) no-repeat -27px -39px,
linear-gradient(#4b4e58, #3f424a));
&:hover {
#include background(image-url($bg-sprite) no-repeat -27px -1px,
linear-gradient(#4b4e58, #3f424a));
}
How can I use linear gradient without repeting, but only changing position of image?
You just need to adjust the background position of the first image.
&:hover {
background-position: -27px -1px, 0 0;
}
I agree with #cimmanon's answer.
Although if you want to use the same thing over and over again on different elements in different stylesheets I would propose to create a mixin and put it into a separate sass file. Than you can import that into the files where you need it:
# mixin.css.scss
#mixin custom-background($bg-sprite, $position-vertical, $position-horizontal) {
#include background(image-url($bg-sprite) no-repeat $position-vertical $position-horizontal,
linear-gradient(#4b4e58, #3f424a));
}
# some.css.scss
#import "mixin.css.scss"
.main-link.first-item {
#include custom-background($bg-sprite, -27px, -39px);
&:hover {
#include custom-background($bg-sprite, -27px, -1px);
}
}

Margins Being Ignored. Specificity Issue

I'm using the Foundation 4 framework, and have run into an issue where the margins are being overridden by the Framework's margins, which means I am unable to apply margins to certain elements without having to use the !important keyword.
Below is my _grid.scss file, which applies the layout grid for mobile browsers.
%row {
#include grid-row;
}
%columns-1 {
#include grid-column(1);
}
%columns-2 {
#include grid-column(2);
}
%columns-3 {
#include grid-column(3);
}
%columns-4 {
#include grid-column(4);
}
%columns-5 {
#include grid-column(5);
}
%columns-6 {
#include grid-column(6);
}
%columns-7 {
#include grid-column(7);
}
%columns-8 {
#include grid-column(8);
}
%columns-9 {
#include grid-column(9);
}
%columns-10 {
#include grid-column(10);
}
%columns-11 {
#include grid-column(11);
}
%columns-12 {
#include grid-column(12);
}
header {
#extend %row;
#branding {
#extend %columns-6;
}
#main-navigation {
#extend %columns-6;
position: absolute;
top: 0;
left: 0;
}
#mobile-navigation-toggle {
}
}
#games-list {
#extend %row;
}
#blog-entries {
#extend %row;
.entry {
#extend %row;
img {
#extend %columns-4;
}
.entry-blurb {
#extend %columns-8;
}
}
}
footer {
#footer-links {
#extend %row;
.link-block {
#extend %columns-6;
}
}
}
And here is the affected line in the base.scss file:
.entry {
margin-bottom: 10px;
.entry-blurb {
.entry-description {
display: none;
}
}
}
It will only work if I apply !important to it. Looking at the Web Dev Tools I can see the issue, but no idea how to solve it:
I think the problem may be because I am defining placeholders in SASS for the grid to avoid code bloat. Usually you would use a mixin, and the code would be included directly within the elements in CSS which would override the margins for that element then.
Extend vs a mixin isn't going to make a difference here. Your selector simply has too much specificity to be overridden by such a simple selector: #blog-entries .entry is a more specific instance of .entry.
You have a few options:
Don't nest your selectors (avoids having such a strong selector like #blog-entries .entry in the first place)
Make your second selector have as much specificity (or more) than the first one
Use !important

Resources