Consuming Less Vars in a generating for loop - css

I'm trying to use a loop to generate the relevant CSS using defined Less vars. However I'm struggling as I'm slowly getting used to Less after years with scss and could use another pair of eyes since I can't seem to find a fitting example.
So say you have Less like this (except in reality my less vars are in a separate file and imported via #import before the loop);
#spacing-xsmall: .25rem; // 4px
#spacing-small: .5rem; // 8px
#spacing-medium: 1rem; // 16px
#spacing-medium-large: 1.5rem; // 24px
#spacing-large: 2rem; // 32px
#spacing-xlarge: 2.5rem; // 40px
#spacing-xxlarge: 4rem; // 64px
#sizes: xsmall, small, medium, medium-large, large, xlarge, xxlarge;
.init-spacing(#i) when (#i > 0) {
#size: extract(#sizes, #i);
#space: ~"#spacing-#{size}";
.margin-#{size} { margin: #space }
.padding-#{size} { padding: #space; }
.init-spacing(#i - 1);
}
.init-spacing(length(#sizes));
This produces output like this;
.margin-xxlarge {
margin: #spacing-xxlarge;
}
.padding-xxlarge {
padding: #spacing-xxlarge;
}
....
Except obviously in the CSS output I would want the variable VALUE instead so the output would be like;
.margin-xxlarge {
margin: 4rem;
}
.padding-xxlarge {
padding: 4rem;
}
....
How can I accomplish this with Less? I know the string interpolation is a likely culprit but am a bit stuck in the docs as it were.
Here's a Codepen to tinker with for a reproducible example of the issue.
I tried this answer but the output just returns like .class { margin: [object, object] } along with a couple other similar I found but can't seem to get my scenario covered where I loop an array like I want to do since my existing variables may not be just an easy index number, and I think that's part of where I'm falling short in a Parametric mixin that would work here. I'm just searching for the nuance I'm missing here to generate the results intended in a rather generic way so I can apply it in other similar instances.

Why not use variable variables ?
#spacing-xsmall: .25rem; // 4px
#spacing-small: .5rem; // 8px
#spacing-medium: 1rem; // 16px
#spacing-medium-large: 1.5rem; // 24px
#spacing-large: 2rem; // 32px
#spacing-xlarge: 2.5rem; // 40px
#spacing-xxlarge: 4rem; // 64px
#sizes: xsmall, small, medium, medium-large, large, xlarge, xxlarge;
.init-spacing(#i) when (#i > 0) {
#size: extract(#sizes, #i);
#space: ~"spacing-#{size}";
.margin-#{size} { margin: ##space }
.padding-#{size} { padding: ##space; }
.init-spacing(#i - 1);
}
.init-spacing(length(#sizes));
I got (with a preview)
.margin-xxlarge {
margin: 4rem;
}
.padding-xxlarge {
padding: 4rem;
}
.margin-xlarge {
margin: 2.5rem;
}
.padding-xlarge {
padding: 2.5rem;
}
.margin-large {
margin: 2rem;
}
.padding-large {
padding: 2rem;
}
.margin-medium-large {
margin: 1.5rem;
}
.padding-medium-large {
padding: 1.5rem;
}
.margin-medium {
margin: 1rem;
}
.padding-medium {
padding: 1rem;
}
.margin-small {
margin: 0.5rem;
}
.padding-small {
padding: 0.5rem;
}
.margin-xsmall {
margin: 0.25rem;
}
.padding-xsmall {
padding: 0.25rem;
}

Related

Using wildcard as a value

Is there a way to use wildcard to write all these twenty rules as one:
.margin-bottom-1rem {
margin-bottom: 1rem;
}
.margin-bottom-2rem {
margin-bottom: 2rem;
}
...
.margin-bottom-20rem {
margin-bottom: 20rem;
}
I found many resources about capturing all the class names, but none about using a part of the captured name as a value. If this is not possible with wildcards, is there another workaround to avoid these duplicated rules?
SCSS
$sizes: 1rem, 2rem, 5rem, 10rem, 20rem;
#each $size in $sizes {
.margin-bottom-#{$size} {
margin-bottom: $size;
}
}
SASS
$sizes: 1rem, 2rem, 5rem, 10rem, 20rem
#each $size in $sizes
.margin-bottom-#{$size}
margin-bottom: $size
Both produce this:
.margin-bottom-1rem {
margin-bottom: 1rem;
}
.margin-bottom-2rem {
margin-bottom: 2rem;
}
.margin-bottom-5rem {
margin-bottom: 5rem;
}
.margin-bottom-10rem {
margin-bottom: 10rem;
}
.margin-bottom-20rem {
margin-bottom: 20rem;
}
Do something for every element
https://sass-lang.com/documentation/values/lists#do-something-for-every-element

merge #extend with parent style and make one class name

I'm trying to merge the style into one class but its showing an error. Look at the example below.
%banner-style{
banner {
padding: 140px 0 210px;
background: url(https://im2.ezgif.com/tmp/ezgif-2-92c6382d82ba.jpg) top center/cover no-repeat;
&.row {
margin: 0;
}
.main-heading {
font-size: 40px;
letter-spacing: -1px;
font-weight: 600;
padding-right: 20px;
sup {
font-size: 10px;
vertical-align: super;
}
}
}
}
And I want it to merge with the parent class .parent
.parent{
color: red;
&_#extend %banner-style;
}
using & to merge into one class name. but showing error unless i do this
.parent{
color: red;
&_{#extend %banner-style};
}
Which is same as if I remove &_.
I wanted .parent_banner {...} but instead got .parent_ banner{...};
Does anyone know how I can accomplish this?
You are getting exactly what is supposed to happen. Extend does not "merge" classes, it extends another class/placeholder into a new selector's styles.
What that means is if I write:
%banner-style {
background: black;
}
.parent {
#extend %banner-style;
}
.other-selector {
#extend %banner-style;
color: red;
}
The css I get will be
.parent {
background: black;
}
.other-selector {
color: red;
background: black;
}
So you are getting expected results. If you'd like to make this "work" the way you want, you can just change your code to:
%banner-style {
padding: 140px 0 210px;
background: url(https://im2.ezgif.com/tmp/ezgif-2-92c6382d82ba.jpg) top center/cover no-repeat;
&.row {
margin: 0;
}
.main-heading {
font-size: 40px;
letter-spacing: -1px;
font-weight: 600;
padding-right: 20px;
sup {
font-size: 10px;
vertical-align: super;
}
}
}
.parent{
color: red;
&_banner {
#extend %banner-style;
};
}
Note: I took out the banner block because it seems you don't want that (and banner isn't a normal html element).

How to change variable value using parent selector in LESS

So lets have following snippet:
.dashboard.tile {
display: block;
color:white;
&-blue {
#body-color:#light-blue;
#footer-color:#dark-blue;
font-size: 10px;
}
#body-color:#greenBright;
#footer-color:#000;
div.tile-body {
border-radius: #tile-border-radius #tile-border-radius 0 0;
background-color: #body-color;
font-size: #font-size-h2;
}
div.tile-footer {
border-radius: 0 0 #tile-border-radius #tile-border-radius;
background-color: #footer-color;
}
}
My goal is to create additional classes like tile-blue tile-red etc that would only change color variable values.
Unformtunelty this does not work. tile-blue gets generated with font-size:10 but variables are unaffected - more over, default color is not applied so I guess that i have messed something up with selector hierarchy etc.
Found the solution that exactly suits my needs
.dashboard-tile{
&-blue{
.dashboard-tile(#light-blue,#dark-blue)
}
&-green{
.dashboard-tile(#light-green,#dark-green)
}
&-red{
.dashboard-tile(#light-red,#dark-red)
}
&-violet{
.dashboard-tile(#light-violet,#dark-violet)
}
}
.dashboard-tile(#body-color,#footer-color) {
margin-bottom: 5px;
margin-top: 5px;
display: block;
color: white;
.tile-body {
border-radius: #tile-border-radius #tile-border-radius 0 0;
background-color: #body-color;
font-size:#font-size-h2;
#tilePadding;
}
.tile-footer {
border-radius: 0 0 #tile-border-radius #tile-border-radius;
background-color: #footer-color;
#tilePadding;
}
}

Why is my css not being applied to my page?

I recently message boxes that style a message given during a php statement, and they style the message.
This is my php code.
mysql_query("UPDATE account SET is_active=1, activation_code='' WHERE activation_code='$code'");
$message_good = '<div class="register_thankyou"><p>Success! Your account has been activated. You may now log in.</p></div>';
} else {
$message_good = '<div class="register_error"><p>Account has already been activated, or you have an invalid activation code.</p></div>';
}
For some reason, it loads the style register_thankyou but not for register_error.
Both styles have this css setup.
/* ACTIVATION MESSAGES */
.register_thankyou {
background-color: #e3f6da;
border: 1px solid #3b7008;
padding: 20px;
}
.register_thankyou h3 {
font-size: 20px;
font-weight: normal;
margin: 0 0 10px;
padding: 0;
}
.register_thankyou p {
margin: 0;
padding: 0;
}
.register_error {
background-color: #f8e5e4;
border: 1px solid #c41100;
padding: 20px;
}
.register_error h3 {
font-size: 20px;
font-weight: normal;
margin: 0 0 10px;
padding: 0;
}
.register_error p {
margin: 0;
padding: 0;
}
All thats different is the background and border color
Can someone explain to me why one style is being applied and the other isnt?
Thank you:
error:
it works on here tho: http://jsfiddle.net/q8ozgfmu/
As i alarmed alien said, check in the Browser's inspector. The style might be getting overridden. Or the other reason could be that the updated style.css may not have loaded in the browser. Try clearing the cache and load the page again.

#import in #if statement in Sass

I want to load only the css needed for the login page for performance. On my other pages I want a grouped css file that will be cached on every page which contain all my css.
I have the following files:
minifiedcssforloginpage.scss
grouped-pages.scss
In minifiedcssforloginpage.scss I declare $load-complete-css:false. Afterwards I import myproject.scss which contains all the imports of my modules, layouts, core... In myproject.scss i want to do something like
#if $load-complete-css {
#import module1;
#import module2;
#import module3;
}
So minifiedcssforloginpage.scss would generate minifiedcssforloginpage.css with less css then grouped-pages.css (that has a var $load-complete-css set to true).
But I get an error that this is not possible "Import directives may not be used within control directives or mixins".
It's one of those things that's just not allowed. The only thing you can do is turn those imports into mixins (import the file outside the #if and call the mixin where appropriate).
Clarification:
_partial.scss
#mixin partial {
.test { color: red }
// other styles here
}
styles.scss
#import "partial";
#if $someval == true {
#include partial;
}
The core dev team is reluctant to implement this feature, although they are considering the implementation of a brand new dependency system.
See the following Github issues :
Allow #import within #if (#451)
Using #import statements within control directives or mixins (#779)
Allow optional #imports (#779)
Dynamic Dependencies (#739)
Put your styles into various partial files in a way that makes sense to you. Then, you can have create a separate SASS file for your login page that imports only the files with the relevant styles.
To quote from my answer to another question:
It is currently not possible to use SASS to include files dynamically.
#import cannot be used within control directives (e.g. #if) or
mixins, using a variable in an import directive is erroneous syntax,
and there is no directive for ending file execution early (which
effectively would allow conditional imports). However, you can
solve your issue by changing how you structure your style rules.
... If you have styles that [should be] conditionally included [they]
should be encapsulated in mixins, in 'module' or 'library' files. ...
The main idea is that importing one such file will not output any
css. This way, you can import these files redundantly so you can use
the mixins wherever you need them.
There isn't currently a way to place import statements within if blocks, unfortunately.
The closest alternative I'm aware of is to use the additionalData field to add a preprocessor function to your webpack sass-loader config:
{
loader: "sass-loader",
options: {
sassOptions: {
includePaths: [...],
},
additionalData: (content: string, loaderContext)=>{
// More info on available properties: https://webpack.js.org/api/loaders
const {resourcePath, rootContext} = loaderContext;
const finalPath = someCondition ? path1 : path2;
return content.replace(/SomeDynamicPathPlaceholder/g, finalPath);
},
},
},
More info on the additionalData field here: https://webpack.js.org/loaders/sass-loader/#additionaldata
I know this is a seriously old question, but we recently implemented this in our own tiny UI framework like this:
ui-framework/config.scss
$components: (
"component-a": true,
"component-b": false
) !default;
// A bunch of other default config
ui-framework/main.scss
#import "component-a";
#import "component-b";
ui-framework/component-a.scss
#if (map-get($components, "component-a") {
.component-a {
// Bunch of code here
}
}
ui-framework/component-b.scss
#if (map-get($components, "component-b") {
.component-b {
// Bunch of code here
}
}
And then in each project:
a-project/main.scss
// NOTE: We only want component b in this project
$components: (
"component-a": false,
"component-b": true
);
#import "ui-framework/config.scss";
#import "ui-frameowrk/main.scss";
We don't do this for every single component, but the huge ones that aren't always in use (like slideshow, dialog, form related code etc).
Old question, I know; just felt I'd provide an alternative scenario and expanded example based on something I was working on.
I ran into this issue because I was hoping to use one SCSS file for smaller screens and one for larger (top menu nav on desktop and burger menu for mobiles).
Using Blazor without Bootstrap, I was wanting to use the one menu structure in terms of the actual html and then use the SCSS to switch between the two at the relevant sizes. I'd created a SCSS file for the desktop version of the nav, and started on one for the mobile version. My plan, before I was aware of this stumbling block, was to selectively import the SCSS based on a media query in a mixin (aptly named mobileOrDesktop).
My idea was to use this mixin to do all the base structure manipulation for the media sizes. Something like this:
#mixin mobileOrDesktop {
#media (min-width: 961px) {
#import 'desktopNavbar.scss';
.container-fluid {
margin-top: 70px;
height: calc(100% - calc(60px + 70px));
}
//show the footer, maybe tweak the font size, etc
}
#media (max-width:960px) {
#import 'moblieNavbar.scss';
.container-fluid {
height: 100%;
}
//hide the footer, maybe tweak font sizes, etc
}
}
Unfortunately, we can't do that due to how SCSS works. So, rather than just dumping all the CSS in the media query (I wanted to keep it relatively split up so that it was more manageable for debug/altering), I had a hunt for alternatives.
Similarly to Cinnamon, I found the most viable solution to be importing the SCSS outside of the mixin and simply including it within the mixin:
#import 'desktopNavbar.scss';
#import 'mobileNavbar.scss';
#mixin mobileOrDesktop {
#media (min-width: 961px) {
#include desktopNavbar;
.container-fluid {
margin-top: 70px;
height: calc(100% - calc(60px + 70px));
}
}
#media (max-width:960px) {
#include moblieNavbar;
.container-fluid {
height: 100%;
}
}
}
With the imported SCSS files being a mixin themselves, i.e. the desktopNavbar.scss becomes:
#import 'siteVariables.scss';
#mixin desktopNavbar {
#navbar {
.burgerIcon {
display: none;
}
.nav {
overflow: hidden;
background-color: $navy;
vertical-align: middle;
height: 70px;
line-height: 70px;
color: $blizzard;
position: fixed;
top: 0;
width: 100%;
display: block;
z-index: 99999999;
.leftBlock, .midBlock, .rightBlock {
display: inline-block;
vertical-align: middle;
height: 70px;
padding: 0px;
margin: 0px;
line-height: 70px;
}
.leftBlock {
width: 20%;
.imgLogo {
margin-left: 10px;
margin-top: 5px;
max-width: 120px;
}
}
.midBlock {
width: 60%;
text-align: center;
.navbar-nav {
display: inline-flex;
flex-wrap: nowrap;
flex-grow: 2;
flex-shrink: 2;
list-style: none;
vertical-align: middle;
margin: 0px;
padding: 0px;
.nav-item {
max-width: 175px;
color: $white;
display: inline-block;
vertical-align: middle;
height: 70px;
.btn-link {
font-size: 16px;
text-align: right;
color: $white;
padding: 14px;
line-height: 20px;
text-decoration: none;
vertical-align: middle;
span {
font-family: 'Font Awesome Solid';
line-height: 60px;
height: 60px;
vertical-align: middle;
padding: 5px;
}
}
&.dropdown {
font-size: 16px;
text-align: right;
line-height: 20px;
text-decoration: none;
vertical-align: middle;
.dropbtn {
font-size: 16px;
text-align: right;
line-height: 20px;
text-decoration: none;
vertical-align: middle;
span {
font-family: 'Font Awesome Solid';
line-height: 60px;
height: 60px;
vertical-align: middle;
padding: 5px;
}
}
.dropdown-content {
display: none;
position: fixed;
top: 68px;
text-align: center;
background-color: $star-command;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 99999999999;
.dropdown-header {
color: $blizzard;
}
.dropdown-item {
color: $powder;
padding: 12px 16px;
text-decoration: none;
display: block;
&:hover {
background-color: $blizzard;
color: $navy;
}
}
}
&:hover {
background-color: $star-command;
.dropdown-content {
display: block;
}
}
}
}
}
}
.rightBlock {
width: 20%;
}
}
}
}
And the site SCSS can simply be:
#import '../../FontAwesome/scss/fontawesome.scss';
#import '../../FontAwesome/scss/regular.scss';
#import '../../FontAwesome/scss/solid.scss';
#import 'siteVariables.scss';
#import 'mixins.scss';
//import other stuff here
html, body {
height: 100%;
overflow: hidden;
width: 100%;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif !important;
margin: 0px;
#include mobileOrDesktop;
.container-fluid {
overflow: auto;
main {
padding: 15px;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
.dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
}
}

Resources