How to reuse SASS variable in JavaScript - javascript

I have list of color variables in JavaScript file as an Object.
export const colors = [
{
colorVariable: "$ui-background"
},
{
colorVariable: "$ui-01"
},
{
colorVariable: "$ui-02"
},
...
]
I created above object file based on scss file which have list of 50 colors. I used same name as scss variable So, i can use scss variable in my React component.
$carbon--theme: (
ui-background: #ffffff,
ui-01: #f3f3f3,
ui-02: #ffffff,
...
)
I imported those colors to jsx file from color.js file.
import { colors } from './theme';
And i want use that color variable to give background of following div. I tried following way So, i can apply scss variable. But, not working.
component.jsx
Code:
Object.keys(colors).map(key => {
return (
<div style={{ background: colors[key].colorVariable }} className={'theme-color')} />
)
})
I tried many things but nothing worked. Any help would be greatly appreciated. (It would be nice if i can use that variable in css (component.scss) file inside theme-color class. If not then ok ).
I am using react 16. Any help would be greatly appreciated and let me know if any questions.

First of all - the SASS variables does not exist on runtime. Are available only during compilation time.
The output CSS file does not include the SASS variables at all because and replaces any variable occurence with the raw value.
You might create CSS variables basing on the SCSS counterparts, then reuse the CSS variables in your code.
:root { /* or your element selector instead of `:root` so the variables will be scoped */
--ui-background: #{map-get($carbon--theme, "ui-background")};
--ui-01: #{map-get($carbon--theme, "ui-01")};
--ui-02: #{map-get($carbon--theme, "ui-02")};
}
Just create additional SASS/SCSS file with the snippet above.
Then simply adjust the values in the colors variable.
export const colors = [
{
colorVariable: "var(--ui-background)"
},
{
colorVariable: "var(--ui-01)"
},
{
colorVariable: "var(--ui-02)"
}
]
It perfectly works in runtime.
Bonus - if for some reason you need to reuse every color you might just iterate over the theme map using SASS's #each flow-control rule like:
:root { /* or your element selector */
#each $name, $color in $carbon--theme {
--#{$name}: #{$color};
}
}
BTW
The colors variable is an array so you don't need to get the keys in order to iterate over it.
There is an additional problem that probably throws an error:
className={'theme-color')} - you have a closing paren ) there.
Anyway, for the string values you can simply omit the JSX placeholders and pass the value directly in quotes.
Look at the fixed example:
colors.map(
(item) => <div key={item.colorVariable} style={{ background: item.colorVariable }} className="theme-color"/>
);
If the colors array consists only with the color values you might simply replace it with array of strings like:
['--ui01', '--ui02', ...]
Then it is even simpler:
colors.map(
(color) => <div key={color} style={{ background: color }} className="theme-color"/>
);
BTW #2.
If for some reason you don't want to use the CSS variables the other option is to use some library like scss-to-json that extracts the SCSS variables and outputs as JSON. This require an additional build step as well as importing/merging the JSON in your code base.

Related

Is it possible to inject a variable value into a javascript comment?

