How to add media query to non component variable in styled components? - css

I am working on a React JS project where I am using styled-components for styling the web pages.
I am using reactstrap components in the project.
I am styling the Button component of reactstrap and trying to add the media query.
ButtonX Component
import styled, {css} from 'styled-components';
import { Button } from 'reactstrap';
import media from '../media';
const orange = `
background-color: #E4572E;
border: 1px solid #E4572E;
color: #fff;
&:hover,
&:focus {
background-color: #e97857;
border: 1px solid #e97857;
color: #fff;
}
`;
const inline = `
position: absolute;
top: 1rem;
right: 1rem;
`;
const ButtonX = styled(Button)`
padding: 0.5rem 1.5rem;
font-weight: 500;
font-size: 0.8rem;
transition: all 0.4s linear;
border-radius: 0.2rem;
${props => props.orange ? css`${orange}` : ''};
${props => props.inline ? css`${inline}`: ''}
${props => props.inline ?
(media.smalldevice`
top: 4rem;
right: 5rem;
`)
: ''}
`;
export {
ButtonX
}
I am Writing the ButtonX component like this
<ButtonX orange="true" inline="true">Sign Up</ButtonX>
As, In the ButtonX component I am checking if inline is set then add the css code as well as media query for the css.
${props => props.inline ? css`${inline}`: ''}
${props => props.inline ?
(media.smalldevice`
top: 4rem;
right: 5rem;
`)
: ''}
I am checking the condition 2 times if inline is true then add css and media query.
1) The above is working, but I want to add both the queries in a single condition which is not working.
2) Else, I would like to know how can I write this media query in the inline variable above like this.
const inline = `
position: absolute;
top: 1rem;
right: 1rem;
${
media.smalldevice`
top: 4rem;
right: 5rem;
`
}
`;
I tried the second one but that is not working as I am sure if we can write media query in the variable.

When adding something like a media query inside a template string, you are interpolating a function. These cannot work inside normal template literals as you have provided inline with.
styled-components provides a helper called css that can be used to interpolate functions. In your case it can be used:
import { css } from "styled-components";
const inline = css`
position: absolute;
top: 1rem;
right: 1rem;
${media.smalldevice`
top: 4rem;
right: 5rem;
`}
`;

Related

Is there any way i can style an element based on other elements state using styled components?

I just started using styled-components and currently converting all my css into styled components in a project. But i am not sure how to achive some things and i could not find anything relevant in documentation too. I would like to know if the following problem is achievable using this approach in styled-components.
I have two checkboxes(radio input), if its checked i want to style a span element(Dot component in this case).
<div className="acc__type">
<RadioInput type="radio" name="accType" id="dot-1" required />
<RadioInput type="radio" name="accType" id="dot-2" required />
<Category>
<AccTypeLabel for="dot-1">
<Dot></Dot>
<AccTypeTitle>Student</AccTypeTitle>
</AccTypeLabel>
<AccTypeLabel for="dot-2">
<Dot></Dot>
<AccTypeTitle>Staff</AccTypeTitle>
</AccTypeLabel>
</Category>
</div>
here are my styled components
export const RadioInput = styled.input`
display: none;
`;
export const Category = styled.div`
display: flex;
width: 80%;
margin: 15px 0;
justify-content: space-between;
`;
export const AccTypeLabel = styled.label`
display: flex;
align-items: center;
`;
export const AccTypeTitle = styled.div`
font-size: 20px;
font-weight: 500;
`;
export const Dot = styled.span`
height: 18px;
width: 18px;
background: #d9d9d9;
border-radius: 50%;
margin: 10px;
border: 5px solid transparent;
transition: all 0.3s ease;
//here iam trying to change this components styles based on <RadioInput/> components state.
#dot-1:checked & {
border-color: #d9d9d9;
background: #9b59b6;
}
`;
in css i did this and it worked
#dot-1:checked ~ span,
#dot-2:checked ~ span {
border-color: var(--sub-grey);
background: var(--main-purple);
}
/* <Dot/> component was span here */
any help!
In Styled Components it's an ever so slightly different syntax, the reason is that it hashes the class names in a way that makes it difficult to target them. So this is how you do it in a way that ensures SC knows what's what:
const dot1StyledComponent = styled.div`
...
`;
${ dot1StyledComponent }:checked ~ span {
border-color: #d9d9d9;
background: #9b59b6;
}

CSS using styled-components and color themes messing up

