본문 바로가기

Web

[CSS] Toggle 버튼

디자인이 너무 이쁜 Toggle 버튼이 있어서 해당 코드를 베이스로 CSS 공부를 하려고 한다. 

너무 아름다운 디자인

컴포넌트 분석 

// components/toggle
import { useState } from "react";
import "./toggle.style.css";

const Toggle = () => {
  const [toggle, setToggle] = useState(false);

  const handleClickToggle = () => {
    setToggle((prev) => !prev);
  };

  const btnClassName = [
    "toggle-btn",
    toggle ? "toggle-btn-on" : "toggle-btn-off",
  ].join(" ");

  return (
    <label className="toggle-container" aria-label="Toggle">
      <input
        className="toggle-input"
        type="checkbox"
        checked={toggle}
        onClick={handleClickToggle}
        data-testid="toggle-input"
      />
      <span className={btnClassName} />
    </label>
  );
};

export default Toggle;

CSS를 공부하기 전에 컴포넌트가 어떻게 구성되었는지 먼저 확인하자

 

  const [toggle, setToggle] = useState(false);

  const handleClickToggle = () => {
    setToggle((prev) => !prev);
  };

Toggle 버튼을 누르면 상태가 변화하는 부분이다. 

간단하게 useState를 사용해서 상태를 나타내고 함수에서도 변경 말고는 하는 게 없다. 

 

  const btnClassName = [
    "toggle-btn",
    toggle ? "toggle-btn-on" : "toggle-btn-off",
  ].join(" ");

디자인을 위함 ClassName이다. 

기본적으로 toggle-btn이 있고, 상태에 따라 on / off로 나뉜다. 

 

    <label className="toggle-container" aria-label="Toggle">
      <input
        className="toggle-input"
        type="checkbox"
        checked={toggle}
        onClick={handleClickToggle}
        data-testid="toggle-input"
      />
      <span className={btnClassName} />
    </label>

렌더링 부분이다. 

특별한 것은 없고 전체 틀은 toggle-container, 그리고 실질적인 input은 toggle-input

그 외 디자인 요소인 span은 앞서 만든 btnClassName을 넣었다. 

 

디자인

.toggle-container {
  display: block;
}

.toggle-btn {
  box-sizing: initial;
  display: inline-block;
  outline: 0;
  width: 8em;
  height: 4em;
  position: relative;
  cursor: pointer;
  user-select: none;
  background: #fbfbfb;
  border-radius: 4em;
  padding: 4px;
  transition: all 0.4s ease;
  border: 2px solid #e8eae9;
}
.toggle-input:focus + .toggle-btn::after,
.toggle-btn:active::after {
  box-sizing: initial;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1), 0 4px 0 rgba(0, 0, 0, 0.08),
    inset 0px 0px 0px 3px #9c9c9c;
}
.toggle-btn::after {
  left: 0;
  position: relative;
  display: block;
  content: "";
  width: 50%;
  height: 100%;
  border-radius: 4em;
  background: #fbfbfb;
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    padding 0.3s ease, margin 0.3s ease;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1), 0 4px 0 rgba(0, 0, 0, 0.08);
}
.toggle-btn.toggle-btn-on::after {
  left: 50%;
}
.toggle-btn.toggle-btn-on {
  background: #86d993;
}
.toggle-btn.toggle-btn-on:active {
  box-shadow: none;
}
.toggle-btn.toggle-btn-on:active::after {
  margin-left: -1.6em;
}
.toggle-btn:active::after {
  padding-right: 1.6em;
}
.toggle-btn[disabled] {
  opacity: 0.7;
  cursor: auto;
}
.toggle-input {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  white-space: nowrap;
}

어질어질한 CSS 코드이다...

 

.toggle-container {
  display: block;
}

먼저 label의 display를 block으로 변경하였다. 

이건 label의 영역을 자식 전체를 포함하게 하기 위해서 설정한 것 같다. 

 

display: block이 없는 경우

이걸 하지 않는다고 Toggle이 작동하지 않는 것은 아니다. 하지만 aria-label을 사용한다면 

자식 전체를 포함해야 스크린리더를 사용했을 때 버튼 영역을 알 수 있어서 설정한 것 같다. ( 디테일 ㄷㄷ )

 

Toggle Input

.toggle-input {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  white-space: nowrap;
}

input 부분이다. 화면에서 input을 숨기기 위해서 사용한 것 같다. 

근데 이 부분은 개인적으로 그냥 opacity 또는 visibility를 사용하는 게 더 간편하지 않을까 생각이 든다. 

 

물론 position: absolute는 필요하다 ( 그래야 영역을 차지하지 않기 때문에 ) 

 

그리고 opacity와 visibility 모두 렌더링을 시키지만 화면에서 투명하게 나타내는 속성인데, 차이점으로는 

opacity는 뒤에 있는 요소를 클릭할 수 없지만, visibility는 클릭할 수 있다. 

 

그러므로 visibility가 좀 더 어울릴 것 같다. 

 

