How to style child components in React with CSS Modules - javascript

I'm using React with SASS and CSS Modules. How can I style the children component without passing a new ClassName or something like that. For Ex.
I want to position or do some styles over the child components, without having to give a specific class, just like when you do p span just for example, all the spans would be the childrencomponent, and I just want to do some styling referencing all the children component in the parent. But as the class is compiled, I don't know how to reference those children.
//ParentComponent.js
Import Styles from 'ParentComponent.scss';
Import Child from 'ChildComponent';
Import ChildB from 'ChildComponentB';
...
return(
<div>
<ChildB />
<Child />
<Child />
</div>
);
//ParentComponent.scss (?)(?)
.child {...}
Here how do I reference the Child components only without passing down a ClassName for example, or without importing the ChildComponent's SASS file in order to get reference to the component class.
//ChildComponent.js
Import Styles from 'ChildComponent.scss';
...
return(
<div classNames={Styles.child}></div>
);
//ChildComponent.scss
.child {...}

There is multiple approach for this, with and without drawbacks.
Wrap each child in a div
This first one is to wrap each of your child components in a div and then add a class on it which then you can reference in your stylesheet:
return(
<div>
<div className={style.child}><ChildB /></div>
<div className={style.child}><Child /></div>
<div className={style.child}><Child /></div>
</div>
);
Pass the className as props
You can pass the class name as props and then add this props to any tag you want in your Child component. On the other hand, you have to do this for every components that you would like to have a class.
return(
<div>
<ChildB className={style.child}/>
<Child className={style.child}/>
<Child className={style.child}/>
</div>
);
//ChildComponent.js
Import Styles from 'ChildComponent.scss';
...
export default ({ className }) =>
<div className={`${Styles.child} ${className}`}></div>
Use the CSS child combinator
In your parent stylesheet, you can use the direct children selector > to select any direct children. You can also combine this operator with the star operator, but be careful with this one since it may slow the browser if used to frequently on a page
If we assume all your Child component is a div:
/* ParentComponent.scss */
.parent > div {
}
Or if you don't how of what Child are made of
/* ParentComponent.scss */
.parent > *{
}

Custom properties (--*): CSS variables could be an option if you have control over those child components.
Firstly, define child style with CSS variables, such as
.some-selector {
/* '--custom-var-name' is the css variable name, while 'white' is the default value */
background-color: var(--custom-var-name, 'white');
}
Then define --custom-var-name in parent components at any level.
<Parent style={{ '--custom-var-name': 'black' }}>
<Child />
</Parent>
or
<GrandParent style={{ '--custom-var-name': 'black' }}>
<Parent>
<Child />
</Parent>
</GrandParent>

Related

Styling with dynamically used class names and attributes in JS(React)?

I wonder if there is a feasible way to implement something like that.
Say, I want to specify padding but I don't want to use inline styling and want to use classes instead. But I also don't want to specify all possible values in CSS.
Like I write:
<Container className="p25 m10" />
what can be rendered to the self-generated classes p25 and m10
<div class="p25 m10"/> /* Classes p25 and m10 are self-generated and have padding and margin */
or even
<Container p=25, m=10 />
rendered into the same or at least to
<div style="padding: 25px; margin: 10px"/>
I don't see that there is a way to auto-generate a css class. But,
this your first option, are classes that represent some kind of pure css style, what you can do is create a class like this:
.p25 {
padding: 25px;
}
<Container className="p25"/>
Your second option, are props that inside the functional component, through some logic, you will carry out the implementation, for example:
export default function Container({p, m}) {
return (
<div style={{padding: `${p}px`}, margin: `${m}px`}></div>
)
}
install tailwind and config manual stuff into config file.
module.exports = {
theme: {
extend: {
spacing: {
'25': '25px',
}
}
}
}
then use it this way
<div class="p-[25]">
<!-- ... -->
</div>

Style Mui child component within parent component using CSS modules