Image of the problem.
So i have themes and colors defined in my React app.
Everything works well except these labels. When i change the theme to green and then change it back to blue and then when i focus on the input, the color again changes to the opposite theme. I don't understand what I'm doing wrong. Also this problem only occurs after i change the theme. Initially when i focus on the input the label remains blue.
This is my code for the theme part.
{
type: "light",
primaryColor1: "#33b2ff", //lightest
primaryColor2: themeColor === "blue" ? "#009fff" : "#00b05d", //ChangeColor()
primaryColor3: "#007fcc", //darkest
textColor1: "#111", //darkest
textColor2: "#555",
textColor3: "#888", //lightest
backgroundColor1: "#fcfcfc", //lightest
backgroundColor2: "#e3e3e3",
backgroundColor3: "#cacaca", //darkest
borderColor: "009fff",
white: "#fff",
black: "000",
}
This object returns all my theming colors but the primary color part is where it checks a state called themeColor which when set to blue, returns blue as the primary color for the entire app or green if otherwise. The entire app is wrapped with useContext's Provider and the Theme is passed via styled-components' ThemeProvider like so:
import { ThemeProvider } from "styled-components";
<ThemeContext.Provider value={{theme,themeToggler,themeColor,setThemeColor}}>
<ThemeProvider theme={themeStyle}>
{children}
</ThemeProvider>
</ThemeContext.Provider>
This is the CSS part using styled-components for the Input and Label elements:
export const Input = styled.input( ({theme}) => `
border-radius: 10px;
width: 100%;
font-size: 1rem;
background: ${theme.backgroundColor3};
border: 0;
color: ${theme.textColor1};
line-height: 50px;
padding: 5px 20px;
box-sizing: border-box;
font-family: Montserrat;
&:focus
{
outline: none;
}
&[type=password]
{
font-size: 1.75rem;
}
`);
export const Label = styled.label( ({theme}) => `
font-size: 1rem;
font-family: Montserrat;
color: ${theme.textColor3};
cursor: text;
position: absolute;
top: 30%;
left: 5.5%;
border: 1px solid ${theme.borderColor};
transition: 0.2s ease all;
background: none;
${Input}:focus + &,
${Input}:not(:placeholder-shown) + &
{
background: ${theme.primaryColor2};
padding: 0.2rem 0.5rem 0.2rem;
border-radius: 0.5rem;
top: -15%;
left: 3%;
font-size: 0.9rem;
color: #fff;
}
`);
Maybe !important rule solve your problem. You can try write !important after your css properties.
Example:
background: ${theme.primaryColor2} !important;

How to center Items in react

Im crating a navbar for a cite and currently formatting the page Im pretty new to react so im trying to start with the basics. I am stuck on how to center my NavBarLinks, I have used text-center and position to make the links be in the center of the navbar but if I make the web browser smaller it wont stay in the center. My question is what is the right to center items in react.
import React, { useState } from "react";
import {
NavbarContainer,
TopContainer,
BottomContainer,
NavbarExtendedContainer,
NavbarInnerContainer,
NavbarLinkContainer,
NavbarLink,
Logo,
OpenLinksButton,
NavbarLinkExtended,
} from "../styles/Navbar.style";
import LogoImg from "../assets/logo.png";
function Navbar() {
const [extendNavbar, setExtendNavbar] = useState(false);
return (
<NavbarContainer extendNavbar={extendNavbar}>
<NavbarInnerContainer>
<TopContainer>
<NavbarLinkContainer>
<OpenLinksButton
onClick={() => {
setExtendNavbar((curr) => !curr);
}}
>
{extendNavbar ? <>✕</> : <> ≡</>}
</OpenLinksButton>
<Logo src={LogoImg}></Logo>
</NavbarLinkContainer>
</TopContainer>
<BottomContainer>
<NavbarLinkContainer>
<NavbarLink to="/"> Home</NavbarLink>
<NavbarLink to="/products"> Products</NavbarLink>
<NavbarLink to="/contact"> Contact Us</NavbarLink>
<NavbarLink to="/about"> About Us</NavbarLink>
</NavbarLinkContainer>
</BottomContainer>
</NavbarInnerContainer>
{extendNavbar && (
<NavbarExtendedContainer>
<NavbarLinkExtended to="/"> Home</NavbarLinkExtended>
<NavbarLinkExtended to="/products"> Products</NavbarLinkExtended>
<NavbarLinkExtended to="/contact"> Contact Us</NavbarLinkExtended>
<NavbarLinkExtended to="/about"> About Us</NavbarLinkExtended>
</NavbarExtendedContainer>
)}
</NavbarContainer>
);
}
export default Navbar;
Style page
import styled from "styled-components";
import { Link } from "react-router-dom";
export const NavbarContainer = styled.nav`
width: 100%;
background-color: black;
#media (min-width: 700px) {
height: 80px;
}
`;
export const TopContainer = styled.div`
padding-left: 5%;
`;
export const BottomContainer = styled.div`
padding-right: 50px;
background-color:salmon;
`;
export const NavbarInnerContainer = styled.div`
width: 100%;
height: 80px;
`;
export const NavbarLinkContainer = styled.div`
display: flex;
`;
export const NavbarLink = styled(Link)`
color: white;
font-size: x-large;
font-family: Arial, Helvetica, sans-serif;
position: relative;
left: 43%;
top:10%;
text-decoration: none;
margin: 10px;
height: 30px;
text-align: center;
#media (max-width: 700px) {
display: none;
}
`;
export const NavbarLinkExtended = styled(Link)`
color: white;
font-size: x-large;
font-family: Arial, Helvetica, sans-serif;
text-decoration: none;
margin: 10px;
`;
export const Logo = styled.img`
#media (min-width: 700px) {
margin: auto;
}
margin: 10px;
max-width: 180px;
height: auto;
`;
I really wouldn't recommend using a whole different page for css instead just add your code to index.css, from there you can access your css from any component in your react app.
And to center the NavbarLink you can use flexbox.
Create some css code in index.css:
.navbar__link {
width: 100%
display: flex;
justify-content: center;
}
And add that class in your component element:
<NavbarLinkContainer className='navbar__link'>
<NavbarLink to="/"> Home</NavbarLink>
<NavbarLink to="/products"> Products</NavbarLink>
<NavbarLink to="/contact"> Contact Us</NavbarLink>
<NavbarLink to="/about"> About Us</NavbarLink>
</NavbarLinkContainer>

