[React] CSSでiOSのUISwitchライクなSwitchを作る

CSSでiOSのUISwitchライクなReactコンポーネントを作ってみました。
2023.03.17

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは。CX事業本部 Delivery部 フロントエンドチームの加藤です。 今回はCSSでiOSのUISwitchライクなReactコンポーネントを作ってみたいと思います。

開発環境

  • macOS Monterey 12.6
  • CodeSandbox(React Typescriptテンプレートを使用)

作ったコンポーネント

iOSのUISwitchのようにクリックするたびにON/OFFが切り替わります。

コード

Switch.tsx

SwitchのReactコンポーネントです。 現在のON/OFF状態とON/OFFを切り替えた時の処理をpropsで受け取ります。 以下がポイントです。

  • ON/OFF状態を持たせるため、type="checkbox"input要素を使う
  • input要素自体はON/OFFの状態を持つだけで、外観のカスタマイズはlabel要素を使う
  • label要素クリック時にON/OFFを切り替えるためにhtmlForinput要素とlabel要素を紐づける
import "./styles.css";

type Props = {
  checked: boolean;
  onChange: (checked: boolean) => void;
};

export default function Switch(props: Props) {
  const { checked, onChange } = props;
  return (
    <>
      <input
        id="switch"
        type="checkbox"
        checked={checked}
        onChange={() => {
          onChange(!checked);
        }}
      />
      <label htmlFor="switch"></label>
    </>
  );
}

styles.css

cssです。 実際の開発ではクラスセレクターを使うことが多いかと思いますが、今回は動作確認目的であるため、要素型セレクターを使っています。 以下がポイントです。

  • inputはON/OFFの状態を持つだけで外観はlabelが担当するので、inputは非表示にする
  • labelを使ってSwitchの外観部分を表現する
  • 擬似要素label::beforeを使って白丸部分を表現する
  • 擬似クラスinput:checkedを使って、SwitchがONの時に背景色や白丸左側の余白を変更する
  • 背景色や余白の変更はtransitionでアニメーションさせる
input {
  /* チェックボックスは使わないので非表示 */
  display: none;
}

input:checked + label {
  /* スイッチONの時のSwitchの背景色*/
  background-color: #35c659;
}

input:checked + label::before {
  /* スイッチONの時の左側余白 */
  left: 1.9em;
}

label {
  position: relative;
  background-color: #e9e9ea; /* スイッチOFF時の背景色 */
  border-radius: 1em; /* 角丸 */
  cursor: pointer;
  display: flex;
  align-items: center;
  font-size: 24px;
  width: 3.75em;
  height: 2em;
  transition: 0.2s;
}

label::before {
  position: absolute;
  background-color: #ffffff;
  border-radius: 0.85em;
  content: "";
  width: 1.7em;
  height: 1.7em;
  left: 0.15em;
  transition: 0.2s ease-out;
}

App.tsx

Switchコンポーネントを呼び出す上位のコンポーネントです。 ON/OFFをuseStateで管理しています。

import { useState } from "react";
import "./styles.css";
import Switch from "./Switch";

export default function App() {
  const [switchIsChecked, setSwitchIsChecked] = useState(false);

  return (
    <Switch
      checked={switchIsChecked}
      onChange={(checked) => {
        setSwitchIsChecked(checked);
      }}
    />
  );
}

おわりに

今回はCSSでiOSのUISwitchライクなReactコンポーネントを作ってみました。 擬似要素と擬似クラスを組み合わせることで実現できました。

以上です。