When reusing component, my CSS doesn't get applied to it - css

I am trying to figure out how something like this might occur. Bear with me, since the details might be a little bit sloppy.
I have a Header Component which simply takes up all the viewport, and then adds a NavigationBar Component. The Header Component works just fine in the other place I used it, but for some reason, when I tried reusing it just now, the NavigationBar inside it gets funky (all CSS is simply gone).
Here is the Header component that has the following styling (which works btw): {position: relative;
height: 100vh;
width: 100%;}
import React from "react";
import NavigationBar from "../NavigationBar/NavigationBar";
import "./Header.css";
const Header = (props) => (
<div className="blog-header">
<NavigationBar />
{props.children}
</div>
);
export default Header;
My NavigationBar is a simple React-Bootstrap Navbar (I have decided to delete what was inside navigationItems because I don't think those matter to the problem at hand):
import React from "react";
import { Container, Navbar, Nav, NavbarBrand } from "react-bootstrap";
import Logo from "../Logo/Logo";
import "./NavigationBar.css";
const navigationItems = []
const NavigationBar = (props) => (
<Container>
<Navbar id="navigation" bg="transparent" variant="dark" expand="lg">
<div className="div-brand d-flex flex-grow-1">
<NavbarBrand href="/">
<Logo />
</NavbarBrand>
<div className="w-100 text-right">
<Navbar.Toggle data-toggle="collapse" data-target="#da-navbarNav">
<span className="navbar-toggler-icon"></span>
</Navbar.Toggle>
</div>
</div>
<Navbar.Collapse
className="text-uppercase flex-grow-1 text-right"
id="da-navbarNav"
>
<Nav className="ml-auto flex-nowrap">
{navigationItems.map((navItem, index) => {
return (
<Nav.Item key={index}>
<Nav.Link
id={navItem.id ? navItem.id : null}
href={navItem.path}
className={navItem.classes.join(" ")}
onClick={(event) =>
props.navItemClick(event, window.location.pathname, navItem)
}
>
{navItem.placeholder}
</Nav.Link>
</Nav.Item>
);
})}
</Nav>
</Navbar.Collapse>
</Navbar>
</Container>
);
Navbar.css code:
#navigation {
z-index: 10;
}
#navigation .div-brand {
align-items: center;
}
.navbar-dark .navbar-nav .nav-link {
color: rgba(255, 255, 255, 0.75);
letter-spacing: 1px;
font-size: 0.95rem;
font-weight: bold;
line-height: 24px;
width: 6.4rem;
text-align: center;
}
.navbar-dark .navbar-nav .nav-link:hover,
.navbar-dark .navbar-nav .nav-link:active {
color: #da3833;
}
.navbar-dark #btn-contact {
background-color: #da3833;
border-radius: 3px;
text-align: center !important;
}
.navbar-dark #btn-contact:hover,
.navbar-dark #btn-contact:active {
color: white !important;
}
#media (max-width: 992px) {
.navbar-dark .navbar-nav .nav-link {
text-align: right;
margin-top: 0.2rem;
margin-left: auto;
}
.navbar-dark .navbar-nav .nav-item {
text-align: right;
width: 100%;
}
.navbar-toggler {
outline: none !important;
}
}
I'm currently reusing it inside this component that has as styling the following:
.article-header {
height: inherit;
width: inherit;
position: absolute;
top: 0;
}
import React, { useState, useEffect, useCallback } from "react";
import Header from "../../../components/Header/Header";
import "./ArticlePage.css";
const ArticlePage = (props) => {
const [id, setId] = useState(null);
const loadQueryParams = useCallback(() => {
setId(props.match.params.id ? props.match.params.id : null);
}, []);
useEffect(() => loadQueryParams(), [loadQueryParams]);
return (
<div>
<Header>
<div
className="article-header"
style={{ backgroundColor: "black", opacity: "0.2" }}
>
{id}
</div>
</Header>
</div>
);
};
export default ArticlePage;
If you might have an idea where this might go wrong, feel free to answer. I'm also gonna leave here how the navigationbar should look, and how it rendered.
If you need any more info or details please announce me! Thank you!
EDIT: as requested, here is a demo

I managed to solve it. The problem was that my "bootstrap.css" folder that contains my bootstrap theme was not being imported globally in "index.js", and instead it was inside "index.html".
To be noted: I was also using a Router to go to this new Component in which I was using Navbar, and since the css files weren't imported globally, the css wasn't there.

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?

