SCSS setting a value based upon another - css

I'm just trying to work out if something is possible or not with SCSS.
Please feel free to ask me for more details if I'm not very clear in what I'm asking, but here's what I'm trying to achieve.
Pseudo code:
.class1 { width:100px; }
.class2 { margin-right:[.class1{width}] + 2 }
compiling into
.class1 {width:100px; }
.class2 { margin-right:102px; }

I believe to get what you want, a variable is best used:
$yourWidth: 100px;
.class1 { width: $yourWidth; }
.class2 { margin-right: ($yourWidth + 2); }
Update (based on comment info)
You might add a global variable below $ColCount that begins as an empty list, like so:
$WidthList: ();
Then inside #mixin columns($numCols) after $colWidth is calculated, add the width value for that column to the $WidthList by adding this function:
join($WidthList, $colWidth);
Then, once all the columns have calculated, you should have a list containing all the width values, so that you can access them if you desire elsewhere, and thus...
.class2 { margin-right: (nth($WidthList, 5) + 2); }
...should yield the .cl-col5 value you want for the margin.
Note that I did not test this. Nor have I actually ever used SASS. I am basing this strictly off the documentation found here and elsewhere on their site.

Related

SASS loop to generate chained :not() from variables string

I have a long list of classes I wish to use in a couple of ways.
The list looks something like this (but much longer):
$my-components: '.some-component', '.some-other-component', '.another-component';
One of the ways I need to use this list of class names in SASS (scss), which I can't figure out, is to create a long chained selector of :not()s. The final rendered output should look like this:
.parent {
> * {
&:last-of-type:not(.some-component):not(.some-other-component):not(.another-component):not(etc) {
// style rules
}
}
}
(The goal being to select the last child element of .parent that doesn't have one of the classes in the list).
Question: How can I make the above code DRY by using the $my-components variable?
Note 1: The loop's output needs to be able to be appended to that &:last-of-type, as in above example.
Note 2: I'm using the $my-components variable already in a different function, so I'd like to keep it in the same format if possible.
Note 3: I know this seems hacky and stupid, and that I should just give all of those elements a common shared class instead. But unfortunately I can not currently modify that part of the DOM.
Use a #each loop
scss:
$my-components: '.some-component', '.some-other-component', '.another-component';
.parent {
> * {
$selector: '';
#each $component in $my-components {
$selector: $selector + ":not(#{$component})"
}
&:last-of-type#{$selector} {
color: blue;
}
}
}
css:
.parent > *:last-of-type:not(.some-component):not(.some-other-component):not(.another-component) {
color: blue;
}
What's happening ?
I define a new string variable $selector.
During the #each loop, I'm concatening the string with :not(#{$component}) to add your new selector.

SASS Customize Class Names with Variables

Is there any way to customize the variables in SASS?
For example:
.m-b-{$number} {
margin-bottom: $number;
}
If I give class="m-b-50" to an element, it should take margin-bottom 50. I just want to know if it is possible with SASS.
Yes it is possible with the help of variable interpolation or variable substitution which uses #{} for variable substitution in SASS and mixins which is a block of code just like function.
Interpolation is the process of evaluating an expression or a string containing one or more variables, yielding a result in which the variables are replaced with their corresponding values.
Simple example of interpolation and set values to the css property in SASS:
$number:60;
$n: 20px;
.m-b-#{$number}{
margin-bottom: #{$number}px;
margin-top: $n;
}
To create customize class names, will use mixins:
#mixin margin-class($side, $number) {
$firstLetter: str-slice($side, 0, 1);
.m-#{$firstLetter}-#{$number}{
margin-#{$side}: #{$number}px;
}
}
$margins: (10, 20);
$sides: ("top", "right", "bottom", "left");
#mixin generate-margin(){
#each $margin in $margins{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
#include generate-margin();
Here, generate-margin() will get executed which will call margin-class() for each $margins and $sides, and will generate the below CSS classes:
.m-t-10 {
margin-top: 10px;
}
.m-r-10 {
margin-right: 10px;
}
.m-b-10 {
margin-bottom: 10px;
}
.m-l-10 {
margin-left: 10px;
}
.m-t-20 {
margin-top: 20px;
}
.m-r-20 {
margin-right: 20px;
}
.m-b-20 {
margin-bottom: 20px;
}
.m-l-20 {
margin-left: 20px;
}
That's the one way when you want only for specific values, but if you want to create margin class for 0-20, you can loop thru 0 to 20 as shown below:
#mixin generate-margin(){
#for $margin from 1 through 20{
#each $side in $sides{
#include margin-class($side, $margin);
}
}
}
For anyone else facing this issue, here is how one can achieve this:-
#for $i from 1 through 10 {
.mb-#{$i} {
margin-bottom: #{$i}rem;
}
}
The answer is: no it is not possible. SASS is just a language to pre-generate CSS for you. There is no on-demand, dynamic creation of classes triggered by the contents of your HTML markup. When it comes time for the browser to render your HTML and apply your specified classes, it is still just using CSS. I.e. if you assign class="m-b-50" to an element, the class .m-b-50 must already be explicitly defined somewhere. As noted in the other answers, SASS can make it easier to generate a bunch of pre-defined classes but you must know which values you want to support up front.
Now, you could generate classes for some very large, all-inclusive range like -1000 to 1000 to effectively support all values you might ever try to use and it would seem to do what you wanted, but you would be forcing your users to download a larger CSS file with, most likely, a large percentage of it being unused CSS which is wasteful and can be inconsiderate in a world of paid & limited data plans.

