Generating Dynamic CSS

How to dynamically create CSS class in JavaScript and apply?

Here is an option:

var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.cssClass { color: #F00; }';
document.getElementsByTagName('head')[0].appendChild(style);

document.getElementById('someElementId').className = 'cssClass';

Dynamically generated css

Another benefit would be that each additional asset that is loaded in the page slows down the overall page load because a browser is typically only loading 4 assets simultaneously at a given time.

However, doing all of that combining on page load is likely to cause the page to slow down just as much from the processing alone... depending on how many things it needs to iterate through to make up the final css block.

In terms of performance, it seems very unlikely to me that javascript is going to elegantly / efficiently replace the styles that would've been included in a combined css file.

The other thing is that in production environments, you also can do another option which is to use a library to pre-generate a set of minified, optimized, (and obfuscated in the case of JS) code and then have 1 js file and 1 css file loaded at the time of page load, which in-turn you'd cache for speed/loading benefits. Boilerplate lib has a script for this i think.

End of the day: energy is neither created nor destroyed -- your optimizations in one place and hurt you elsewhere if you're not careful.

Generating dynamic CSS

However, serving CSS as a view seems like it would cause a significant amount of overhead in a file that is constantly requested, so this is probably not a good solution.

And what if you would generate that CSS once?

  1. Default CSS is: /common/css.css
  2. Member customize CSS, now <link /> elements points to /user-specific/123.css?ts=123123123. 123 is of course an identifier of the member, and ts parameter contains a timestamp - a date of last CSS modification
  3. Make sure that your CSS generator sets proper HTTP headers responsible for client-side caching
  4. User browser request a CSS file - server replies with simple 304 Not Modified header - there is no need for any script execution or contents download
  5. When member modifies his CSS then you just update ts - once again just a single request is needed

Generate css dynamically via templates and placeholders

Wound up playing with this and CSS variables. I'm adding a second answer because it's very different method from my first answer and it better aligns with your original question (updating CSS variables with JS).

BUT... don't do this. :) Browser support in IE < Edge doesn't exist and it is almost certainly slower than updating an on-page <style> element though I haven't tested it. This jsperf tests various style update methods. It doesn't include innerHTML on a single style element (likely the fastest) but you can see that the following CSS DOM methods are slower than the rest.

// get the stylesheet

// array position depends on how many style sheets you're loading.

// adjust as needed.

var sheet = document.styleSheets[0];

// simplest method: insertRule()

// setTimeout only for demo so you can see the change

window.setTimeout(function(){

// @media all {} is a trick to insert more than one

// selector and/or properties at once. Otherwise it's:

// sheet.insertRule(":root", "--header-color: green"); ...repeat...

sheet.insertRule("@media all { :root { --header-color: green; --main-color: orange; } }", 1);

}, 1200);

// SAFER method via addCSSRule.

// button and getAjaxStyles are just placeholders, obviously

var btn = document.querySelector('button');

btn.addEventListener("click", getAjaxStyles);

function getAjaxStyles() {

// success callback... break apart the json and update the CSS variables

addCSSRule(sheet, ":root", "--header-color: orange");

addCSSRule(sheet, ":root", "--main-color: blue");

addCSSRule(sheet, ":root", "--alt-color: red");

addCSSRule(sheet, ":root", "--borderColorA: lavender");



// or go with a single big string. definitely faster:

// addCSSRule(sheet, ":root", "--alt-color: red; --borderColorA: #0ff; ")

}

// Credit for addCSSRule() goes to Diego Flórez in a comment on

// https://davidwalsh.name/add-rules-stylesheets

var addCSSRule = function(sheet, selector, rules) {

//Backward searching of the selector matching cssRules

var index = sheet.cssRules.length - 1;

for (var i = index; i > 0; i--) {

var current_style = sheet.cssRules[i];

if (current_style.selectorText === selector) {

//Append the new rules to the current content of the cssRule;

rules = current_style.style.cssText + rules;

sheet.deleteRule(i);

index = i;

}

}

if (sheet.insertRule) {

sheet.insertRule(selector + "{" + rules + "}", index);

} else {

sheet.addRule(selector, rules, index);

}

return sheet.cssRules[index].cssText;

}
/* Set initial CSS variables */

:root {

--header-color: #333;

--main-color: #888;

--alt-color: #bbb;

--borderColorA: #ccc;

}

h1 {

color: var(--header-color);

}

p {

border-bottom: 1px solid var(--borderColorA);

color: var(--main-color);

}

p+p {

color: var(--alt-color);

}
<h1>header</h1>

<p>paragraph 1</p>

<p>paragraph 2</p>

<button>Update CSS Variables</button>

Generate dynamic css based on variables angular

Direct approach available in angular is using ngstyle as follows

<div [ngStyle]="{'color': style.colorVal ? style.colorVal : '#000', 'font-size' : style.fontSize ? style.fontSize : '16px' }"></div>

After going through different methods and approached to add dynamic css to all pages on angular app I ended up with following solutions.

Requirement : generate dynamic css based on values returned from and API to change design and styling.

Solution :

  1. create a new component and create a service to load dynamic css variables from API.
  2. Add style tag in template file and use variable values for properties.
  3. Load this template on all pages or on main template.
  4. On app build style will be moved to head tag.

Code sample

import { CssService} from './Css.service';

@Component({
selector: 'DynamicCss',
templateUrl: './DynamicCss.component.html',
styleUrls: ['./DynamicCss.component.scss']
})
export class ServiceProviderComponent implements OnInit {
cssVariables: any;
constructor(private cssService:CssService){
/* call the service/api to get the css variable values in cssVariables */

}
}

Now apply css using jquery or javascript to append css with help of function like following

appendCss(customData)
{
let text = '.custom-form-1 {
background-image: url("`+customData.background_image+`");
}';
$(document).ready(function(){
$("style").append(text);
});
}