React - how to stop div onClick from changing sibling divs

I have 6 div elements that I want to be able to click one of and have the className change for just the div I've clicked on. Currently, when I click on one div, ALL the div classNames change. This idea came from a vanilla JS concept that I'm trying to convert into a React component. I'm not sure where/what is going wrong, if anyone can tell me how to prevent the sibling divs' onClicks from being fired or if what I have is wrong fundamentally, I would be eternally grateful. This is what I have so far:
import React, { useState} from "react";
import { Panels } from "../../components/index";
import { data } from "../../constants/index";
import "./gallery.css";
const Gallery = () => {
const [isOpen, setIsOpen] = useState(false);
const toggleOpen = () => {
setIsOpen(!isOpen);
};
return (
<div className="panels">
{data.restaurants.map((restaurant, index) => (
<div
className={`panel panel${index} ${isOpen ? "open open-active" : ""}`}
onClick={toggleOpen}
>
<Panels
key={restaurant.name + index}
description={restaurant.description}
name={restaurant.name}
website={restaurant.website}
/>
</div>
))}
</div>
);
};
export default Gallery;
This is my Panels Component code:
import React from "react";
const Panels = ({ name, description, website }) => {
return (
<div className="panel_text">
<p>{description}</p>
<p>{name}</p>
<a href={website}>
<p>Visit {name}</p>
</a>
</div>
)};
export default Panels;
Aaaand this is my CSS code:
*, *:before, *:after {
box-sizing: inherit;
}
.panels {
min-height: 100vh;
overflow: hidden;
display: flex;
}
.panel, .panel_text {
background: '#fff';
box-shadow: inse 0 0 0 5px rgba(255, 255, 255, 0.1);
color: var(--color-golden);
text-align: center;
align-items: center;
transition:
font-size 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11),
flex 0.7s cubic-bezier(0.61, -0.19, 0.7, -0.11);
font-size: 20px;
background-size: cover;
background-position: center;
flex: 1;
justify-content: center;
display: flex;
flex-direction: column;
}
.panel0 {background-image: url(../../assets/defuegocarousel.jpg);}
.panel1 {background-image: url(../../assets/HaydenslakeSz.jpg);}
.panel2 {background-image: url(../../assets/stonecliffSz.jpg);}
.panel3 {background-image: url(../../assets/shigezooutside.png);}
.panel4 {background-image: url(../../assets/southparkSz.jpeg);}
.panel5 {background-image: url(../../assets/lechonoutside.jpg);}
.panel > * {
margin: 0;
width: 100%;
transition: transform 0.5s;
flex: 1 0 auto;
display: flex;
justify-content: center;
align-items: center;
}
.panel > *:first-child {transform: translateY(-100%);}
.panel.open-active > *:first-child {transform: translateY(0); }
.panel > *:last-child { transform: translateY(100%); }
.panel.open-active > *:last-child {transform: translateY(0); }
.panel_text p, a {
text-transform: uppercase;
}
.panel p:nth-child(2) {
font-size: 4rem;
}
.panel.open {
flex: 5;
font-size: 40px;
}
#media only screen and (max-width: 600px) {
.panel p {
font-size: 1rem;
}
}
You're saving a boolean value as a div element is open or not. So this value is considered for all div element's because there is no identifier which div element is open. You need to save a div element value to identify the open div element.
So you can use a div element's index instead of a boolean value. For example, try the below code.
import React, { useState} from "react";
import { Panels } from "../../components/index";
import { data } from "../../constants/index";
import "./gallery.css";
const Gallery = () => {
const [isOpen, setIsOpen] = useState(null);
return (
<div className="panels">
{data.restaurants.map((restaurant, index) => (
<div
className={`panel panel${index} ${isOpen === index ? "open open-active" : ""}`}
onClick={() => setIsOpen(index)}
>
<Panels
key={restaurant.name + index}
description={restaurant.description}
name={restaurant.name}
website={restaurant.website}
/>
</div>
))}
</div>
);
};
export default Gallery;
your isOpen state is common between all your div's
you should specify a unique value for isOpen of each div
you can change your isOpen state to an object like this :
import React, { useState} from "react";
import { Panels } from "../../components/index";
import { data } from "../../constants/index";
import "./gallery.css";
const Gallery = () => {
const [isOpen, setIsOpen] = useState({});
const toggleOpen = (index) => {
setIsOpen(prevState => {...prevState ,[index]:!(!!prevState[index]) });
};
return (
<div className="panels">
{data.restaurants.map((restaurant, index) => (
<div
className={`panel panel${index} ${isOpen[index] ? "open open-active" : ""}`}
onClick={()=>toggleOpen(index)}
>
<Panels
key={restaurant.name + index}
description={restaurant.description}
name={restaurant.name}
website={restaurant.website}
/>
</div>
))}
</div>
);
};
export default Gallery;

