How can I add custom CSS to my React component and override the default CSS value? - reactjs

I have a component for my button with its own css file, but I'm trying to figure out how to add a className directly to the component and change the CSS for mobile view. Right now, the Button.css file overrides everything and my new className added to the component doesn't even show up in the html.
Here is the Button.js file
import React from 'react';
import './Button.css'
const STYLES = [
'btn--primary',
'btn--outline'
]
const SIZES = [
'btn--medium',
'btn--large'
]
export const Button = ({
children,
type,
onClick,
buttonStyle,
buttonSize
}) => {
const checkButtonStyle = STYLES.includes(buttonStyle) ? buttonStyle : STYLES[0]
const checkButtonSize = SIZES.includes(buttonSize) ? buttonSize : SIZES[0]
return (
<button className={`btn ${checkButtonStyle} ${checkButtonSize}`} onClick={onClick} type={type}>
{children}
</button>
)
}
Here is the Button.css
:root {
--primary: #3acbf7;
}
.btn {
padding: 8px 20px;
border-radius: 4px;
outline: none;
border: none;
cursor: pointer;
}
.btn:hover {
background-color: transparent;
color: #fff;
padding: 8px 20px;
border-radius: 4px;
border: solid 2px var(--primary);
transition: all 0.3s ease-out;
}
.btn--primary {
background-color: var(--primary);
}
.btn--outline {
background-color: transparent;
color: #fff;
padding: 8px 20px;
border-radius: 4px;
border: solid 1px var(--primary);
transition: all 0.3s ease-out;
}
.btn--medium {
padding: 8px 20px;
border-radius: 4px;
font-size: 18px;
color: #fff;
}
.btn--large {
padding: 12px 26px;
border-radius: 4px;
font-size: 20px;
color: #fff;
}
And here is the main component where I'm trying to add a custom className to override the code above. Note this code is inside of a Navbar component.
import React, { Component } from 'react';
import { MenuItems } from "./MenuItems"
import { Button } from "../Button"
import './Navbar.css'
class Navbar extends Component {
render() {
return (
<nav className="NavbarItems">
<Button className="btn-mobile">Sign Up</Button>
</nav>
)
}
}
If I try to add .btn-mobile to my Navbar.css, none of the properties show up in the html. I tried to add an id and still didn't work. Not sure how to custom the button since it's a component with preset properties

The way that I generally accomplish this is to accept className as a prop and spread it into the default classes of the element, like so:
const Button = ({className='', text, ...rest}) => (
<button className={`my-button-class ${className}`} {...rest}>{text}</button>
)
Then you could use it like so:
<Button className="my-custom-class" text="press me" id="my-id" />
and it will render as
<button class="my-button-class my-custom-class" id="my-id">press me</button>
By defaulting the className prop to an empty string, it won't be added as the string of "undefined" if the implementation doesn't pass a className. Note that we also spread in any other props that were passed.

Related

How to inherit styles from another styled component and turning regular component into styled component at the same time?

I am using StyledComponents stying framework and This is my regular react component
const SelectButton = ({className,children, ...rest}) => {
return (
<select className = {className}{...rest}>
{children}
</select>
);
}
I want to turn this component into styled component by calling styled() function and for that purpose I have attached className prop to DOM element of my react component (SelectButton).
export const StyledSelectButton = styled(SelectButton);
But instead of putting the css in this styled component, I want to inherit from different styled component which is StyledButton.js, which has following css properties.
export const StyledButton = styled(Button).attrs(({ type }) => ({
type: type || "button",
}))
display: inline-block;
height: auto;
padding: 0.8rem 2rem;
border: none;
border-radius: 6px;
font-weight: 500;
font-size: 1.6rem;
text-decoration: none;
text-transform: capitalize;
cursor: pointer;
overflow: hidden;
background-color: ${({ primary }) => (primary ? "#646ff0" : "#cccdde")};
color: ${({ primary }) => (primary ? "white" : "#646681")};
.__select {
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
};
How can I achieve that?
I have tried doing this way , but I am repeating my code.
export const StyledSelectButton = styled(SelectButton)
display: inline-block;
height: auto;
padding: 0.8rem 2rem;
border: none;
border-radius: 6px;
font-weight: 500;
font-size: 1.6rem;
text-decoration: none;
text-transform: capitalize;
cursor: pointer;
overflow: hidden;
background-color: ${({ primary }) => (primary ? "#646ff0" : "#cccdde")};
color: ${({ primary }) => (primary ? "white" : "#646681")};
&__select {
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
}
You can do something like this,
import styled, {css} from "styled-components";
import { StyledButton } from './Button';
const style = css`
color: #585858;
font-family: Poppins;
padding: 1rem;
border: none;
background-color: #cccdde;
width: 150px;
cursor: pointer;
`;
Using function declaration method:
export function StyledSelectButton({ className, children, ...rest }){
return (
<select className={className} {...rest}>
{children}
</select>
);
};
To turn this component into a styled component, pass it to the styled() function.
StyledSelectButton = styled(StyledButton).attrs((props) => ({
as: "select"
}))`
${style}
`;

