Can I extend external scss rule without including it in the output? - css

GIVEN that there is existing scss file that defines rules like .btn {..} or .btn-primary...
I WANT to declare my own rules by extending existing rules
.my-button {
#extend .btn
#extend .btn-primary
}
without actually including the .btn and .btn-primary classes in my generated css file?
Normally I need to #import exiting.scss, but that includes all the rules from the file in my css output.

Sass does not currently support this by default, neither with the #import nor #use rule.
Nonetheless, if you (can) use npm packages (npm / yarn) in your project, then node-sass-magic-importer may come in handy for you.
In your example, you could do the following:
#import '{ .btn, .btn-primary } from ~bootstrap';
.my-button {
#extend .btn
#extend .btn-primary
}
Note that the above will not do exactly what you desire – it will still import the other two classes though at least not the entire stylesheet. If you'd still like to go one step further, you could do:
#import '{ .btn as .my-button } from /bootstrap/_buttons.scss';
#import '[variables] from /bootstrap/_variables.scss';
#import '[mixins] from /bootstrap/mixins/_buttons.scss';
.my-button {
#include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border);
}

I will recommend you to use #mixins and #include for this.
Although because as you said in your question, you are using an existing file (probably third party) that defines this rules. It may be tedious to turn the classes from this file into mixins.
so if you are going to use only a few classes from this file I recommend you to do that.
You will have to turn:
.btn{
/*
some cool styles
*/
}
into:
#mixin{
/*
cooler styles
*/
}
but still mixins as declared in the Sass documentation do exactly what you want.
source code SCSS:
#mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
#mixin horizontal-list {
#include reset-list;
li {
display: inline-block;
margin: {
left: -2px;
right: 2em;
}
}
}
nav ul {
#include horizontal-list;
}
result CSS:
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav ul li {
display: inline-block;
margin-left: -2px;
margin-right: 2em;
}

when you import as #import in scss and when you do a production build the webpack dependancy graph will only include it once at the top level as it becomes a common chunk since you are using it in multiple places. Yes there is a side effect that unused scss is also included as there isn't much of tree shaking that is done.
Hence it should not affect the production build.

Related

How do I create a scoped class without #include?

I managed to create a scoped CSS class like this:
.container {
#import "./baa";
/* other props ... */
}
but since #import is getting depreciated, what are my options to make a scoped CSS class now?
If you want to keep your CSS separated from any "framework" setup, the best way is probably to use the mixins system. It's definitely not the best way, but it's what come the closest as what you want, without going with CSS modules or else.
You can define your mixins in some file and import them where you need.
Exemple:
#mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
#mixin horizontal-list {
#include reset-list;
li {
display: inline-block;
margin: {
left: -2px;
right: 2em;
}
}
}
nav ul {
#include horizontal-list;
}

How do I wrap a file in a mixin to #use in a scss file?

