I'm using SASS's handy ampersand notation to add BEM-style modifiers to my classes:
.box {
display: inline-block;
width: 100px;
height: 100px;
background: magenta;
&--selected {
background: cyan;
}
}
I'm exploring the possibility of only having to apply a single class to my elements (ex: <div class="box--selected">...</div> as opposed to <div class="box box--selected">...</div>). I'd like to avoid using #extend, it casts too wide a net. Currently I'm using mixins, which are good, but a little verbose to use for every single class with a modifier.
What I'd really like to do is get &--selected to inherit all the properties from the parent enclosure, and only from the parent enclosure - ie ignore any other class definitions of .box that careless devs may have inserted.
I know you've expressed the desire to avoid #extend but this method may allow you to avoid other dev's definitions of .box while still achieving your goal.
You could use placeholders to create your own parent enclosure and extend the placeholder(example of placeholder extension) inheriting only from the placeholder. As a placeholder there is no chance of conflicts with classes from other devs on .box:
%box{
display: inline-block;
width: 100px;
height: 100px;
background: magenta;
}
.box--selected{
#extend %box;
background: cyan;
}
Compared to mixins this method lacks the use of parameters like the following example from the article mentioned above [1]:
#mixin padMasterJ ($param: 10px) {
padding: $param;
}
Another thing worth noting is that when #extend is used on a parent selection the result will include all nested selectors #extend cannot be used to directly extend a nested selector.
Related
The code is as follows:
.home-feat2 {
background-color: stencilColor("color-greyLight");
img {width: 10rem;}
margin-bottom: spacing("single");
#include breakpoint("medium") {
margin-bottom: 3rem;
}
}
Expected declaration to come before rule - order/order points to the line with margin-bottom: spacing("single"); however I tried looking up what this error meant but I can't find a lot of descriptive documentation on stylelint. Maybe it's because I just don't understand the terminology, but I'm having trouble finding anything on this subject. Any help is appreciated.
Your linters expects you to write declarations before rules.
In CSS, a declaration is the key-value pair of a CSS property and its value, like margin-bottom: spacing("single").
See a visual representation of a declaration block.
A rule is the block defined by one or multiple selectors, containing declarations, like: img { width: 10rem; }.
See a visual representation of a rule set.
What it means for you, it means that you should probably write the rule img {} after the declarations:
.home-feat2 {
background-color: stencilColor("color-greyLight");
margin-bottom: spacing("single");
#include breakpoint("medium") {
margin-bottom: 3rem;
}
img {width: 10rem;}
}
This specific rule purpose is to allow an easy to read code.
When applied, you can see at the first glance that background-color and margin-bottom are applied to .home-feat2 and width is applied to img.
edit: added some additional informations thanks to jeddy3
I don't know why but while compiling with grunt or anything there is an error called invalid property name
#flotTip {
border: none !important;
font-size: $font-size-small !important;
line-height: 1px !important;
#extend .tooltip-inner() !important;
}
in the above code in the line-height it produces an undefined property. My task was to convert all less files into sass files. Used many solutions to convert all of them to sass as far as I can find. But this one I can't find any solution. Can anyone answer what might be the problem?
Extend is only for extending simple selectors, like class, element, or id. You cannot use !important with #extend. This is the correct way to use extend:
.foo {
color: red;
}
#flotTip {
#extend .foo;
}
You may be confused confusing extends with mixins, which also cannot use !important. This is the correct way to use mixins:
#mixin foo() {
color: red;
}
#flotTip {
#include foo();
}
The line-height: 1px !important; line looks fine. The problem is with the following line. If you're trying to include a mixin, use #include and don't prefix the mixin's name with . (dot). Also, don't put !important after it.
I would guess that you are using #extend incorrectly. See the docs here: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#how_it_works
I've been reading up on Block-Element-Modifier naming conventions, semantically-named style rules, etc. and I'd like to replace multiple class names in my html with a single, semantic class name. Can't seem to make it happen, though. Example:
I'd like to replace my Bootstrap navbar
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
with
<nav class="header__navbar" role="navigation">
I know I could use jQuery to do this procedurally, but I'd prefer not to. I've tried several approaches using variable names, #{} interpolation syntax, mixins, and #extend, etc. but just get syntax errors or invalid results.
Is this just not something Sass was designed to do?
Is this just not something Sass was designed to do?
Sass does support this use case.
What you need is extending.
Have navbar, navbar-default and navbar-fixed-top classes defined as you would with a non-semantic approach:
.navbar { display: table; }
.navbar-default { background-color: deeppink; }
.navbar-fixed-top { position: fixed; top: 0; left: 0; width: 100%; }
Below, extend your header__navbar class with those:
.header__navbar {
#extend .navbar;
#extend .navbar-default;
#extend .navbar-fixed-top;
}
Once you get rid of non-semantic classes in your HTML markup completely, you no longer need them in your CSS. So turn them into placeholder selectors aka silent selectors. To do so, replace the . with % both in the rules where they are defined and in #extend directives that use them. E. g.:
%navbar { display: table; }
%navbar-default { background-color: deeppink; }
%navbar-fixed-top { position: fixed; top: 0; left: 0; width: 100%; }
.header__navbar {
#extend %navbar;
#extend %navbar-default;
#extend %navbar-fixed-top;
}
Another way is you can extend multiple class in an single line like:
Normal way
.header__navbar {
#extend .navbar;
#extend .navbar-default;
#extend .navbar-fixed-top;
}
Extend multiple class in single line
.header__navbar {
#extend #{'.navbar','.navbar-default','.navbar-fixed-top'};
}
This extend example same work for first example.
Thanks
In Sass, I can't quite discern the difference between using #include with a mixin and using #extend with a placeholder class. Don't they amount to the same thing?
Extends do not allow customization, but they produce very efficient CSS.
%button
background-color: lightgrey
&:hover, &:active
background-color: white
a
#extend %button
button
#extend %button
Result:
a, button {
background-color: lightgrey;
}
a:hover, button:hover, a:active, button:active {
background-color: white;
}
With mixins, you get duplicated CSS, but you can use arguments to modify the result for each usage.
=button($main-color: lightgrey, $active-color: white)
background-color: $main-color
border: 1px solid black
border-radius: 0.2em
&:hover, &:active
background-color: $active-color
a
+button
button
+button(pink, red)
Results in:
a {
background-color: lightgrey;
border: 1px solid black;
border-radius: 0.2em;
}
a:hover, a:active {
background-color: white;
}
button {
background-color: pink;
border: 1px solid black;
border-radius: 0.2em;
}
button:hover, button:active {
background-color: red;
}
Please follow this consecutive set of code examples to see how you can make your code cleaner and more maintainable by using extends and mixins effectively: http://thecodingdesigner.com/posts/balancing
Note that SASS unfortunately does not allow using extends inside media queries (and corresponding example from the above link is wrong). In the situation where you need to extend based on media queries, use a mixin:
=active
display: block
background-color: pink
%active
+active
#main-menu
#extend %active // Active by default
#secondary-menu
#media (min-width: 20em)
+active // Active only on wide screens
Result:
#main-menu {
display: block;
background-color: pink;
}
#media (min-width: 20em) {
#secondary-menu {
display: block;
background-color: pink;
}
}
Duplication is inevitable in this case, but you shouldn't care too much about it because web server's gzip compression will take care of it.
PS Note that you can declare placeholder classes within media queries.
Update 2014-12-28: Extends produce more compact CSS than mixins do, but this benefit is diminished when CSS is gzipped. If your server serves gzipped CSS (it really should!), then extends give you almost no benefit. So you can always use mixins! More on this here: http://www.sitepoint.com/sass-extend-nobody-told-you/
A good approach is to use both - create a mixin that will allow you lots of customisation and then make extends for common configurations of that mixin. For example (SCSS Syntax):
#mixin my-button($size: 15, $color: red) {
#include inline-block;
#include border-radius(5px);
font-size: $size + px;
background-color: $color;
}
%button {
#include my-button;
}
%alt-button {
#include my-button(15, green);
}
%big-button {
#include my-button(25);
}
This saves you from calling the my-button mixin over and over. It also means you don't have to remember the settings for common buttons but you still have the ability to make a super unique, one-off button should you choose.
I take this example from a blog post I wrote not long ago. Hope this helps.
In my opinion extends are pure evil and should be avoided. Here is why:
given the scss:
%mystyle {color: blue;}
.mystyle-class {#extend %mystyle}
//basically anything not understood by target browser (such as :last-child in IE8):
::-webkit-input-placeholder {#extend %mystyle}
The following css will be generated:
.mystyle-class, ::-webkit-input-placeholder { //invalid in non-webkit browsers
color: blue;
}
When a browser doesn’t understand a selector, it invalidates the entire line of selectors. This means that your precious mystyle-class is no longer blue (for many browsers).
What does this really mean? If at any time you use an extend where a browser may not understand the selector every other use of the extend will be invalidated.
This behavior also allows for evil nesting:
%mystyle {color: blue;}
#mixin mystyle-mixin {#extend %mystyle; height: 0;}
::-webkit-input-placeholder {#include mystyle-mixin}
//you thought nesting in a mixin would make it safe?
.mystyle-class {#extend %mystyle;}
Result:
::-webkit-input-placeholder, .mystyle-class { //invalid in non-webkit browsers
color: blue;
}
::-webkit-input-placeholder {
height: 0;
}
Tl;dr: #extend is perfectly ok for as long as you never use it with any browser spesific selectors. If you do, it will suddenly tear down the styles wherever you have used it. Try to rely on mixins instead!
Use mixins if it accepts a parameter, where the compiled output will change depending on what you pass into it.
#include opacity(0.1);
Use extend (with placeholder) for any static repeatable blocks of styles.
color: blue;
font-weight: bold;
font-size: 2em;
I totally agree with the previous answer by d4nyll. There is a text about extend option and while I was researching this theme I found a lot of complaints about extend, so just have in mind that and if there is a possibility to use mixin instead of extend, just skip extend.
Is it possible to refer to a property previously defined in a selector without introducing an intermediate variable?
I'd like to say something like:
.foo {
padding: 15px;
width: 300px - $padding;
}
I know that $padding syntactically looks for a defined variable, I only use it in the above example to illustrate what I want to achieve in functionality.
The above example would be equivalent to this:
.foo {
$padding: 15px;
padding: $padding;
width: 300px - $padding * 2;
}
No, you can't, and it would be great.
I haven't tested, but as far as I know the only css pre-processor that can do that is stylus. Look at the variable section in its documentation, where it says Property Lookup. It works that way:
.foo {
padding: 15px;
width: 300px - #padding * 2;
}
But no, in Sass you can't, as far as I'm concerned.
If its an option to use an other preprocessor then scss, I really recommend using Stylus. There is a feature called Property lookup which is exactly what you want.