73

We are making an Angular2 application and we want to be able to somehow create a global CSS variable (and update the properties' values whenever changed when the variable is assigned).

We had used Polymer for a while (now we are switching to Angular2 components) and we had used CSS properties (Polymer has some polyfill) and we had just update styles using Polymer.updateStyles().

Is there any way how we can achieve a similar function?

EDIT:

We want something similar to Sass color: $g-main-color or to CSS custom properties color: var(--g-main-color) and whenever we decide to change the value of the property, e.g. something like updateVariable('g-main-color', '#112a4f') it dynamicly update the value everywhere. All that while an app is running.

EDIT 2:

I want to use some global CSS variables in different parts (host, child element...) of my CSS and be able to change the value immediately - so if I change my-color variable, it changes everywhere in app.

I will use Sass syntax for example:

:host { border: 2px solid $my-color }
:host .some-label { color: $my-color }

Is possible to use something like Angular pipes? (But it supposedly only works in HTML)

:host { border: 2px solid {{ 'my-color' | cssvariable }} }
:host .some-label { color: {{ 'my-color' | cssvariable }} }
tenhobi
  • 1,977
  • 3
  • 23
  • 49
  • 3
    I'm interested to know what approach you took. We are having similar requirements. – Yousuf Nov 01 '16 at 23:41
  • I still have no solution for this. Only resonable today approach is probably use of CSS variables with some kind of polyfill... – tenhobi Nov 02 '16 at 07:35
  • Do you know of any polyfill that can be used in angular2 to replace variables? – Yousuf Nov 02 '16 at 14:12
  • Is there a reason binding to objects using ngStyle wouldn't work? Those objects can come from anywhere and you can alter them at will, during runtime. This way you don't have to know the property or the value in advance, you just structure an object, assign it to the value the element is bound to, and the change occurs. This mean that you can leave any kind of pre-made, static or compiled CSS out of the equation. If that's no good, you can always bind all your elements to innerHTML, and alter the innerHTML by applying a styled span, which you create dynamically. – Tim Consolazio Feb 21 '17 at 10:32
  • You could use a pipe: https://stackoverflow.com/a/66244720/1974681 – adrisons Feb 17 '21 at 15:38

5 Answers5

71

1) Using inline styles

<div [style.color]="myDynamicColor">

2) Use multiple CSS classes mapping to what you want and switch classes like:

 /* CSS */
 .theme { /* any shared styles */ }
 .theme.blue { color: blue; }
 .theme.red { color: red; }

 /* Template */
 <div class="theme" [ngClass]="{blue: isBlue, red: isRed}">
 <div class="theme" [class.blue]="isBlue">

Code samples from: https://angular.io/cheatsheet

More info on ngClass directive : https://angular.io/docs/ts/latest/api/common/index/NgClass-directive.html

Francisco
  • 2,018
  • 2
  • 16
  • 16
Gerard Sans
  • 2,269
  • 1
  • 13
  • 13
  • 2
    This may works, but there is one issue - you have to separate dynamic styles from CSS file, which is never the best way. :( I rather would like to have something inside CSS, but I will have to probably deal with it. – tenhobi Nov 07 '15 at 21:43
  • I don't understand. Use SASS/LESS. For dynamic styles from JavaScript I can only see those. Anyway you can't mix static and dynamic styles unless you are doing it wrong. Static: css. Dynamic: JavaScript. – Gerard Sans Nov 07 '15 at 21:45
  • I did say that it would be nice to have something like Angular2 pipes - the ability to write it inside the CSS file or area. But it probably is not possible, so I will have to make it like you had wrote in 1) and maybe just add comment into Sass file, that there is used (or should be) global variable from outside. – tenhobi Nov 07 '15 at 21:55
  • If you are feeling adventurous you could build a step to take SASS and output a JS module that you can import ^_^ – Gerard Sans Nov 07 '15 at 21:58
  • not useful for variables that have numbers in them , you don't wanna create 10000 classes – Milad Jan 29 '19 at 06:20
25

Just use standard CSS variables:

Your global css (eg: styles.css)

body {
  --my-var: #000
}

In your component's css or whatever it is:

span {
  color: var(--my-var)
}

Then you can change the value of the variable directly with TS/JS by setting inline style to html element:

document.querySelector("body").style.cssText = "--my-var: #000";

Otherwise you can use jQuery for it:

$("body").css("--my-var", "#fff");
raythurnevoid
  • 2,652
  • 1
  • 25
  • 24
  • 4
    This is exactly what I wanted, thanks for sharing this – Noopur Dabhi Dec 15 '18 at 20:17
  • Isn't using direct DOM manipulation and jQuery a last resort when using Angular 2.0+? – Giant Elk Aug 11 '19 at 17:23
  • Check this out, Renderer2 from Angular docs: https://angular.io/api/core/Renderer2 – Giant Elk Aug 11 '19 at 22:12
  • @GiantElk Right but i'm just changing a CSS variable value. Since there's no support for [binding CSS variables](https://github.com/angular/angular/issues/9343) in ngStyle I think that this is actually the simplest solution. Personally I would never use a difficult API like Renderer just to change a value of a variable. – raythurnevoid Aug 14 '19 at 01:57
  • how can i multiple variables.? i am not using jquery ($) – Sahi Jul 04 '22 at 07:57
5

You don't have any example code but I assume you want to do something like this?

@View({
directives: [NgClass],
styles: [`
    .${TodoModel.COMPLETED}  {
        text-decoration: line-through;
    }
    .${TodoModel.STARTED} {
        color: green;
    }
`],
template: `<div>
                <span [ng-class]="todo.status" >{{todo.title}}</span>
                <button (click)="todo.toggle()" >Toggle status</button>
            </div>`
})

You assign ng-class to a variable which is dynamic (a property of a model called TodoModel as you can guess).

todo.toggle() is changing the value of todo.status and there for the class of the input is changing.

This is an example for class name but actually you could do the same think for css properties.

I hope this is what you meant.

This example is taken for the great egghead tutorial here.

Shikloshi
  • 3,761
  • 7
  • 32
  • 58
  • 5
    That is not what we want. We want something like in Sass `color: $g-main-color` or in CSS custom properties `color: var(--g-main-color)` and whenever we decide to change value of the property, e.g. something like JS `updateVariable('g-main-color', '#112a4f')` it dynamicly update the value everywhere. All that while an app is running. – tenhobi Oct 25 '15 at 11:43
2

I did this plunker to explore one way to do what you want.

Here I get mystyle from the parent component but you can get it from a service.

import {Component, View} from 'angular2/angular2'

@Component({
  selector: '[my-person]',
  inputs: [
    'name',
    'mystyle: customstyle'
  ],
  host: {
    '[style.backgroundColor]': 'mystyle.backgroundColor'
  }
})
@View({
  template: `My Person Component: {{ name }}`
})
export class Person {}
bertrandg
  • 3,147
  • 2
  • 27
  • 35
  • I have to be able change styles of either host and child elements which I can not with your solution plus it is kind of messy write styles from outside of "CSS area". Take look to EDIT 2. Btw. instead of taking color from input via an attribute it would be better just import some class and use a variable from it, because I want global variables for whole app. ;) – tenhobi Oct 27 '15 at 23:08
-1

Angular 6 + Alyle UI

With Alyle UI you can change the styles dynamically

Here a demo stackblitz

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    CommonModule,
    FormsModule,
    HttpClientModule,
    BrowserAnimationsModule,
    AlyleUIModule.forRoot(
      {
        name: 'myTheme',
        primary: {
          default: '#00bcd4'
        },
        accent: {
          default: '#ff4081'
        },
        scheme: 'myCustomScheme', // myCustomScheme from colorSchemes
        lightGreen: '#8bc34a',
        colorSchemes: {
          light: {
            myColor: 'teal',
          },
          dark: {
            myColor: '#FF923D'
          },
          myCustomScheme: {
            background: {
              primary: '#dde4e6',
            },
            text: {
              default: '#fff'
            },
            myColor: '#C362FF'
          }
        }
      }
    ),
    LyCommonModule, // for bg, color, raised and others
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Html

<div [className]="classes.card">dynamic style</div>
<p color="myColor">myColor</p>
<p bg="myColor">myColor</p>

For change Style

import { Component } from '@angular/core';
import { LyTheme } from '@alyle/ui';

@Component({ ... })
export class AppComponent  {
  classes = {
    card: this.theme.setStyle(
      'card', // key
      () => (
        // style
        `background-color: ${this.theme.palette.myColor};` +
        `position: relative;` +
        `margin: 1em;` +
        `text-align: center;`
         ...
      )
    )
  }
  constructor(
    public theme: LyTheme
  ) { }

  changeScheme() {
    const scheme = this.theme.palette.scheme === 'light' ?
    'dark' : this.theme.palette.scheme === 'dark' ?
    'myCustomScheme' : 'light';
    this.theme.setScheme(scheme);
  }
}

Github Repository

Enm
  • 11
  • 2