Stylus: how to create a CSS variable with a Mixin interpolation - css

How can a Stylus Mixin interpolate for both sides of the definition of a variable, such as:
--MyVariable: MyVariable
In the compiled CSS, --MyVariable should remain as the variable name, whereas the second variable should be the numerical value computed for this Stylus variable defined elsewhere.
I will have many such pairs of a CSS variable and a Stylus variable. Now, even though I could just manually write them down in the Stylus file, I would like to have a Mixin that allows me to create them by writing the shared part of the names just once as the Mixin's single argument, such as:
VariablePair(MyVariable-1) // --MyVariable-1: MyVariable-1
VariablePair(MyVariable-2) // --MyVariable-2: MyVariable-2
VariablePair(MyVariable-3) // --MyVariable-3: MyVariable-3
I tried:
VariablePair(VariableName)
--{VariableName}: VariableName
VariablePair(MyVariable)
It didn't parse.
I realized that MyVariable without '' would come as an actual value that couldn't be affixed to --. But, adding '' to it as VariablePair('MyVariable') would result in the right side of the definition itself becoming a string instead of the Stylus variable for computation.
I tried the different combinations of the presence/lack of the brackets and '' and $ as well as concatenations, but none of them seem to work. '--' + VariableName + ': ' + VariableName (with/without the brackets/$) isn't working, it either doesn't parse or does parse but without creating a line in the compiled CSS.
Is there a solution to this?

unquote works for this:
VariablePair(name, val)
{unquote('--' + name)}: val
.selector
VariablePair('fontcolor', red)
color: var(--fontcolor)
will output
.selector {
--fontcolor: #f00;
color: var(--fontcolor);
}

Related

How to add "$" string to an interpolated value?

say I want to produce $small by adding an interpolated value to '$', or, in other words, by concatenating the string '$' to an interpolated value. Something like this:
$#{value}
or this:
"$" + #{value}
None of them seem to work in sass/scss. Is there a working syntax for this?
I need this because I'm building this mixin that accept media queries names (it comes from my framework, too long to explain, and a little offtopic) and I'd like to strip out the dollar when passing arguments:
#mixin prog-prop($property-name, $mq-values){
#each $mq in $mq-values{
#include mq(#{nth($mq, 1)}){ #{$property-name}: #{nth($mq, 2)} }
}
}
I don't know why you want to do so, but this is a way:
$value:'string';
$value: '$#{$value}';
.value{
content: $value;
}

How to add/sub to a dynamic variable?

