Overriding component SASS variables in dynamic themes


#1

I’m working on allowing my users to flip between two themes at runtime. light-theme and dark-theme. I’m having trouble using sass variables within my two classes.

In my variables.scss, I import my two scss files as follow:

@import "theme.light";
@import "theme.dark";

In theme.light.scss I have the following code:

.light-theme {
  $customColor: yellow;
  $list-md-background-color: $customColor;

  .item {
    background-color: $customColor;
  }
}

At the root of my application, I apply the selectedTheme dynamically as follow:

<ion-nav [root]="rootPage" #content swipeBackEnabled="false" [class]="selectedTheme"></ion-nav>

This partially works when I defined classes in my theme files as follow:

.item {
    background-color: $customColor;
  }

but it does not work when attempting to define sass variables to customize ionic components as follow:

$list-md-background-color: #ff0000;

Perhaps I’m misunderstanding how to use SASS but is there a way to override Ionic component SASS variable in separate theme files?


#2

Ionic theming is based on you overriding the (shared) Sass variables found in Ionic’s source scss files.

For example (variables.scss):

$text-color:         white !default;
$link-color:         color($colors, light) !default;
$toolbar-background: transparent !default;

Then:

@import "ionic.theme.default";

@import "./green-and-blue-theme";
@import "./facebook-messenger-theme";

See: https://github.com/Robinyo/big-top/blob/master/src/theme/variables.scss

And in app.html:

<ion-nav main [root]="rootPage" #content swipeBackEnabled="false" [class]="theme"></ion-nav>

See: https://github.com/Robinyo/big-top/blob/master/src/app/app.html

And in app.component.ts:

  public toggleTheme() {

    if (this.theme === 'facebook-messenger-theme') {
      this.theme = 'green-and-blue-theme';
    } else {
      this.theme = 'facebook-messenger-theme';
    }
  }

See: https://github.com/Robinyo/big-top/blob/master/src/app/app.component.ts

Ref: https://robferguson.org/blog/2017/11/12/theming-your-ionic-3-app/


#3

Thanks for the example Rob. It seems to confirm what I was thinking.
When importing theme files, it doesn’t look like you can override Ionic component SASS variables. In your variables.scss file, you make global changes as follow:

$text-color:         white !default;
$link-color:         color($colors, light) !default;
$toolbar-background: transparent !default;

but once you get into the individual theme files, you make modifications to the classes themselves instead of the sass variables:

.green-and-blue-theme {
  ion-content {
    @include linear-gradient(left,$green-and-blue);
    -webkit-background-size: cover;
    -moz-background-size: cover;
    background-size: cover;
  }
}

I was hoping to “skin” my app with various theme files using the Ionic SASS variables. Do you know if there is a way to do that?

Otherwise, I’d have to inspect the resulting html and override the values of the resulting classes such as ion-content and toolbar-background, etc… kind of a pain. (But then again, scrolling through the list of Ionic scss variables is not exactly a time saver either). Would there be a downside to creating multiple theme files who each override 10-20 classes like ion-content and toolbar-background and not using the sass variables?


#4

Did you resolve it? I have the same question!
I have plenty of custom themes, but I cannot change de color of the tabs icon, I’ve tried with $tabs-tab-color-active: #ff0000; but that’s globally, and red does’nt fit with black, jaja.


#5

it’s simply not possible. sass is transpiled to css at build time so the variables become part of the final CSS, and hence they’re static. This means that they are statically coded in the CSS.
I’m also looking for some smart solution to achieve the same goal as you but one thing I’m pretty sure is that sass does not offer any way how to achieve this…
The only solution I can see so far is to target tags and components in the theme css files. Not ideal IMHO.


#6

I got it working decently well. Here is how:

In each page’s scss, I set only the layout for my components. No colors. In the html, I always set two classes. One for the layout and one for the color.

Then in my Themes folder, I added two scss files. theme.dark.scss and theme.light.scss.

In my variables.scss I import both files:

@import "theme.light";
@import "theme.dark";

My two theme files go as follow:

.dark-theme {

  $bodyBackground: #110521;
  $navBarBackground: #040f1f;
  $canceled: #ffea00;
  $textColor: #e3e3e3;
  $rowColor: linear-gradient(to bottom, #30304c 0%, #1a2232 49%, #0e1113 100%);
  $rowBorder: thin solid rgba(35, 34, 71, 0.7);
  $facebook: #4267B2;
  $fadedText: rgba(255, 255, 255, 0.5);
  $menuBorder: rgba(128, 128, 128, 0.51);

  $sectionHeaderBackground: linear-gradient(90deg, rgb(26, 59, 74) 0%, rgb(28, 32, 51) 20%, rgba(20, 19, 18, 0) 60%);
  $tableHeaderBackground: linear-gradient(to bottom, rgba(42,42,51,1) 0%, rgba(20,57,82,1) 51%, rgba(20,19,18,1) 100%);
  $tableBackground: #000048;
  $tableBorder: thin solid grey;
  $tableRowBackground: linear-gradient(to bottom, rgba(72,72,87,1) 0%, rgba(42,78,102,1) 51%, rgba(57,62,92,1) 100%);

  .sectionHeaderColors {
    background: $sectionHeaderBackground;
  }
}

My theme.light.scss of course starts with

.light-theme {...

and contains the same color classes. Only the $variables at the top change from one theme to the other.

You’ll notice that my theme files only specify colors. In my various pages, I could go specify that one of my section header is affected by 2 classes. .sectionHeaderLayout and .sectionHeaderColors

The trick is to switch the class that’s applied at the root of your app so that you are using theme.dark or theme.light. The result is that you’ll switch from using all the color classes from one theme to the other.

My app.html contains:

<ion-nav [root]="rootPage" #content swipeBackEnabled="false" [class]="selectedTheme"></ion-nav>

Then in your app.component.ts, you just have to switch the variable “selectedTheme” to the name of the class you want to apply at the root. So:

this.selectedTheme = "dark-theme";
or 
this.selectedTheme = "light-theme";

this way, you app will switch from on color theme to the other throughout your entire app. :slight_smile:
Also important, if you’re using a menu component, you’ll notice in your app.html that the ion-menu is outside the ion-nav tag to which you applied the dynamic theme. The solution is to simply apply the dynamic theme to your menu the same way you applied it to the ion-app:

<ion-menu [class]="selectedTheme" [content]="content">

Let me know if this works for you guys.


#7

See: https://robferguson.org/blog/2018/04/09/theming-your-ionic-3-app-bespoke-svg-icons/

For example, coloured SVG icons (in Tabs component):


#8

In all honesty, I kind of gave up trying to theme Ionic components with this multi-theme method. I simply build my own components with good old divs.