img-replace with SASS - css

I created a mixin to manipulate easily images and replace, now my app it is growing and I don't know how to improve this code.
basically I have a include: #include img-replace("logo.png", 104px, 47px, inline-block); where I simple change the name of the image and define the pixels width and height.
I would like change it because now, some developers want just change that image name and not worry about the size anymore understand?
in that case the image has: width: 104px and height:47px, so they would like not to worry about it anymore since the next image can be bigger or smaller.
so guys any solution for this? thank you.
$path--rel : "../images";
#mixin img-replace($img, $w, $h, $disp: block) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
width: $w;
height: $h;
display: $disp;
}
.site-logo {
#include img-replace("logo.png", 104px, 47px, inline-block);
margin-top: 8px;
margin-left: 6px;
}

Using SASS, you are able to set default values against parameters in a mixin; in your example for instance, I have specified the width to be 104px by default and the height to be 47px by default:
$path--rel: "../images";
#mixin img-replace($img, $w:104px, $h:47px, $disp:null) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
width: $w;
height: $h;
#if ($disp) {display: $disp;}
}
.site-logo {
#include img-replace(
$img: "logo.png",
$disp: "inline-block"
);
margin-top: 8px;
margin-left: 6px;
}
If $w,$h or $disp are left off the default values get rendered. This essentially makes them optional.

The problem is that if you make the sizes optional, the element will have no width or height. This means the dev will still have to determine the elements' size or else it will just be 0x0 and the picture won't show!
If the problem is that the dev is too lazy to find and write the size of the images, you could always use a map to store all images and their sizes, so the function would insert the correct sizing depending on the image value. Read more here

So if i understand correctly, you want to use this mixin, by just passing the image path. But each image has a different size.
This cannot be done with SASS.
Instead, you should add your image inline, eg:
<img src="images/logo.png" alt="">
or
<img src="images/logo.png" alt="" width="104" height="74">
Otherwise the answer by #chris-spittles above is correct, meaning that you should pass the default width and height to your mixin.
And if you want to continue using the mixin you will need to pass the width and height for the images that have different dimensions.

As suggested before if you change your mixin to this -
#mixin img-replace($img, $w: null, $h: null, $disp: block) {
background-image: url('#{$path--rel}/#{$img}');
background-repeat: no-repeat;
display: $disp;
width: $w;
height: $h;
}
You can use your code flexibly without having any need to assign width and height arguments. So, now, if you write this -
.site-logo {
#include img-replace("logo.png");
margin-top: 8px;
margin-left: 6px;
}
it will get compiled to -
.site-logo {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
margin-top: 8px;
margin-left: 6px;
}
It will also preserve your previously written codes, without any changes.
Now, if you have any specific requirements, like providing default values to different types of images which a developer can assign, you can add maps in your code -
$small-img: ( w: 100px, h: 100px );
$medium-img: ( w: 200px, h: 200px );
Now you can call img-replace like this -
.site-logo {
#include img-replace("logo.png", $small-img...);
}
.site-medium-image {
#include img-replace("logo.png", $medium-img...);
}
This will get compiled to -
.site-logo {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
width: 100px;
height: 100px;
}
.site-medium-image {
background-image: url("../images/logo.png");
background-repeat: no-repeat;
display: block;
width: 200px;
height: 200px;
}
This ... makes arguments variable arguments
Sass supports "variable arguments," which are arguments at the end of
a mixin or function declaration that take all leftover arguments and
package them up as a list. These arguments look just like normal
arguments, but are followed by ...

Related

Make two Sass mixins aware of each other: the output of one mixin should be different depending on how the other mixin was applied

