Reduce amount of CSS generated by a SASS function - css

I'm very new to the (I guess) more advanced features of SASS. Referencing this article I've been able to create a SASS setup that lets me easily do dark mode theming! I'd like to reduce the amount of CSS it generates, however, and wanted to know if I've reached the limit of my knowledge or just what SASS/CSS is capable of.
I have this map of "themes":
$themes: (
light: (
primaryOne: #f0f,
primaryTwo: #0ff
),
dark: (
primaryOne: #000,
primaryTwo: #111
)
);
I have these mixins/functions that were mostly lifted from the article:
#mixin themify($themes: $themes) {
#each $theme, $map in $themes {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#if $theme == dark {
#media (prefers-color-scheme: dark) {
#content;
}
} #else {
#content;
}
$theme-map: null !global;
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
And finally a custom mixin and the class where I'm using it:
#mixin set-color-primary {
#include themify($themes) {
color: themed('primaryOne');
}
}
.foo-container div {
#include set-color-primary;
}
Finally this generates CSS like the following:
.foo-container div {
color: #f0f;
}
#media (prefers-color-scheme: dark) {
.foo-container div {
color: #000;
}
}
I was hoping I could squeeze out a few more lines and get something more like:
.foo-container div {
color: #f0f;
#media (prefers-color-scheme: dark) {
color: #000;
}
}
Is this a limitation of my SASS knowledge or is this not possible in plain CSS?

Related

Sass Mixin Themify not Effecting Body

I've inherited some SASS code used for theming from someone and it looks like it is only compiling into rules that target children of the element that has the themed class.
$themes:(
default:(
background:#CCCCCC
)
);
#mixin themify($themes: $themes) {
#each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
Gives a compiled css rule as
.theme-default body {
background-color: #CCCCCC;
}
The code seems fine but the highest element that can have the themed class is the body but then how can the body be given a themed property? I tried to add an additional mixin that will not use the children (just removing the & to invert the element relation)
#mixin themifyDirect($themes: $themes) {
#each $theme, $map in $themes {
.theme-#{$theme} {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
But this compiles into a css rule
body .theme-default {
background-color: #26282F; }
This is close but not enough because the rule I need is
body.theme-default {
background-color: #CCCCCC;
}
To target body which has the class
#mixin themifyDirect($themes: $themes) {
#each $theme, $map in $themes {
&.theme-#{$theme} {
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
outputs
body.theme-default {
background-color: #CCCCCC;
}
You we're really close - note the leading '&' on line 3 which removes the space.

Use Sass #content and unify parent selector to immediate child selector

I'm trying to use Sass / SCSS to unify the #content directive to the parent class. I am attempting to use a conditional statement within the mixin to allow for both use cases:
$themes: (
Light: (
page_background: #ffffff,
),
Dark: (
page_background: #181818,
)
);
#function get_color($key) {
#return map-get($theme-map, $key);
}
#mixin theme($makeAncestor: true) {
#each $theme, $map in $themes {
$theme-map: $map !global;
#if $makeAncestor {
.#{$theme} & {
#content;
}
} #else {
.#{$theme} & {
#content;
}
}
}
$theme-map: null !global;
}
.fixed {
#include theme() {
background: get_color(page_background);
}
}
Here is the output:
.Light .fixed {
background: #ffffff;
}
Desired output:
.Light.fixed {
background: #ffffff;
}
You can use #at-root in your mixin and interpolate the parent selector:
#mixin theme($makeAncestor: true) {
#at-root {
#each $theme, $map in $themes {
$theme-map: $map !global;
#if $makeAncestor {
.#{$theme}#{&} {
#content;
}
} #else {
.#{$theme}#{&} {
#content;
}
}
}
$theme-map: null !global;
}
}

How to Change High Contrast Background Images using SASS

