Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to add dark mode to my application with SCSS?

Tags:

css

sass

angular

I've been set the task of adding a toggle on an angular web application which will allow users to switch from the default light mode theme to a dark mode theme. I can't find a way to successfully implement this.

When I got the task there was a _variables.scss file in the styles directory. This contained variables for colours, fonts, sizing and spacing. The colours were in maps and then each shade was assigned to a variable using the map-get() method e.g $shade-0: map-get($shades, 'shade-0').

Initially I thought that I could create a themes.scss file and import it alongside _variables.scss. This file would then link to 2 further scss files lightTheme.scss and darkTheme.scss. Each theme file would hold a list of colour variables similar to the original ones in variables.scss. I can get this to work for 1 theme or the other, but I can't switch between theme files.

darkTheme.scss

$shades: (
  'shade-6':                            #f5f5f5,
  'shade-5':                            #BDBDBD,
  'shade-4':                            #9E9E9E,
  'shade-3':                            #757575,
  'shade-2':                            #616161,
  'shade-1':                            #303437,
  'shade-0':                            #404447,
);

$shade-0:                              map-get($shades, 'shade-0');
$shade-1:                              map-get($shades, 'shade-1');
$shade-2:                              map-get($shades, 'shade-2');
$shade-3:                              map-get($shades, 'shade-3');
$shade-4:                              map-get($shades, 'shade-4');
$shade-5:                              map-get($shades, 'shade-5');
$shade-6:                              map-get($shades, 'shade-6');

$colors: (
  'forest':                            #239F28CC,
  'aqua':                              #8ab4f8,
  'ruby':                              #C93939CC,
  'zing':                              #20CAC3CC,
  'carrot':                            #E9853ECC,
  'grape':                             #7542F2CC,
  'midnight':                          #433F5CCC,
  'slate':                             #657786CC,
);

$forest:                               map-get($colors, 'forest');
$aqua:                                 map-get($colors, 'aqua');
$ruby:                                 map-get($colors, 'ruby');
$zing:                                 map-get($colors, 'zing');
$carrot:                               map-get($colors, 'carrot');
$grape:                                map-get($colors, 'grape');
$midnight:                             map-get($colors, 'midnight');
$slate:                                map-get($colors, 'slate');

$bg-color:                            map-get($shades, 'shade-1');
$border-color:                        map-get($shades, 'shade-2');
$border-dark-color:                   map-get($shades, 'shade-3');
$text-color:                          map-get($shades, 'shade-6');
$muted:                               map-get($colors, 'slate');
$subtle:                              map-get($shades, 'shade-4');

lightTheme.scss

$colors: (
      'forest':                            #239F28,
      'aqua':                              #186EEF,
      'ruby':                              #C93939,
      'zing':                              #20CAC3,
      'carrot':                            #E9853E,
      'grape':                             #7542F2,
      'midnight':                          #433F5C,
      'slate':                             #657786,
);
$shades: (
  'shade-0':                            #ffffff,
  'shade-1':                            #f5f5f5,
  'shade-2':                            #d8d8d8,
  'shade-3':                            #bbbbbb,
  'shade-4':                            #979797,
  'shade-5':                            #535353,
  'shade-6':                            #0c0c0c,
);
$shade-0:                              map-get($shades, 'shade-0');
$shade-1:                              map-get($shades, 'shade-1');
$shade-2:                              map-get($shades, 'shade-2');
$shade-3:                              map-get($shades, 'shade-3');
$shade-4:                              map-get($shades, 'shade-4');
$shade-5:                              map-get($shades, 'shade-5');
$shade-6:                              map-get($shades, 'shade-6');
$forest:                               map-get($colors, 'forest');
$aqua:                                 map-get($colors, 'aqua');
$ruby:                                 map-get($colors, 'ruby');
$zing:                                 map-get($colors, 'zing');
$carrot:                               map-get($colors, 'carrot');
$grape:                                map-get($colors, 'grape');
$midnight:                             map-get($colors, 'midnight');
$slate:                                map-get($colors, 'slate');
$bg-color:                             map-get($shades, 'shade-1');
$border-color:                         map-get($shades, 'shade-2');
$border-dark-color:                    map-get($shades, 'shade-3');
$text-color:                           map-get($shades, 'shade-6');
$muted:                                map-get($colors, 'slate');
$subtle:                               map-get($shades, 'shade-4');

themes.scss

@import 'global/lightTheme';
@import 'global/darkTheme';

I did try changing the variables from scss variables to css variables and use them with var() but I ran into difficulties as certain component use darken(), lighten() and mix() and therefore don't compile. Is there a way to get this working?

like image 335
Ben Newton Avatar asked Jul 13 '19 09:07

Ben Newton


1 Answers

This question was asked a year ago, but still helpful for anyone reading this, here's a simpler solution.

Highlights

  • Javascript is only used to toggle the class of your root element.
  • You don't have to define separate classes of themes for each element.

All you have to do in your scss file is:

.content {
  padding: 32px;
  @include theme() {
    color: theme-get('text-color');
    background-color: theme-get('bg-color');
  }
}

Implementation

You can make a separate file, let's say themes.scss in which you can define properties for both of your themes:

$themes: (
  darkTheme: (
    'text-color': white,
    'bg-color': #424242
  ),
  lightTheme: (
    'text-color': black,
    'bg-color': #f5f5f5
  )
);

Use a mixin:

// From Sass 2.0 on, it is no longer allowed to declare globals on the fly.
$theme-map: null;

@mixin theme() {
  @each $theme, $map in $themes {
    // $theme: darkTheme, lightTheme
    // $map: ('text-color': ..., 'bg-color': ...)

    // make the $map globally accessible, so that theme-get() can access it
    $theme-map: $map !global;

    // make a class for each theme using interpolation -> #{}
    // use & for making the theme class ancestor of the class
    // from which you use @include theme() {...}
    .#{$theme} & {
      @content;    // the content inside @include theme() {...}
    }
  }
  // no use of the variable $theme-map now
  $theme-map: null !global;
}

Now, you can access the property of a theme using map-get($theme-map, ...). But we can avoid passing $theme-map as an argument every time, by defining a function which will do it for us.

@function theme-get($key) {
  @return map-get($theme-map, $key);
}

The resultant css file will be:

.content {
  padding: 32px;
}

.darkTheme .content {
  color: white;
  background-color: #424242;
}

.lightTheme .content {
  color: black;
  background-color: #f5f5f5;
}

Example

Here is a fiddle for demonstration: https://jsfiddle.net/aksh101099/zubnapr9/1/

like image 82
Akshdeep Singh Avatar answered Sep 16 '22 16:09

Akshdeep Singh