Css module in React component - css

Having this css module:
mymodule.module.css:
ol li:not(:last-child)::after {
color: red;
margin: 1px;
}
React component:
import myStyles from './mymodule.module.css';
...
export const MyComponent: React.FunctionComponent<MyComponentProps> = ({...} => {
...
return (
<li className={myStyles}>
...
</li>
);
There is a red line under className word which when hover states:
Type '{ readonly [key: string]: string; }' is not assignable to type
'string'.ts(2322) index.d.ts(1749, 9): The expected type comes from
property 'className' which is declared here on type
'DetailedHTMLProps<LiHTMLAttributes, HTMLLIElement>'
Any suggestions?

Your CSS file:
# Add your default tag styles
ol li:not(:last-child)::after {
color: red;
margin: 1px;
}
# Add your class
.my-class {
margin: 1rem;
}
Your TSX
// import your stylesheet
import './mymodule.module.css';
...
export const MyComponent: React.FunctionComponent<MyComponentProps> =
({...}) => {
...
// use the class as you would in HTML
return (
<li className="my-class">
...
</li>
);
}

Class Name property is for assigning specific class defined in your css file if you want to apply css on li tag, you just need to import css file in ts file or if you want to give it a styling with class name, you should give its a parent a class like this:
React component:
<ol className={myStyles.abc}
<li>
...
</li>
</ol>
mymodule.module.css:
.abc li:not(:last-child)::after {
color: red;
margin: 1px;
}

Related

Why is this CSS selector taking precedence over more specific selector?

I have this in my React app:
<Nav>
<BasicBtn>Basic button</BasicBtn>
<MainBtn>Main button</MainBtn>
</Nav>
The font size for buttons is set globally:
// globals.css
button {
font-size: 24px;
}
What I want to do is to reduce BasicBtn's font size via the Nav component to 20px, meaning I want it to be smaller when wrapped inside Nav; however, I do NOT want to affect MainBtn, whose font size is set to 14px in its own module. What happens, instead, is that the font size set in Nav overrides the font size set in MainBtn, so MainBtn is 20px as well instead of 14px.
This is my Nav component:
// Nav.js
import styles from "./Nav.module.css"
const Nav = ( { children } ) => <nav className={ styles.Nav }>{ children }</nav>
export default Nav
// Nav.module.css
.Nav button {
font-size: 20px;
}
This is my BasicBtn component:
// BasicBtn.js
import styles from "./BasicBtn.module.css"
import cn from "classnames"
const BasicBtn = ( { children, extraStyles } ) => {
return <button className={ cn( styles.BasicBtn, extraStyles ) }>{ children }</button>
}
export default BasicBtn
// BasicBtn.module.css
.BasicBtn {
padding: 10px;
background: green;
color: white;
}
This is my MainBtn component:
// MainBtn.js
import styles from "./MainBtn.module.css"
import BasicBtn from "../BasicBtn"
const MainBtn = ( { children } ) => <BasicBtn extraStyles={ styles.MainBtn }>{ children }</BasicBtn>
export default MainBtn
// MainBtn.module.css
.MainBtn {
font-size: 14px;
}
This is the generated HTML:
<nav class="Nav_Nav__WC_B6">
<button class="BasicBtn_BasicBtn__q_G1X">Basic button</button>
<button class="BasicBtn_BasicBtn__q_G1X MainBtn_MainBtn__92Bu4">Main button</button>
</nav>
And these, in this order, are the CSS rules that I get when clicking on the generated MainBTN (copied from DevTools):
.Nav_Nav__WC_B6 button {
font-size: 20px;
}
.MainBtn_MainBtn__92Bu4 {
font-size: 14px;
}
.BasicBtn_BasicBtn__q_G1X {
padding: 10px;
background: green;
color: white;
}
Shouldn't the .MainBtn_MainBtn__92Bu4 selector take precedence over .Nav_Nav__WC_B6 button, seeing as it is more specific?

Is there a way to keep linked stylesheets localized to single components?

I have a component that relies on external stylesheets. I'm bringing the stylesheet into the component like this:
Child component
export default class Child extends Component {
render() {
return (
<div>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" />
...my code here...
</div>
);
}
}
But what's happening is this is forcing those styles onto the parent component as well.
Parent Component
export default class Parent extends Component {
render() {
return (
<div>
...code here...
<Child />
... more code here...
</div>
);
}
}
Is anyone aware of a way that I can keep that stylesheet link localized to just that child component so the styles aren't applied to the parent component as well?
Edit 2
Currently trying the shadow dom route, trying to pass down some children. Getting an error after the initial render saying Cannot read properties of undefined (reading 'children'). It does render the this.props.children initially...
import React, { Component } from 'react';
class MyComponent extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
${this.props.children}
`;
}
};
export default class Child extends Component {
render() {
return (
<div>
<script>
{!customElements.get("my-component") && customElements.define('my-component', MyComponent)}
</script>
<my-component>
<h1>Hello from shadow</h1>
</my-component>
<h1>Hello</h1>
</div>
);
}
}
You can try CSS Modules. Add :local(.className) to the class you want to use in your code which is in the font-awesome-min.css file. Then import the styles to your component. For example import styles from './font-awesome-min.css' then use the module in your code. The styles will only apply to specific element and won't affect other elements in the document. So let's say you have a class called .usericon in your css you do this in the css file.
CSS
:local(.usericon){
fill: red;
}
React Code
import styles from './font-awesome-min.css'
export default function Profile(){
return (
<i className={styles.usericon}>User Icon</i>
)
}
One way to truly isolate your CSS is with Web Components. Web Components are a browser API that allows defining custom elements with their own "shadow DOM". If a style is defined inside the shadow DOM, it is truly sandboxed with no styles going in or out. You can use whatever selectors you like:
class FancyBox extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
.fancy-box {
border: solid 3px darkblue;
background: dodgerblue;
padding: 10px;
color: white;
font: 16px sans-serif;
}
</style>
<div class="fancy-box">
<slot></slot>
</div>
`;
}
}
customElements.define('fancy-box', FancyBox);
.fancy-box {
border: dashed 3px darkred !important;
background: crimson !important;
padding: 10px !important;
color: white !important;
font: 16px sans-serif;
}
<fancy-box>Safe in my shadow DOM</fancy-box>
<div class="fancy-box">I am affected by outside stylesheets</div>
Note the use of <slot></slot>. This is a placeholder for child elements of the component.
If I wanted to use this custom element from React, it needs to be defined separately so it only runs once.
class FancyBox extends HTMLElement { /*...*/ };
customElements.define('fancy-box', FancyBox);
class ReactFancyBox extends React.Component {
constructor() {
super();
this.state = { value: 'hello world!' }
}
handleChange(e) {
this.setState({ value: e.currentTarget.value });
}
render() {
return (
<div>
<fancy-box>
<strong>{this.state.value}</strong>
</fancy-box>
<input value={this.state.value} onChange={e => this.handleChange(e)} />
</div>
);
}
};

