Change css variables dynamically in angular - css

In my angular project, I have some css variables defined in top level styles.scss file like this. I use these variable at many places to keep the whole theme consistent.
:root {
--theme-color-1: #f7f7f7;
--theme-color-2: #ec4d3b;
--theme-color-3: #ffc107;
--theme-color-4: #686250;
--font-weight: 300
}
How can I update values of these variables dynamically from app.component.ts ? And What is the clean way to do this in angular ?

You can update them using
document.documentElement.style.setProperty('--theme-color-1', '#fff');
If u want to update many values, then create a object
this.styles = [
{ name: 'primary-dark-5', value: "#111" },
{ name: 'primary-dark-7_5', value: "#fff" },
];
this.styles.forEach(data => {
document.documentElement.style.setProperty(`--${data.name}`, data.value);
});
The main thing here is document.documentElement.style.setProperty. This line allows you to access the root element (HTML tag) and assigns/overrides the style values.
Note that the names of the variables should match at both places(css and js files)
if you don't want to use document API, then you can use inline styles on HTML tag directly
const styleObject = {};
this.styles.forEach(data => {
styleObject[`--${data.name}`] = data.value;
});
Then In your template file using ngStyle (https://angular.io/api/common/NgStyle)
Set a collection of style values using an expression that returns
key-value pairs.
<some-element [ngStyle]="objExp">...</some-element>
<html [ngStyle]="styleObject" >...</html> //not sure about quotes syntax
Above methods do the same thing, "Update root element values" but in a different way.
When you used :root, the styles automatically got attached to HTML tag

Starting with Angular v9 you can use the style binding to change a value of a custom property
<app-component-name [style.--theme-color-1="'#CCC'"></app-component-name>

Some examples add variables directly to html tag and it seem in the element source as a long list. I hope this helps to you,
class AppComponent {
private variables=['--my-var: 123;', '--my-second-var: 345;'];
private addAsLink(): void {
const cssVariables = `:root{ ${this.variables.join('')}};
const blob = new Blob([cssVariables]);
const url = URL.createObjectURL(blob);
const cssElement = document.createElement('link');
cssElement.setAttribute('rel', 'stylesheet');
cssElement.setAttribute('type', 'text/css');
cssElement.setAttribute('href', url);
document.head.appendChild(cssElement);
}
}

Related

Vue 3 with Tailwind using v-bind:class shows classes in html element but not render while style works correctly

I am new to Vue and I'm trying to bind multiple classes in a v-for loop from a const array of object imported from a file.js.
But the trick I'm trying is to import const and than return classes from method that evaluates one property of object looped.
I've tried all ways, methods, computed, setup, onMounted, beforeMount, but even if i can see my classes in html they aren't rendered in styles section of DevTools.
The only way that works is to v-bind:style instead of class. Or just put exact classes in my const array object as a property but I prefer to avoid this.
It seems to save something in cache, but i have tried to delete and to lunch application in hidden mode but it won't works
Is there someone who can help me to understand and maybe to resolve?
Thanks in advance
this is my actual code:
<template>
<div id="cv" class="tp3-flex md:tp3-grid md:tp3-grid-cols-[repeat(27,_minmax(0,_1fr))] md:tp3-grid-rows-[repeat(6,_minmax(0, 5rem))] tp3-justify-center tp3-content-center tp3-justify-items-center tp3-mx-auto tp3-p-2 tp3-bg-cyan-500 tp3-text-blue-50">
<div v-for="(softSkill, index) in softSkills" :key="`softSkill-${index}`"
class="tp3-flex tp3-w-20 tp3-h-20 -tp3-rotate-45 tp3-rounded-full tp3-rounded-tr-none tp3-justify-center tp3-items-center tp3-bg-slate-400 tp3-opacity-70 tp3-mb-4 tp3-mt-4 tp3-shadow-md tp3-overflow-hidden"
v-bind:class="posCols(softSkill)">
<div class="tp3-rotate-45">
<span v-html="softSkill.text"></span>
</div>
</div>
</div>
</template>
<script>
import {softSkills} from "#/assets/skills/softSkills";
export default {
name: "ComponentSoftSkills",
data(){
return{
softSkills: null
}
},
beforeMount() {
this.softSkills = softSkills;
},
methods: {
posCols(softSkill){
console.log(softSkill);
return ' tp3-col-start-['+softSkill.col+'] tp3-col-end-['+(softSkill.col+1)+']';
}
}
}
</script>
<style lang="css" scoped>
</style>
and my file.js is:
export const softSkills = [
{text:`skill 1`, col:1, row:1},
{text:`skill 2`, col:5, row:1},
{text:`skill 3`, col:2, row:2},
{text:`skill 4`, col:15, row:1},
]
I have a suspicion that this might be due to your tailwind setup.
Because the classes are assigned dynamically and tailwind (depending on the configuration) is only making classes available that it can find during compilation. So the classes, even though you see them populated correctly, are not made available through tailwind. simply put, when tailwind scans the code, it doesn't recognize md:tp3-grid-cols-[repeat(27,_minmax(0,_1fr))] or tp3-col-start-[${softSkill.col}] as a valid class name and does not generate the class for it.
Assuming this is the issue and not knowing the exact version on configuration can't give an exact solution, but here are some tips for it.
Instead of using dynamic class names, define all the class names and assign dynamically
so instead of using tp3-col-start-[${softSkill.col}] tp3-col-end-[${(softSkill.col+1)}]
you could make sure all possible classes are clear and accessible by the tailwind parser:
let colClass = `tp3-col-start-[0] tp3-col-end-[1]`;
if(softSkill.col === 1) colClass = "tp3-col-start-[1] tp3-col-end-[2]";
if(softSkill.col === 2) colClass = "tp3-col-start-[2] tp3-col-end-[3]";
if(softSkill.col === 3) colClass = "tp3-col-start-[3] tp3-col-end-[4]";
if(softSkill.col === 4) colClass = "tp3-col-start-[4] tp3-col-end-[5]";
if(softSkill.col === 5) colClass = "tp3-col-start-[5] tp3-col-end-[6]";
// ...etc
this is obviously very verbose, but the classes are clearly defined in the code, so tailwind can find them when scanning your code.
Safelisting classes
using safelisting of classes is another option. Instead of having the code in your js, you would have it in the configuration
// tailwind.config.js
module.exports = {
// ...other stuff
safelist: [
'tp3-col-start-[0]',
'tp3-col-start-[1]',
'tp3-col-start-[2]',
'tp3-col-start-[3]',
'tp3-col-start-[4]',
'tp3-col-start-[5]',
// ...etc
'tp3-col-end-[1]',
'tp3-col-end-[2]',
'tp3-col-end-[3]',
'tp3-col-end-[4]',
'tp3-col-end-[5]',
// ...etc
],
}
there's also a way to use regex, which might look something like this:
// tailwind.config.js
module.exports = {
// ...other stuff
safelist: [
{
pattern: /tp3-col-start-[(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15)]/,
variants: ['sm', 'lg'], // you can add variants too
},
{
pattern: /tp3-col-end-[(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16)]/,
},
],
}
you can read more about safelisting here safelisting-classes

How to add stylesheet to toolbar

Using the Firefox Addon SDK, I am creating a toolbar with several buttons and I want to create a mouseover effect for the buttons.
At first I thought to use a mouseover event, but then I would have to create a mouseout event to return it to normal, so I figured the best way would be to use css
In my old XUL version of my addon I was able to attach the stylesheet by linking to it in the XUL code and just add css for my #buttonID, which worked perfectly.
But how do I add the css stylesheet for my toolbar using the Addon SDK?
Here's what I've tried so far (which does not produce any errors), but I think this is just for content; if this is correct, then I'm not sure how to bind to the element:
const { browserWindows } = require("sdk/windows");
const { loadSheet } = require("sdk/stylesheet/utils");
//This is how to load an external stylesheet
for(let w of browserWindows){
loadSheet(viewFor(w), "./myStyleSheet.css","author" );
}
I've also tried this:
var Style = require("sdk/stylesheet/style").Style;
let myStyle = Style({source:'./myStyleSheet.css'});
for(let w of browserWindows){
attachTo(myStyle, viewFor(w))
};
And this:
var { attach, detach } = require('sdk/content/mod');
const { browserWindows } = require("sdk/windows");
var { Style } = require('sdk/stylesheet/style');
var stylesheet = Style({
uri: self.data.url('myStyleSheet.css')
});
for(let w of browserWindows){
attach(stylesheet, viewFor(w))
};
And here is my css:
#myButton:hover{list-style-image(url("./icon-16b.png")!important; }
Tested this in Browser Toolbox:
const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js"); // skip this in SDK
const { browserWindows: windows } = require("sdk/windows");
const { viewFor } = require("sdk/view/core");
const { attachTo } = require("sdk/content/mod");
const { Style } = require("sdk/stylesheet/style");
let style = Style({ source: "#my-button{ display: none!important; }" });
// let self = require("sdk/self");
// let style = Style({ uri: self.data.url("style.css") });
for (let w of windows)
attachTo(style, viewFor(w));
The commented part allows to load from a stylesheet file in the addon data directory.
Notice that you need to import SDK loader to use it in the toolbox.
When in an SDK addon, just use require directly.
NB: there is a difference in spelling: self.data.url vs { uri }
See self/data documentation.
NB2: SDK uses a custom widget ID scheme for toggle and action buttons so your button ID might not be what you expect:
const toWidgetId = id =>
('toggle-button--' + addonID.toLowerCase()+ '-' + id).replace(/[^a-z0-9_-]/g, '');
OR
const toWidgetId = id =>
('action-button--' + addonID.toLowerCase()+ '-' + id).replace(/[^a-z0-9_-]/g, '');
using this code, you should be able to use the mouse over or hover to change how it looks.
#buttonID {
//Normal state css here
}
#buttonID:hover {
//insert css stuff here
}
This goes in the javascript file:
const { browserWindows } = require("sdk/windows");
const { viewFor } = require("sdk/view/core");
const { loadSheet } = require("sdk/stylesheet/utils");
const { ActionButton } = require("sdk/ui/button/action");
var StyleUtils = require('sdk/stylesheet/utils');
var myButton = ActionButton({
id: "mybutton",
label: "My Button",
icon: { "16": "./icon-16.png", "32":"./icon-32.png", "64": "./icon-64.png" },
onClick: function(state) {
console.log("mybutton '" + state.label + "' was clicked");
}
});
//this is how you attach the stylesheet to the browser window
function styleWindow(aWindow) {
let domWin = viewFor(aWindow);
StyleUtils.loadSheet(domWin, "chrome://myaddonname/content/myCSSfile.css", "agent");
}
windows.on("open", function(aWindow) {
styleWindow(aWindow);
});
styleWindow(windows.activeWindow);
And here is the css for that
//don't forget to add the .toolbarbutton-icon class at the end
#action-button--mystrippedadonid-mybuttonid .toolbarbutton-icon,{
background-color: green;
}
There are several gotchas here.
First, as of this posting, you should not use capital letters in the id for the button because they get completely removed - only lowercase letters and hyphens are allowed.
The id of the element is not the same as the id you gave it in the button declaration. See below for how to come up with this identifier.
To specify content in the url for the stylesheet file (in the loadSheet function call) you will also need to create a chrome.manifest in the root of your addon folder, and put this in it: content spadmintoolbar data/ where "data" is the name of a real directory in the root folder. I needed a data/ folder so I could load icons for the button declarations, but you need to declare your virtual directories in chrome.manifest which jpm init does not do for you.
How to get the element id for your css file:
The easy way to get the id for your button element for use in an external style sheet is by testing your addon and then using the browser-toolbox's inspector to locate the element, whence you can fetch the id from the outputted code.
However, if you want to figure it yourself, try this formula.
[button-class] = the sdk class for the button. An Action Button becomes action-button
[mybuttonid] = the id you gave the button in the sdk button declaration
[myaddonname] = the name you gave the addon in it's package.json file.
[strippedaddonid] = take the id you assigned the addon in the package.json file, and remove any # symbol or dots and change it to all lowercase.
Now put it all together (don't include the square brackets):
`#[button-class]--[strippedaddonid]-[mybuttonid]]`
An example: action-button--myaddonsomewherecom-mybutton
Really simple isn't it?!
credit for the stylesheet attach code goes to mconley

With an express app, how to use my schema data in css/stylus?

I'm trying to create a themes for my express app and i'm trying to do something that i'm not sure is possible. I need some expert advice on ways or methods to do this.
This is my schema...
var colorSchema = new Schema({
primaryColor: {
type: String
},
secondaryColor: {
type: String
}
});
module.exports = mongoose.model('Color', colorSchema);
Let's say i've entered hex colors into primaryColor and secondaryColor when I create a new schema. How can I parse color.primaryColor as a value in my css or stylus like you do in jade?
Be sure to first pass the data to Jade through render in Express:
// Default both to white
res.render('template_name', {
primaryColor: color.primaryColor || '#FFF',
secondaryColor: color.secondaryColor || '#FFF',
someOtherVar: 'foo'
});
Then, in your Jade template:
style(type='text/css')
| .primary-color-bg { background-color: #{primaryColor}; }
| .secondary-color-bg { background-color: #{secondaryColor}; }
You can then apply primary-color-bg and secondary-color-bg classes to any DOM elements you want to set the background of. You can replicate for color, border, etc. attributes.
Stylus actually has a JavaScript API that allows you to render even arbitrary strings of Stylus code.
stylus = require('stylus');
variable = '#fff';
stylus.render(
"\n.thing" +
"\n color: " + variable + "\n",
console.log);
This is a pretty dumb example, but it gets the point of freedom you have. That API also allows for more "composable" compile-function definition, that lets you expose JS values (via .define) into Stylus.
stylus(str)
.set('filename', 'nesting.css')
.define('whatever', '#fff')
.render(console.log);

Dynamic scss variable meteor

I have a scss variable $tint-color that is used in about 100 places.
Once the user logs in, I would like to load a color based on their profile and replace all the usages of $tint-color.
So far I have found two non-ideal solutions:
1) Iterate through all elements and replace the relevant properties.
I am constantly generating new elements -- so this would need to happen repeatedly.
2) Create an override stylesheet, that targets each element.
This will require a lot of duplicate code.
Is there a better / simpler way? I have thought about adding a class to an element in scss, but I am not sure this is possible. Thank you for your help in advance!
What I am doing now, is loading a theme css file after the profile is loaded.
On the server I expose an iron-router route that dynamically replaces any occurrence of the color and returns the theme css.
The issue is that I am not replacing the scss variables, instead I am replacing any occurrence of the color. This is because when the code is executed the .scss files have already been bundled into a .css file on the server.
// return a theme based on the tintColor parameter
this.route('theme', {
where: 'server',
action: function () {
var files = fs.readdirSync('../client');
// find the css file (not the .map file)
var cssFile = _(files).find(function (fileName) {
return fileName.indexOf('.css') > 0 && fileName.indexOf('.map') < 0;
});
var style = fs.readFileSync('../client/' + cssFile, 'utf8');
// remove comments (cannot have them for minification)
style = style.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '');
// replace the default tint-color with the dynamic color
style = style.replace(/8cb850/g, this.params.tintColor);
// minify css
if (Settings.isProduction()) {
// from the minifiers package
style = CssTools.minifyCss(style);
}
this.response.writeHead(200, {'Content-Type': 'text/css'});
this.response.end(style);
}
});
Update: I got it to generate with scss variables.
Theme.compile = function (tintColor) {
var dirName = path.dirname(styleFile);
var styles = fs.readFileSync(styleFile, 'utf8');
//replace default theme with dynamic theme
var theme = '$tint-color: #' + tintColor + ';' + '\n';
styles = styles.replace('#import "app/theme.scssimport";', theme);
var options = {
data: styles,
sourceComments: 'map',
includePaths: [dirName] // for #import
};
var css = sass.renderSync(options);
// minify css
if (Settings.isProduction()) {
// remove comments -- cannot have them for minification
css = css.replace(/(?:\/\*(?:[\s\S]*?)\*\/)|(?:([\s;])+\/\/(?:.*)$)/gm, '');
// Use CssTools from the minifiers package
css = CssTools.minifyCss(css);
}
return css;
};
If you do this make sure you add the scss files as assets in the package, example here.
Set a basic $tint-color in your original css.
Then use meteor to send inline CSS with the selected user-tint.
Example:
.tint {
background-color: USER-TINT;
color: USER-TINT;
}
That way you can cache the original css file and save loads of transfer!

Export Html Table to excel and keep css styles

I'm using excel web queries to export an html table (mvc view) to excel. How do I get it to carry across the css styles? If I set class="redLabel" it doesn't interpret that and make the label red. I have to use inline styles in my table for this to work. Any ideas?
As far as I know, most Office programs do NOT support included styling, but only inline styling.
It's likely that you'll be required to include your styling inline (exporting sucks, almost like mail styling).
Excel does support using css styling, but only if there is one class on the element. If there are multiple classes then it will not do any style on the element, see CSS style class not combining in Excel
Having said that, this is the code I put together to grab all the styles on a page and export an HTML table. It's a brute force, grab everything approach, but you could probably pair it down if you know the specifics. The function returns a jQuery Promise. From that you can do whatever with the result.
function excelExportHtml(table, includeCss) {
if (includeCss) {
var styles = [];
//grab all styles defined on the page
$("style").each(function (index, domEle) {
styles.push($(domEle).html());
});
//grab all styles referenced by stylesheet links on the page
var ajaxCalls = [];
$("[rel=stylesheet]").each(function () {
ajaxCalls.push($.get(this.href, '', function (data) {
styles.push(data);
}));
});
return $.when.apply(null, ajaxCalls)
.then(function () {
return "<html><style type='text/css'>" + styles.join("\n") + "</style>\n" + table.outerHTML + "</html>";
});
}
else {
return $.when({ owcHtml: table.outerHTML })
.then(function (result) {
return "<html>" + result.owcHtml + "</html>";
});
}
}
You can export table with outer css style. Here is my solution declare a document template:
var e = this;
var style = "<style></style"; //You can write css or get content of .css file
e.template = {
head: "<html xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:x=\"urn:schemas-microsoft-com:office:excel\" xmlns=\"http://www.w3.org/TR/REC-html40\"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets>",
sheet: {
head: "<x:ExcelWorksheet><x:Name>",
tail: "</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>"
},
mid: "</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->>"+style+"</head><body>",
table: {
head: "<table>",
tail: "</table>"
},
foot: "</body></html>"
};

Resources