I am following these docs in order to style a material ui component (Paper) within a component (Menu) I am using.
I am using CSS modules to style my components (with Webpack as a bundler) :
// menu.js
import React from 'react';
import { StyledEngineProvider } from '#mui/material/styles';
...
import styles from './styles.module.css';
import Menu from '#mui/material/Menu';
import MenuItem from '#mui/material/MenuItem';
const MyMenu = (props) => {
...
return (
<StyledEngineProvider injectFirst>
<div id="my-menu">
<Button id="button-react-component" onClick={handleClick}>
My Menu
</Button>
<Menu
id="menu-react-component"
...
className={styles.menu}
>
<MenuItem ...>
<span> Example 1 <span>
</MenuItem>
</Menu>
</div>
);
}
// styles.module.css
.menu {
color: white;
}
.menu .MuiPaper-root {
background-color: red
}
// Also tried :
.menu .root {
background-color: red
}
My goal is to have the MuiPaper component have a given background-color. MuiPaper is a component that comes from the Menu component, but I am not using MuiPaper directly as I am only declaring the parent (<Menu>).
Ideally I want to use .css files for styling. I use webpack to bundle my css files into modules.
Here's what I see in my browser :
Notice how the background-color "red" is not applied on that last screenshot.
Thanks :)
CSS modules can't override a style from another CSS module (or elsewhere). There's a few ways to get around this:
Add another class specifically for the .menu paper, e.g. .menuPaper, and add it via PaperProps on the Menu component:
.menuPaper {
background-color: blue;
}
<Menu
id="menu-react-component"
...
className={styles.menu}
PaperProps={{ className: styles.menuPaper }}
>
Add the :global selector to your css selector:
.menu :global .MuiPaper-root {
background-color: red;
}
CSS modules work by "modulifying" CSS classnames by adding a unique ID to the end of them. The :global selector can be used to disable this and preserve the classname instead.
The difference between these two methods is that if you had multiple Menu components in your MyMenu component, using the :global method would give all the Menu instances inside of MyMenu the same background. With the PaperProps method only specific Menus with PaperProps={{ className: styles.menuPaper }} would get the styles applied.
css-loaderdocs: https://github.com/webpack-contrib/css-loader#scope
MUI Menu docs: https://mui.com/api/menu/#props (also see Popover component)

How to target a component in svelte with css?

How would I do something like this:
<style>
Nested {
color: blue;
}
</style>
<Nested />
i.e. How do I apply a style to a component from its parent?
You need to pass props to the parent component with export let, then tie those props to class or style in the child component.
You can either put a style tag on the element in the child you want to style dynamically and use a variable you export for the parent to determine the value of a style directly, then assign the color on the tag like this:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested color="green"/>
<!-- in Nested.svelte -->
<script>
export let color;
</script>
<p style="color: {color}">
Yes this will work
</p>
Upside here is flexibility if you only have one or two styles to adjust, downside is that you won't be able to adjust multiple CSS properties from a single prop.
or
You can still use the :global selector but just add a specific ref to the element being styled in the child like so:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested ref="green"/>
<style>
:global([ref=green]) {
background: green;
color: white;
padding: 5px;
border-radius: .5rem;
}
</style>
<!-- in Nested.svelte -->
<script>
export let ref;
</script>
<p {ref}>
Yes this will work also
</p>
This ensures global only affects the exact ref element inside the child it's intended for and not any other classes or native elements. You can see it in action at this REPL link
The only way I can think of is with an additional div element.
App.svelte
<script>
import Nested from './Nested.svelte'
</script>
<style>
div :global(.style-in-parent) {
color: green;
}
</style>
<div>
<Nested />
</div>
Nested.svelte
<div class="style-in-parent">
Colored based on parent style
</div>
Multiple Nested elements
You could even allow the class name to be dynamic and allow for different colors if you use multiple Nested components. Here's a link to a working example.
You could use inline styles and $$props...
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested style="background: green; color: white; padding: 10px; text-align: center; font-weight: bold" />
<!-- in Nested.svelte -->
<script>
let stylish=$$props.style
</script>
<div style={stylish}>
Hello World
</div>
REPL
using :global(*) is the simplest solution.
No need to specify a class in the child if you want to style all immediate children for example
In the parent component:
<style>
div > :global(*) {
color: blue;
}
<style>
<div>
<Nested />
<div>
Nested will be blue.
I take a look and found nothing relevant (maybe here), so here is an alternative by adding <div> around your custom component.
<style>
.Nested {
color: blue;
}
</style>
<div class="Nested">
<Nested />
</div>
Maybe you will found something but this one works.
The way I do it is like this:
<style lang="stylus">
section
// section styles
:global(img)
// image styles
</style>
This generates css selectors like section.svelte-15ht3eh img that only affects the children img tag of the section tag.
No classes or tricks involved there.