정리하면 

.toggle-input {
  visibility: hidden;
  position: absolute;
}

 나라면 이 부분은 이렇게 작성할 것 같다. 

 

Toggle 버튼

.toggle-btn {
  box-sizing: initial;
  display: inline-block;
  outline: 0;
  width: 8em;
  height: 4em;
  position: relative;
  cursor: pointer;
  user-select: none;
  background: #fbfbfb;
  border-radius: 4em;
  padding: 4px;
  transition: all 0.4s ease;
  border: 2px solid #e8eae9;
}

다음은 Toggle 버튼의 디자인 요소이다. 

 

display: inline-block;

먼저 display이다. 

본인은 왜 block이 아니라 inline-block을 했는지 이해가 잘 되지 않았다. 

 

이해하려면 block과 inline의 차이를 알아야 했다. 

간단하게 정리하면 inline은 줄 바꿈 없이 한 줄에 다른 요소들을 배치할 수 있다. 

하지만 height와 width, margin-top / down을 가질 수 없다. 

 

이것을 block은 가질 수 있다. 그런데, inline-block은 둘의 특징을 모두 가지고 있다. 

줄 바꿈이 없고, width, height, margin-top/down을 가질 수 있다. 

 

  outline: 0;

outline은 브라우저마다 제각기 다르게 표현될 수 있다. 

그래서 디자인의 통일을 위해서 0으로 두고 border 또는 box-shadow를 사용해서 테두리를 그려준다. 

 

  user-select: none;

user-select 속성은 사용자의 드래그, 텍스트 선택을 설정하는 속성이다. 

나도 처음 알게 된 속성인데, 굳이 Toggle 버튼에 설정한 이유는 모르겠다. 추후 span 영역에 텍스트를 넣을 경우를 

예상한 거라면 그럴 수 있겠다 싶다. 

 

  transition: all 0.4s ease;

추후 애니메이션의 딜레이를 0.4초로 설정하였다. 

 

.toggle-btn::after {
  left: 0;
  position: relative;
  display: block;
  content: "";
  width: 50%;
  height: 100%;
  border-radius: 4em;
  background: #fbfbfb;
  transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    padding 0.3s ease, margin 0.3s ease;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1), 0 4px 0 rgba(0, 0, 0, 0.08);
}

필자가 개인적으로 제일 어려워하는 ::after이다. 

after는 가상 선택자로 본 컨텐츠의 마지막에 생성된다. 

 

  display: block;
  width: 50%;
  height: 100%;

display를 block으로 설정해서 width와 height를 설정했다. 

width를 50%로 설정했더라도 block의 특징으로 전체 라인을 가지고 있기 때문에 나머지 50%에 다른 것이 들어갈 수 없다. 

 

  left: 0;
  position: relative;

position:relative는 left 속성을 사용할 수 있게 하기 위해서 설정했다. 

 

transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    padding 0.3s ease, margin 0.3s ease;

기본적으로 전체적으로 0.3초 딜레이를 주었고, cubic-bezier는 진행 속도를 나타낸다. 

0.175초가 흘렀을 때 애니메이션은 885 / 1000 % 진행되고, 0.32초가 흘렀을 때 1275 / 1000 %가 진행된다는 뜻. 

이것을 ease, linear, ease-in 등 키워드로 나타낼 수 있고, 다음과 같이 수치로 직접 줄 수 있다. 

 

.toggle-btn.toggle-btn-on::after {
  left: 50%;
}
.toggle-btn.toggle-btn-on {
  background: #86d993;
}

Toggle이 활성화된다면 left를 50% 이동시켜 가운데로 이동시켜주는 역할인 것 같다. 

또한 배경색을 지정해서 on / off를 표시하였다. 

 

.toggle-btn.toggle-btn-on:active::after {
  margin-left: -1.6em;
}
.toggle-btn:active::after {
  padding-right: 1.6em;
}

 active 가상 선택자로 마우스를 클릭했을 때를 나타낸다. 

기본적으로 Toggle을 클릭하면 padding-right로 1.6em이 커진다.

여기서 Toggle이 활성화가 되었을 때 버튼은 오른쪽으로 이동하므로 그것을 margin-left로 1.6em 이동시켜 

실제로는 오른쪽으로 1.6em 커졌지만 왼쪽으로 커지는 애니메이션을 만들었다. 

 

.toggle-btn[disabled] {
  opacity: 0.7;
  cursor: auto;
}

disabled 시 설정을 주어서 Toggle이 비활성화 시 따로 디자인을 주었다. 

 

 

 

반응형

'Web' 카테고리의 다른 글

[CSS] 사파리 환경 input 스타일 초기화  (0) 2023.03.13
[HTML] 크롬 & 사파리 Video  (1) 2023.03.12
[CSS] 가상 요소 vs 가상 클래스  (0) 2023.02.17
[CSS] user-select  (0) 2023.02.15
[CSS] Block VS Inline  (0) 2023.02.14