React Styled Component's CSS method optimization

Which is more optimized and why?
I hear that the css method from Styled-component is pretty expensive but I'm wondering if the multiple nested ${(prop) => {}} is more expensive than the single fn with the css method used inside.
Approach 1
const Foo = styled.div`
border-radius: 50%;
display: flex;
width: ${(props) => props.size};
height: ${(props) => props.size};
color: ${(props) => props.bgColor};
background-color: ${(props) => props.bgColor};
`;
vs
Approach 2
const Bar = styled.div`
border-radius: 50%;
display: flex;
${(props) => {
return css`
width: ${props.size};
height: ${props.size};
color: ${props.bgColor};
background-color: ${props.bgColor};
`;
}}
`;
Performance difference should be negligible. The only optimization Styled Components would make is if your styles were static (with no interpolations).
Note that styled.div or any other of the Styled CSS methods already do the same work as css. And these methods accept functions as arguments (instead of interpolated strings). So you can take it a step further and do this:
const Baz = styled.div((props) => css`
border-radius: 50%;
display: flex;
width: ${props.size};
height: ${props.size};
color: ${props.bgColor};
background-color: ${props.bgColor};
`);

Is there a way to modify '&:before/:after' to give a dynamic property?

A styled-component that has &:before attribute that I want to give access to a dynamic color property. But I'm lost to how to apply that.
I've Tried passing the props to <Hover /> and it works. But the Triangle in &:before cannot access that.
const Hover = styled.div`
padding:7px;
margin: -102px auto;
border-style: solid;
border-radius: 15px;
border-width: 3px;
text-align: left;
font-weight: 400;
font-size: 19px;
color: #000000;
font-family: sans-serif;
position: absolute;
left:-15px;
z-index:10;
&:before {
content: "";
width: 0px;
height: 0px;
position: absolute;
border-left: 10px solid ;
border-right: 10px solid transparent;
border-top: 10px solid ;
border-bottom: 10px solid transparent;
left: 19px;
bottom: -19px;
z-index:10;
}
`;
As a single styled-component the following class is:
class MarkerHover extends Component {
render() {
const {color, children}= this.props
return (
<Hover style={{backgroundColor: color }}>
{...children}
</Hover>
);
}
}
export default MarkerHover;
I expect to have a whole colored Window after successfully passing the color props to the &:before division.
As the documentation of Styled Components states (see https://www.styled-components.com/docs/basics#passed-props) you can read passed props:
const Hover = styled.div`
color: ${props => props.color};
`;
when you pass them like this:
<Hover color="#f00" />
You can use that same prop in your pseudo-element as well:
const Hover = styled.div`
color: ${props => props.color};
&::before {
background: ${props => props.color};
}
`;
So don't use the style attribute on your styled component, but pass regular props.
If you want to apply a CSS rule only after a certain condition, you can do that like this:
const Hover = styled.div`
color: #ccc;
&::before {
content: "";
padding: 16px;
${props => props.withBg ? `background-color: ${props.withBg}` : ''}
}
`;
With background:
<Hover withBg="#f00" />
Without background:
<Hover />

Resources