HTML Markup: Write elements textContent via attribute instead of via passing the value as a child?

I'm pretty sure the answer to this is no, but,
Is there any way to write HTML elements like this:
<button innerText="This is the button text" />
<!-- INSTEAD OF LIKE THIS: -->
<button>This is the button text</button>
For an example like this, my request seems unwieldy, but it makes more sense when you have multiple elements that look like this that you're returning via ReactJS.
<button
className="btn"
somePropA="Some Value"
somePropB="Some Value"
somePropC="Some Value"
title="Some title"
>
Some button innerText
</button>
<!-- VERSUS: -->
<button
className="btn"
somePropA="Some Value"
somePropB="Some Value"
somePropC="Some Value"
title="Some title"
innerText="Some button innerText"
/>
I'd love to be able to do this for button, span, etc.
With React, you can create a component for Button (or input, or any other HTML element) and pass the info as props, like this:
From a parent component:
import React, {Component} from 'react';
import Button from './button'; // This is your button component
class App extends Component {
render() {
return (<Button
innerText="Your inner text" // Here, you can pass the text as props. You could also pass a class name, id, etc.
/>);
}
}
export default App;
From a child (Button) component. I'm using a functional stateless component, but you can use a stateful component if you prefer:
import React from 'react';
const Button = ({innerText})=>{
return <button>{innerText}</button>
}
export default Button;
That can easily be done using CSS using its pseudo element and attr() function, like this, and you can target any element with the attribute selector.
For custom attribute one is suppose to use the data-* prefix, so I added that here.
Stack snippet
[data-innerText]::before {
content: attr(data-innerText);
}
/* specifically target span base element */
span[data-innerText] {
display: inline-block;
color: red;
font-size: 20px;
padding: 20px;
letter-spacing: 10px;
text-transform: uppercase;
}
<button data-innerText="This is the button text"></button>
<div>
<span data-innerText="This is a span"></span>
</div>
<div>
<button>And it only adds the text to elements that have the attribute set</button>
</div>
You could do this with Javascript, but there is no way built into HTML. Your javascript would look something like:
document.getElementById("mybtn").value = "Hello";
You can do it for any element, using any attribute with only HTML and CSS's attr() function:
button:after {
content: attr(any-attr);
}
<button any-attr="YOUR TITLE"></button>

Getting height of children elements in a React component

Inside a React component, I want to be able to calculate the combined height of each of my child containers, in this case, the three h3 elements, so that I can calculate the exact height of my parent div when animating the transition of its height. I have seen examples of this with basic js as well as using component mounting methods, but how do I get the height of these elements using just JavaScript?
class Div extends React.Component {
render() {
return (
<div>
<h3>Description One</h3>
<h3>Description Two</h3>
<h3>Description Three</h3>
</div>
);
}
}
ReactDOM.render(<Div />, app);
div {
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app"></div>
In React, to manipulate the DOM, you'll want to use Refs: React Docs - Adding a Ref to a DOM element.
In your example, for your <h3> elements, you'd do something like this:
<h3 ref={(elem) => this.Line1 = elem}>Description One</h3>
<h3 ref={(elem) => this.Line2 = elem}>Description Two</h3>
<h3 ref={(elem) => this.Line3 = elem}>Description Three</h3>
Once you have a Ref to an element, you can use methods & properties of any DOM element on it. For instance, to get its height, you can use Element.clientHeight:
calcHeight() {
return this.Line1.clientHeight + this.Line2.clientHeight + this.Line3.clientHeight;
}
Working JSFiddle

Categories