Setting value of a global variable in media query (revisited) - sass

Based on this reply, I wrote:
$global-padding: 1em;
#media only screen and (max-width: 20em) {
$global-padding: 0.5em !global;
}
For some reason, the padding is always 0.5em, not only when the screen is at most 20em wide. What am I missing?

As far as I understand the documentation the global variable gets overwritten during compile time, as soon as the nested rule is processed - and not if any condition is met. This is not a runtime feature. All SCSS is compile to plain old CSS during compile time.
A variable declaration flagged as !global will always assign to the global scope.
So doing something like this:
$variable: foo;
.content {
$variable: bar !global;
value: $variable;
}
.sidebar {
value: $variable;
}
will produce
.content {
value: bar;
}
.sidebar {
value: bar;
}
What you are trying to do can be done with css custom variables:
:root {
--global-padding: 1em;
#media only screen and (max-width: 20em) {
--global-padding: 0.5em;
}
}

Related

check if the parent has a specific class on it

This is the usual list where one of the items is .open
for this I want to check if the parent (in this case is .item but its not relevant i think) has a specific class.
I've tried > but it doesnt seem to work.
Essentially how to put this:
&.open .info {
display: none;
}
&.open .inner-info {
display: flex;
}
inside of the their specific classes:
.info {
display: flex;
/* some other stuff */
}
.inner-info {
display: none;
/* some other stuff */
}
all of this is inside an .item{} block
So how do i have it so that i only have two blocks inside the .item{}?
It seems overkill to me, but you can use a hacky way to do that using a mixin and various functions. Please note that this will work for your specific example but probably not for something else.
I used the helper functions str-to-list and nth-delete, which are not native to SASS.
#mixin parentWithClass($class) {
$parent: nth-delete(str-to-list(#{&}), -1);
#at-root #{selector.replace(&, $parent, #{$parent}#{$class})} {
#content;
}
}
.item {
.inner {
color: blue;
#include parentWithClass(".open") {
color: orange;
}
}
.inner-info {
color: red;
#include parentWithClass(".open") {
color: grey;
}
}
}
You can also nest -info in inner.

sass merge selectors on ampersand

assuming I have an existing SASS rule like:
[dir] .foo {
background-image: ...;
// ... some more
}
And I want to add specific behaviors for ltr / rtl like:
[dir] .foo {
background-image: ...;
[dir='ltr'] & {
padding-right: ...;
}
[dir='rtl'] & {
padding-left: ...;
}
}
this would generate undesired css like:
[dir='rtl'] [dir] .foo {
padding-left: ...;
}
This will not match what I want.
Assuming I cannot change the parent selectors (due to specificity), is there any way I can write such nested selectors in a way that compiles to just [dir='rtl'] .foo {...} for the nested elements?
Some resources about the ampersand: https://css-tricks.com/the-sass-ampersand/#aa-qualifying-based-on-context
There is no way that I know to merge selectors as requested.
As you're not allowed to change the parent selector, the only solution I see would be to use the #at-root rule.
#at-root
The #at-root rule is usually written #at-root { ... } and causes everything within it to be emitted at the root of the document instead of using the normal nesting. It's most often used when doing advanced nesting with the SassScript parent selector and selector functions.
Definition on sass-lang.
Here is an example:
[dir] .foo {
$root: '.foo';
background-image: linear-gradient(black, white);
#at-root {
[dir=ltr] #{$root} {
padding-right: 1em;
}
[dir=rtl] #{$root} {
padding-left: 1em;
}
}
}
This will compile to:
[dir] .foo {
background-image: linear-gradient(black, white);
}
[dir=ltr] .foo {
padding-right: 1em;
}
[dir=rtl] .foo {
padding-right: 1em;
}
You could create a mixin to help you with that:
#mixin dir($dir: ltr, $selector: &) {
#at-root {
[dir=#{$dir}] #{$selector} {
#content;
}
}
}
[dir] .foo {
$root: '.foo';
background-image: linear-gradient(black, white);
#include dir(ltr, $root) {
padding-right: 1em;
}
#include dir(rtl, $root) {
padding-right: 1em;
}
}
Food for thougt
If you don't have to support internet explorer, you might want to check padding-inline-end and padding-inline-start properties.
They will free you from the need to have different rules for different directions.
padding-inline-end
The padding-inline-end CSS property defines the logical inline end padding of an element, which maps to a physical padding depending on the element's writing mode, directionality, and text orientation.
MDN Docs - padding-inline-end
padding-inline-start
The padding-inline-start CSS property defines the logical inline start padding of an element, which maps to a physical padding depending on the element's writing mode, directionality, and text orientation.
MDN Docs - padding-inline-start

