How to change Project Theme Dynamically in Aurelia - css

I want to change my project theme dynamically.I created different themes files like as "Blue",Red","Light","Black" etc .As my Given code below "light-theme.scss" is my theme file which I am requiring in App.html above from "ezmax-home-pages.scss" which is main file.
Basically light-theme file is importing in main file and changeing project theme.Its working for me .But there are many theme files.I cant change theme file path manually every time.
I google and find many solutions but none of those worked.I found that you cant use any variable in require tag etc Kindly tell is there any solution for my problem.
<require from="./assets/css/light-theme.scss"></require>
<require from="./assets/css/ezmax-home-pages.scss"></require>
Light theme
:root {
--themeColor: #007ACC;
--otherButtonColor: #26a69a;
--cancelButtonColor: rgb(244, 67, 54);
--themeBackColor: #F5F5F5;
--themeElementColor: #fff;
--themeAltColor: #f8fafb;
--themeNormalColor: #fff;
--themetxtColor: #000;
--themeAlttxtColor: #8997a6;
--themeDarkBorderColor: #D7D7DB;
--themeBorderColor: #e8eaed;
--themeDarkAltColor: #f0f4f6;
}
I want to make dynamic approach where i can change theme file path by selecting theme file name from drop-down like and code will change path of my theme file and project theme change dynamically.

Aamir. In your case, as you have webpack for bundling, you'd have to manage users stylesheets separate from the source code. If not, with every new customer you'd be forced to recompile and publish.
A solution is to have a repository with customers's stylesheets and build something like this:
themes = [
{
title: "default",
url:
"https://bootswatch.com/_vendor/bootstrap/dist/css/bootstrap.min.css"
},
{
title: "cerulean",
url: "https://bootswatch.com/4/cerulean/bootstrap.min.css"
},
{
title: 'darkly',
url: 'https://bootswatch.com/4/darkly/bootstrap.min.css'
}
];
activate() {
this.changeTheme(this.themes[0]);
}
changeTheme(theme) {
const head = document.getElementsByTagName("head")[0];
const itemId = 'css-sheet';
let link = document.getElementById(itemId);
if (!link) {
link = document.createElement("link");
link.id = itemId;
link.rel = "stylesheet";
link.type = "text/css";
link.href = theme.url; <-- here occurs the switch
link.media = "all";
head.appendChild(link);
} else {
link.href = theme.url;
}
}
This example works by choosing the theme from a dropdown menu. In your case, the url of the sheet should be inferred from the customer's profile.
A working example with external stylesheets is available on the following link https://codesandbox.io/s/aurelia-dynamic-css-enrge?fontsize=14.
Wish you the best.

Related

Choose between different style for an Ember.JS application

