How to make Sass mixin that extends parent? - css

You could hard-code extending a selector in Sass like:
.box {
width: 100px;
height: 100px;
color: red;
}
.box-green {
#extend .box;
color: green;
}
And then .box-green has all the properties of .box, with its own additional ones. What I want to do is write a generic mixin that does that - takes a class, adds all its properties with its own and adds a modifier to the class name. If something like this pseudo-code worked (which it doesn't), it would be ideal.
.box {
width: 100px;
height: 100px;
color: red;
#include make-modifier(&, green) {
color: green;
}
}
#mixin make-modifier(parent, modifier-name) {
.#{$parent}-#{$modifier-name} {
#content;
}
}
Is there a way to do it? Even if only in the latest version it's fine.

I would suggest a simpler solution like this
#mixin box($color:"") {
width: 100px;
height: 100px;
color: red;
#if $color !="" {
&-#{$color} {
color: $color;
}
}
}
.box {
#include box(green);
}
The output will be:
.box {
width: 100px;
height: 100px;
color: red;
}
.box-green {
color: green;
}
An example: http://sassmeister.com/gist/04cb6a6fe14972c3ffe4

Related

Using only CSS is it possible to set a property based on class name?

Say I have some css like:
.modal-350 {
width: 350px;
}
.modal-400 {
width: 400px;
}
.modal-500 {
width: 500px;
}
etc. Using only CSS is it possible to set the width (or other property) just from the class name?
I know in javascript this is easy and also I could just use:
.modal-auto {
display: inline-block;
width:auto;
}
It's not production code, I'm just curious.
No. Even though we can use variables in CSS, we can only do so in property values and not in selector names. So something like this will not work:
.modal-$size {
width: ${size}px;
}
You can, however, use a CSS preprocessor such as LESS or SASS, and generate such rules automagically, given the requested sizes.
A SASS example:
$modal-sizes: 50 100 200 500;
%modal-default {
border-radius: 50%;
color: red;
background: green;
border-color: blue;
}
#mixin modals {
#each $size in $modal-sizes {
.modal-#{$size} {
#extend %modal-default;
width: #{$size}px;
}
}
}
#include modals;
This will compile as:
.modal-50, .modal-100, .modal-200, .modal-500 {
border-radius: 50%;
color: red;
background: green;
border-color: blue;
}
.modal-50 {
width: 50px;
}
.modal-100 {
width: 100px;
}
.modal-200 {
width: 200px;
}
.modal-500 {
width: 500px;
}

Trigger hover state in a mixin

