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

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

Related

Positioning a button on top of another item in css styled components

i am trying to get a button to overlap and be positioned at a certain place of a searchbar. Here is what it currently looks like. I tried using the transform: translateX() property but when I add a :hover element, the button gets moved back to its original position before the transform: translateX() event
But this is what i want it to look like.
Here is my css code. I'm using styled components.
const Button = styled.button`
display:block;
width:40px;
height:40px;
/* line-height:80px; */
border: none;
border-radius: 50%;
color:#f5f5f5;
text-align:center;
text-decoration:none;
background: #0072FF;
box-shadow: 0 0 3px gray;
font-size:20px;
font-weight:bold;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
transform: translateX(-180%);
&:hover {
outline: none;
transform: scale(1.05);
}
`;
const Input = styled.input`
color: inherit;
border : none;
background: none;
padding-left: 10px;
width: 100%;
&:focus {
outline : none;
}
`;
const Form = styled.form`
background-color : white;
border-radius : 7px;
height: 5%;
width: 75%;
align-items: center;
display: flex;
transition: all .3s;
margin-left: 6%;
`;
const Img = styled.img`
height: 15px;
width: 15px;
margin-left: 10px;
`
And here are my components.
import React from 'react'
import NewChat from '../newChat/newChat';
import { Input, Form, Img } from './searchBar.elements';
function SearchBar() {
return (
<Form>
<Img src={require("../../../../assets/img/search.svg")} />
<Input placeholder="Find people and conversations" />
<NewChat />
</Form>
)
}
export default SearchBar;
import React from 'react'
import { Button} from './newChat.elements';
import plus from '../../../../assets/img/plus_button.svg';
function NewChat() {
return (
<div>
{/* <img src={require("../../../../assets/img/plus_button.svg")} /> */}
<Button>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 4C11.4477 4 11 4.44772 11 5V11H5C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13H11V19C11 19.5523 11.4477 20 12 20C12.5523 20 13 19.5523 13 19V13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H13V5C13 4.44772 12.5523 4 12 4Z"
fill="currentColor"
/>
</svg>
</Button>
</div>
)
}
export default NewChat;
First for the i am trying to get a button to overlap and be positioned at a certain place of a searchbar maybe you should use position: relative on your father component (Form) and position: absolute on your Button, with that your father becomes a parameter for the absolute position of the button so you can use the properties Left, Top, Right, Bottom to get it to the correct location.
const Form = styled.form`
...otherProperties,
position: relative;
`;
const Button = styled.button`
...otherProperties,
position: absolute,
right: 180px // for example
top: 50px // example
`;
Second for the when I add a :hover element, the button gets moved back to its original position before the transform: translateX() event I think it is because you change the TRANSFORM property inside Button.
const Button = styled.button`
display:block;
width:40px;
height:40px;
/* line-height:80px; */
border: none;
border-radius: 50%;
color:#f5f5f5;
text-align:center;
text-decoration:none;
background: #0072FF;
box-shadow: 0 0 3px gray;
font-size:20px;
font-weight:bold;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
transform: translateX(-180%); // Here the transform property is translateX //
&:hover {
outline: none;
transform: scale(1.05); // Here the transform property translateX now becomes scale //
`;
To correct this you need to make the hover property not lose the previous one.
&:hover {
outline: none;
transform: translateX(-180%) scale(1.05);
}

Why isn't my custom CSS transition working when updating the state of a parent React component?

