SASS HEX to RGB without 'rgb' prefix - sass

The Question:
Is there a SASS function/technique that transforms a HEX value to a simple RGB string.
Simple here meaning just a string without it being enclosed in rgb() ?
E.g: #D50000 --> "213,0,0"
Why I need this:
I'm using Material Design Lite as my UI 'framework'. More specifically I'm using the SASS version so I can tweak the color variables according to my app's style-guide.
For some reason the color variables in _variables.scss of MDL take this format for color definitions:
$color-primary: "0,0,0" !default; // supposed to be black
which is really, really odd. I expected, at most, something along the lines of
$color-primary: rgba(0,0,0,1) !default;
My color variables are stored in another file called _globals.scss in which I store my variables in regular HEX format so I can easily reuse them in other places:
$brand-primary: #FA3166;
$brand-primary-dark: #E02C59;
I don't want to define 2 times my colours (1 HEX & 1 MDL-compatible RGB string), hence the reason I need to transform HEX to RGB-string.

#nicholas-kyriakides's answer works perfectly fine, but here is a more concise function using Sass interpolation.
#function hexToRGBString($hexColor) {
#return "#{red($hexColor)},#{green($hexColor)},#{blue($hexColor)}";
}
You can pass in either a hex either explicity or from rgb() or rgba() with opacity as 1.
For example:
$color-white: hexToRGBString(#fff) => "255,255,255"
$color-white: hexToRGBString(rgb(255,255,255)) => "255,255,255"
$color-white: hexToRGBString(rgba(#fff,1)) => "255,255,255"

I've hacked around it with a SASS function:
#function hexToString($hexColor) {
// 0.999999 val in alpha actually compiles to 1.0
$rgbaVal: inspect(rgba($hexColor,0.9999999));
// slice substring between 'rgba(' and '1.0)'
#return str-slice($rgbaVal, 6, str-length($rgbaVal)-6);
}
Usage:
$brand-primary: #333;
$color-primary: hexToString($brand-primary);
I think the MDL team intended to have a different way to customise the palette and I'm missing it, so if someone knows a better way to customise MDL's palette I'm open to suggestions. Either way this solves the original question.

Related

Scss function returns no calculated value

I am working on a mixin for breakpoints and I have the following issue.
when a specific state (mode for max-width) is set then the breakpoint should be recalculated by extracting one em value (1px/16 (default font size)).
This is the important part of my code (I might get rid of the function, basically this can be done inline):
$mediaBreakpoint: map-get( $breakpoints, $breakpoint );
// if the mode is for max-width then subtract 1px.
#if map-get( $modes, $mode ) == 'max-width' {
$mediaBreakpoint: calculateMaxWidth(#{$mediaBreakpoint})
}
#debug $mediaBreakpoint;
/**
* calculate the max width based on input
*/
#function calculateMaxWidth($breakpoint){
$newBreakpoint: calc( #{$breakpoint} - 0.0625em ); // 1px in em sizing.
#return $newBreakpoint;
}
But whatever I try, the #debug value shows as:
48em-0.0625em // this is invalid, I need the actual outcome (in this case 47.9375) .
64em // valid min-width
This is the compiled css:
#media screen and (max-width: calc( 48em - 0.0625 )) {
}
What am I missing?
I found the answer myself after a lot of debugging. At first I misunderstood the interpolation. After reading the docs in depth I noticed that I should have wrapped the whole calculation instead of just the variable because I am working inside the map expression.
Quoted from the official Sass docs:
Interpolation can be used almost anywhere in a Sass stylesheet to embed the result of a SassScript expression into a chunk of CSS.
I changed my function to calculate like this and then the mixin started working. hooray!
$newBreakpoint: #{$breakpoint - 0.0625em};

SASS/SCSS: Define variables based on other variables

How does one define variables with the use of other variables in SASS?
This is how one could do it with LESS:
// import Google Material Colors
// returns variables ie #blue-500, #blue-400 etc
#import 'material.colors.less';
// base
#_color: 'blue';
#_secondary: 'amber';
// primary colors
#color-primary: ~"#{#{_color}-500}";
#color-primary-bright: ~"#{#{_color}-300}";
#color-primary-brighter: ~"#{#{_color}-200}";
#color-primary-brightest: ~"#{#{_color}-50}";
// secondary colors
#color-secondary: ~"#{#{_secondary}-500}";
#color-secondary-bright: ~"#{#{_secondary}-300}";
#color-secondary-brighter: ~"#{#{_secondary}-200}";
#color-secondary-brightest: ~"#{#{_secondary}-50}";
The LESS-way certainly isn't clean and dandy, but -- it works™
The idea is to set a base primary and then just set the other color(s) dynamically based on that.
I can't imagine that one would have to loop/map etc just to do this with SASS?(!)
Sass does not support dynamic variables, period.
You will need to use maps, but I'm not sure this will help you in this specific case (as you are already using external colors):
#import 'material.colors';
$colors: (
primary: (
default: $blue-500,
bright: $blue-300,
brighter: $blue-200,
brightest: $blue-50
),
secondary: (
default: $amber-500,
bright: $amber-300,
brighter: $amber-200,
brightest: $amber-50
)
);
#function color($color, $brightness: default) {
#return map-get(map-get($colors, $color), $brightness);
}
h1 {
color: color(primary, bright);
background-color: color(secondary);
}
Of course you can do it. You can use neat sass color functions and do what you need in a nice and clean way. Take a look at lighten function and even darken function, or at other color functions in general.
Basically, you do it like this:
$primary-color: #08f;
$primary-light-color: lighten($primary-color, 20%);
$primary-lighter-color: lighten($primary-color, 30%);
$primary-dark-color: darken($primary-color, 20%);
$primary-darker-color: darken($primary-color, 30%);
You can see this in action here:
https://codepen.io/anon/pen/PjXRjM?editors=1100#0
Or if you feel like it's a good idea, you could automate it a little with lists and loops. Take a look at this article: https://www.sitepoint.com/managing-color-values-with-sass/, where its author gets through it. (To be honest, I'm not sure if that a good idea at all, as it easily may be hard to understand and maintain later. That's another topic, though.)