Not sure what the best way to import is but '#import' is deprecated and #use should be used instead. I have a grid file that I want to include in style.scss .
_grid.scss
.main-content {
display:grid;
/* Set up grid stuff */
}
header{
grid-area: header;
}
I need to import into here.
style.scss
body{
/* More code here */
}
The final complied code should look like this:
.main-content {
display:grid;
/* Set up grid stuff */
}
header{
grid-area: header;
}
body{
/* More code here */
}
I know this is wrong, but this is what I have tried:
#use 'grid' as *;
$main-content
What about using a mixin?
_grid.scss
#mixin grid {
/* Wrap all the code here from _grid.scss */
.main-content {
display:grid;
/* Set up grid stuff */
}
header{
grid-area: header;
}
}
style.css
#use 'grid' as *;
#include $grid
body{
}
#use is really tricky at the moment ... a real big impact to the language.
(A) First of all you should check if you indeed use the most actual SASS version: DART SASS. HEAD UP: MOST actual compilers even in good maintained frameworks DO NOT YET!!! Here are information about how to find/install compiler with most actual version:
Solution with installing via NPM:
Is it better to use the Live Sass Compiler (VS Code extension) or to install and run Sass via npm? (+ tips how to change from node-sass to dart-sass)
If more comfortable with VS Code:
Live Sass Compiler - #use causes compilation error
(B) #use changes the structure how to write SASS code substantial.
What NOT works with #use is to import code right in the middle of the code like you can do with #import. So, if you need to do so you are right with your last code example using a mixin including code to the body tag. But you need to move the mixin to the body tag where you want to include the code:
//### > SASS _grid.scss
#mixin grid {
.main-content {
display:grid;
/* Set up grid stuff */
}
header{
grid-area: header;
}
}
//### > SASS main.scss
#use 'grid' as *;
body {
#include grid;
}
//### --> compiles to CSS
body .main-content {
display: grid;
/* Set up grid stuff */
}
body header {
grid-area: header;
}
But honestly there are very few scenarios when you need to nest your grid css into the body tag!
If you divide your SASS into partial files and do the output in a main file which only wraps all code together you are able to do use #use very similar to the common use of #import. Example:
//### > SASS PARTIAL _typography.scss
/* TYPOGRAPHY */
html {
font-family: sans-serif;
}
h1 {
font-size: 24px;
}
//### > SASS PARTIAL _structure.scss
/* STRUCTURE */
header{
padding: 10px 20px;
background: darkgray;
}
main {
padding: 20px 20px;
background: white;
}
footer {
padding: 20px 20px 80px 20px;
background: black;
}
//### > SASS PARTIAL _grid.scss
/* GRID HELPER CLASSES */
.grid-col_4 {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
}
//### > SASS MAIN main.scss
// WRAP PARTIALS TOGETHER
// only '#use' files, no css code here
#use 'typography' as *;
#use 'structure' as *;
#use 'grid' as *;
//### --> compiles to CSS
/* TYPOGRAPHY */
html {
font-family: sans-serif;
}
h1 {
font-size: 24px;
}
/* STRUCTURE */
header {
padding: 10px 20px;
background: darkgray;
}
main {
padding: 20px 20px;
background: white;
}
footer {
padding: 20px 20px 80px 20px;
background: black;
}
/* GRID HELPER CLASSES */
.grid-col_4 {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
}
HEAD UP:
Even that seems very similar to the use of #import there is a VERY BIG DIFFERENCE to #import.
If you use variables/mixins/functions you are not able to #import them to the main.scss as before first and they are ready to use by the next included files! With #use now YOU HAVE TO #use the files separatly in every partial file where you need the variables/mixins/function.
If you like you may have an additional look about how that works:
https://stackoverflow.com/a/66300336/9268485
But note: what sounds easy indeed is more complicated in practical use ... even when the projects and modules are bigger and more structured ...
Project configurations splinters to different configurations files and in some cases it additional seems to lead to code/variables repetitions if you need to include different modules with same variables settings ... in every case more code is needed. But up to now that seems to be the future ...
I ended up just using #import , I seriously doubt they would remove it in the next 3 years and break everyone's code everywhere

Using CSS variables in LESS

This might seem basic, but I can't figure out how to use CSS variables in LESS?
variables.css:
.root {
--header-color: white;
--content-color: yellow;
}
styles.less:
#import "../variables.css";
.header {
color: #header-color;
}
I get error "#header-color is undefined".
LESS allows you to use normal CSS code, so use one option could be just use the variable as CSS:
#import "../variables.css";
.header {
color: var(--header-color);
}
Also, you can save the css var to a LESS var:
#import "../variables.css";
#header-color: var(--header-color);
.header {
color: #header-color;
}

Compile non-root CSS custom property

Are there any tools to compile CSS custom properties declared at not :root rule? I want following code with custom properties
.dark {
--bg-color: black;
--fg-color: white;
}
.light {
--bg-color: white;
--fg-color: black;
}
.foo {
background: var(--bg-color);
display: block;
}
.bar {
color: var(--fg-color);
display: inline;
}
be compiled to their non-custom-prop equivalents like that
.light .foo, .light.foo {
background: white;
}
.dark .foo, .dark.foo {
background: black;
}
.light .bar, .light.bar {
color: black;
}
.dark .bar, .dark.bar {
color: white;
}
.foo {
display: block;
}
.bar {
display: inline;
}
The goal is to
switch color schemes by switching dark/light class on root DOM element
use valid css syntax (no sass less)
keep rules code compact
It's actually not safe to do that. I can tell you because I tried so hard to make a safe transformation.
But I failed.
https://github.com/postcss/postcss-custom-properties/issues/1
Ideal solution. Your example is valid CSS and can be used in many browsers (not in IE, Edge (but is in development) and Opera Mini as of writing this answer, 2017-03-27, other major browsers are fine).
Suboptimal solution. Some CSS can be transpiled to achieve better browser support. The solution I found does not support variables on non-:root elements, however. There are also other objections against transpiling of 'future' CSS into 'current' CSS. To the best of my knowledge, you will have to implement your own transpiler (or postcss plugin) if you want to transpile custom properties not on the :root element, but be warned that that is hard in general. Now you don't need the general part, so it is possible. Just does, to the best of my knowledge, not exist yet.
Preprocessing solution. Of course, you don't need a general implementation of custom properties. You have different themes that have their own values for the same set of properties and that's it. Thus, a separate stylesheet can be created as a preprocessing step using any CSS preprocessor.
Now you say the following,
use valid css syntax (no sass less)
but I am going to show this anyway, because I believe that it is a valid solution to your problem. It is definitely the only one I know that actually works if you want to/need to support IE, Edge and/or older versions of other major browsers (Firefox < 31, Chrome < 49, Safari < 9.1, Opera < 36)
You could do this using SASS for example, to do the transpiling on the server side.
// define styles, use variables throughout them
// your entire style definition goes into this mixin
#mixin myStyles($fg-color, $bg-color) {
.foo {
display: block;
background: $bg-color;
}
.bar {
display: inline;
color: $fg-color;
}
}
// define themes, that set variables for the above styles
// use named arguments for clarity
.dark {
#include myStyles(
$fg-color: white,
$bg-color: black
);
}
.light {
#include myStyles(
$fg-color: black,
$bg-color: white
);
}
This compiles to the following.
.dark .foo {
display: block;
background: black;
}
.dark .bar {
display: inline;
color: white;
}
.light .foo {
display: block;
background: white;
}
.light .bar {
display: inline;
color: black;
}
This is not exactly what you want to obtain, but very close. Realistically, I think this is the closest you will get to obtaining your desired output. I know you want to
keep rules code compact
but what you are saying there (I think) is that you want to split out custom properties from their rules to save on number of rules, which is not something any preprocessor I know supports.
You can organize your source SASS in separate files to keep an overview easily. You can even set up a build system that generates a separate stylesheet for every theme you have. It is then possible to have your users select an alternative stylesheet. Browsers have some support for this, but switching using JavaScript is also definitely possible in the latter case. Simply set all stylesheets to be disabled except for the selected one. Here is an example.