I am trying to make a mixin for standard background images and high contrasted background images, not sure how do i do it. can someone pl. shade some light
I have a mixin for text color and background color :
$colors:(
standard: (
primary-color-blue: #2aace2,
mid-blue-color:#2695d2,
dark-blue-color:#124b62
),
contrasted: (
primary-color-blue: #177eab,
mid-blue-color:#1c6f9b,
dark-blue-color:#124b62
)
);
#function get-color($key, $type: 'standard'){
#each $name, $color in map-get($colors, $type){
#if($key == $name){
#return $color
}
}
}
#mixin get-color($property,$color){
#{$property}: get-color($color);
#at-root body.contrasted & {
#{$property}: get-color($color, contrasted);
}
}
.className{
#include get-color(background-color, primary-color-blue)
}
output :
.className {
background-color: #2aace2;
}
body.contrasted .className {
background-color: #177eab;
}
Thanks !!
EDITED AS PER ANSWER : Edited with extra color / transparent variable, but throws error Edited with extra color / transparent variable, but throws error
$images:(
standard: (
pdp-more-icon-mob: white + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Mobile.png',
pdp-more-icon-mob-retina : #f00 + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Mobile__2x.png'
),
contrasted: (
pdp-more-icon-mob: green + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Desktop-High-Contrast.png',
pdp-more-icon-mob-retina : $green + $img_file_path + 'product-detail-page/Product-surface-display/more-icon/More-Desktop-High-Contrast__2x.png'
)
);
#function get-images($key, $type: 'standard'){
#each $name, $image in map-get($images, $type){
#if($key == $name){
#return $image
}
}
}
#mixin get-images($property, $color, $image, $retina:false){
#{$property}: get-images($color + $image);
#at-root body.contrasted & {
#{$property}: get-images($color + $image, contrasted);
}
#if($retina){
#media (-webkit-min-device-pixel-ratio: 2){
#{$property}: get-images($color + $image#{-retina});
#at-root body.contrasted & {
#{$property}: get-images($color + $image#{-retina}, contrasted);
}
}
}
}
.className{
#include get-images(background, color, image, true);
}
Without refactoring the mixin is possible to use it this way—by adding your images properly in your map:
$colors:(
standard: (
primary-color-blue: #2aace2,
mid-blue-color:#2695d2,
dark-blue-color:#124b62,
image: 'img.png',
image-retina: 'img-retina.png'
),
contrasted: (
primary-color-blue: #177eab,
mid-blue-color:#1c6f9b,
dark-blue-color:#124b62,
image: 'img2.png',
image-retina: 'img2-retina.png'
)
);
#function get-color($key, $type: 'standard'){
#each $name, $color in map-get($colors, $type){
#if($key == $name){
#return $color
}
}
}
#mixin get-color($property,$color){
#{$property}: get-color($color);
#at-root body.contrasted & {
#{$property}: get-color($color, contrasted);
}
}
.className{
#include get-color(background-image, image);
#media (-webkit-min-device-pixel-ratio: 2){
#include get-color(background-image, image-retina)
}
}
But you can also adapt this mixin for images and do so:
$images:(
standard: (
image: 'img.png',
image-retina: 'img-retina.png'
),
contrasted: (
image: 'img2.png',
image-retina: 'img2-retina.png'
)
);
#function get-images($key, $type: 'standard'){
#each $name, $image in map-get($images, $type){
#if($key == $name){
#return $image
}
}
}
#mixin get-images($property,$image, $retina:false){
#{$property}: get-images($image);
#at-root body.contrasted & {
#{$property}: get-images($image, contrasted);
}
#if($retina){
#media (-webkit-min-device-pixel-ratio: 2){
#{$property}: get-images($image#{-retina});
#at-root body.contrasted & {
#{$property}: get-images($image#{-retina}, contrasted);
}
}
}
}
.className{
#include get-images(background-image, image, true);
}
Where the true in 'get-images' is calling to image + '-retina' in the $images map, and outputs:
.className {
background-image: "img.png";
}
body.contrasted .className {
background-image: "img2.png";
}
#media (-webkit-min-device-pixel-ratio: 2) {
.className {
background-image: "img-retina.png";
}
body.contrasted .className {
background-image: "img2-retina.png";
}
}

Sass ampersand and two parents together in mixin?

