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};
Related
I have two examples that I'm trying to solve:
Example 1
$test: #101E41
body
--colors-dim: rgba(#{$test}, 0.64)
Output: rgba(#101E41, 0.64)
Example 2
body
--colors-active: #101E41
--colors-dim: rgba(var(--colors-active), 0.64)
Output: rgba(var(--colors-active), 0.64)
Both of these look like are examples that should be valid as shown here: https://sass-lang.com/documentation/modules#rgb
Is there something I'm missing?
You need to make use of interpolation to use Sass inside CSS Custom Properties
CSS custom properties, also known as CSS variables, have an unusual declaration syntax: they allow almost any text at all in their declaration values. What’s more, those values are accessible to JavaScript, so any value might potentially be relevant to the user. This includes values that would normally be parsed as SassScript.
Because of this, Sass parses custom property declarations differently than other property declarations. All tokens, including those that look like SassScript, are passed through to CSS as-is. The only exception is interpolation, which is the only way to inject dynamic values into a custom property.
$bar: #900;
:root {
--foo: #{rgba($bar, 0.5)};
}
Results in:
:root {
--foo: rgba(153, 0, 0, 0.5);
}
For your second example, you're going to have to get a little... creative... since Sass will bail and ignore any CSS Custom Property syntax it sees, you can't make use of Sass's rgba function with Custom Properties - the Sass compiler won't resolve the values for you.
Thankfully, you can still use the native CSS rgba function with Custom Properties, the only downside is that you'll need to break your hexadecimal value into its R, G, and B values.
#function toRGB($color)
#return red($color), green($color), blue($color)
$bar: #900
:root
--foo: #{$bar}
--foo-rgb: #{toRGB($bar)}
--foo-dim: #{rgba($bar, 0.5)}
--foo-dim: rgba(var(--foo-rgb), 0.5)
.button
background-color: var(--foo-dim)
Compiles to:
:root {
--foo: #900;
--foo-rgb: 153, 0, 0;
--foo-dim: rgba(153, 0, 0, 0.5);
--foo-dim: rgba(var(--foo-rgb), 0.5);
}
.button {
background-color: var(--foo-dim);
}
https://www.sassmeister.com/gist/39ffc57c492de73066831afe5a9696f6
I'm calculating different line-heights within a Sass mixin to ensure that different elements adhere to a vertical rhythm. My $vertical-rhythm-base is currently 8px.
Some browsers (at least Chrome) seem to floor a value of 1.33333 and use 23px instead of the desired 24px I had in mind. So I tried to correct that value within my mixin:
$vertical-rhythm-base: 8px
=calculateLineHeight($itemFontSize)
$lineHeightBase: $vertical-rhythm-base * 3
$itemLineHeight: (ceil($itemFontSize/$lineHeightBase)*$lineHeightBase)/$itemFontSize
// fix miscalculation for this value since browsers resort to 23px
// instead of 24px in the current setup.
#if $itemLineHeight == 1.33333
$itemLineHeight: 1.3334
line-height: $itemLineHeight
.someElement
+calculateLinHeight(18px)
font-size: 18px
This doesn't seem to work since the items that should get a line-height: 1.33334 still get line-height: 1.33333.
I don't get what is going wrong here. Changing the #if to use = instead of == results in assigning line-height: 1.33334 to everything instead of calculating it correctly.
Sass doesn't perform rounding until after the comparisons are already done and it is time to generate the output. Your variable still contains a repeating decimal, even if it looks like it doesn't. You must compare repeating values to repeating values if you're checking for equality:
$foo: 4 / 3;
#debug $foo == 1.33333; // false
#debug $foo == 4 / 3; // true
Note that if Sass performed rounding at comparison time rather than after, your if-statement would only evaluate to true when using the default precision setting of 5 (there are a number of Sass libraries that require higher precision settings than that, I ran the above code with a precision of 10). Also note that the default precision for Sass has changed once before and is likely to change again in the future.
1,33333 is repetend. Try "==(4/3)"
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.
I have a question. So in a mixing I am making a reference to the parent selector "&". This works as long as the mixin is not nested. Is there a way to to detect if the mixing is being used in a non nested scenario, or to check if "&" is null?
This works when the mixin call is not nested
=myresponsiveMixin($media)
#if $media == small {
#media only screen and (max-width: $break-small)
#content
#else if $media == medium
#media only screen and (min-width: $break-small + 1) and (max-width: $break-large - 1)
#content
This works great when the mixin call is nested, but will not resolve '&' when not nested
=myresponsiveMixin($media)
#if $media == small {
#media only screen and (max-width: $break-small)
.classInHTMLToAllowMediaQueries &
#content
#else if $media == medium
#media only screen and (min-width: $break-small + 1) and (max-width: $break-large - 1)
.classInHTMLToAllowMediaQueries &
#content
So the question is, if there is a way to be able to check the value of parent selector "&", so I can cover all bases in a single mixin?
#mixin does-parent-exist {
#if & {
.exists & {
color: red;
}
} #else {
.doesnt-exist {
color: red;
}
}
}
http://sass-lang.com/documentation/file.SASS_REFERENCE.html#parent-script
You're trying a wrong solution to solve your issue.
Have a look at how this problem is addressed in powerful SASS frameworks. Let's take Susy by Eric Meyer as a great example.
Let's imagine you've got the following HTML:
<div class="container">
<div class="parent">
<div class="child">
Bla bla
</div>
</div>
</div>
When you call a mixin for the first time, you're doing it simply (the code is in the indented .sass syntax):
$total-columns: 8 // Declaring a varible that will be used by the mixin
.parent
+span-columns(4) // Span four of eight columns
But when you call that for a child element, the proportions would be crooked, because the parent is already proportioned:
.child
+span-columns(2) // This will fail. You want 2 of 8 columns,
// but due to nesting the math is crooked.
// It will be "2 of (4 of 8)".
To address the issue, you provide an optional argument: a context that is used to do the math:
.child
+span-columns(2, 4) // Now the mixin will take 2 parts of 4
// instead of 2 parts of four
The source code for this mixin is available on GitHub.
In short, it creates an optional argument like this (the code is in the CSS-like .scss syntax):
#mixin span-columns(
$columns,
$context: $total-columns
//...
) {
//...
width: columns($cols, $context /*...*/);
//...
}
See how $context has a default value? Thanks to the default value this argument can be omitted. In other words, $context is an optional argument.
When calling this mixin, if $context is not provided (e. g. span-columns(2)), then it is set equal to $total-columns. The $total-columns variable should be set prior to calling the mixin for the first time (see my example above).
Then the two arguments are used to calculate the width.
UPD 2013-03-30
I am not trying to figure out things in regards to columns... I have modifier my question to make it clearer.
First of all, my recommendation concerns not only grid columns. It's a universal technique you can adopt.
Secondly, now i see that you're trying to nest media queries.
Well, some media queries of different type can be combined in CSS3: e. g. print and width. But you can't put a min-width: 601px inside max-width: 600px, this just won't work!
There's an extensive answer here on StackOverflow describing why you should not nest media queries of the same type: https://stackoverflow.com/a/11747166/901944
Thirdly, you're trying to invent the wheel. There's already a fantastic mixin for crunching media queries: Respond To by Snugug. It's super easy to use and very effective.
Fourthly, the XY thing. Instead of asking about your crooked mixin, please describe the problem that you're trying to solve with it! Show us the actual HTML and explain what behavior you would like to achieve.
We will show you that it can be solved with a simple, elegant, semantic solution that does not require SASS hacking.
I am writing a #mixin with some math in it that calculates the percentage width of an element, but since it is very useful I would like to use the same function for other properties too, like margins and paddings.
Is there a way to pass the property name as an argument to a mixin?
#mixin w_fluid($property_name, $w_element,$w_parent:16) {
$property_name: percentage(($w_element/$w_parent));
}
You need to use interpolation (eg. #{$var}) on your variable in order for Sass to treat it as a CSS property. Without it, you're just performing variable assignment.
#mixin w_fluid($property_name, $w_element, $w_parent:16) {
#{$property_name}: percentage(($w_element / $w_parent));
}
In addition to the #rcorbellini response
You can use string and variable together
#mixin margin($direction) { // element spacing
margin-#{$direction}: 10px;
}