TL;DR: is it possible to inject constant variables (that won't change during runtime) in comments
I've come across a very unique situation where I need my comments to have a specific value in them.
I'm code splitting in React and the way to name chunks in react is to add comment next to the import like this:
const MyComponent = lazy(() =>
import('./MyComponent' /* webpackChunkName: "MyComponent" */)
)
This gives my lazy loaded chunks readable names over generated id names.
There's a section in my codebase where I generate lazy loaded routes for my components based on my folder structure, but I've hit a wall where I can't set my chunk name to a variable set by a function.
Here's my function:
function generateLink(label: string) {
const path = label.replaceAll(' ', '');
return {
Element: lazy(
() => import(`./pages/${path}`, /* webpackChunkName: "{{this should be path}}" */)
),
label,
path
};
}
Is there anyway for me to inject the path variable into that comment?
An extra side note, this generateLink function is not run during runtime, the links are static.
Unfortunately there's no simple method for injecting variables into your comments. However, there is a webpack solution to your specific problem, just use Magic Comments
Updating your generateLink function to the following should resolve your problem.
function generateLink(label: string) {
const path = label.replaceAll(' ', '');
return {
Element: lazy(
() => import(`./pages/${path}`, /* webpackChunkName: "[request]" */)
),
label,
path
};
}
Now if your path is HomePage, your chunk name will resolve to /HomePage.[chunkId].js
Just make sure that all your file names are unique, otherwise webpack will suffix a number to your chunks for non-unique file names.
E.g. if you have 2 file names of HomePage.jsx, then you will end up with /HomePage0.[chunkId].js and /HomePage2.[chunkId].js
This question has been answered before in https://stackoverflow.com/a/52345836/2188587
You can use a placeholder [request] and webpack will automatically use the resolved file name.
import(/* webpackChunkName: "[request]" */ `./pages/${path}`)
I checked the docs, it still exists.
Exception: It will use the file name, so if you have nested files and path variable can also use directories, you're probably out of luck.

Importing a variable with import or defining it with const in the module has a behavior that I do not understand

I have been playing with this issue for a while, I am placing some styling variables (blue, red, yellow) in a separate file and I am importing them in a module to use them.
I have a problem understanding how we store the values there.
1) import statement
If I use the import statement, I can get the values in my class with a console.log(blue), console.log(eval(blue)) but I can't read it with console.log(eval(formColor)).
formColor is a string that is equal to "blue", "red", or "yellow".
I get this error for console.log(eval(formColor)) and can't explain why:
2) using const
If I define the styling variables directly at the beginning of my file, before the class definition, I can access them with a console.log(blue/eval(blue)/eval(formColor))
My question is, I want to reuse these variables somewhere else so they should be store in another file to make the code clear. How can I achieve that?
FYI, I am using Rails and Stimulus on this project.
radio_style.js (file were I define some styling variables)
const blue = { classToApplyNotChecked: [
"flex",
"justify-center",
"items-center",
"bg-white",
"rounded",
"border-2",
"border-blue-200"], classToApplyChecked: [
"flex",
"justify-center",
"items-center",
"bg-blue-200",
"rounded",
"border-2",
"border-blue-400"],};
form_controller.js (file were I want to import the styling variables in my controller class)
import { Controller } from "stimulus";
// import { blue, red, yellow } from "../plugins_variables/radio_style";
const blue = {...};
export default class extends Controller {
static get targets() {
return ["formWrapper", "errorMessage"];
}
...
styleRadio(){
...
console.log(blue);
console.log(eval(blue));
console.log(formColor); // formColor retrieves a string value equal to "blue", "red" or "yellow"
console.log(eval(formColor));
...
}
}
Directories:

Vue.js/NuxtJS - how to create components with a default design customizable by a JSON configuration file

I'm developing a NuxtJS website and the pages/components can have either a generic design by default, either one that is customizable by client, and will be specified in the url.
Something alone the lines of:
http://localhost:3000/registration - For a generic page
http://localhost:3000/client-name/registration - Client specific page
To achieve that goal, I have a JSON configuration file per client (say client-name.json) that has this structure.
{
"some_configuration_property": {},
"another_configuration_property": {},
"design": {
"logoUrl": "/assets/client-name/logo.png",
"backgroundColor": "#000000",
"primaryColor": "#ffffff",
"secondaryColor": "#ffff00"
},
}
To start things, I implemented the routing system and I can successfully read each client's configuration based on the current route (inside the <script> tag of the Vue file of that route), inside the setup method (I use #nuxt/composition-api).
The problem that I'm facing now is to figure out how to pass these "design variables" into the <style> tag of my Vue file, which uses SCSS. The behaviour that I wanted to implement was to have a default design for a specific component/page, but that could be overridden by these "design variables" specific to each client.
The first thing that came to my mind was to use CSS variables, that
would allow me to create variables with a default values but that I
would be able to override inside the styles. I created a sample component for test and it worked with CSS properties and the v-deep pseudo element. However, this means that I would have to create a class for each client in the customized component and that's what I'd like to avoid. I first thought to this approach because it would give me a lot of flexibility about how I choose to use these design colors inside the styles.
Example:
// From the customizable component
.my-button {
color: (--button-color, teal);
}
// Styling from a parent component/view
// Had to create a selector with a style like <div> for superior specificity though, not so clean
v::deep {
div {
&.my-button {
--button-color: purple;
}
}
}
I've seen the /deep/ selector or ::v-deep pseudo selector but I don't think it's a very clean solution since it would be used a lot in the codebase. Styling component from parents would make the code hardly maintainable.
Another approach could be to pass a variable, classArray for instance, inside the setup method to dynamically bind CSS classes on the DOM elements. Although, it would be way too cumbersome to create a CSS class per client with the associated styles.
Like this:
<template>
<my-button :class="classArray"></my-button>
</template>
<script lang="ts">
import { defineComponent } from '#nuxtjs/composition-api'
export default defineComponent({
name: 'MyPage',
setup() {
const clientName = 'someClientName';
const classArray = [clientName]
return { classArray };
},
})
</script>
<style lang="scss" scoped>
.someClientName {
// some custom styles
}
</style>
What would be your approach in this situation?
Thanks for help!
If custom theme configuration needs to be loaded at runtime, this requires to use CSS variables (properties). They can be wrapped in SCSS functions and have default theme fallbacks:
// theme.scss
$primaryColor: #abc;
// $buttonColor: $primaryColor
#function primaryColor() {
#return #{var(--primary-color, $primaryColor)}
}
#function buttonColor() {
#return #{var(--button-color, primaryColor())}
}
Then primaryColor(), etc are used instead of direct use of $primaryColor, etc like it's done in regular SCSS theme:
// From the customizable component
.my-button {
color: buttonColor();
}
And custom theme can be applied on load to the entire document or a part of it (a hierarchy of components) that should be affected by custom theme:
const config = await loadClientConfig();
document.documentElement.style.setProperty(--primary-color, config.design.primaryColor)

