개발경험

React + styled-components 재사용 Toggle 버튼

양찬우 2021. 10. 18. 20:49
728x90

생성된 토글버튼

이슈

현재 개발에 참여 중인 MATATA 서비스의 프론트엔드 업무 중 새로운 요구사항을 전달받았다. 

디자인 변경으로 인해, 현재 기본 checkbox input을 애니메이션이 추가된 Toggle 버튼으로 바꿔야 했다.

디자인/기획 담당이 원하는 토글 버튼 디자인의 예시로 보내준 링크를 보니, 다음과 같았다. 

 

29 Unique CSS Toggle Switches – FrontEnd Resource

Here is a collection of some of the most unique CSS toggle switches for you. You may also like Pure CSS Toggles Dev: Rafael González Pure CSS Toggle Buttons | ON-OFF Switches Dev: Himalaya Singh Pure CSS toggle buttons Dev: Mauricio Allende Pure CSS iOS S

frontendresource.com

예시 토글

예시가 많아 기획자와 상담 후 요구사항을 정리해보니, 다음과 같았다. 

  1. 현재 원형 체크박스 형태인 토글버튼을 타원형(pill)으로 생성
  2. 선택 위치를 나타내는 원형 오브젝트 외의 공간에 선택한 텍스트 표시
  3. 동작 애니메이션 추가 
  4. 선택에 따른 색상 변경  
  5. 재사용 컴포넌트로 생성

처음엔 보내준 링크의 생성된 토글 버튼이나 외부 라이브러리를 통해 약간의 수정만 할 생각이었으나, 한 번도 직접 토글 버튼을 생성해 본 경험이 없다는 점이 생각났고, 현재 프로젝트에서 사용 중인 styled-component를 jsx에 사용하는 형태를 위반하고 싶지 않아서 직접 토글 컴포넌트를 생성해보기로 결정했다. 

 

해결 및 결과

먼저 다른 개발자들이 생성한 토글 버튼의 형태를 살펴봤다. 대부분의 경우 input 태그에 ::before, ::after, content를 통해 토글버튼을 구성한 것을 확인했다. 기본적인 구조는 다음과 같다. 

  • 체크 X 
    • ::before : 체크 안 된 상태의 텍스트를 content로 입력, 토글의 왼쪽에 위치한다.    
    • ::after : 빈 content를 block 형태로 생성하고 토글의 오른쪽에 위치한다.
  • 체크 O(&:checked)
    • ::before : 체크 된 상태의 텍스트를 content로 입력, 토글의 오른쪽에 위치한다.
    • ::after : 빈 content를 block 형태로 생성, 토글의 왼쪽에 위치한다. 
<CheckBox type="checkbox"/>
/* css 기본 형태 */

.input { 
  /*체크 안된 상태*/
  ::before{
  	content:'텍스트1';
  }
  ::after{
	content:'';
	display:block;
  }
  
  /*체크된 상태*/
  &:checked{
  	::before{
  		content:'텍스트2';
  	}
  	::after{
  		content:'';
		display:block;
  	}
  }
}

Toggle component

해당 형태를 이해한 후, Toggle.js를 생성하고 부모 컴포넌트로부터 props로 한 개의 원 색상, onChange 시 변경값을 전달할 함수, 체크된 상태와 체크 안 된 상태의 각각 2개씩 텍스트 색상, 배경 색상을 받도록 설정했다. 

import React from 'react';
import styled from 'styled-components';

export default function Toggle({
  left,
  right,
  leftColor,
  rightColor,
  leftBgColor,
  rightBgColor,
  circleColor,
  setChecked,
}) {
  return (
    <Wrapper>
      <CheckBox
        left={left}
        right={right}
        leftColor={leftColor}
        rightColor={rightColor}
        leftBgColor={leftBgColor}
        rightBgColor={rightBgColor}
        circleColor={circleColor}
        onChange={() => setChecked()}
        type="checkbox"
      />
    </Wrapper>
  );
}

CSS Styled-component

props로 받은 색상을 styled-component에서 사용할 수 있도록 각 항목에서 설정하고, css 스타일, 애니메이션을 추가했다. 애니메이션의 경우 transition Mozilla 문서를 참고하여 ease-in-out으로 설정했다. 

  • props로 값을 할당할 때, content의 경우 기본적으로 값이 ' '에 감싸져 있어야 한다. 
  • transition의 경우 배경색, 텍스트, 원을 각각 설정해줘야 한다.
// checkbox wrapper
const Wrapper = styled.div`
  justify-content: center;
  align-items: center;
  display: flex;
  z-index: 0;
`;

const CheckBox = styled.input`
  z-index: 1;
  width: 5rem;
  height: 2rem;
  background: var(--second);
  background: ${(props) => props.leftBgColor ?? 'var(--grey)'};
  border-radius: 2em;
  /* 선택X 텍스트 */
  ::before {
    position: absolute;
    content: '${(props) => props.left ?? 'Yes'}';
    padding-left: 1em;
    width: 5rem;
    height: 2rem;
    display: flex;
    justify-content: flex-start;
    align-items: center;
    color: ${(props) => props.leftColor ?? 'var(--white)'};
    font-weight: var(--bold);
    font-size: var(--small);
    /* 텍스트 트랜지션 */
    transition: all 0.2s ease-in-out;
  }
  /* 선택X 원 */
  ::after {
    position: relative;
    content: '';
    display: block;
    width: 1.6em;
    height: 1.6em;
    top: calc((2rem - 1.6em) / 2);
    left: calc(5rem - 1.9em);
    border-radius: 50%;
    background: ${(props) => props.circleColor ?? 'var(--white)'};
    /* 원 이동 트랜지션 */
    transition: all 0.2s ease-in-out;
  }
  &:checked {
    background: ${(props) => props.rightBgColor ?? 'var(--black)'};
    /* 배경색 변경 트랜지션 */
    transition: all 0.2s ease-in-out;
    /* 선택 O 텍스트 */
    ::before {
      position: absolute;
      padding-right: 1em;
      content: '${(props) => props.right ?? 'No'}';
      align-items: center;
      justify-content: flex-end;
      color: ${(props) => props.rightColor ?? 'var(--white)'};
    }
    /* 선택 O 원 */
    ::after {
      content: '';
      z-index: 2;
      top: calc((2rem - 1.6em) / 2);
      left: calc((2rem - 1.6em) / 2);
      width: 1.6em;
      height: 1.6em;
      display: block;
      border-radius: 50%;
      background: ${(props) => props.circleColor ?? 'var(--white)'};
      position: relative;
    }
  }
`;

회고

단순히 외부 라이브러리 / UI 프레임워크를 사용하여 코드 복사 & 붙여넣기를 하는 것보다 훨씬 많은 것을 배우고 경험할 수 있었다. ::before와 ::after 태그에 대한 더 깊은 이해와 styled-component의 활용에 대해 더 자세한 이해를 했다.  

See the Pen React+styled-component Toggle by Yang Chan Woo (@oizys18) on CodePen.

 

728x90