CSS Pre-processors: Getting the current value of a css property and changing it

Let's say I have following declaration
.foo {
.grid(1);
}
.grid(#num) {
width: ... // Some calculated percentage
}
Now later I realize that I need .foo to be 5% wider is it somehow possible todo .foo { width: +5%;} or something similar?
For LESS
In LESS you cannot get at that info so directly. You would need to modify the .grid mixin to something like:
.grid(#num, #adjust: 0) {
width: (your regular calaculation) + #adjust; // Some calculated percentage
}
By giving it a default of 0, then you can still just pass .grid(1) when you want to, but then if .foo needed an adjustment, you do:
.foo {
.grid(1, 5%);
}
For SASS
I'm not 100% certain, but I believe SASS cannot access the property directly either, and would essentially need to do some type of similar solution where the mixin calculation itself is changed.

Custom LESS styles like Bootstrap's "spanX"

I want to make a LESS style like so:
.td-middle50
{
line-height:50px;
vertical-align:middle;
}
that I can apply to to make all the elements have a line-height of 50px and be vertically aligned.
Where 50 is a variable.
This is as far as I have got:
.td-middle(#vheight){
line-height:(#vheight);
vertical-align:middle;
}
But this:
A) Doesn't even work
B) I would have to apply to each td instead of the tr
Take a look on how .span1-.span12 classes are being defined in Twitter Bootstrap's LESS files (source). They are defined using so called "mixins" (see example here) that are then executed (example here).
From Bootstrap's code (mixins.less):
// The Grid
#grid {
.core (#gridColumnWidth, #gridGutterWidth) {
.spanX (#index) when (#index > 0) {
(~".span#{index}") { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
/* ... a lot of code here ... */
}
/* ... other code ... */
}
Usage of mixins (within grid.less):
// Fixed (940px)
#grid > .core(#gridColumnWidth, #gridGutterWidth);
// Fluid (940px)
#grid > .fluid(#fluidGridColumnWidth, #fluidGridGutterWidth);
Good start would be to learn more about mixins: http://lesscss.org/#-mixins
But I have one advice: if you want this to work for every value of mentioned "variable", then stop. This must be compiled to CSS and CSS will not make you able to do what you wanted (to apply styles dynamically for every class matching criteria, based on part of the class's name), better rethink your idea.

LESS issues (scope and explicit node name)

Is there any way to bypass LESS scoping? It's becoming annoying. Basically, I have a .text-box which defines background, border, etc. Then, in a sub-section there's a one-off change to add a margin-top: .text-box { margin-top: 10px }. Now I can't use .text-box within that section and get my original box styles; instead, all I get is the margin-top. How can I get the definition higher in the heirarchy? I suppose I could make it a function, and call that function in both places, but being that I'm using LESS, I want to do less and KISS. In PHP, you'd get to the global namespace by using / prefix, or in C++ using :: prefix.
Additionally, it doesn't seem like any definitions with the node name work for prototyping. Meaning, I can't declare it ul.products, and then use ul.categories { ul.products }. I have to omit the node name in order to re-use it. Meaning: .categories { .products }. Is this an oversight/impossibility?
Thanks
ok so let's say you've got your mixin defined, for example:
.text-box {
background: #eee;
border: 1px solid #ccc;
color: #333;
margin-top: 5px;
}
now you want to add property or modify it in some subsection, then simply do this:
div.content {
div.sub_section {
.text-box;
margin-top: 10px; // this will override 5px defined in the mixin.
}
}
...which is putting your mixin in place, and adding some property you need to add (which will override any property from the mixin itself BUT make sure the overriding property is defined AFTER the mixin is called.
it's not ideal solution, as it creates two declarations in the output css file (there will be one from mixin followed by the one you defined in .sub_section), but otherwise I don't know a solution to this problem other than defining a parametric mixin..
--
your second issue - I think that less doesn't support scope-limited definitions on purpose... if you really need to know that certain mixin is to be used by a specific tag, I would deal with it like so:
.ul_products { ... }
.ul_categories { .ul_products; ... }
ul.categories { .ul_categories; }
you can also define a bundle and call stuff from there:
#ul {
.products { ... }
.categories { ... }
}
ul.categories { #ul > categories; }
i hope i got it right.. ?

Resources