I'm using functional components with hooks (state hooks mostly) and when I update the "local" state (state within the same component) my custom css transitions works fine. However, when I move this state to the parent component and update it from the child component the css transistion will not work. Instead the changes will render immediately. What am I doing wrong?
I have tried to search for a while but without success. I am making a navbar with a burger menu that will slide in a navigation list from the right when klicked.
This below will not work
import React, { useState, useEffect } from "react";
import "./navbar.css";
const Navbar = (props) => {
const [scrollState, setScrollState] = useState("top");
const [burgerOpen, setBurgerOpen] = useState(false); //updating this state transistions does not work!
const Burger = () => {
//const [open, setOpen] = useState(false); //updating this state transistions work!
const handleKeyDown = (event) => {
if (event.key === "Enter") {
setBurgerOpen(!burgerOpen);
}
};
return (
<>
<div className={"btn-container"} onKeyDown={handleKeyDown}>
<div
className={burgerOpen ? "navbar-btn open" : "navbar-btn"}
open={burgerOpen}
onClick={() => setBurgerOpen(!burgerOpen)}
>
<div className="navbar-burger" />
</div>
</div>
<Menu open={burgerOpen} key="menu" />
</>
);
};
const Menu = ({ open }) => {
return (
<ul className={open ? "open" : ""}>
<li>
<a to="/projects">Projects</a>
</li>
<li>
<a to="/design">Design</a>
</li>
<li>
<a to="/contact">Contact</a>
</li>
</ul>
);
};
return (
<nav
className={`navbar ${scrollState === "top" ? "dark-nav" : "light-nav"}`}
>
<Burger />
</nav>
);
};
Navbar.propTypes = {};
export default Navbar;
This however works but I need to move the state to the parent because of rerenders that I might add (e.g. I want to keep the menu open...)
import React, { useState, useEffect } from "react";
import "./navbar.css";
const Navbar = (props) => {
const [scrollState, setScrollState] = useState("top");
//const [burgerOpen, setBurgerOpen] = useState(false); //updating this state transistions does not work!
const Burger = () => {
const [open, setOpen] = useState(false); //updating this state transistions work!
const handleKeyDown = (event) => {
if (event.key === "Enter") {
open(!open);
}
};
return (
<>
<div className={"btn-container"} onKeyDown={handleKeyDown}>
<div
className={open ? "navbar-btn open" : "navbar-btn"}
open={open}
onClick={() => setOpen(!open)}
>
<div className="navbar-burger" />
</div>
</div>
<Menu open={open} key="menu" />
</>
);
};
const Menu = ({ open }) => {
return (
<ul className={open ? "open" : ""}>
<li>
<a to="/projects">Projects</a>
</li>
<li>
<a to="/design">Design</a>
</li>
<li>
<a to="/contact">Contact</a>
</li>
</ul>
);
};
return (
<nav
className={`navbar ${scrollState === "top" ? "dark-nav" : "light-nav"}`}
>
<Burger />
</nav>
);
};
Navbar.propTypes = {};
export default Navbar;
And the CSS:
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
z-index: 999;
width: 100%;
top: 0;
padding: 0.7rem 2rem;
height: 170px;
transition: all 0.8s ease;
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0.815) 50%,
rgba(0, 0, 0, 0.35) 50%
)
right;
background-size: 100% 200%;
background-position: top;
}
.light-nav {
/*background-color: rgba(0, 0, 0, 0.35);*/
background-position: bottom;
}
.navbar ul {
display: flex;
flex-flow: row nowrap;
}
.navbar li {
padding: 10px 10px;
font-size: 20px;
}
.navbar a {
background: linear-gradient(to right, royalblue, royalblue 50%, #f4f4f4 50%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 200% 100%;
background-position: 100%;
transition: background-position 0.5s ease;
}
.navbar a:hover {
background-position: 0 100%;
}
.navbar-logo {
height: auto;
width: 200px;
padding: 2px;
}
.navbar-logo img {
max-width: 100%;
max-height: 100%;
}
.navbar-btn {
visibility: hidden;
display: none;
position: relative;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.5s ease-in-out;
width: 50px;
height: 50px;
padding-top: 10px;
}
.btn-container:focus > .navbar-btn {
border: #f4f4f4 1px solid;
}
.btn-container:focus,
.navbar-btn:focus {
border: none;
}
.navbar-burger {
width: 35px;
height: 3px;
background: #f4f4f4;
border-radius: 5px;
transition: all 0.5s ease-in-out;
}
.navbar-burger::before,
.navbar-burger::after {
content: '';
position: absolute;
width: 35px;
height: 3px;
background: #f4f4f4;
border-radius: 5px;
transition: all 0.5s ease-in-out;
}
.navbar-burger::before {
transform: translateY(-10px);
}
.navbar-burger::after {
transform: translateY(10px);
}
.navbar-btn.open .navbar-burger {
transform: translateX(-50px);
background: transparent;
}
.navbar-btn.open .navbar-burger::before {
transform: rotate(45deg) translate(35px, -35px);
}
.navbar-btn.open .navbar-burger::after {
transform: rotate(-45deg) translate(35px, 35px);
}
.navbar-btn:hover .navbar-burger,
.navbar-btn:hover .navbar-burger::before,
.navbar-btn:hover .navbar-burger::after {
background: royalblue;
}
.navbar-btn.open:hover .navbar-burger {
background: transparent;
}
#media (max-width: 768px) {
.navbar {
height: 110px;
}
.navbar-logo {
width: 140px;
}
.navbar ul {
flex-flow: column nowrap;
position: fixed;
top: 110px;
right: 0;
height: 100vh;
width: 200px;
transform: translateX(100%);
transition: 0.5s ease-in-out;
background-color: rgba(0, 0, 0, 0.815);
}
.navbar ul li {
transform: translateX(100%);
transition: 0.5s ease-in-out;
}
.navbar ul.open li {
transform: translateX(0);
}
.navbar ul li:nth-child(1) {
transition-delay: 0.1s;
}
.navbar ul li:nth-child(2) {
transition-delay: 0.2s;
}
.navbar ul li:nth-child(3) {
transition-delay: 0.3s;
}
.navbar ul.open {
transform: translateX(0);
}
.navbar-btn {
visibility: visible;
display: flex;
padding-bottom: 10px;
}
}
Here is a sandbox of the working example but I need to move out the state of the Burger (resize for burger menu):
Working example (resize!)

