access SASS values ($colors from variables.scss) in Typescript (Angular2 ionic2)

AngularTypescriptSassIonic2

Angular Problem Overview


In Ionic 2, I would like to access the $colors variables from the file "[my project]\src\theme\variables.scss".

This file contains:

$colors: (
  primary:    #387ef5,
  secondary:  #32db64,
  danger:     #f53d3d,
  light:      #f4f4f4,
  dark:       #222,
  favorite:   #69BB7B
);

In a component, I draw a canvas. It looks like that:

import {Component, Input, ViewChild, ElementRef} from '@angular/core';

@Component({
    selector: 'my-graph',
})
@View({
    template: `<canvas #myGraph class='myGraph'
     [attr.width]='_size'
     [attr.height]='_size'></canvas>`,
})

export class MyGraphDiagram {
    private _size: number;

    // get the element with the #myGraph on it
    @ViewChild("myGraph") myGraph: ElementRef; 

    constructor(){
        this._size = 150;
    }

    ngAfterViewInit() { // wait for the view to init before using the element

      let context: CanvasRenderingContext2D = this.myGraph.nativeElement.getContext("2d");
      // HERE THE COLOR IS DEFINED AND I D LIKE TO ACCESS variable.scss TO DO THAT
      context.fillStyle = 'blue';
      context.fillRect(10, 10, 150, 150);
    }

}

As one can see, at some point in this code the color of the shape is defined: context.fillStyle = 'blue' , I would like to use instead something like context.fillStyle = '[variables.scss OBJECT].$colors.primary '.

Has anyone an idea?

Angular Solutions


Solution 1 - Angular

Unfortunately, there is no way to access SASS variable directly from typescript/javascript code. However, we can make a workaround to access those variables.

Let me describe briefly the steps to access SASS variables from within typescript source code:

1. Creating a SASS Helper Component

Create ../providers/sass-helper/sass-helper.component.scss:

$prefix: "--"; //Prefix string for custom CSS properties

//Merges a variable name with $prefix
@function custom-property-name($name) {
    @return $prefix + $name;
}

// Defines a custom property
@mixin define-custom-property($name, $value) {
    #{custom-property-name($name)}: $value;
}

body {
    // Append pre-defined colors in $colors:
    @each $name, $value in $colors {
        @include define-custom-property($name, $value);
    }

    // Append SASS variables which are desired to be accesible:
    @include define-custom-property('background-color', $background-color);
}

In this SCSS file, we simply create custom properties inside the body section of the DOM. You should add each SASS variable that you want to be accessible into this SCSS file by using the mixin called define-custom-property which expects two parameters: variable name and variable value.

As an example, I have added entries for all the colors defined in $colors as well as an entry for the SASS variable $background-color defined in my theme/variables.scss file. You can add as many variables as you wish.

Create ../providers/sass-helper/sass-helper.component.ts:

import { Component } from '@angular/core';

export const PREFIX = '--';

@Component({
    selector: 'sass-helper',
    template: '<div></div>'
})
export class SassHelperComponent {

    constructor() {

    }

    // Read the custom property of body section with given name:
    readProperty(name: string): string {
        let bodyStyles = window.getComputedStyle(document.body);
        return bodyStyles.getPropertyValue(PREFIX + name);
    }
}

2. Integrating SASS Helper Component

From now on, we can follow standard Ionic2 framework principles for component integration and usage.

  • Add the component class name (SassHelperComponent) into the declarations section of your NgModule in app.module.ts

  • Insert the following HTML code into the HTML template of your page from where you want to access those magic variables:

    <sass-helper></sass-helper>


3. Using Helper Component

In your page's TS file, you should insert the following lines into your page class:

@ViewChild(SassHelperComponent)
private sassHelper: SassHelperComponent;

Finally, you can read the value of any SASS variable by just calling the child class method as follows:

// Read $background-color:
this.sassHelper.readProperty('background-color');

// Read primary:
this.sassHelper.readProperty('primary');

Solution 2 - Angular

One possibility is to generate a .ts file from the .scss file. A simple example of this process:

  1. Install npm i --save-dev scss-to-json.

  2. Put this in your package.json:

    "scripts": { ... "scss2json": "echo "export const SCSS_VARS = " > src/app/scss-variables.generated.ts && scss-to-json src/variables.scss >> src/app/scss-variables.generated.ts" },

and run it with npm run scss2json. Windows users will need to adjust the example.

  1. Access the variables:

    import {SCSS_VARS} from './scss-variables.generated'; ... console.log(SCSS_VARS['$color-primary-1']);

One advantage of this is, that you'll get type completion from IDE's and it's a quite simple means to achieve your goal in general.

Of course you could make this more advanced, for example by making the generated file read only and by putting the script into it's own .js file and make it work on every OS.

Solution 3 - Angular

This is possible using CSS Modules.

CSS Modules

From the project description:

> When importing the CSS Module from a JS Module, it exports an object with all mappings from local names to global names.

In a way that we could read variables from css/scss file like this:

import styles from "./style.css";    

element.innerHTML = '<div class="' + styles.className + '">';

