Dynamically changing the style of an Angular component using different CSS style sheets - css

I am trying to get a dynamic style-sheet change for a singlepage-application to work in Angular. The idea is to create different themes and let the users choose from them in a dedicated menu. There may be many themes, so nevermind that the example below only has two variants. The functionality is ready. As of now, I have collected all styles in single large scss-files, one for each theme. (Note that I am well aware that in Angular you often split the styles in many sub-scss files, for each component. But for this idea to work, I wanted to have it all in single files for each theme). I have the menu working, and use localstorage for the app to remember which theme has been chosen, since the site has to be reloaded for the changes to take effect.
Now for the problem:
In app.component.ts, the style sheet is defined, in standard form (i.e. styleUrls: [filename]). When using a static file path/name there, and when using a variable, defined just outside the #component-code, the styles works perfectly. But for the dynamic theme change, I have a simple fetch of the variable from localstorage:
var settingsString = localStorage.getItem('usergraphicsdata');
if (isDefined(settingsString)) {
let myUserSettings = JSON.parse(settingsString);
const themename = myUserSettings.theme;
It all works there too. Different console.logs confirms it understands everything it should. But then comes the problem.
if(themename == "theme1"){
var stylePath = "./app.component_theme1.scss";
var graphicFolder = '/assets/theme1/';
} else if(themename == "theme2"){
var stylePath = "./app.component_theme2.scss";
var graphicFolder = '/assets/theme2/';
}
}
Then comes the #component with its styleUrls: [stylePath]
For some reason the if-conditions are not regarded, and the result is always that the theme declared first (in the above case "theme1") will be active. So if I just change the order in the condition, and put the code for theme 2 first, that will be chosen for the site, disregarding which theme variable is actually fetched from localstorage
I have tried several other variants as well, but this one seems to be closest to a solution. Could it be an issue with Angular limiting style changes in runtime contra build? Or have I forgotten something basic in the if-condition?
Best regards and sorry for my limited english.
Per

