Reference SCSS Variable Internally Within Component - css

I'm trying to reference a color variable from my main.scss file within my component but not sure what the correct syntax is. Right now it works by hard coding the color hex value.
I can make it work by adding an external stylsheet, and referencing the theme color that way however don't want to create an extra file but rather use internal styling.
Here's main.scs:
$theme-colors: (
'primary': #00a677,
);
Here's the component:
import React from 'react';
const box = {
color: '#9D2235',
};
const Box = () => {
return(
<div style={box}><h1>I'm A Box!</h1></div>
)
}
export default Box;
Any idea how I can do this?

What you can do is, put a class on the box component
In main.scss
$primary: #00a677
.box {
color: $primary
}
In your component
import React from 'react';
const Box = () => {
return(
<div className='box'><h1>I'm A Box!</h1></div>
)
}
export default Box;

Related

How do I use tailwindcss with sass modules in React?

I have two components
/components/main/index.jsx
/components/main/main.scss
My main component is as follows
import css from './main.module.scss'
import React from 'react'
const Main = () => {
return <div className={css.main}>Main Component</div>
}
export default Main
and in my main.scss I would like to do the following
.main {
color: theme('colors.black');
}
or if possible even use the #apply keyword
.main {
#apply bg-black-200;
}
but I have not been able to figure out a way to do this?

How do you style your React.js components differently depending on where you are using them in your application?

Let's say you have a navbar and when you're using this component on your homepage you want it to have a certain background color and display property, but when you use that same navbar component on another page in your application you want to change these CSS properties. Seeing as the component has one CSS file linked how would you change the style of a component depending on where it is being rendered?
My personal favourite method nowadays is styled components. Your component might look something like this:
// NavBar.js
import styled from 'styled-components'
const StyledDiv = styled.div`
width: 100%;
height: 2rem;
background-color: ${props => props.bgColor};
`
const NavBar = (bgColor) => {
return <StyledDiv bgColor={bgColor}>
}
Then to use it in your different contexts you simply pass the color prop:
// homepage.js
<NavBar bgColor="red" />
// otherpage.js
<NavBar bgColor="#123ABC" />
Styled components are becoming a very popular way of doing things, but be aware that there are a huge array of ways you can do this.
https://styled-components.com/
(Code not tested)
Well If you just want to use plain CSS then you can change the className based on route so the styles also changes.
Example:
import { useLocation } from "react-router-dom";
const Navigation = () => {
let location = useLocation();
...
return(
<nav className={location.pathname === "/home" ? "homepage-navbar" : "default-navbar"}>
...
</nav>
)
}
You can write longer condition for multiple pages as well.
Other better thing you can do is pass the location.pathname and value of className as prop.
import { useLocation } from "react-router-dom";
const Home = () => {
let location = useLocation();
...
return (
<>...
<Navigation location={location.pathname} styleClass={"homepage-navbar"}/>
</>
)
}
const Navigation = ({location, styleClass}) => {
...
return(
<nav className={location === "/home" ? styleClass : "default-navbar"}>
...
</nav>
)
}
So now you can pass different values for className from different components and get different styles for the navbar.

How to select a nested styled-component that is imported from another file

I Have the following scenario:
This should be a reusable component to be imported in different parts of the app
import React from 'react';
import styled from 'styled-components';
const Wrapper = styled.div`
// some basic styling here
`
const DisplayInfo = styled.div`
//more styling
`
const Component = props => (
<Wrapper>
<DisplayInfo>{props.title}</DisplayInfo>
</Wrapper>
export default Component;
);
That component will be imported in another component and some of it's styling should be overwritten
....
import Component from './Component';
const NewWrapper = styled.div`
//here I should select the ${Component} and overwrite some of it's css
`;
const ParentComponent = props => ({
<NewWrapper>
<Component title={props.title} />
</NewWrapper>
})
My issue that is I tried a lot of tactics to target the imported 'Component' inside the NewWrapper styling, but nothing works. Only If I declare 'Component' inside the second file does it works.
Isn't there another way because I want them to be separated?
P.S: Also tried to pass 'classname' to the Wrapper in the first file as it says in the styled-components doc, but it still didn't seem to work.
You're quite close actually. When you're targeting another Styled Component inside a Styled Component, like this:
const StyledParent = styled.div`
${StyledChild} {
...styles
}
`;
The parent expects to receive a Styled Component, not a React component. Change your export in the first file, and try this:
export default styled(Component);

Switchable custom bootstrap themes with sass

