Reactjs Styled-components not working properly - css

I have a styled checkbox and I want to set its background to white when checked:false and to green when checked:true
But the problem is background color is always staying green and i dont know why.
The react component in which I'm using this checkbox component is acting right,Its just background color that i cant adjust
Any suggestions please?
const CheckboxContainer = styled.div`
display: inline-block;
vertical-align: middle;
`
const Icon = styled.svg`
fill: none;
stroke: white;
stroke-width: 2px;
`
const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
border: 0;
clip: rect(0 0 0 0);
clippath: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
`
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: #ffffff;
border-radius: 3px;
transition: all 150ms;
margin-top: 10px;
${HiddenCheckbox}:focus + & {
background: ${({ checked }) => (checked ? '#06ba63' : '#ffffff')};
}
${Icon} {
visibility: ${({ checked }) => (checked ? 'visible' : 'hidden')};
}
`
const Checkbox = ({ className, checked, ...props }) => (
<CheckboxContainer className={className}>
<HiddenCheckbox checked={checked} {...props} />
<StyledCheckbox checked={checked}>
<Icon viewBox="0 0 24 24">
<polyline points="20 6 9 17 4 12" />
</Icon>
</StyledCheckbox>
</CheckboxContainer>
)
export default Checkbox

you should check for checked instead of focus of the checkbox :
${HiddenCheckbox}:checked + & {
background: ${({ checked }) => (checked ? '#06ba63' : '#ffffff')};
}

