Dynamically call css with material-ui and React - css

We have a website created with material-ui v.4 and React. On certain pages, we want to load a css file whose name is dependent on a variable. We would like to be able to do something like this:
React.useEffect(()=> {
makeApiCall((data_from_database) => {
const cssName = data_from_database.lab_id + '.css'
import(`some/folder/${cssName}`)
})
})
Unfortunately imports in React have to be at the top level, where we don't have the variable for the css filename available (the variable is retrieved inside a component). Another option would be if we have the css styling saved in the database instead of a file, and we somehow injected it from there.
Does anyone have an idea of how this could be accomplished?

Related

How to dynamically load a css file in a react function component?

In my react application, in a component, I need to load a separate css file dynamically based on a condition and the css styles should get applied.
The condition here is in the url if I have a query string say "customStyleName" with some value , then this dynamic css file should get loaded. I am using css modules and loading a sass file for this component. But, when this query string "customStyleName" present in the url, this dynamic css file should be loaded and the styles would be overrides
I could manage to check the condition if customStyleName query string present in the url and load the css file using require() (dynamic imports didn't work here) and the styles get applied in my local. It's working fine in my local.
localhost:3000/ss/ext/onboarding?customStyleName='ss'
useEffect(() => {
const query = new URLSearchParams(window.location.search);
const customStyleName = query.get("customStyleName");
if (customStyleName) {
console.log("apply customStyleName");
require("./customStyles.css");
} else {
console.log("customStyleName empty");
return;
}
}, []);
But, in dev or other environments, it is loading the dynamic css file even though the necessary query string not present in the url. My react application is created using create-react-app.
when I checked in the developer tools, the css file is getting loaded in dev environments. I am confused why the same thing working perfectly in my local and its not in other environments. Any help would be appreciated. Thanks!

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

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.

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>

How to dynamically store some css, font-style file to react's public folder?

I am working on an epub project using electron with react, and using epub to parse the epub file. Through this parser, I am able to fetch the css, ttf those files. However, these files won't be available in a regular way. For example, for current section's html might want to import css like <link href="flow0003.css" rel="stylesheet" type="text/css">, and this css file won't be loaded.
To work around this, is it possible to store some files to react's public folder dynamically?
I had been searching online, and it seems impossible to do so due to security reason. I had also searched for similar approach with electron, and it seems there no advices available since this project is using electron with react.
By the way, I am able to dynamically inject style like this:
Book.tsx
useEffect(() => {
const css = Object.values(book.manifest).filter(({ href }) => href.endsWith('.css'));
Promise.all(css.map(c => getCss(book, c.id)))
.then(arr => arr.join('\n'))
.then(styles => {
if (!ref.current) return;
ref.current.setAttribute('style', styles);
});
}, [book]);
However, the nested improt for font file won't work anyway.
So is it possible for electron with react, to store style sheets, font files to its public folder (and need to be deleted later as well)?
I know that it's possible to parse img, style's data with html. For example, use react's dom.setAttribute to dynamically inject style sheet. Similarly, for img tag, image's data can be directly injected like <img src={data:${mimeType};base64, ${imgData.toString('base64')}}>. So is there any equivalent way for to load font-face?
Use electron's ipcMain and ipcRenderer to communicate, and electron has assets to file system.

Resources