why don't you solve it with routing? Duplicate the component for each theme, just with a different css-file but use for all those components the same html-file and put in the menu a link to that component (with a specific theme). The added value would be that the theme name also appears in the url and you can easily apply different logic without things getting to be convoluted (different typescript-files).
Something like this:
#Component({
selector: 'sunflower',
templateUrl: './detail.component.html',
styleUrls: ['./sunflower.component.scss']
})
export class SunflowerComponent {
#Component({
selector: 'roses',
templateUrl: './detail.component.html',
styleUrls: ['./roses.component.scss']
})
export class RosesComponent {
If you tried it, please share the problems you've encountered.
update
I made an extremely basic demo (that I've tested), you can find it here:
repo
Take care & good luck.

Related

Conditional stylesheet in Nuxt SSR depending on a store value

I've been surfing StackOverflow and the Nuxt documentation, but I cannot get my head around this one.
So, I need to do RTL. My project is in Nuxt and we use SCSS. I'm setting the htmlAttrs to dir="rtl" conditionally depending on a store getter that tells me if the language is RTL or not. The requirement for this specific task is that a RTL stylesheet should be imported conditionally from the server side also if the country is RTL, so that it overrides the main.scss file.
Now, in nuxtServerInit(), I cannot set the stylesheet in the head, because the route will not direct me to the file, and, most importantly, Webpack won't compile it, as it's outside the regular flow of the application and not imported by main.scss, which is the stylesheet the Nuxt config is pointing to, and which contains all other styles. I realize that I could use a class and use it conditionally in components, but that is not the requirement. The nuxt.config.js file, in the relevant part, looks like this:
css: [
'#/assets/styles/main.scss'
]
There I obviously don't have access to the store.
What I said I tried was this:
if (isRTL) {
service.addEntryToArray('link', {
rel: 'stylesheet',
type: 'text/css',
href: '../assets/styles/main.rtl.scss'
});
}
(We use a service to add things to the head)
I understand that was naive on my part, because Webpack has no say there, so there is no transpilation, and there is no actual asset, which means the route is just wrong, and even if it were right, the browser would not be able to read it.
Any ideas on how this could be achieved at a non-component level? This needs to be part of the Webpack flow, has to be added server-side, and needs to be an import --I cannot just import it regularly and use just a class (again, I'm not stubborn, I'm working on a project, and this is a requirement by the client).
If you need more information, I'll update the post, but I don't know what else to provide.

How to change tailwind-config.js dynamically based on user settings in rails

I have a Rails 6 app set up to use Tailwind CSS with Webpacker similarly to how it's done in this GoRails tutorial.
I want to be able to change the Tailwind defaults dynamically based on the controller and action so that it's very easy for users to "skin" sections of the site by selecting a few options that then dynamically adjust a few of the Tailwind config options. (An example of how this could be used would be users logged into the admin area of the site changing their font family and background color to match their brand.)
I can't just add a stylesheet to the layout based on a conditional because I'd have to override all of the instances where a Tailwind css variable I want to change (like "sans-serif"). That would be a lot of work and brittle to maintain as Tailwind evolves.
It would be ideal if there was a way to dynamically insert choices selected by the user into the Tailwind config file (/javascript/stylesheets/tailwindcss-config.js), but I'm not sure how to do this.
Also is there a better way to do this in Rails when using Tailwind? It seems like there should be some way to use Javascript from the controller to dynamically change the settings in my tailwindcss-config.js (The Tailwind config file is explained here). So, something in that file like this:
theme: {
fontFamily: {
display: ['Gilroy', 'sans-serif'],
body: ['Graphik', 'sans-serif'],
},
What was a font stack hard-coded as a configuration in Tailwind would become this:
theme: {
fontFamily: {
display: DYNAMICALLY INSERTED FONT STACK,
body: ANOTHER DYNAMICALLY INSERTED FONT STACK,
},
How would you do this in Rails? I have that Tailwind config file living at /javascript/stylesheets/tailwindcss-config.js. Is this possible to do with Webpack in rails? Is this even the correct approach to take with Rails 6 using Webpacker + Tailwind?
I have the feeling that we'd be trying to use a 'buildtime' tool for a 'runtime' operation
To directly inject the variable into tailwindcss config file would imply a rebuild of the actual css served to the user, applying the instructions in tailwind config file to the actual content put in app/javascript/css (assuming the setup used in the mentioned video tutorial).
The operation is carried on by webpack, integrated through the webpacker gem.
IMHO, neither webpack nor tailwind were designed with the purpose of rebuilding the assets at runtime, and, even if I'm definitely aware that a universal machine can do anything ;) I wonder where taking this route would take one, mainly in terms of maintainability.
From this link it seems that triggering a rebuild of webpack on a config change is not straightforward.
Here's a somewhat different path to try:
In the <head> section of the application define css variables (more precisely 'css custom properties') for the settings you want your user to access, which can be set and changed dynamically (from js too)
<style>
:root{
--display-font: "<%= display_font_families %>";
--body-font: "<%= body_font_families %>";
--link-color: "<%= link_color %>";
}
</style>
Alternatively you could create app/assets/stylesheets/root.css.erb (the extension is important) and include it in your template before tailwind
Then you should be able to change your tailwindcss config to something like the following:
theme: {
fontFamily: {
display: "var(--display-font)",
body: "var(--body-font)",
},
extend: {
colors: {
link: "var(--link-color)",
},
}
This way we define a dynamic css layout that responds to the value of css variables. The variables and the structure they act on reside on the same logical level, which corresponds to the actual webpage served to the user.
css variables are easily accessible from js, this is one way to have a clean access from rails too
Now let's imagine that the user wants to change the link color (applied to all the links).
In our imaginary settings form, she chooses an arbitrary color (in any css-valid format - the only constraint here is that it must be a valid css value, something you'll need to address with some form of input validation).
We'd likely want
a preview feature (client side/js): without reloading the page the user should be able to apply the new settings temporarily to the page. This can be done with a js call that sets the new value for the variable --link-color
// userSelectedColor is the result of a user's choice,
// say it's "#00FF00"
document.documentElement.style
.setProperty('--link-color', userSelectedColor);
as soon as this value is changed, all the classes previously created by tailwind, and any rule that make use of the variable, will reflect the change, no need to rebuild the css at all.
Please note that our user is not constrained to an arbitrary subset of the possible values, anything that can be accepted by css is fair game. By assigning to the config parameter a css variable, we actually have instructed tailwindcss to specify it in all its classes as a variable value, which now is under our control through css/js ...
We definitely DON'T NEED (nor want) webpack to rebuild the styles
To try to make it clearer, with our color example, in the generated css there will be classes like these - have a look at this link for an explanation of how customizing tailwind theme works
/* GENERATED BY TAILWIND - well, this or something very similar :) */
.text-link {
color: var(--link-color);
}
.bg-link{
background-color: var(--link-color);
}
/* .border-link { ... */
clearly the browser needs to know the value of --link-color (we've defined it in the :root section) and the value itself can be any valid css, but what interests us is that it can be changed anytime, automagically propagating the change to every rule using it, it's a css variable ...
and we'll want a save feature (server side/rails): when the user clicks on 'save', the new settings should be made persistent (saved in db)
this is plainly accomplished (for example) handling the form submit, saving the new value, which will then be pulled from the db to valorize the css variables on the next render of the page
just my 2 cents :) have fun !
As tailwind config file is "js" you can call ajax data then can add it in the config file data to make it dynamic. As we import the theme file in taiwlind config
I was having the same issue today. This worked for me.
# tailwind.config.js
theme: {
fontFamily: {
custom: ['Gilroy', 'sans-serif']
},
and use it like
#sample.html.erb
<span class="font-custom"> Hello Tailwind! </span>

Using two style.css for the same react app

I am trying to use two snippets as components from bootsnipp, and each snippet has its own css. i tried to put them both in the style.css, but it ended up damaging one component for the other to look fine.
I'm thinking about how to use both these styles.css, since in the index.js i can only import style.css.
can i use router to use multiple pages, and import style.css in the second page? but wouldn't that mean i'll have to use the second page as app.js, which is called only once in react? this is kind of confusing me.
EDIT: can I put the css of one component in another css file, and then import it INSIDE that component instead of index.js?
it doesn't bother me by the way whether i put that component inside index.js or not; in fact, I'm not going to use it there.
I would say you need to deal with the global namespace issue. You could create two components with its own css file.
Then add a unique className to stop collisions.
The benefit here is that you could also enable code spitting, so you would only load html/css/js when you need it (see React.lazy).
—-
By trying to load two styles in different times or manners you will still have the same issue of conflicting styles.

Bootstrap 4 Sass - changing theme dynamically

New to sass I stuck with the problem how to enable the dynamic change of a website theme - lets say a dark and a light theme - through user interaction. The template I use as a base (coreui) has a _custom.scss file in which various variables are defined
...
$navbar-bg: #fff;
...
The values of these variables would need to be set dynamically depending on the user choice of the theme and I have no clue how to get this done. Any pointer how to implement this would be highly appreciated.
SASS is a preprocessor which means essentially it gets compiled down into regular CSS and then shipped to the client. If you want to change themes you'll have to dynamically load different stylesheets using javascript.
Case 1
In the case that you want the user to pick between multiple prepackaged themes. This can easily be done with multiple "theme" style sheets which import the various parts of your style. First import the different colors, then import the main bodies of your sass. For example:
theme1.sass:
#import 'theme1';
#import 'base';
#import 'other';
theme2.sass:
#import 'theme2';
#import 'base';
#import 'other';
Then in javascript you could remove the old stylesheet and add the new one when the user does whatever is needed to change the theme. For example inside the onclick of a button you could put:
document.getElementById('cssTheme').setAttribute("href", "/path/to/theme");
It's probably best to take a bit of care and put the element in the head of the document, but this is a good starting point. That could be made to look a lot nicer with Jquery, but I didn't want to assume you'd have that.
Case 2
In the case that you want the user to dynamically change colors of individual element colors it might be worth looking into CSS Variables. Current support in IE/Edge is crumby but it is pretty interesting. Then as the user changes the fields you could just be changing the css variable in a <style> tag somewhere on the page and it should propagate through the document.
If you want more browser support then I think really the best way would be with OK sure's answer. This one gives you the benefit of just changing a variable and not having to reset each element style that uses that variable.
You have 2 options I think.
Option 1) Recompile the styles whenever a change is made by running a command serverside to generate a new CSS file for the user. This will be potentially very resource hungry and probably not recommended.
Option 2) Take the variables you want to be accessible, find where they are mentioned in the bootstrap source and either generate a file or just inline these styles after the stylesheet is included in the template.
So for your example here, depending what language you're coding in, or templating (this is a twig example) you're using, you could inline the following in the head of your template:
<style>
.navbar {
background-color: {{ user_theme.navbar-bg | default('#eeeeee') }}
}
</style>
It's tough to tell you exactly how to do this without knowing what frameworks/languages/infrastructure you're using.
If you were using Twig & PHP for example, you could send a user_theme object to the template, and have an include file which contains all the styles that need modifying with default values as above.

Adding Stylesheets in Angular 2 Components

I'm trying to add stylesheets encapsulated in specific components so as not to pollute the global scope with stylesheet rules. I have a series of widgets, each of which will need different 3rd-party stylesheets. In the end there could be hundreds of widgets, each one loaded on demand. It seems obvious that the following code should work, and both index.css and material.scss should both get bundled up with my components, and their rules will be encapsulated from the rest of the application.
#Component({
templateUrl: 'data-table1-widget.component.html',
styleUrls: [
'data-table1-widget.component.sass',
'../../../assets/css/ngx-datatable/index.css',
'../../../assets/css/ngx-datatable/material.scss',
'test.css'
]
})
However, I'm definitely not seeing what I would expect from index.css or material.css if they are included properly. I get no errors about missing files, but I don't see the rules for index or material getting applied. However, they DO work if you include them from the main styles.sass. So am I missing something here? How do I get multiple stylesheets loading into my component? Index.css works just fine, so it makes me wonder if there's something about how the rules are defined that makes them not work.

Resources