I'm faced with the task of theming the site. I found a suitable mixin. Everything would work well, if not for the mixin for events. It turns out that I need to do something, so that if the topic's mixin is invoked in the mixin of events, then the class did not go cascade, but substituted for the topic class, the .no-touchevents class on the html tag.
Ideally, that would be so on the output:
.card {
color: #fff;
}
.t-dark .card {
color: #000;
}
.no-touchevents .card:hover {
color: #000;
}
.t-dark.no-touchevents .card:hover {
color: #fff;
}
It's a little hacky (or maybe a lot hacky) but by adding an additional parameter to the themify mixin and building our selector manually, you can achieve the output you're looking for.
$themes: (
dark: (
colorDark: #000,
colorLight: #fff
)
);
#mixin on-event() {
.no-touchevents &:hover {
#content;
}
}
#mixin themify($themes, $flat: ' ') { // Add a parameter to separate by default
#each $theme, $map in $themes {
#at-root .t-#{$theme}#{$flat}#{&} { // Build our selector manually
$theme-map: () !global;
#each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
#content;
$theme-map: null !global;
}
}
}
#function themed($key) {
#return map-get($theme-map, $key);
}
.card {
color: #fff;
#include themify($themes) {
color: themed('colorDark')
}
#include on-event() {
color: #000;
#include themify($themes, '') { // Tell themify not to separate selectors
color: themed('colorLight')
}
}
}

Sass and libraries for theming [duplicate]

I'm refactoring some of my Sass code and I came across a weird issue. My code currently looks like this:
// household
$household_Sector: 'household';
$household_BaseColor: #ffc933;
// sports
$sports_Sector: 'sports';
$sports_BaseColor: #f7633e;
// the mixin to output all sector specific css
#mixin sector-css($sector_Sector,$sector_BaseColor) {
.sector-#{$sector_Sector} {
&%baseColor {
background-color: $sector_BaseColor;
}
}
}
// execute the mixin for all sectors
#include sector-css($household_Sector, $household_BaseColor);
#include sector-css($sports_Sector, $sports_BaseColor);
.product-paging {
h2 {
#extend %baseColor;
}
}
DEMO
The compiled result looks like this:
.product-paging h2.sector-household {
background-color: #ffc933;
}
.product-paging h2.sector-sports {
background-color: #f7633e;
}
But what I need is this:
.sector-household.product-paging h2 {
background-color: #ffc933;
}
.sector-sports.product-paging h2 {
background-color: #f7633e;
}
What I don't understand is why my placeholder (&%baseColor) isn't attached to the parent selector (&%baseColor) as I added the ampersand right in front of it?
Is this maybe a bug when combining & and %? Is there another solution on how to achieve what I want?
EDIT
Alright I figured out why this isn't possible. Anyway is there a workaround for what I'd like to achieve?
Extends, as you've already discovered, can get rather messy. I would go about solving your problem by using an #content aware mixin in combination with global variables (this uses mappings, which are part of 3.3... you can do it with lists of lists, but it's a little less elegant):
$base-color: null; // don't touch
$accent-color: null; // don't touch
$sections:
( household:
( base-color: #ffc933
, accent-color: white
)
, sports:
( base-color: #f7633e
, accent-color: white
)
);
// the mixin to output all sector specific css
#mixin sector-css() {
#each $sector, $colors in $sections {
$base-color: map-get($colors, base-color) !global;
$accent-color: map-get($colors, accent-color) !global;
&.sector-#{$sector} {
#content;
}
}
}
.product-paging {
#include sector-css() {
h2 {
background-color: $base-color;
}
}
}
Output:
.product-paging.sector-household h2 {
background-color: #ffc933;
}
.product-paging.sector-sports h2 {
background-color: #f7633e;
}
Update: Since you want to guarantee that the sector class is always at the top, you just need to switch around a little.
#mixin sector-css() {
#each $sector, $colors in $sections {
$base-color: map-get($colors, base-color) !global;
$accent-color: map-get($colors, accent-color) !global;
.sector-#{$sector} {
#content;
}
}
}
#include sector-css() {
&.product-paging {
h2 {
background-color: $base-color;
}
h3 {
background-color: #CCC;
}
h2, h3 {
color: $accent-color;
}
}
}

Resources