I was able to find many different approaches on how to make a switchable themes in SASS I wasn't able to figure out how can I apply that approach to bootstrap overrides. Let's say I want to have light and dark theme and both themes would override bootstraps colors differently.
// Light-theme
$theme-colors: (
"primary": #8A2F4F
);
// Dark-theme
$theme-colors: (
"primary": #fff
);
In the example above are overrides for primary of theme-colors. How can I conditionally set them based on theme the user has selected?
Well,
there are many ways to do this. I'd suggest you to generate multiple CSS files based on the _variables.scss file. All you have to do is build new theme with different variable file. You can check this method in my public repo.
Other way would be to use CSS custom variables (called as custom properties). You can do something like this. I've just copy pasted and altered the CSS. It is just to give an idea.
in sass file,
$theme-colors: (
"primary": var(--primary-color);
);
and in other variable file,
element.dark {
--primary-color: black;
}
element.light {
--primary-color: white;
}
These are two method I would suggest.
Here is how I did it with React, Bootstrap and SASS:
In an index.scss file:
div#blue {
#import './blue.scss';
}
div#red {
#import './red.scss';
}
// The rest of bootstrap
// Seems to be necessary to import twice to get fonts to work
#import "node_modules/bootstrap/scss/bootstrap";
Then red.scss I over-ride Boostrap where needed:
$theme-colors: (
"primary": red,
);
// The rest of bootstrap
#import "node_modules/bootstrap/scss/bootstrap";
blue.scss is similar.
Finally my top level React component:
import './index.css'
import * as React from 'react'
import Button from 'react-bootstrap/Button'
export const Container = () => {
const [color, setColor] = useState('red');
const buttonClick = () => {
if (color === 'red') setColor('blue')
if (color === 'blue') setColor('red')
}
return (
<div id={theme} >
Switch themes...
<Button variant="primary" onClick={buttonClick}>Button</Button>
</div>
)
}
Press the button and it will change from red to blue. I am guessing that a className rather than a div selector is a better way to do this - but this approach is working for me so I am happy. Not sure how robust this solution is in complex applications yet.

conditional css in create-react-app