How to prefix SASS blocks with a specific scope ID via mixin

Looking to be able to add the app scope id to my sass files when we have multiple apps reusing class names.
That way I can have the following definition:
$app-scope-id: 'appOne';
And then write my SCSS in that app
.blockName{
background: blue;
&__element{
color: orange;
}
}
And call a mixin or something else to just go
#include prefixMixin(){
.blockName{
background: blue;
&__element{
color: orange;
}
}
}
And that render out css like:
.appOne-blockName{ background: blue; }
.appOne-blockName__element{ color: orange }
I'm aware I can use interpolation at the beginning of my block, but was hoping I could keep it cleaner with just a mixin call where necessary and only call it once for an entire SASS file if I wanted.
I don't think it's possible to do what you want with SASS. You could maybe do something like this:
$app-scope-id: 'appOne';
#mixin prefix($selectorType: ".") {
#at-root {
#{$selectorType}#{$app-scope-id}-#{&} {
#content;
}
}
}
blockName {
#include prefix() {
background: blue;
&__element{
color: orange;
}
}
}
Which compiles as:
.appOne-blockName { background: blue; }
.appOne-blockName__element { color: orange; }
But you would still need to include it for each selector that needs the prefix. I'm not sure this can be called "clean" either.

Is it possible to create functions (or something else) that include selectors with SASS?

I want to be able to write:
div {
background-size: 100%;
#bgimgfunction('img1.png');
}
and have SASS produce something like:
div {
background-size: 100%;
/* Generated by the call to #bgimgfunction */
background-image:('/img/img1-medium.png');
#media (max-width: 640px) {
background-image:('/img/img1-low.png');
}
#media (min-width: 1600px) {
background-image:('/img/img1-high.png');
}
/* End generated by the call to #bgimgfunction */
}
mixins I think don't work because I can't pass a parameter
functions I think don't work because they are only valid after a selector.
Is there a way to do this?
What you need is a #mixin which does take parameters. Given the structure of your image URLs, I think you need two arguments, one for the image name and one for its extension:
#mixin bgImageFunction($imageName, $imageExt) {
$path: '/img/' + $imageName;
background-image: url("#{$path}-medium.#{$imageExt}");
#media (max-width: 640px) {
background-image: url("#{$path}-low.#{$imageExt}");
}
#media (min-width: 1600px) {
background-image: url("#{$path}-high.#{$imageExt}");
}
}
div {
background-size: 100%;
#include bgImageFunction('img1', 'png');
}
You can also use a default parameter for the extension and only pass the name as argument:
#mixin bgImageFunction($imageName, $imageExt: 'png') {
...
}

Using a sass variable in an #each loop

I'm trying to create a little overview for all the colors we use in our corporate identity. All our colors have been defined in _settings-colors.scss, and the only reason I need this bit of css is for the library, where the colors need to be listed.
What I have now is as follows:
$colors-brand: color-brand, color-brand-40, color-brand-60, color-brand-70;
.prfx-color {
display: block;
height: 5rem;
width: 100%;
#each $color in $colors-brand {
&--#{$color} {
background-color: #{'$'+$color};
&::after {
content: '$'+$color;
}
}
}
}
These color-brand variables are set in another file which I'm including in this scss file.
The code above outputs this:
.prfx-color {
display: block;
height: 5rem;
width: 100%;
}
.prfx-color--color-brand {
background: $color-brand;
}
.prfx-color--color-brand::after {
content: "$color-brand";
} [...etc]
What I'm after however, is this:
.prfx-color--color-brand {
background: #00ff11; // don't worry, brand is not actually this color
}
The problem I'm having is that the $color-brand variable isn't interpreted as a sass variable anymore, but is a literal value. I need the #hheexx that this variable refers to!
All the solutions I've found so far consist of using two lists, or a key-value pair. In my situation these variables have already been set once, and I want a solution where I don't want to have to manually edit the library if the colors change.
Is this at all possibe, or am I too greedy here?
And I realized I overcomplicated it. You don't need any extra functions because the #each is designed to work with maps and iterating over multiple values.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $key, $val in $colors {
&--#{$key} {
background-color: $val;
&::after { content: "$#{$key}"; }
}
}
}
You could use a map.
Here's a sassmeister playground for you.
$cool: blue;
$mad: red;
$colors: (
cool: $cool,
mad: $mad
);
.prfx-color {
#each $color in map-keys($colors) {
&--#{$color} {
background-color: map-get($colors, $color);
&::after { content: "$#{$color}"; }
}
}
}

Resources