How to dynamically add css style to pseudo classes in React - css

I'm trying to build a React component that shows multiple images stored in a database, where below each thumbnail image there is a button linking to its dedicated page. Now, I would like each button to show the dominant color of the image as background on hover. I already have the colors stored in database (artwork.palette.DOMINANT), but struggles to pass the hex code to the React component.
The problem is, inline style cannot set properties of pseudo selectors like a:hover, and because the color is dynamically fetched with the artwork object, I can not set it in a global and static css. Is there a way to set component-scoped style in React? Presumably like <style scoped>...</style>.
Here is my code, I only kept the gist of it for simplicity's sake.
const ImageBox = ({ artwork }) => {
return (
<Fragment>
<div className="image">
<img src={artwork.resources.THUMBNAIL} alt={artwork.title} />
</div>
<div className="caption">
<span>By {artwork.artist}.</span>
</div>
<div className="button">
<a href="!#" style={{ color: artwork.palette.DOMINANT }}>
Details
</a>
</div>
</Fragment>
);
};

You could use JS to modify the global properties of CSS.
Declare properties in index.css or App.css and add your basic styles that will utilize these variables.
:root {
--color-surface: white;
}
button {
background: var(--color-surface);
}
Modify these properties using JS(onMouseEnter and onMouseLeave). i.e.
//onMouseEnter
document.documentElement.style.setProperty("--color-surface", "black");
//onMouseLeave
document.documentElement.style.setProperty("--color-surface", "white")
There a few references you can follow:
Blog and CodSandBox
Note: Not sure if it's a good practice(I haven't seen in projects I've worked on), I would recommend using CSS-in-JS or libraries such as styled component.

Thanks to #PramodMali, I found the CSS-in-JS approach as a elegant way to solve this problem. For anyone who stumbles upon the same struggle in the future, here's how I solved it with react-css:
import { createUseStyles } from "react-jss";
const useStyles = (dominantColor = "#fff") =>
createUseStyles({
toDetailPage: {
color: dominantColor,
"&:hover": {
color: "#000",
background: dominantColor,
}
}
});
After defining the style generator, use it to dynamically set classes in component:
const ImageBox = ({ artwork }) => {
const classes = useStyles(artwork.palette.dominant)();
return (
<Fragment>
<div className="image">
<img src={artwork.resources.THUMBNAIL} alt={artwork.title} />
</div>
<div className="caption">
<span>By {artwork.artist}.</span>
</div>
<div className="button">
<a href="!#" className={classes.toDetailPage}>
Details
</a>
</div>
</Fragment>
);
};
This will then generate dynamic classes on render, for instance detailsButton-0-2-7, which applies the properties passed to the generator function when defining classes.

I was looking for how to set styles dynamically. My main intention was to write a style for hover. Using state to track whether it is hovered or not is good but that will cost render for each update. So I thought How I can solve it with out any state change. I end up doing this and it works perfectly.
<a
target='_blank'
href="#"
onMouseOver={(e) => e.target.style.color = 'red'}
onMouseOut={(e) => e.target.style.color = ''}
>
This is my link
</a>

Related

How to use nested css class (cascade) with React CSS Modules

I did see a lot of similar questions on SO but at end, it seems that my issue is a bit different..
I have a react project where (for some reason) I want two types of CSS loading:
Some global (imported from main.tsx as import 'assets/global.css';)
Some scoped (imported from a component as import style from './style.module.css';)
The global ones works as expected, it's the module one that are weird.. If I want to style a simple <a></a> within a div, this following works:
copyrights
.copyrights a { // as Link is rendered as a `<a></a>`
text-decoration: none;
}
/*...*/
import style from './style.module.css';
export const CopyrightsComponent = () => {
return (
<Container className={style.copyrights}>
<Row className={`justify-content-center`}>
<p>
{COPYRIGHTS_TEXT}
<Link to={TERMS_OF_USE_ROUTE_ROUTE}>{COPYRIGHTS_TERMS_OF_USE_HYPERLINK}</Link>
<Link to={TERMS_OF_USE_ROUTE_ROUTE}>{COPYRIGHTS_PRIVACY_POLICIES_HYPERLINK}</Link>
</p>
</Row>
</Container>
);
};
export default CopyrightsComponent;
HOWEVER! When trying to nest CSS in order to select the right child (like a specific img within a certain div), it doesn't work.. I don't understand why
foo
.foo .bar1 a {
text-decoration: none;
}
.foo .bar2 a {
color: red;
}
/*...*/
import style from './style.module.css';
export const FooComponent = () => {
return (
<div className{style.foo}>
<div className{style.bar1}>
<a>bar 1</a>
</div>
<div className{style.bar2}>
<a>bar 2</a>
</div>
</div>
);
};
export default FooComponent;
Thanks for any help...
This doesen't work since your .bar class is scoped to the element.
It should work, if you reduce the specificity of the selector to either:
.foo div a
or,
.bar a
When you utilize CSS modules, and you link your class directly to an element - the class name becomes unique, since obfuscation is appended to the class name, as per the CSS modules spec (It is scoped to the element). You can see this by inspecting a DOM element that you have linked to a class with this technique; It looks something like this: component_name-module--class_name--eq-vo.
Because of this, when you try to chain custom selectors like you did originally, the middle part of the selector (.bar) doesen't exist in its original simplicity because of this obfuscation.

How to add padding within React Modal?