REACT + TypeScript Accordion transition doesn't works

Sup guys i have a problem, i would like to do an animated accordion, it should to have a transition animated when it opens and collapse, and in icon switching
I'm creating by myself an accordion in react + typescript but this transition doesnt works and idk why, code below:
This my index.tsx
import { useState } from "react";
import { AccordionButton, AccordionContent, Wrapper } from "./styles";
import { AccordionProps } from "./interfaces";
import { BsChevronDown, BsChevronUp } from "react-icons/bs";
export default function Accordion({ title, text }: AccordionProps) {
const [isOpen, setIsOpen] = useState(false);
const handleClick = () => {
setIsOpen(!isOpen);
returnIcon();
};
const returnIcon = () => {
return isOpen ? <BsChevronUp /> : <BsChevronDown />;
};
return (
<Wrapper>
<AccordionButton onClick={handleClick}>
{title} {returnIcon()}
</AccordionButton>
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>
);
}
and this is my styled component below:
import styled from "styled-components";
import { AccordionContentProps } from "./interfaces";
export const AccordionButton = styled.button`
background-color: #606582;
color: #ffffff;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.6s;
&:hover {
background-color: #60658295;
}
> svg {
float: right;
}
`;
export const AccordionContent = styled.div<AccordionContentProps>`
display: ${(props) => (props.isOpen === false ? "none" : "block")};
padding: 0 18px;
background-color: white;
overflow: hidden;
`;
export const Content = styled.div`
padding: 10px 0px;
flex-wrap: wrap;
max-width: 750px;
margin-right: auto;
margin-left: auto;
#media only screen and (max-width: 600px) {
max-width: 80%;
}
`;
export const Wrapper = styled.div`
padding: 10px 0px;
`;
I've tried to add this code but still doesnt working
-webkit-transition: all 0.4s ease-in;
-moz-transition: all 0.4s ease-in;
-o-transition: all 0.4s ease-in;
transition: all 0.4s ease-in;
Try to replace the return block with the following:
return (
<Wrapper>
{isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronUp />
</AccordionButton>}
{!isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronDown />
</AccordionButton>}
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>)
and we can also delete the function returnIcon - we don't need it

Styled-components extends props

I have a ready button like this , These are the features I want every button to have.
If I send props for the icon, I don't want the icon to be created, if I don't, I don't want it to be created.
import Icon from 'components/icons/Icon';
import styled from 'styled-components';
const Button = styled.button`
border-radius: 8px;
color: white;
cursor: pointer;
outline: none;
border: none;
display: flex;
font-family: 'DM Sans', sans-serif;
font-weight: 700;
`;
const DefaultButton = ({ name, children }) => {
return (
<Button>
{name && <Icon name={name}></Icon>}
{children}
</Button>
);
};
export default DefaultButton;
I have a button component that I have customized like this.
I want it to inherit its properties from the default button.
But Primary's features do not appear on my button.
I wonder what should I fix, thanks friend
import styled from 'styled-components';
import { sizes } from 'constants/components/button';
import { colors } from 'theme';
import DefaultButton from './DefaultButton';
const PrimaryButton = styled(DefaultButton)`
padding: ${({ size }) => sizes[size]} 48px;
background-color: ${colors.primary.color};
font-size: 14px;
font-style: bold;
&:focus {
border: 1px solid #ffffff;
box-sizing: border-box;
box-shadow: 0px 0px 0px 3px rgba(24, 207, 152, 1);
}
&:hover {
background-color: ${colors.primary.pressed};
box-shadow: 0px 14px 24px rgba(23, 207, 151, 0.3);
color: white;
}
&:disabled {
background-color: ${colors.primary.disabled};
cursor: not-allowed;
pointer-events: none;
color: white;
}
`;
export default PrimaryButton;
This is how I use my component and I want it to be like this
<PrimaryButton size="md" name="test">
Sign in
</PrimaryButton>
If you want to extend/override the styles of another component, you need to pass down the className prop, as mentioned in the docs.
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
const DefaultButton = ({ name, children, className }) => {
return (
<Button className={className}>
{name && <Icon name={name}></Icon>}
{children}
</Button>
);
};
I find #svrbst solution very effective. I extended it to add other dependencies as shown is the question and it works fine. Here is the forked codesandbox