and call this function after loading custom data from service or other variable like I did it ngOnInit

ngOnInit(){
this.appendCss(this.customizeFormData);
}

Its using jquery but can be done with javascript/typescript as well if you dont want to use jquery in your angular app

Other useful resource https://github.com/angular/angular/issues/9343#issuecomment-312035896

How to dynamically generate CSS class and/or set its property

What you are trying to do is the subject of this open issue about stylesheet binding in Angular. Until that feature is available, you can get what you want with a custom directive. Here is a directive that retrieves the checkbox element generated by ng-zorro-antd and applies two color attributes to it. The two colors are @Input properties and the directive implements OnChanges which allows to react to property binding changes.

@Directive({
selector: "[nz-checkbox][nz-chk-style]"
})
export class CheckBoxStyleDirective implements OnInit, OnChanges {

@Input("nz-chk-bkgnd") chkBkgndColor: string;
@Input("nz-chk-border") chkBorderColor: string;

private checkbox: HTMLElement;

constructor(private renderer: Renderer2, private el: ElementRef) { }

ngOnInit() {
this.checkbox = this.el.nativeElement.querySelector(".ant-checkbox-inner");
this.updateBackgroundColor();
this.updateBorderColor();
}

ngOnChanges(changes: SimpleChanges) {
if (changes.chkBkgndColor) {
this.updateBackgroundColor();
}
if (changes.chkBorderColor) {
this.updateBorderColor();
}
}

updateBackgroundColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "background-color", this.chkBkgndColor);
}
}

updateBorderColor() {
if (this.checkbox) {
this.renderer.setStyle(this.checkbox, "border-color", this.chkBorderColor);
}
}
}

Once the directive attribute selector nz-chk-style is applied to the 3rd party element, you can set the checkbox background and border colors with property binding as follows:

<span nz-checkbox nz-chk-style [nz-chk-bkgnd]="bkgndColor" [nz-chk-border]="borderColor" >

See this interactive stackblitz for a demo.

Generate CSS classes dynamically based on variables - SCSS

use @each loop. Instead of creating the vars in :root add those in a single var (see below example)

$colors : (
"primary-50": "1,1,1",
"primary-100": "2,2,2",
"primary-200": "3,3,3",
);

@each $color, $value in $colors {
.bg-#{$color} {
background-color: rgb($value);
}
}

the above code compiled into

.bg-primary-50 {
background-color: #010101;
}
.bg-primary-100 {
background-color: #020202;
}
.bg-primary-200 {
background-color: #030303;
}

And for CSS --variables

:root {
@each $color, $value in $colors {
--color-#{$color}: rgb($value);
}
}

and you have CSS Variables

:root {
--color-primary-50: #010101;
--color-primary-100: #020202;
--color-primary-200: #030303;
}

Like you mentioned in your comment "will this solution work for the light and dark modes?" for that you can do something like this

html[data-color-mode="dark"] {
$dark-mode-colors: (
"primary-color-50": "0, 0, 0",
"primary-color-100": "1, 1, 1",
"primary-color-200": "2, 2, 2",
)

@each $color, $value in $colors {
.bg-#{$color} {
background-color: $value;
}
}
}

// change your color scheme as you prefer method will remain the same
html[data-color-mode="light"] {
$light-mode-colors: (
"primary-color-50": "0, 0, 0",
"primary-color-100": "1, 1, 1",
"primary-color-200": "2, 2, 2",
)

@each $color, $value in $colors {
.bg-#{$color} {
background-color: $value;
}
}
}



Related Topics



Leave a reply



Submit