Trigger hover state in a mixin - css

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;
}

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;
}

How to scope to a class instead of a style

Is there a way to shorten the following code?
.outer-a {
.inner {
background-color: white;
}
}
.outer-b {
.inner {
background-color: white;
}
}
My expected output:
.outer-a .inner {
background-color: white;
}
.outer-b .inner {
background-color: white;
}
I can't find any functionality in Less documentation that provides this.
You can club the parents like
.outer-a,
.outer-b {
.inner {
background-color: white;
}
}
I guess you could try with commas. Something like this:
.outer-a, .outer-b {
.inner {
background-color: white;
}
}
This then compiles into this
.outer-a .inner,
.outer-b .inner {
background-color: white;
}

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

SASS rule reuse

I have a set of icons with different colors and each color is used with different status declared with CSS classes. For example, <span class="icon icon--app"><span> gives a gray app icon while <span class="icon icon--app icon__light icon__selected"><span> gives a white app icon.
The following code is written in SCSS.
span.icon {
display: inline-block;
width: 32px;
height: 32px;
&.icon--app {
background: url(../images/app_gray.png);
&.icon__selected {
background: url(../images/app.png);
}
&.icon__light {
background: url(../images/app_gray.png);
&.icon__selected {
background: url(../images/app_white.png);
}
}
}
&.icon--device {
background: url(../images/device_gray.png);
&.icon__selected {
background: url(../images/device.png);
}
&.icon__light {
background: url(../images/device_gray.png);
&.icon__selected {
background: url(../images/device_white.png);
}
}
}
}
The problem is, there's a long list of CSS rules as above, which shares much similarity for app and device and other icons. I wonder if I can simplify these CSS rules using SASS?
I think you can use mixin in Sass:
e.g.
#mixin icon($type) {
.icon-#{$type} {
background: url(../images/#{$type}_gray.png);
}
}
#include icon(app);
#include icon(device);
I created a mixin for you:
#mixin icon($type) {
&.icon--#{$type} {
background: url(../images/#{$type}_gray.png);
&.icon__selected {
background: url(../images/#{$type});
}
&.icon__light {
background: url(../images/#{$type});
&.icon__selected {
background: url(../images/#{$type}_white.png)
}
}
}
}
span.icon {
display: inline-block;
width: 32px;
height: 32px;
#include icon(app);
#include icon(device);
}

How to make Sass mixin that extends parent?

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

Resources