Emotion styled components not applying rules

I'm trying to use emotion.
My .js code is:
import React from 'react';
import { css } from '#emotion/core';
export const Menu = () => (
<>
<nav
css={css`
position: absolute;
color: white;
`}
>
<h1
css={css`
color: white;
`}
>
Title
</h1>
<ul>
<li>Proyectos</li>
<li>Blog</li>
<li>Acerca</li>
<li>Contacto</li>
</ul>
</nav>
</>
);
export default Menu;
When inspecting the element, I get this instead:
You have tried to stringify object returned from css function. It isn't supposed to be used directly (e.g. as value of the className prop), but rather handed to emotion so it can handle it (e.g. as value of css prop).
Use JSX Pragma with jsx function from '#emotion/react'. This allow use css prop.
Docs: https://emotion.sh/docs/css-prop#jsx-pragma
/** #jsx jsx */
import { jsx } from '#emotion/react'
import { css } from '#emotion/core'
const App = () => (
<nav
css={css`
position: absolute;
color: white;
`}
>
<h1
css={css`
color: white;
`}
>
Title
</h1>
<ul>
<li>Proyectos</li>
<li>Blog</li>
<li>Acerca</li>
<li>Contacto</li>
</ul>
</nav>
)

Targeting a styled component in React with a webpack generated identifier?