I've got a Less mixin with a default state and a hover state.
Now I need to activate the hover state in the mixin when I'm hovering over the parent element.
Less
#icons () {
.settingsIcon() {
background: url("settings.png");
&:hover {
background: url("settings_hover.png");
}
}
}
.SettingsButton {
height: 50px;
width: 200px;
.icon {
#icons > .settingsIcon();
}
}
HTML:
<div class="SettingsButton" >
<span>Settings</span>
<div class="icon"></div>
</div>
I need to make sure that the same effect that happens on settingsIcon hover is triggered on the button hover also.
So I think I need to do something like this, where I 'call' the hover of settingsIcon somehow. Something like this (not valid Less code)
.SettingsButton:hover {
.icon {
#icons > .settingsIcon():hover;
}
}
How would I do that?
It would probably be better to re-write your mixins like in the below example. Essentially we are splitting your base setting and hover setting into two different mixins within the same namespace (#icons()) and then call them as required.
#icons () {
.settingsIcon() {
background: url("settings.png");
}
.settingsIconHover(){
background: url("settings_hover.png");
}
}
.SettingsButton {
height: 50px;
width: 200px;
.icon {
#icons > .settingsIcon();
&:hover{
#icons > .settingsIconHover();
}
}
&:hover .icon{
#icons > .settingsIconHover();
}
}
Another way to achieve this while still maintaining only a single entry in #icons for all the states is to move the .icon selector also to be within the mixin like in the below snippet.
#icons () {
.settingsIcon() {
.icon{
background: url("settings.png");
}
&:hover .icon, & .icon:hover{ /* & here means .SettingsButton */
background: url("settings_hover.png");
}
}
}
.SettingsButton {
height: 50px;
width: 200px;
#icons > .settingsIcon();
}
It is my understanding that you can solve this issue by using extend too.
.settingsIcon {
background: url("settings.png");
&:hover {
background: url("settings_hover.png");
}
}
.SettingsButton {
height: 50px;
width: 200px;
&:extend(.settingsIcon all);
.icon {
&:extend(.settingsIcon all);
}
}
outputs:
.settingsIcon,
.SettingsButton,
.SettingsButton .icon {
background: url("settings.png");
}
.settingsIcon:hover,
.SettingsButton:hover,
.SettingsButton .icon:hover {
background: url("settings_hover.png");
}
.SettingsButton {
height: 50px;
width: 200px;
}
Notice that the above generate a not used .settingsIcon selector in your CSS. Cause Less does not enable you to extend mixins (see: https://github.com/less/less.js/issues/1177) which do not output, you can only solve this by putting the .settingsIcon declaration in a different file a #import that file by using the reference keyword:
settingsicon.less:
.settingsIcon {
background: url("settings.png");
&:hover {
background: url("settings_hover.png");
}
}
project.less:
#import (reference) "settingsicon.less";
.SettingsButton {
height: 50px;
width: 200px;
&:extend(.settingsIcon all);
.icon {
&:extend(.settingsIcon all);
}
}
Now compiling project.less generates:
.SettingsButton,
.SettingsButton .icon {
background: url("settings.png");
}
.SettingsButton:hover,
.SettingsButton .icon:hover {
background: url("settings_hover.png");
}
.SettingsButton {
height: 50px;
width: 200px;
}
--
NB aren't you looking for CSS for hover that includes all child elements in the first place?
update
#harry wrote:
My understanding was that the same background needed to be applied for .SettingsButton:hover .icon and .SettingsButton .icon:hover only.
I think that makes sense. You can do that by using extend too, but i also agree that extending the same class twice is not so DRY too.
With settingsicon.less being again:
.settingsIcon {
background: url("settings.png");
&:hover {
background: url("settings_hover.png");
}
}
You can use:
#import (reference) "settingsicon.less";
.SettingsButton {
height: 50px;
width: 200px;
&.icon {
&:extend(.settingsIcon);
}
&:hover, & .icon:hover {
&:extend(.settingsIcon:hover);
}
}
or:
#import (reference) "settingsicon.less";
.SettingsButton {
height: 50px;
width: 200px;
& .icon {
&:extend(.settingsIcon all);
}
&:hover {
&:extend(.settingsIcon:hover);
}
}
which both compile into:
.SettingsButton.icon {
background: url("settings.png");
}
.SettingsButton:hover,
.SettingsButton .icon:hover {
background: url("settings_hover.png");
}
.SettingsButton {
height: 50px;
width: 200px;
}

SASS: attach class to amperand operator [duplicate]

This question already has an answer here:
Append the parent selector to the end with Sass
(1 answer)
Closed 8 years ago.
My current SASS looks like this:
.container {
width: 200px;
.element {
color: red;
}
}
I want to achieve that ONLY containers that have the class .small have elements with color:blue. I have tried the following, but this does not work:
.container {
width: 200px;
.element {
color: red;
.small& {
color: blue
}
}
}
My desired compilation is: .small.container .element { color: blue }
How can I achieve that?
You should consider rearranging your scss like this:
.container {
width: 200px;
.element
{
color: red;
}
&.small .element
{
color: blue;
}
}
This will output follwing css:
.container {
width: 200px;
}
.container .element {
color: red;
}
.container.small .element {
color: blue;
}
Read here about the parent referencing selector of sass
If I undestood correctly....This Sass:
.container {
width: 200px;
.element {
color: blue;
}
&.small .element{
color: red;
}
}
compiles to:
.container {
width: 200px;
}
.container .element {
color: blue;
}
.container.small .element {
color: red;
}
Hopes this helps

LESS puts spaces in wrong places

So... I'am creating a small bootstrap and i want it efficiently done, so i've chose the LESS to do some job for me. And i found that LESS compiler puts spaces between classes when it is written like this:
div.cb {
input[type="text"] {
border: 1px #d9d9d9 solid;
height: 15px;
padding: 5px;
.large {
width: 250px;
}
.medium {
width: 150px;
}
.small {
width: 50px;
}
.fill {
width: 100%;
}
}
}
results in:
div.cb input[type="text"] {
border: 1px #d9d9d9 solid;
height: 15px;
padding: 5px;
}
div.cb input[type="text"] .large {
width: 250px;
}
div.cb input[type="text"] .medium {
width: 150px;
}
div.cb input[type="text"] .small {
width: 50px;
}
div.cb input[type="text"] .fill {
width: 100%;
}
and the gaps between the element and classes prevents in my browser (chrome) in the correct processing. Is there a way to have same or similar code in LESS and have right outputting CSS? Without those gaps...
With less you can reference the parent of a code block by using &
So this:
.class
{
.anotherClass
{
background: red;
}
}
Compiles to:
.class .anotherClass { background: red; }
Whereas this:
.class
{
&.anotherClass
{
background: red;
}
}
Compiles to this:
.class.anotherClass { background: red; }
I hope that makes the difference obvious

Making use of CSS vs Sass (SCSS) - base class issues and redundency

I'm trying to clean up my CSS to be cleaner by using SCSS.
Standard CSS:
.dark-hr,
.light-hr {
width: 100%;
height: 1px;
margin: 15px 0px;
}
.dark-hr {
background-color: #595959;
}
.light-hr {
background-color: #cccccc;
}
vs SCSS:
.generic-hr {
width: 100%;
height: 1px;
margin: 15px 0px;
}
.dark-hr {
#extend .generic-hr;
background-color: #595959;
}
.light-hr {
#extend .generic-hr;
background-color: #cccccc;
}
Is there any way to avoid creating the 'generic-hr' class that won't be used? I was hoping that some kind of nest would work well.
In this scenario the CSS is definitely way cleaner and more readable than SCSS.
Ideally I would need this to work in SCSS:
.## {
// base class that is not outputted
.dark-hr {
//attributes the extend the base class '.##'
}
.light-hr {
//attributes the extend the base class '.##'
}
}
OUTPUT:
.dark-hr, .light-hr {
//shared attributes defined by '.##'
}
.dark-hr {
// overrides
}
.light-hr {
// overrides
}
What you're wanting to use is an extend class (I call them "silent classes"), which is signified by using a % instead of a ..
hr%base {
width: 100%;
height: 1px;
margin: 15px 0px;
}
.dark-hr {
#extend hr%base;
background-color: #595959;
}
.light-hr {
#extend hr%base;
background-color: #cccccc;
}
Wouldn't you normally do something like this:
.generic-hr {
width: 100%;
height: 1px;
margin: 15px 0px;
&.dark {
background-color: #595959;
}
&.light {
background-color: #cccccc;
}
}
My pattern for this kind of thing is a mixin:
#mixin generic-hr {
width: 100%;
height: 1px;
margin: 15px 0px;
}
.dark-hr {
#include generic-hr;
background-color: #595959;
}
.light-hr {
#include generic-hr;
background-color: #cccccc;
}
This has the added advantage of being extensible, so if you find yourself needing several selectors with really similar properties you can add in variables:
#mixin generic-hr($background-color: transparent) {
width: 100%;
height: 1px;
margin: 15px 0px;
background-color: $background-color;
}
.dark-hr {
#include generic-hr(#595959);
}
.light-hr {
#include generic-hr(#cccccc);
}
.medium-hr {
#include generic-hr(#818181);
}

Resources