Is it bad to use Refs to change styles in react?

here is a code example where i use Ref's to change style
import React, {useRef, useState, useEffect} from 'react'
import S from "./collapsible.module.css"
import PropTypes from 'prop-types'
import { faMinus, faPlus } from '#fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
function Collapsible(props) {
let myRef = useRef(null);
let buttonRef = useRef(null);
let [ button, setButton] = useState(true)
let Show = () => {
if(button) {
setButton(false)
buttonRef.current.style.backgroundColor = "#555"
myRef.current.style.maxHeight = myRef.current.scrollHeight + "px";
} else {
setButton(true)
buttonRef.current.style.backgroundColor = "hsl(181, 100%, 11%)"
myRef.current.style.maxHeight = "0px";
}
}
return (
<div
className={S.body}
>
<button
className={S.collapsible}
onClick={Show}
ref={buttonRef}
> {props.label}
<div className={S.icon}>
{button? <FontAwesomeIcon icon={faPlus} />:
<FontAwesomeIcon icon={faMinus} />}
</div>
</button>
<div
className={S.content}
ref={myRef}
>
<h3>{props.body}</h3>
</div>
</div>
)
}
Collapsible.propTypes = {
label: PropTypes.string,
body: PropTypes.string,
}
Collapsible.defaultProps = {
label: '',
body: "",
}
export default Collapsible
css:
.collapsible {
display: flex;
background-color: hsl(181, 100%, 11%);
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
outline: none;
font-size: 15px;
border-radius: 3px;
/* margin-bottom: 3px; */
box-shadow: 0px 1px 5px 1px black;
margin-top:13px;
}
.icon{
color:white;
position:absolute;
right:50px;
text-align:right;
justify-content: flex-end;
}
.active, .collapsible:hover {
background-color: #555;
}
.content {
padding: 0 18px;
max-height: 0px;
overflow: hidden;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
This is just replicating this in React:
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_collapsible_animate
I have read that using Refs is bad, especially when using it to change the DOM, but if I didn't change the style with the exact amount shown in "scrollHeight" then the transition would be a messed up speed.
If there is another method Is this still bad practice?
It's more common practice to use a state value to determine the style like this:
<button
className={S.collapsible}
onClick={Show}
style={{backgroundColor: button ? "#555" : "hsl(181, 100%, 11%)"}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
Or have a conditional className for your styles:
<button
className={`${S.collapsible} ${
button ? S.buttonColor : S.anotherButtonColor
}`}
onClick={Show}
>
{props.label}
<div className={S.icon}>
{button ? (
<FontAwesomeIcon icon={faPlus} />
) : (
<FontAwesomeIcon icon={faMinus} />
)}
</div>
</button>
and add .buttonColor and .anotherButtonColor to your CSS Module file (collapsible.module.css).
.buttonColor {
background-color: #555;
}
.anotherButtonColor {
background-color: hsl(181, 100%, 11%);
}
For the maxHeight on myRef, I'd do something like:
<div className={S.content} ref={myRef}>
<div style={{ maxHeight: myRef.current.scrollHeight }}>
<h3>{props.body}</h3>
</div>
</div>

CSS to React components

I started rewriting CSS into React Components but i've encountered a problem, I do know how to do this (using styled-components btw ) :
You have 5 ways to styling component in React.
Every approach have pros & cons(personally I use 4-th ways.)
1.Inline Css
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
2. CSS in JS
const Button = (props) => (
<button className={ 'large' in props && 'large' }>
{ props.children }
<style jsx>{`
button {
padding: 20px;
background: #eee;
color: #999
}
.large {
padding: 50px
}
`}</style>
</button>
)
/* Creates a regular button */
<Button>Hi</Button>
/* Creates a large button */
<Button large>Big</Button>
3. Styled Components
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
render(
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
4. Css Modules (scss || sass)
#width: 10px;
#height: #width + 10px;
#header {
width: #width;
height: #height;
}
5. Stylable - https://github.com/wix/stylable
import React from 'react';
import styled, {css} from 'styled-components';
const Info = styled.div`
display: flex;
// etc.
${props => props.title && css`font-size: 15px`}
`
const Box = styled.div`
&:first-of-type {
// some more css
}
`
// usage
<Info title>some info</Info>
I recommend you follow the official docs as stated in comments by #Mikkel

styled component doesn't work with hierachy selectors

I'm learning to use styled components,but it looks like when i target css classes in hierarchy,it doesn't seem to work.Here i'm using i would like to make apply some styles wheneve use hovers on navigations links.
Here is my navbar code :
import React from "react";
import { Nav, Navbar } from "react-bootstrap";
import Flag from "../common/Image";
import styled from "styled-components";
import NavLink from "../common/NavLink";
const imageURL = "/static/img/abc.png";
const Navigation = ({ className }) => {
return (
<Navbar className={className} collapseOnSelect expand="lg" variant="light">
<Navbar.Brand href="#home">
<Flag imageSource={imageURL} size={[80, 70]} />
</Navbar.Brand>
<NavLink linkName="A" URL={"#"} />
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto">
<NavLink linkName="B" URL={"#a"} />
<NavLink linkName="C" URL={"#b"} />
<NavLink linkName="D" URL={"#b"} />
</Nav>
<Nav>
<NavLink linkName="Espace de discussion" URL={"#discussions"} />
<NavLink linkName="Contactez-nous" URL={"#contact"} />
<Nav.Link>
<i clasName="fas fa-envelope" />
</Nav.Link>
<Nav.Link>
<i className="fas fa-bell" />
</Nav.Link>
<Nav.Link>
<i className="fas fa-user-circle" />
</Nav.Link>
</Nav>
</Navbar.Collapse>
</Navbar>
);
};
export default styled(Navigation)`
background-color: white;
border-bottom: 1px solid #e4e8f0;
`;
And my NavLink component
import React from "react";
import styled from "styled-components";
import { Nav } from "react-bootstrap";
import PropTypes from "prop-types";
const NavLink = ({ linkName, URL, className }) => {
return (
<Nav.Link className={className} href={URL}>
{linkName}
</Nav.Link>
);
};
NavLink.PropTypes = {
linkName: PropTypes.string,
URL: PropTypes.string
};
export default styled(NavLink)`
cursor: pointer;
color: green;
transition: 0.4s linear;
padding: 10px;
&:hover {
color: white;
font-size: 90;
background-color: #2e384d;
border-radius: 10px;
}
.navbar-light .navbar-nav .nav-link &:hover {
color: white;
}
`;
I the bellow gif,the animation is for changind links styled is working for all the links,but the color is only changing to white for the A link.But the form other background,border are changing but not the link color.Here the behaviour:
When i use the following code : .navbar-light .navbar-nav .nav-link &:hover {
color: white;
} in a normal css file without using styled component i get the good the exptected behavior.For solving i tried to use sass way of doing in the definition of my styled component like this :
.navbar-light {
.navbar-nav {
.nav-link {
&:hover {
color: white;
}
}
}
}
But nothing changes.How can i do for making all links text become white with styled-compont definition?
Alright, because of how <Nav> is wrapping your <NavLink>, the nav-link className has a higher specificity than your styled component className (the NavLink component is applying the styled component className before "nav-link" and, as a result, doesn't override the Bootstrap CSS). For example, the className looks like: "sc-lhVmIH gcJAof nav-link", where the styled component className: "sc-lhVmIH gcJAof" is being overridden by the last applied className "nav-link". There are several solutions to fix this, as shown below.
Solutions
Simply add color: white !important; in the styled NavLink:
export default styled(NavLink)`
cursor: pointer;
color: green;
transition: 0.4s linear;
padding: 10px;
border-radius: 10px;
&:hover {
color: white !important;
font-size: 90;
background-color: #2e384d;
border-radius: 10px;
}
`;
In Navigation, where <NavBar className={className}>...</NavBar> accepts a styled component className, add the following css to override the Bootstrap CSS stylesheet:
export default styled(Navigation)`
background-color: white;
border-bottom: 1px solid #e4e8f0;
&.navbar-light {
& .navbar-nav {
& .nav-link:hover {
color: white;
}
}
}
`;
Import the Bootstrap less into a less file and override the nav-link:hover className.
CSS Specificity
Here's how the CSS specificity is being applied to the DOM:
Demos
Click here for a working demo.
Working codesandbox (contains solution #1 and #2 -- you'll only need to use one of them, not both):

Resources