Using Rebass/Forms in react and I cannot properly resize the Switch ponent using styles properly. (I also am using @emotion/styled
)
I have tried using a size
attribute, but that does not give the desired effect of simply changing the scale of the switch.
I have tried using the sx
property and giving it a width
and a height
, but that only resizes the button element and not the inner div which is the "sliding dot"
I know that I could write some styling targeting the inner div itself, but I would like to find a way to give it a height and width a single time and it apply to both the button and the inner div.
<Switch
sx={{ width: "30px", height: "15px" }}
/>
Using Rebass/Forms in react and I cannot properly resize the Switch ponent using styles properly. (I also am using @emotion/styled
)
I have tried using a size
attribute, but that does not give the desired effect of simply changing the scale of the switch.
I have tried using the sx
property and giving it a width
and a height
, but that only resizes the button element and not the inner div which is the "sliding dot"
I know that I could write some styling targeting the inner div itself, but I would like to find a way to give it a height and width a single time and it apply to both the button and the inner div.
<Switch
sx={{ width: "30px", height: "15px" }}
/>
https://codesandbox.io/s/styling-rebass-switch-uu7wg
Share Improve this question edited Jul 16, 2020 at 23:19 Daniele Ricci 15.8k1 gold badge31 silver badges61 bronze badges asked Apr 28, 2020 at 21:51 amasteramaster 2,1635 gold badges28 silver badges55 bronze badges 2-
also looking at help applying color styles.
bg
andcolor
don't have the effect you would assume. – amaster Commented Jul 16, 2020 at 22:34 -
What's your expectation regarding
color
? – sudo bangbang Commented Jul 21, 2020 at 7:52
5 Answers
Reset to default 4 +250It is not possible to do what you want 'out of the box' because the height and width are hard coded in the source
Luckily the internals of rebass
are very nice, so it's possible to create your own with a little copy-pasta from the rebass source code.
import React from "react";
import { Box } from "reflexbox";
export const ResizableSwitch = ({
checked,
height = 24,
width = 40,
...props
}) => (
<Box
as="button"
type="button"
role="switch"
tx="forms"
variant="switch"
aria-checked={checked}
{...props}
__css={{
appearance: "none",
m: 0,
p: 0,
width,
height,
color: "primary",
bg: "transparent",
border: "1px solid",
borderColor: "primary",
borderRadius: 9999,
"&[aria-checked=true]": {
bg: "primary"
},
":focus": {
outline: "none",
boxShadow: "0 0 0 2px"
}
}}
>
<Box
aria-hidden
style={{
transform: checked ? `translateX(${width - height}px)` : "translateX(0)"
}}
sx={{
mt: "-1px",
ml: "-1px",
width: height,
height,
borderRadius: 9999,
border: "1px solid",
borderColor: "primary",
bg: "background",
transitionProperty: "transform",
transitionTimingFunction: "ease-out",
transitionDuration: "0.1s",
variant: "forms.switch.thumb"
}}
/>
</Box>
);
https://codesandbox.io/s/styling-rebass-switch-r6tmx?file=/src/App.js
Unfortunately, you can't. I took a deep look into the package itself and it seems like there is no fixed rule for the ponents written in this package. Some ponent do get the props
and the sx
. But there are ponents, like switch
that hosts another ponent as a children, and no prop passed to it.
If you take a look at the implementation of switch
in this page here:
export const Switch = forwardRef(({
checked,
...props
}, ref) =>
<Box
ref={ref}
as='button'
type='button'
role='switch'
tx='forms'
variant='switch'
aria-checked={checked}
{...props}
__css={{
appearance: 'none',
m: 0,
p: 0,
width: 40,
height: 24,
color: 'primary',
bg: 'transparent',
border: '1px solid',
borderColor: 'primary',
borderRadius: 9999,
'&[aria-checked=true]': {
bg: 'primary',
},
':focus': {
outline: 'none',
boxShadow: '0 0 0 2px'
},
}}>
<Box
aria-hidden
style={{
transform: checked ? 'translateX(16px)' : 'translateX(0)',
}}
sx={{
mt: '-1px',
ml: '-1px',
width: 24,
height: 24,
borderRadius: 9999,
border: '1px solid',
borderColor: 'primary',
bg: 'background',
transitionProperty: 'transform',
transitionTimingFunction: 'ease-out',
transitionDuration: '0.1s',
variant: 'forms.switch.thumb',
}}
/>
</Box>
)
There are 2 Box
ponents (which are the base ponent of the package), one is a children of the other. The first Box
is the area of the switch, and the child Box
is the circle/button you are looking for, If you take a look on this ponent, you will see there is no outside variable that passed to it, so nothing can be changed - the style is already written.
This is the Button/Circle ponent:
<Box
aria-hidden
style={{
transform: checked ? 'translateX(16px)' : 'translateX(0)',
}}
sx={{
mt: '-1px',
ml: '-1px',
width: 24,
height: 24,
borderRadius: 9999,
border: '1px solid',
borderColor: 'primary',
bg: 'background',
transitionProperty: 'transform',
transitionTimingFunction: 'ease-out',
transitionDuration: '0.1s',
variant: 'forms.switch.thumb',
}}
/>
If you are still willing to use that package, you can overe this by overwriting the css, giving the ponent a className and apply styling to its children.
Plus, you can open an issue or suggest a fix on the package github repository.
Looking at Switch source code it seems no properties are propagated to the inner <div>
... would you open an issue?
In the mean while you can set css properties for children and/or based on attributes:
.myswitch {
width: 30px !important;
height: 15px !important;
background: gray !important;
}
.myswitch[aria-checked="true"] {
background: red !important;
}
.myswitch div {
width: 15px;
height: 15px;
background: red;
}
then:
<Switch className="myswitch" />
https://codesandbox.io/s/styling-rebass-switch-o0j8t
You could use CSS transform scale to scale down/up an element and it's children. As, you're using emotion, here's something that goes along with it.
scale documentation
CodeSandbox: https://codesandbox.io/s/styling-rebass-switch-5fqku?file=/src/App.js
import React, { useState } from "react";
import styled from "@emotion/styled";
import { Label, Checkbox, Switch } from "@rebass/forms";
const Title = styled.h1`
text-align: center;
`;
const FormLabel = styled(Label)`
align-items: center;
`;
const Control = styled.div`
width: 40px;
`;
const Toggle = styled(Switch)`
transform: scale(.7)
`;
export default function App() {
const [switched, setSwitched] = useState(false);
const toggleSwitch = () => {
setSwitched(!switched);
};
return (
<div className="App">
<Title>How to Style Rebass/Forms Switch</Title>
<FormLabel sx={{ padding: "10px" }}>
<Control>
<Checkbox size="16px" sx={{ marginLeft: "10px" }} />
</Control>
CheckBox
</FormLabel>
<FormLabel sx={{ padding: "10px" }}>
<Control>
<Toggle
checked={switched}
onClick={() => toggleSwitch()}
/>
</Control>
Switch
</FormLabel>
</div>
);
}
https://codesandbox.io/s/styling-rebass-switch-zto4z?file=/src/styles.css:37-1020
button.switch,
button.switch:hover,
button.switch:focus {
outline: none;
border: 1px solid grey;
box-shadow: none;
}
button.switch > div {
content: "";
width: 14px;
height: 14px;
background-color: #9fa2ab;
}
button.switch > div:after {
content: "";
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
left: -2px;
top: -3px;
transition: left 0.3s ease, background-color 0.3s ease, box-shadow 0.1s ease,
transform 0.1s ease;
background-color: #5b5c60;
-webkit-box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),
0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2),
0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
}
button.switch[aria-checked="true"] {
background-color: #d0f35e;
}
button.switch[aria-checked="true"] > div:after {
background-color: #86af00;
}
button.switch[aria-checked="false"] {
background-color: #ffffff;
left: 18px;
}
Add class switch
<Switch
className="switch"
sx={{ width: "30px", height: "15px" }}
checked={switched}
onClick={() => toggleSwitch()}
/>