[CSS] Toggle 버튼

2023. 2. 18. 15:24·Web
반응형

디자인이 너무 이쁜 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
'Web' 카테고리의 다른 글
  • [CSS] 사파리 환경 input 스타일 초기화
  • [HTML] 크롬 & 사파리 Video
  • [CSS] 가상 요소 vs 가상 클래스
  • [CSS] user-select
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    redux
    webpack
    다이소
    타입스크립트
    프로그래머스
    식물
    CCNA
    next.js
    영어독학
    react
    Babel
    영어회화
    리액트
    Docker
    네이버 부스트캠프
    네트워크
    ChatGPT
    javascript
    자바스크립트
    타일러영어
    typescript
    리얼클래스
    바질
    덤프
    바질 키우기
    알고리즘
    리얼학습일기
    CSS
    Node.js
    ReactNative
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[CSS] Toggle 버튼
상단으로

티스토리툴바