If both Hidden and Styled checkboxes are get the same value from checked, you only need to use the value of checked passed to StyledCheckbox (like you do with Icon):
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: #ffffff;
border-radius: 3px;
transition: all 150ms;
margin-top: 10px;
background: ${({ checked }) => (checked ? '#06ba63' : '#ffffff')};
${Icon} {
visibility: ${({ checked }) => (checked ? 'visible' : 'hidden')};
}
`
The other option is only to apply the style if HiddenCheckbox is checked via CSS selectors:
const StyledCheckbox = styled.div`
display: inline-block;
width: 16px;
height: 16px;
background: #ffffff;
border-radius: 3px;
transition: all 150ms;
margin-top: 10px;
background: white;
${Icon} {
visibility: hidden;
}
${HiddenCheckbox}:checked + & {
background: #06ba63;
}
${HiddenCheckbox}:checked + & > ${Icon} {
visibility: visible;
}
`

Related

transform shows old and new position instead of only new one

the transform works fine, but not only the new positions of the dashes are shown also the old positions are shown on click.
i want the horizontal dashes to disappear on transform.
open === false
open === true
what I expect on open === true
const StyledBar = styled.span`
width: 2rem;
height: 0.25rem;
background: ${({ theme }) => theme.colors.primary};
border-radius: 10px;
transition: all 0.3s linear;
position: relative;
transform-origin: 1px;
`;
const StyledBurgerButton = styled.button`
position: absolute;
top: 24px;
right: 24px;
display: flex;
flex-direction: column;
justify-content: space-around;
width: 2rem;
height: 2rem;
background: transparent;
border: none;
cursor: pointer;
padding: 0;
z-index: 10;
${StyledBar} {
:first-child {
transform: ${({ open }) => (open ? "rotate(45deg)" : "rotate(0)")};
}
:nth-child(2) {
opacity: ${({ open }) => (open ? "0" : "1")};
transform: ${({ open }) => (open ? "translateX(20px)" : "translateX(0)")};
}
:nth-child(3) {
transform: ${({ open }) => (open ? "rotate(-45deg)" : "rotate(0)")};
}
}
`;
const BurgerButton = ({ open, setOpen }) => {
return (
<StyledBurgerButton open={open} onClick={() => setOpen(!open)}>
<StyledBar />
<StyledBar />
<StyledBar />
</StyledBurgerButton>
);
};

How to change CSS of a child component from parent component

I have a react component called TeamBanner like so
import styles from "./teamBanner.module.css";
import { useNavigate } from "react-router-dom";
import { TeamBannerProps } from "../ProjectTypes.types";
export const TeamBanner = ({
team,
onBannerClick,
showName,
}: TeamBannerProps) => {
let navigate = useNavigate();
const handleOnClick = () => {
navigate(`/${team}`);
onBannerClick();
};
return (
<div
className={`${styles.container} flex theme-light ${
showName ? "" : styles["no-name"]
}`}
title={team}
onClick={handleOnClick}
>
<div>
<img
src={require(`../logos/${team}.svg`)}
alt="logo"
className={`${styles.logo} ${showName ? "" : styles["only-logo"]}`}
/>
</div>
{showName ? team : ""}
</div>
);
};
The teamBanner.module.css file is like so
.container {
margin: 0.25em;
align-items: center;
cursor: pointer;
top: 0;
z-index: 2;
background-color: hsl(var(--color-background));
padding: 0.1em 0.25em;
border-radius: 0.5em;
box-shadow: 0.05rem 0.05rem 0.2rem rgb(0 0 0 / 50%);
font-family: var(--ff-serif);
font-size: var(--fs-200);
}
.logo {
display: inline-block;
vertical-align: middle;
width: 3vmax;
height: 3vmax;
}
.container:hover {
outline: 0.15em solid hsl(240, 90%, 67%);
}
.no-name {
border-radius: 0.25em;
}
.only-logo {
width: 5vmax;
height: 5vmax;
}
I am using it to create a list of team of a page & this works just fine.
However, I want to use it in another place also. In the place, the to now want the .container:hover effect & I want the font properties of the .contaner and width & height properties of the .logo changed.
How can I accomplish this? only changing these items of the module.css? Can any one help? Thanks

:Active works in styled components but :checked doesn't

I am attempting to transition from css to styled components. I am creating a toggle switch component. The animation on the :active class works when the switch is checked but, when the checkbox is checked the switch does not move to the left like it would with just normal css.
import React from 'react';
import styled from 'styled-components'
const Button = styled.span`
content: '';
position: absolute;
top: 3.7px;
left: 5px;
width: 42px;
height: 42px;
border-radius: 45px;
transition: 0.2s;
background: #fff;
box-shadow: 0 0 2px 0 rgba(10, 10, 10, 0.29);
`;
const Label = styled.label`
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
width: 100px;
height: 50px;
background: dodgerblue;
border-radius: 100px;
position: relative;
transition: background-color 0.2s;
&:active ${Button} {
width: 50px;
}
`;
const Input = styled.input`
height: 0;
width: 0;
visibility: hidden;
&:checked ${Button} {
left: calc(100% - 5px);
transform: translateX(-100%);
}
`;
const Switch = ({ isOn, handleToggle, onColor }) => {
return (
<>
<Input
checked={isOn}
onChange={handleToggle}
className='react-switch-checkbox'
id={`react-switch-new`}
type='checkbox'
/>
<Label
style={{ background: isOn && onColor }}
className='react-switch-label'
htmlFor={`react-switch-new`}>
<Button className={`react-switch-button`} />
</Label>
</>
);
};
export default Switch;
Your problem is not related to styled components. The rule &:checked ${Button} { assumes the Button is a child of Input, but it's actually a child of Input's sibling Label.
Update the styled component rule to:
&:checked + ${Label} ${Button} {
left: calc(100% - 5px);
transform: translateX(-100%);
}
Sandbox

React: Caret icon keeps moving outside of dropdown menu whenever page is resized to iPad or smaller

I have a styled react component that features a hand rolled dropdown menu including a caret icon and every time I test the responsiveness of the dropdown the caret icon pops outside. I've wrapped the dropdown inside of a larger component to place everything on the same line and then included the icon inside of the styled select component. I've used this methodology in another app and the dropdown is completely responsive and doesn't have any issues with icon placement so I'm completely scratching my head with this one. Any help/suggestions would be great
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { FaCaretDown } from 'react-icons/fa'
import Table from '../core/Table'
import SubHeader from '../elements/SubHeader'
import EligibleOffer from './EligibleOffer'
import { tableDataProps, tableColumnProps } from '../../propTypes'
const BUNDLE_KEY = 'bundle'
const rowKey = 'id'
class OfferBundlesTable extends Component {
static propTypes = {
bundles: PropTypes.arrayOf(tableDataProps),
columns: tableColumnProps,
viewedOfferIds: PropTypes.arrayOf(PropTypes.string),
onBundleSelect: PropTypes.func
}
static defaultProps = {
viewedOfferIds: []
}
renderCell = (cell = {}) => (
<div>
{cell.title && <SubHeader>{cell.title}</SubHeader>}
<p>{cell.details}</p>
</div>
)
renderBundle = bundle => (
<StyledOffer
offer={bundle}
onOfferSelect={this.props.onBundleSelect}
viewed={this.props.viewedOfferIds.includes(bundle.OfferId)}
/>
)
getColumns = columns =>
Object.entries(columns).reduce(
(cols, [key, value]) => ({
...cols,
[key]: {
...value,
renderCell: key === BUNDLE_KEY ? this.renderBundle : this.renderCell
}
}),
{}
)
render() {
const { bundles = [], columns = [] } = this.props
return (
<div>
<DropdownRow>
<StyledSelect>
<StyledStrong>SORT BY: </StyledStrong>
<select>
<option>Default</option>
<option>Promo Price (High to Low)</option>
<option>Promo Price (Low to High)</option>
<option>Alphabetically (A-Z)</option>
<option>Alphabetically (Z-A)</option>
<option>Internet Tier (High to Low)</option>
<option>Internet Tier (Low to High)</option>
</select>
<StyledSelectIcon size="1.5rem" />
</StyledSelect>
<StyledSelect>
<StyledStrong>CONTRACT TERMS (MONTHS):</StyledStrong>
<select>
<option>36</option>
<option>24</option>
<option>12</option>
</select>
<StyledSelectIcon size="1.5rem" />
</StyledSelect>
</DropdownRow>
<StyledTable
rowKey={rowKey}
rows={bundles}
columns={this.getColumns(columns)}
/>
</div>
)
}
}
const StyledTable = styled(Table)`
th:first-child {
width: 25%;
}
`
const StyledOffer = styled(EligibleOffer)`
margin-bottom: 15px;
`
const DropdownRow = styled.div`
th:first-child {
width: 25%;
}
flex: 1;
display: flex;
align-items: stretch;
padding: 20px 30px 20px 10px;
width: 60%;
margin-left: 42%;
`
const StyledSelectIcon = styled(FaCaretDown)`
position: absolute;
top: 0;
right: 10px;
bottom: 0;
margin: auto;
color: black;
pointer-events: none;
`
const StyledSelect = styled.div`
position: relative;
margin-left: auto;
select {
padding: 5px 45px 5px 10px;
font-size: 1.2rem;
line-height: 1.4rem;
font-weight: 500;
color: black;
border: 2px solid black;
height: 36px;
appearance: none;
background: transparent;
border-radius: 0;
}
`
const StyledStrong = styled.strong`
padding-right: 5px;
`
export default OfferBundlesTable
After looking at your code, I would suggest an alternative approach, including the caret in the css for your select. This makes it impossible for it to move away from the select, as it's now part of it. Run the snippet below to see it work--of course you would want to put that stuff into the styled-component css definition, and remove the <StyledSelectIcon/> altogether (here's a forked stackblitz).
Styled component definition:
const StyledSelect = styled.div`
position: relative;
margin-left: auto;
select {
padding: 5px 45px 5px 10px;
font-size: 1.2rem;
line-height: 1.4rem;
font-weight: 500;
color: black;
border: 2px solid black;
height: 36px;
background-color: transparent;
border-radius: 0;
-moz-appearance: none;
-webkit-appearance:none;
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg fill="black" viewBox="0 0 320 512" height="1.5rem" width="1.5rem" xmlns="http://www.w3.org/2000/svg"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"></path></svg>');
background-repeat: no-repeat;
background-position: right 8px center;
}
`
Snippet to run here:
.customselect{
padding: 5px 45px 5px 10px;
font-size: 1.2rem;
line-height: 1.4rem;
font-weight: 500;
color: black;
border: 2px solid black;
height: 36px;
background-color: transparent;
border-radius: 0;
-moz-appearance: none;
-webkit-appearance:none;
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg fill="black" viewBox="0 0 320 512" height="1.5rem" width="1.5rem" xmlns="http://www.w3.org/2000/svg"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"></path></svg>');
background-repeat: no-repeat;
background-position: right 8px center;
}
<select class="customselect">
<option>Option A</option>
<option>Option B</option>
<option>Option C</option>
<option>Option D</option>
</select>

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