I have a component called LargeDialog which encapsulates a StyledDialogContent (both of which are from the Dialog class of the Material UI library).
LargeDialog.jsx
...
const StyledDialogContent = styled(DialogContent)`
padding: 30px;
`;
class LargeDialog extends Component {
...
render(){
return (<StyledDialogContent> ... </StyledDialogContent>) // Some content within.
}
}
...
The styled components adds a padding: 30px to the DialogContent.
I would like to override this with padding: 0px if the LargeDialog modal is reused in another place.
However, the generated webpack CSS has a random identifier i.e. MuiDialogContentroot-0-3-439 FullDialogModal__StyledDialogContent-ogd6um-6 iMpISc and I'm not sure how to target this.
AnotherComponent.jsx
import LargeDialog from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
// What do I put here to override StyledDialogContent with a random identifier?
`;
class AnotherComponent extends Component {
}
...
I tried exporting StyledDialogContent and targetting it as such:
import LargeDialog, {StyledDialogContent} from './LargeDialog'
...
const LargeDialogWrapper = styled(LargeDialog)`
${StyledDialogContent} {
padding: 0px;
}
`;
But that didn't work too.
Example:
https://codesandbox.io/embed/styled-components-d5pzv?fontsize=14&hidenavigation=1&theme=dark
You target it within the style like so:
const Box = styled.div`
background-color: black;
height: 100px;
`;
const Yellow = styled.div`
background-color: blue;
height: 200px;
${Box} {
background-color: yellow;
}
`;
const App = () => {
return (
<>
<Box />
<Yellow>
<Box />
</Yellow>
</>
);
};
Refer to the related docs section.
If it helps, you can check this example file (note the Heading style for example).
An edit after OP question update
In your example, you missing className if you want to enable styling for your components.
Also, you need WrapperDiv to be a direct child, this is how the CSS works, remember that you writing simple CSS just in javascript:
class LargeDialog extends Component {
render() {
return (
<WrapperDiv className={this.props.className}>
<div>{this.props.children}</div>
</WrapperDiv>
);
}
}
const WrapperLargeDialog = styled(LargeDialog)`
${WrapperDiv} {
background-color: blue;
}
`;
// LargeDialog should be red.
// WrapperLargeDialog should be blue.
class App extends Component {
render() {
return (
<div>
<LargeDialog />
<br />
<WrapperLargeDialog>
<WrapperDiv />
</WrapperLargeDialog>
</div>
);
}
}

React - Apply **active** class to an element using CSS modules

I'm diving into React and I found a little problem managing active class names when using CSS modules.
Suppose I want to develop a Tabs React component. I would like to apply an active class to the current list item. The tab headers is built by the following component:
import React, { Component } from 'react';
import styles from './Tabs.scss';
export default class TabHeader extends Component {
render() {
let activeTabIndex = this.props.activeTabIndex;
return (
<ul className={styles['tabs-header']}>
{
this.props.data.map((item, index) => {
return (
<li key={index}>
<a className={(index === activeTabIndex) ? 'active' : ''} href="#">
<span>{item.header}</span>
</a>
</li>
)
})
}
</ul>
);
}
}
As you can see, I conditionally added the class active to the interested list item. The stylesheet code is Tabs.scss:
.tabs-header {
display: table;
width: 100%;
list-style-type: none;
& li {
display: table-cell;
text-align: center;
color: #ECF0F1;
cursor: pointer;
a {
display: block;
padding: 15px;
background: #212F3D;
transition: all .2s ease-in;
transform: skew(-40deg);
&:hover {
background: #2471A3;
color: #F7F9F9;
}
& span {
display: block;
transform: skew(40deg);
}
&.active {
background: #2471A3;
}
}
}
}
With this setup, the active item is not using the active css code. How can I solve the problem?
EDIT: the prop activeTabIndex (integer greater than or equal to zero) is correctly working. If I inspect the elements, I can see the class active being added to the active item list, but it is not pointing to the class defined in Tabs.scss. Just to point this out, when using className={styles['tabs-header']} in the ul element, this is going to be converted to Tabs__tabs-header__2LSPG.
I’d need to try it out to be confident of this, but I think you should be using styles.active instead of 'active’.
this should work<a
className={${index === activeTabIndex && css.active}}
href="#"
{item.header}
Your condition would leave an empty space in a class name because of the condition:
(index === activeTabIndex) ? 'active' : ''
Instead you can do a short-circuit evaluation:
<a className={(index === activeTabIndex) && styles['active']} href="#">
<span>{item.header}</span>
</a>
All about CSS modules in React:
For a className with the dash use: style["upper-level"]
Single word className: style.active
To combine a multiple classNames use: style["upper-level"] + " " + style.active
import style from "./style.module.css";
...
className={
menuOpened
? style["upper-level"] + " " + style.active
: style["upper-level"]
}

Resources