I have a problem with the following code:
#viewport-large: 1440px;
#viewport-medium: 1366px;
#viewport-small: 1280px;
#type: medium;
#setting: ~"#{viewport-#{type}}";
#value: (#setting + 1); // here can't add 1
It will show an error message in the LESS compiler saying: "Operation on an invalid type".
Can anyone tell me why this is the case? What should I do?
The output of Less' CSS escape function (e() or ~"") is a string and you can't add a number to it. This is why the compiler reports Operation on invalid type.
So, instead of doing it that way make use of the double resolution (##) like in the below code block:
#viewport-large: 1440px;
#viewport-medium: 1366px;
#viewport-small: 1280px;
#type: medium;
#setting: "viewport-#{type}"; /* this won't resolve into 1336px as yet */
#value: ##setting + 1px; /* resolution to 1336px + addition happens here */
In this method, we just form the variable name and set it to the #setting variable (instead of setting a actual px value) and so the real px value's type remains unpolluted. In the next line when we use the double #, Less compiler would try to fetch the value that is held by the variable whose name is same as the value of #setting variable and immediately sum 1px to it instead of converting it to a String.
Note: If you have the Strict Math option (--strict-math) enabled then the addition operation must be wrapped inside extra braces like below. Else, it would plainly output a concatenated value like 1366px + 1px instead of performing the addition and outputting 1367px.
#value: (##setting + 1px);
The --strict-math setting is disabled by default but some of your projects could have enabled it.

Is there a way to print SASS value to output?

Suppose we have:-
$value: 13.37;
$length: $value + 0em;
Now i wanted to check the value of $length.
Is there anything similar to Javascript's Console.log?
If you want it to appear on the page itself, I believe you could attach it as the value of a pseudo-element. Something like:
body::before{ content: "#{$length}"}
Additionally, sass includes #error, #warn, #debug directives that will log things to the terminal to verying degrees of of noisiness.
#error "the value of $length is `#{$length}`"
More info on those can be found here

Building of a string, which depends on variable number of parameters

My question is: how to build a string in Less, which depends on variable number of parameters. For instance, I would like to make a mixin, which helps me to write #font-face CSS rules. So I need to build src:... fonts property for arbitrary number of formats (.eot, .ttf, .oft, .woff, .woff2, .svg) of my font. Here is my Less loop to process all font formats in list:
// #some-types - it is my font formats list, just smth. like 'otf', 'eot',...
// #old-src-value - it is string with src for my font from previous loop
// #counter - it is my loop counter
.make-font-src(#some-types; #old-src-value; #counter) when (#counter <= length(#some-types)) {
// Here we get next font format from #some-types
#font-type: extract(#some-types, #counter);
// Used for building 'format("opentype")' - like part of src value string
.get-font-format(#font-type);
// Building a part of src value string for this iteration
#src-value: e('#{old-src-value}, url("#{font-path}#{font-filename}.#{font-type}") format("#{font-format}")');
// Recursive call of this mixin for looping
.make-font-src(#some-types; #src-value; (#counter + 1));
}
So I'm stuck in how to fetch complete src value string, when all font formats will be processed in the loop? Also please refer to this codepen demo.
As mentioned in my comment, this would not cause a recursive definition error because you have assigned the value to a different variable and then used it. However, it seems like Less is processing the property-value setting line as soon as the first iteration of the loop is completed. You can verify this by changing the counter value for the first iteration itself to 2 or more.
One solution (a better approach to the problem in my opinion) would be to use the property merging with comma feature and set the property-value pair directly like in the below snippet:
.make-font-src(#some-types; #counter) when (#counter <= length(#some-types)) {
#font-path: 'some/test/path/';
#font-filename: 'Arial';
#font-type: extract(#some-types, #counter);
src+: e('url("#{font-path}#{font-filename}.#{font-type}") format("#{font-type}")');
.make-font-src(#some-types; (#counter + 1));
}
div.test {
.make-font-src('eot', 'woff', 'svg'; 1);
}
This when compiled would produce the following output:
div.test {
src: url("some/test/path/Arial.eot") format("eot"),
url("some/test/path/Arial.woff") format("woff"),
url("some/test/path/Arial.svg") format("svg");
}
Finally, I found my own solution: if we add special 'getter' mixin with guard, which triggered on last iteration of the loop, we can get full src value from our loop mixin.
.getter(#cond; #list) when (#cond = length(#list)) {
#font-src-full: #src-value;
}
Here is a fiddle with demo

How can I search CSS with Perl?

First question from a long time user.
I'm writing a Perl script that will go through a number of HTML files, search them line-by-line for instances of "color:" or "background-color:" (the CSS tags) and print the entire line when it comes across one of these instances. This is fairly straightforward.
Now I'll admit I'm still a beginning programmer, so this next part may be extremely obvious, but that's why I came here :).
What I want it to do is when it finds an instance of "color:" or "background-color:" I want it to trace back and find the name of the element, and print that as well. For example:
If my document contained the following CSS:
.css_class {
font-size: 18px;
font-weight: bold;
color: #FFEFA1;
font-family: Arial, Helvetica, sans-serif;
}
I would want the script to output something like:
css_class,#FFEFA1
Ideally it would output this as a text file.
I would greatly appreciate any advice that could be given to me regarding this!
Here is my script in full thus far:
$color = "color:";
open (FILE, "index.html");
#document = `<FILE>`;
close (FILE);
foreach $line (#document){
if($line =~ /$color/){
print $line;
}
}
Since you asked for advice (and this isn't a coding service) I'll offer just that.
Always use strictures and warnings:
use strict;
use warnings;
Always check the return value of open calls:
open(FILE, 'filename') or die "Can't read file 'filename' [$!]\n";
Use the three-arg form of open and lexical filehandles instead of globs:
open(my $fh, '<', 'filename') or die "Can't read file 'filename' [$!]\n";
Don't slurp when line-by-line processing will do:
while (my $line = <$fh>) {
# do something with $line
}
Use backreferences to retrieve data from regex matches:
if ($line =~ /color *: *(#[0-9a-fA-F]{6})/) {
# color value is in $1
}
Save the class name in a temporary variable so that you have it when you match a color:
if ($line =~ /^.(\w+) *\{/) {
$class = $1;
}
Well, this is not as simple as it seems.
CSS classes can be defined in many ways. For example,
.classy {
color: black;
}
Good luck using a line-by-line approach for parsing that.
Actually, my first approach would be searching CPAN. This looks promising:
CSS - Object oriented access to Cascading Style Sheets (CSS)
Edit:
I installed HTML::TreeBuilder and CSS modules from CPAN and concocted the following aberration:
use strict;
use HTML::TreeBuilder;
use CSS;
foreach my $file_name (#ARGV) {
my $tree = HTML::TreeBuilder->new; # empty tree
$tree->parse_file($file_name);
my $styles = $tree->find('style');
if ($styles) {
foreach my $style ($styles) {
# This is an insane hack, not guarantee
# to work in the future.
my $css = CSS->new;
$css->read_string(join "\n", #{$style->{_content}});
print $css->output;
}
}
$tree = $tree->delete;
}
This thing only prints all the CSS selectors from list of HTML files, but nicely formatted so you should be able to continue from here.
For yet another way to do it, you can ask perl to read from the file in sections other than lines, for example by using the "}" as a record separator.
my $color = "color:";
open (my $fh, '<', "index.html") || die "Can't open file: $!";
{
local $/ = "}";
while( my $section = <$fh>) {
if($section =~ /$color(.*)/) {
my ($selector) = $line =~ /(.*){/;
print "$selector, $section\n";
}
}
Untested! Also, this of course assumes that your CSS neatly ends its sections with a } on a line on it's own.
I'm not having problems with the regex's but rather with the capture of data. Since CSS elements are typically multi-line, I need to figure out how to create an array between the { and } with each linebreak as a delimiter for list items.
No, you don't.
For the problem as stated, the only lines of interest will be those containing either a class name or a color definition, and possibly also lines containing } to mark the end of a class. All other lines can be ignored, so there's no need to put them into an array.
Since class specifications cannot be nested[1], the last seen set of class names will always be the active set of classes. Therefore, you need only record the last seen set of class names and, when a color specification is encountered, print those class names.
There are still some potential difficulties handling cases in which a specification block is shared by multiple classes (.foo, .bar, .baz { ... }), which may or may not be spread across multiple lines, or if multiple attributes are defined on the same line, but dealing with those should follow fairly easily from what I've already laid out. Depending on your input data, you may also need to include a basic state engine to keep track of whether you're in comments or not.
[1] i.e., Although you can have semantically-nested classes, such as .foo and .foo .bar, they have to be specified in the CSS file as
.foo {
...
}
.foo .bar {
...
}
and cannot be
.foo {
...
.bar {
...
}
}
Although I have not tested the code below, but something like this should work:
if ($line =~ m/\.(.*?) \{(.*?)color:(.*?);(.*)/) {
print "$1,$3\n";
}
You should invest some time learning regular expressions for Perl.

Resources