I'm building a reusable, customizable component. The component has several configuration options, each option has several values to choose from.
Consider these two options for example:
content-position: left, right, top, bottom, center,
content-padding: small, medium, large.
The tricky part is that the padding must be different depending on the position. E. g. when content position is left, then the left padding must be enlarged and the right padding must be reduced.
It's pretty trivial to achieve with HTML classes:
<MyComponent class="--content-position-left --content-padding-small"/>
.MyComponent__Content {
.MyComponent.--content-padding-small & {
padding: 50px;
}
.MyComponent.--content-padding-small.--content-position-left & {
padding-left: 75px;
padding-right: 25px
}
.MyComponent.--content-padding-medium & {
padding: 100px;
}
.MyComponent.--content-padding-medium.--content-position-left & {
padding-left: 150px;
padding-right: 50px;
}
}
This works perfectly when you are OK with applying configuration via HTML.
But I want to provide a way of configuring this component with pure Sass, without applying HTML classes.
It should be possible to do something like this:
.Page__Products {
.MyComponent {
#include my-component--content-position("left");
#include my-component--content-padding("small");
}
}
How can I make the my-component--content-padding know which padding side should be larger, if any?
I see two obvious solutions:
Merge two mixins into one, and make it accept two arguments, something like:
#include my-component--content-position-and-padding("small", "left");
I don't like this approach. It gets really messy when there are multiple options depending on each other.
Instead of multiple single-purpose mixins, you end up with a single "god" mixin to control everything.
Keep mixins separate, but make each mixin accept all the information it needs:
#include my-component--content-position("left");
#include my-component--content-padding("small", "left");
This is better, but I don't like the redundancy: you have to specify "left" twice.
So I wonder if I could do the following, and the padding mixin would know how exactly to distort padding depending on which position has been applied by the other mixin:
.Page__Products {
.MyComponent {
#include my-component--content-position("left");
#include my-component--content-padding("small");
}
}
This would be trivial to do with a real programming language, but Sass seems to fall short?
PS I don't want mixins to depend on the order they have been applied in and on global variables.
constants:
$none: none;
$left: left;
$right: right;
$mediumPadding: 100px;
$mediumPaddingLeft: 100px 50px 100px 150px;
$mediumPaddingRight: 100px 150px 100px 50px;
$smallPadding: 50px;
$smallPaddingLeft: 50px 25px 50px 75px;
$smallPaddingRight: 50px 75px 50px 25px;
Mixins:
#mixin align-padding($align, $padding)
{
float: $align;
padding: $padding;
}
SCSS:
.what-you-want-to-hang-it-on
{
#include align-padding($none, $mediumpadding);
}

CSS Operations with calc()

I have been working with the calc() CSS property and I have one doubt about it:
.main {
display: block;
margin-left: 4rem;
width: (100% - nav);
background: red;
}
nav {
position: fixed;
top: 0;
left: 0;
width: 4rem;
height: 100vh;
background: #292929;
border-right: 1px solid rgba(255, 255, 255, 0.1);
}
Like you see in the code I'm trying to subtract 100% - The nav WIDTH that it is in rem for the responsive mode, but obviously it doesn't work, is there any way to do this?
you can't use selectors in calc() as argument, if you want to subtract the nav's width then subtract nav's width already set:
.main {
width: calc(100% - 4rem);
/* mores styles */
}
The calc() CSS function can be used anywhere a <length>, <frequency>,
<angle>, <time>, <number>, or <integer> is required.
.....
Expressions
The expression can be any simple expression combining the following
operators, using standard operator precedence rules:
+
Addition.
-
Subtraction.
*
Multiplication. At least one of the arguments must be a <number>.
/
Division. The right-hand side must be a <number>.
The operands in the expression may be any length syntax value. You can
use different units for each value in your expression, if you wish.
You may also use parentheses to establish computation order when
needed.
I figured I would see if CSS variables could work and it seems to work for me in Chrome (though, not in Edge). I'm pretty sure compatibility issues might make this answer unacceptable until browser support increases, but I wanted to add it for future visitors or personal projects:
:root {
--nav-width: 5em;
}
nav {
background: red;
width: var(--nav-width);
height: 1em;
}
div {
background: blue;
width: calc(100% - var(--nav-width));
margin-left: var(--nav-width);
height: 1em;
}
<nav></nav>
<div></div>
Unfortunately it doesn't work that way. It needs to be a predetermined value such as "10px" or "3rem".
However you can do this:
.main {
/* ... */
width: calc(100% - 4rem);
}
You can't substract whole element(nav), it must be exact value:
<length>, <frequency>, <angle>, <time>, <number>, or <integer>
If you want to do it this way consider using preprocessor like Sass, you can add new variable and make it equal to some width value. You can substract then your width and this variable.
Sass way:
$nav-width: 10px;
.main {
display: block;
margin-left: 4rem;
width: (100% - $nav-width);
background: red;
}

What are the parameters in this LESS sprite shorthand specification?