I have default css file and separate css file that should be applied (to owerride default) only when certain conditions are met.
I am using create-react-app wit default import 'file.css' syntax.
What is the best way forward to decide whether to load or not load particular css file dynamically?
The require method only worked in development (as all the CSS is bundled upon build), and the import method did not work at all (using CRA version 3.3).
In our case, we have multiple themes, which cannot be bundled - so we solved this using React.lazy and React.Suspense.
We have the ThemeSelector, which loads the correct css conditionally.
import React from 'react';
/**
* The theme components only imports it's theme CSS-file. These components are lazy
* loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
*/
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));
const ThemeSelector: React.FC = ({ children }) => (
<>
{/* Conditionally render theme, based on the current client context */}
<React.Suspense fallback={() => null}>
{shouldRenderTheme1 && <Theme1 />}
{shouldRenderTheme2 && <Theme2 />}
</React.Suspense>
{/* Render children immediately! */}
{children}
</>
);
export default ThemeSelector;
The Theme component's only job, is to import the correct css file:
import * as React from 'react';
// 👇 Only important line - as this component should be lazy-loaded,
// to enable code - splitting for this CSS.
import 'theme1.css';
const Theme1: React.FC = () => <></>;
export default Theme1;
The ThemeSelector should wrap the App component, in the src/index.tsx:
import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
As I understand, this forces each Theme to be split into separate bundles (effectively also splitting CSS).
As mentioned in the comments, this solution does not present an easy way of switching themes runtime. This solution focuses on splitting themes into separate bundles.
If you already got themes split into separate CSS files, and you want to swap themes runtime, you might want to look at a solution using ReactHelmet (illustrated by #Alexander Ladonin's answer below)
You can use require('file.css') syntax instead. This will allow you to put it inside of a conditional.
e.g.
if(someCondition) {
require('file.css');
}
Use React Helmet. It adds links, meta tags etc into document header dynamically.
Add it into any render method.
import {Component} from 'react';
import ReactHelmet from 'react-helmet';
class Example extends Component{
render(
<ReactHelmet link={
[{"rel": "stylesheet", type:"text/css", "href": "/style.css"}]
}/>);
}
}
You can rewrite it on next <ReactHelmet/> rendering.
One simple solution that I found that works in production is to use vercel's styled-jsx. First, install styled-jsx:
npm install --save styled-jsx
Or if you use Yarn:
yarn add styled-jsx
Now create strings from your css file, so for instance:
const style1 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
const style2 = `
div {
display: flex;
flex-direction: column;
align-items: center;
}
`
And then in your React Component, you can do something like this:
const MyComponent = () => {
return (
<div className='my-component'>
<style jsx>
{
conditionA ? style1: style2
}
</style>
</div>
)
}
Simply add <style jsx>{your_css_string}</style> to the component which you wish to add styling to and you can then to implement conditions just use different strings to import different css styling.
If you are here you most likely are trying to condition a CSS or SCSS import, probably to make some light/dark mode theme or something. The accepted answer works just on mount, after the second css is loaded they are both loaded and you dont have a way to unload them, or actually you have, keep reading...
The use of React lazy and suspense is awesome but in this case we need to help our selves from webpack, because is actually the guy that bundles stuff and can also unbundle stuff, which is what you need, a toggle of css imports basically
Adding webpack lazyStyleTag
Go to your webpack config file and add the following rules
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
// Probly you already have this rule, add this line
exclude: /\.lazy\.css$/i,
use: ["style-loader", "css-loader"],
},
// And add this rule
{
test: /\.lazy\.css$/i,
use: [
{ loader: "style-loader", options: { injectType: "lazyStyleTag" } },
"css-loader",
],
},
],
},
};
Now take your CSS files and change their name to the lazy named convention
You probably have this
styles.css
// or
styles.min.css
Now will be this:
styles.lazy.css
Then create your React theme Provider in a simple React context, this context will wrap your App so it will load the conditioned CSS everytime the context state changes. This context state is going to be availabe anywhere inside your app as well as the setter via a custom hook we will export from the same file, check this out:
import React, {
useEffect, createContext, useState, useContext,
} from 'react';
import { Nullable } from 'types';
// Import both files here like this:
// Import of CSS file number 1
import LightMode from './theme/styles.lazy.css';
// Import of CSS file number 2
import DarkMode from './theme/styles.lazy.css';
interface IContext {
theme: Nullable<string>
toggleTheme: () => void
}
const Context = createContext<IContext>({
theme: null,
toggleTheme: () => { },
});
// Your Provider component that returns the Context.Provider
// Let's also play with the sessionStorage, so this state doesn't
// brake with browser refresh or logouts
const ThemeProvider: React.FC = ({ children }) => {
// Im initialazing here the state with any existing value in the
//sessionStorage, or not...
const [theme, setTheme] = useState<Nullable<string>>(sessionStorage.getItem('themeMode') || 'dark');
// this setter Fn we can pass down to anywhere
const toggleTheme = () => {
const newThemeValue = theme === 'dark' ? 'light' : 'dark';
setTheme(newThemeValue);
sessionStorage.setItem('themeMode', newThemeValue);
};
// Now the magic, this lazy css files you can use or unuse
// This is exactly what you need, import the CSS but also unimport
// the one you had imported before. An actual toggle of import in a
// dynamic way.. brought to you by webpack
useEffect(() => {
if (theme === 'light') {
DarkMode.unuse();
LightMode.use();
} else if (theme == 'dark') {
LightMode.unuse();
DarkMode.use();
}
}, [theme]);
return (
<Context.Provider value={{ theme, toggleTheme }}>
{children}
</Context.Provider>
);
};
export default ThemeProvider;
// This useTheme hook will give you the context anywhere to set the state of // theme and this will toggle the styles imported
export const useTheme = () => useContext(Context);
Remember to put this state on the sessionStorage like in this example so your user has the state available every time it comes back or refreshes the page
Don't forget to wrap the friking App with the Provider:
import ThemeProvider from './ThemeProvider'
const App = () => {
return (
<ThemeProvider>
<App />
</ThemeProvider>
)
}
Now just toggle the CSS imports of your application using your cool useTheme hook
import { useTheme } from './yourContextFile';
// inside your component
const AnyComponentDownTheTree = () => {
const { theme, toggleTheme } = useTheme()
// use the toggleTheme function to toggle and the theme actual value
// for your components, you might need disable something or set active a
// switch, etc, etc
}
Other solution does not work for me. After one day of the search, I obtain bellow solution. In my issue, I have two CSS files for RTL or LTR like app.rtl.css or app.ltr.css
Create a functional component Style like this:
import React, { useState } from "react";
export default function Style(props) {
const [stylePath, setStylePath] = useState(props.path);
return (
<div>
<link rel="stylesheet" type="text/css" href={stylePath} />
</div>
);
}
And then you can call it, for example in App.js:
function App() {
...
return (
<Style path={`/css/app.${direction}.css`} />
)}
direction param contains rtl or ltr and determine which file should be loaded.
I tested some alternatives available in some tutorials and the best for me was to use only classes in css.
One of the problems I encountered when using
require: did not override on some occasions
import: delay generated to load css
The best way for me was to actually put a class switch
.default-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#ff944d !important;
}
.dark-sidebar {
--side-text-icon:rgba(255,255,255,.9) !important;
--side-text-section: rgb(255,255,255,.8) !important;
--side-separator-section:#262626 !important;
}
'
<div className={`root-sidebar ${condition?'default-sidebar':'dark-sidebar'}`}></div>

Resources