The issue I have is that I can't find a way to 'change' the css style within my application.
The thing that I want to access is for example: I have a red theme, but I want that the user can choose an other predefined theme, like a green, or a blue theme.
The idea is that I have different app.css, how can I change between one another, I can't find method to do so. Maybe I can do it in my environnement.js?
Any tips is apreciated.
tl;dr: How to set multiple css style in our Ember.JS app?
You can achieve this by generating multiple stylesheets, see: https://ember-cli.com/asset-compilation#configuring-output-paths
I suggest using ember-cli-head to add a specific link element with the additional theme stylesheet. You could set the stylesheet in your head.hbs using the headData service.
Full Example:
ember-cli-build.js
var app = new EmberApp({
outputPaths: {
app: {
css: {
// app/styles/red.css
'red': '/assets/themes/red.css'
}
}
},
// Exclude theme css files from fingerprinting,
// otherwise your file is named `red-somehash.css`
// which we don't (easily) now at runtime.
fingerprint: {
exclude: [ 'red.css' ]
}
})
head.hbs
{{#if theme}}
<link rel="stylesheet" href="/assets/{{theme}}.css">
{{/if}}
some-component.js (or Route or whatever)
export default Ember.Component.extend({
headData: Ember.inject.service(),
actions: {
setTheme(themeName) {
this.set('headData.theme')
}
}
})

Make webpack conditionally load additional or alternative css file

I'm trying to implement a kind of css theming in an angular 4 project. We use webpack 3 for bundling. The product is intended to be used by several companies and has to look according to their brandbooks. So we need themes.
We gonna have several builds, but we don't want to have several versions of code. All themes should remain in the same codebase. The differences are minimal: colors, icons, fonts — everything may be changed in css.
I have thought of several ways to do it, the most obvious would be to implement theming via :host-context for components and change the class of body by changing environment variable for webpack. With such method we will heve every theme inside our bundle, which is not good. Maybe there's another way?
I wonder if it is possible to have webpack load not the css file it is asked for. Instead it could look for another file by pattern, and if it exists, use that file instead of original one. Or load both files.
For example, we have a button.component.ts which imports button.component.css. If we don't tell webpack to use any theme, it works as usual. But if we do, it tries to read button.component.theme-name.css in the same directory. If that file exists, webpack imports it instead (or altogether with) the default file.
That's basically what I'm trying to do. I guess, the same mechanism would be useful for html templates in angular.
Is there a plugin to do such magic? Or maybe some sophisticated loader option? If you have another way to solve my task — feel free to drop a comment!
I created a loader which can append or replace the content of a loaded file with the content of its sibling which has a chosen theme's title in its name.
TL;DR
Create a file with loader.
Use it in webpack config.
Run webpack in THEME=<themeName> evironment.
theme-loader.js
const fs = require('fs');
const loaderUtils = require('loader-utils');
module.exports = function (mainData) {
const options = loaderUtils.getOptions(this);
let themeName = options.theme;
let mode = options.mode;
if (themeName) {
// default mode
if (!Object.keys(transform).includes(mode)) {
mode = 'replace';
}
// fileName.suffix.ext -> fileName.suffix.themeName.ext
const themeAssetPath = this.resourcePath.replace(/\.([^\.]*)$/, `.${themeName}.$1`);
const callback = this.async();
// for HMR to work
this.addDependency(themeAssetPath);
fs.readFile(themeAssetPath, 'utf8', (err, themeData) => {
if (!err) {
callback(null, transform[mode](mainData, themeData));
} else if (err.code === 'ENOENT') {
// don't worry! if it's not here then it's not needed
callback(null, mainData);
} else {
callback(err);
}
});
} else {
return mainData;
}
};
const transform = {
// concat theme file with main file
concat: (mainData, themeData) => mainData + '\n' + themeData,
// replace main file with theme file
replace: (mainData, themeData) => themeData
};
A piece of sample webpack.config.js to use this handmade loader:
resolveLoader: {
modules: [
paths.libs, // ./node_modules
paths.config // this is where our custom loader sits
]
},
module: {
rules: [
// component styles
{
test: /\.css$/,
include: path.join(paths.src, 'app'),
use: [
'raw-loader',
// search for a themed one and append it to main file if found
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'concat'
}
}
]
},
// angular templates — search for a themed one and use it if found
{
test: /\.html$/,
use: ['raw-loader',
{
loader: 'theme-loader',
options: {
theme: process.env.THEME,
mode: 'replace'
}
}
]
}
]
}
For example, an app.component.css:
:host {
background: #f0f0f0;
color: #333333;
padding: 1rem 2rem;
display: flex;
flex-direction: column;
flex: 1;
justify-content: center;
}
nav {
/* ... */
/* something about nav element */
/* ... */
}
header {
/* ... */
/* pile of styles for header */
/* ... */
}
To implement dark theme we don't need to change all that flex and padding staff and maybe nav and header don't have their own background and font color settings. So we'll just have to override host element style. We create app.component.dark.css:
:host {
background: #222222;
color: #e0e0e0;
}
The we run webpack with environment variable THEME set to dark. The loader takes a request to process app.component.css, tries to load app.component.dark.css and voila! Themed css is appended to the end of resulting file. Because of cascade,
if multiple competing selectors have the same importance and specificity, … later rules will win over earlier rules (MDN).
For HTML we don't have such method. So we'll have to rewrite our template completely. Hopefully, you won't need to do it too often. I my case, I wanted to change like header and footer to fit the cutomer's branding demand.
This was my first attempt to create a webpack loader, please leave a comment if you see a problem with it.

How to switch css file when using Webpack to load css?

I use gulp to compile my sass file to css files, and reference the css file in my html. The project support theme switch. For example, I have 3 css theme files:
red.css
yellow.css
blue.css
I can currently switch the theme css like this:
var styleDom = $('#theme-style');
var newHref = 'styles/themes/' + themeName + '.css';
if (styleDom.attr('href') !== newHref) {
styleDom.attr('href', newHref);
}
Now I want to use webpack to load the css file.
require('styles/themes/red.css');
It seems work well, but I cannot find a way to switch the theme css file now, does anyone have a solution?
Your approach doesn’t need to change. Just use Extract Text plugin to save out the CSS files. You’ll need to make multiple entry points to create multiple CSS files.
OR
More ideally, (the approach I would take) make your CSS switch based on a different html or body class and just change the class. It won’t add much overhead, and it will be a more ideal UX when changing themes.
You'll need to use a combination of webpacks style-loader and file-loader (second example ) and use require.ensure (second example "dynamic imports") to accomplish this:
function switchTheme(name) {
// Remove the current theme stylesheet
// Note: it is important that your theme css always is the last
// <link/> tag within the <head/>
$('head link[rel="stylesheet"]').last().remove();
// Since webpack needs all filePaths at build-time
// you can't dynamically build the filePath
switch(name) {
case 'red':
// With require.ensure, it is possible to tell webpack
// to only load the module (css) when require is actually called
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/red.css');
});
case 'yellow':
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/yellow.css');
});
case 'blue':
return require.ensure([], function () {
require('style-loader/url!file-loader!styles/themes/blue.css');
});
default:
throw new Error('Unknown theme "' + name + '"');
}
}
Then a call like switchTheme('blue') should do the trick.
And you might have to check your current webpack.config.js, in case you already have configured a loader for .css files.

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

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!

Resources