I'm fairly new to LESS, and I have some code--which seems to work--for a sprite given to me that looks like this. First a variable is defined as follows:
#my_img: 0px 105px 0px -105px 22px 22px 44px 150px 'sprites/sprite-img.png';
This variable gets used like this:
.someClass {
.sprite(#my_img);
}
CSS output of this looks like this:
.someClass{
background-image: url("sprites/sprite-img.png");
background-position: 0 -105px;
height: 22px;
width: 22px;
}
What do the parameters in that variable definition indicate? The last one (url) is obvious, and I believe from looking at the sprite that the 3rd and 4th ones seem to be the background position offsets (X and Y). But what are the others? What do each of the 9 elements in this variable specify?
?
?
horizontal position
vertical position
width
height
?
?
background-image url
The comments on other answers helped make it clear that the .sprite usage is actually not part of LESS, but is a mixin definition, as follows. So the extra stuff in the variable definition is not used in the sprite at all, but exists for other uses.
.sprite-width(#sprite) {
width: ~`"#{sprite}".split(', ')[4]`;
}
.sprite-height(#sprite) {
height: ~`"#{sprite}".split(', ')[5]`;
}
.sprite-position(#sprite) {
#sprite-offset-x: ~`"#{sprite}".split(', ')[2]`;
#sprite-offset-y: ~`"#{sprite}".split(', ')[3]`;
background-position: #sprite-offset-x #sprite-offset-y;
}
.sprite-image(#sprite) {
#sprite-image: ~`"#{sprite}".split(', ')[8].slice(1, -2)`;
background-image: url(#sprite-image);
}
.sprite(#sprite) {
.sprite-image(#sprite);
.sprite-position(#sprite);
.sprite-width(#sprite);
.sprite-height(#sprite);
}

Issue with background-position in SASS mixin

I'm in the midst of creating a SASS mixin that will let me take a spritesheet of social media icons and display them, but I'm having an issue with the background-position when it's hovered over, here's the SASS code:
#mixin social-button($imgurl,$xpos,$ypos,$height,$width) {
background-image: url($imgurl);
background-position: $xpos $ypos;
display: block;
float: left;
height: $height;
width: $width;
&:hover {
background-position: 0px -$height;
}
& span {
position: absolute;
top: -999em;
}
}
And my include:
a.facebook {
#include social-button("../img/social-buttons.png",0px,0px,26px,26px);
}
If you'd like to see/use the spritesheet in action, I've uploaded it here: http://i49.tinypic.com/2evtbwp.png
Here's the HTML as well:
<a class="facebook" href="#"><span>Facebook</span></a>
Essentially the CSS output for the hover is displaying as this:
a.facebook:hover {
background-position: -26px;
}
And in Firebug it displays as this:
a.facebook:hover {
background-position: -26px center;
}
Any help is greatly appreciated, I'm pretty stumped on what I'm doing wrong at this point.. thanks!
PS. I'm going to be creating 5 of these, so I wouldn't mind creating a loop that could auto generate that for me, but at the present time it's not a huge deal, just need to get the hovers working first!
You have to add parentheses around variables when you change them to negatives otherwise it just does the math (0px - $height):
background-position: 0px (-$height);
You probably want to fix the 0px, too.

Compass sprite generating too much CSS classes

I am using compass to generate sprite images. And I have MANY sprite icons, and it is generating too much CSS code (too many class selectors for the background image). So lets analyze the compass sprite code:
as you can see here http://compass-style.org/help/tutorials/spriting/
#import "my-icons/*.png";
#include all-my-icons-sprites;
Will generate:
.my-icons-sprite,
.my-icons-delete,
.my-icons-edit,
.my-icons-new,
.my-icons-save { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
If you see I use this way: <div class="my-icons-sprite my-icons-delete"></div>
I want Compass to generate this code:
.my-icons-sprite { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
Else each new image, it'll add for background and background position. Causing too many selectors.
Is there a configuration for that?
Thanks
Have you tried this snippet for Compass?
$icons: sprite-map("icons/*.png");
i{
background: $icons;
display: inline-block; // or block
}
#each $i in sprite_names($icons){
.icn-#{$i}{
background-position: sprite-position($icons, $i);
#include sprite-dimensions($icons, $i);
}
}
This example uses the <i></i>-tag with a class containing the prefix icn- combined with the filename of the separate .png-files in your icons-folder. Like this:
<i class="icn-delete"></i>
The generated CSS looks like this:
i {
background: url('/path/to/generated/spritemap/my-icons-xxxxxxxxxxx.png');
display: inline-block;
}
.icn-delete {
background-position: 0 0;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
.icn-edit{
background-position: 0 -32px;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
.icn-new {
background-position: 0 -64px;
height: 32px; // assuming the width is 32px
width: 32px; // assuming the height is 32px
}
...
..
.
Still, I haven't quite figured out how to use this in combination with Compass' Magic Selectors.
Magic Selectors works very nice when you need different states (:hover, :active, :target). All you have to do is name your files like this: filename_state.png (delete_hover.png, delete_active.png etc). Compass' Magic Selectors then automatically generates css for :hover, :active and :target (delete:hover, delete_hover and delete-hover). This way you are quite free to choose how you would represent a state-change.
If you, in my first example, has filenames with the postfix for hover/ active states, the snippet only writes CSS like this:
.icn-edit_hover {
background-position: -32px -32px;
height: 32px;
width: 32px;
}
I'd really like to have it print this:
.icn-edit:hover, .icn-edit_hover, .icn-edit-hover{
background-position: 0 -32px;
height: 32px;
width: 32px;
}
like the traditional Compass' Magic Selectors does. Any idea?
In my opinion, it seems like the best of both worlds (less HTML and CSS) would be to have this code (using an attribute selector for the image):
HTML
<div class="my-icons-delete"></div>
CSS
[class^="my-icons-"] { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }
.my-icons-delete { background-position: 0 0; }
.my-icons-edit { background-position: 0 -32px; }
.my-icons-new { background-position: 0 -64px; }
.my-icons-save { background-position: 0 -96px; }
Unfortunately, I do not know how to get Compass to export like that. However, unless you are using Compass dynamically rather than just to build your back end static css, you could just change it once generated.
For anyone looking to the answer to ScottS question.
How can I use a css selector for anything starting with a baseclass
Try this:
http://codepen.io/Acts7/pen/nwsEb
I'm pasting the code below.
the spriteGen mixin requires two parameters
1) the baseclass you want to use (in ScottS case --- "myicons"
2) the second parameter is the folder location
Also DONT forget the "." before #{$mySpriteBaseClass}.
Otherwise you get >> myicons-home_icon{background-position:...}
(notice no . for class name selector)
// _custom.scss
// ---------------------------------------------------------
// Sprite Generation
--------------------- */
#include spriteGen('sprites','sprites');
// _mixins.scss
// ---------------------------------------------------------
// Sprite Generation Mixin with options
#mixin spriteGen($mySpriteBaseClass:'.spritebc',$mySpriteFolder:'sprites'){
$mySprites:$mySpriteFolder + "/*.png";
$spritefoldername-map: sprite-map($mySprites,
$spacing: 10px,
$layout: vertical
);
// if using base class as starter for sprite name class
[class^="#{$mySpriteBaseClass}"]{
/*// if using a separate base class
.#{$mySpriteBaseClass}{*/
// TODO:
// Add if/else to set width globally
// or let spriting assign it per each
//width: 48px;
//height: 48px;
display: inline-block;
vertical-align: middle;
background: $spritefoldername-map no-repeat;
}
#each $sprite in sprite_names($spritefoldername-map) {
// if using sprite base class as prefix to full sprite class name
.#{$mySpriteBaseClass}-#{$sprite} {
/*// if using separate base class and sprite name class
.#{$sprite} {*/
background-position: sprite-position($spritefoldername-map, $sprite);
#include sprite-dimensions($spritefoldername-map, $sprite);
}
}
}
What's wrong with the current output?
You can already assign my-icons-delete/edit/new/save only, this is semantic enough - it already says it's an icon and it's a delete icon.
This is what I'm currently doing, it requires Sass 3.3 though:
$icons: sprite-map('icons/*.png');
.icon {
background: $icons;
display: inline-block;
vertical-align: middle;
}
#each $i in sprite_names($icons) {
$underscore: str-index($i, _);
#if ($underscore < 1) {
.icon--#{$i} {
background-position: sprite-position($icons, $i);
#include sprite-dimensions($icons, $i);
}
} #else {
$prefix: str-slice($i, 0, $underscore - 1);
$postfix: str-slice($i, $underscore + 1);
.icon--#{$prefix}:#{$postfix} {
background-position: sprite-position($icons, $i);
}
}
}
I'm using BEM here so it assumes you'll use this like <i class="icon icon--star></i>, so if if you have a "star.png" and "star_hover.png" images it'll generate .icon--star and .icon--star:hover class names.

Resources