Reactjs Styled-components not working properly

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;
}
`

React styled component animation not working

I am trying to create an animation for form textfield and label.
I want to be able to change the position of LabelText to top of the TextBox component.
Please let me know what I am missing on, My entered text and label are overlapping i.e. label is not animated.
Request help!
Styled components:
export const TextBox = styled.input`
display: flex;
width: 100%;
height: 2em;
border-radius: 5px;
border: none;
background: ${styles.backgroundGradient};
`;
export const LabelText = styled.label`
position: absolute;
top: 0;
left: 0;
font-size: 18px;
line-height: 16px;
pointer-events: none;
transition: 0.5s;
color: ${styles.formContentColor};
${TextBox}:focus ~ &,
${TextBox}:not([value=""]) ~ & {
top: -1.5em;
left: 0;
color: ${styles.formContentColor};
font-size: 20px;
}
`;
export const InputWrapper = styled.div`
position: relative;
width: 60%;
`;
Order of placement in components
<InputWrapper>
<LabelText>Product Name</LabelText>
<TextBox type="text" />
</InputWrapper>
I read through comment by #mosh feu and other answers on stackoverflow.
Detect if an input has text in it using CSS -- on a page I am visiting and do not control?
There are couple of approaches that can serve the purpose i.e. to check whether input field is empty or not drafting them below for future refrrence.
input:not(:placeholder-shown):
input:not([value=""]):
In my case input is value of TextBox.
export const LabelText = styled.label`
position: absolute;
top: 1.5em;
left: 0;
font-size: 18px;
line-height: 16px;
pointer-events: none;
transition: 0.6s;
color: ${styles.formContentColor};
${TextBox}:focus ~ &,
${TextBox}:not(:placeholder-shown) ~ & {
top: 0;
left: 0;
color: ${styles.formContentColor};
font-size: 16px;
}
`;

material-ui icon button highlights with an elliptical background when cursor is hovered over it

IconButton in #material-ui/core/IconButton is showing a weird elliptical background when I hover the cursor over it.
I thought it is a mistake by me, so I just copied the code from material-ui website, but the problem remains.
However, when I created new react project, and created an icon button in it, the background was the usual circle.
I'm new to react and can't figure out what is going on, I'm using icon button without any explicit styling,
App.js
import React, { Component } from 'react';
import './App.css';
import { IconButton } from '#material-ui/core';
import WorkIcon from '#material-ui/icons/Work';
import CssBaseline from '#material-ui/core/CssBaseline';
class App extends Component {
render() {
return (
<div>
<CssBaseline />
<IconButton>
<WorkIcon />
</IconButton>
</div>
);
}
}
export default App;
App.css
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
#keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.MuiCardContent-root-29 {
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 500px;
height: 300px;
margin: auto;
background-color: #f3f3f3;
}
.login {
margin-top: 50px;
margin-left: 50px;
}
.form-group {
margin-bottom: 35px;
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from "react-redux";
import './index.css';
import App from './App';
import store from "./store/index";
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
index.css
body {
background-color : #484848;
margin: 0;
padding: 0;
}
h1 {
color : #000000;
text-align : center;
font-family: "SIMPSON";
}
form {
width: 300px;
margin: 50px auto;
}
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
opacity: 0.9;
width: 100px;
}
.tableHeader {
background-color: green !important;
}
.header {
color: green;
font-weight: bold;
}
.edit {
height: 30px;
cursor: pointer;
}
.delete {
height: 20px;
cursor: pointer;
padding-left: 10px;
}
This problem persists in my whole project wherever I use icon buttons, and not just with this file only. And when I use this same file in a new project it works as expected: No elliptical backgrounds.
EDIT:
The accepted answer works well. In my also case I tried setting the width in button of index.css to auto and it fixed the error too.
This is what I did to remove the elliptical shape:
<IconButton style={{borderRadius: 0}}>
<DeleteIcon/>
</IconButton>
Now, it will be a rectangular shape when hovered.
I don't know why the above two solutions didn't work for me. So I added margin and width to the parent element and padding-right to the child element in App.css.
//For the buttons on top
button.MuiButtonBase-root {
margin: 10px;
width: 50px;
}
button.MuiButtonBase-root span span {
padding-right: 50px;
}
//For the checkboxes
span.MuiButtonBase-root {
margin-left: 10px;
width: 45px;
}
span.MuiButtonBase-root span {
padding-right: 10px;
}
The problem is the button CSS in your index.css. It is setting the width of all buttons to 100px. IconButton is implemented via a button tag around the icon.
Fixing the look of IconButton is easy -- just remove that button CSS. The more tedious part is that presumably you want that button styling on all your other buttons.
One way to handle this is to change the following in index.css:
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
opacity: 0.9;
width: 100px;
}
to be a CSS class rather than targeting all buttons:
.standard-button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
opacity: 0.9;
width: 100px;
}
and then change places where you are rendering button elements to use:
<button className="standard-button">
instead of just <button>.
This worked for me
<IconButton style={{height:"45px",marginTop:"20px"}}>
<DeleteIcon/>
</IconButton>
Hi you can override ripple root and child style to change border radius or background color.
const useStyles = makeStyles({
root: {
borderRadius: 0, // <---- icon button root style
'.MuiTouchRipple-ripple .MuiTouchRipple-child': { // <----this should change ripple style when clicked or touched
borderRadius: 0,
backgroundColor: 'red'
},
},
});
<IconButton className={classes.rippleRoot}>
<WorkIcon />
</IconButton>
OR MUI5 with sx props
<IconButton
sx={{
borderRadius: 0,
'.MuiTouchRipple-ripple .MuiTouchRipple-child': {
borderRadius: 0,
backgroundColor: 'red',
},
}}
>
<WorkIcon />
</IconButton>
use height and width with same value to have circular shade on hover-
<IconButton sx={{height:"40px",width:"40px"}}>
<WorkIcon />
</IconButton>

Resources