Input value into react-native svg uri

Hello I am trying to input a value into an SVG URI in React-Native as follows:
source={require('../tasks/images/' + {task.image} + '.png')}
I am currently iterating through an array of objects and outputting the svgs for each, so I want to do it this way but the syntax isnt accepted.
Any help or advice is welcome!
Because there is no way to make the string passed to require dynamically, you could have an image collection file that references every image you would need, for example:
Create a file ImageCollection.js with the following:
export default imageCollection={
"1": require("./image1.png"),
"2": require("./image2.png"),
"3": require("./image3.png"),
"4": require("./image4.png")
}
Then import it into your file where you need to require your images and do something like this:
import Images from './ImageCollection.js';
class YourComponent extends Component {
renderItem = ({item}) => (
<View>
<Text>Image: {item}</Text>
<Image source={Images[item]}/>
</View>
);
render () {
const data = ["1","2","3","4","5"]
return (
<FlatList data={data} renderItem={this.renderItem}/>
)
}
}
export default YourComponent;
But if you really need to pass dynamic image sources then you could use a package like react-native-image-progress which lets you pass a variable as an image source, I wouldn't recommend this approach unless if there is absolutely no other way for you to solve this problem.
Here, you cannot pass dynamic images path which is available locally as your assests.
Moreover if you have image URI which is coming from the server then you can just provide here and it will work like a charm. But for image Path which is present locally you have to provide actual path. More you can read here.
However, what you can do just provide nested if else for the path based on the conditions, but then please note, you have to provide the full path as well.
Example :-
source={this.state.data===1?require('../tasks/images/image1.png'):this.state.data===2?require('../tasks/images/image2.png'):require('../tasks/images/image3.png')}
Thanks....Hope it helps :)

Best way to do database-driven styles in react

This is a little bit of a subjective question but I hope SO can help.
I'm making a ReactJS app and I need to customize certain parts of the style (the color scheme mostly) based on a user's settings in a database. The color palette is spread out over a lot of the style sheet so I'd like to avoid setting all those colors by hand in the render() method.
One option is to server-side render the full css file on every request. Would something like SASS be able to do this in real time? I know most preprocessors are designed for compile-time use.
Another option would be to embed the styling in a JavaScript module with something like react-style. Each components "stylesheet" could actually become a function that takes some preferences and returns styles. This strategy has some downsides (as described here) plus the extra inconvenience of having to pas a "styleSettings" prop down to EVERY component so it could know how to request its styles.
Alternatively I could just have a single global or page-wide style sheet so I have to deal with fewer props, but I like the modularity of each component getting its own style.
Are there any other options I'm missing here? Is there a best practice for doing this type of thing?
I'm not sure if this is the best way to do this, but I accomplished something similar using a theme-provider HoC.
class ThemeProvider extends React.Component {
componentDidMount() {
// fetch required data if you don't already have it
// create a <style> node
this.styleTag = document.createElement('style')
this.styleTag.type = 'text/css'
document.head.appendChild(this.styleTag)
}
/* update the styles if the data source has changed */
componentDidUpdate ({ styles: prevStyles }, prevState) {
const { styles: currentStyles } = this.props
if (!isEqual(currentStyles, prevStyles)) {
// generate the css ex
const css = `
.some_selector {
color: ${currentStyles.color}
}
`
// update the <style> node
this.styleTag.textContent = css
}
}
render() {
const { children } = this.props
return children
}
}
you would use it like
<ThemeProvider>
<RootComponent/>
</ThemeProvider>
pros
globally apply CSS from one location
fairly cheap in terms of reconciliation
cons
a little bit of "magic" involved (i.e. where are these styles coming from?)
component is mounted before styles are applied - i.e. after render... meaning the first load might have styles "flash" in
slightly destructive - .some_selector { color: ... } would override hover, etc effects... nothing you can't easily fix, though.
It turns out styled-components does most of what I wanted to do pretty easily. As a bonus, it has built in support for global themes, so I don't have to think about theme setting in my component props.
It sounds like you either intend to or already have made use of inline styles within your React components. I myself use inline styles from time to time even if this is frowned upon by the vox populi of the React community. Sometimes inline styles are actually the most elegant solution to a React problem, however, in this situation I propose that you stick with using class names so you can leave the application of color to your stylesheet. You would still want to use a CSS preprocessor like LESS or SASS to apply the different color themes based on the user's settings, which is what I am currently doing on my project. So you would apply classes such as: color-primary, color-secondary, color-accent to your DOM content and those classes would have their colors set via the user data.

Categories

Resources