Sass function to construct a URL

I'm trying to write an SCSS function that returns a URL. (So I could use it like background-image: getURL(thing);)
The difficult thing is I want to interpolate arguments into it, including escaping the args, e.g. # should become %23 so it is URL-safe.
Is this sort of thing even possible with Sass?
#function getURL($name, $color: #ffffff) {
// ???
}
// How I want it to work:
getURL('foo');
// returns: url("http://example.com/foo.png?color=%23ffffff")
// And if possible, this would be cool
// (accepting any color type, and turning it into a hex color):
getURL('bar', rgb(255,0,0));
// returns: url("http://example.com/bar.png?color=%23ff0000")
You could write your own Ruby method to do the URL encoding and color conversion. It looks like Color#inspect returns a hex representation for colors with no alpha value.
You may be able to do some very simple URL encoding with SASS's str- index/insert/slice functions.
It might be easier to fake it:
#function getURL($name, $color-hex-str: ffffff) {
#return url(http://example.com/#{$name}.png?color=%23#{$color-hex-str});
}

Change Default d3.js colors

I was looking for a way to change the default colors of the different categories in d3.js.
I found where the colors are laid out in the main d3.js. They look like this for one category:
var ml = [2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175].map(yt)
I've tried replacing these values with everything from Hex codes to HSL to RGB and it never yields the expected colors.
Any ideas how I can generate the proper numbers for whatever colors I want?
Thanks.
First, just FYI, to see the RGB (i.e. hex) value that corresponds to these numbers:
(2062260).toString(16); // 16 for hex, aka base 16
> "1f77b4"
Next, given an RGB (again, hex) that you want to convert to number:
parseInt("1f77b4", 16); // 16 for hex
> 2062260
And that would be the number you want to use.
The colors you got from the d3 source are used to construct what you get from d3.scale.category10(). You can get the same thing but with your own colors — and without modifying d3's source code — by constructing a d3.scale.ordinal:
var myCategory3 = d3.scale.ordinal()
.domain(["red", "#1f77b4", "rgb(128, 255, 128)"]);// All kinds of colors are possible
myCategory3("X");// "red"
myCategory3("blabla");// "#1f77b4"
myCategory3("X");// "red"
myCategory3(123456);// "rgb(128, 255, 128)"

How to get second attribute level with Selenium's xpath

I'm trying to check in Selenium if an element has a green border or not.
i can use //div[#id="target"]/#style to get the parsed style string applied... and use string search to search for the border... i have to work around some brosers showing the shorthand (border: solid 3px green) and other showing the expanded (border-style: solid; border-color: green....) ...but how could i do that more cleanly?
ideally something like: //div[#id="target"]/#style.borderColor
I also would like to avoid using the contains selector because the syntax is awful. But if it's the only way, so be it. of course.
XPath doesn't know anything about CSS styles, it doesn't understand the attributes and takes them just as simple strings. It would be weird if a XML query language understood CSS styles, right?
Besides, the getAttribute() uses a construct similar to XPath, but not XPath! It also selects the attribute via # sign, right. But consider this locator: "id=target#style". It would work, but it's definitely no XPath.
It would also return the explicitly set style attribute, not it's internal computed value, so it's useless unless there actually is a style attribute on the element. If there is style attribute defined, go for it.
Selenium itself can't give you the right answer (WebDriver has getCssValue(), but there's no counterpart for it in Selenium RC, afaik).
Therefore, the only way to get the computed style (the final value computed from all sources) of an element in Selenium RC is via JavaScript.
The script that has been working for me for ages (after a slight edit) is this (from quirksmode.org):
function getStyle(id, stylePropJs, stylePropCss) {
var x = document.getElementById(id);
if (x.currentStyle)
var y = x.currentStyle[stylePropJs];
else if (window.getComputedStyle)
var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(stylePropCss);
return y;
}
Note that IE needs a JavaScript name of the property, but every other browser uses CSS names. Also, you can't use the shorthand property and you definitely must use the expanded one. Therefore, in your case:
String script = "var x = document.getElementById('target');" +
"if (x.currentStyle) var y = x.currentStyle['borderTopColor'];" +
"else if (window.getComputedStyle) var y = document.defaultView.getComputedStyle(x,null).getPropertyValue('border-top-color');" +
"y;";
String color = selenium.getEval(script);
This returns the color of the top border (yep, you'll need to call it four times in order to get the whole border) of the target element. But guess what! Every browser returns the color in a different format. Fortunately, at least in Java, there's a Color class that can easily reformat it:
String color = Color.fromString(color).asHex();
Now that you have a hex encoded color stored, you can trivially make check whether it's green or not.

Resources