Support for CSS Modules is already setup by default by the Angular CLI which uses Webpack configured with the css-loader.

The steps to make it work are:

  1. Export only the scss variables that you want to use.
  2. Configure a typescript module for styles.scss.
  3. Import the variables in your typescript components.

1 - Export the variables

In your styles.scss, use the keyword :export to export $colors. It seems that :export doesn't support exporting maps, only strings, so we have to create a mixin to convert a map into strings:

$colors: (
  primary: #387ef5,
  secondary: #32db64,
  danger: #f53d3d,
  light: #f4f4f4,
  dark: #222,
  favorite: #69bb7b,
);

@mixin rule($key, $value, $prefix) {
  #{$prefix}-#{$key}: $value;
}
@mixin map-to-string($map, $prefix) {
  @each $key, $value in $map {
    @include rule($key, $value, $prefix);
  }
}

:export {  
  @include map-to-string($colors, "colors");
}

The generated :export will be:

:export {
  "colors-danger": "#f53d3d";
  "colors-dark": "#222";
  "colors-favorite": "#69bb7b";
  "colors-light": "#f4f4f4";
  "colors-primary": "#387ef5";
  "colors-secondary": "#32db64";
}

2 - Configure a typescript module for styles.scss

We have to create a styles.scss.d.ts file with the following content to allow the import of styles.scss in our typescript files:

export interface globalScss {}

export const styles: globalScss;

export default styles;

3 - Import the variables in the target typescript component

As we used a default export, we could import it in our component like this:

//...
import styles from 'src/styles.scss';

@Component({
  selector: 'app-colors-use',
  templateUrl: './colors-user.component.html',
  styleUrls: ['./colors-user.component.scss'],
})
export class ColorsUserComponent implements OnInit {

  buttonColor = styles["colors-primary"] //"#387ef5"

4 - (Plus) Add type definition to styles.scss.d.ts

You could add type information to style.scss.d.ts:

export interface globalScss {  
  "colors-danger": string
  "colors-dark": string
  "colors-favorite": string
  "colors-light": string
  /**
   * Used for app-button, usually blue
   */
  "colors-primary": string
  /**
   * Used for borders, usually green
   */
  "colors-secondary": string
}

export const styles: globalScss;

export default styles;

In that way, you could have some benefits in an editor like VS code:

Comments

Auto complete

UPDATE:

The configuration above only works until ng 10. Css Modules configuration has changed considerably from ng 10 to ng 11.

Solution 4 - Angular

I would like to add up something to @mete-cantimur answer.

import {Component, OnInit, ViewEncapsulation} from '@angular/core';

const PREFIX = '--';

@Component({
  selector: 'app-styles-helper',
  templateUrl: './styles-helper.component.html',
  styleUrls: ['./styles-helper.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class StylesHelperComponent implements OnInit {

  ngOnInit(): void {

  }

  readProperty(name: string): string {
    const bodyStyles = window.getComputedStyle(document.body);
    return bodyStyles.getPropertyValue(PREFIX + name);
  }
}

My helper component wasn't being able to modify body styles. Even I set up everything correctly, custom properties were not being saved.

I had to add encapsulation: ViewEncapsulation.None to the component in order to let it modify body styles.

Hope this helps.

Solution 5 - Angular

I know this question is now a few years old, but I thought I'd share the solution I use. It is a more simplistic version of @mete-cantimur's answer, there is no requirement to set up any extra CSS style sheets. It will read from the loaded styles on the page instead.

import {Directive, ElementRef} from '@angular/core';

@Directive({
	selector: '[css-helper]',
})
export class CssHelperDirective {

	element: any;

	constructor(_ref: ElementRef) {
		this.element = _ref.nativeElement;
	}

	readProperty(name: string): string {
		return window.getComputedStyle(this.element).getPropertyValue(name);
	}
}

Usage:

<div #primary css-helper class="primary"></div>
@ViewChild('primary', {read: CssHelperDirective})
private cssHelper: CssHelperDirective;
let color = this.cssHelper.readProperty('background-color');

Solution 6 - Angular

In Windows, I used the following taken from Bersling's answer.

npm i --save-dev ruoqianfengshao/scss-to-json

npm i --save-dev node-sass

"scripts": {
...
    "scss2json": "echo export const SCSS_VARS =  > .\\src\\app\\scss-variables.generated.ts && scss-to-json .\\src\\app\\_variables.scss >> .\\src\\app\\scss-variables.generated.ts"
}

npm run scss2json

import {SCSS_VARS} from './scss-variables.generated';
...
console.log(SCSS_VARS['$color-primary-1']);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnylujeView Question on Stackoverflow
Solution 1 - AngularMete CantimurView Answer on Stackoverflow
Solution 2 - AngularberslingView Answer on Stackoverflow
Solution 3 - AngulartiagolisalvesView Answer on Stackoverflow
Solution 4 - AngularyuvaView Answer on Stackoverflow
Solution 5 - AngularMarc EvansView Answer on Stackoverflow
Solution 6 - AngularPost ImpaticaView Answer on Stackoverflow