I'm building my first React modal. The basic structure is now done but I want to have more padding in between the border and the contents. I thought this would be simple but I've tried several things and none work.
return (
<div className={classes.backDrop}>
<Modal
backdrop={'static'}
size='lg'
show={true}
centered={true}
style={classes.modalContainer}
data-testid='addFleetCustomerModal'
>
<div className='modalContainer'>
<ModalHeader>
</ModalHeader>
<Modal.Body>
<Modal.Title>
Add Customer
</Modal.Title>
<Form.Group>
<Form.Label className={classes.highlight}>Company Name*</Form.Label>
<Form.Control id='companyName' data-testid='companyName' type='text' placeholder='For example: ABC Corp.'
/>
</Form.Group>
<Form.Group>
<Form.Label>
<strong>NOTES</strong><br/>
Notes added here can only be viewed and edited by other team members.
</Form.Label>
<textarea className="form-control" id="companyNotes" rows="3"></textarea>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Row>
<a href='/#'>Cancel</a>
<Button variant='secondary' size='sm'> Next </Button>
</Row>
</Modal.Footer>
</div>
</Modal>
</div>
);
Any ideas on what CSS I should add (and where) to move the content of the modal inward a bit more?
add this to your css:
.modal-header,
.modal-body,
.modal-footer {
padding: 2rem; //change the padding as you want
}
this will change the padding but the with full width lines between sections.
See Working demo 1
you can also add the padding around the whole modal but this this won't make the lines full width:
.modal-content {
padding: 2rem;
}
See Working demo 2
My apologies, everyone. I must have been really tired yesterday afternoon when I posted this. Let me explain the solution:
The code I posted above is inside a functional component that is defined like this:
const AddCustomer = ({ classes }) => {
classes comes from the parent component, which is defined like this:
class UserMgmtPage extends React.Component {
In this parent component, the CSS styleSheet is injected into via react-jss code. I then pass does these same CSS classes into functional child component.
You'll notice this 2nd line, which was always working:
<div className={classes.backDrop}>
My failure was to use the same syntax. Thus the solution is this:
<div className={classes.modalContainer}>
Sorry for the trouble but I do appreciate everyone who tried to help!

How to set color of existing css class, from props of a react component

I am trying to modify the styling (colors) of existing class from the videojs Library.
I found that we can modify from css file. But I need to modify from the colour we get from react props, which is dynamic each time.
So, we have a class called vjs-control-bar coming from the videojs library to which we need to apply a color property with the value coming from react-props for eg. this.props.color. How we can achieve this ?
<div data-vjs-player>
<video ref={node => (this.videoNode = node)} className="video-js" />
</div>
The vjs-control-bar class wasn't here as it is coming from the video-js library
The right approach here would be to use one of those CSSinJS libraries and add rules that target the DOM elements that are rendered by the VideoJS library you are using.
Another alternative is to simply render a <style></style> tag in the component where you render the <video> component using the dangerouslySetInnerHTML prop.
Here is an example of how this might work:
function InnerComponent() {
return <h1 class="title">Text Color</h1>;
}
function App(props) {
return (
<div className="App">
<style
dangerouslySetInnerHTML={{
__html: `
.title {
color: ${props.color};
}
`
}}
/>
<h1>Hello CodeSandbox</h1>
<InnerComponent />
</div>
);
}
Here is sandbox - https://codesandbox.io/embed/white-star-9o897

Add className attribute to dangerouslySetInnerHTML contents

How to specify className attribute for div which contains 'hello world':
<div dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} />
One way is to set is to outer div like so:
<div dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} className='class-name'/>
and then in css style the child:
.class-name div {
(css stuff here)
}
But I want to set className directly to the div with 'hello world'
EDIT: Adding class name to injected html does not solve the problem, for example:
let content = '<div class="class-name">hello world</div>'
<div dangerouslySetInnerHTML={{__html: content}}
does not work, because in case same content is used many times then CSS collisions occur (I am using style-loader with CSS modules on)
I came across this question after 2 years and I wanted to achieve the exact same results. I didn't find any accurate answers here but I came up with a solution thanks to #jash8506 due to the brilliant answer to this question.
We have to utilize two react hooks
useRef
useLayoutEffect
According to the documentation basically, useRef can be used to access the DOM elements in React and useLayoutEffect can be used to read layout from the DOM and synchronously re-render.
We can access the firstChildElement in the container div and add classes to it's classList
Here is how the completed code looks like
const containerRef = useRef();
useLayoutEffect(()=>{
if (containerRef.current){
containerRef.current.firstElementChild.classList.add('class-name');
}
});
return (
<div ref={elRef} dangerouslySetInnerHTML={{__html: '<div>hello world</div>'}} />
)
<div className="post-excerpt" dangerouslySetInnerHTML={{ __html: post.excerpt}}/>

React css layouting inner components

Imagine that I have such html code:
<header>
<div class="header__logo logo">
<img class="logo__img" ...>
<span class="logo__status"...>
</div>
</header>
Here
.logo - is class that has styles, special for logo component.
.header-logo - has styles positioning logo inside the header.
So in react inside header component I would like to have something like:
<header>
<Logo className="header__logo" />
<header/>
But I can't since react is not automaticly handle className property.
Here I see this options:
Create a div wrapper to the Logo component and add this class to wrapper. As for me it is not an elegant way because it produce a lot of unnecessary divs.
Add logic to the Logo component, that will handle className property and append all outer classes to the root div inside the component. This is also ugly, because I have to add this boilerplate logic each time I want to layout a component inside some other component.
What is the react approach for solving such problems?
Yes, you cannot provide className attribute to a react Component. If you want to provide custom class to the same component, you can provide the className as a prop to the component which is added as a className in the child component.
I believe u need to do something like this.
var Header = React.createClass({
render: function() {
return (
<header>
<Logo customClass="header__logo logo"/>
</header>
);
}
});
var Logo = React.createClass({
render: function() {
return (
<div className={this.props.customClass}>
<img className="logo__img" />
<span className="logo__status" ></span>
</div>
);
}
});
Let me know if this is what you wanted to know or something else.

Resources