最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to style Rebass Switch - Stack Overflow

programmeradmin3浏览0评论

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 and color 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
Add a ment  | 

5 Answers 5

Reset to default 4 +250

It 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()}
          />
发布评论

评论列表(0)

  1. 暂无评论