Why child Styled-components is not applying styles?

I have a Notification component :
import React, { useContext } from "react"
import Wrapper from "./Wrapper"
import context from "./context"
import Close from "./Close"
const Notification = ({ children }) => {
const { type } = useContext(context)
return (<Wrapper type={type}>
<Close /> {// This component does not apply styles !!}
{children}
</Wrapper>)
}
export default Notification
My Wrapper :
import styled from "styled-components"
const Wrapper = styled.div`
position:absolute;
bottom:0;
right:0;
margin-right:1.2em;
margin-bottom:1.2em;
background-color: #fff;
padding:15px;
min-width:350px;
min-height:100px;
border-radius:20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`
const pickShadowColor = (type) => {
switch (type) {
case "info": return "rgba(51,153,255,0.3)";
case "warning": return "rgba(255,157,0,0.3)";
case "success": return "rgba(0,216,0,0.3)";
case "error": return "rgba(255,0,0,0.3)";
default: return "rgba(190,190,190,0.3)";
}
}
export default Wrapper
The result is :
The problem is with Close component while it has a custom style but it still shows the default style of button.
Close component :
import React from 'react';
import { Button } from './Button';
const Close = () => (
<Button>close</Button>
)
export default Close;
and the styled Button :
import styled from 'styled-components';
export const Button = styled.button({
'background-color': 'red'
})
Expected Behavior
The button should be red.
Actual Behavior
The button has a default style.
Additional Info
Navigator: Chrome.
The button does not accept any styles.
The problem its on box-shadow. You are missing a ; and have an extra {
Change it FROM:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)}}`;
TO:
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};`;
The complete style:
const Wrapper = styled.div`
position: absolute;
bottom: 0;
right: 0;
margin-right: 1.2em;
margin-bottom: 1.2em;
background-color: #fff;
padding: 15px;
min-width: 350px;
min-height: 100px;
border-radius: 20px;
box-shadow: 0px 4px 30px 0px ${({ type }) => pickShadowColor(type)};
`;

How can I style react-datepicker?

