Merge classes in CSS modules using React, SASS and webpack - reactjs

I am trying to create a simple way to override CSS classes in React components that use CSS Modules.
I can see the stringified names of the classes being imported from styles and being passed in via theme, but I can't seem to figure out how to see the actual CSS properties.
Is there a way to get access to these and merge them into a new class which can then be applied via className?
// component1.scss
.base {
background-color: black; <-- override this property
}
// component1.js
import Component2 from './component2';
import styles from './component1.scss';
const theme = {
base: styles.base
};
function Component1() {
return <Component2 theme={theme} />;
}
// component2.scss
.base {
background-color: yellow;
text-align: center;
ul {
margin: 10px;
}
}
// component2.js
import Component2 from './component2';
import styles from './component2.scss';
function Component2({ theme }) {
return (
<div className={() => someFunctionThatMergesCss(styles, theme)}>
<ul>
...
</ul>
</div>
);
}

Related

Alternative to createGlobalStyle from styled-components that can also be importable

we are currently loading Fonts and few other global styles like this:
import { createGlobalStyle } from 'styled-components';
export default createGlobalStyle`
#font-face {
font-family: 'Name';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/name/v12/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-B4iFU0U1Z4Y.woff2) format('woff2');
}
// more fonts..
}
And in every _app.tsx (from every project in repo) we just
import GlobalStyle from #our-company/ui;
// few other imports
const AppProviders = ({ children, messages, locale }: Props): JSX.Element => {
return (
<IntlProvider
locale={locale || 'en-GB'}
key={locale}
messages={messages[locale]}
defaultLocale="en-GB"
>
<GlobalStyle />
<DsThemeProvider
locale={locale}
>
{children}
</DsThemeProvider>
</IntlProvider>
);
};
But we noticed unnecessary font reloads caused by this GlobalStyle when clicking, for example, in checkbox elements (tried putting this in a .css and just load it and never happens again).
Any idea how could export this styles as GlobalStyle name without using styled-components so we don't have to change all import from all apps in the project?
why you dont create main.css and import it on your index.css or app.css , its download and cache on users browser so you don't need to use global styled component anymore
It seems like you're looking for something like injectGlobal:
import { injectGlobal } from 'styled-components';
injectGlobal`
/* your #font-face stuff here */
`
This seems like it would be a good fit for your situation, as it would be relatively easy to transition from the existing structure using createGlobalStyle to one that uses this.
In your case, the code would look like:
import { injectGlobal } from 'styled-components';
injectGlobal`
#font-face {
font-family: 'Name';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/name/v12/iJWZBXyIfDnIV5PNhY1KTN7Z-Yh-B4iFU0U1Z4Y.woff2) format('woff2');
}
// more fonts..
}
If you wish to use a solution that is backwards-compatible with any code that is rendering <GlobalStyle />, you can include a null component as the default export as a stop-gap solution, alongside your injectGlobal code:
export default () => null;
Would this solve your problem?
You can also bind the style directly inside the JS file
var stylingObject = {
div: {
color: "red",
border: "1px solid red"
}, input: {
margin: "2px",
padding: "5px"
}
}
function App() {
return (
<div style={stylingObject.div}>
<input style={stylingObject.input} type="text" />
</div>
);
}
Originally I had hoped to import some CSS file with the fonts defined there and then just importing it so it would affect the entire page, obviously because the way react works such a thing isn't possible cause it would take place only in the imported component not in the one that did the import.
the reason createGlobalStyle from styled-components renders each time is also due to react's workings - when we set the style for a "component like" (or actual component) object, like every react component its not static and renders only when needed. Even if we make it render by force on page load its not the same one (cause on each page we render it separately) so keeping up with the current config doesn't seem to be possible by react's standard.
If we want to make the styles static or "more" static we would have to :
either import them in each page separately - making us do a major refactor
or use either createGlobalStyle (allowing us to use our already created styled-components component) or importing a main css file in the main application component like in the example below:
globalStyles.js
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
body {
margin: 0;
padding: 0;
background: teal;
font-family: Open-Sans, Helvetica, Sans-Serif;
}
`;
export default GlobalStyle;
App.js
import React, { Fragment } from 'react';
import GlobalStyle from './theme/globalStyle';
import Content from './components/Content';
function App() {
return (
<Fragment>
<GlobalStyle />
<Content />
</Fragment>
);
}
export default App;
If you have a rather large global stylesheet like we did while migrating, you can use styled-components css method to leverage styled-components (css) IDE syntax highlighting & linting you can also do the following:
import React from 'react'
import { createGlobalStyle, css } from 'styled-components'
const Reset = css`
* {
box-sizing: border-box;
}
`
const Accessibility = css`
.hidden {
display: none !important;
visibility: hidden;
}
`
const BaseStyles = createGlobalStyle`
${Reset};
${Accessibility};
`
export const GlobalStyles = () => (
<>
<BaseStyles />
</>
)
Import GlobalStyles and render as sibling to {children}

Styled Components - use styled component as base for another component

I thought this was possible with styled components
Using the first styled Component Block as the bases for another component like
export const BlockOne = styled.Block
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import styled from 'styled-components'
export const Block = styled.div`
width: 100px;
height: 100px;
background: red;
`
export const BlockOne = styled.Block`
background: yellow;
`
const App = () => {
return (
<div>
<Block/>
<BlockOne/>
</div>
);
}
render(<App />, document.getElementById('root'));
Is there a way to do this
Yes, like this
export const BlockOne = styled(Block)`
background: yellow;
`
styled-component only has basic components (tags such as div, span, etc.) as attributes. For anything else, you pass it as a prop.
If you pass a custom component to it, make sure that it accepts a className and that it passes it down to a div or something:
const MyComponent = ({className}) => {
return <div className={className}></div> // styled-component will use this classname to apply the style
}
export const MyStyledComponent = styled(MyComponent)`
background: yellow;
`
Else it would have no effect.

styled components - overwrite default headings

How can I overwrite default headings globally with styled components theme?
For colors I'm doing:
import React from "react";
import { ThemeProvider } from "styled-components";
const theme = {
colors: {
main: "#8563FF",
mainLight: "#AB93FF",
.....
}
};
const Theme = ({ children }) => (
<ThemeProvider theme={theme}>{children}</ThemeProvider>
);
export default Theme;
and them in my component I'm getting those colors trough props
${props => props.theme.colors.main}
I would like to overwrite default headings eg. h1 with my custom fontSize, weight..., so that when I'm using it in my component it looks like this:
const StyledH1 = styled.h1`
/* additional local styling for overwritten h1 */
color: ${props => props.theme.colors.main};
marginTop: 20px;
`;
On top of that, it should contains media query inside overwritten heading.
How to add overwrites to theme?
You should use createGlobalStyle and ensure that its ThemeProvider's child.
A helper function to generate a special StyledComponent that handles global styles.
const GlobalStyle = createGlobalStyle`
h1 {
color: ${props => props.theme.colors.main};
margin-top: 20px;
}
`
// will have margin-top
styled.h1``
<ThemeProvider>
<GlobalStyles />
</ThemeProvider>
You can create reset styles with createGlobalStyle and put it in the root of your project.
For example here are reset styles for all HTML attributes, you can import it in the root index file, and in the project, all default styles of HTML tags will be reset. (styles like margins of <p> or <h1> and so on)
You can do some modifies to set default values of some attributes, for example like the default color of <p>, <h1> or others
Examples of reset.css
https://gist.github.com/DavidWells/18e73022e723037a50d6
https://cssreset.com/scripts/eric-meyer-reset-css/

Passing custom props to each styled component through Provider

I would like to pass a custom prop (exactly: theme name as string) to each passed styled component through Provider, so it was available throughout the css definition.
ThemeProvider almost does it, but it expects object, not the string. I do not want to pass whole object with theme settings, just the name of my theme.
I do not want to use special theme prop or similar, because then I would have to it manually every single time I create new styled component. Provider seems like the best option if only it cooperated with string.
Is there any possibility to pass a string through Provider to Consumer builded in styled components?
EDIT:
[PARTIAL SOLUTION]
I found what I was looking for when I realized styled-components exports their inner context. That was it. Having access to pure react context gives you original Provider, without any 'only objects' restriction ('only objects' is a styled-components custom provider restriction).
Now I can push to each styled component exactly what I want and if I want.
import styled, { ThemeContext } from 'styled-components';
const StyledComponent = styled.div`
color: ${props => props.theme == 'dark' ? 'white' : 'black'};
`;
const Component = props => {
const theme = 'dark';
return (
<ThemeContext.Provider value={theme}>
<NextLevelComponent>
<StyledComponent />
</NextLevelComponent>
</ThemeContext.Provider>
);
};
Hope I have this correct, from what I've been able to glean. I haven't tried this out but it seems it might work for you. This is lifted directly from the reactjs.org docs regarding context. It passed the string name of the theme down.
const ThemeContext = React.createContext('green');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="blue">
<SomeComponent />
</ThemeContext.Provider>
);
}
}
function SomeComponent(props) {
return (
<div>
<OtherComponent />
</div>
);
}
class OtherComponent extends React.Component {
static contextType = ThemeContext;
render() {
return <ThirdComponent theme={this.context} />
}
}
I hope this helps you understand the idea behind ThemeContext from styled-components. I've passed string "blue" to ThemeContext just to show, that it should not be object and you can use just string.
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styled, { ThemeContext } from "styled-components";
// Define styled button
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
color: ${props => props.theme};
border: 2px solid ${props => props.theme};
`;
// Define the name of the theme / color (string)
const themeName = "blue";
const ThemedButton = () => {
// Get the name from context
const themeName = useContext(ThemeContext);
return <Button theme={themeName}>Themed color: {themeName}</Button>;
};
function App() {
return (
<div className="App">
<ThemeContext.Provider value={themeName}>
<ThemedButton />
</ThemeContext.Provider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Demo: https://codesandbox.io/s/styled-components-example-with-themecontext-cso55

How can I setup SCSS files (ViewEncapsulated way) in 'react app 2' like Angular component specific SCSS?

I installed 'react app 2' as well as node-sass. It's working fine with SCSS. But I just want to know how can I create component specific SCSS like Angular (that will never be a conflict with other components SCSS)
Angular automatically add an attribute for ViewEncapsulation see below example
In angular, there is an option for
encapsulation: ViewEncapsulation.None (Use to disable CSS Encapsulation for this component)
enter link description here
I know the question is old, but it has no answer, thus I want to share this article. Alsomst does the trick, with the exception that it seems to does not have support for something like ::ng-deep
React doesn't have native component styles like Angular does because it aims to keep away from any functionality that could easily be handled by third-party packages. So you have two pretty simple options:
Use styled-components to create component-specific styles. This is a pretty straightforward package that allows you to define styles for each element within a component and you can even pass variables into the styles. It generates internal CSS (kept in <style> tags in the document head) which will take precedence over external styles by default. Example:
// MainComponent.jsx
import React from 'react';
import styled from 'styled-components';
const Title = styled.h1`
color: red
`
const MainComponent = (props) => <Title>Hello World</Title>
In each of your components, add a class or ID to the root element so that you can simply add that selector to the beginning of your SCSS to only style that specific component. Example:
// MainComponent.jsx
import React from 'react';
const MainComponent = (props) => (
<div className="main-component">
<h1>Hello World</h1>
</div>
)
// MainComponent.scss
.main-component {
h1 {
color: red;
}
}
Now only h1 elements in your MainComponent will be red.
//JS
import React from "react";
import "./yourComponentName.scss";
export default props => {
const { className, children, ...restOperator } = props;
return (
<a className={`yourComponentName ${className}` } {...restOperator}>
{children}
</a>
);
}
//yourComponentName.scss
.yourComponentName{
position:relative;
background:red;
/* your property and value use nesting*/
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}

Resources