SASS and Bootstrap - mixins vs. #extend

I'm using the SASS port of Bootstrap, and I'm wondering if there's any difference between using the pre-defined mixins and using SASS's #extend.
For instance, if I have:
<div class="wrapper">
Some content here....
</div>
Is there any difference between doing
.wrapper {
#include make-row();
}
and
.wrapper {
#extend .row;
}
?
If there's no difference, are there other mixins that aren't equivalent to a single #extend statement? If there aren't such mixins, why do the mixins even exist?
The big difference between #extend and a mixin is the way the css is compiled. It doesn't look like much in simple examples, but the differences and implications are significant and can be a real headache in the wild if used carelessly. #extend is a little bit like fools gold, looks great at first, but ...
Let's look at a simple example:
#extend
.row {
width: 50px;
}
.new-row {
#extend .row;
}
.another-row {
#extend .row;
}
compiles into:
.row,
.new-row,
.another-row {
width: 50px;
}
mixin
#mixin row() {
width: 50px;
}
.new-row {
#include row();
}
.another-row {
#include row();
}
compiles into:
.new-row {
width: 50px;
}
.another-row {
width: 50px;
}
A mixin includes the properties everywhere it is hit - copying them each time - whereas an #extend groups the selectors and defines the properties once. This isn't immediately obvious, because the difference is in the compiled css but it has some important implications:
Load order
With #extend the selectors will be grouped at the first point in the sass where they are encountered which can lead to some weird over-riding. If you define a selector and use #extend to bring in a property to and try to override a property defined earlier in your sass, but after the point at which the extended properties are grouped in the css then the override will not work. This can be quite perplexing.
Consider this logically ordered set of css definitions and the likely HTML: <div class='row highlight-row'></div>:
.red-text {
color: red;
}
.row {
color: green;
}
.highlight-row {
#extend .red-text;
}
compiles into:
.red-text,
.highlight-row {
color: red;
}
.row {
color: green;
}
So even though the sass ordering makes it look like the row colour would be red, the compiled css will make it green
Poor groupings
#extend can result in poorly grouped selectors in the resulting css. You can end up with thirty or forty unrelated things all sharing the same property for example. Using #extend for fonts is a good example of this.
Nesting
If you are using deeply nested sass (which is not good, btw) and you use #extend you will duplicate the fully nested selector for every #extend you use, resulting in bloated css. I've seen this a lot:
.selector-1 .selector-2 .selector-3 .selector-4,
.selector-1 .selector-2 .selector-3 .selector-4 a,
.selector-1 .selector-2 .selector-3 .selector-4 li,
.selector-1 .selector-2 .selector-3 .selector-4 td {
font-family: arial;
}
If you're new to SASS it pays to look at the compiled css.
Media queries
#extend do not work inside media queries, because media queries are not selectors.
Conclusion
My rule of thumb is to use an #extend over a mixin if you have no parameters and if you can reasonably define the #extend and share it amongst a few tightly related selectors that exist nearby in the sass, for example, in the same file that defines a sass module. Buttons are a good example of well used #extend:
%button {
padding: 10px;
}
.call-to-action {
#extend %button;
background-color: $green;
}
.submit {
#extend %button;
background-color: $grey;
}
The best article to help make the choice is here
PS, the % sign is a use of placeholder extends

Resources