I'm using webpack, react-datepicker and have managed to import its css with the provided css module.
import 'react-datepicker/dist/react-datepicker-cssmodules.css
The component looks fine and dandy, but now I want to make it full width like the time element above it.
Looking at the CSS, what it needs is for the react-datepicker-wrapper element that gets dynamically added by the library to have display: block. Any modifications I make to react-datepicker-wrapper in my own css does nothing.
What should I do?
date-picker.component.jsx
import React from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker-cssmodules.css';
import './date-picker.component.bootstrap.css';
// eslint-disable-next-line no-confusing-arrow
const buildClassNames = (touched, isInvalid) =>
touched && isInvalid ? 'form-control is-invalid' : 'form-control';
export const DatePickerBootstrap = (props) => {
const { setFieldValue, setFieldTouched, errors, touched } = props;
const { name, value, label, ...rest } = props;
return (
<div className="form-group">
<label className='datePickerLabel' htmlFor={name}>{label}</label>
<DatePicker
selected={value}
onChange={(e) => {
setFieldValue(name, e);
setFieldTouched(name);
}}
className={buildClassNames(touched, !!errors)}
customInput={
<input
type="text"
id={name}
placeholder={label} />
}
{...rest}
/>
<div className="invalid-feedback">
{errors}
</div>
</div>
);
};
export default DatePickerBootstrap;
From https://github.com/Hacker0x01/react-datepicker/issues/2099#issuecomment-704194903
Try with wrapperClassName property:
<DatePicker wrapperClassName="datePicker" dateFormat="dd/MM/yyyy" />
CSS:
.datePicker {
width: 100%;
}
This way you won't modify the styles for the whole app
styled-components bonus:
import React from 'react';
import styled, { css, createGlobalStyle } from 'styled-components';
import DatePicker from 'react-datepicker';
const DatePickerWrapperStyles = createGlobalStyle`
.date_picker.full-width {
width: 100%;
}
`;
<>
<DatePicker wrapperClassName='date_picker full-width' />
<DatePickerWrapperStyles />
</>
I think you're just missing some CSS. Try this in your custom stylesheet (anywhere after the datepicker's stylesheet):
.react-datepicker-wrapper,
.react-datepicker__input-container,
.react-datepicker__input-container input {
display: block;
width: 100%;
}
Demo
styled-components
<>
<DatePickerWrapper
popperContainer={Popper}
calendarContainer={Calendar}
/>
</>
const DatePickerWrapper = styled(({ className, ...props }) => (
<DatePicker {...props} wrapperClassName={className} />
))`
width: 100%;
`;
const Calendar = styled.div`
border-radius: 10px;
box-shadow: 0 6px 12px rgba(27, 37, 86, 0.16);
overflow: hidden;
`;
const Popper = styled.div`
position: absolute;
top: 0;
left: 0;
z-index: 2;
`;
You can get your css work by putting !important at the end of the lines:
display: block !important;
And, you should import your css file at the end:
import 'library0.css';
import 'library1.css';
import 'library2.css';
import 'yourCss.css'; // Your css
overwrite the default css like
.react-datepicker__input-container input {
width: 100%;
}
working example
You can put it inside a flexbox.
If you are using bootstrap, you can use the d-flex and flex-column classes on the wrapper element.
<div className="d-flex flex-column">
<DatePickerField name="date" label="Date" />
</div>
If not, you can style the wrapper using CSS properties.
<div className="date-picker-wrapper">
<DatePickerField name="date" label="Date" />
</div>
CSS:
.date-picker-wrapper {
display: flex !important;
flex-direction: column !important;
}
You can add to the style sheet:
.react-datepicker__input-container{
input{
width: 100%;
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
-moz-padding-start: calc(0.75rem - 3px);
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
appearance: none;
}
}
Output
You can use the class of each element of the picker to override its default style, using the inspect tool you can identify each element's class, if your new style won't be applied you can use the !important property to override the default style
The following code is applied to most of the pickers elements
.react-datepicker__triangle {
display: none;
}
.react-datepicker__day.react-datepicker__day--keyboard-selected {
border: none;
border-radius: 7px;
background-color: var(--dark);
color: var(--white);
}
.react-datepicker__day.react-datepicker__day--keyboard-selected:hover {
border: none;
border-radius: 7px;
background-color: var(--dark);
color: var(--white);
}
.react-datepicker-popper .react-datepicker__navigation {
padding-top: 12px;
color: #000;
}
.react-datepicker {
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.189);
border: none !important;
font-family: "Inter" !important;
}
.react-datepicker__header {
border-bottom: solid 5px var(--light) !important;
background: white !important;
}
.react-datepicker__current-month {
color: var(--dark) !important;
}
.react-datepicker__day.react-datepicker__day--today {
border-radius: 7px;
border: solid 2px var(--brand) !important;
background-color: white !important;
color: var(--dark) !important;
width: 30px;
height: 30px;
}
.react-datepicker__day.react-datepicker__day--selected {
border: none;
border-radius: 7px;
background-color: black;
color: white;
}
.react-datepicker__day--selected:hover,
.react-datepicker__day--in-selecting-range:hover,
.react-datepicker__day--in-range:hover,
.react-datepicker__month-text--selected:hover,
.react-datepicker__month-text--in-selecting-range:hover,
.react-datepicker__month-text--in-range:hover,
.react-datepicker__quarter-text--selected:hover,
.react-datepicker__quarter-text--in-selecting-range:hover,
.react-datepicker__quarter-text--in-range:hover,
.react-datepicker__year-text--selected:hover,
.react-datepicker__year-text--in-selecting-range:hover,
.react-datepicker__year-text--in-range:hover {
border: none;
border-radius: 7px !important;
background-color: var(--brand) !important;
color: var(--dark) !important;
}
From https://github.com/Hacker0x01/react-datepicker/issues/2099#issuecomment-704194903
Try
<DatePicker calendarClassName={/* styles */}></DatePicker>

Resources